diff --git a/.claude/settings.local.json b/.claude/settings.local.json index ae94ccd..fd3cd36 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -45,7 +45,18 @@ "Bash(python -c \"import base64; print\\(base64.b64encode\\(open\\(r''C:\\\\Users\\\\bryan\\\\Desktop\\\\Discord Clone\\\\discord-clone-release.keystore'',''rb''\\).read\\(\\)\\).decode\\(\\)\\)\")", "WebFetch(domain:gitea.moyettes.com)", "Bash(grep:*)", - "WebFetch(domain:addyosmani.com)" + "WebFetch(domain:addyosmani.com)", + "Bash(cp -r \"Brycord New UI/apps/web/src/components/auth/\"* \"packages/shared/src/components/auth/\")", + "Bash(cp -r \"Brycord New UI/apps/web/src/components/channel/\"* \"packages/shared/src/components/channel/\")", + "Bash(cp -r \"Brycord New UI/apps/web/src/components/dm/\"* \"packages/shared/src/components/dm/\")", + "Bash(cp -r \"Brycord New UI/apps/web/src/components/layout/\"* \"packages/shared/src/components/layout/\")", + "Bash(cp -r \"Brycord New UI/apps/web/src/components/member/\"* \"packages/shared/src/components/member/\")", + "Read(//tmp/**)", + "Bash(timeout 15 npm run dev:web)", + "Bash(sed -n '1,30p' packages/shared/src/components/channel/ChannelHeader.tsx)", + "Bash(sed -n '1,30p' packages/shared/src/components/member/MemberListContainer.tsx)", + "Bash(timeout 20 npm run dev:web)", + "Bash(timeout 8 npm run dev:web)" ] } } diff --git a/.gitignore b/.gitignore index e788f3e..a1b0b8e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,8 +9,11 @@ apps/electron/dist apps/web/dist # Legacy discord-html-copy - todo +# Reference codebases used to port UI / features — not part of the build +fluxer +Brycord New UI + # Deploy deploy/server/node_modules \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 88af6a5..807f12f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,3 +1,7 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + **Update this file when making significant changes.** See also: [CONVEX_RULES.md](./CONVEX_RULES.md) | [CONVEX_EXAMPLES.md](./CONVEX_EXAMPLES.md) @@ -15,6 +19,33 @@ See also: [CONVEX_RULES.md](./CONVEX_RULES.md) | [CONVEX_EXAMPLES.md](./CONVEX_E - **E2E Encryption**: Platform-specific crypto (Electron: Node crypto via IPC, Web: Web Crypto API) - **File Storage**: Convex built-in storage (`generateUploadUrl` + `getUrl`) +## Development Commands + +```bash +# Install +npm install # Installs all workspaces + +# Backend +npx convex dev # Start Convex dev server (creates .env.local) + +# Frontend (run alongside backend) +npm run dev:web # Web app at localhost:5173 +npm run dev:electron # Electron app (Vite + Electron concurrently) + +# Production builds +npm run build:web # Web production build -> apps/web/dist +npm run build:electron # Electron build with electron-builder +npm run build:android # Web build + Capacitor sync + +# Android +cd apps/android && npx cap sync && npx cap open android + +# Preview +cd apps/web && npx vite preview # Preview web production build +``` + +**No test framework or linter is configured in this project.** + ## Project Structure ``` @@ -33,66 +64,29 @@ Discord Clone/ │ │ ├── App.jsx # Router + AuthGuard │ │ └── index.css # Global styles │ └── platform-web/ # Web/Capacitor platform implementations -│ └── src/ -│ ├── crypto.js # Web Crypto API (RSA-OAEP, Ed25519, AES-256-GCM, scrypt) -│ ├── session.js # localStorage session persistence -│ ├── settings.js # localStorage settings -│ ├── idle.js # Page Visibility API idle detection -│ └── index.js # Bundles all web platform APIs +│ └── src/ # Web Crypto API, localStorage session/settings, Page Visibility idle ├── apps/ -│ ├── electron/ # Electron desktop app -│ │ ├── main.cjs # Electron main process -│ │ ├── preload.cjs # IPC bridge (window.* APIs) -│ │ ├── updater.cjs # electron-updater integration -│ │ ├── splash.html # Update splash screen -│ │ └── src/ -│ │ ├── main.jsx # Entry: PlatformProvider + HashRouter -│ │ └── platform/index.js # Electron platform (delegates to window.* APIs) -│ ├── web/ # Web browser app -│ │ └── src/ -│ │ └── main.jsx # Entry: PlatformProvider + BrowserRouter +│ ├── electron/ # Electron desktop app (main.cjs, preload.cjs, updater.cjs) +│ │ └── src/main.jsx # Entry: PlatformProvider + HashRouter +│ ├── web/ # Web browser app (PWA enabled via VitePWA) +│ │ └── src/main.jsx # Entry: PlatformProvider + BrowserRouter │ └── android/ # Capacitor Android wrapper -│ └── capacitor.config.ts # Points webDir at ../web/dist ├── package.json # Root workspace config -├── .env.local # Convex + LiveKit + Tenor keys +├── .env.local # Convex + LiveKit + Klipy keys └── CLAUDE.md ``` -## Key Convex Files (convex/) +## Vite & Import Aliases -- `schema.ts` - Full schema: userProfiles (with avatarStorageId, aboutMe, customStatus, joinSoundStorageId), categories (name, position), channels (with categoryId, topic, position), messages, messageReactions, channelKeys, roles, userRoles, invites, dmParticipants, typingIndicators, voiceStates, channelReadState, serverSettings (serverName, afkChannelId, afkTimeout, iconStorageId) -- `auth.ts` - getSalt, verifyUser, createUserWithProfile, getPublicKeys (includes avatarUrl, aboutMe, customStatus, joinSoundUrl), updateProfile (includes joinSoundStorageId, removeJoinSound), updateStatus, getMyJoinSoundUrl -- `categories.ts` - list, create, rename, remove, reorder -- `channels.ts` - list, get, create (with categoryId/topic/position), rename, remove (cascade), updateTopic, moveChannel, reorderChannels -- `members.ts` - getChannelMembers (includes isHoist on roles, avatarUrl, aboutMe, customStatus) -- `channelKeys.ts` - uploadKeys, getKeysForUser -- `messages.ts` - list (with reactions + username), send, edit, pin, listPinned, remove (with manage_messages permission check) -- `reactions.ts` - add, remove -- `serverSettings.ts` - get (resolves iconUrl), update (manage_channels permission), updateName (manage_channels permission), updateIcon (manage_channels permission), clearAfkChannel (internal) -- `typing.ts` - startTyping, stopTyping, getTyping, cleanExpired (scheduled) -- `dms.ts` - openDM, listDMs -- `invites.ts` - create, use, revoke -- `roles.ts` - list, create, update, remove, listMembers, assign, unassign, getMyPermissions -- `voiceState.ts` - join, leave, updateState, getAll (includes joinSoundUrl per user), afkMove (self-move to AFK channel) -- `voice.ts` - getToken (Node action, livekit-server-sdk) -- `files.ts` - generateUploadUrl, getFileUrl -- `gifs.ts` - search, categories (Node actions, Tenor API) -- `readState.ts` - getReadState, markRead, getAllReadStates, getLatestMessageTimestamps (unread tracking) +All Vite configs use `envDir: '../../'` to pick up root `.env.local`. -## Shared Frontend (packages/shared/src/) +| Alias | Resolves to | +|-------|-------------| +| `@discord-clone/shared` | `packages/shared/src/` | +| `@discord-clone/platform-web` | `packages/platform-web/src/` | +| `@shared` | `packages/shared/src/` | -- `App.jsx` - Router + AuthGuard (uses `usePlatform().session`) -- `pages/Login.jsx` - Convex auth (uses `usePlatform().crypto` for key derivation) -- `pages/Register.jsx` - Convex auth (uses `usePlatform().crypto` for key generation) -- `pages/Chat.jsx` - useQuery for channels, categories, channelKeys, DMs -- `components/ChatArea.jsx` - Messages, typing, reactions via Convex queries/mutations -- `components/Sidebar.jsx` - Channel/category creation, key distribution, invites, drag-and-drop via @dnd-kit -- `contexts/VoiceContext.jsx` - Voice state via Convex + LiveKit + idle detection via `usePlatform().idle` -- `components/TitleBar.jsx` - Conditional: only renders if `platform.features.hasWindowControls` -- `components/UpdateBanner.jsx` - Conditional: only renders if `platform.features.hasNativeUpdates` -- `components/ScreenShareModal.jsx` - Uses `usePlatform().screenCapture` -- `components/MessageItem.jsx` - Message rendering, link opening via `usePlatform().links` -- `platform/PlatformProvider.jsx` - React context providing platform APIs via `usePlatform()` hook +Convex imports from shared components use relative path `../../../../convex/_generated/api` (4 levels up from shared src subdirs). ## Platform Abstraction (usePlatform()) @@ -112,7 +106,6 @@ All platform-specific APIs are accessed via the `usePlatform()` hook: - Channel IDs use Convex `_id` (not `id`) - all references use `channel._id` - Auth: client hashes DAK -> HAK before sending, server does string comparison - First user bootstrap: createUserWithProfile creates Owner + @everyone roles -- Vite configs use `envDir: '../../'` to pick up root `.env.local` - Convex queries are reactive - no need for manual refresh or socket listeners - File uploads use Convex storage: `generateUploadUrl` -> POST blob -> `getFileUrl` - Typing indicators use scheduled functions for TTL cleanup @@ -126,6 +119,10 @@ All platform-specific APIs are accessed via the `usePlatform()` hook: - Custom join sounds: stored as `joinSoundStorageId` on `userProfiles` - Server icon: `serverSettings` stores `iconStorageId`, resolved to `iconUrl` - `userPreferences.js` `setUserPref` takes optional `settings` param for disk persistence via platform +- Module-scope functions needing crypto accept it as parameter (e.g., `encryptKeyForUsers(users, channelId, keyHex, crypto)`) +- `randomBytes(size)` returns hex string on both platforms +- Keys exchanged as PEM strings (SPKI public, PKCS8 private) for cross-platform interop +- TitleBar/UpdateBanner render conditionally based on `platform.features.*` ## Environment Variables @@ -135,12 +132,4 @@ In `.env.local` at project root: - `VITE_LIVEKIT_URL` - LiveKit server URL - `LIVEKIT_API_KEY` - LiveKit API key (used in Convex Node action) - `LIVEKIT_API_SECRET` - LiveKit API secret (used in Convex Node action) -- `TENOR_API_KEY` - Tenor GIF API key (used in Convex Node action) - -## Running the App - -1. `npm install` (installs all workspaces) -2. `npx convex dev` (starts Convex backend, creates `.env.local`) -3. Electron: `npm run dev:electron` -4. Web: `npm run dev:web` -5. Android: `npm run build:web && cd apps/android && npx cap sync && npx cap open android` +- `KLIPY_API_KEY` - Klipy GIF API customer id (used in `convex/gifs.ts`). Replaces the old `TENOR_API_KEY` after Tenor's shutdown — the legacy var name is still read as a fallback so existing deployments only need to swap the value. diff --git a/apps/android/android/app/build.gradle b/apps/android/android/app/build.gradle index c31c338..c97b2bb 100644 --- a/apps/android/android/app/build.gradle +++ b/apps/android/android/app/build.gradle @@ -8,7 +8,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 27 - versionName "1.0.40" + versionName "1.0.50" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/apps/android/package.json b/apps/android/package.json index ad427d1..61be39d 100644 --- a/apps/android/package.json +++ b/apps/android/package.json @@ -1,7 +1,7 @@ { "name": "@discord-clone/android", "private": true, - "version": "1.0.40", + "version": "1.0.50", "type": "module", "scripts": { "cap:sync": "npx cap sync", diff --git a/apps/electron/package.json b/apps/electron/package.json index 4f8850c..1f12f03 100644 --- a/apps/electron/package.json +++ b/apps/electron/package.json @@ -1,7 +1,7 @@ { "name": "@discord-clone/electron", "private": true, - "version": "1.0.40", + "version": "1.0.50", "description": "Brycord - Electron app", "author": "Moyettes", "type": "module", diff --git a/apps/electron/src/main.jsx b/apps/electron/src/main.jsx index 6b5e125..ca102c3 100644 --- a/apps/electron/src/main.jsx +++ b/apps/electron/src/main.jsx @@ -7,11 +7,8 @@ import App from '@discord-clone/shared/src/App'; import { ThemeProvider } from '@discord-clone/shared/src/contexts/ThemeContext'; import { VoiceProvider } from '@discord-clone/shared/src/contexts/VoiceContext'; import { SearchProvider } from '@discord-clone/shared/src/contexts/SearchContext'; -import { UpdateProvider } from '@discord-clone/shared/src/components/UpdateBanner'; -import TitleBar from '@discord-clone/shared/src/components/TitleBar'; import electronPlatform from './platform'; -import '@discord-clone/shared/src/styles/themes.css'; -import '@discord-clone/shared/src/index.css'; +import '@discord-clone/shared/src/global.css'; const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL); @@ -19,18 +16,15 @@ ReactDOM.createRoot(document.getElementById('root')).render( - - - - - - - - - - - - + + + + + + + + + , diff --git a/apps/electron/vite.config.js b/apps/electron/vite.config.js index bb6fbfc..a63155a 100644 --- a/apps/electron/vite.config.js +++ b/apps/electron/vite.config.js @@ -10,6 +10,25 @@ export default defineConfig({ dedupe: ['react', 'react-dom'], alias: { '@discord-clone/shared': path.resolve(__dirname, '../../packages/shared'), + '@discord-clone/ui': path.resolve(__dirname, '../../packages/ui/src'), + '@discord-clone/constants': path.resolve(__dirname, '../../packages/constants/src'), + '@brycord/ui': path.resolve(__dirname, '../../packages/ui/src'), + '@brycord/constants': path.resolve(__dirname, '../../packages/constants/src'), + '@brycord/matrix-client': path.resolve(__dirname, '../../packages/shared/src/_shims/matrix-client.ts'), + '@app/stores': path.resolve(__dirname, '../../packages/shared/src/_shims/stores'), + '@app/actions': path.resolve(__dirname, '../../packages/shared/src/_shims/actions'), + '@app/components': path.resolve(__dirname, '../../packages/shared/src/components'), + '@app/hooks': path.resolve(__dirname, '../../packages/shared/src/_shims/app/hooks'), + '@app/utils': path.resolve(__dirname, '../../packages/shared/src/_shims/app/utils'), + '@app/data': path.resolve(__dirname, '../../packages/shared/src/_shims/app/data'), + '@app': path.resolve(__dirname, '../../packages/shared/src/_shims/app'), + 'mobx-react-lite': path.resolve(__dirname, '../../packages/shared/src/_shims/mobx-react-lite.ts'), + mobx: path.resolve(__dirname, '../../packages/shared/src/_shims/mobx.ts'), + }, + }, + css: { + modules: { + localsConvention: 'camelCaseOnly', }, }, build: { diff --git a/apps/web/package.json b/apps/web/package.json index f96ee7f..dddd1f1 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,7 +1,7 @@ { "name": "@discord-clone/web", "private": true, - "version": "1.0.40", + "version": "1.0.50", "type": "module", "scripts": { "dev": "vite", diff --git a/apps/web/src/main.jsx b/apps/web/src/main.jsx index 66f7736..39d5f4f 100644 --- a/apps/web/src/main.jsx +++ b/apps/web/src/main.jsx @@ -7,10 +7,8 @@ import App from '@discord-clone/shared/src/App'; import { ThemeProvider } from '@discord-clone/shared/src/contexts/ThemeContext'; import { VoiceProvider } from '@discord-clone/shared/src/contexts/VoiceContext'; import { SearchProvider } from '@discord-clone/shared/src/contexts/SearchContext'; -import { UpdateProvider } from '@discord-clone/shared/src/components/UpdateBanner'; import webPlatform from '@discord-clone/platform-web'; -import '@discord-clone/shared/src/styles/themes.css'; -import '@discord-clone/shared/src/index.css'; +import '@discord-clone/shared/src/global.css'; const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL); @@ -19,15 +17,13 @@ ReactDOM.createRoot(document.getElementById('root')).render( - - - - - - - - - + + + + + + + diff --git a/apps/web/vite.config.js b/apps/web/vite.config.js index 3ebf133..1787e45 100644 --- a/apps/web/vite.config.js +++ b/apps/web/vite.config.js @@ -72,6 +72,25 @@ export default defineConfig({ alias: { '@discord-clone/shared': path.resolve(__dirname, '../../packages/shared'), '@discord-clone/platform-web': path.resolve(__dirname, '../../packages/platform-web'), + '@discord-clone/ui': path.resolve(__dirname, '../../packages/ui/src'), + '@discord-clone/constants': path.resolve(__dirname, '../../packages/constants/src'), + '@brycord/ui': path.resolve(__dirname, '../../packages/ui/src'), + '@brycord/constants': path.resolve(__dirname, '../../packages/constants/src'), + '@brycord/matrix-client': path.resolve(__dirname, '../../packages/shared/src/_shims/matrix-client.ts'), + '@app/stores': path.resolve(__dirname, '../../packages/shared/src/_shims/stores'), + '@app/actions': path.resolve(__dirname, '../../packages/shared/src/_shims/actions'), + '@app/components': path.resolve(__dirname, '../../packages/shared/src/components'), + '@app/hooks': path.resolve(__dirname, '../../packages/shared/src/_shims/app/hooks'), + '@app/utils': path.resolve(__dirname, '../../packages/shared/src/_shims/app/utils'), + '@app/data': path.resolve(__dirname, '../../packages/shared/src/_shims/app/data'), + '@app': path.resolve(__dirname, '../../packages/shared/src/_shims/app'), + 'mobx-react-lite': path.resolve(__dirname, '../../packages/shared/src/_shims/mobx-react-lite.ts'), + mobx: path.resolve(__dirname, '../../packages/shared/src/_shims/mobx.ts'), + }, + }, + css: { + modules: { + localsConvention: 'camelCaseOnly', }, }, build: { diff --git a/convex/_generated/api.d.ts b/convex/_generated/api.d.ts index 7a152b4..ebff6e8 100644 --- a/convex/_generated/api.d.ts +++ b/convex/_generated/api.d.ts @@ -17,13 +17,16 @@ import type * as dms from "../dms.js"; import type * as files from "../files.js"; import type * as gifs from "../gifs.js"; import type * as invites from "../invites.js"; +import type * as links from "../links.js"; import type * as members from "../members.js"; import type * as messages from "../messages.js"; +import type * as polls from "../polls.js"; import type * as presence from "../presence.js"; import type * as reactions from "../reactions.js"; import type * as readState from "../readState.js"; import type * as recovery from "../recovery.js"; import type * as roles from "../roles.js"; +import type * as savedMedia from "../savedMedia.js"; import type * as serverSettings from "../serverSettings.js"; import type * as storageUrl from "../storageUrl.js"; import type * as typing from "../typing.js"; @@ -46,13 +49,16 @@ declare const fullApi: ApiFromModules<{ files: typeof files; gifs: typeof gifs; invites: typeof invites; + links: typeof links; members: typeof members; messages: typeof messages; + polls: typeof polls; presence: typeof presence; reactions: typeof reactions; readState: typeof readState; recovery: typeof recovery; roles: typeof roles; + savedMedia: typeof savedMedia; serverSettings: typeof serverSettings; storageUrl: typeof storageUrl; typing: typeof typing; diff --git a/convex/auth.ts b/convex/auth.ts index 984e730..5bcbc32 100644 --- a/convex/auth.ts +++ b/convex/auth.ts @@ -208,6 +208,7 @@ export const getPublicKeys = query({ aboutMe: v.optional(v.string()), customStatus: v.optional(v.string()), joinSoundUrl: v.optional(v.union(v.string(), v.null())), + accentColor: v.optional(v.string()), }) ), handler: async (ctx) => { @@ -232,6 +233,7 @@ export const getPublicKeys = query({ aboutMe: u.aboutMe, customStatus: u.customStatus, joinSoundUrl, + accentColor: u.accentColor, }); } return results; @@ -248,6 +250,7 @@ export const updateProfile = mutation({ customStatus: v.optional(v.string()), joinSoundStorageId: v.optional(v.id("_storage")), removeJoinSound: v.optional(v.boolean()), + accentColor: v.optional(v.string()), }, returns: v.null(), handler: async (ctx, args) => { @@ -258,6 +261,7 @@ export const updateProfile = mutation({ if (args.customStatus !== undefined) patch.customStatus = args.customStatus; if (args.joinSoundStorageId !== undefined) patch.joinSoundStorageId = args.joinSoundStorageId; if (args.removeJoinSound) patch.joinSoundStorageId = undefined; + if (args.accentColor !== undefined) patch.accentColor = args.accentColor; await ctx.db.patch(args.userId, patch); return null; }, diff --git a/convex/channelKeys.ts b/convex/channelKeys.ts index 2600f0b..138d35f 100644 --- a/convex/channelKeys.ts +++ b/convex/channelKeys.ts @@ -1,6 +1,78 @@ import { query, mutation } from "./_generated/server"; import { v } from "convex/values"; +/** + * Rotate the symmetric key for a DM channel. Inserts a brand-new + * versioned row for each participant — existing rows are left alone + * so previously-encrypted messages remain decryptable. + * + * The caller proves they're a DM participant by passing their own + * userId; the server cross-checks against `dmParticipants` for the + * channel. Every recipient userId in `entries` must also be a + * participant — no leaking keys to random users. + * + * The new rows are tagged with `maxExistingVersion + 1`. + */ +export const rotateDMKey = mutation({ + args: { + channelId: v.id("channels"), + initiatorUserId: v.id("userProfiles"), + entries: v.array( + v.object({ + userId: v.id("userProfiles"), + encryptedKeyBundle: v.string(), + }), + ), + }, + returns: v.object({ keyVersion: v.number() }), + handler: async (ctx, args) => { + const channel = await ctx.db.get(args.channelId); + if (!channel) throw new Error("Channel not found"); + if (channel.type !== "dm") { + throw new Error("rotateDMKey is only supported for DM channels"); + } + + // Verify every (initiator + entries) userId is in dmParticipants. + const participants = await ctx.db + .query("dmParticipants") + .withIndex("by_channel", (q) => q.eq("channelId", args.channelId)) + .collect(); + const participantSet = new Set(participants.map((p) => p.userId as string)); + if (!participantSet.has(args.initiatorUserId as unknown as string)) { + throw new Error("Not a participant in this DM"); + } + for (const entry of args.entries) { + if (!participantSet.has(entry.userId as unknown as string)) { + throw new Error("Target userId is not a participant in this DM"); + } + } + + // Find the current max keyVersion for this channel. New rows go + // one above that. If no rows exist yet, start at 2 so legacy + // messages tagged version 1 still hit their original key. + const existing = await ctx.db + .query("channelKeys") + .withIndex("by_channel", (q) => q.eq("channelId", args.channelId)) + .collect(); + const maxVersion = existing.reduce( + (m, k) => (k.keyVersion > m ? k.keyVersion : m), + 0, + ); + const newVersion = maxVersion + 1; + + for (const entry of args.entries) { + await ctx.db.insert("channelKeys", { + channelId: args.channelId, + userId: entry.userId, + encryptedKeyBundle: entry.encryptedKeyBundle, + keyVersion: newVersion, + }); + } + + return { keyVersion: newVersion }; + }, +}); + // Batch upsert encrypted key bundles export const uploadKeys = mutation({ args: { diff --git a/convex/customEmojis.ts b/convex/customEmojis.ts index 4fe2057..8d1177a 100644 --- a/convex/customEmojis.ts +++ b/convex/customEmojis.ts @@ -12,12 +12,20 @@ export const list = query({ emojis.map(async (emoji) => { const src = await getPublicStorageUrl(ctx, emoji.storageId); const user = await ctx.db.get(emoji.uploadedBy); + let avatarUrl: string | null = null; + if (user?.avatarStorageId) { + avatarUrl = await getPublicStorageUrl(ctx, user.avatarStorageId); + } return { _id: emoji._id, name: emoji.name, src, createdAt: emoji.createdAt, + animated: emoji.animated ?? false, + uploadedById: emoji.uploadedBy, uploadedByUsername: user?.username || "Unknown", + uploadedByDisplayName: user?.displayName || null, + uploadedByAvatarUrl: avatarUrl, }; }) ); @@ -30,6 +38,7 @@ export const upload = mutation({ userId: v.id("userProfiles"), name: v.string(), storageId: v.id("_storage"), + animated: v.optional(v.boolean()), }, returns: v.null(), handler: async (ctx, args) => { @@ -64,6 +73,7 @@ export const upload = mutation({ name, storageId: args.storageId, uploadedBy: args.userId, + animated: args.animated ?? false, createdAt: Date.now(), }); @@ -71,6 +81,53 @@ export const upload = mutation({ }, }); +/** + * Rename a custom emoji in place. Enforces the same name validation + * as `upload` and rejects collisions with other existing emojis so + * two rows can't end up sharing a shortcode. + */ +export const rename = mutation({ + args: { + userId: v.id("userProfiles"), + emojiId: v.id("customEmojis"), + name: v.string(), + }, + returns: v.null(), + handler: async (ctx, args) => { + const roles = await getRolesForUser(ctx, args.userId); + const canManage = roles.some( + (role) => (role.permissions as Record)?.["manage_channels"] + ); + if (!canManage) { + throw new Error("You don't have permission to manage emojis"); + } + + const emoji = await ctx.db.get(args.emojiId); + if (!emoji) throw new Error("Emoji not found"); + + const name = args.name.trim(); + if (!/^[a-zA-Z0-9_]+$/.test(name)) { + throw new Error("Emoji name can only contain letters, numbers, and underscores"); + } + if (name.length < 2 || name.length > 32) { + throw new Error("Emoji name must be between 2 and 32 characters"); + } + + if (name !== emoji.name) { + const clash = await ctx.db + .query("customEmojis") + .withIndex("by_name", (q) => q.eq("name", name)) + .first(); + if (clash && clash._id !== args.emojiId) { + throw new Error(`A custom emoji named "${name}" already exists`); + } + } + + await ctx.db.patch(args.emojiId, { name }); + return null; + }, +}); + export const remove = mutation({ args: { userId: v.id("userProfiles"), diff --git a/convex/gifs.ts b/convex/gifs.ts index 4f1a5a4..766d6cb 100644 --- a/convex/gifs.ts +++ b/convex/gifs.ts @@ -3,41 +3,265 @@ import { action } from "./_generated/server"; import { v } from "convex/values"; -// Search GIFs via Tenor API +/** + * GIF search action — backed by the Klipy GIF API (Tenor's + * Google-shutdown replacement). Klipy embeds the customer id in the + * URL path and returns results under `data.data[]` with sized + * variants under `file.{hd|md|sm}.gif.url`. We normalize the response + * into a flat `{results: [{id, title, url, previewUrl, width, height}]}` + * shape so callers don't have to know which provider is upstream. + * + * Reads `KLIPY_API_KEY` from the Convex environment. Falls back to + * the legacy `TENOR_API_KEY` env var so existing deployments keep + * working as soon as the old key is replaced with a Klipy customer + * id — no `convex env set` rename required. + */ + +interface NormalizedGif { + id: string; + title: string; + url: string; + previewUrl: string; + width?: number; + height?: number; +} + +interface GifSearchResponse { + results: NormalizedGif[]; +} + +/** + * In-memory TTL cache for Klipy responses. Convex Node actions run on + * warm container instances, so this survives between invocations on + * the same worker until it's recycled. Best-effort only — a cold + * start will re-fetch upstream. Search queries live for 5 minutes, + * trending / categories for 30 minutes since they change slowly. + */ +interface CacheEntry { + value: T; + expiresAt: number; +} + +const CACHE = new Map>(); +const MAX_CACHE_SIZE = 200; +const SEARCH_TTL_MS = 5 * 60 * 1000; +const TRENDING_TTL_MS = 30 * 60 * 1000; +const CATEGORIES_TTL_MS = 30 * 60 * 1000; + +function cacheGet(key: string): T | null { + const entry = CACHE.get(key); + if (!entry) return null; + if (entry.expiresAt < Date.now()) { + CACHE.delete(key); + return null; + } + return entry.value as T; +} + +function cacheSet(key: string, value: T, ttlMs: number): void { + if (CACHE.size >= MAX_CACHE_SIZE) { + // Drop the oldest insertion — Map iteration order is insertion order. + const oldest = CACHE.keys().next().value; + if (oldest !== undefined) CACHE.delete(oldest); + } + CACHE.set(key, { value, expiresAt: Date.now() + ttlMs }); +} + +function normalizeGifItems(items: any[]): NormalizedGif[] { + return items + .map((item, idx): NormalizedGif => { + const file = item?.file ?? {}; + const md = file?.md?.gif ?? file?.sm?.gif ?? file?.hd?.gif ?? {}; + const previewVariant = + file?.sm?.gif ?? file?.md?.gif ?? file?.hd?.gif ?? {}; + const fullUrl: string = md?.url ?? item?.url ?? ""; + const previewUrl: string = previewVariant?.url ?? fullUrl; + const width: number | undefined = + typeof md?.width === "number" ? md.width : undefined; + const height: number | undefined = + typeof md?.height === "number" ? md.height : undefined; + return { + id: String(item?.slug ?? item?.id ?? `${idx}`), + title: String(item?.title ?? ""), + url: fullUrl, + previewUrl, + width, + height, + }; + }) + .filter((r) => !!r.url); +} + export const search = action({ args: { q: v.string(), limit: v.optional(v.number()), }, returns: v.any(), - handler: async (_ctx, args) => { - const apiKey = process.env.TENOR_API_KEY; - + handler: async (_ctx, args): Promise => { + const apiKey = process.env.KLIPY_API_KEY || process.env.TENOR_API_KEY; if (!apiKey) { - console.warn("TENOR_API_KEY missing"); + console.warn("KLIPY_API_KEY missing"); return { results: [] }; } - const limit = args.limit || 8; - const url = `https://tenor.googleapis.com/v2/search?q=${encodeURIComponent(args.q)}&key=${apiKey}&limit=${limit}`; + const limit = Math.min(Math.max(args.limit ?? 24, 1), 50); + const query = args.q.trim().toLowerCase(); + const cacheKey = `search:${query}:${limit}`; + const cached = cacheGet(cacheKey); + if (cached) return cached; + + // Klipy customer id goes in the path; per-page caps the result + // set without a separate `limit` query param. + const url = `https://api.klipy.com/api/v1/${encodeURIComponent(apiKey)}/gifs/search?q=${encodeURIComponent(args.q)}&per_page=${limit}&page=1&locale=en`; + + let response: Response; + try { + response = await fetch(url, { + headers: { + Accept: "application/json", + "User-Agent": + "Brycord/1.0 (+https://brycord.com) Klipy-Client", + }, + }); + } catch (err) { + console.error("Klipy fetch error:", err); + return { results: [] }; + } - const response = await fetch(url); if (!response.ok) { - console.error("Tenor API Error:", response.statusText); + console.error("Klipy API error:", response.status, response.statusText); return { results: [] }; } - return await response.json(); + const json = (await response.json()) as any; + const items: any[] = Array.isArray(json?.data?.data) ? json.data.data : []; + const results = normalizeGifItems(items); + const payload: GifSearchResponse = { results }; + cacheSet(cacheKey, payload, SEARCH_TTL_MS); + return payload; }, }); -// Get GIF categories +/** + * Trending GIFs — used for the picker's home feed when no query is + * typed. Returns the same normalized shape as `search` so callers + * can use a single render path for both. + */ +export const trending = action({ + args: { + limit: v.optional(v.number()), + }, + returns: v.any(), + handler: async (_ctx, args): Promise => { + const apiKey = process.env.KLIPY_API_KEY || process.env.TENOR_API_KEY; + if (!apiKey) return { results: [] }; + + const limit = Math.min(Math.max(args.limit ?? 24, 1), 50); + const cacheKey = `trending:${limit}`; + const cached = cacheGet(cacheKey); + if (cached) return cached; + + const url = `https://api.klipy.com/api/v1/${encodeURIComponent(apiKey)}/gifs/trending?per_page=${limit}&page=1&locale=en`; + + let response: Response; + try { + response = await fetch(url, { + headers: { + Accept: "application/json", + "User-Agent": "Brycord/1.0 (+https://brycord.com) Klipy-Client", + }, + }); + } catch (err) { + console.error("Klipy trending fetch error:", err); + return { results: [] }; + } + + if (!response.ok) { + console.error( + "Klipy trending API error:", + response.status, + response.statusText, + ); + return { results: [] }; + } + + const json = (await response.json()) as any; + const items: any[] = Array.isArray(json?.data?.data) ? json.data.data : []; + const results = normalizeGifItems(items); + const payload: GifSearchResponse = { results }; + cacheSet(cacheKey, payload, TRENDING_TTL_MS); + return payload; + }, +}); + +/** + * Trending categories — Klipy exposes `/categories` returning a list + * of slugs the picker can show as quick-search chips. Normalized + * into `{categories: [{name, image, query}]}` so the consumer + * doesn't depend on the upstream shape. + */ +interface NormalizedCategory { + name: string; + image: string; + query: string; +} + export const categories = action({ args: {}, returns: v.any(), - handler: async () => { - // Return static categories (same as the JSON file in backend) - // These are loaded from the frontend data file - return { categories: [] }; + handler: async (): Promise<{ categories: NormalizedCategory[] }> => { + const apiKey = process.env.KLIPY_API_KEY || process.env.TENOR_API_KEY; + if (!apiKey) { + return { categories: [] }; + } + + const cacheKey = `categories`; + const cached = cacheGet<{ categories: NormalizedCategory[] }>(cacheKey); + if (cached) return cached; + + const url = `https://api.klipy.com/api/v1/${encodeURIComponent(apiKey)}/gifs/categories?locale=en`; + + let response: Response; + try { + response = await fetch(url, { + headers: { + Accept: "application/json", + "User-Agent": + "Brycord/1.0 (+https://brycord.com) Klipy-Client", + }, + }); + } catch (err) { + console.error("Klipy categories fetch error:", err); + return { categories: [] }; + } + + if (!response.ok) { + console.error( + "Klipy categories API error:", + response.status, + response.statusText, + ); + return { categories: [] }; + } + + const json = (await response.json()) as any; + const items: any[] = Array.isArray(json?.data?.data) + ? json.data.data + : Array.isArray(json?.data) + ? json.data + : []; + + const categories: NormalizedCategory[] = items + .map((item) => ({ + name: String(item?.name ?? item?.title ?? ""), + image: String(item?.image ?? item?.preview ?? ""), + query: String(item?.query ?? item?.search_term ?? item?.name ?? ""), + })) + .filter((c) => !!c.query); + + const payload = { categories }; + cacheSet(cacheKey, payload, CATEGORIES_TTL_MS); + return payload; }, }); diff --git a/convex/links.ts b/convex/links.ts new file mode 100644 index 0000000..69927dc --- /dev/null +++ b/convex/links.ts @@ -0,0 +1,116 @@ +"use node"; +import { action } from "./_generated/server"; +import { v } from "convex/values"; + +export const fetchPreview = action({ + args: { url: v.string() }, + returns: v.union( + v.object({ + url: v.string(), + title: v.optional(v.string()), + description: v.optional(v.string()), + image: v.optional(v.string()), + siteName: v.optional(v.string()), + }), + v.null(), + ), + handler: async (_ctx, args) => { + try { + // Validate URL + prevent loopback SSRF + const u = new URL(args.url); + if (u.protocol !== "http:" && u.protocol !== "https:") return null; + + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 8000); + + const res = await fetch(u.toString(), { + method: "GET", + headers: { + // Discordbot User-Agent — a lot of sites (YouTube included) + // only emit og: metadata when they recognise a known crawler, + // and the generic Brycord UA gets routed to consent / interstitial + // pages that never include the tags we're after. + "User-Agent": + "Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com)", + Accept: + "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.9", + }, + signal: controller.signal, + redirect: "follow", + }); + clearTimeout(timeout); + + if (!res.ok) return null; + const contentType = res.headers.get("content-type") || ""; + if (!contentType.includes("text/html")) return null; + + // Read up to 512 KB so giant pages don't DOS the action + const reader = res.body?.getReader(); + if (!reader) return null; + const chunks: Uint8Array[] = []; + let total = 0; + const MAX = 512 * 1024; + while (total < MAX) { + const { value, done } = await reader.read(); + if (done) break; + if (value) { + chunks.push(value); + total += value.length; + } + } + try { await reader.cancel(); } catch {} + + const merged = new Uint8Array(total); + let offset = 0; + for (const c of chunks) { + merged.set(c, offset); + offset += c.length; + } + const html = new TextDecoder("utf-8").decode(merged); + + // Parse OG / twitter / tags with regex — no DOM in Node + const pick = (re: RegExp): string | undefined => { + const m = html.match(re); + return m ? decodeEntities(m[1].trim()) : undefined; + }; + + const title = + pick(/<meta[^>]+property=["']og:title["'][^>]+content=["']([^"']+)["']/i) ?? + pick(/<meta[^>]+name=["']twitter:title["'][^>]+content=["']([^"']+)["']/i) ?? + pick(/<title[^>]*>([^<]+)<\/title>/i); + const description = + pick(/<meta[^>]+property=["']og:description["'][^>]+content=["']([^"']+)["']/i) ?? + pick(/<meta[^>]+name=["']twitter:description["'][^>]+content=["']([^"']+)["']/i) ?? + pick(/<meta[^>]+name=["']description["'][^>]+content=["']([^"']+)["']/i); + let image = + pick(/<meta[^>]+property=["']og:image(?::secure_url)?["'][^>]+content=["']([^"']+)["']/i) ?? + pick(/<meta[^>]+name=["']twitter:image(?::src)?["'][^>]+content=["']([^"']+)["']/i); + const siteName = + pick(/<meta[^>]+property=["']og:site_name["'][^>]+content=["']([^"']+)["']/i); + + // Resolve relative image URLs + if (image) { + try { + image = new URL(image, u).toString(); + } catch {} + } + + if (!title && !description && !image) return null; + return { url: u.toString(), title, description, image, siteName }; + } catch { + return null; + } + }, +}); + +function decodeEntities(s: string): string { + return s + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/ /g, " ") + .replace(/&#(\d+);/g, (_, n) => String.fromCodePoint(Number(n))); +} diff --git a/convex/messages.ts b/convex/messages.ts index edb4267..35eb1a5 100644 --- a/convex/messages.ts +++ b/convex/messages.ts @@ -4,6 +4,8 @@ import { v } from "convex/values"; import { getPublicStorageUrl } from "./storageUrl"; import { getRolesForUser } from "./roles"; +const DEFAULT_ROLE_COLOR = "#99aab5"; + async function enrichMessage(ctx: any, msg: any, userId?: any) { const sender = await ctx.db.get(msg.senderId); @@ -12,19 +14,112 @@ async function enrichMessage(ctx: any, msg: any, userId?: any) { avatarUrl = await getPublicStorageUrl(ctx, sender.avatarStorageId); } + // Highest-position role with a non-default colour — mirrors how + // Discord tints usernames in chat. The Owner role is deliberately + // skipped so owners fall through to the next non-default role's + // colour (per product decision: we don't want every owner's name + // to glow in the bootstrap pink). Default grey (`#99aab5`) is + // treated as "no colour" so regular users fall back to + // `--text-primary`. + let senderRoleColor: string | null = null; + try { + const senderRoleDocs = await ctx.db + .query("userRoles") + .withIndex("by_user", (q: any) => q.eq("userId", msg.senderId)) + .collect(); + let best: { position: number; color: string } | null = null; + for (const ur of senderRoleDocs) { + const role = await ctx.db.get(ur.roleId); + if (!role) continue; + if ((role as any).name === "Owner") continue; + const color = (role as any).color as string | undefined; + const position = (role as any).position ?? 0; + if (!color || color.toLowerCase() === DEFAULT_ROLE_COLOR) continue; + if (!best || position > best.position) { + best = { position, color }; + } + } + senderRoleColor = best?.color ?? null; + } catch { + senderRoleColor = null; + } + const reactionDocs = await ctx.db .query("messageReactions") .withIndex("by_message", (q: any) => q.eq("messageId", msg._id)) .collect(); - const reactions: Record<string, { count: number; me: boolean }> = {}; + // Accumulate into a Map so we don't use emoji surrogates as object + // field names — Convex's return-value validator rejects non-ASCII + // field names, which is what caused "Field name 👍 has invalid + // character" errors on any channel with a unicode reaction. + // + // For each emoji we also collect a capped list of reactor profiles + // (up to MAX_REACTION_USERS) so the client can render the hover + // tooltip + the full reactions modal without a second round-trip. + // Reactor profiles are cached per-message so the same user picked + // for multiple emojis only costs one db lookup. + const MAX_REACTION_USERS = 100; + const profileCache = new Map< + string, + { userId: string; username: string; displayName: string | null } + >(); + const resolveProfile = async (reactorUserId: any) => { + const key = String(reactorUserId); + const cached = profileCache.get(key); + if (cached) return cached; + const profile = await ctx.db.get(reactorUserId); + const shaped = { + userId: key, + username: profile?.username || "Unknown", + displayName: profile?.displayName || null, + }; + profileCache.set(key, shaped); + return shaped; + }; + + interface ReactionAccumulator { + count: number; + me: boolean; + users: Array<{ + userId: string; + username: string; + displayName: string | null; + }>; + } + const reactionMap = new Map<string, ReactionAccumulator>(); for (const r of reactionDocs) { - const entry = (reactions[r.emoji] ??= { count: 0, me: false }); + let entry = reactionMap.get(r.emoji); + if (!entry) { + entry = { count: 0, me: false, users: [] }; + reactionMap.set(r.emoji, entry); + } entry.count++; if (userId && r.userId === userId) { entry.me = true; } + if (entry.users.length < MAX_REACTION_USERS) { + entry.users.push(await resolveProfile(r.userId)); + } } + const reactions: Array<{ + emoji: string; + count: number; + me: boolean; + users: Array<{ + userId: string; + username: string; + displayName: string | null; + }>; + }> = []; + reactionMap.forEach((info, emoji) => { + reactions.push({ + emoji, + count: info.count, + me: info.me, + users: info.users, + }); + }); let replyToUsername: string | null = null; let replyToDisplayName: string | null = null; @@ -58,7 +153,8 @@ async function enrichMessage(ctx: any, msg: any, userId?: any) { displayName: sender?.displayName || null, public_signing_key: sender?.publicSigningKey || "", avatarUrl, - reactions: Object.keys(reactions).length > 0 ? reactions : null, + senderRoleColor, + reactions: reactions.length > 0 ? reactions : null, replyToId: msg.replyTo || null, replyToUsername, replyToDisplayName, @@ -92,6 +188,43 @@ export const list = query({ }, }); +/** + * Pull the latest N messages from each of the given channel IDs in a + * single round-trip. Used by SearchPanel to scan across every channel + * the user can read without spinning up N independent useQuery hooks + * (React would error on hook-count drift between renders). + * + * `perChannelLimit` is clamped to 200 server-side to keep payload + * bounded even if the client over-asks. + */ +export const searchScan = query({ + args: { + channelIds: v.array(v.id("channels")), + perChannelLimit: v.optional(v.number()), + userId: v.optional(v.id("userProfiles")), + }, + returns: v.any(), + handler: async (ctx, args) => { + const limit = Math.min(Math.max(args.perChannelLimit ?? 100, 1), 200); + const out: Array<{ + channelId: string; + messages: any[]; + }> = []; + for (const channelId of args.channelIds) { + const rows = await ctx.db + .query("messages") + .withIndex("by_channel", (q) => q.eq("channelId", channelId)) + .order("desc") + .take(limit); + const enriched = await Promise.all( + rows.map((msg) => enrichMessage(ctx, msg, args.userId)), + ); + out.push({ channelId, messages: enriched }); + } + return out; + }, +}); + export const send = mutation({ args: { channelId: v.id("channels"), diff --git a/convex/polls.ts b/convex/polls.ts new file mode 100644 index 0000000..f9a0c18 --- /dev/null +++ b/convex/polls.ts @@ -0,0 +1,358 @@ +import { mutation, query } from "./_generated/server"; +import { v } from "convex/values"; + +const pollOptionValidator = v.object({ + id: v.string(), + text: v.string(), +}); + +const pollDocValidator = v.object({ + _id: v.id("polls"), + _creationTime: v.number(), + channelId: v.id("channels"), + createdBy: v.id("userProfiles"), + question: v.string(), + options: v.array(pollOptionValidator), + allowMultiple: v.boolean(), + disclosed: v.boolean(), + closed: v.boolean(), + closesAt: v.optional(v.number()), + createdAt: v.number(), +}); + +const pollReactionValidator = v.object({ + emoji: v.string(), + count: v.number(), + me: v.boolean(), +}); + +const pollResultsValidator = v.object({ + poll: pollDocValidator, + totals: v.record(v.string(), v.number()), + totalVotes: v.number(), + myVote: v.union(v.array(v.string()), v.null()), + // Aggregated as an array — arrays keep emoji out of object field + // names (Convex's return-value validator rejects non-ASCII fields, + // same issue we hit on messages.list). + reactions: v.array(pollReactionValidator), +}); + +export const create = mutation({ + args: { + channelId: v.id("channels"), + createdBy: v.id("userProfiles"), + question: v.string(), + options: v.array(pollOptionValidator), + allowMultiple: v.boolean(), + disclosed: v.boolean(), + closesAt: v.optional(v.number()), + }, + returns: v.id("polls"), + handler: async (ctx, args) => { + const question = args.question.trim(); + if (question.length === 0) { + throw new Error("Poll question cannot be empty"); + } + if (question.length > 500) { + throw new Error("Poll question is too long"); + } + + const cleanOptions = args.options + .map((o) => ({ id: o.id, text: o.text.trim() })) + .filter((o) => o.text.length > 0); + + if (cleanOptions.length < 2) { + throw new Error("Polls need at least 2 options"); + } + if (cleanOptions.length > 20) { + throw new Error("Polls support at most 20 options"); + } + + // Enforce unique option ids so vote diffing is unambiguous. + const seen = new Set<string>(); + for (const opt of cleanOptions) { + if (seen.has(opt.id)) { + throw new Error("Duplicate option id"); + } + seen.add(opt.id); + } + + const pollId = await ctx.db.insert("polls", { + channelId: args.channelId, + createdBy: args.createdBy, + question, + options: cleanOptions, + allowMultiple: args.allowMultiple, + disclosed: args.disclosed, + closed: false, + closesAt: args.closesAt, + createdAt: Date.now(), + }); + + return pollId; + }, +}); + +export const vote = mutation({ + args: { + pollId: v.id("polls"), + userId: v.id("userProfiles"), + optionIds: v.array(v.string()), + }, + returns: v.null(), + handler: async (ctx, args) => { + const poll = await ctx.db.get(args.pollId); + if (!poll) throw new Error("Poll not found"); + if (poll.closed) throw new Error("Poll is closed"); + if (poll.closesAt && poll.closesAt < Date.now()) { + throw new Error("Poll has expired"); + } + + // Validate the submitted option ids exist on the poll. + const validIds = new Set(poll.options.map((o) => o.id)); + for (const id of args.optionIds) { + if (!validIds.has(id)) { + throw new Error(`Unknown option id "${id}"`); + } + } + + if (args.optionIds.length === 0) { + throw new Error("Select at least one option"); + } + if (!poll.allowMultiple && args.optionIds.length > 1) { + throw new Error("This poll only allows one answer"); + } + + // Upsert: one row per (pollId, userId). + const existing = await ctx.db + .query("pollVotes") + .withIndex("by_poll_and_user", (q) => + q.eq("pollId", args.pollId).eq("userId", args.userId), + ) + .unique(); + + if (existing) { + await ctx.db.patch(existing._id, { + optionIds: args.optionIds, + votedAt: Date.now(), + }); + } else { + await ctx.db.insert("pollVotes", { + pollId: args.pollId, + userId: args.userId, + optionIds: args.optionIds, + votedAt: Date.now(), + }); + } + return null; + }, +}); + +export const clearVote = mutation({ + args: { + pollId: v.id("polls"), + userId: v.id("userProfiles"), + }, + returns: v.null(), + handler: async (ctx, args) => { + const existing = await ctx.db + .query("pollVotes") + .withIndex("by_poll_and_user", (q) => + q.eq("pollId", args.pollId).eq("userId", args.userId), + ) + .unique(); + if (existing) { + await ctx.db.delete(existing._id); + } + return null; + }, +}); + +export const close = mutation({ + args: { + pollId: v.id("polls"), + userId: v.id("userProfiles"), + }, + returns: v.null(), + handler: async (ctx, args) => { + const poll = await ctx.db.get(args.pollId); + if (!poll) throw new Error("Poll not found"); + if (poll.createdBy !== args.userId) { + throw new Error("Only the poll creator can close it"); + } + if (poll.closed) return null; + await ctx.db.patch(args.pollId, { closed: true }); + return null; + }, +}); + +export const remove = mutation({ + args: { + pollId: v.id("polls"), + userId: v.id("userProfiles"), + }, + returns: v.null(), + handler: async (ctx, args) => { + const poll = await ctx.db.get(args.pollId); + if (!poll) return null; + if (poll.createdBy !== args.userId) { + throw new Error("Only the poll creator can delete it"); + } + const votes = await ctx.db + .query("pollVotes") + .withIndex("by_poll", (q) => q.eq("pollId", args.pollId)) + .collect(); + await Promise.all(votes.map((v) => ctx.db.delete(v._id))); + const reactions = await ctx.db + .query("pollReactions") + .withIndex("by_poll", (q) => q.eq("pollId", args.pollId)) + .collect(); + await Promise.all(reactions.map((r) => ctx.db.delete(r._id))); + await ctx.db.delete(args.pollId); + return null; + }, +}); + +export const listByChannel = query({ + args: { + channelId: v.id("channels"), + }, + returns: v.array(pollDocValidator), + handler: async (ctx, args) => { + const polls = await ctx.db + .query("polls") + .withIndex("by_channel", (q) => q.eq("channelId", args.channelId)) + .order("desc") + .collect(); + return polls; + }, +}); + +export const get = query({ + args: { + pollId: v.id("polls"), + userId: v.optional(v.id("userProfiles")), + }, + returns: v.union(pollResultsValidator, v.null()), + handler: async (ctx, args) => { + const poll = await ctx.db.get(args.pollId); + if (!poll) return null; + + const votes = await ctx.db + .query("pollVotes") + .withIndex("by_poll", (q) => q.eq("pollId", args.pollId)) + .collect(); + + // Tally totals per option. Voters that picked multiple options + // each contribute a +1 to every option they picked. + const totals: Record<string, number> = {}; + for (const opt of poll.options) { + totals[opt.id] = 0; + } + for (const vote of votes) { + for (const id of vote.optionIds) { + if (totals[id] !== undefined) { + totals[id] += 1; + } + } + } + + let myVote: string[] | null = null; + if (args.userId) { + const mine = votes.find((v) => v.userId === args.userId); + myVote = mine ? mine.optionIds : null; + } + + // Aggregate reactions into an array of {emoji, count, me} rows. + // Using a Map avoids putting unicode surrogates into object field + // names, which Convex's return-value validator would reject. + const reactionDocs = await ctx.db + .query("pollReactions") + .withIndex("by_poll", (q) => q.eq("pollId", args.pollId)) + .collect(); + const reactionMap = new Map<string, { count: number; me: boolean }>(); + for (const r of reactionDocs) { + let entry = reactionMap.get(r.emoji); + if (!entry) { + entry = { count: 0, me: false }; + reactionMap.set(r.emoji, entry); + } + entry.count++; + if (args.userId && r.userId === args.userId) { + entry.me = true; + } + } + const reactions: Array<{ emoji: string; count: number; me: boolean }> = []; + reactionMap.forEach((info, emoji) => { + reactions.push({ emoji, count: info.count, me: info.me }); + }); + + return { + poll, + totals, + totalVotes: votes.length, + myVote, + reactions, + }; + }, +}); + +/** + * Toggle-add a reaction on a poll. Idempotent per (pollId, userId, + * emoji) — re-adding the same reaction is a no-op. + */ +export const addReaction = mutation({ + args: { + pollId: v.id("polls"), + userId: v.id("userProfiles"), + emoji: v.string(), + }, + returns: v.null(), + handler: async (ctx, args) => { + const existing = await ctx.db + .query("pollReactions") + .withIndex("by_poll_user_emoji", (q) => + q + .eq("pollId", args.pollId) + .eq("userId", args.userId) + .eq("emoji", args.emoji), + ) + .unique(); + if (!existing) { + await ctx.db.insert("pollReactions", { + pollId: args.pollId, + userId: args.userId, + emoji: args.emoji, + }); + } + return null; + }, +}); + +/** + * Remove a reaction on a poll. No-op if the user hasn't reacted + * with that emoji. + */ +export const removeReaction = mutation({ + args: { + pollId: v.id("polls"), + userId: v.id("userProfiles"), + emoji: v.string(), + }, + returns: v.null(), + handler: async (ctx, args) => { + const existing = await ctx.db + .query("pollReactions") + .withIndex("by_poll_user_emoji", (q) => + q + .eq("pollId", args.pollId) + .eq("userId", args.userId) + .eq("emoji", args.emoji), + ) + .unique(); + if (existing) { + await ctx.db.delete(existing._id); + } + return null; + }, +}); diff --git a/convex/roles.ts b/convex/roles.ts index f8c75a5..c658298 100644 --- a/convex/roles.ts +++ b/convex/roles.ts @@ -77,6 +77,12 @@ export const update = mutation({ handler: async (ctx, args) => { const role = await ctx.db.get(args.id); if (!role) throw new Error("Role not found"); + // Owner is frozen — we can't let a client rename/recolour it + // or strip its permissions, since that would defeat the + // assign/unassign guards in one shot. + if (role.name === "Owner") { + throw new Error("The Owner role can't be edited."); + } const { id, ...fields } = args; const updates: Record<string, unknown> = {}; @@ -92,6 +98,33 @@ export const update = mutation({ }, }); +/** + * Batch-reorder roles by passing a list of `{id, position}` pairs. + * Used by the Roles & Permissions settings surface after a drag- + * drop drop. Owner and @everyone are refused so their positions + * stay pinned at the natural extremes of the list. + */ +export const reorder = mutation({ + args: { + updates: v.array( + v.object({ + id: v.id("roles"), + position: v.number(), + }), + ), + }, + returns: v.null(), + handler: async (ctx, args) => { + for (const u of args.updates) { + const role = await ctx.db.get(u.id); + if (!role) continue; + if (role.name === "Owner" || role.name === "@everyone") continue; + await ctx.db.patch(u.id, { position: u.position }); + } + return null; + }, +}); + // Delete role export const remove = mutation({ args: { id: v.id("roles") }, @@ -99,6 +132,12 @@ export const remove = mutation({ handler: async (ctx, args) => { const role = await ctx.db.get(args.id); if (!role) throw new Error("Role not found"); + if (role.name === "Owner") { + throw new Error("The Owner role can't be deleted."); + } + if (role.name === "@everyone") { + throw new Error("The @everyone role can't be deleted."); + } const assignments = await ctx.db .query("userRoles") @@ -139,6 +178,16 @@ export const assign = mutation({ }, returns: v.object({ success: v.boolean() }), handler: async (ctx, args) => { + // Owner is immutable — it's granted once during first-user + // bootstrap (convex/auth.ts) and the app never reassigns it. The + // UI already hides it from the ManageRoles checkbox list, but we + // also reject it server-side so a crafted client can't sneak it + // onto another user. + const role = await ctx.db.get(args.roleId); + if (role?.name === "Owner") { + throw new Error("The Owner role can't be assigned."); + } + const existing = await ctx.db .query("userRoles") .withIndex("by_user_and_role", (q) => @@ -165,6 +214,13 @@ export const unassign = mutation({ }, returns: v.object({ success: v.boolean() }), handler: async (ctx, args) => { + // Owner is immutable — see `assign` above. Removing it would + // leave the server without a permission-bearing admin. + const role = await ctx.db.get(args.roleId); + if (role?.name === "Owner") { + throw new Error("The Owner role can't be removed."); + } + const existing = await ctx.db .query("userRoles") .withIndex("by_user_and_role", (q) => diff --git a/convex/savedMedia.ts b/convex/savedMedia.ts new file mode 100644 index 0000000..6c626dd --- /dev/null +++ b/convex/savedMedia.ts @@ -0,0 +1,113 @@ +import { mutation, query } from "./_generated/server"; +import { v } from "convex/values"; + +const savedMediaValidator = v.object({ + _id: v.id("savedMedia"), + _creationTime: v.number(), + userId: v.id("userProfiles"), + url: v.string(), + kind: v.string(), + filename: v.string(), + mimeType: v.optional(v.string()), + width: v.optional(v.number()), + height: v.optional(v.number()), + size: v.optional(v.number()), + encryptionKey: v.string(), + encryptionIv: v.string(), + savedAt: v.number(), +}); + +/** + * Save (favorite) an attachment to the user's media library. Idempotent + * per (userId, url) — re-saving the same media just updates the + * existing row's filename / metadata in case it changed. + */ +export const save = mutation({ + args: { + userId: v.id("userProfiles"), + url: v.string(), + kind: v.string(), + filename: v.string(), + mimeType: v.optional(v.string()), + width: v.optional(v.number()), + height: v.optional(v.number()), + size: v.optional(v.number()), + encryptionKey: v.string(), + encryptionIv: v.string(), + }, + returns: v.null(), + handler: async (ctx, args) => { + const existing = await ctx.db + .query("savedMedia") + .withIndex("by_user_and_url", (q) => + q.eq("userId", args.userId).eq("url", args.url), + ) + .unique(); + if (existing) { + await ctx.db.patch(existing._id, { + kind: args.kind, + filename: args.filename, + mimeType: args.mimeType, + width: args.width, + height: args.height, + size: args.size, + encryptionKey: args.encryptionKey, + encryptionIv: args.encryptionIv, + }); + return null; + } + await ctx.db.insert("savedMedia", { + userId: args.userId, + url: args.url, + kind: args.kind, + filename: args.filename, + mimeType: args.mimeType, + width: args.width, + height: args.height, + size: args.size, + encryptionKey: args.encryptionKey, + encryptionIv: args.encryptionIv, + savedAt: Date.now(), + }); + return null; + }, +}); + +/** + * Remove a saved-media entry by (userId, url). No-op if not present. + */ +export const remove = mutation({ + args: { + userId: v.id("userProfiles"), + url: v.string(), + }, + returns: v.null(), + handler: async (ctx, args) => { + const existing = await ctx.db + .query("savedMedia") + .withIndex("by_user_and_url", (q) => + q.eq("userId", args.userId).eq("url", args.url), + ) + .unique(); + if (existing) await ctx.db.delete(existing._id); + return null; + }, +}); + +/** + * List the user's saved media in reverse-chron order (newest first). + */ +export const list = query({ + args: { + userId: v.id("userProfiles"), + }, + returns: v.array(savedMediaValidator), + handler: async (ctx, args) => { + const items = await ctx.db + .query("savedMedia") + .withIndex("by_user", (q) => q.eq("userId", args.userId)) + .order("desc") + .collect(); + return items; + }, +}); diff --git a/convex/schema.ts b/convex/schema.ts index 8e56af8..23eccc5 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -17,6 +17,7 @@ export default defineSchema({ aboutMe: v.optional(v.string()), customStatus: v.optional(v.string()), joinSoundStorageId: v.optional(v.id("_storage")), + accentColor: v.optional(v.string()), }).index("by_username", ["username"]), categories: defineTable({ @@ -142,8 +143,71 @@ export default defineSchema({ name: v.string(), storageId: v.id("_storage"), uploadedBy: v.id("userProfiles"), + // `true` for animated (GIF / APNG) uploads so the settings UI + // can split Static vs Animated in separate sections. Optional + // so existing rows without the flag still validate — they + // surface as static in the UI by default. + animated: v.optional(v.boolean()), createdAt: v.number(), }).index("by_name", ["name"]) .index("by_uploader", ["uploadedBy"]), + + polls: defineTable({ + channelId: v.id("channels"), + createdBy: v.id("userProfiles"), + question: v.string(), + options: v.array( + v.object({ + id: v.string(), + text: v.string(), + }), + ), + allowMultiple: v.boolean(), + disclosed: v.boolean(), + closed: v.boolean(), + closesAt: v.optional(v.number()), + createdAt: v.number(), + }) + .index("by_channel", ["channelId"]) + .index("by_creator", ["createdBy"]), + + pollVotes: defineTable({ + pollId: v.id("polls"), + userId: v.id("userProfiles"), + optionIds: v.array(v.string()), + votedAt: v.number(), + }) + .index("by_poll", ["pollId"]) + .index("by_poll_and_user", ["pollId", "userId"]), + + pollReactions: defineTable({ + pollId: v.id("polls"), + userId: v.id("userProfiles"), + emoji: v.string(), + }) + .index("by_poll", ["pollId"]) + .index("by_poll_user_emoji", ["pollId", "userId", "emoji"]) + .index("by_user", ["userId"]), + + savedMedia: defineTable({ + userId: v.id("userProfiles"), + // Convex storage URL — also the dedupe key for a single user. + url: v.string(), + kind: v.string(), // 'image' | 'video' | 'audio' + filename: v.string(), + mimeType: v.optional(v.string()), + width: v.optional(v.number()), + height: v.optional(v.number()), + size: v.optional(v.number()), + // Re-post path: keep the per-file AES key + iv so the same + // attachment metadata can be embedded in a future message + // without re-uploading. The file stays encrypted in storage — + // saving just bookmarks the metadata. + encryptionKey: v.string(), + encryptionIv: v.string(), + savedAt: v.number(), + }) + .index("by_user", ["userId"]) + .index("by_user_and_url", ["userId", "url"]), }); diff --git a/convex/voice.ts b/convex/voice.ts index 2feef65..b6d9f4d 100644 --- a/convex/voice.ts +++ b/convex/voice.ts @@ -2,9 +2,24 @@ import { action } from "./_generated/server"; import { v } from "convex/values"; -import { AccessToken } from "livekit-server-sdk"; +import { AccessToken, RoomServiceClient } from "livekit-server-sdk"; -// Generate LiveKit token for voice channel +/** + * Generate a LiveKit join token for a voice channel. + * + * LiveKit servers run with `room.auto_create: false` reject joins for + * rooms that don't already exist — the client gets a 404 "requested + * room does not exist" back from the /rtc/v1/validate endpoint. To + * make this deployment-agnostic, we pre-create the room via the + * LiveKit Server SDK before minting the token. When auto-create is + * enabled the `createRoom` call is idempotent (409 Conflict is + * swallowed silently), so the same code path works on both + * configurations. + * + * Requires `LIVEKIT_URL` (or the frontend's `VITE_LIVEKIT_URL` as a + * fallback) in the Convex environment so the RoomServiceClient + * can talk to the LiveKit API. + */ export const getToken = action({ args: { channelId: v.string(), @@ -15,6 +30,46 @@ export const getToken = action({ handler: async (_ctx, args) => { const apiKey = process.env.LIVEKIT_API_KEY || "devkey"; const apiSecret = process.env.LIVEKIT_API_SECRET || "secret"; + const livekitUrl = + process.env.LIVEKIT_URL || process.env.VITE_LIVEKIT_URL || ""; + + // Ensure the room exists. The LiveKit API accepts `http(s)` URLs + // for the management endpoint, but the frontend connect URL is a + // `wss://` — swap the scheme when needed. + if (livekitUrl) { + const httpUrl = livekitUrl + .replace(/^wss:\/\//i, "https://") + .replace(/^ws:\/\//i, "http://"); + try { + const roomService = new RoomServiceClient(httpUrl, apiKey, apiSecret); + await roomService.createRoom({ + name: args.channelId, + // Empty rooms auto-destroy after 5 minutes with no participants, + // matching LiveKit's own default so stale rooms from a crashed + // client don't pile up forever. + emptyTimeout: 5 * 60, + // 50 participants is plenty for a voice channel in this + // single-server deployment and keeps any runaway join loop + // from hitting the global limit. + maxParticipants: 50, + }); + } catch (err: any) { + // 409 / "already exists" is expected when a room has already + // been created by an earlier join — swallow it and continue. + const message = String(err?.message ?? err ?? ""); + const status = err?.status ?? err?.statusCode; + const alreadyExists = + status === 409 || + /already exists/i.test(message) || + /AlreadyExists/i.test(message); + if (!alreadyExists) { + // Non-fatal: log and fall through to token generation. If the + // real issue was misconfiguration the client will surface the + // 404 it already does. + console.warn("LiveKit createRoom failed:", message); + } + } + } const at = new AccessToken(apiKey, apiSecret, { identity: args.userId, diff --git a/discord-html-copy/css/015ca14af3bac62c.css b/discord-html-copy/css/015ca14af3bac62c.css deleted file mode 100644 index a92d600..0000000 --- a/discord-html-copy/css/015ca14af3bac62c.css +++ /dev/null @@ -1,15753 +0,0 @@ -.container__75920 { - background: transparent; - border-radius: var(--radius-md); - margin: calc(var(--space-12)*-1) 0; - padding: var(--space-12) 0; - scroll-margin-bottom: var(--space-16); - scroll-margin-top: var(--space-16); - transition: background .4s ease-out .75s -} - -.flash__75920 { - background: var(--message-highlight-background-default); - transition: background 0s ease-in-out -} - -.baseControlItem_dbfbe0 { - border-radius: var(--radius-lg); - display: flex; - flex-direction: row; - flex-grow: 1; - gap: var(--space-12); - min-height: 48px; - padding: var(--space-12); - transition: background-color .15s ease -} - -.baseControlItem_dbfbe0:hover.clickable_dbfbe0 { - background-color: var(--interactive-background-hover); - cursor: pointer -} - -.baseControlItemLeadingElement_dbfbe0,.baseControlItemTrailingElements_dbfbe0 { - align-items: center; - display: flex; - justify-content: center -} - -.baseControlItemTrailingElements_dbfbe0 { - gap: var(--space-4) -} - -.baseControlItemTitle_dbfbe0 { - display: flex; - flex-direction: row; - gap: var(--space-8) -} - -.baseControlItemContent_dbfbe0 { - display: flex; - flex-direction: column; - flex-grow: 1; - gap: var(--space-4); - justify-content: center -} - -.secondaryTitle_dbfbe0:before { - color: var(--text-muted); - content: "•"; - margin: 0 var(--space-8) -} - -.navigatorIcon__15430 { - align-items: center; - background-color: var(--background-mod-muted); - border-radius: var(--radius-sm); - display: flex; - height: 48px; - justify-content: center; - width: 48px -} - -.triggerButton__64c86 { - all: unset; - align-items: center; - background-color: var(--background-mod-subtle); - border-radius: var(--radius-sm); - cursor: pointer; - display: flex; - height: 48px; - justify-content: center; - width: 48px -} - -.panel__64c86 { - height: var(--disclosure-panel-height); - overflow: clip -} - -.full-motion .panel__64c86:not(.disablePanelAnimation__64c86) { - transition: height .2s ease-in-out -} - -.full-motion .icon__64c86 { - transition: transform .2s ease-out -} - -.icon__64c86 { - transform: rotateX(0deg) -} - -.iconClosed__64c86 { - transform: rotateX(180deg) -} - -.headerTitle__450f6 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-8) -} - -.notice_c15d44 { - padding: 0 var(--space-12) -} - -.title__3c320 { - padding-top: var(--space-xs) -} - -.subtitle__3c320,.title__3c320 { - padding-inline:var(--space-sm)} - -.withSubtitle__3c320 { - padding-bottom: var(--space-xxs) -} - -.divider__143d2 { - margin: 0 var(--space-12); - width: auto -} - -.listTitle_a5d75d { - margin-bottom: var(--space-8); - margin-inline:var(--space-12)} - -.collapsibleContainer_a5d75d { - position: relative -} - -.hoverDivider_a5d75d { - opacity: 1; - transition: opacity .15s ease -} - -.collapsibleContainer_a5d75d:hover .hoverDivider_a5d75d { - opacity: 0 -} - -.panel__6131a { - margin-block:var(--space-64) var(--space-64);margin-inline: auto; - max-width: 696px; - min-width: 300px; - padding: 0 var(--space-16); - position: relative -} - -@media (max-width: 1080px) { - .panel__6131a { - margin-block:var(--space-80) var(--space-80); - margin-inline:auto} -} - -.panel__6131a * { - box-sizing: border-box -} - -.scroller__6131a { - height: 100%; - overflow-x: visible; - scroll-padding-top: 24px; - scrollbar-gutter: stable both-edges -} - -.tabBar__6131a { - margin-bottom: var(--space-24) -} - -.notice__6131a { - bottom: 0; - inset-inline: 0; - margin: 0 auto; - max-width: 696px; - min-width: 300px; - padding: var(--space-16); - position: absolute; - z-index: 2 -} - -.categories__6131a { - display: flex; - flex-direction: column; - flex-grow: 1 -} - -.divider__6131a { - --custom-divider-margin: calc(var(--space-xxl) + var(--space-xs)); - margin-bottom: var(--custom-divider-margin); - margin-top: var(--custom-divider-margin) -} - -.title__28f6b { - margin-inline:var(--space-12);margin-bottom: var(--space-4) -} - -.split__678d3 { - align-items: start; - display: grid; - gap: var(--space-16); - grid-template-columns: repeat(2,minmax(0,1fr)) -} - -@media (max-width: 800px) { - .split__678d3 { - grid-template-columns:1fr - } -} - -.animator_d17ea2 { - display: block; - position: relative -} - -.item_d17ea2 { - inset-inline: 0; - bottom: 0; - position: absolute; - top: 0 -} - -.container_e6b065 { - background: var(--background-base-lowest); - border-radius: 50%; - flex-direction: row; - flex-grow: 0; - flex-shrink: 0; - overflow: hidden -} - -.container_e6b065,.trophyIconContainer_e6b065 { - align-items: center; - display: flex; - justify-content: center; - position: relative -} - -.trophyIconContainer_e6b065 { - flex-direction: column -} - -.lockContainer_e6b065 { - align-items: center; - background: var(--background-base-lowest); - border-radius: 50%; - bottom: 0; - display: flex; - flex-direction: row; - flex-grow: 0; - flex-shrink: 0; - inset-inline-end: 0; - justify-content: center; - position: absolute -} - -.confettiIcon_e6b065 { - margin-inline-start:2px;margin-bottom: 5px -} - -.container__8e75f { - align-items: center; - background-color: var(--background-base-lower); - border-radius: var(--radius-sm); - display: flex; - flex-direction: row; - justify-content: flex-start; - padding: 12px; - position: relative -} - -.container__8e75f:hover { - background-color: var(--background-mod-normal) -} - -.actionable__8e75f { - cursor: pointer -} - -.iconContainer__8e75f { - align-items: center; - display: flex; - flex-grow: 0; - flex-shrink: 0; - height: 40px; - justify-content: center; - margin-inline-end:12px;position: relative; - width: 40px -} - -.nameContainer__8e75f { - display: flex; - flex-direction: column -} - -.unlocked__8e75f { - margin-bottom: 2px -} - -.backContainer__951af { - align-items: center; - cursor: pointer; - display: flex; - flex-direction: row; - justify-content: flex-start -} - -.icon__951af { - fill: var(--interactive-text-default); - color: var(--interactive-text-default); - margin-inline-end:4px} - -.headerContainer__951af { - align-items: center; - display: flex; - flex-direction: row; - justify-content: center; - margin-top: 32px -} - -.bottomPadding__951af { - flex: 1; - height: 200px; - width: 100px -} - -.headerIcon__951af { - align-items: center; - background: var(--background-base-lowest); - border-radius: 50%; - display: flex; - flex-direction: row; - flex-grow: 0; - flex-shrink: 0; - justify-content: center; - padding: 8px -} - -.headerTextContainer__951af { - align-items: flex-start; - display: flex; - flex-direction: column; - flex-grow: 1; - flex-shrink: 1; - gap: 8px; - justify-content: flex-start -} - -.divider__951af { - margin-bottom: 32px; - margin-top: 32px -} - -.achievementGrid__951af { - display: grid; - gap: 8px; - grid-template-columns: repeat(2,1fr); - margin-top: 8px -} - -.slideAnimator_f8824b { - overflow-x: clip -} - -.hideButton_f8824b { - align-self: flex-start -} - -.sliderLabel_f8824b { - margin-bottom: 8px -} - -.sliderDescription_f8824b { - margin-bottom: 16px -} - -.achievementContainer_f8824b { - background-color: var(--background-base-lower); - border-radius: var(--radius-sm); - cursor: pointer; - padding: 12px -} - -.achievementContainer_f8824b,.achievementIconContainer_f8824b { - align-items: center; - display: flex; - flex-direction: row; - justify-content: center -} - -.achievementIconContainer_f8824b { - background: var(--background-base-lowest); - border-radius: 50%; - flex-grow: 0; - flex-shrink: 0; - padding: 8px -} - -.achievementTextContainer_f8824b { - align-items: flex-start; - display: flex; - flex-direction: column; - flex-grow: 1; - flex-shrink: 1; - justify-content: flex-start; - margin-inline-start:12px} - -.rightCaretIcon_f8824b { - fill: var(--interactive-text-default); - color: var(--interactive-text-default) -} - -.enableAnimation_f8824b { - height: 100%; - width: 100% -} - -.enableAnimationOverlay_f8824b { - align-items: center; - background-color: var(--opacity-black-28); - bottom: 0; - display: flex; - position: fixed; - top: 0; - inset-inline: 0; - justify-content: center; - transition: opacity .2s ease; - z-index: 1 -} - -.enableAnimationOverlayHidden_f8824b { - opacity: 0; - pointer-events: none -} - -.enableAnimationOverlayVisible_f8824b { - opacity: 1 -} - -.sectionDescription__8d742 { - color: var(--text-default) -} - -.card__8d742 { - background-image: linear-gradient(90deg,var(--teal-460) 0,var(--teal-430) 55%,var(--teal-360) 100%); - flex-wrap: wrap; - gap: 16px -} - -.manageSubscriptionsButton__8d742 { - background-color: var(--white); - color: var(--teal-530) -} - -.manageSubscriptionsButton__8d742:hover { - background-color: var(--teal-100); - color: var(--teal-630) -} - -.manageSubscriptionsButton__8d742:active { - background-color: var(--teal-130); - color: var(--teal-900) -} - -.handImage__8d742 { - margin-block:-6px;margin-inline:-16px 16px} - -.textContainer__8d742 { - flex: 1; - position: relative -} - -.header__8d742 { - margin-bottom: 4px; - margin-top: 5px -} - -.description__8d742,.header__8d742 { - color: var(--white) -} - -.description__8d742 strong { - font-weight: var(--font-weight-medium) -} - -.section_e335a7 { - margin-bottom: 20px -} - -.paymentBanner_e335a7 { - align-items: center; - background: var(--background-surface-high); - border-radius: 4px; - display: flex; - margin-bottom: 14px; - padding: 10px -} - -.paymentBannerNew_e335a7 { - margin-top: 28px; - position: relative -} - -.paymentBannerIcon_e335a7 { - height: 20px; - width: 20px -} - -.paymentBannerText_e335a7 { - margin-inline-start:8px} - -.renewalMutationNotice_e335a7 { - margin-bottom: 16px -} - -.grandfatheredMessage_e335a7 { - align-items: center; - background-image: linear-gradient(to left,var(--premium-tier-1-purple),var(--premium-tier-1-blue)); - border-radius: 3px; - color: var(--white); - display: flex; - font-size: 12px; - line-height: 1.25; - margin-bottom: 16px; - padding-block:8px;padding-inline:8px 16px} - -.grandfatheredMessageText_e335a7 { - flex: 1; - margin-inline-end:16px} - -.grandfatheredIcon_e335a7 { - align-self: flex-start; - flex-shrink: 0; - height: 24px; - margin-inline-end:8px;width: 24px -} - -.subscriptionRows_e335a7 { - margin-bottom: 32px -} - -.dupSubscriptionRow_e335a7 { - margin-bottom: 48px; - position: relative; - z-index: 1 -} - -.dupSubscriptionRow_e335a7:after { - background-color: var(--background-mod-muted); - border-radius: 4px; - content: ""; - display: block; - height: calc(100% + 32px); - inset-inline-start: -16px; - position: absolute; - top: -16px; - width: calc(100% + 32px); - z-index: -1 -} - -.dupSubscriptionRow_e335a7:first-child { - margin-top: 32px -} - -.dupSubscriptionRow_e335a7:last-child { - margin-bottom: 0 -} - -.guildSubscriptionRow_e335a7 { - margin-top: 16px -} - -.guildBoostingSubscriptionRow_e335a7 { - align-items: flex-start; - border-radius: 4px; - color: var(--white); - display: flex; - font-size: 16px; - line-height: 20px; - padding: 16px; - position: relative -} - -.guildBoostingSubscriptionRowActive_e335a7 { - background-image: linear-gradient(90deg,var(--guild-boosting-blue-for-gradients) 0,var(--guild-boosting-purple-for-gradients) 100%) -} - -.guildBoostingSubscriptionRowActive_e335a7 .guildBoostingSubscriptionRowBackground_e335a7 { - background-image: url(/assets/257849697e9ea2a8.svg) -} - -.guildBoostingSubscriptionRowActive_e335a7 .guildBoostingImage_e335a7 { - background-image: url(/assets/b5b61f59756386fc.svg) -} - -.guildBoostingSubscriptionRowPaused_e335a7 { - background: var(--primary-500) -} - -.guildBoostingSubscriptionRowPaused_e335a7 .guildBoostingSubscriptionRowBackground_e335a7 { - background-image: url(/assets/858f6789052dceec.svg) -} - -.guildBoostingSubscriptionRowPaused_e335a7 .guildBoostingImage_e335a7 { - background-image: url(/assets/3c2c35373162aa43.svg) -} - -.guildBoostingSubscriptionRowCanceled_e335a7 { - background-image: linear-gradient(90deg,#f18623,#f6a12a 56%,#fab930); - color: var(--black) -} - -.guildBoostingSubscriptionRowCanceled_e335a7 .guildBoostingSubscriptionRowBackground_e335a7 { - background-image: url(/assets/874b60b1d3997aa5.svg) -} - -.guildBoostingSubscriptionRowCanceled_e335a7 .guildBoostingImage_e335a7 { - background-image: url(/assets/3c2c35373162aa43.svg) -} - -.guildBoostingSubscriptionRowFailedPayment_e335a7 { - background-image: linear-gradient(90deg,#f25a5d,#f14e76 77%,#f04a7f) -} - -.guildBoostingSubscriptionRowFailedPayment_e335a7 .guildBoostingSubscriptionRowBackground_e335a7 { - background-image: url(/assets/87055dac44c4340f.svg) -} - -.guildBoostingSubscriptionRowFailedPayment_e335a7 .guildBoostingImage_e335a7 { - background-image: url(/assets/af24ad118c39d6aa.svg) -} - -.sectionTitle_e335a7 { - margin-bottom: 16px -} - -.sectionDescription_e335a7 { - color: var(--text-default); - line-height: 20px; - margin-bottom: 16px; - margin-top: 0 -} - -.guildBoostingSubscriptionRowBackground_e335a7 { - background-position: bottom; - background-repeat: no-repeat; - border-radius: 4px; - bottom: 0; - position: absolute; - top: 0; - inset-inline: 0 -} - -.guildBoostingImage_e335a7 { - flex-shrink: 0; - height: 54px; - margin-inline-end:8px;position: relative; - width: 96px -} - -.guildBoostingSubscriptionRowBody_e335a7 { - flex-grow: 1; - position: relative -} - -.guildBoostingWordmark_e335a7 { - background-image: url(/assets/5f0d3753df69ebf7.svg); - height: 17px; - margin-bottom: 16px; - width: 184px -} - -.guildBoostingWordmark_e335a7.canceled_e335a7 { - background-image: url(/assets/7641e347e1ace434.svg) -} - -.details_e335a7 { - display: flex -} - -@media (max-width: 485px) { - .details_e335a7 { - flex-direction:column - } -} - -.detailsBlock_e335a7 { - background-color: var(--background-mod-muted); - border: 1px solid var(--border-muted); - border-radius: var(--radius-sm); - flex-basis: 50%; - flex-grow: 1; - margin-inline-end:16px;min-width: 200px; - padding: var(--space-16) -} - -@media (max-width: 485px) { - .detailsBlock_e335a7 { - margin-inline-end:0; - margin-bottom: 20px - } -} - -.paymentDropdown_e335a7 { - max-width: 400px -} - -.redBorder_e335a7 { - border: 1px solid; - border-color: var(--red-360) -} - -.detailsBlock_e335a7:last-child { - margin-inline-end:0} - -.detailBlockHeader_e335a7,.duplicateHeader_e335a7 { - margin-bottom: 8px -} - -.duplicateHeader_e335a7 { - color: var(--red-400) -} - -.billingInformation_e335a7 { - color: var(--text-default); - font-size: 14px; - line-height: 18px -} - -.finePrint_e335a7 { - color: var(--text-muted); - line-height: 1.4; - margin-top: 16px -} - -.cardText_e335a7 { - padding-inline-start:8px} - -.noItemsCard_e335a7 { - background-color: var(--background-base-lower); - border: none; - color: var(--interactive-text-active); - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px; - margin-top: 16px; - padding: 16px 19px -} - -.theme-dark .noItemsIcon_e335a7 { - background: #4f545c -} - -.newSparkles_e335a7 { - height: 34px; - inset-inline-start: -10px; - position: absolute; - top: -18px; - width: 47px -} - -.boostingDesktopAppBlurb_e335a7 { - margin-top: 8px -} - -.duplicateSubscriptionsBanner_e335a7 { - align-items: center; - background: var(--background-mod-muted); - border-radius: 4px; - display: flex; - margin-bottom: 14px; - padding: 10px -} - -.duplicateSubscriptionsBannerText_e335a7 { - margin-inline-start:8px} - -.helpNoticeDismissIcon_e335a7 { - cursor: pointer; - padding-top: 2px -} - -.button__95e2d { - background-color: var(--background-mod-normal); - cursor: pointer -} - -.button__95e2d:first-child { - border-radius: 8px 8px 0 0 -} - -.button__95e2d:last-child { - border-radius: 0 0 8px 8px -} - -.button__95e2d:last-child .contentContainer__95e2d { - border-bottom: none -} - -.contentContainer__95e2d { - align-items: center; - border-bottom: 1px solid var(--opacity-white-4); - display: flex; - justify-content: space-between; - margin-inline-start:16px;padding-block:16px;padding-inline:0 16px} - -.arrow__95e2d { - color: var(--interactive-text-default); - height: 24px; - transform: rotate(-90deg); - width: 24px -} - -.label__95e2d { - color: var(--interactive-text-active) -} - -.container_a1d343 { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: var(--radius-sm); - display: flex; - gap: 16px; - margin-bottom: 34px; - margin-top: 44px; - padding: 16px 25px -} - -.iconContainer_a1d343 { - flex-shrink: 0 -} - -.bannerImage_a1d343 { - height: 69px; - width: 117px -} - -.textContent_a1d343 { - display: flex; - flex: 1; - flex-direction: column; - gap: 4px -} - -.buttonContainer_a1d343 { - flex-shrink: 0 -} - -.categorySubGroup__65726 { - display: flex; - flex-direction: column; - gap: var(--space-16) -} - -.categorySubGroup__65726:empty { - display: none -} - -.homeBody__0920e { - box-sizing: border-box; - margin: 0 auto; - max-width: var(--custom-application-store-home-store-home-width); - padding: 0 20px; - width: 100% -} - -.categoryHeader__0920e { - border-bottom-width: 1px; - border-style: solid; - display: flex; - flex-direction: column; - font-size: 16px; - font-weight: var(--font-weight-medium); - margin: 32px 0 20px; - padding-bottom: 10px -} - -.viewAllGamesButton__0920e { - display: flex; - justify-content: center; - margin: 20px auto 40px -} - -.viewAllGamesButton__0920e:hover .viewAllIcon__0920e { - transform: rotate(-90deg) translateY(3px) -} - -.viewAllGamesButtonInner__0920e { - align-items: center; - display: flex -} - -.viewAllGames__0920e,.viewAllNitroGames__0920e { - align-items: center; - cursor: pointer; - display: flex; - font-size: 16px -} - -.full-motion .viewAllGames__0920e,.full-motion .viewAllNitroGames__0920e { - transition: transform .2s ease -} - -.viewAllGames__0920e:hover .viewAllIcon__0920e,.viewAllNitroGames__0920e:hover .viewAllIcon__0920e { - transform: rotate(-90deg) translateY(3px) -} - -.viewAllIcon__0920e { - height: 20px; - width: 20px -} - -.homeTile__0920e { - margin: 0 10px -} - -.homeTile__0920e:first-child { - margin-inline-start:0} - -.homeTile__0920e:last-child { - margin-inline-end:0} - -.spinner__0920e { - margin-bottom: 40px; - margin-top: 40px -} - -.homeWrapper__0920e { - background-color: var(--background-base-lowest); - border-top: 1px solid var(--app-frame-border); - display: flex; - flex-direction: column; - position: relative -} - -.homeWrapperNormal__0920e { - height: 100%; - width: 100% -} - -.marketingWrapper__0920e { - flex-shrink: 0 -} - -.premiumApplicationsHeader__0920e { - border-bottom-width: 1px; - border-style: solid; - display: flex; - flex-direction: column; - font-size: 16px; - font-weight: var(--font-weight-medium); - margin: 32px 0 20px; - padding-bottom: 10px -} - -.theme-dark .categoryHeader__0920e,.theme-dark .premiumApplicationsHeader__0920e { - border-color: var(--primary-500) -} - -.theme-light .categoryHeader__0920e,.theme-light .premiumApplicationsHeader__0920e { - border-color: var(--primary-200) -} - -@media (min-width: 849px) { - .categoryHeader__0920e,.premiumApplicationsHeader__0920e { - align-content:center; - color: var(--text-strong); - flex-direction: row; - justify-content: space-between - } -} - -@media (max-width: 485px) { - .premiumContainer__0920e { - padding:16px - } -} - -.viewAllGames__0920e { - color: var(--text-strong) -} - -.headerBarV2LightBackground__0920e { - background: var(--white)!important -} - -.headerBarV2DarkBackground__0920e { - background: var(--black)!important -} - -.container__0f226 { - align-items: flex-start; - background-color: var(--background-mod-normal); - border-radius: var(--radius-sm); - display: flex; - filter: saturate(var(--saturation-factor,1)); - flex-direction: column; - justify-content: center; - max-width: 1066px; - overflow: hidden; - position: relative; - width: 100% -} - -.bannerImage__0f226 { - height: 100%; - inset-inline-end: 0; - position: absolute; - z-index: -1 -} - -.textContainer__0f226 { - align-items: flex-start; - display: flex; - flex-direction: column; - justify-content: center; - padding: 32px 50px 40px; - width: 327px -} - -.heading__0f226 { - margin-bottom: 8px -} - -.giftCardButton__0f226 { - background-color: var(--background-mod-normal); - height: 40px; - margin-top: 14px; - width: 100% -} - -.giftButtonCTA__0f226 { - font-size: 14px; - font-weight: 700 -} - -.theme-light .giftCardButton__0f226 { - border-color: var(--premium-tier-2-pink-for-gradients-2); - color: var(--premium-tier-2-pink-for-gradients-2) -} - -.theme-dark .giftCardButton__0f226 { - border-color: #fff; - color: #fff -} - -.images-light .textContainer__0f226 { - background-image: linear-gradient(to right,var(--background-mod-normal),hsl(var(--primary-160-hsl)/40%) 90%,transparent) -} - -.images-dark .textContainer__0f226 { - background-image: linear-gradient(to right,var(--background-mod-normal),hsl(var(--primary-660-hsl)/40%) 90%,transparent) -} - -.container_e0cd4a { - background-color: var(--card-background-default); - border: 1px solid var(--border-muted); - border-radius: var(--radius-lg); - box-sizing: border-box; - display: flex; - filter: saturate(var(--saturation-factor,1)); - justify-content: space-between; - max-width: 1066px; - overflow: hidden; - padding: var(--space-12); - position: relative; - width: 100% -} - -.bannerImage_e0cd4a { - height: 300px; - padding: var(--space-64); - z-index: -1 -} - -.textContainer_e0cd4a { - align-items: flex-start; - display: flex; - flex-direction: column; - justify-content: center; - padding: var(--space-24) -} - -.heading_e0cd4a { - margin-bottom: var(--space-12) -} - -.giftButtonContainer_e0cd4a { - margin-top: var(--space-24) -} - -.gradientBackground_e0cd4a:before { - opacity: .3 -} - -.container__7137c { - background: var(--background-surface-high); - border: 1px solid var(--border-muted); - border-radius: 20px; - flex-direction: row; - gap: 24px; - margin-top: 40px; - max-width: 700px; - padding: 16px 24px -} - -.asset__7137c,.container__7137c { - align-items: center; - display: flex -} - -.asset__7137c { - flex-direction: column; - flex-shrink: 0; - justify-content: center -} - -.assetImage__7137c { - border-radius: var(--radius-sm) -} - -.bannerContent__7137c { - align-items: flex-start; - display: flex; - flex: 1; - flex-direction: column; - gap: 2px; - justify-content: flex; - min-height: 62px -} - -.bannerDescription__7137c { - align-items: flex-start; - text-align: start -} - -.buttonContainer__7137c { - margin-inline-start:32px} - -@media (max-width: 900px) { - .container__7137c { - flex-direction:column - } - - .bannerContent__7137c,.container__7137c { - align-items: center; - text-align: center - } - - .bannerDescription__7137c { - justify-content: center; - text-align: center - } - - .buttonContainer__7137c { - margin-inline-start:0} - - .termsApplyAnchor__7137c { - color: var(--text-link); - margin-inline-start:2px;text-decoration: underline; - text-underline-offset: 2px - } - - .termsApplyAnchor__7137c:hover { - text-decoration: none - } -} - -.freeTrialText__1995d { - text-transform: capitalize -} - -.applicationHomeBanner__62eb1 { - flex-direction: row; - height: 226px; - margin: 24px 16px; - max-width: 1052px; - position: relative -} - -.applicationHomeBanner__62eb1,.settingsBanner__62eb1 { - border-radius: var(--radius-lg); - display: flex -} - -.settingsBanner__62eb1 { - background: var(--background-mod-normal); - box-sizing: border-box; - flex: 1; - height: 171px; - margin-bottom: 24px; - max-width: 820px; - overflow: hidden; - width: 100% -} - -.settingsImageContainer__62eb1 { - display: flex; - height: auto; - width: auto -} - -.settingsBannerLeftImageContainer__62eb1 { - max-width: 130px -} - -.settingsBannerRightImageContainer__62eb1 { - max-width: 81px -} - -.bannerBoxOutlinePosition__62eb1 { - height: 226px; - position: absolute; - top: -2px; - width: 100% -} - -.backgroundGradient__62eb1 { - background: linear-gradient(90deg,#db00a4,#5968f0); - border-radius: var(--radius-lg); - height: 100%; - -webkit-mask: linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0); - mask: linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0); - -webkit-mask-composite: xor; - mask-composite: exclude; - padding: 2px; - width: calc(100% - 2px) -} - -.imageContainer__62eb1 { - border-radius: 0 16px 16px 0; - display: flex; - flex: 518 1 0%; - order: 0; - position: relative -} - -.settingsImage__62eb1 { - overflow: hidden; - width: auto -} - -.applicationHomeImage__62eb1,.settingsImage__62eb1 { - height: 100%; - -o-object-fit: cover; - object-fit: cover -} - -.applicationHomeImage__62eb1 { - border-radius: 0 14px 14px 0; - -o-object-position: left center; - object-position: left center; - width: 100% -} - -.bannerContent__62eb1 { - flex: 436 1 0%; - flex-direction: column; - justify-content: space-between; - padding: 40px 25px -} - -.bannerContent__62eb1,.settingsBannerContent__62eb1 { - display: flex; - position: relative; - width: 100% -} - -.settingsBannerContent__62eb1 { - flex: 405 1 0%; - flex-direction: column; - gap: 8px; - min-width: 300px; - padding: 25px -} - -@container premium-marketing-page-container (max-width: 960px) { - .bannerHeader__62eb1 { - font-size: 32px; - font-weight: 800 - } - - .bannerHeader__62eb1,.bannerText__62eb1 { - font-style: normal; - line-height: 1.25 - } - - .bannerText__62eb1 { - font-size: 16px; - font-weight: 500 - } -} - -@container premium-marketing-page-container (max-width: 960px) and (min-width: 720px) { - .bannerHeader__62eb1 { - font-size: 20px; - font-style: normal; - font-weight: 800; - line-height: 24px - } - - .bannerText__62eb1 { - font-size: 14px; - font-style: normal; - font-weight: 500; - line-height: 1.2857142857142858 - } -} - -@container premium-marketing-page-container (max-width: 720px) { - .bannerHeader__62eb1 { - font-size: 20px; - font-style: normal; - font-weight: 800; - line-height: 24px - } - - .bannerText__62eb1 { - font-size: 14px; - font-style: normal; - font-weight: 500; - line-height: 1.2857142857142858 - } - - .bannerContent__62eb1 { - padding: 16px - } -} - -.subscribeButton__62eb1 { - margin-top: 8px; - min-width: 110px; - width: -moz-fit-content; - width: fit-content -} - -.subscribeButtonText__62eb1 { - max-width: unset -} - -.premiumTrialBadge__62eb1 { - align-items: center; - border-radius: var(--radius-md); - display: flex; - inset-inline-start: 24px; - margin-inline-start:auto;padding: 0 6px; - pointer-events: all; - position: absolute; - text-transform: uppercase; - top: -8px -} - -.badgeGradient__62eb1 { - background: linear-gradient(90deg,#db00a4,#5968f0) -} - -.buttonGradient__62eb1 { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color); - color: var(--white)!important -} - -.subBanner__62eb1 { - align-items: center; - background: var(--background-surface-high); - border-radius: var(--radius-lg); - display: flex; - flex-direction: row; - height: 100px; - justify-content: center; - margin: auto auto 40px; - padding: 16px 24px; - position: relative; - width: 620px -} - -.subHomeBannerImageContainer__62eb1 { - display: flex; - height: auto; - max-width: 96px; - width: auto -} - -.subBannerContent__62eb1 { - align-items: flex-start; - display: flex; - flex: 405 1 0%; - flex-direction: column; - gap: 8px; - min-width: 300px; - padding: 25px; - position: relative; - text-align: start; - width: 100% -} - -.container_ce5f0d { - background: var(--background-surface-high); - border: 1px solid var(--border-muted); - border-radius: 20px; - flex-direction: row; - gap: 40px; - padding: 32px 24px -} - -.bannerContainer_ce5f0d,.container_ce5f0d { - align-items: center; - display: flex; - max-width: 800px -} - -.bannerContainer_ce5f0d { - justify-content: center -} - -.bannerContent_ce5f0d { - flex: 1 0 0; - flex-direction: column; - max-width: 476px -} - -.bannerContent_ce5f0d,.bannerContentText_ce5f0d { - align-items: flex-start; - display: flex -} - -.bannerContentText_ce5f0d { - flex-direction: column; - gap: 8px; - margin-bottom: 24px; - text-align: start -} - -.referredFriendAvatar_ce5f0d { - border: 2px solid var(--background-surface-high); - border-radius: 50% -} - -.availableReferralSlot_ce5f0d { - align-items: center; - background: var(--background-mod-strong); - border-radius: 32px; - color: var(--text-strong); - display: flex; - flex-shrink: 0; - font-weight: var(--font-weight-medium); - height: 24px; - justify-content: center; - line-height: 18px; - width: 24px -} - -.progressContainer_ce5f0d { - align-items: center; - align-self: stretch; - background: var(--background-surface-high); - display: flex; - flex-direction: row; - gap: 8px; - margin-bottom: 12px; - max-width: 800px -} - -.progressCircle_ce5f0d { - align-items: center; - display: flex; - flex-direction: column; - flex-shrink: 0; - height: 160px; - justify-content: center; - padding: 8px; - width: 160px -} - -.progressCircleImage_ce5f0d { - max-width: 150px -} - -.progressCircleBorder_ce5f0d { - height: "100%"; - inset-inline-start: 0; - position: "absolute"; - top: 0; - transform: rotate(-90deg); - width: "100%" -} - -.progressCircleBackground_ce5f0d { - fill: var(--background-surface-high) -} - -.progressCircleRing_ce5f0d { - fill: var(--background-mod-strong) -} - -.glow_ce5f0d { - background: transparent; - border-radius: 50%; - box-shadow: 0 0 1px #53ac66,0 0 1px #53ac66,0 0 15px #53ac66; - height: 100%; - width: 100% -} - -.timerContainer_d0d75b { - display: flex; - flex-direction: column; - gap: 10px; - position: relative -} - -.timeUnitsOuterContainer_d0d75b { - display: flex; - flex-direction: row; - justify-content: center -} - -.timeUnitInnerContainer_d0d75b { - display: flex; - flex-direction: column; - gap: 6px -} - -.time_d0d75b { - display: flex; - flex-direction: row; - gap: 2px -} - -.timeBoxOutline_d0d75b { - align-content: center; - background-color: hsl(var(--opacity-24-hsl)/.23921568627450981); - border-radius: 6px; - height: 36px; - justify-content: center; - width: 24px -} - -.unitDivider_d0d75b { - margin: 0 6px; - position: relative; - top: 4px -} - -.offerPill__3b397 { - align-items: center; - background: linear-gradient(90deg,#db00a4,#5968f0); - border-radius: 20px; - display: flex; - margin-bottom: 12px; - padding: 0 4px; - position: relative; - text-transform: uppercase; - width: -moz-fit-content; - width: fit-content -} - -.premiumBrandRefreshOfferPill__3b397 { - -webkit-backdrop-filter: blur(12px); - backdrop-filter: blur(12px); - background: linear-gradient(90deg,rgba(155,29,165,.34),rgba(30,35,83,.34) 120%); - border: 1px solid var(--border-normal); - border-radius: var(--radius-xl); - padding: var(--space-8) var(--space-12); - text-transform: capitalize; - width: -moz-max-content; - width: max-content -} - -.banner__4524c { - align-items: center; - background: var(--background-surface-high); - border-radius: 20px; - display: flex; - flex-direction: row; - height: 80px; - justify-content: center; - margin: auto auto 40px; - padding: 16px 24px; - position: relative; - width: 624px -} - -.bodyContainer__4524c { - align-items: flex-start; - display: flex; - flex-direction: column; - gap: 2px; - text-align: start; - width: 400px -} - -.trialPill__4524c { - justify-content: center; - position: absolute; - top: -13px -} - -.promoNitroButton__4524c { - margin-inline-start:auto;min-height: 40px; - min-width: 110px -} - -.bannerArt__4524c { - margin-inline-end:16px} - -.pill__4524c { - inset-inline-start: 20px; - position: absolute; - top: -8px -} - -.buttonGradient__4524c { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color); - background-clip: padding-box; - border-radius: var(--radius-sm); - color: var(--white) -} - -.boltContainer__24d89,.carContainer__24d89,.hammerContainer__24d89,.keyContainer__24d89,.starContainer__24d89 { - pointer-events: none -} - -.carContainer__24d89,.hammerContainer__24d89 { - z-index: -1 -} - -.jumpingWumpusContainer__13b73 { - bottom: -100px; - inset-inline-start: calc(100% + 20px); - pointer-events: none; - position: absolute -} - -.jumpingWumpus__13b73 { - height: 227px -} - -.hammerContainer__13b73,.keyContainer__13b73,.starContainer__13b73 { - pointer-events: none; - position: absolute -} - -.hammerContainer__13b73 { - inset-inline-end: 40px; - top: -50px -} - -.keyContainer__13b73 { - inset-inline-start: -180px; - top: calc(100% + 20px) -} - -.starContainer__13b73 { - inset-inline-start: -150px; - top: -50px -} - -.starTrinket__13b73 { - height: 80px -} - -.keyTrinket__13b73 { - height: 60px -} - -.hammerTrinket__13b73 { - height: 30px -} - -@container premium-marketing-page-container (max-width: 960px) { - .keyContainer__13b73,.starContainer__13b73 { - transform: translateX(50px) - } - - .jumpingWumpusContainer__13b73 { - transform: translateX(-50px) - } -} - -@container premium-marketing-page-container (max-width: 720px) { - .jumpingWumpus__13b73 { - height: 163px - } - - .keyContainer__13b73 { - transform: translateX(100px) - } - - .starTrinket__13b73 { - height: 62px - } - - .keyTrinket__13b73 { - height: 46px - } -} - -.header__07389 { - font-size: 50px; - font-style: italic; - font-weight: 900; - letter-spacing: -1px; - line-height: 100%; - text-transform: uppercase -} - -@container premium-marketing-page-container (max-width: 720px) { - .header__07389.responsive__07389 { - font-size: 42px; - letter-spacing: -.84px - } -} - -@container premium-marketing-page-container (max-width: 540px) { - .header__07389.responsive__07389 { - font-size: 38px; - letter-spacing: -.76px - } -} - -.container__101ad { - max-height: 300px; - min-width: 800px -} - -.container__101ad,.containerV2__101ad { - align-items: center; - display: flex; - flex-direction: column; - padding-bottom: 80px; - text-align: center; - width: 100% -} - -.containerV2__101ad { - justify-content: center; - padding-top: 42px -} - -.trialPill__101ad { - justify-content: center; - margin-bottom: 16px -} - -.content__101ad { - min-width: 500px; - position: absolute; - top: 90px -} - -@media (max-width: 485px) { - .content__101ad { - min-width:unset - } -} - -.churnDiscountContent__101ad { - top: 20px -} - -.contentV2__101ad { - min-width: 500px -} - -@media (max-width: 485px) { - .contentV2__101ad { - min-width:unset - } -} - -.hidden__101ad { - visibility: hidden -} - -.heroHeadingOriginalButtonContainer__101ad { - align-items: center; - display: flex; - justify-content: center -} - -.header__101ad { - margin-bottom: 16px -} - -.premiumSubscribeButton__101ad { - align-items: center; - display: flex; - justify-content: center -} - -.button__101ad { - box-shadow: 0 12px 36px 0 rgba(0,0,0,.12); - height: 40px -} - -.reverseTrialGiftButton__101ad { - height: 40px; - margin: auto; - min-width: 183px; - position: relative -} - -.subscribeButton__101ad { - margin-inline-end:12px} - -.headerArt__101ad,.headerArtV2__101ad { - pointer-events: none; - width: 100% -} - -.headerArtV2__101ad { - min-width: 800px; - position: absolute; - top: 0; - z-index: -1 -} - -.reverseContainer__101ad { - gap: 40px; - text-align: center; - width: 100% -} - -.reverseContainer__101ad,.reverseTrialHomeHeader__101ad { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center -} - -.reverseTrialHomeHeader__101ad { - flex-shrink: 0; - margin-top: 40px; - position: relative; - width: 656px -} - -.flexCentered__101ad { - display: flex; - justify-content: center -} - -.tooltip__101ad { - margin-bottom: 15px; - text-align: center -} - -.pillContainer__101ad { - display: inline-block; - margin-top: 5px -} - -.pillIcon__101ad { - padding-inline-end:5px} - -.referralBanner__101ad { - margin: 64px auto auto -} - -.churnEntrypointButtons__101ad { - display: flex; - flex-direction: row; - gap: 8px; - width: 380px -} - -.churnEntrypointDescriptionBrandRefresh__101ad { - margin-bottom: -12px; - margin-top: 16px -} - -.churnEntrypointDescription__101ad { - margin-bottom: 20px -} - -.churnDiscountHeader__101ad { - margin-bottom: 16px; - max-width: 600px -} - -.heroOfferCountdown__101ad { - margin-bottom: 20px -} - -.container__1c7f8 { - align-items: center; - display: flex; - flex-direction: column; - padding: 80px 0 var(--space-64); - width: 100% -} - -.gradientBackground__1c7f8 { - position: absolute; - top: 0; - z-index: -1 -} - -.content__1c7f8 { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-32); - text-align: center -} - -.content__1c7f8>:last-child { - margin-top: var(--space-32) -} - -.headerContainer__1c7f8 { - margin: 0 100px; - max-width: 625px; - min-width: -moz-min-content; - min-width: min-content; - position: relative; - width: -moz-fit-content; - width: fit-content -} - -@container premium-marketing-page-container (max-width: 960px) { - .headerContainer__1c7f8 { - max-width: 500px - } -} - -@container premium-marketing-page-container (max-width: 720px) { - .headerContainer__1c7f8 { - max-width: 400px - } -} - -.twoButtonContainer__1c7f8 { - align-items: center; - display: flex; - gap: 16px; - justify-content: center -} - -.wrapper__0a838 { - bottom: 0; - inset-inline-start: 0; - margin-bottom: 42px; - opacity: 0; - pointer-events: none; - position: sticky; - width: 100%; - z-index: 10 -} - -.innerWrapper__0a838,.wrapper__0a838 { - display: flex; - justify-content: center -} - -.innerWrapper__0a838 { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: 16px; - box-sizing: border-box; - padding: 16px; - pointer-events: all; - position: relative -} - -.innerWrapper__0a838:after,.innerWrapper__0a838:before { - content: ""; - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.invisible__0a838>.innerWrapper__0a838 { - pointer-events: none -} - -.tier2Gradient__0a838 { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color); - color: var(--white) -} - -.descriptionContainer__0a838 { - display: flex; - flex-direction: column; - margin: 0 16px; - width: 320px -} - -.nitroIcon__0a838 { - border-radius: var(--radius-sm); - filter: saturate(var(--saturation-factor,1)); - height: 48px; - width: 48px -} - -.cardDescription__1f069 { - background-image: linear-gradient(to bottom,var(--card-background-default) 50%,transparent 100%) -} - -.nitroGradientBorderHover__1f069 { - position: relative -} - -.nitroGradientBorderHover__1f069:before { - background: linear-gradient(95.07deg,var(--premium-tier-2-purple-for-gradients) 0,var(--premium-tier-2-purple-for-gradients-2) 49.96%,var(--premium-tier-2-pink-for-gradients) 95.93%),var(--premium-tier-2-purple-for-gradients); - border-radius: calc(var(--custom-card-border-radius) + 2px); - content: ""; - inset: -2px; - opacity: 0; - position: absolute; - z-index: -10 -} - -.full-motion .nitroGradientBorderHover__1f069:before { - transition: opacity .2s ease-in-out -} - -.nitroGradientBorderHover__1f069:hover:before { - opacity: 1 -} - -.container_e99fef,.progressHeader_e99fef { - align-items: flex-start; - align-self: stretch; - display: flex; - flex-direction: column -} - -.progressHeader_e99fef { - width: 100% -} - -.progressBar_e99fef { - background-color: var(--background-surface-highest); - border-radius: 94px; - height: 10px; - position: relative; - width: 100% -} - -.progressBarContainer_e99fef { - margin: var(--space-12) 0; - width: 100% -} - -.progressBarFill_e99fef { - background: linear-gradient(90deg,#13184c,#5865f2); - height: 100%; - fill: #5865f2; - border-radius: 5px 0 0 5px -} - -.progressBarIndicator_e99fef { - align-items: center; - border-radius: 50%; - box-shadow: 0 0 20px 5px #5865f2; - display: flex; - height: 100%; - position: absolute -} - -.cards_e99fef { - gap: var(--space-12); - width: 100% -} - -.cards_e99fef,.promoCardContainer_e99fef { - display: flex; - flex-direction: column -} - -.promoCardContainer_e99fef { - align-items: stretch; - background-color: var(--background-surface-highest); - border: 1px solid var(--border-muted); - border-radius: var(--radius-lg); - gap: 14px; - margin-top: var(--space-12); - padding: var(--space-16) 0 -} - -.wrap_e99fef { - display: flex; - flex-direction: row; - gap: 10px; - padding: 0 var(--space-16) -} - -.promoCardContent_e99fef { - width: 100% -} - -.promoCardContentDetails_e99fef { - width: 60% -} - -.promoCardContentText_e99fef { - display: flex; - flex-direction: column; - gap: 4px; - margin: var(--space-12) 0 -} - -.promoCardAssetContainer_e99fef { - align-items: center; - display: flex; - width: 40% -} - -.promoCardAsset_e99fef { - margin: 0 auto -} - -.lockIconContainer_e99fef { - align-items: center; - aspect-ratio: 1/1; - background: var(--background-mod-subtle); - border-radius: var(--radius-sm); - display: flex; - flex-shrink: 0; - height: 32px; - justify-content: center; - width: 32px -} - -.claimedFooterContainer_e99fef { - border-top: 1px solid var(--border-muted); - padding-top: var(--space-16) -} - -.claimedFooter_e99fef,.claimedFooterContainer_e99fef { - align-items: center; - display: flex; - flex-direction: row; - justify-content: center; - width: 100% -} - -.claimedFooter_e99fef { - gap: 10px; - margin: 0 var(--space-16) -} - -.claimedFooterCode_e99fef { - flex: 1 0 0 -} - -.extraCodeButton_e99fef { - align-items: center; - background-color: var(--background-surface-highest); - border-radius: 0 0 var(--radius-lg) var(--radius-lg); - cursor: pointer; - display: flex; - justify-content: center; - margin: 0 auto; - padding: var(--space-4) 0; - width: 80% -} - -.container_f8fa75 { - align-items: center; - display: flex; - flex-direction: row; - justify-content: center -} - -.image_f8fa75 { - margin-top: var(--space-16); - width: 100% -} - -.container__29469 { - display: flex; - flex-direction: column; - height: 100% -} - -.imageContainer__29469 { - margin-top: auto -} - -.container__1f2ac { - height: 100%; - justify-content: space-between -} - -.container__1f2ac,.textColumn__1f2ac { - align-items: center; - display: flex; - flex-direction: column -} - -.badgeText__1f2ac { - margin-bottom: 4px -} - -.badgeImage__1f2ac { - align-self: center; - height: auto; - max-height: 66%; - pointer-events: none; - width: 70% -} - -.full-motion .badgeImage__1f2ac { - transition: all .2s ease-in-out -} - -.badgeImage__1f2ac.wide__1f2ac { - width: 85% -} - -.upcomingBadge__1f2ac { - filter: saturate(0); - opacity: .4 -} - -div:hover>div>.container__1f2ac .badgeImage__1f2ac:not(.upcomingBadge__1f2ac) { - filter: drop-shadow(0 0 34px var(--custom-badge-glow-color)) -} - -.hoverWrapper_b5493b { - display: flex -} - -.hoverWrapper_b5493b:hover .flipCardContainer_b5493b,.hoverWrapper_b5493b:hover .noFlipCardContainer_b5493b,.hoverWrapper_b5493b:hover .noFlipCardContainerCarousel_b5493b { - transform: translateY(-8px) -} - -.hoverWrapper_b5493b:hover .card_b5493b { - box-shadow: var(--elevation-medium) -} - -.hoverWrapper_b5493b:hover .unavailablePerkPill_b5493b { - display: none -} - -.hoverWrapper_b5493b:hover .cardDescription_b5493b { - opacity: 1 -} - -.hoverWrapper_b5493b:hover .cardDescription_b5493b button { - bottom: 24px; - opacity: 1; - pointer-events: all; - transform: scale(1) -} - -.hoverWrapper_b5493b:hover .hoverCardImage_b5493b { - border-radius: 20px; - margin: -24px; - width: calc(100% + 48px) -} - -.hoverWrapper_b5493b:hover .carouselCardImage_b5493b,.hoverWrapper_b5493b:hover .carouselCardImageGrayscale_b5493b { - margin-inline-start:0;margin-bottom: -9px; - scale: 116.5% -} - -.hoverWrapper_b5493b:hover .cardComponent_b5493b { - scale: 65%; - transform-origin: bottom -} - -@media (max-width: 959px) { - .hoverWrapper_b5493b:hover .cardComponent_b5493b { - scale:85% - } -} - -@media (max-width: 820px) { - .hoverWrapper_b5493b:hover .cardComponent_b5493b { - scale:75% - } -} - -@media (max-width: 700px) { - .hoverWrapper_b5493b:hover .cardComponent_b5493b { - scale:65% - } -} - -@media (max-width: 650px) { - .hoverWrapper_b5493b:hover .cardComponent_b5493b { - scale:55% - } -} - -@media screen and (max-width: 1330px) { - .hoverWrapper_b5493b:hover .carouselCardImage_b5493b,.hoverWrapper_b5493b:hover .carouselCardImageGrayscale_b5493b { - margin-inline-start:0; - margin-bottom: -9px; - scale: 122% - } -} - -.hoverWrapper_b5493b:hover .partialFlipCard_b5493b { - --custom-y-rotation: -30deg; - transform: rotateY(var(--custom-y-rotation)) -} - -.hoverWrapper_b5493b:hover .partialFlipCard_b5493b.reducedMotion_b5493b { - transform: none -} - -.hoverWrapper_b5493b:hover .ultraFlipCard_b5493b { - --custom-y-rotation: -570deg; - transform: rotateY(var(--custom-y-rotation)) -} - -.hoverWrapper_b5493b:hover .rotateCard_b5493b { - --custom-x-rotation: -520deg; - --custom-y-rotation: -1140deg; - transform: rotateX(var(--custom-x-rotation)) rotateY(var(--custom-y-rotation)) -} - -.hoverWrapper_b5493b:hover .flipCard_b5493b.flipped_b5493b { - transform: rotateY(-180deg) -} - -.reducedMotion_b5493b .hoverWrapper_b5493b { - transition: none -} - -.flipCardContainer_b5493b,.noFlipCardContainer_b5493b,.noFlipCardContainerCarousel_b5493b { - display: flex; - filter: saturate(var(--saturation-factor,1)); - width: 100% -} - -.flipCardContainer_b5493b.forceShadow_b5493b .card_b5493b,.noFlipCardContainer_b5493b.forceShadow_b5493b .card_b5493b,.noFlipCardContainerCarousel_b5493b.forceShadow_b5493b .card_b5493b { - box-shadow: var(--elevation-medium) -} - -.full-motion .noFlipCardContainer_b5493b,.full-motion .noFlipCardContainerCarousel_b5493b { - transition: all .1s ease-in-out -} - -@media screen and (min-width: 1331px) { - .noFlipCardContainerCarousel_b5493b { - min-width:350px - } -} - -.confettiCanvas_b5493b { - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.flipCardContainer_b5493b { - min-height: 350px; - perspective: 1000px -} - -.full-motion .flipCardContainer_b5493b { - transition: all .1s ease-in-out -} - -.flipCardContainer_b5493b .card_b5493b { - height: calc(100% - 48px); - width: calc(100% - 48px) -} - -.flipCardContainer_b5493b .flipCard_b5493b.clickable_b5493b .cover_b5493b { - opacity: 1 -} - -.flipCardContainer_b5493b .flipCard_b5493b.flipped_b5493b { - transform: rotateY(-180deg) -} - -.flipCardContainer_b5493b .flipCard_b5493b.flipped_b5493b .flipCardButtonContainer_b5493b { - opacity: 0; - pointer-events: none -} - -.flipCardContainer_b5493b .flipCard_b5493b.flipped_b5493b .cover_b5493b { - opacity: 0 -} - -.flipCard_b5493b { - position: relative; - transform-style: preserve-3d; - width: 100% -} - -.full-motion .flipCard_b5493b { - transition: transform .8s -} - -.flipCard_b5493b.reducedMotion_b5493b { - transition: none -} - -.flipCardBack_b5493b,.flipCardFront_b5493b { - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.flipCardBack_b5493b { - transform: rotateY(180deg) -} - -.flipCardHidden_b5493b { - opacity: 0; - pointer-events: none -} - -.flipCardButtonContainer_b5493b { - align-items: center; - bottom: 0; - display: flex; - position: absolute; - top: 0; - inset-inline: 0; - justify-content: center; - pointer-events: none; - z-index: 40 -} - -.cardComponent_b5493b { - transition: scale .15s ease-in-out -} - -.card_b5493b { - --custom-card-border-radius: 20px; - align-items: flex-start; - background-color: var(--card-background-default); - background-repeat: no-repeat; - background-size: cover; - border: 1px solid var(--border-muted); - border-radius: var(--custom-card-border-radius); - display: flex; - flex-direction: column; - justify-content: flex-start; - padding: 24px; - position: relative; - width: 100% -} - -.full-motion .card_b5493b { - transition: all .1s ease-in-out -} - -.hideOverflow_b5493b { - overflow: hidden -} - -.clickable_b5493b { - cursor: pointer; - pointer-events: all -} - -.pill_b5493b { - background-image: linear-gradient(to right,var(--premium-tier-2-purple-for-gradients),var(--premium-tier-2-pink-for-gradients)); - border-radius: 10px; - color: var(--white); - inset-inline-start: 24px; - padding: 0 8px; - position: absolute; - top: -8px; - z-index: 30 -} - -.cardHeading_b5493b { - background-clip: text; - -webkit-background-clip: text!important; - background-color: var(--text-strong); - color: transparent; - display: inline-block; - margin-bottom: 8px; - white-space: pre-wrap; - width: 100%; - z-index: 10 -} - -.cardSubtitle_b5493b { - flex-grow: 1; - margin-bottom: 16px; - width: 100%; - z-index: 10 -} - -.carouselCardImage_b5493b { - background: linear-gradient(193deg,var(--premium-tier-0-header-gradient-1) 0,var(--premium-tier-0-header-gradient-3) 49.52%,var(--premium-tier-0-header-gradient-4) 82.85%,var(--premium-tier-0-header-gradient-5) 90.53%); - border-radius: 16px; - height: 150px; - padding: 15px; - pointer-events: none; - transform: translateX(1px); - width: 270px; - z-index: 10 -} - -.full-motion .carouselCardImage_b5493b { - transition: all .1s ease-in-out -} - -.carouselCardImageGrayscale_b5493b { - background: gray; - border-radius: 16px; - filter: grayscale(1%); - height: 180px; - pointer-events: none; - transform: translateX(1px); - width: 300px; - z-index: 10 -} - -.full-motion .carouselCardImageGrayscale_b5493b { - transition: all .1s ease-in-out -} - -@media screen and (max-width: 1330px) { - .carouselCardImageGrayscale_b5493b { - height:150px; - width: 230px - } -} - -.imageOverlayTextContainer_b5493b { - display: flex; - justify-content: center; - position: absolute; - top: 140px; - width: 100% -} - -@media screen and (max-width: 1330px) { - .imageOverlayTextContainer_b5493b { - top:120px - } -} - -.imageOverlayText_b5493b { - background: #313338; - border-radius: 20px; - color: var(--white); - display: inline-block; - font-size: 14px; - padding: 0 8px; - z-index: 100 -} - -@media screen and (max-width: 1330px) { - .carouselCardImage_b5493b { - height:120px; - width: 200px - } - - .unavailablePerkPill_b5493b { - top: 117px - } -} - -.cardIllustrationNoHover_b5493b,.cardImage_b5493b { - width: 100%; - z-index: 10 -} - -.full-motion .cardIllustrationNoHover_b5493b,.full-motion .cardImage_b5493b { - transition: all .1s ease-in-out -} - -.cardImage_b5493b { - border-radius: 14px; - pointer-events: none -} - -.cardImage_b5493b img { - border-radius: inherit; - width: 100% -} - -.cardIllustrationNoHover_b5493b { - display: flex; - flex-direction: column; - height: 100%; - justify-content: flex-end -} - -.cardDescription_b5493b { - align-items: flex-start; - border-radius: 20px; - display: flex; - flex-direction: column; - inset: 0; - justify-content: flex-start; - opacity: 0; - padding: 24px; - position: absolute; - z-index: 20 -} - -.full-motion .cardDescription_b5493b { - transition: all .1s ease-in-out -} - -.cardDescription_b5493b p { - margin: 0 -} - -.cardDescription_b5493b p+p { - margin-top: 8px -} - -.cardDescription_b5493b button { - inset-inline: 24px; - bottom: 8px; - opacity: .5; - position: absolute; - transform: scale(.75) -} - -.full-motion .cardDescription_b5493b button { - transition: all .1s ease-in-out -} - -.reducedMotion_b5493b .cardDescription_b5493b { - transition: none -} - -.cover_b5493b { - border-radius: 20px; - inset: 0; - opacity: 0; - pointer-events: none; - position: absolute -} - -.full-motion .cover_b5493b { - transition: all .1s ease-in-out -} - -.reducedMotion_b5493b .cover_b5493b { - transition: none -} - -.above_b5493b { - background-color: rgba(0,0,0,.6); - z-index: 30 -} - -.below_b5493b { - background-image: linear-gradient(to bottom,hsl(var(--primary-660-hsl)/.5) 0,transparent 60%); - z-index: 1 -} - -.theme-light .below_b5493b { - background-image: linear-gradient(to bottom,hsl(var(--primary-160-hsl)/.5) 0,hsla(0,0%,100%,0) 60%) -} - -.topCover_b5493b .below_b5493b { - opacity: 1 -} - -.purpleGradient_b5493b { - background-image: linear-gradient(130.41deg,#727eff 7.43%,#b940ff 82.67%) -} - -.pinkGradient_b5493b { - background-image: linear-gradient(130.41deg,#ff52d2 7.43%,#b940ff 82.67%) -} - -.blueGradient_b5493b { - background-image: linear-gradient(130.41deg,#0fb3ff 7.43%,#b940ff 82.67%) -} - -.relative_b5493b { - position: relative -} - -.nitroGradientBackground_b5493b { - background: radial-gradient(100% 100% at 50% 0,var(--card-background-default) 60%,var(--transparent) 100%),linear-gradient(90deg,var(--expressive-gradient-green-end) 0,var(--expressive-gradient-green-start) 100%) -} - -.section__3af27 { - display: flex; - flex-direction: column; - justify-content: center; - max-width: 1066px; - width: calc(100% - 32px) -} - -.heading__3af27 { - margin: 0 24px 16px; - text-align: center -} - -.sectionHeader__3af27 { - display: flex; - justify-content: space-between; - width: 100% -} - -.sectionHeaderSeeAll__3af27 { - flex-grow: 1; - text-align: center -} - -.confettiCanvas__3af27 { - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.container__3af27 { - display: flex -} - -.sectionHeader__3af27 .heading__3af27 { - flex-grow: 1; - text-align: start -} - -.subtitle__3af27 { - margin: 0 24px 24px; - max-width: 544px -} - -.subtitleWithButton__3af27 { - margin: 0 24px 16px; - max-width: 544px -} - -.centerAlignSubtitle__3af27,.leftAlignSection__3af27 { - position: relative -} - -.leftAlignSubtitle__3af27 { - text-align: start -} - -.centerAlignSubtitle__3af27 { - text-align: center -} - -.moreSubtitleMargin__3af27 { - margin-bottom: 32px -} - -.section__3af27.noBackground__3af27 .subtitle__3af27 { - max-width: unset -} - -.fullWidth__3af27 { - max-width: 100% -} - -.cardContainer__3af27,.cardContainerNarrowWidth__3af27 { - display: grid; - gap: 16px; - justify-content: center; - min-height: 380px; - width: 100% -} - -.cardContainer__3af27 { - grid-template-columns: 1fr -} - -.cardContainerNarrowWidth__3af27 { - grid-template-columns: 1fr 1fr -} - -@media (min-width: 960px) { - .cardContainer__3af27 { - grid-template-columns:1fr 1fr 1fr - } -} - -.centerAlignSection__3af27 { - align-items: center -} - -.leftAlignSection__3af27 { - align-items: start -} - -.showAllPerksButton__3af27 { - margin-inline-start:auto;z-index: 9 -} - -.showAllPerksButtonCenter__3af27 { - align-items: center; - margin-bottom: 24px; - z-index: 9 -} - -.footer__3af27 { - margin-bottom: 64px -} - -.container__893f1 { - flex-direction: column; - max-height: 600px -} - -.container__893f1,.containerInner__893f1 { - align-items: center; - display: flex; - justify-content: center; - text-align: center; - width: 100% -} - -.containerInner__893f1 { - flex-direction: row; - height: 100px; - margin-inline-start:-182px;position: absolute; - top: 60px -} - -.content__893f1 { - flex: 1; - min-width: 500px; - position: relative -} - -.backButton__893f1 { - align-items: center; - background-color: var(--background-base-low); - border-radius: 24px; - color: var(--white); - cursor: pointer; - display: flex; - font-family: gg sans; - font-size: 14px; - font-weight: 500; - justify-content: center; - line-height: 18px; - padding-block:4px;padding-inline:16px 20px} - -.backButton__893f1:hover { - background-color: var(--background-base-lower) -} - -.backButtonContainer__893f1 { - margin-inline-end:92px} - -.headerArt__893f1 { - margin-top: -48px; - min-height: 400px; - -o-object-fit: cover; - object-fit: cover; - pointer-events: none; - width: 100% -} - -.theme-light .backButton__893f1 { - background-color: var(--opacity-white-20) -} - -.theme-light .backButton__893f1:hover { - background-color: var(--opacity-white-28) -} - -@media (max-width: 1282px) { - .backButtonContainer__893f1 { - inset-inline-start:10px; - position: absolute; - top: -40px - } - - .containerInner__893f1 { - margin-inline-start:0} -} - -.cardContainer__3f7d1 { - display: grid; - gap: 16px; - grid-template-columns: 1fr; - justify-content: center; - min-height: 385px; - padding-bottom: 20px; - padding-inline-start:4px;position: relative; - width: 100% -} - -.cardInnerContainer__3f7d1 { - align-items: center; - display: flex; - justify-content: start; - margin-inline-start:6px;overflow: hidden; - padding: 5px; - position: relative -} - -.card__3f7d1 { - display: flex; - height: calc(100% - 20px); - max-width: 348px; - padding: 5px; - position: absolute; - transition: width .1s ease-in-out,height .1s ease-in-out,padding .1s ease-in-out -} - -.perkInfo__3f7d1 { - display: flex; - height: 100%; - width: 100% -} - -.inactiveArrow__3f7d1,.leftArrow__3f7d1,.rightArrow__3f7d1 { - align-items: center; - background-color: var(--background-base-low); - border-radius: 100%; - box-shadow: var(--shadow-low); - cursor: pointer; - display: flex; - height: 40px; - justify-content: center; - position: absolute; - top: 50%; - transform: translateY(-50%); - width: 40px; - z-index: 9999 -} - -.leftArrow__3f7d1:hover,.rightArrow__3f7d1:hover { - box-shadow: var(--shadow-low) -} - -.leftArrow__3f7d1:active,.rightArrow__3f7d1:active { - box-shadow: var(--shadow-low-hover) -} - -.rightArrow__3f7d1 { - inset-inline-end: -1px -} - -.leftArrow__3f7d1 { - inset-inline-start: 0 -} - -.cardProgressBar__3f7d1 { - align-items: center; - background-color: var(--background-base-lower); - border-radius: 14px; - bottom: -16px; - display: flex; - gap: 4px; - inset-inline-start: 50%; - justify-content: center; - margin: 5px; - padding: 6px 18px; - position: absolute; - transform: translateX(-50%) -} - -.dot__3f7d1,.selectedDot__3f7d1 { - background-color: var(--background-mod-muted); - border-radius: 50%; - height: 6px; - transition: background-color 1s ease; - width: 6px -} - -.selectedDot__3f7d1 { - background-color: var(--interactive-text-default) -} - -@media screen and (max-width: 1330px) { - .card__3f7d1 { - padding-inline:10px; - width: 280px - } -} - -.arrowIcon__3f7d1 { - height: 32px; - width: 32px -} - -.inactiveArrow__3f7d1 { - box-shadow: var(--shadow-ledge); - cursor: default -} - -.inactiveArrow__3f7d1 .arrowIcon__3f7d1 { - fill: var(--icon-muted) -} - -.theme-dark .leftArrow__3f7d1,.theme-dark .rightArrow__3f7d1 { - background-color: var(--primary-530) -} - -.theme-dark .leftArrow__3f7d1:hover,.theme-dark .rightArrow__3f7d1:hover { - background-color: var(--primary-600) -} - -.theme-dark .leftArrow__3f7d1:active .arrowIcon__3f7d1,.theme-dark .rightArrow__3f7d1:active .arrowIcon__3f7d1 { - fill: var(--white) -} - -.theme-dark .inactiveArrow__3f7d1 { - background-color: var(--primary-530) -} - -.theme-light .leftArrow__3f7d1,.theme-light .rightArrow__3f7d1 { - background-color: var(--white); - fill: var(--primary-500) -} - -.theme-light .leftArrow__3f7d1:active .arrowIcon__3f7d1,.theme-light .rightArrow__3f7d1:active .arrowIcon__3f7d1 { - fill: var(--primary-860) -} - -.theme-light .inactiveArrow__3f7d1 { - background-color: var(--white) -} - -.leftArrow__3f7d1,.rightArrow__3f7d1 { - box-shadow: var(--shadow-low) -} - -.leftArrow__3f7d1 .arrowIcon__3f7d1,.rightArrow__3f7d1 .arrowIcon__3f7d1 { - fill: var(--text-default) -} - -.leftArrow__3f7d1:active,.rightArrow__3f7d1:active { - background-color: var(--background-base-low) -} - -.hammerContainer__01bd8 { - inset-inline-start: 100%; - top: -25px -} - -.hammerContainer__01bd8,.keyContainer__01bd8 { - pointer-events: none; - position: absolute -} - -.keyContainer__01bd8 { - bottom: -50px; - inset-inline-end: 100% -} - -@container premium-marketing-page-container (max-width: 720px) { - .hammerTrinket__01bd8 { - height: 62px - } - - .keyTrinket__01bd8 { - height: 60px - } -} - -.container_fab2fa { - display: grid; - grid-template-columns: 1fr; - grid-template-rows: 1fr; - justify-content: center; - max-width: 1100px; - padding: var(--space-64) var(--space-32) var(--space-64); - text-align: center; - width: 100% -} - -.backButtonContainer_fab2fa { - grid-column: 1; - grid-row: 1; - justify-self: start; - margin-inline-start:var(--space-16);z-index: 1 -} - -.content_fab2fa { - grid-column: 1; - grid-row: 1; - position: relative -} - -.headerContainer_fab2fa { - justify-self: center; - margin: 0 130px; - max-width: 600px; - min-width: -moz-min-content; - min-width: min-content; - position: relative; - width: -moz-fit-content; - width: fit-content -} - -.container__80907 { - --custom-content-width-with-padding: calc(100% - 32px); - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - position: relative; - width: 100% -} - -.responsiveContainer__80907 { - container-name: premium-marketing-page-container; - container-type: inline-size -} - -.hiddenGradient__80907:before { - display: none -} - -.perksDiscoverability__80907,.whatsNew__80907 { - margin-bottom: 64px -} - -.perksCards__80907 { - width: var(--custom-content-width-with-padding) -} - -.manageMembership__80907 { - margin-top: 64px; - min-height: 111px -} - -.giftNitro__80907 { - margin-top: 64px; - width: var(--custom-content-width-with-padding) -} - -.seeAllPerksButton__80907 { - background: var(--background-surface-high); - border-radius: 4px; - box-shadow: 0 12px 36px 0 var(--opacity-black-16); - color: var(--text-default); - font-family: var(--font-primary); - font-size: 14px; - font-weight: var(--font-weight-normal); - padding-block:8px;padding-inline:16px 8px} - -.seeAllPerksButton__80907:hover { - background: linear-gradient(120deg,var(--premium-tier-2-purple-for-gradients) 20%,var(--premium-tier-2-purple-for-gradients-2) 50%,var(--premium-tier-2-pink-for-gradients) 80%) -} - -.seeAllPerksButtonContent__80907 { - align-items: center; - display: flex -} - -.footerSpacing__80907 { - height: 180px -} - -.bottomIllustration__80907 { - bottom: 0; - filter: saturate(var(--saturation-factor,1)); - position: absolute -} - -.heroHeading__80907 { - margin-bottom: 16px; - width: var(--custom-content-width-with-padding) -} - -.heading__80907 { - align-self: start; - margin: 0 32px 16px; - text-align: start -} - -.column__80907 { - display: flex; - flex-direction: column; - margin-top: -128px; - max-width: 1100px; - width: 100% -} - -.column__80907.premiumBrandRefresh__80907 { - margin-top: 0 -} - -.bottomOfPageVisibilitySensor__80907 { - bottom: 0; - height: 300px; - position: absolute -} - -.mainPageScroller__80907 { - background: var(--background-gradient-chat,var(--background-base-low)); - contain: layout size -} - -.custom-client-theme .mainPageScroller__80907 { - background: var(--background-base-low) -} - -.allPerksScroller__80907 { - background: var(--background-gradient-chat,var(--background-base-low)); - contain: layout size; - height: calc(100% - 48px); - inset-inline-start: 100%; - position: absolute; - top: 48px; - transition: left .75s ease-in-out; - width: 100% -} - -.allPerksScroller__80907.open__80907 { - inset-inline-start: 0 -} - -.openCloseReduceMotion__80907 { - transition: none -} - -.cardCarousel__80907 { - display: flex; - flex-direction: column; - justify-content: center; - margin-bottom: 64px; - width: 100% -} - -.theme-light .seeAllPerksButton__80907 { - border: 1px solid var(--border-subtle) -} - -.loading__80907 { - align-items: center; - display: flex; - height: 520px; - justify-content: center -} - -.premiumBrandRefreshGradientBackground__80907 { - position: absolute; - top: 0; - z-index: -1 -} - -.container__2e50f { - border-radius: 20px; - container-name: animated-border-card-container; - container-type: inline-size; - filter: saturate(var(--saturation-factor,1)); - overflow: visible; - position: relative; - width: 100% -} - -.glowAnimation__2e50f { - filter: blur(var(--custom-blur-amount)) saturate(var(--saturation-factor,1)) hue-rotate(var(--custom-hue-rotate,0deg)); - height: calc(100% + var(--custom-glow-amount)*2 + 2px); - inset-inline-start: calc(var(--custom-glow-amount)*-1 - 1px); - pointer-events: none; - position: absolute; - top: calc(var(--custom-glow-amount)*-1 - 1px); - width: calc(100% + var(--custom-glow-amount)*2 + 2px) -} - -.card__2e50f { - border-radius: 20px; - border-width: 0; - display: flex; - flex-direction: column; - overflow: hidden; - position: relative; - width: 100% -} - -@container animated-border-card-container (min-width: 960px) { - .card__2e50f { - flex-direction: row - } -} - -.baseContainer__40d5b { - container-name: premium-marketing-page-container; - container-type: inline-size; - filter: saturate(var(--saturation-factor,1)); - max-width: 1066px; - overflow: visible -} - -.baseContainer__40d5b,.card__40d5b { - border-radius: 20px; - position: relative; - width: 100% -} - -.card__40d5b { - border-width: 0; - display: flex; - flex-direction: column; - overflow: hidden -} - -.video__40d5b { - height: 100%; - inset: 0; - -o-object-fit: cover; - object-fit: cover; - position: absolute; - width: 100% -} - -.betaBadge__40d5b { - margin-bottom: 12px -} - -.progressContainer__40d5b { - width: 75% -} - -.bannerImageContainer__40d5b { - flex: 0 0 auto; - height: 250px; - overflow: visible; - position: relative -} - -.bannerImage__40d5b,.bannerImageContainer__40d5b { - -o-object-position: center; - object-position: center; - width: 100% -} - -.bannerImage__40d5b { - height: 100%; - inset: 0; - -o-object-fit: cover; - object-fit: cover; - position: absolute; - transform: scaleX(-1) -} - -.textContainer__40d5b { - align-items: flex-start; - background-color: var(--card-background-default); - display: flex; - flex: 1 1 100%; - flex-direction: column; - justify-content: center; - padding: var(--space-48) var(--space-24); - position: relative; - z-index: 1 -} - -.heading__40d5b { - margin-bottom: var(--space-12) -} - -.actionButtonContainer__40d5b { - margin-top: var(--space-24) -} - -.totalLifetimeRewardsContainer__40d5b { - align-items: center; - color: var(--text-muted); - display: flex; - gap: var(--space-4); - margin-top: var(--space-12) -} - -.stackedGradientOverlay__40d5b { - background: linear-gradient(to bottom,transparent 0,color-mix(in srgb,var(--card-background-default) 70%,transparent) 50%,var(--card-background-default) 100%); - position: absolute; - top: auto; - inset-inline: 0; - bottom: 0; - height: 100px -} - -@container premium-marketing-page-container (min-width: 720px) { - .card__40d5b { - flex-direction: row; - height: 350px - } - - .bannerImageContainer__40d5b { - height: auto; - margin-inline-end:0;min-width: 0; - width: 50%; - z-index: 0 - } - - .bannerImage__40d5b { - -o-object-position: center; - object-position: center - } - - .textContainer__40d5b { - background-color: transparent; - padding-block:var(--space-48);padding-inline: var(--space-32); - width: 50%; - z-index: auto - } - - .heading__40d5b,.textContainer__40d5b { - color: var(--white) - } - - .bodyText__40d5b,.totalLifetimeRewardsContainer__40d5b { - color: hsl(var(--white-hsl)/.6) - } - - .bodyText__40d5b a { - color: var(--blue-new-27) - } - - .bodyText__40d5b a: hover { - color:var(--blue-new-31) - } - - .stackedGradientOverlay__40d5b { - display: none - } -} - -@container premium-marketing-page-container (min-width: 960px) { - .textContainer__40d5b { - flex: 0 0 40%; - max-width: 40%; - padding-inline:var(--space-24)} - - .bannerImage__40d5b { - -o-object-position: calc(100% + 250px) center; - object-position: calc(100% + 250px) center - } - - .bannerImageContainer__40d5b { - margin-inline-end:0} -} - -.container__72cd3 { - align-items: center; - border-radius: 0 0 var(--radius-sm) var(--radius-sm); - bottom: 0; - display: flex; - inset-inline: 0; - justify-content: space-between; - padding: var(--space-8); - position: absolute -} - -.textSection__72cd3 { - display: flex; - flex-direction: column; - gap: 2px; - justify-content: center; - margin-inline-start:var(--space-16);max-width: 57% -} - -.avatar__72cd3 { - bottom: 8px; - inset-inline-end: 8px; - position: absolute; - width: 120px -} - -.full-motion .avatar__72cd3 { - bottom: -4px; - inset-inline-end: -4px; - width: 136px -} - -.pricePerInterval_fd0764 { - font-weight: 200; - margin-bottom: 18px -} - -.price_fd0764 { - font-weight: var(--font-weight-bold) -} - -.priceSpinner_fd0764 { - display: flex; - height: 22px; - justify-content: flex-start; - margin-bottom: 18px -} - -.annualDiscountString_fd0764 { - display: flex; - flex-direction: column -} - -.firstPromotionalAvatar__55414 { - z-index: 1 -} - -.secondPromotionalAvatar__55414 { - margin-inline-end:-18px} - -.container__55414 { - display: flex; - flex-direction: row-reverse -} - -.promotionalGiftMessage__6fbbd { - border-radius: 0 0 16px 16px; - display: flex; - flex-direction: row; - inset-inline-end: 24px; - justify-content: space-between; - min-height: 24px; - padding: 12px 24px; - position: relative; - top: 24px; - width: 100% -} - -.promotionalTextSection__6fbbd { - display: flex; - flex-direction: column; - justify-content: center; - margin-inline-end:12px} - -.avatar__6fbbd { - bottom: -15px; - inset-inline-end: 0; - position: absolute -} - -.trialHeader__784e4 { - margin-bottom: 18px; - max-width: 60% -} - -.price__784e4 { - margin: 0 0 8px -} - -.price__784e4:last-of-type { - margin: 0 0 18px -} - -.container__33718 { - align-items: center; - background: linear-gradient(90deg,hsla(0,0%,100%,0),hsla(0,0%,100%,.1) 50%,hsla(0,0%,100%,0)); - display: flex; - height: 36px -} - -.text__33718 { - margin-inline-start:6px} - -.textWithAD__33718 { - margin-inline-start:9px} - -.premiumCards__6df1a { - display: flex; - gap: var(--space-32); - width: 100% -} - -.premiumCardHover__6df1a { - transition: transform .4s linear(0,.372 7.6%,.658 15.6%,.866 24.2%,.942 28.8%,1.002 33.7%,1.036 37.8%,1.061 42.2%,1.075 46.9%,1.08 52%,1.069 61%,1.011 85%,1) -} - -.premiumCardHover__6df1a:hover { - position: relative; - transform: scale(1.0975) translateZ(0); - z-index: 1 -} - -.tier2ApplicationHomeSubheader__6df1a { - margin-bottom: var(--space-16) -} - -.applicationHomeCard__6df1a { - border-radius: var(--radius-lg)!important; - box-sizing: border-box; - height: 470px; - margin: 0 auto; - width: 390px -} - -.applicationHomeCard__6df1a.narrow__6df1a { - width: 354px -} - -.card__6df1a { - background: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - display: flex; - flex-direction: column; - padding: var(--space-24); - position: relative; - text-align: start -} - -.card__6df1a.withGiftBanner__6df1a { - padding-bottom: 90px -} - -.card__6df1a.borderGradient__6df1a { - background: var(--background-surface-high); - border: none; - position: relative -} - -.card__6df1a.borderGradient__6df1a:after { - background: linear-gradient(to bottom,var(--illo-pink-50) 0,var(--illo-nitro-blue) 100%); - border-radius: calc(var(--radius-sm) + 1px); - content: ""; - inset: -1px; - opacity: .8; - position: absolute; - z-index: -1 -} - -.card__6df1a.borderGradient__6df1a.applicationHomeCard__6df1a:after { - border-radius: var(--radius-lg) -} - -.tier0__6df1a.pillMargin__6df1a { - margin-top: var(--space-24) -} - -.wordmark__6df1a { - margin-bottom: var(--space-8); - min-height: 25px; - width: -moz-fit-content; - width: fit-content -} - -.wumpus__6df1a { - position: absolute; - z-index: 1 -} - -.wumpus__6df1a.inCard__6df1a { - inset-inline-end: 24px; - top: 30px -} - -.wumpus__6df1a.outerCorner__6df1a { - height: 165px; - inset-inline-end: -33px; - top: -40px -} - -.wumpus__6df1a.insideCorner__6df1a { - inset-inline-end: 10px; - top: -20px -} - -.bigCloud__6df1a { - opacity: .62; - position: absolute; - width: 86px -} - -.bigCloud__6df1a.inCard__6df1a { - inset-inline-end: 8px; - top: 12px -} - -.bigCloud__6df1a.insideCorner__6df1a,.bigCloud__6df1a.outerCorner__6df1a { - inset-inline-end: 140px; - top: 105px; - width: 40px -} - -.smallCloud__6df1a { - opacity: .42; - position: absolute; - width: 47px -} - -.smallCloud__6df1a.inCard__6df1a { - inset-inline-end: 172px; - top: 40px -} - -.smallCloud__6df1a.insideCorner__6df1a,.smallCloud__6df1a.outerCorner__6df1a { - inset-inline-end: 172px; - top: 65px -} - -.divider__6df1a { - margin: var(--space-16) 0; - opacity: .1; - z-index: 0 -} - -.firstFeatureItemContainer__6df1a { - max-width: 250px -} - -.featureItem__6df1a { - align-items: center; - color: var(--text-default); - display: flex; - flex-direction: row; - margin-bottom: var(--space-8) -} - -.featureItem__6df1a:last-child { - margin-bottom: 0 -} - -.featureItemApplicationHome__6df1a { - margin-bottom: var(--space-12) -} - -.promotionalBackgroundImage__6df1a { - inset-inline-end: 0; - position: absolute; - top: 0; - transform: scale(50%); - transform-origin: top right -} - -.priceHeader__6df1a { - margin-top: var(--space-4) -} - -.pill__6df1a { - inset-inline-start: 50%; - position: absolute; - top: 1px; - transform: translate(-50%,-50%) -} - -.CTAButton__6df1a { - display: flex; - margin-top: auto; - padding-top: 0; - width: 100% -} - -.premiumCardsContainer__6df1a { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center -} - -.premiumCardsHeader__6df1a { - font-style: italic; - margin-bottom: var(--space-64); - text-align: center; - text-transform: uppercase -} - -.tier0CardOrder__6df1a { - order: 0 -} - -.tier2CardOrder__6df1a { - order: 1 -} - -.premiumGroupCardOrder__6df1a { - order: 2 -} - -@container premium-marketing-page-container (max-width: 960px) { - .premiumCards__6df1a { - flex-direction: column - } - - .tier0CardOrder__6df1a { - order: 1 - } - - .tier2CardOrder__6df1a { - order: 0 - } -} - -.cardTitle__6df1a { - font-size: 32px; - font-style: italic; - line-height: 27px; - text-transform: uppercase -} - -.betaPill__6df1a,.cardTitle__6df1a { - margin-bottom: 8px -} - -.cardHeader__6df1a { - align-items: center; - display: flex; - flex-direction: row; - justify-content: space-between -} - -.boxBackdrop_b0db00 { - align-items: center; - display: flex; - flex-direction: column; - width: 100% -} - -.bentoSectionHeader_b0db00 { - font-style: italic; - margin-bottom: var(--space-32); - text-transform: uppercase -} - -.bentoBoxesGrid_b0db00 { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - max-width: 1105px; - row-gap: var(--space-32) -} - -.bentoBoxButton_b0db00 { - margin-top: var(--space-24) -} - -.backgroundColor_b0db00 { - background: var(--background-surface-high) -} - -.description_b0db00 { - display: inline; - flex-direction: column; - gap: var(--space-12); - margin-top: var(--space-12) -} - -.badgeContainer_b0db00 { - margin-bottom: var(--space-8) -} - -.badge_b0db00 { - align-items: center; - background: linear-gradient(90deg,#db00a4,#5968f0); - border-radius: 19px; - justify-content: center; - padding: 0 var(--space-4); - text-transform: uppercase; - width: -moz-fit-content; - width: fit-content -} - -.boxVideo_b0db00 { - height: 100%; - -o-object-fit: contain; - object-fit: contain; - width: 100% -} - -.header_b0db00 { - -webkit-hyphens: auto; - hyphens: auto; - word-break: break-word -} - -.boxArtContainer_b0db00.large_b0db00 { - align-content: right; - order: 0; - position: relative; - width: 100% -} - -.boxArtContainer_b0db00.medium_b0db00 { - height: 288px; - order: 1; - position: relative -} - -.boxArtContainer_b0db00.small_b0db00 { - align-items: center; - display: flex; - flex-direction: column; - height: 348px; - justify-content: flex-end; - order: 1; - overflow: hidden; - position: relative -} - -.boxContainer_b0db00.large_b0db00 { - border-radius: var(--radius-lg); - display: flex; - flex-direction: row; - height: 400px; - padding: var(--space-12); - width: 100% -} - -.boxContainer_b0db00.large_b0db00.overlayMode_b0db00 { - overflow: hidden; - padding: 0 -} - -.boxContainer_b0db00.medium_b0db00,.boxContainer_b0db00.small_b0db00 { - border-radius: var(--radius-lg); - box-sizing: border-box; - display: flex; - flex-direction: column; - padding: var(--space-12) var(--space-12) var(--space-24) -} - -.boxContainer_b0db00.medium_b0db00 { - width: calc(50% - 16px) -} - -.boxContainer_b0db00.small_b0db00 { - width: calc(33.33333% - 21.33333px) -} - -.boxContainer_b0db00.overlayImageMode_b0db00 { - padding: 0 0 var(--space-24) -} - -.boxContainer_b0db00.overlayImageMode_b0db00 .boxArtContainer_b0db00 { - background-color: #000; - border-radius: var(--radius-lg) var(--radius-lg) 0 0; - overflow: hidden -} - -.boxContainer_b0db00.overlayImageMode_b0db00 .boxVideo_b0db00 { - -o-object-fit: cover; - object-fit: cover -} - -.boxContainer_b0db00.large_b0db00.overlayImageMode_b0db00 { - overflow: hidden; - padding: 0 -} - -.textBox_b0db00.large_b0db00 { - display: flex; - flex: 1; - flex-direction: column; - justify-content: center; - min-width: 334px; - order: 0; - padding: 0 var(--space-24) -} - -.textBox_b0db00.medium_b0db00,.textBox_b0db00.small_b0db00 { - display: flex; - flex-direction: column; - justify-content: center; - order: 2 -} - -.textBox_b0db00.medium_b0db00 { - margin: var(--space-24) var(--space-32) var(--space-32) -} - -.textBox_b0db00.small_b0db00 { - margin-top: var(--space-12) -} - -.gradientBackground_b0db00:before { - opacity: .3 -} - -.overlayTextBox_b0db00 { - inset-inline-start: var(--space-12); - z-index: 1 -} - -.backgroundVideoContainer_b0db00 { - border-radius: var(--radius-lg); - overflow: hidden; - z-index: 0 -} - -.backgroundVideo_b0db00,.backgroundVideoContainer_b0db00 { - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.backgroundVideo_b0db00,.overlayImage_b0db00 { - -o-object-fit: cover; - object-fit: cover -} - -.overlayImage_b0db00 { - height: 100%; - -o-object-position: 40% center; - object-position: 40% center; - width: 100% -} - -@container premium-marketing-page-container (max-width: 960px) { - .boxArtContainer_b0db00.medium_b0db00 { - height: 225px - } - - .textBox_b0db00.large_b0db00 { - min-width: 280px - } -} - -@container premium-marketing-page-container (max-width: 720px) { - .bentoSectionHeader_b0db00 { - margin-bottom: var(--space-24) - } - - .backgroundVideoContainer_b0db00 { - display: none - } - - .header_b0db00 { - font-size: 24px - } - - .boxContainer_b0db00.large_b0db00,.boxContainer_b0db00.medium_b0db00,.boxContainer_b0db00.small_b0db00 { - border-radius: var(--radius-lg); - box-sizing: border-box; - display: flex; - flex-direction: column; - height: unset; - padding: 0 0 var(--space-24); - width: 100% - } - - .boxArtContainer_b0db00.large_b0db00,.boxArtContainer_b0db00.medium_b0db00,.boxArtContainer_b0db00.small_b0db00 { - height: 360px; - order: 1; - position: relative - } - - .overlayMode_b0db00 .boxArtContainer_b0db00 { - background-color: #000; - border-radius: var(--radius-lg) var(--radius-lg) 0 0; - overflow: hidden - } - - .overlayMode_b0db00 .overlayImage_b0db00 { - -o-object-position: center center; - object-position: center center - } - - .textBox_b0db00.large_b0db00,.textBox_b0db00.small_b0db00 { - display: flex; - flex-direction: column; - justify-content: center; - margin: var(--space-24) var(--space-24) var(--space-32); - order: 2; - padding: unset; - width: unset - } -} - -.boxBackdrop_abac7b { - align-items: center; - display: flex; - flex-direction: column; - width: 100% -} - -.bentoBoxesGrid_abac7b { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - margin: 16px 16px 0; - max-width: 1064px; - row-gap: 16px -} - -.referralProgramBackground_abac7b,.yourStyleBackground_abac7b { - background: linear-gradient(180deg,#000,#19033d) -} - -.customThemesBackground_abac7b,.displayNameStylesBackground_abac7b,.serverProfilesBackground_abac7b,.yourSpaceBackground_abac7b { - background: linear-gradient(180deg,#000,#2a1332) -} - -.noLimitsBackground_abac7b { - background: linear-gradient(180deg,#000,#36266d) -} - -.emojisBackground_abac7b { - background: linear-gradient(180deg,#000,#031a3b) -} - -.theme-light .referralProgramBackground_abac7b,.theme-light .yourStyleBackground_abac7b { - background: linear-gradient(180deg,#f7f7fe 22.95%,#a9b7ff 153.33%) -} - -.theme-light .serverProfilesBackground_abac7b { - background: linear-gradient(180deg,#f7f7fe 50%,#ffc6fd 153.33%) -} - -.theme-light .customThemesBackground_abac7b,.theme-light .displayNameStylesBackground_abac7b,.theme-light .yourSpaceBackground_abac7b { - background: linear-gradient(180deg,#f7f7fe 50%,#ffc6fd 153.33%) -} - -.theme-light .noLimitsBackground_abac7b { - background: linear-gradient(180deg,#f7f7fe 22.95%,#b6b9fc 153.33%) -} - -.theme-light .emojisBackground_abac7b { - background: linear-gradient(180deg,#f7f7fe 22.95%,#b6ddfc 153.33%) -} - -.bentoSideGradient_abac7b { - background: radial-gradient(45% 70% at -22% 50%,#ff70f9 0,transparent 100%),radial-gradient(45% 70% at -22% 50%,#ff903f 0,transparent 100%); - height: 900px; - inset-inline-start: calc(50% - 50vw); - opacity: .4; - pointer-events: none; - position: absolute; - top: calc(100% - 509px); - width: 100vw -} - -.boxVideo_abac7b { - height: 100%; - -o-object-fit: contain; - object-fit: contain; - width: 100% -} - -.elevateProfileVideo_abac7b,.shareNitroVideo_abac7b { - height: 70%; - width: unset -} - -.shareNitroVideo_abac7b { - position: absolute; - top: 50px -} - -.header_abac7b p { - margin: 0 -} - -.description_abac7b { - display: flex; - flex-direction: column; - gap: var(--space-12) -} - -.description_abac7b p { - margin: 0 -} - -.bentoBoxButton_abac7b { - width: -moz-fit-content; - width: fit-content -} - -.boxesSubHeader_abac7b { - margin-bottom: 10px -} - -.bentoSectionHeader_abac7b { - font-style: italic; - text-transform: uppercase -} - -.boxArtContainer_abac7b.large_abac7b { - align-content: end; - flex: 1; - order: 0; - position: relative -} - -.boxArtContainer_abac7b.medium_abac7b { - height: 316px; - order: 1; - position: relative -} - -.boxArtContainer_abac7b.small_abac7b { - align-items: center; - display: flex; - flex-direction: column; - height: 348px; - justify-content: flex-end; - order: 1; - overflow: hidden; - position: relative -} - -.boxContainer_abac7b.large_abac7b { - border-radius: 16px; - display: flex; - flex-direction: row; - width: 100% -} - -.boxContainer_abac7b.medium_abac7b,.boxContainer_abac7b.small_abac7b { - border-radius: 16px; - box-sizing: border-box; - display: flex; - flex-direction: column -} - -.boxContainer_abac7b.medium_abac7b { - padding: 24px; - width: calc(50% - 8px) -} - -.boxContainer_abac7b.small_abac7b { - padding: 0 24px 24px; - width: calc(33.33333% - 10.66667px) -} - -.textBox_abac7b.large_abac7b { - gap: 24px; - margin-inline-end:13px;order: 0; - padding: 24px; - width: 222px -} - -.textBox_abac7b.large_abac7b,.textBox_abac7b.medium_abac7b,.textBox_abac7b.small_abac7b { - display: flex; - flex-direction: column; - justify-content: center -} - -.textBox_abac7b.medium_abac7b,.textBox_abac7b.small_abac7b { - gap: 16px; - margin-top: 16px; - order: 2 -} - -.header_abac7b.large_abac7b { - max-width: 95% -} - -.header_abac7b.large_abac7b,.header_abac7b.medium_abac7b,.header_abac7b.small_abac7b { - -webkit-hyphens: auto; - hyphens: auto; - word-break: break-word -} - -.bentoBoxButtonWhite_abac7b { - border-color: var(--white); - color: var(--white) -} - -.bentoBoxButtonBrand_abac7b { - border-color: var(--control-secondary-border-default); - color: var(--control-secondary-border-default)!important -} - -.bentoBoxButtonBrand_abac7b:hover { - color: var(--white)!important -} - -@container premium-marketing-page-container (max-width: 720px) { - .header_abac7b.large_abac7b,.header_abac7b.medium_abac7b { - font-size: 24px; - font-style: normal; - font-weight: 800; - line-height: 30px - } - - .textBox_abac7b.large_abac7b { - gap: 16px; - width: 121px - } - - .boxArtContainer_abac7b.medium_abac7b { - height: unset - } - - .boxArtContainer_abac7b.small_abac7b { - height: 212px - } -} - -@container premium-marketing-page-container (max-width: 720px) and (min-width: 540px) { - .header_abac7b.small_abac7b { - font-size: 20px; - font-style: normal; - font-weight: 800; - line-height: 24px - } -} - -@container premium-marketing-page-container (max-width: 540px) { - .boxContainer_abac7b.large_abac7b,.boxContainer_abac7b.small_abac7b { - flex-direction: column; - padding: 0 24px 24px; - width: 100% - } - - .textBox_abac7b.large_abac7b { - margin: 16px 0 0; - order: 1; - padding: 0; - width: 100% - } - - .boxArtContainer_abac7b.small_abac7b { - height: 330px; - position: relative - } - - .elevateProfileVideo_abac7b,.shareNitroVideo_abac7b { - height: 100%; - -o-object-fit: contain; - object-fit: contain; - position: relative; - top: 0; - width: 100% - } -} - -.badgeContainer_abac7b { - margin-bottom: var(--space-8) -} - -.badge_abac7b { - align-items: center; - background: linear-gradient(90deg,#db00a4,#5968f0); - border-radius: 19px; - justify-content: center; - padding: 0 var(--space-4); - text-transform: uppercase; - width: -moz-fit-content; - width: fit-content -} - -.wrapper__70f7f { - bottom: 0; - display: flex; - inset-inline-start: 0; - justify-content: center; - opacity: 0; - pointer-events: none; - position: sticky; - width: 100%; - z-index: 10 -} - -.fullscreenWrapper__70f7f { - margin-bottom: 42px -} - -.innerWrapperBase__70f7f { - align-items: center; - background-color: var(--background-surface-higher); - border-radius: var(--radius-md); - box-shadow: var(--shadow-border),var(--shadow-high); - box-sizing: border-box; - display: flex; - justify-content: center; - padding: 16px; - pointer-events: all; - position: relative -} - -.innerWrapper__70f7f { -} - -.innerWrapper__70f7f:after,.innerWrapper__70f7f:before { - border: 2px solid transparent; - border-radius: inherit; - box-sizing: border-box; - content: ""; - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.innerWrapper__70f7f:after { - border-color: var(--guild-boosting-blue); - -webkit-mask: linear-gradient(0deg,#000,transparent); - mask: linear-gradient(0deg,#000,transparent) -} - -.innerWrapper__70f7f:before { - border-color: var(--guild-boosting-purple) -} - -.innerWrapperApplicationHome__70f7f { -} - -.innerWrapperApplicationHome__70f7f:after,.innerWrapperApplicationHome__70f7f:before { - border: 2px solid transparent; - border-radius: inherit; - box-sizing: border-box; - content: ""; - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.innerWrapperApplicationHome__70f7f:after { - border-color: #5968f0; - -webkit-mask: linear-gradient(0deg,#000,transparent); - mask: linear-gradient(0deg,#000,transparent) -} - -.innerWrapperApplicationHome__70f7f:before { - border-color: #db00a4 -} - -.invisible__70f7f>.innerWrapper__70f7f { - pointer-events: none -} - -.theme-light .innerWrapper__70f7f { - background-color: var(--background-base-lower) -} - -.button__70f7f { - border-radius: 4px; - min-width: 160px -} - -.button__70f7f:first-child { - margin-inline-end:16px} - -.subButton__70f7f { - color: var(--brand-500)!important -} - -.tier2Gradient__70f7f { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color); - color: var(--white) -} - -.root__26dae { - display: flex; - flex-direction: column; - max-width: 945px; - width: 100% -} - -.titleText__26dae { - justify-content: center; - margin-bottom: 44px; - text-align: center -} - -.logo__26dae { - width: 100px -} - -.table__26dae { - border-collapse: separate; - border-spacing: 10px 20px; - display: flex; - flex-direction: column; - margin-bottom: 27px; - padding: 0 40px -} - -.headerRow__26dae,.row__26dae { - display: flex; - flex-direction: row; - z-index: 5 -} - -.headerRow__26dae { - height: 40px; - margin-top: 24px -} - -.row__26dae { - min-height: 57px -} - -.rowBottomBorder__26dae { - border-bottom: 1px solid var(--interactive-background-active) -} - -.cell__26dae,.headerCell__26dae { - align-items: center; - display: flex; - flex-direction: column; - width: 170px -} - -.headerCell__26dae { - justify-content: start -} - -.cell__26dae { - justify-content: center -} - -.headerLabelCell__26dae,.labelCell__26dae { - display: flex; - flex: 1; - justify-content: flex-start; - text-align: start -} - -.headerLabelCell__26dae { - align-items: start -} - -.labelCell__26dae { - align-items: center; - margin: 12px 0 -} - -.closeIcon__26dae { - color: var(--icon-muted) -} - -.checkmarkIcon__26dae { - color: var(--interactive-text-active) -} - -.button__26dae { - height: 40px; - width: 180px -} - -.tableWrapper__26dae { - padding: 2px; - position: relative -} - -.backgroundGradient__26dae { - background: linear-gradient(90deg,#db00a4,#5968f0); - -webkit-mask: linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0); - mask: linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0); - -webkit-mask-composite: xor; - mask-composite: exclude; - padding: 2px -} - -.tier0ColumnPosition__26dae { - height: 100%; - inset-inline-end: 208px; - position: absolute; - top: 0 -} - -.tier2ColumnPosition__26dae { - height: 100%; - inset-inline-end: 40px; - position: absolute; - top: 0 -} - -.tier0ColumnOuter__26dae { - border-radius: var(--radius-lg) -} - -.tier0ColumnOuter__26dae,.tier2ColumnOuter__26dae { - height: 100%; - width: 174px; - z-index: 9 -} - -.tier2ColumnOuter__26dae { - border-radius: 16px; - margin-top: -26px -} - -.tier2Pill__26dae { - border-radius: 16px; - inset-inline-end: 0; - position: absolute; - top: 0; - width: 212px; - z-index: 9 -} - -.topBorderRadius__26dae { - border-start-end-radius: 16px; - border-start-start-radius: 16px -} - -.comparisonTablePill__26dae { - background: linear-gradient(90deg,#db00a4,#5968f0); - border-radius: 8px; - margin: auto; - max-height: 24px; - max-width: 80px; - padding: 4px 8px; - position: relative; - top: -12px; - z-index: 10 -} - -.mostPopularText__26dae { - text-align: center; - text-transform: uppercase -} - -.mostPopularText__26dae,.rowLabelText__26dae { - color: var(--white) -} - -.theme-light .rowLabelText__26dae { - color: var(--black) -} - -.basicCheckMark__26dae { - color: #68bdff -} - -.premiumCheckMark__26dae { - color: #fe58d5 -} - -.bottomMargin__26dae,.section__26dae { - margin-bottom: 30px -} - -.jumpingWumpusContainer__82b10 { - inset-inline-start: 50%; - position: absolute; - top: -300px; - transform: translateX(-50%) -} - -.jumpingWumpus__82b10 { - height: 283px -} - -.boltContainer__82b10,.carContainer__82b10,.hammerContainer__82b10,.keyContainer__82b10,.starContainer__82b10 { - position: absolute -} - -.boltContainer__82b10 { - inset-inline-start: 0; - top: 200px -} - -.carContainer__82b10 { - inset-inline-end: 90px; - top: -80px -} - -.hammerContainer__82b10 { - inset-inline-start: -65px; - top: 40px -} - -.keyContainer__82b10 { - inset-inline-end: 50px; - top: 185px -} - -.starContainer__82b10 { - inset-inline-start: 50px; - top: -200px -} - -@container premium-marketing-page-container (max-width: 720px) { - .boltContainer__82b10 { - transform: translateY(75px) - } - - .keyContainer__82b10 { - transform: translateY(125px) - } - - .boltTrinket__82b10,.carTrinket__82b10,.hammerTrinket__82b10,.keyTrinket__82b10,.starTrinket__82b10 { - width: 60px - } -} - -.container__15b9a { - align-items: center; - display: flex; - justify-content: center; - margin-top: 100px; - min-height: 650px; - padding: 0 40px; - position: relative -} - -.contentContainer__15b9a { - max-width: 800px; - min-width: 600px; - position: relative; - text-align: center; - z-index: 1 -} - -.footerHeader__15b9a { - font-size: 72px; - font-style: italic; - font-weight: 900; - letter-spacing: -1.28px; - line-height: 100%; - text-transform: uppercase -} - -@container premium-marketing-page-container (max-width: 960px) { - .container__15b9a { - min-height: 750px - } -} - -.attributionBannerContainer_afe9b4 { - align-items: center; - display: flex; - justify-content: start; - margin-top: 8px; - padding-bottom: 20px; - width: 100% -} - -.premiumBrandRefreshContainer_afe9b4 { - align-items: center; - background: var(--background-mod-normal); - border-radius: var(--radius-md); - display: flex; - padding: var(--space-12) var(--space-16) -} - -.attributionBannerContainerRebranded_afe9b4 { - align-items: center; - background: var(--color-background-mod-normal,hsla(240,4%,61%,.2)); - border: 1px solid var(--border-muted); - border-radius: var(--radius-md); - display: flex; - flex-direction: column; - gap: 100px; - margin: 24px auto; - padding: 12px 16px; - width: 100% -} - -.attributionBannerContentApplicationHomeRebranded_afe9b4 { - align-items: center; - display: flex; - justify-content: start; - width: 100% -} - -.textContainerRebranded_afe9b4 { - color: var(--white); - margin-inline-start:12px;text-align: start -} - -.attributionBannerContent_afe9b4 { - align-items: center; - background: linear-gradient(90deg,rgba(133,71,198,0),rgba(133,71,198,.5) 25%,rgba(184,69,193,.5) 50.24%,rgba(171,93,138,.5) 80%,rgba(171,93,138,0)); - display: flex; - height: 50px; - justify-content: center; - margin-top: 24px; - min-width: 768px; - padding-bottom: 4px; - padding-top: 4px -} - -.iconContainer_afe9b4 { - display: flex; - padding-inline-start:135px} - -.iconContainerApplicationHome_afe9b4 { - display: flex -} - -.icon_afe9b4+.icon_afe9b4 { - margin-inline-start:-8px} - -.textContainer_afe9b4 { - color: var(--white); - margin-inline-start:9px;text-align: start -} - -.attributionIcon_afe9b4 { - margin-inline-start:auto;margin-bottom: -12px -} - -.flyingWumpusContainer_d033b5 { - inset-inline-end: -155px; - pointer-events: none; - position: absolute; - top: 200px; - z-index: 2 -} - -.flyingWumpusAsset_d033b5 { - height: 293px -} - -.boltContainer_d033b5,.carContainer_d033b5,.hammerContainer_d033b5,.keyContainer_d033b5,.starContainer_d033b5 { - position: absolute -} - -.boltContainer_d033b5 { - inset-inline-end: -250px; - top: 115px -} - -.carContainer_d033b5 { - inset-inline-start: 40px; - top: 270px -} - -.hammerContainer_d033b5 { - inset-inline-end: 90px; - top: 60px -} - -.keyContainer_d033b5 { - inset-inline-start: -70px; - top: 350px -} - -.starContainer_d033b5 { - inset-inline-start: -135px; - top: 110px -} - -@container premium-marketing-page-container (max-width: 960px) { - .flyingWumpusContainer_d033b5 { - inset-inline-end: -100px; - top: 170px - } - - .flyingWumpusAsset_d033b5 { - height: 200px - } - - .boltContainer_d033b5 { - inset-inline-end: -100px; - top: 325px - } - - .carContainer_d033b5,.keyContainer_d033b5,.starContainer_d033b5 { - transform: translateX(30px) translateY(-50px) - } -} - -@container premium-marketing-page-container (max-width: 720px) { - .flyingWumpusContainer_d033b5 { - inset-inline-end: -80px; - top: 180px - } - - .flyingWumpusAsset_d033b5 { - height: 170px - } - - .keyContainer_d033b5,.starContainer_d033b5 { - transform: translateX(75px) translateY(-50px) - } - - .carContainer_d033b5 { - transform: translateX(-75px) translateY(-50px) - } - - .boltTrinket_d033b5,.carTrinket_d033b5,.hammerTrinket_d033b5,.keyTrinket_d033b5,.starTrinket_d033b5 { - width: 60px - } -} - -@container premium-marketing-page-container (max-width: 540px) { - .flyingWumpusAsset_d033b5 { - height: 140px - } -} - -:root { - --custom-premium-marketing-hero-heading-padding-top: 120px -} - -.container__3849c { - justify-content: center; - text-align: center -} - -.container__3849c,.contentContainer__3849c { - align-items: center; - display: flex; - position: relative -} - -.contentContainer__3849c { - flex-direction: column; - gap: var(--space-24); - padding-top: var(--custom-premium-marketing-hero-heading-padding-top); - z-index: 1 -} - -.containerWithOfferCountdown__3849c .contentContainer__3849c { - padding-top: 80px -} - -.body__3849c { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-24); - margin: 0 80px -} - -.marketingPageTextContainer__3849c { - max-width: 800px; - min-width: -moz-min-content; - min-width: min-content; - width: -moz-fit-content; - width: fit-content -} - -@container premium-marketing-page-container (max-width: 960px) { - .marketingPageTextContainer__3849c { - max-width: 650px - } -} - -@container premium-marketing-page-container (max-width: 720px) { - .marketingPageTextContainer__3849c { - max-width: 500px - } -} - -.descriptionContainer__3849c { - max-width: 400px -} - -@container premium-marketing-page-container (max-width: 720px) { - .descriptionContainer__3849c { - max-width: 250px - } -} - -.marketingPageHeading__3849c { - font-size: 64px; - font-style: italic; - font-weight: 900; - letter-spacing: -1.28px; - line-height: 82%; - text-transform: uppercase -} - -.twoButtonContainer__3849c { - align-items: center; - display: flex; - gap: 16px; - justify-content: center -} - -.singleButtonContainer__3849c { - margin: auto; - min-width: 260px -} - -.offerPillContainer__3849c { - inset-inline-start: 50%; - position: absolute; - top: calc(var(--custom-premium-marketing-hero-heading-padding-top) - 40px); - transform: translateX(-50%) -} - -.affinitiesContainer__3849c,.referrerAttributionContainer__3849c { - margin: 0 auto -} - -.header__3849c { - font-size: 58px; - font-style: italic; - font-weight: 900; - letter-spacing: -1.16px; - line-height: 100%; - text-transform: uppercase -} - -@container premium-marketing-page-container (max-width: 960px) { - .header__3849c { - font-size: 48px; - letter-spacing: -.96px - } -} - -@container premium-marketing-page-container (max-width: 720px) { - .header__3849c { - font-size: 38px; - letter-spacing: -.76px - } -} - -.heroOfferCountdown__3849c { - margin-bottom: -12px -} - -.wrapper_d38e00 { - bottom: 0; - display: flex; - justify-content: center; - position: sticky; - z-index: 1 -} - -.buttonContainer_d38e00 { - background-color: var(--background-surface-highest); - border-radius: var(--radius-md); - box-shadow: var(--shadow-low); - display: flex; - flex-direction: row; - gap: var(--space-12); - padding: var(--space-12) -} - -.root_cbfae2 { - display: flex; - flex-direction: column; - margin: 0 var(--space-24); - width: 1080px -} - -.titleText_cbfae2 { - font-style: italic; - justify-content: center; - margin-bottom: var(--space-64); - text-align: center; - text-transform: uppercase -} - -.planTitleWithPrice_cbfae2 { - display: flex; - flex-direction: column; - z-index: 1 -} - -.planTitle_cbfae2 { - align-items: center; - display: flex; - gap: var(--space-4); - justify-content: center; - margin-bottom: var(--space-4) -} - -.table_cbfae2 { - display: flex; - flex-direction: column -} - -.headerRow_cbfae2,.row_cbfae2 { - display: flex; - flex-direction: row -} - -.headerRow_cbfae2 { - border-bottom: 1px solid var(--border-strong); - padding-bottom: var(--space-16) -} - -.cell_cbfae2 { - align-items: center; - display: flex; - flex-direction: column; - width: 180px -} - -.headerLabelCell_cbfae2,.labelCell_cbfae2 { - display: flex; - flex: 1; - justify-content: flex-start; - text-align: start -} - -.sectionTitle_cbfae2 { - margin-top: 42px -} - -.planComparisonItem_cbfae2 { - border-bottom: 1px solid var(--border-strong); - padding: var(--space-24) 0 -} - -.tableWrapper_cbfae2 { - position: relative -} - -.backgroundGradient_cbfae2 { - background: linear-gradient(180deg,var(--pink-49) 0,var(--premium-tier-0-purple) 100%); - -webkit-mask: linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0); - mask: linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0); - -webkit-mask-composite: xor; - mask-composite: exclude; - padding: 2px -} - -.gradientColumn_cbfae2 { - height: calc(100% + 28px); - position: absolute; - top: -31px; - width: 180px; - z-index: 1 -} - -.gradientColumn_cbfae2.tier0_cbfae2 { - inset-inline-end: 180px -} - -.gradientColumn_cbfae2.tier2_cbfae2 { - inset-inline-end: 0 -} - -.gradientColumnBorder_cbfae2 { - border-radius: var(--radius-md); - height: 100%; - inset-inline-start: -2px; - position: absolute; - top: 0; - width: 100% -} - -.pill_cbfae2 { - inset-inline-start: 50%; - position: absolute; - top: 1px; - transform: translate(-50%,-50%); - z-index: 2 -} - -.pill_cbfae2.pillGradient_cbfae2 { - background: linear-gradient(90deg,rgba(155,29,165,.84) 0,rgba(30,35,83,.84) 120%),var(--background-base-lower) -} - -.gradientBackgroundContainer_cbfae2 { - border-radius: calc(var(--radius-md) - 2px); - height: 100%; - overflow: hidden; - position: absolute; - top: 0; - width: 100%; - z-index: -1 -} - -.gradientBackgroundPosition_cbfae2 { - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.nitroWheelIconColor_cbfae2 { - fill: var(--icon-strong) -} - -.heroHeadingContainer__81281 { - margin-top: 30px; - margin-inline-start:30px} - -.container__81281 { - align-items: center; - display: flex; - justify-content: center; - position: relative; - text-align: center -} - -.settingsContainer__81281 { - background: linear-gradient(192.85deg,var(--premium-tier-0-header-gradient-1) 42.31%,var(--premium-tier-0-header-gradient-2) 72.74%,var(--premium-tier-0-header-gradient-3) 86.45%,var(--premium-tier-0-header-gradient-4) 98.78%,var(--premium-tier-0-header-gradient-5) 123.01%),linear-gradient(180deg,var(--primary-700) 26.55%,var(--primary-660) 71.14%,var(--primary-630) 100%); - border-radius: 24px; - height: 330px; - margin-bottom: 24px; - width: 100% -} - -.header__81281 { - font-size: 44px -} - -@media (max-width: 1100px) { - .header__81281 { - font-size:34px - } -} - -.affinityHeight__81281 { - height: 395px -} - -.fullscreenTextContainer__81281 { - max-width: 650px -} - -.settingsTextContainer__81281 { - max-width: 500px -} - -.description__81281 { - margin-top: 24px -} - -.descriptionV2__81281 { - display: flex; - justify-content: start -} - -.descriptionBottomMargin__81281 { - margin-bottom: 32px -} - -.affinityDescription__81281,.affinityDescriptionNoMargin__81281 { - margin-top: 22px -} - -.affinityDescription__81281 { - margin-bottom: 22px -} - -.affinityDescriptionContainer__81281 { - margin-bottom: 10px; - margin-top: 10px -} - -.buttonContainer__81281 { - align-items: center; - display: flex; - gap: 16px; - justify-content: center; - width: 100% -} - -.button__81281 { - height: 40px; - min-width: 160px -} - -.extendedButton__81281 { - max-width: 339px; - width: 100% -} - -.sparkleStar__81281 { - color: var(--white); - position: absolute -} - -.settingsSparkleStar1__81281 { - height: 36px; - inset-inline-start: 76px; - top: 42px; - width: 18px -} - -.settingsSparkleStar2__81281 { - height: 24px; - inset-inline-start: 31px; - top: 80px; - width: 12px -} - -.settingsSparkleStar3__81281 { - bottom: 78px; - height: 32px; - inset-inline-end: 94px; - width: 16px -} - -.settingsSparkleStar4__81281 { - bottom: 38px; - height: 24px; - inset-inline-end: 54px; - width: 12px -} - -.fullscreenSparkleStar1__81281 { - height: 32px; - inset-inline-start: -60px; - top: 60px; - width: 16px -} - -.fullscreenSparkleStar2__81281 { - height: 32px; - inset-inline-start: -12px; - top: 98px; - width: 16px -} - -.fullscreenSparkleStar3__81281 { - bottom: -24px; - height: 32px; - inset-inline-end: 20px; - width: 16px -} - -.fullscreenSparkleStar4__81281 { - bottom: 16px; - height: 48px; - inset-inline-end: -36px; - width: 24px -} - -.backgroundColor__81281 { - margin-top: 40px -} - -.theme-light .backgroundColor__81281 { - background: linear-gradient(279deg,hsl(var(--black-hsl)/0) 0,var(--opacity-black-8) 50.8%,hsl(var(--black-hsl)/0) 100%) -} - -.marketingPageCTAContainer__81281 { - display: flex; - justify-content: start -} - -.marketingPageCTAMargin__81281 { - margin-top: 16px -} - -.marketingPageTextContainer__81281 { - max-width: 447px -} - -.marketingPageHeading__81281 { - font-size: 30px; - font-weight: 800; - line-height: 32px; - text-align: start -} - -@media (min-width: 972px) and (max-width:1150px) { - .marketingPageHeading__81281 { - font-size:34px; - font-weight: 800; - line-height: 36px - } -} - -@media (min-width: 1150px) { - .marketingPageHeading__81281 { - font-size:44px; - font-weight: 800; - line-height: 42px - } -} - -.premiumOfferPill__81281 { - align-items: center; - background: linear-gradient(90deg,#db00a4,#5968f0); - border-radius: 20px; - display: flex; - margin-bottom: 12px; - padding: 0 4px; - position: relative; - text-transform: uppercase; - width: -moz-fit-content; - width: fit-content -} - -.subButton__81281 { - color: hsl(235,calc(var(--saturation-factor, 1)*85.6%),64.7%)!important -} - -.whiteSubButton__81281 { - color: var(--white)!important -} - -.tier2Gradient__81281 { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color); - color: var(--white) -} - -.heroHeadingV2Art__81281 { - height: 100%; - -o-object-fit: contain; - object-fit: contain; - width: 100% -} - -.heroHeadingV2ArtContainer__81281 { - align-content: center; - display: flex; - justify-content: end; - margin-inline-start:50px;max-width: 500px; - width: 100% -} - -.container__14cf2 { - display: flex; - flex-direction: column; - position: relative; - text-align: center -} - -.footerHeader__14cf2 { - font-style: italic; - margin-bottom: 36px; - text-transform: uppercase -} - -.footerCTAContainer__14cf2 { - margin-bottom: 30px -} - -.footerArtContainer__14cf2 { - display: flex; - width: 100% -} - -.footerArt__14cf2 { - height: 100%; - -o-object-fit: contain; - object-fit: contain; - width: 100% -} - -@media (max-width: 975px) { - .footerHeader__14cf2 { - font-size:24px; - font-weight: 800; - line-height: 28px - } -} - -.leftGradient__14cf2 { - background: radial-gradient(45% 75% at 0 100%,#ff4cd2 0,transparent 100%); - inset-inline-start: calc(50% - 50vw); - opacity: .23 -} - -.leftGradient__14cf2,.rightGradient__14cf2 { - height: 100%; - pointer-events: none; - position: absolute; - width: 100vw -} - -.rightGradient__14cf2 { - background: radial-gradient(45% 75% at 90% 100%,#2d01dc 0,transparent 100%),radial-gradient(45% 75% at 90% 100%,#b182ff 0,transparent 100%); - opacity: .4 -} - -.navBar__88ef1 { - -webkit-backdrop-filter: blur(11.5px); - backdrop-filter: blur(11.5px); - border-bottom: 1px solid var(--color-border-strong,hsla(240,4%,61%,.44)); - margin-bottom: calc(var(--custom-channel-header-height)*-1); - padding: 0; - position: sticky; - top: 0 -} - -.nitroWheelIcon__88ef1 { - margin-top: 3px; - margin-inline-end:var(--space-24)} - -.nitroWheelIconColor__88ef1 { - fill: var(--icon-strong) -} - -.navBarContent__88ef1 { - justify-content: space-between; - padding: 0 var(--space-16); - width: 100% -} - -.navBarContent__88ef1,.navBarSectionContentContainer__88ef1 { - align-items: center; - display: flex; - height: 100% -} - -.navBarSectionContent__88ef1 { - display: flex; - gap: 20px; - height: 100%; - margin-inline-end:var(--space-16)} - -.sectionClickable__88ef1 { - align-items: center; - cursor: pointer; - display: flex; - height: 100%; - position: relative; - width: -moz-max-content; - width: max-content -} - -.sectionUnderline__88ef1 { - background-color: var(--text-default); - bottom: -.5px; - height: 2px; - position: absolute; - width: 100%; - z-index: 1 -} - -.sectionUnderline__88ef1.fadeIn__88ef1 { - animation: underlineAppear__88ef1 .3s ease-out -} - -@keyframes underlineAppear__88ef1 { - 0% { - transform: scaleX(0) - } - - to { - transform: scaleX(1) - } -} - -.container_f48ee4 { - align-items: center; - display: flex; - flex: 1; - flex-direction: column; - margin: 0 auto; - position: relative -} - -.fadeInFromTop_f48ee4 { - animation: fadeInFromTop_f48ee4 .6s ease-out forwards; - will-change: opacity,transform -} - -@keyframes fadeInFromTop_f48ee4 { - 0% { - opacity: 0; - transform: translateY(-20px) - } - - to { - opacity: 1; - transform: translateY(0) - } -} - -.containerBackground_f48ee4 { - background: var(--background-base-lowest) -} - -.responsiveContainer_f48ee4 { - container-name: premium-marketing-page-container; - container-type: inline-size -} - -.sectionsContainer_f48ee4 { - align-items: center; - box-sizing: border-box; - display: flex; - flex-direction: column; - gap: 164px; - padding: 0 40px; - width: 100% -} - -.bestOfNitroSectionContainer_f48ee4,.heroHeadingContainer_f48ee4,.planComparisonTableContainer_f48ee4,.premiumTierCardsContainer_f48ee4,.promoBannerContainer_f48ee4,.whatsNewSectionContainer_f48ee4 { - display: flex; - justify-content: center; - position: relative; - width: 100% -} - -.promoBannerContainer_f48ee4 { - max-width: 1052px; - z-index: 1 -} - -.bottomOfPageVisibilitySensor_f48ee4 { - bottom: 0; - height: 300px; - position: absolute -} - -.scroller_f48ee4 { - background-color: var(--background-base-low); - contain: layout size -} - -.topOfPageGradient_f48ee4 { - position: absolute; - top: 0 -} - -.topOfPageGradientWithCountdown_f48ee4 { - opacity: .75 -} - -.bottomOfPageGradient_f48ee4 { - bottom: 0; - position: absolute; - transform: rotate(180deg) -} - -.container__59141 { - align-items: center; - display: flex; - flex: 1; - flex-direction: column; - margin: 0 auto; - position: relative -} - -.containerBackground__59141 { - background: linear-gradient(180deg,#04050c,#19033d) -} - -.responsiveContainer__59141 { - container-name: premium-marketing-page-container; - container-type: inline-size -} - -.sectionsContainer__59141 { - align-items: center; - flex-direction: column -} - -.heroHeadingContainer__59141,.sectionsContainer__59141 { - display: flex; - justify-content: center; - width: 100% -} - -.heroHeadingContainer__59141 { - flex-direction: row; - position: relative -} - -.promoBannerContainer__59141 { - justify-content: center; - max-width: 1052px; - position: relative; - width: 100%; - z-index: 1 -} - -.whatsNewSectionContainer__59141 { - margin-top: 80px -} - -.bestOfNitroSectionContainer__59141,.whatsNewSectionContainer__59141 { - margin-bottom: 100px; - position: relative -} - -.premiumTierCardsContainer__59141 { - margin-bottom: 80px; - max-width: 824px; - width: 100% -} - -.planComparisonTableContainer__59141 { - align-items: center; - display: flex; - justify-content: center; - position: relative; - width: 100% -} - -.bottomOfPageVisibilitySensor__59141 { - bottom: 0; - height: 300px; - position: absolute -} - -.scroller__59141 { - background-color: var(--background-base-low); - contain: layout size -} - -.heroHeaderSideGradient__59141 { - background: radial-gradient(45% 60% at -30% 40%,#70cbff 0,transparent 100%),radial-gradient(45% 60% at -30% 40%,#172365 0,transparent 100%); - height: 800px; - inset-inline-start: 0; - opacity: .4; - pointer-events: none; - position: absolute; - width: 100vw -} - -.heroHeaderBackgroundStars__59141 { - inset-inline-start: 30px; - position: absolute; - top: -40px; - width: 100% -} - -.container__3f0b7 { - align-items: flex-start; - background: linear-gradient(90deg,rgba(180,115,245,.1),rgba(226,146,170,.1)); - border: 2px solid var(--redesign-button-premium-primary-purple-for-gradient-2); - border-radius: 16px; - display: flex; - flex-direction: column; - padding: 32px -} - -.header__3f0b7 { - margin-bottom: var(--space-4) -} - -.wordmark__3f0b7 { - font-size: 24px; - font-style: italic; - font-weight: 900; - text-transform: uppercase -} - -.subheader__3f0b7 { - margin-bottom: var(--space-16) -} - -.root_e4ef5c { - display: flex; - flex-direction: column -} - -.titleText_e4ef5c { - justify-content: center; - margin-bottom: 44px; - text-align: center -} - -.logo_e4ef5c { - width: 100px -} - -.table_e4ef5c { - display: flex; - flex-direction: column -} - -.headerRow_e4ef5c,.row_e4ef5c { - display: flex; - flex-direction: row; - z-index: 5 -} - -.headerRow_e4ef5c { - height: 56px; - margin-top: 24px -} - -.row_e4ef5c { - height: 68px -} - -.rowBottomBorder_e4ef5c { - border-bottom: 1px solid var(--border-muted) -} - -.wideRow_e4ef5c { - height: 128px -} - -.shortRow_e4ef5c { - height: 20px -} - -.cell_e4ef5c,.headerCell_e4ef5c { - align-items: center; - display: flex; - flex-direction: column; - width: 208px -} - -.headerCell_e4ef5c { - justify-content: start -} - -.cell_e4ef5c { - justify-content: center -} - -.headerLabelCell_e4ef5c,.labelCell_e4ef5c { - display: flex; - flex: 1; - justify-content: flex-start; - text-align: start -} - -.headerLabelCell_e4ef5c { - align-items: start -} - -.buttonsCell_e4ef5c,.labelCell_e4ef5c { - align-items: center -} - -.buttonsCell_e4ef5c { - display: flex; - flex-direction: column; - gap: 16px; - justify-content: flex-start; - padding-top: 16px -} - -.closeIcon_e4ef5c { - color: var(--icon-muted) -} - -.checkmarkIcon_e4ef5c { - color: var(--interactive-text-active) -} - -.button_e4ef5c { - height: 40px; - width: 180px -} - -.tableWrapper_e4ef5c { - padding: 2px; - position: relative -} - -.tier0ColumnOuter_e4ef5c,.tier0ColumnOuterBackground_e4ef5c { - border: 2px solid var(--premium-tier-0-purple-for-gradients); - border-radius: var(--radius-lg); - height: 100%; - inset-inline-end: 208px; - pointer-events: none; - position: absolute; - top: 0; - width: 212px; - z-index: 9 -} - -.tier2ColumnOuter_e4ef5c,.tier2ColumnOuterBackground_e4ef5c { - border: 2px solid var(--premium-tier-2-purple-for-gradients-2); - height: 100% -} - -.tier2ColumnOuter_e4ef5c,.tier2ColumnOuterBackground_e4ef5c,.tier2Pill_e4ef5c { - border-radius: 16px; - inset-inline-end: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 212px; - z-index: 9 -} - -.tier0ColumnOuterBackground_e4ef5c:before,.tier2ColumnOuterBackground_e4ef5c:before { - content: ""; - opacity: .1; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - z-index: -1 -} - -.tier0ColumnOuterBackground_e4ef5c:before { - background: linear-gradient(268deg,var(--premium-tier-0-blue) 17.62%,var(--premium-tier-1-blue-for-gradients) 100%) -} - -.tier2ColumnOuterBackground_e4ef5c:before { - background: linear-gradient(101deg,var(--premium-tier-2-purple-for-gradients) 25.21%,var(--premium-tier-2-purple-for-gradients-2) 62.43%,var(--premium-tier-2-pink-for-gradients) 95.57%) -} - -.topBorderRadius_e4ef5c { - border-start-end-radius: 16px; - border-start-start-radius: 16px -} - -.bottomBorderRadius_e4ef5c { - border-end-end-radius: 16px; - border-end-start-radius: 16px -} - -.mostPopularPill_e4ef5c { - background: linear-gradient(95.07deg,var(--premium-tier-2-purple-for-gradients) 0,var(--premium-tier-2-purple-for-gradients-2) 49.96%,var(--premium-tier-2-pink-for-gradients) 95.93%); - border-radius: 8px; - margin: auto; - max-height: 24px; - max-width: 112px; - padding: 4px 8px; - position: relative; - top: -12px; - z-index: 10 -} - -.mostPopularText_e4ef5c { - color: var(--white); - text-align: center; - text-transform: uppercase -} - -.trialOfferPill_e4ef5c { - margin-top: 4px -} - -.betaTag_e4ef5c { - background: var(--custom-premium-colors-premium-gradient-tier-2); - display: inline -} - -.bogoPillWithSparkles_e4ef5c,.freeTrialPillWithSparkles_e4ef5c { - justify-content: center; - max-height: 24px; - max-width: 100%; - position: relative; - top: -13px; - z-index: 10 -} - -.brandShineLight_e4ef5c { - color: hsl(var(--brand-500-hsl)/.1) -} - -.brandShineDark_e4ef5c { - color: var(--opacity-white-8) -} - -.premiumGroupCard_e4ef5c { - margin-bottom: 50px -} - -.textLogo_e4ef5c { - font-size: 20px; - font-style: italic; - text-transform: uppercase -} - -.circleContainer_c1b44f { - overflow: visible; - position: relative -} - -.childrenContainer_c1b44f,.circleContainer_c1b44f { - align-items: center; - display: flex; - justify-content: center -} - -.childrenContainer_c1b44f { - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.theme-light .baseProgressCircle_c1b44f { - stroke: var(--white) -} - -.circleSVG_c1b44f { - height: 100%; - overflow: visible; - width: 100% -} - -.nitroGemAnimation__7d30a { - animation: gemGlow__7d30a 5s ease-in-out infinite -} - -.nitroGemContainer__7d30a { - overflow: visible -} - -.gemBackgroundFill__7d30a { - fill: var(--background-base-low) -} - -.theme-light .gemBackgroundFill__7d30a { - fill: var(--white) -} - -.progressCircle__7d30a { - margin-inline:auto;width: 80% -} - -.progressCricleBottomMargin__7d30a { - margin-bottom: 15px -} - -@keyframes gemGlow__7d30a { - 0%,50%,to { - filter: drop-shadow(0 0 0 hsl(var(--premium-tier-2-pink-for-gradients-2-hsl)/.6)) - } - - 25%,75% { - filter: drop-shadow(0 0 20px hsl(var(--premium-tier-2-pink-for-gradients-2-hsl)/.6)) - } -} - -.activeProgressCircle__7d30a { - fill: transparent; - transform: rotate(-90deg); - transform-origin: center -} - -.activeProgressCircleAnimation__7d30a { - animation: glow__7d30a 5s ease-in-out infinite,pulse__7d30a 5s ease-in-out infinite; - transition: stroke-dashoffset .5s ease-out -} - -@keyframes glow__7d30a { - 0%,50%,to { - filter: none - } - - 25%,75% { - filter: drop-shadow(0 0 5px var(--premium-tier-2-pink-for-gradients-2)) - } -} - -@keyframes pulse__7d30a { - 0%,50%,to { - stroke-width: 2 - } - - 25%,75% { - stroke-width: 3 - } -} - -.avatarDecoContainer__7d30a { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - padding: 12%; - width: 100% -} - -.avatarDecoContainer__7d30a img:before { - background-color: transparent -} - -.avatarDeco__7d30a { - align-items: center; - background: var(--background-base-low); - border-radius: 50%; - display: flex; - justify-content: center; - padding: 15% -} - -.avatarDecoImage__7d30a { - height: 100%; - width: 100% -} - -.expandedProgressBarContainer_a61c56,.expandedProgressBarContainerSettingsPage_a61c56 { - align-items: center; - -webkit-backdrop-filter: blur(8px); - backdrop-filter: blur(8px); - box-sizing: border-box; - display: flex; - margin-top: 40px; - min-width: 560px -} - -@media (max-width: 485px) { - .expandedProgressBarContainer_a61c56,.expandedProgressBarContainerSettingsPage_a61c56 { - min-width:unset - } -} - -.expandedProgressBarContainer_a61c56 { - border-radius: var(--radius-lg); - max-width: 989px; - min-height: 260px -} - -.expandedProgressBarContainerSettingsPage_a61c56 { - background: var(--card-background-default); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - margin-bottom: -40px; - min-height: 292px -} - -.allReferralsSentBorder_a61c56 { - border: 1px solid var(--premium-tier-2-pink-for-gradients-2) -} - -.containerWithGlowWithoutBanner_a61c56,.containerWithoutGlow_a61c56 { - border-radius: var(--radius-lg); - max-width: 989px; - min-height: 260px -} - -.containerWithMargins_a61c56 { - margin-inline:16px} - -.containerWithGlowWithoutBanner_a61c56 { - box-shadow: 0 0 25px 6px rgba(231,66,225,.3) -} - -.containerWithGlowOnSettingsPage_a61c56 { - border-radius: 20px -} - -.expandedProgressBarContent_a61c56 { - margin-inline-start:24px;text-align: start -} - -.userAvatarProgressBarContainer_a61c56 { - align-items: center; - display: flex; - flex-direction: row; - margin-bottom: 8px; - margin-top: 8px -} - -.userAvatarProgressBarUnit_a61c56 { - align-items: center; - border-radius: 32px; - display: flex; - height: 32px; - justify-content: center; - width: 32px -} - -.avatarProgressBarUnitConnectorContainer_a61c56 { - height: 4px; - position: relative; - width: 24px -} - -.avatarProgressBarUnitConnectorBase_a61c56 { - background: linear-gradient(90deg,rgba(180,115,245,.3),rgba(226,146,170,.3)); - height: 100%; - width: 100% -} - -.fullHighlight_a61c56,.halfHighlight_a61c56 { - background: var( --gradients-nitro-nitro-horizontal,linear-gradient(90deg,var(--premium-tier-2-purple) 0,var(--premium-tier-2-pink) 100%) ); - position: absolute; - top: 0 -} - -.fullHighlight_a61c56 { - height: 100%; - width: 100% -} - -.halfHighlight_a61c56 { - border-end-end-radius: 3px; - border-start-end-radius: 3px; - height: 4px; - width: 12px -} - -.expandedProgressBarButtonContainer_a61c56 { - display: flex; - gap: 8px; - margin-top: 16px -} - -.expandedProgressBarButtonContainerLayout_a61c56 { - flex-direction: row -} - -.expandedProgressBarGiftingCTA_a61c56 { - flex: 1 -} - -.expandedProgressBarGiftingCTALayout_a61c56 { - margin-inline-end:8px} - -@media (max-width: 568px) { - .expandedProgressBarButtonContainerLayout_a61c56 { - flex-direction:column; - margin-bottom: 32px - } - - .expandedProgressBarGiftingCTALayout_a61c56 { - margin-bottom: 8px - } -} - -.expandedProgressBarSelectFriendsCTA_a61c56 { - background: linear-gradient(101deg,var(--premium-tier-2-purple-for-gradients) 25.21%,var(--premium-tier-2-purple-for-gradients-2) 62.43%,var(--premium-tier-2-pink-for-gradients) 95.57%); - background-clip: padding-box; - color: var(--white); - flex: 1 -} - -.banner_a61c56 { - align-items: center; - background: linear-gradient(90deg,hsl(var(--premium-tier-2-purple-for-gradients-hsl)/.95) 0,hsl(var(--premium-tier-2-pink-for-gradients-hsl)/.95) 100%); - border-radius: 8px 8px 0 0; - color: var(--white); - display: flex; - height: 40px; - padding-inline-start:32px;position: absolute; - text-align: start; - top: -40px; - width: calc(100% - 32px) -} - -.containerWithBanner_a61c56 { - border-radius: 0 0 8px 8px -} - -.referralInfoContent_a61c56 { - display: flex; - flex-direction: row; - padding-block:24px 16px;padding-inline:24px 48px} - -.progressBar_a61c56 { - background: rgba(180,115,245,.3); - border-radius: 10px; - flex-shrink: 0; - height: 8px; - margin-bottom: 8px; - margin-top: 10px; - max-width: 300px; - width: 60% -} - -.referralCountdownDays_a61c56 { - color: var(--premium-tier-2-purple); - margin-bottom: 10px -} - -.fill_a61c56 { - background: linear-gradient(to right,var(--premium-tier-2-purple),var(--premium-tier-2-pink)); - border-radius: 10px; - height: 100%; - transition: width .3s ease-in-out -} - -.expandedProgressBarSelectFriendsCTAInner_a61c56 { - align-items: center; - display: flex; - flex-direction: row -} - -.expandedProgressBarSelectFriendsIcon_a61c56 { - margin-inline-end:4px} - -.giftIcon_a61c56 { - color: #fff; - height: 16px; - width: 16px -} - -.expandedProgressBarHeader_a61c56 { - color: var(--text-strong) -} - -.unsentTooltipContent_a61c56 { - text-align: center -} - -.referralProgressBarIcon_a61c56 { - max-width: 150px -} - -.referralProgressBarIconSettings_a61c56 { - max-width: 120px -} - -.theme-light .expandedProgressBarContainer_a61c56 { - background: rgba(235,237,239,.7) -} - -.theme-light .userAvatarProgressBarUnitNum_a61c56 { - background: #860091 -} - -.theme-light .userAvatarProgressBarUnit_a61c56 { - background: linear-gradient(90deg,rgba(180,115,245,.6),rgba(226,146,170,.6)) -} - -.theme-light .expandedProgressBarGiftingCTA_a61c56 { - border-color: var(--premium-tier-2-pink-for-gradients-2); - color: var(--premium-tier-2-pink-for-gradients-2) -} - -.theme-dark .expandedProgressBarContainer_a61c56 { - background: rgba(35,36,40,.5) -} - -.theme-dark .userAvatarProgressBarUnitNum_a61c56 { - background: var( --gradients-nitro-nitro-horizontal,linear-gradient(90deg,var(--premium-tier-2-purple) 0,var(--premium-tier-2-pink) 100%) ) -} - -.theme-dark .userAvatarProgressBarUnit_a61c56 { - background: linear-gradient(90deg,rgba(180,115,245,.3),rgba(226,146,170,.3)) -} - -.theme-dark .expandedProgressBarGiftingCTA_a61c56 { - border-color: var(--white); - color: var(--white) -} - -:root .userAvatarProgressBarUnitNum_a61c56 { - -webkit-background-clip: text; - background-clip: text; - color: transparent -} - -.divider_a45ed3 { - background-color: var(--border-subtle); - height: 1px; - margin-bottom: 24px; - margin-top: 24px; - width: 100% -} - -.tierCard_a45ed3 { - background-image: linear-gradient(95.07deg,var(--premium-tier-2-purple-for-gradients) 0,var(--premium-tier-2-purple-for-gradients-2) 49.96%,var(--premium-tier-2-pink-for-gradients) 95.93%); - border-radius: var(--radius-lg); - color: var(--white); - display: flex; - flex-direction: row; - justify-content: space-between; - margin-bottom: 25px; - position: relative; - width: 100% -} - -.tierCard_a45ed3.withTier2Rim_a45ed3 { - border: 1px solid var(--premium-tier-2-purple-for-gradients) -} - -.tierInfo_a45ed3 { - flex: 1; - margin-top: 40px; - margin-inline:40px 16px;margin-bottom: 40px -} - -.tierImage_a45ed3 { - align-items: center; - display: flex; - flex: 1; - width: 100% -} - -.image_a45ed3 { - width: 100% -} - -.tierTitle_a45ed3 { - height: 40px; - margin-bottom: 4px; - width: 160px -} - -.tierCardButton_a45ed3 { - height: 40px; - margin-top: 16px; - width: 100% -} - -.tierCardButtonCTA_a45ed3 { - color: var(--brand-500) -} - -.managePlanButton_a45ed3 { - margin-top: 16px; - width: 100% -} - -.giftCard_a45ed3 { - background: var(--card-background-default); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - margin: 25px 0; - padding: 40px -} - -.giftCard_a45ed3,.giftCardPromotion_a45ed3 { - align-items: center; - color: var(--white); - display: flex; - flex-direction: row; - justify-content: space-around; - overflow: hidden; - position: relative; - width: 100% -} - -.giftCardPromotion_a45ed3 { - border-radius: var(--radius-lg); - height: 200px; - margin-bottom: 25px; - padding: 0 30px -} - -.giftImagePromotion_a45ed3 { - inset-inline-start: 32px; - position: absolute; - width: 220px -} - -.full-motion .giftImagePromotion_a45ed3 { - inset-inline-start: 24px; - top: 10px; - width: 235px -} - -.giftImageAnimatedPromotion_a45ed3 { - padding-bottom: 20px -} - -.giftInfoPromotion_a45ed3 { - margin-inline-start:auto;max-width: 59% -} - -.giftInfoTitlePromotion_a45ed3 { - margin-bottom: 8px -} - -.giftInfo_a45ed3 { - max-width: 50% -} - -.giftImage_a45ed3 { - height: 146px -} - -.seasonalGiftImage_a45ed3 { - bottom: 0; - height: 100%; - inset-inline-end: 47.5%; - position: absolute -} - -.giftTitle_a45ed3 { - margin-bottom: 16px -} - -.giftText_a45ed3 { - margin-bottom: var(--space-12) -} - -.giftAdditionalTerm_a45ed3 { - font-style: italic -} - -.giftCardButton_a45ed3 { - height: 40px; - margin-top: 16px; - width: 100% -} - -.giftButtonCTA_a45ed3 { - font-size: 14px; - font-weight: 700 -} - -.theme-light .giftCardButtonColor_a45ed3 { - border-color: var(--premium-tier-2-pink-for-gradients-2); - color: var(--premium-tier-2-pink-for-gradients-2) -} - -.theme-dark .giftCardButtonColor_a45ed3 { - border-color: #fff; - color: #fff -} - -.halloweenColor_a45ed3,.seasonalColor_a45ed3 { - border-color: #fff; - color: #fff -} - -.bottomOfPageVisibilitySensor_a45ed3 { - bottom: 0; - height: 300px; - position: absolute -} - -.tier2PlanComparisonTableBackground_a45ed3 { - background: linear-gradient(90deg,rgba(180,115,245,.1),rgba(226,146,170,.1)) -} - -.topRimPillWithSparkles_a45ed3 { - inset-inline-end: 8px; - position: absolute; - top: -14px; - z-index: 2 -} - -.rimGlowTier2_a45ed3 { - background: linear-gradient(-90deg,var(--premium-tier-2-purple-for-gradients) 0,#fff 4.98%,#fff 50.52%,var(--premium-tier-2-pink-for-gradients) 100%); - height: 1px; - inset-inline-end: 5%; - position: absolute; - top: -1px; - width: 88%; - z-index: 1 -} - -.trialHeader_a45ed3 { - margin-bottom: 16px -} - -.fractionalPremiumTopRimPill_a45ed3 { - inset-inline-end: 70px; - position: absolute; - top: -10px; - z-index: 2 -} - -.fractionalPremiumSubheader_a45ed3 { - margin-bottom: 16px -} - -.fractionalPremiumSubheader_a45ed3 p { - margin: 0 -} - -.fractionalPremiumSubheader_a45ed3 a { - color: #fff; - text-decoration: underline -} - -.premiumGroupHeader_a45ed3 { - font-size: 32px; - font-style: italic; - line-height: 23.643px; - text-transform: uppercase -} - -.container__60b20 { - align-items: center; - background-color: var(--background-base-lower); - border: none; - box-sizing: border-box; - display: flex; - max-width: 1000px; - padding: 8px; - width: 100% -} - -.container__60b20.lightTextLink__60b20 a { - color: var(--blue-345) -} - -.container__60b20.centerText__60b20 { - justify-content: center; - margin-bottom: unset -} - -.icon__60b20 { - color: var(--status-warning); - height: 13px; - margin-inline-end:6px;min-height: 13px; - min-width: 13px; - width: 13px -} - -.icon__60b20.lightThemeColorOnly__60b20 { - color: var(--yellow-300) -} - -.root__52b47 { - -webkit-mask-image: linear-gradient(180deg,#000 48.68%,transparent); - mask-image: linear-gradient(180deg,#000 48.68%,transparent); - position: absolute; - z-index: -1 -} - -.root__52b47.lifted__52b47 { - border-start-end-radius: var(--radius-lg); - border-start-start-radius: var(--radius-lg); - margin-top: -32px; - width: 95.6%; - z-index: 0 -} - -.stop1__52b47 { - stop-color: var(--premium-tier-0-header-gradient-1) -} - -.stop2__52b47 { - stop-color: var(--premium-tier-0-header-gradient-2) -} - -.stop3__52b47 { - stop-color: var(--premium-tier-0-header-gradient-3) -} - -.stop4__52b47 { - stop-color: var(--premium-tier-0-header-gradient-4) -} - -.stop5__52b47 { - stop-color: var(--premium-tier-0-header-gradient-5) -} - -.tag_c6d624 { - background-color: var(--brand-500); - font-size: 12px; - line-height: 16px; - padding: 0 6px -} - -.perksContainer_b62c4e { - margin: 0 auto; - max-width: 1066px; - position: relative -} - -.perksTitle_b62c4e,.perksTitleStackedCards_b62c4e { - margin-bottom: 32px; - text-align: center -} - -.customAppIconImage_b62c4e { - height: 115px; - width: 230px -} - -.perksTitleStackedCards_b62c4e { - margin-bottom: 24px -} - -.perksCardArt_b62c4e { - filter: saturate(var(--saturation-factor,1)) -} - -.perkCardContainer_b62c4e { - display: grid; - gap: 16px; - grid-template-columns: repeat(auto-fit,minmax(260px,1fr)); - justify-items: center; - margin-bottom: 32px -} - -.perkCardContainerExpanded_b62c4e { - margin-bottom: 16px -} - -.perkCard_b62c4e { - align-items: center; - background-color: var(--card-background-default); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - box-sizing: border-box; - display: flex; - flex-direction: column; - justify-content: space-between; - padding: 32px 24px; - position: relative; - width: 100% -} - -.perkCardNewBadge_b62c4e { - background: var(--custom-premium-colors-premium-gradient-tier-2); - border-radius: 8px; - color: #fff; - inset-inline-start: -4px; - position: absolute; - top: -4px -} - -.perkCardHeading_b62c4e { - margin-bottom: 8px; - margin-top: 16px; - text-align: center -} - -.perkCardDescription_b62c4e { - min-height: 36px; - text-align: center -} - -.perkCardEarlyAccessBadge_b62c4e { - background: var(--custom-premium-colors-premium-gradient-tier-2); - border-radius: 8px; - color: #fff; - inset-inline-start: 24px; - position: absolute; - top: -4px -} - -.stickersImage_b62c4e { - width: 222px -} - -.perGuildProfilesImage_b62c4e { - width: 195px -} - -.clientThemesCard_b62c4e .perkCardDescription_b62c4e { - padding: 0 30px -} - -.clientThemesImage_b62c4e { - width: 202px; - width: 227px -} - -.badgeImage_b62c4e { - width: 193px -} - -.longerMessagesImage_b62c4e { - width: 222px -} - -.moreGuildsImage_b62c4e { - width: 116px -} - -.soundboardImage_b62c4e,.superReactionsImage_b62c4e { - height: 132px -} - -.uploadImage_b62c4e { - width: 220px -} - -.streamingImage_b62c4e { - width: 175px -} - -.emojiImage_b62c4e { - width: 210px -} - -.videoBackgroundImage_b62c4e { - width: 222px -} - -.moreGuildsAltImage_b62c4e { - width: 260px -} - -.newTag_b62c4e { - inset-inline-start: -2px; - position: absolute; - top: -4px -} - -.betaTag_b62c4e { - background: var(--custom-premium-colors-premium-gradient-tier-2); - display: inline; - vertical-align: middle -} - -.sizeGizmo_b62c4e { - bottom: 0; - inset-inline-start: 50%; - position: absolute; - transform: translate(-50%,-100%); - z-index: 1000 -} - -.sizeGizmoExpanded_b62c4e { - display: flex; - justify-content: center -} - -.sizeGizmoText_b62c4e { - align-items: center; - display: flex; - justify-content: flex-end -} - -.sizeGizmoText_b62c4e .arrow_b62c4e { - margin-inline-start:8px} - -.subscriberNitroHome_b62c4e { - max-height: 2000px; - overflow: hidden; - transition: max-height .4s ease-in-out -} - -.subscriberNitroHome_b62c4e.reducedMotion_b62c4e { - transition: none -} - -.partiallyHidden_b62c4e { - max-height: 500px; - transition: none -} - -.customButton_b62c4e { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: 24px; - color: var(--text-default); - display: flex; - font-size: 14px; - gap: 4px; - justify-content: center; - padding-block:8px;padding-inline:16px 8px} - -.cover_b62c4e { - background-image: linear-gradient(to bottom,hsl(var(--primary-600-hsl)/.71) 0,var(--background-base-low) 85%); - bottom: 0; - height: 31%; - inset-inline: 0; - pointer-events: none; - position: absolute -} - -.cover_b62c4e.hidden_b62c4e { - opacity: 0 -} - -.reducedMotion_b62c4e .cover_b62c4e { - transition: none -} - -.theme-light .cover_b62c4e { - background-image: linear-gradient(to bottom,var(--opacity-white-72) 0,var(--background-base-low) 85%) -} - -.container__94da4 { - align-items: center; - display: flex; - flex: 1; - flex-direction: column; - margin: 0 auto -90px; - padding-bottom: 60px; - position: relative -} - -.settingsContainer__94da4 { - width: 100% -} - -.settingsHeroNoBackground__94da4 { - width: 660px -} - -.noTopPaddingOverride__94da4 { - padding-top: 0 -} - -.settingsHeroBogoHeight__94da4 { - height: 260px -} - -.premiumTierCardsContainerSettings__94da4 { - margin-bottom: 60px; - max-width: 824px; - width: 100% -} - -.promoBannerContainer__94da4 { - justify-content: center; - margin-bottom: var(--space-24); - max-width: 1052px; - position: relative; - width: 100%; - z-index: 1 -} - -.giftPromoCard__94da4 { - margin-bottom: 25px -} - -.perkCardsContainerSpacingSettings__94da4 { - margin-bottom: 60px!important; - width: 100% -} - -.planComparisonTable__94da4 { - max-width: 700px -} - -.planComparisonTableContainer__94da4 { - align-items: center; - display: flex; - justify-content: center; - position: relative; - width: 100% -} - -.bottomIllustration__94da4 { - bottom: 0; - filter: saturate(var(--saturation-factor,1)); - position: absolute -} - -.existingSubscriberSpacing__94da4 { - margin-bottom: 24px -} - -.footerSpacing__94da4 { - height: 128px -} - -.zIndex1__94da4 { - z-index: 1 -} - -.trialMarketingMargin__94da4 { - margin-bottom: 56px; - margin-top: 56px; - padding: unset -} - -.bottomOfPageVisibilitySensor__94da4 { - bottom: 0; - height: 300px; - position: absolute -} - -.container__61306 { - box-sizing: border-box; - display: flex; - flex: 1; - flex-direction: column; - margin: 0 auto; - max-width: 660px -} - -.applicationStoreHomeWrapper__61306 { - margin: 60px 40px 80px -} - -.loading__61306 { - align-items: center; - display: flex; - height: 520px; - justify-content: center -} - -.background__61306 { - background-repeat: no-repeat; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.content__61306 { - position: relative -} - -.sectionTier2__61306 { - margin-top: 32px; - text-align: center -} - -.marketingRefreshTier2Intro__61306 { - max-width: 325px -} - -.marketingRefreshTitle__61306 { - color: var(--text-strong); - font-size: 24px; - font-weight: var(--font-weight-semibold); - line-height: 30px; - margin-bottom: 8px; - margin-top: 0 -} - -.marketingRefreshSubtitle__61306 { - color: var(--text-default); - font-size: 16px; - line-height: 20px; - margin: 0 -} - -.marketingRefreshHeaderAnimation__61306 { - margin-inline-start:16px;width: 332px -} - -.marketingRefreshTier2Cta__61306 { - margin-top: 48px; - text-align: center -} - -.marketingRefreshSectionTier1__61306 { - border-top: 1px solid var(--border-subtle); - color: var(--text-default); - display: flex; - margin-top: 48px; - padding-top: 48px -} - -.marketingRefreshTier1Graphic__61306 { - display: block; - flex-shrink: 0; - height: 148px; - margin-inline-end:60px;width: 175px -} - -@media (max-width: 920px) { - .marketingRefreshTier1Graphic__61306 { - display:none - } -} - -.marketingRefreshTier1Logo__61306 { - color: var(--text-strong); - display: block; - height: 20px; - margin-bottom: 8px; - width: 194px -} - -.marketingRefreshDescription__61306 { - font-size: 16px; - line-height: 20px; - margin: 0 0 16px -} - -.tier1InfoTooltipList__61306 { - list-style-type: disc; - padding-inline-start:15px} - -.tier1InfoTooltipListItem__61306 { - padding: 2px -} - -.tier1InfoTooltipListItemOffsetItem__61306 { - inset-inline-start: -5px; - position: relative -} - -.tier1InfoIcon__61306 { - height: 10px; - width: 10px -} - -.tier2Buttons__61306 { - margin-top: 24px -} - -.subSectionHeader__61306 { - text-transform: uppercase -} - -.sectionAccountCredit__61306,.subscriptionDetails__61306 { - border-bottom: 1px solid; - margin-bottom: 40px; - padding-bottom: 40px -} - -.accountCredit__61306 { - margin-top: 16px -} - -.accountCreditTitle__61306 { - margin-bottom: 16px -} - -.accountCreditDescription__61306 { - color: var(--text-default); - line-height: 20px; - margin-bottom: 16px; - margin-top: 0 -} - -.tier2Plan__61306 { - display: flex -} - -.marketingRefreshTier2PlanWrapper__61306 { - display: flex; - margin-bottom: 48px -} - -.marketingRefreshTier2PlanName__61306 { - color: var(--logo-primary); - display: block; - height: 60px; - margin-bottom: 32px; - width: auto -} - -.subscriptionsRedirectContainer__61306 { - align-items: center; - background-color: var(--background-base-lower); - border: none; - display: flex; - margin-bottom: 24px; - padding: 8px -} - -.subscriptionsRedirectWarningIcon__61306 { - color: var(--status-warning); - height: 13px; - margin-inline-end:6px;width: 13px -} - -.theme-light .sectionAccountCredit__61306,.theme-light .subscriptionDetails__61306 { - border-color: hsl(var(--primary-300-hsl)/.3) -} - -.theme-dark .sectionAccountCredit__61306,.theme-dark .subscriptionDetails__61306 { - border-color: hsl(var(--primary-400-hsl)/.3) -} - -.perkList__61306,.perkListItem__61306 { - margin-bottom: 16px -} - -.perkListItem__61306 { - align-items: center; - display: flex -} - -.perkListItem__61306:last-child { - margin-bottom: 0 -} - -.perkListItemIcon__61306 { - flex-shrink: 0; - height: 20px; - margin-inline-end:16px;width: 20px -} - -.theme-dark .tier1CustomEmojiPerk__61306 { - color: #fee64f -} - -.theme-dark .tier1StreamPerk__61306 { - color: #9bdd73 -} - -.theme-light .tier1CustomEmojiPerk__61306 { - color: #d0820c -} - -.theme-light .tier1StreamPerk__61306 { - color: #69974d -} - -.images-light .tier1Banner__61306 { - background-image: url(/assets/460e67c2a6814672.svg) -} - -.images-dark .tier1Banner__61306 { - background-image: url(/assets/cbe5498434e90cc1.svg) -} - -.tier1Banner__61306 { - background-color: var(--background-base-lower); - color: var(--text-strong) -} - -.tier1ProfilePerk__61306 { - color: #4087ed -} - -.tier1BoostPerk__61306 { - color: var(--guild-boosting-pink) -} - -.tier1UploadPerk__61306 { - color: #776bff -} - -.modal_a327d2 { - min-height: 180px; - overflow-x: hidden -} - -.form_a327d2 { - display: flex; - flex-direction: column; - flex-grow: 1; - min-height: 0 -} - -.content_a327d2 { - height: 100%; - padding-bottom: 20px -} - -.modalTitle_a327d2 { - padding: 16px 16px 0 -} - -.divider_a327d2 { - border-top: 1px solid; - margin: 0 20px 12px -} - -.errorBlock_a327d2 { - margin-bottom: 20px -} - -.sequencer_a327d2 { - display: flex; - flex-direction: column -} - -@media (max-width: 485px) and (max-height:450px) { - .form_a327d2 { - display:block - } -} - -.theme-light .divider_a327d2 { - border-color: hsl(var(--primary-200-hsl)/.6) -} - -.theme-dark .divider_a327d2 { - border-color: var(--primary-630) -} - -.externalLink__900c6 { - width: 100% -} - -.theme-dark .container__900c6 { - background-color: var(--primary-630) -} - -.theme-light .container__900c6 { - background-color: hsl(var(--primary-100-hsl)/.6) -} - -.currency__900c6 { - color: var(--text-default); - font-size: 14px; - margin-top: 12px -} - -.currency__900c6 .currencyDropdown__900c6 { - flex-grow: 1; - margin-inline-start:4px;width: 80px -} - -.container__37475 { - align-items: flex-start; - background-color: var(--background-base-lower); - border-radius: 4px; - display: flex; - max-width: 640px; - padding: 10px; - width: 100% -} - -.textContainer__37475 { - display: flex; - flex: 1 -} - -.warningIcon__37475 { - height: 20px; - width: 20px -} - -.info__37475,.warningIcon__37475 { - color: var(--interactive-text-default) -} - -.info__37475 { - font-size: 14px; - line-height: 18px -} - -.error__37475 { - color: var(--text-feedback-critical); - font-weight: var(--font-weight-medium) -} - -.benefits__25f6c { - background-color: var(--background-base-lower); - border-radius: 8px; - padding: 16px -} - -.container__0ab43 { - background-color: var(--background-base-lower); - border-radius: 8px; - padding: 16px -} - -.headerContainer__0ab43 { - align-items: center; - cursor: pointer; - display: flex -} - -.headerTextContainer__0ab43 { - flex: 1; - margin-inline-start:16px} - -.guildName__0ab43 { - color: var(--interactive-text-active); - margin-bottom: 2px -} - -.arrowIcon__0ab43,.tierName__0ab43 { - color: var(--interactive-text-default) -} - -.arrowIcon__0ab43 { - height: 24px; - width: 24px -} - -.arrowIconExpanded__0ab43 { - transform: rotate(180deg) -} - -.headerSubtitleContainer__0ab43 { - align-items: center; - display: flex; - gap: 6px -} - -.divider__0ab43 { - border-bottom: 1px solid var(--opacity-white-4); - margin: 16px 0 -} - -.changePlanNotice__0ab43 { - background-color: var(--background-mod-normal); - margin-bottom: 16px -} - -.subscriptionInfoCards__0ab43 { - display: flex; - flex-wrap: wrap; - gap: 16px -} - -.container__0ab43 .paymentSourceDropdown__0ab43 { - margin-bottom: 16px -} - -.container__0ab43 .paymentSourceDropdown__0ab43,.paymentSourceDropdownOption__0ab43 { - background-color: var(--background-mod-normal) -} - -.infoCard__0ab43 { - background-color: var(--card-background-default); - border: 1px solid var(--border-subtle); - border-radius: 8px; - flex: 1; - padding: 16px -} - -.infoCardLabel__0ab43 { - color: var(--interactive-text-default); - margin-bottom: 16px; - text-transform: uppercase -} - -.infoCardLabelContainer__0ab43 { - display: flex; - flex-direction: row -} - -.infoCardIcon__0ab43 { - color: var(--interactive-text-default); - height: 16px; - margin-inline-start:8px;width: 16px -} - -.infoCardValue__0ab43 { - color: var(--text-strong); - font-weight: var(--font-weight-normal) -} - -.rowButton__0ab43 { - background-color: var(--background-mod-normal); - cursor: pointer -} - -.rowButton__0ab43:first-child { - border-radius: 8px 8px 0 0 -} - -.rowButton__0ab43:last-child { - border-radius: 0 0 8px 8px -} - -.rowButton__0ab43:last-child .rowButtonContent__0ab43 { - border-bottom: none -} - -.rowButtonContent__0ab43 { - align-items: center; - border-bottom: 1px solid var(--opacity-white-4); - display: flex; - justify-content: space-between; - margin-inline-start:16px;padding-block:16px;padding-inline:0 16px} - -.rowButtonArrow__0ab43 { - color: var(--interactive-text-default); - height: 24px; - transform: rotate(-90deg); - width: 24px -} - -.rowButtonLabel__0ab43 { - color: var(--interactive-text-active); - font-weight: var(--font-weight-medium) -} - -.paymentDueBadge__0ab43 { - color: var(--primary-860); - letter-spacing: .02em -} - -.pageDescription__8f372 { - margin-bottom: 40px -} - -.subscriptionsContainer__8f372 { - display: flex; - flex-direction: column; - gap: 16px -} - -.buttonWrapper_bf1984 { - display: inline-flex; - max-width: var(--custom-button-button-lg-width); - min-width: -moz-min-content; - min-width: min-content -} - -.sectionDescription__9851a { - color: var(--text-default) -} - -.card__9851a { - background-image: linear-gradient(90deg,var(--application-subscription-start) 0,var(--application-subscription-end) 100%); - flex-wrap: wrap; - gap: 16px -} - -.bannerImage__9851a { - align-self: flex-end; - margin-bottom: -16px -} - -.textContainer__9851a { - flex: 1; - position: relative -} - -.header__9851a { - margin-bottom: 4px; - margin-top: 5px -} - -.description__9851a,.header__9851a { - color: var(--white) -} - -.description__9851a strong { - font-weight: var(--font-weight-medium) -} - -.tryItOutButtons_fbfab6 { - display: flex; - gap: 12px -} - -.premiumCta_fbfab6 { - border-radius: var(--radius-sm); - margin-inline-start:8px} - -.basicThemeSelectors_fbfab6 { - margin-bottom: 24px; - margin-top: 0 -} - -.previewThemeButton_fbfab6 { - margin-bottom: 26px; - margin-top: 16px -} - -.premiumTier2Divider_fbfab6 { - background-image: linear-gradient(90deg,var(--premium-tier-2-pink-for-gradients) 0,var(--premium-tier-2-purple-for-gradients) 50%,var(--premium-tier-2-pink-for-gradients) 100%); - height: 2px; - margin-inline:-24px} - -.divider_fbfab6 { - margin-bottom: 24px; - margin-top: 32px -} - -.title_fbfab6 { - color: var(--text-strong) -} - -.colorThemesBackground_fbfab6 { - background: var(--background-base-lower); - border: 1px solid var(--border-normal); - border-radius: var(--radius-md); - padding: 24px -} - -.darkSidebarToggle_fbfab6 { - margin: var(--space-24) 0 -} - -.darkSidebarToggle_fbfab6>div { - align-items: center; - display: flex; - height: 48px -} - -.wrapper_be018f { - background-color: var(--background-base-lower); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-low); - box-sizing: border-box; - overflow: hidden -} - -.header_be018f { - align-items: center; - background-color: var(--background-mod-normal); - display: flex; - justify-content: space-between -} - -.content_be018f,.header_be018f { - padding: 16px -} - -.toggle_a63120 { - align-items: center; - color: var(--text-brand); - cursor: pointer; - display: flex; - gap: 4px -} - -.headerWrapper__4909b { - gap: 8px; - height: 72px -} - -.headerContent__4909b { - display: flex; - gap: 16px; - min-width: 0 -} - -.headerContentText__4909b { - display: flex; - flex: 1; - flex-direction: column; - min-width: 0 -} - -.appIcon__4909b { - border-radius: var(--radius-md) -} - -.headerButtons__4909b { - display: flex; - gap: 8px -} - -.details__4909b { - padding-bottom: 16px -} - -.row__4909b { - align-items: center; - border-bottom: 1px solid var(--border-subtle); - box-sizing: border-box; - display: flex; - justify-content: space-between; - padding: 16px 0 -} - -.row__4909b:first-child { - padding-top: 0 -} - -.rowContent__4909b { - text-align: end -} - -.subscriptionTypeRow__4909b { - display: inline-flex; - gap: 8px -} - -.guildSubscriptionContentRow__4909b { - align-items: center; - display: flex; - gap: 8px; - padding-top: 2px -} - -.noticeBanner__4909b { - margin-bottom: 16px -} - -.payment__4909b { - height: 65px -} - -.benefits__4909b { - display: flex; - flex-direction: column; - gap: 16px; - padding-top: 16px -} - -.benefitsHeader__4909b { - align-items: center; - display: flex; - justify-content: space-between -} - -.benefitsBtn__4909b { - margin-inline-start:auto} - -.managementBtns__4909b { - display: flex; - gap: 8px -} - -.button_e08d4d { - background: inherit; - padding: 0 -} - -.seeMore_e08d4d { - align-items: center; - display: flex; - gap: 4px; - margin-top: 4px -} - -.content_e08d4d { - overflow: hidden; - transition: height .25s ease-in-out -} - -.wrapper__3425d { - max-width: 660px; - width: 100% -} - -.headerTitle__3425d { - align-items: center; - display: flex; - gap: 16px -} - -.subscriptionImg__3425d { - border-radius: 48px -} - -.description__3425d { - white-space: pre-line -} - -.benefits__3425d { - display: flex; - flex-direction: column; - gap: 16px; - padding-top: 16px -} - -.header_b87308 { - align-items: flex-end; - border-bottom-width: 1px; - border-color: hsl(var(--primary-300-hsl)/.3); - border-style: solid; - display: flex; - flex-direction: row; - gap: 16px; - margin-bottom: 16px; - padding-bottom: 24px -} - -.theme-dark .header_b87308 { - border-color: hsl(var(--primary-400-hsl)/.3) -} - -.appIcon_b87308 { - border-radius: var(--radius-md) -} - -.subInfo_b87308 { - flex-direction: row; - padding-top: 2px -} - -.guildSubscription_b87308,.subInfo_b87308,.subInfoType_b87308 { - display: flex; - gap: 8px -} - -.subInfoType_b87308 { - align-items: center -} - -.subscriptions_b87308 { - display: flex; - flex-direction: column; - gap: 16px; - padding-top: 32px -} - -.activeSubscriptionCard_b87308 { - border: 2px solid var(--brand-500) -} - -.activeSubscriptionCTA_b87308 { - display: flex; - flex-direction: column; - gap: 4px; - text-align: end -} - -.planNotice_b87308 { - display: flex; - flex-direction: column; - gap: 8px -} - -.subscriptionsContainer__0dc23 { - display: flex; - flex-direction: column; - gap: 16px -} - -.title__0dc23 { - align-items: center; - display: flex; - gap: 20px; - margin-bottom: 40px -} - -.fractionalPremiumAccountCredit__94f72,.premiumSubscriptionAccountCredit__94f72 { - background-color: var(--background-base-lower); - border-radius: 5px -} - -.accountCreditRow__94f72 { - display: flex; - padding: 16px -} - -.accountCreditsContainer__94f72 .accountCreditRow__94f72:not(:last-child) { - border-bottom: 1px solid var(--border-subtle) -} - -.iconBackground__94f72 { - align-items: center; - align-self: center; - border-radius: 4px; - display: flex; - justify-content: center; - padding: 4px -} - -.iconBackgroundTier0__94f72 { - background-image: linear-gradient(90deg,var(--premium-tier-0-purple-for-gradients),var(--premium-tier-0-blue-for-gradients)) -} - -.iconBackgroundTier1__94f72 { - background-image: linear-gradient(90deg,var(--premium-tier-1-dark-blue-for-gradients),var(--premium-tier-1-blue-for-gradients)) -} - -.iconBackgroundTier2__94f72 { - background-image: linear-gradient(90deg,var(--premium-tier-2-purple-for-gradients),var(--premium-tier-2-pink-for-gradients)) -} - -.iconBackgroundFractional__94f72 { - background-image: linear-gradient(0deg,var(--guild-boosting-blue),var(--guild-boosting-purple)) -} - -.icon__94f72 { - color: var(--white) -} - -.icon__94f72,.iconFractional__94f72 { - height: 24px; - width: 24px -} - -.iconFractional__94f72 { - filter: drop-shadow(0 1px 2px rgb(0 0 0/.4)) -} - -.rowDetails__94f72 { - display: flex; - flex: 1; - flex-direction: column; - justify-content: center; - margin: 0 8px -} - -.rowApplied__94f72 { - font-weight: var(--font-weight-normal) -} - -.rowCreditCount__94f72 { - align-self: center; - color: var(--interactive-text-default) -} - -.fractionalTimeRemainingRow__94f72 { - background-color: var(--background-base-lowest); - border-radius: 0 0 5px 5px; - display: flex; - padding: 16px -} - -.fractionalTimeRemainingRowDetails__94f72 { - color: var(--brand-360); - display: flex; - flex: 1; - flex-direction: column; - justify-content: center; - margin: 0 -} - -.fractionalTimeRemainingRowHeader__94f72 { - color: var(--brand-360) -} - -.fractionalTimeRemainingPillWrapper__94f72 { - display: flex; - flex-direction: column; - justify-content: center -} - -.fractionalTimeRemainingPill__94f72 { - align-items: center; - background-image: linear-gradient(0deg,var(--brand-560),var(--brand-460)); - border-radius: var(--radius-md); - display: flex -} - -.fractionalTimeRemainingPillText__94f72 { - color: var(--white); - display: inline-block; - line-height: 18px; - padding: 4px 8px; - text-align: center -} - -.tier1AccountCreditHeader__94f72 { - padding-bottom: 16px; - padding-top: 32px -} - -.fractionalUnactivatedPill__94f72 { - align-items: center; - background-color: var(--background-base-lower); - border-radius: var(--radius-md); - display: flex -} - -.fractionalUnactivatedPillText__94f72 { - color: var(--brand-360); - display: inline-block; - line-height: 18px; - padding: 4px 8px; - text-align: center -} - -.banner__84995 { - background-position: 0 0; - background-repeat: no-repeat; - border-radius: 4px; - box-sizing: border-box; - color: var(--white); - display: flex; - flex-direction: column; - padding: 16px; - position: relative -} - -.bannerBackgroundImage__84995 { - background-image: url(/assets/be68608fe0247064.svg); - background-repeat: no-repeat; - bottom: 0; - position: absolute; - top: 6px; - inset-inline: 0 -} - -.image__84995 { - background-size: 100% 100%; - flex-shrink: 0; - height: 68px; - margin-inline-end:8px;width: 96px -} - -@media (max-width: 485px) { - .image__84995 { - display:none - } -} - -.repositioned__84995 .image__84995 { - height: 68px; - margin-inline-end:16px;width: 88px -} - -.repositioned__84995 .buttons__84995 { - inset-inline-end: 0; - position: relative; - top: 0 -} - -.repositioned__84995 .planInfo__84995 { - margin: 10px 0 16px -} - -.repositioned__84995 .planName__84995 { - height: 28px -} - -.repositioned__84995.tier0__84995 .basicWordmark__84995 { - background-size: contain; - height: 16px -} - -.details__84995,.image__84995 { - position: relative -} - -.tier0__84995.banner__84995 { - background-image: var(--custom-premium-colors-premium-gradient-tier-0) -} - -.tier0__84995 .image__84995 { - background-image: url(/assets/678405b196a10914.svg) -} - -.tier0__84995 .discordWordmark__84995 { - height: 16px; - width: 41px -} - -.tier0__84995 .basicWordmark__84995 { - background-image: url(/assets/dbd86d446dd08738.svg); - background-repeat: no-repeat; - height: 19px -} - -.tier0__84995 .details__84995 { - display: flex; - flex: 1 1 auto; - flex-direction: column; - height: 100% -} - -.tier0__84995.canceled__84995 .image__84995,.tier0__84995.failedPayment__84995 .image__84995,.tier0__84995.pausePending__84995 .image__84995,.tier0__84995.paused__84995 .image__84995 { - background-image: url(/assets/2151b9a5d6e8fd2d.svg) -} - -.tier1__84995.banner__84995 { - background-image: var(--custom-premium-colors-premium-gradient-tier-1) -} - -.tier1__84995 .image__84995 { - background-image: url(/assets/9e74dbc28e1b3b88.svg) -} - -.tier1__84995 .discordWordmark__84995 { - height: 16px; - width: 41px -} - -.tier1__84995 .classicWordmark__84995 { - background-image: url(/assets/1aa632d6117fca62.svg); - height: 19px; - width: 185px -} - -.tier1__84995 .details__84995 { - display: flex; - flex: 1 1 auto; - flex-direction: column; - height: 100% -} - -.tier1__84995.canceled__84995 .image__84995,.tier1__84995.failedPayment__84995 .image__84995,.tier1__84995.pausePending__84995 .image__84995,.tier1__84995.paused__84995 .image__84995 { - background-image: url(/assets/2151b9a5d6e8fd2d.svg) -} - -.tier2__84995.banner__84995 { - background-image: var(--custom-premium-colors-premium-gradient-tier-2) -} - -.tier2__84995 .image__84995 { - background-image: url(/assets/64932bb2d0db66ec.svg) -} - -.tier2__84995 .planName__84995 { - height: 32px; - width: 77px -} - -.tier2__84995.canceled__84995 .image__84995,.tier2__84995.failedPayment__84995 .image__84995,.tier2__84995.pausePending__84995 .image__84995,.tier2__84995.paused__84995 .image__84995 { - background-image: url(/assets/3137261af6151921.svg) -} - -.canceled__84995.banner__84995 { - background-image: linear-gradient(90deg,#f18623,#f6a12a 56%,#fab930) -} - -.canceled__84995 .planInfo__84995,.canceled__84995 .planName__84995 { - color: var(--black) -} - -.failedPayment__84995.banner__84995 { - background-image: linear-gradient(270deg,#e82427,#c81031) -} - -.paused__84995.banner__84995 { - border-radius: 4px -} - -.pausePending__84995.banner__84995,.paused__84995.banner__84995 { - background: var(--primary-500) -} - -.planInfo__84995 { - font-size: 16px; - line-height: 20px; - margin-top: 8px -} - -.buttons__84995 { - inset-inline-end: 20px; - justify-content: flex-end; - position: absolute; - top: 16px -} - -.buttons__84995,.toolsButtons__84995 { - display: flex -} - -.toolsButton__84995 { - margin-inline-start:16px;position: relative -} - -@media (max-width: 390px) { - .premiumSettingsPushButton__84995 { - margin-inline-start:8px - } -} - -.secondaryBannerTextButtonContainerWithPause__84995 { - margin-top: 5px; - margin-inline-end:15px} - -@media (max-width: 485px) { - .secondaryBannerTextButtonContainerWithPause__84995 { - margin-top:0; - margin-inline-end:0} - - .secondaryBannerTextButtonContainerWithPause__84995 button>span { - display: inline-block; - line-break: strict; - overflow-wrap: break-word; - text-align: center; - white-space: normal; - width: 130px; - word-break: keep-all - } -} - -@media (max-width: 390px) { - .secondaryBannerTextButtonContainerWithPause__84995 button>span { - width:120px - } -} - -.secondaryBannerTextButtonContainer__84995 { - margin-top: 5px; - margin-inline-end:15px} - -.linkButton__84995 { - padding: 0 2px; - position: relative -} - -.externalButton__84995 { - text-transform: uppercase -} - -.detailsContainer__84995 { - align-items: center; - display: flex -} - -.discountPill__84995 { - margin-inline-start:8px;position: relative; - top: 5px -} - -.headerLabel__84995 { - display: flex; - flex-direction: row -} - -.wordMark__84995 { - display: flex; - flex: 1 1 auto; - flex-direction: column -} - -.headerColumnB__84995 { - flex-direction: row -} - -.finePrintWithOverheadSeparator_df7724 { - border-top: 1px solid var(--border-subtle); - line-height: 1.4; - margin-top: 32px; - padding-top: 32px -} - -.finePrint_df7724 { - line-height: 1.4 -} - -.container__8f41e { - align-items: center; - border-radius: var(--radius-sm); - display: flex; - justify-content: space-between; - min-width: 0; - padding-block:var(--space-8);padding-inline-end: var(--space-8); - padding-inline-start:var(--space-4)} - -.container__8f41e.hoverBackground__8f41e: hover { - background:var(--background-mod-muted) -} - -.userInfoContainer__8f41e { - align-items: center; - display: flex; - flex-direction: row; - min-width: 0; - overflow: hidden -} - -.userInfoContainer__8f41e.faded__8f41e { - opacity: .5 -} - -.avatar__8f41e { - flex-shrink: 0; - margin-inline-end:var(--space-12)} - -.username__8f41e { - flex-shrink: 1; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.separator__8f41e { - margin-inline-end:var(--space-4);margin-inline-start: var(--space-4) -} - -.label__8f41e { - flex-shrink: 0; - white-space: nowrap -} - -.userIconCircle__8f41e { - align-items: center; - background: var(--background-mod-subtle); - border-radius: 50%; - display: flex; - height: 24px; - justify-content: center; - width: 24px -} - -.textButtonContainer__8f41e { - margin-inline-start:var(--space-12)} - -.logoHeader_f1578d { - font-size: 16px; - font-style: italic; - font-weight: 900; - line-height: 23.643px; - text-transform: uppercase -} - -.container_f1578d { - background: var(--background-surface-high); - border-radius: var(--radius-sm); - padding: var(--space-4) var(--space-24) 0; - width: 100% -} - -.header_f1578d { - align-items: center; - border-bottom: 1px solid var(--border-muted); - display: flex -} - -.headerTitle_f1578d { - height: 16px -} - -.contentGrid_f1578d { - display: grid; - grid-template-columns: 1fr 1fr; - min-height: 320px -} - -.contentGrid_f1578d>* { - min-width: 0 -} - -.descriptionAndButtonContainer_f1578d { - align-items: flex-start; - border-inline-end:1px solid var(--border-muted);box-sizing: border-box; - display: flex; - flex-direction: column; - justify-content: center; - padding-inline-end:var(--space-32);padding-bottom: var(--space-24); - padding-top: var(--space-24) -} - -.descriptionContainer_f1578d { - margin-bottom: var(--space-10) -} - -.buttonContainer_f1578d { - margin-top: var(--space-12) -} - -.usersListHeader_f1578d,.usersListHeaderContainer_f1578d { - margin-bottom: 10px -} - -.usersListHeader_f1578d { - padding: var(--space-12) var(--space-4) 0 -} - -.button_f1578d { - margin-top: var(--space-12) -} - -.premiumGroupIcon_f1578d { - color: var(--text-strong) -} - -.churnDiscountBanner__32fe3 { - border-radius: var(--radius-md); - box-sizing: border-box; - display: flex; - flex-direction: column; - height: 100%; - max-width: 660px; - padding: 16px 24px 24px; - position: relative -} - -.churnDiscountBannerContent__32fe3 { - display: flex; - flex-direction: row -} - -.churnDiscountBannerDetails__32fe3 { - display: flex; - flex-direction: column; - gap: 10px; - height: 100%; - max-width: 420px; - padding-inline-end:12px} - -.churnDiscountBannerBackground__32fe3 { - background-color: var(--background-base-lowest); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-md) -} - -.selectPlanDivider__32fe3 { - background-color: var(--border-subtle); - height: 1px; - margin: 8px 0 16px -} - -.headerLabel__32fe3 { - display: flex; - flex-direction: row; - padding-bottom: 4px -} - -.headerGradient__32fe3 { - border-radius: var(--radius-md); - margin: -1px; - margin-top: calc(var(--custom-modal-padding-top)*-1 - 1px); - padding: 1px; - padding-top: calc(var(--custom-modal-padding-top) + 1px) -} - -.churnDiscountBannerImage__32fe3 { - max-height: 140px; - max-width: 250px; - padding-inline-start:20px} - -.activeSubButtons__32fe3 { - display: flex; - gap: 8px -} - -.churnDiscountBannerButton__32fe3 { - margin-top: 12px; - position: relative -} - -.churnDiscountPill__32fe3 { - margin-inline-start:8px;position: relative -} - -.churnDiscountBannerExpiryDate__32fe3 { - justify-content: flex-end; - margin-inline-start:auto;position: relative -} - -.churnDiscountBannerWordMark__32fe3 { - color: var(--text-strong); - width: 67px -} - -.root_dbec16 { - align-items: center; - background-color: var(--background-base-lower); - border-radius: 4px; - display: flex; - font-size: 14px; - line-height: 18px; - padding: 8px 16px -} - -.infoIcon_dbec16 { - height: 14px; - margin-inline-end:6px;width: 14px -} - -.infoIcon_dbec16,.text_dbec16 { - color: var(--interactive-text-default) -} - -.text_dbec16 { - flex-grow: 1 -} - -.cancelLink_dbec16 { - color: var(--blue-345); - font-weight: var(--font-weight-semibold); - white-space: nowrap -} - -@media (max-width: 485px) { - .cancelLink_dbec16 { - margin-inline-start:6px - } -} - -.modalBody_dbec16 { - color: var(--interactive-text-default); - font-size: 16px; - line-height: 20px; - margin-bottom: 16px -} - -.container__70151 { - box-sizing: border-box; - display: flex; - flex: 1; - flex-direction: column -} - -.loading__70151 { - align-items: center; - display: flex; - height: 520px; - justify-content: center -} - -.background__70151 { - background-repeat: no-repeat; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.content__70151 { - display: flex; - flex-direction: column; - gap: 32px; - position: relative -} - -.subSectionHeader__70151 { - text-transform: uppercase -} - -.accountCredit__70151 { - margin-top: 16px -} - -.accountCreditTitle__70151 { - margin-bottom: 8px -} - -.accountCreditDescription__70151 { - color: var(--text-default); - margin-bottom: 16px; - margin-top: 0 -} - -.cardText__70151 { - padding-inline-start:8px} - -.noItemsCard__70151 { - background-color: var(--background-base-lower); - border: none; - color: var(--interactive-text-active); - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px; - margin-top: 16px; - padding: 16px 19px -} - -.theme-dark .noItemsIcon__70151 { - background: #4f545c -} - -.hr__70151 { - border-color: hsl(var(--primary-300-hsl)/.3); - border-style: solid; - border-width: 1px 0 0; - display: block; - height: 0; - width: 100% -} - -.theme-dark .hr__70151 { - border-color: hsl(var(--primary-400-hsl)/.3) -} - -.profileCustomizationSection__44061 { - position: relative -} - -.profileCustomizationSection__44061 p { - margin-top: 0 -} - -.baseLayout__44061 { - align-items: start; - display: grid; - gap: 32px; - grid-template-columns: 1fr; - position: relative -} - -.previewsContainer__44061 { - display: flex; - flex-direction: column; - gap: 24px; - max-width: 300px -} - -.previewTitle__44061 { - margin-bottom: 8px -} - -@media (min-width: 1024px) { - .baseLayout__44061 { - gap:36px; - grid-template-areas: "form preview"; - grid-template-columns: minmax(0,1fr) 300px - } - - .formContent__44061 { - grid-area: form - } - - .previewsContainer__44061 { - grid-area: preview; - position: sticky; - top: 24px - } -} - -.topSpacing__5273b { - margin-top: 24px -} - -.emptyState__5273b { - align-items: center; - display: flex; - flex-direction: column -} - -.emptyState__5273b,.image__5273b { -} - -.image__5273b { - height: 162px; - width: 186px -} - -.title_ace4f5 { - align-items: center; - display: flex; - margin-bottom: 8px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.viewFullProfileButton_ace4f5 { - margin-inline-start:auto} - -.disabled_ace4f5 { - opacity: .4; - pointer-events: none; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.customizationSection_ace4f5 { - border-bottom: 1px solid var(--border-subtle); - margin-bottom: 24px; - padding-bottom: 24px -} - -.customizationSection_ace4f5.hideDivider_ace4f5 { - border-bottom: none; - padding-bottom: 0 -} - -.customizationSection_ace4f5:last-child:not(.withDivider_ace4f5) { - border-bottom: none; - margin-bottom: 0; - padding-bottom: 0 -} - -.customizationSection_ace4f5.showBorder_ace4f5 { - padding-bottom: 12px -} - -.customizationSectionBackground_ace4f5 { - padding: 16px -} - -.sectionDescription_ace4f5 { - margin-bottom: 16px -} - -.errorMessage_ace4f5 { - margin-top: 8px -} - -.customizationSectionBorder_ace4f5 { - margin: -12px -12px 0 -} - -.bioTextArea__6a919 { - --channel-text-area-placeholder: var(--input-placeholder-text-default); - border-radius: 3px; - height: 136px; - transition: border-color .2s ease-in-out -} - -.bioTextArea__6a919:focus-within { - border-color: var(--text-link) -} - -.bioTextArea__6a919>div { - height: 100% -} - -.bioTextArea__6a919,.bioTextAreaContainer__6a919 { - background: var(--input-background-default) -} - -.bioTextAreaContainer__6a919 { - border: 1px solid var(--input-border-default) -} - -.bioTextAreaContainer__6a919:focus-within { - border-color: var(--text-link) -} - -.buttonsContainer__89253 { - display: flex; - flex-wrap: wrap -} - -.removeButton__89253 { - margin-inline-start:4px} - -.buttonHighlighted__89253 { - background: linear-gradient(95.07deg,var(--premium-tier-2-purple-for-gradients) 0,var(--premium-tier-2-purple-for-gradients-2) 49.96%,var(--premium-tier-2-pink-for-gradients) 95.93%) -} - -.overrideButtonsContainer__89253 { - display: flex; - flex-direction: column; - gap: 8px; - margin-top: 16px -} - -.buttonsContainer__28d5e { - display: flex; - flex-wrap: wrap -} - -.removeButton__28d5e { - margin-inline-start:4px} - -.buttonHighlighted__28d5e { - background: linear-gradient(95.07deg,var(--premium-tier-2-purple-for-gradients) 0,var(--premium-tier-2-purple-for-gradients-2) 49.96%,var(--premium-tier-2-pink-for-gradients) 95.93%) -} - -.newBadge__28d5e { - margin-inline-start:4px} - -.buttonsContainer__4962e { - display: flex; - flex-wrap: wrap; - gap: 4px -} - -.newBadge__4962e { - margin-inline-start:4px} - -.section__25879 { - display: flex; - flex-wrap: wrap; - gap: 4px -} - -.newBadge__25879 { - margin-inline-start:8px} - -.buttonsContainer_ffefd9 { - display: flex -} - -.removeButton_ffefd9 { - margin-inline-start:4px} - -.buttonHighlighted_ffefd9 { - background: linear-gradient(95.07deg,var(--premium-tier-2-purple-for-gradients) 0,var(--premium-tier-2-purple-for-gradients-2) 49.96%,var(--premium-tier-2-pink-for-gradients) 95.93%) -} - -.buttonsContainer_fd47ae { - display: flex; - flex-wrap: wrap -} - -.removeButton_fd47ae { - margin-inline-start:4px} - -.buttonHighlighted_fd47ae { - background: linear-gradient(95.07deg,var(--premium-tier-2-purple-for-gradients) 0,var(--premium-tier-2-purple-for-gradients-2) 49.96%,var(--premium-tier-2-pink-for-gradients) 95.93%) -} - -.newBadge_fd47ae { - align-items: center; - margin-inline-start:2px} - -.colorSwatch__2d060 { - cursor: pointer; - display: inline-block; - height: -moz-fit-content; - height: fit-content; - position: relative; - text-align: center -} - -.colorSwatch__2d060.disabled__2d060 { - cursor: not-allowed; - opacity: .3 -} - -.swatch__2d060 { - align-items: center; - border: 1px solid; - border-radius: 4px; - box-sizing: border-box; - display: flex; - height: 50px; - justify-content: center; - position: relative; - width: 69px -} - -.editPencilIcon__2d060 { - inset-inline-end: 4px; - position: absolute; - top: 4px -} - -.enable-forced-colors .swatch__2d060 { - forced-color-adjust: none -} - -.sectionContainer__04485 { - display: flex; - margin-top: 8px -} - -.newBadge__04485 { - background: var(--custom-premium-colors-premium-gradient-tier-2); - display: inline; - line-height: 18px; - margin-inline-start:2px} - -.resetButton__04485 { - margin-inline-start:4px;margin-top: calc((50px - var(--custom-button-button-sm-height))/2) -} - -.colorSwatchLabel__04485 { - margin-top: 4px -} - -.sparkles__04485 { - inset-inline-start: -10px; - pointer-events: none; - position: absolute; - top: -15px; - z-index: 1 -} - -.sparklesFlip__04485 { - transform: scaleX(-1) scaleY(-1) -} - -.sparkleContainer__04485 { - position: relative -} - -.sparkleContainer__04485:first-child { - margin-inline-end:16px} - -.displayNameStylesSection__17e9b { - margin-top: var(--space-24) -} - -.upsellOverlayContainer__0f7dc { - background: linear-gradient(var(--background-base-low),var(--background-base-low)) padding-box,var(--custom-premium-colors-premium-gradient-tier-2)-diagonal border-box; - border: 2px solid transparent; - border-radius: 8px; - padding: 16px; - position: relative -} - -.upsellOverlay__0f7dc { - align-items: center; - border-radius: 8px; - display: flex; - flex-direction: column; - inset-inline: 0; - bottom: 0; - gap: 16px; - justify-content: center; - padding: 16px 32px; - position: absolute; - text-align: center; - top: 0 -} - -.upsellImage__0f7dc { - height: 200px; - width: 240px -} - -.upsellTextContainer__0f7dc { - background: var(--custom-premium-colors-premium-gradient-tier-2); - border-radius: 8px; - display: flex; - flex-direction: column; - gap: 10px; - max-width: 240px; - padding: 16px 18px 18px -} - -.theme-dark .upsellOverlay__0f7dc { - background-color: var(--opacity-black-40) -} - -.theme-light .upsellOverlay__0f7dc { - background-color: var(--opacity-white-40) -} - -.sectionsContainer__722a8 { - flex: 1 -} - -.nitroWheel__722a8 { - height: 16px; - margin-inline:2px 4px;vertical-align: sub; - width: 16px -} - -.section__999b5 { - margin-top: 16px -} - -.guildSelectOptionIcon__999b5 { - border-radius: 4px -} - -.profilePreviewTitle__14af9 { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.nameplatePreviewPlaceholder__14af9 { - background: var(--background-mod-normal) -} - -.settingsPage_f131e9 { - display: flex; - flex-direction: column; - gap: var(--space-32); - min-width: 0; - width: 100% -} - -.container__8279f { - align-items: center; - background: linear-gradient(0deg,var(--opacity-black-20) 0,var(--opacity-black-20) 100%),linear-gradient(187deg,var(--premium-tier-0-header-gradient-1) -9.85%,var(--premium-tier-0-header-gradient-2) 35.44%,var(--premium-tier-0-header-gradient-3) 55.85%,var(--premium-tier-0-header-gradient-4) 100.07%,var(--premium-tier-0-header-gradient-5) 110.26%); - background-position: 50%; - background-size: cover; - border-radius: var(--radius-sm); - flex-direction: row; - margin: 24px 0; - padding-inline-end:24px} - -.container__8279f,.mainColumn__8279f { - display: flex; - justify-content: center -} - -.mainColumn__8279f { - color: var(--white); - flex: 1; - flex-direction: column; - margin: auto 0; - min-height: 96px; - padding-block:16px;padding-inline:4px 16px} - -.title__8279f { - margin-bottom: 4px -} - -.artContainer__8279f { - align-items: center; - align-self: stretch; - display: flex; - flex-basis: 154px; - height: 100px; - position: relative -} - -.art__8279f { - bottom: -9px; - height: 124px; - inset-inline-start: -10px; - -o-object-fit: contain; - object-fit: contain; - pointer-events: none; - position: absolute; - width: 154px -} - -@keyframes avatarAnimation_abf1df { - 0% { - transform: rotate(0deg) - } - - to { - transform: rotate(1turn) - } -} - -.full-motion .spinningAvatar_abf1df { - animation: avatarAnimation_abf1df 3s ease infinite -} - -.modal__038c3 { - width: 650px -} - -.modalContent__038c3 { - margin-bottom: 0; - padding-inline-start:0} - -.previewContainerParent__038c3 { - background: var(--background-base-lower); - border-start-start-radius: var(--radius-md); - margin-top: -8px; - overflow-y: clip; - padding: var(--space-32); - padding-bottom: 0 -} - -.previewContainer__038c3 { - margin-bottom: -8px; - text-align: start; - width: 240px -} - -.connectedAccountVanityMetadata_fffe42 { - margin-top: 4px -} - -.connectedAccountVanityMetadata_fffe42>strong { - font-weight: inherit -} - -.connectedAccountVanityMetadata_fffe42:not(:last-of-type) { - margin-inline-end:8px} - -.connectedAccountVanityMetadataTag_fffe42 { - background-color: var(--background-mod-subtle); - border-radius: 12px; - padding: 1px 8px -} - -.connectedAccountVanityMetadataItem_fffe42 { - align-items: center; - display: flex -} - -.connectedAccountVanityMetadataItemIcon_fffe42 { - height: 18px; - margin-inline-end:4px;width: 18px -} - -.paypalVerifiedTag_fffe42 { - background-color: var(--status-positive-background); - color: var(--white)!important -} - -.title__9a52c { - margin-bottom: 12px -} - -.container__9a52c { - border-top: 1px solid var(--border-strong); - display: flex; - flex-direction: column; - margin-top: 20px; - padding-top: 24px; - width: 100% -} - -.activityRow__9a52c { - align-items: stretch; - display: flex; - flex-direction: row; - gap: 16px; - padding: 16px 0 -} - -.activitySettings__9a52c { - display: flex; - flex: 1; - flex-direction: column; - gap: var(--space-16); - justify-content: center -} - -.connectionIcon__9a52c { - height: 32px; - width: 32px -} - -.divider__9a52c { - border-bottom: 1px solid var(--border-strong); - width: 100% -} - -.visibilitySwitch__9a52c { - margin-bottom: 0 -} - -.additionalDetailsSwitch__9a52c { - margin-bottom: 0; - margin-top: 8px -} - -.metadataContainer__9a52c { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px; - width: 100% -} - -.metadataContainer__9a52c .metadataItem__9a52c { - margin: 0; - padding: 0 -} - -.metadataContainer__9a52c .metadataItem__9a52c:not(:last-of-type) { - margin-inline-end:0} - -.metadataRefreshButton__9a52c { - justify-self: flex-end; - margin-inline-start:16px} - -.dot__9a52c { - background-color: var(--text-default); - border-radius: 4px; - height: 4px; - margin: 0 4px; - width: 4px -} - -.container__14992 { - display: flex; - flex-direction: row; - margin-bottom: var(--space-12) -} - -.button__14992 { - align-content: center; - background-color: var(--background-base-lowest); - border-radius: var(--radius-sm); - color: var(--interactive-text-default); - cursor: pointer; - justify-items: center; - padding: var(--space-8) -} - -.button__14992:hover { - color: var(--interactive-text-active) -} - -.select__14992 { - cursor: pointer; - display: flex; - height: 40px; - margin-inline-start:var(--space-8);overflow: hidden; - position: relative -} - -.presetImage__14992,.select__14992 { - border-radius: var(--radius-sm); - flex: 1 -} - -.presetImage__14992 { - background-size: cover -} - -.presetLabel__14992 { - align-self: center; - inset: 0; - position: absolute; - text-align: center; - text-transform: uppercase -} - -.tryItOutSection__9d295 { - margin-top: 40px; - width: -moz-fit-content; - width: fit-content -} - -.tryItOutLayout__9d295 { - gap: 30px -} - -.description__9d295 { - margin-bottom: 24px; - margin-top: 8px -} - -.premiumPerksList__9d295 { - list-style: disc; - padding-inline-start:24px} - -.premiumSubscribeButton__9d295 { - margin-top: 24px; - width: 100% -} - -.premiumIcon__9d295 { - height: 16px; - margin-inline-end:-2px} - -.editor__9d295 { - min-width: 315px -} - -.customizationSection__9d295 { - margin-bottom: 16px; - padding-bottom: 16px -} - -.premiumTier2Divider__9d295 { - background-image: linear-gradient(90deg,var(--premium-tier-2-pink-for-gradients) 0,var(--premium-tier-2-purple-for-gradients) 50%,var(--premium-tier-2-pink-for-gradients) 100%); - height: 2px; - margin-top: 68px; - margin-inline:-24px} - -.titleIcon__9d295 { - color: var(--text-strong) -} - -.floatingUpsell__9d295 { - background: var(--background-mod-normal); - margin: 0; - margin-top: var(--space-24) -} - -.container_e7c728 { - flex: 1; - justify-content: space-between -} - -.container_e7c728,.guildPrefixContainer_e7c728 { - align-items: center; - display: flex; - min-width: 0 -} - -.guildPrefixContainer_e7c728 { - justify-content: center -} - -.guildPrefixIcon_e7c728 { - flex: 1 0 auto -} - -.details_e7c728 { - display: flex; - flex: 1 1 auto; - flex-direction: column; - min-width: 0; - padding-inline-start:8px} - -.guildName_e7c728 { - color: var(--text-strong); - font-size: 16px; - font-weight: var(--font-weight-semibold); - line-height: 20px; - margin-inline-end:4px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: 100% -} - -.tag_e7c728 { - background-color: var(--background-surface-high); - border: 1px solid var(--border-strong); - flex: 0; - padding: 4px 8px -} - -.badge_e7c728 { - margin-inline-end:2px;vertical-align: top -} - -.tagContainer_e7c728 { - flex: 0 0 auto -} - -.subtitle__105ba { - margin-bottom: 16px -} - -.select__105ba,.selectPopout__105ba { - gap: 8px -} - -.newBadge__105ba { - margin-inline-start:var(--space-4)} - -.displayNameStylesSection_e60bc0 { - margin-top: var(--space-24) -} - -.newBadge__17510 { - margin-inline-start:4px} - -.sectionsContainer_def11f { - flex: 1 -} - -@media (min-width: 937px) { - .sectionsContainer_def11f { - box-sizing:border-box; - max-width: 337px - } -} - -.floatingNitroUpsell__39749 { - inset: auto; - bottom: var(--space-32); - inset-inline: var(--space-24); - max-width: 740px; - padding: var(--space-16); - position: sticky; - z-index: 100 -} - -.nameplatePreviewPlaceholder__39749 { - background: var(--background-mod-normal) -} - -.tryItOutButton__39749 { - border: 1px solid var(--border-subtle); - border-radius: calc(var(--radius-sm) + 1px) -} - -.divider_d6f9e9 { - margin-bottom: 24px; - padding-bottom: 4px -} - -.divider_d6f9e9,.tabBar_d6f9e9 { - border-bottom: 1px solid var(--border-subtle) -} - -.tabBar_d6f9e9 { - margin-bottom: 16px; - margin-top: 0 -} - -.tabBarItem_d6f9e9 { - padding-bottom: 16px -} - -.guildTabBarItem_d6f9e9 { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px -} - -.clickable__2debe { - cursor: pointer; - display: flex -} - -.info__2debe { - padding: 8px 10px; - text-transform: capitalize; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.line__2debe { - display: inline-block; - width: 100% -} - -.appArch__2debe,.os__2debe,.versionHash__2debe { - text-transform: none -} - -.compact__2debe { - flex-direction: column; - gap: var(--space-4); - width: -moz-fit-content; - width: fit-content -} - -.compactInfo__2debe>:not(:last-child):after { - color: var(--text-muted); - content: "•"; - font-size: 10px; - margin: 0 var(--space-4) -} - -.compact__2debe .versionHash__2debe { - flex: 1; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.socialLinks__7df54 { - font-size: 12px; - line-height: 16px; - padding: 8px 10px -} - -.link__7df54 { - color: var(--interactive-text-default); - margin-inline-end:8px;padding: 0 2px -} - -.link__7df54:hover { - color: var(--interactive-text-hover) -} - -.link__7df54:active { - color: var(--interactive-text-active) -} - -.foreground__7df54 { - transition: color .15s ease -} - -.preview__5d148 { - background-color: var(--background-base-lower); - padding: 16px -} - -.previewMessage__5d148 { - border-radius: var(--radius-xs); - margin-top: 16px; - padding: 8px -} - -.previewHeader__5d148 { - display: flex -} - -.previewAvatars__5d148 { - display: grid; - gap: 8px; - grid-auto-flow: column; - margin-inline-start:8px} - -.ttsSliderMarker__5d148 { - text-transform: uppercase -} - -.syncProfileThemeWithUserTheme__5d148 { - scroll-margin-top: 125px -} - -.reducedMotion__5d148 { - scroll-margin-top: 145px -} - -.custom-theme-background .preview__5d148 { - background: var(--background-gradient-chat-preview) -} - -.appearanceUpsell__5d148 { - padding-top: 24px -} - -.avatarBackground_eb2508 { - align-items: center; - background-color: var(--background-mod-strong); - border-radius: 50%; - height: 92px; - justify-content: center; - width: 92px -} - -.avatar_eb2508,.avatarBackground_eb2508 { - display: flex; - position: relative -} - -.indicator_eb2508 { - align-items: center; - display: flex; - justify-content: center; - position: absolute -} - -.container_d878e7 { - border: none; - display: flex; - flex-direction: row; - gap: var(--space-16); - margin-bottom: var(--space-32); - padding-top: var(--space-8); - width: 100%; - --__card-accent-color: transparent -} - -.profile_d878e7 { - justify-content: center; - margin-inline-start:var(--space-16)} - -.status_d878e7 { - row-gap: var(--space-24) -} - -.status_d878e7,.title_d878e7 { - display: flex; - flex-direction: column; - width: 100% -} - -.title_d878e7 { - row-gap: var(--space-8) -} - -.health_d878e7 { - display: flex; - position: relative; - width: 100% -} - -.line_d878e7 { - background-color: var(--background-mod-strong); - height: 4px; - position: absolute; - top: var(--space-12); - width: 100% -} - -.statusOption_d878e7 { - align-items: center; - display: flex; - flex-direction: column; - position: absolute; - row-gap: var(--space-8); - text-align: center; - width: 15% -} - -.statusOption_d878e7:nth-of-type(2) { - align-items: flex-start; - inset-inline-start: 0; - text-align: start -} - -.statusOption_d878e7:nth-of-type(3) { - inset-inline-start: 25%; - transform: translate(-50%) -} - -.statusOption_d878e7:nth-of-type(4) { - inset-inline-start: 50%; - transform: translate(-50%) -} - -.statusOption_d878e7:nth-of-type(5) { - inset-inline-start: 75%; - transform: translate(-50%) -} - -.statusOption_d878e7:nth-of-type(6) { - align-items: flex-end; - inset-inline-start: 100%; - text-align: end; - transform: translate(-100%) -} - -.statusLabel_d878e7 { - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis -} - -.marker_d878e7 { - align-items: center; - background-color: var(--background-base-low); - border-radius: 50%; - display: flex; - height: 28px; - justify-content: center; - width: 28px; - z-index: 1 -} - -.marker_d878e7 .empty_d878e7 { - background-color: var(--background-mod-strong); - border-radius: 50%; - height: 16px; - width: 16px -} - -.dropdown_edf232 { - background-color: var(--background-base-lower); - border-radius: var(--radius-md); - margin-bottom: 8px -} - -.header_edf232 { - box-sizing: border-box; - cursor: pointer; - flex-direction: row; - padding: 8px; - width: 100% -} - -.header_edf232,.headerIconWrapper_edf232 { - align-items: center; - display: flex -} - -.headerIconWrapper_edf232 { - background-color: var(--background-base-lowest); - border-radius: var(--radius-xl); - height: 40px; - justify-content: center; - margin: 5px; - width: 40px -} - -.caret_edf232 { - color: var(--interactive-text-default); - justify-self: flex-end; - margin: 8px -} - -.title_edf232 { - color: var(--text-muted); - margin-inline:10px auto} - -.items_edf232,.title_edf232 { - display: flex; - flex-direction: column -} - -.items_edf232 { - align-items: center; - color: var(--white); - gap: 8px; - padding: 8px -} - -.itemDetail_edf232,.items_edf232 { - box-sizing: border-box -} - -.itemDetail_edf232 { - background-color: var(--background-mod-strong); - border-radius: var(--radius-md); - cursor: pointer; - height: -moz-max-content; - height: max-content; - margin: 8px 4px 0; - padding: var(--space-16); - width: 100% -} - -.itemDetailNew_edf232 { - border: 1px solid var(--brand-500) -} - -.descriptionContainer_edf232 { - display: flex; - flex-direction: column; - gap: 8px -} - -.incidentTimeBase_edf232 { - border-radius: var(--radius-md); - padding: 3px 6px; - width: -moz-max-content; - width: max-content -} - -.timestamp_edf232 { - background-color: var(--background-base-lowest) -} - -.newBadge_edf232,.timestamp_edf232 { -} - -.newBadge_edf232 { - background-color: var(--brand-500); - color: var(--white) -} - -.expirationDate_edf232 { - color: var(--channels-default) -} - -.content_edf232 { - border: 1px solid var(--border-subtle); - border-radius: var(--radius-md); - min-height: 100px; - min-width: 200px; - padding: 4px -} - -.paginationButton_edf232 { - background-color: var(--background-mod-subtle); - border-end-end-radius: var(--radius-md); - border-end-start-radius: var(--radius-md); - color: var(--white); - font-size: 13px; - margin-top: -8px; - padding: 8px 64px -} - -.emptyState_edf232 { - align-items: center; - display: flex; - flex-direction: column; - gap: 8px; - justify-content: center; - margin-bottom: 12px; - margin-top: 12px -} - -.emptyStateSubtext_edf232,.emptyStateText_edf232 { - margin: 0 -} - -.iconContainer_edf232 { - margin-bottom: var(--space-16); - position: relative -} - -.iconBackground_edf232 { - background-color: var(--background-base-lowest); - border-radius: 36px; - display: inline-block; - padding: 10px -} - -.icon_edf232 { - color: var(--text-default); - height: 42px; - margin: 4px 2px 0; - width: 42px -} - -.stars_edf232 { - inset-inline-start: -18px; - position: absolute -} - -.container_d74ef8,.emptyPage_d74ef8 { - display: flex; - flex-direction: column -} - -.emptyPage_d74ef8 { - align-items: center; - gap: 8px; - justify-content: center -} - -div.nagbar_d74ef8 { - border-radius: 0 0 10px 10px; - margin-top: -24px -} - -.tabBar_f8303a { - border-bottom: 1px solid var(--border-subtle); - max-width: calc(100% - 60px) -} - -.tabBar_f8303a.vertical_f8303a { - border-bottom: none; - gap: var(--space-16) -} - -.tabBarPanel_f8303a { - margin-top: var(--space-24) -} - -.tabBarPanel_f8303a.vertical_f8303a { - flex-grow: 1; - margin-top: 0 -} - -.tab_f8303a { - align-items: center; - display: flex; - flex-shrink: 1; - gap: var(--space-8); - padding-bottom: var(--space-12) -} - -.tab_f8303a.vertical_f8303a { - border-inline-start:1px solid var(--border-subtle);border-radius: 0 -} - -.tab_f8303a.vertical_f8303a.selected_f8303a { - border-inline-start:2px solid var(--text-brand);color: var(--text-brand); - margin-inline-start:-1px} - -.tabbedSettingsContainer_f8303a { - display: flex; - flex-direction: column -} - -.tabbedSettingsContainer_f8303a.vertical_f8303a { - align-items: flex-start; - flex-direction: row; - gap: var(--space-48) -} - -.header__6c75d { - align-items: center; - display: flex; - flex-direction: column; - justify-content: space-between; - padding-bottom: 24px; - padding-top: 24px -} - -.modalCloseButton__6c75d { - inset-inline-end: 16px; - position: absolute; - top: 16px -} - -.content__6c75d { - align-items: center; - display: flex; - justify-content: space-around; - margin-bottom: 8px -} - -.icon__6c75d { - margin-inline-end:16px} - -.input__6c75d { - margin-top: 8px -} - -.settings__6c75d { - margin-top: 32px -} - -.credentialList__6c75d,.description__6c75d { - margin-bottom: 8px -} - -.credentialList__6c75d { - background-color: var(--background-base-lower); - border-radius: 4px -} - -.credentialItem__6c75d { - align-items: center; - border-top: 1px solid var(--border-subtle); - box-sizing: border-box; - display: flex; - justify-content: space-between; - overflow: hidden; - padding: 8px; - padding-inline-start:32px;width: 100% -} - -.credentialItem__6c75d:not(:last-child) { - margin-bottom: 4px -} - -.credentialOptions__6c75d { - height: 24px -} - -.footer__6c75d { - justify-content: space-between -} - -.spacing_a959b1 { - margin-bottom: 20px -} - -.error_a959b1 { - margin-top: 8px -} - -.card_a959b1 { - padding: 10px -} - -.warning_a959b1 { - color: var(--white) -} - -.card__6aefd,.spacing__6aefd { - margin-bottom: 20px -} - -.card__6aefd { - padding: 10px -} - -.error__6aefd { - margin-top: 8px -} - -.lockIcon__2666b { - filter: saturate(var(--saturation-factor,1)) -} - -.btn__2666b { - cursor: pointer; - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 16px; - padding: 10px 20px; - position: relative; - text-align: center; - transition: background-color .2s ease -} - -.btn__2666b+.btn__2666b { - margin-inline-start:8px} - -.btn__2666b:disabled { - cursor: not-allowed; - opacity: .3 -} - -.btn__2666b.btnPrimary__2666b { - background-color: var(--brand-500); - border-radius: 3px; - color: var(--white); - padding: 8px 16px -} - -.btn__2666b.btnPrimary__2666b:hover:not(:disabled) { - background-color: var(--brand-600) -} - -.userSettingsSecurityImage__2666b { - opacity: 1; - transition: opacity .3s ease; - width: 100% -} - -@media (max-width: 840px) { - .userSettingsSecurityImage__2666b { - opacity:0 - } -} - -.userSettingsSecurity__2666b .isEnabled__2666b { - color: var(--text-feedback-positive) -} - -.userSettingsSecurity__2666b .lockIcon__2666b { - height: 14px; - margin-inline-end:4px;position: relative; - top: 2px; - width: 10px -} - -.userSettingsSecurity__2666b .checkboxGroup__2666b .code__2666b { - cursor: text; - font-family: var(--font-code); - font-size: 14px; - line-height: 28px; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.phoneRevealer__2666b { - margin-inline-start:4px} - -.changePasswordButton__2666b { - margin-bottom: 28px -} - -.inlineNoticeText__2666b { - margin-bottom: var(--space-8) -} - -.backupCode__2666b { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-8); - outline: none -} - -.userProfileCustomizationSection__2666b { - margin-top: 40px; - position: relative -} - -.nitroIcon__2666b { - color: var(--text-strong); - margin-inline-end:6px;position: relative; - top: -3px -} - -.customizationTitle__2666b { - align-items: center; - display: flex -} - -.noticeTextButton__2666b { - font-weight: 700; - text-decoration: underline -} - -.noticeTextButton__2666b,.userSettingsSecurity__2666b .backupCode__2666b { - color: var(--text-default) -} - -.tabPanel__2666b { - margin-top: var(--space-32) -} - -.spacer__5ecaa { - width: 8px -} - -.input__5ecaa { - background-color: var(--input-background-default); - border: 1px solid var(--input-border-default); - border-radius: var(--radius-sm); - box-sizing: border-box; - color: var(--text-strong); - font-size: 17px; - height: 48px; - margin-inline:var(--space-4);text-align: center; - width: 48px -} - -.enable-forced-colors .input__5ecaa { - border: 1px solid ButtonText -} - -.enable-forced-colors .input__5ecaa:focus { - border-color: Highlight -} - -.phoneField_a0c54f { - display: grid; - gap: var(--space-8); - grid-template-columns: 5fr 6fr; - position: relative -} - -.phoneVerificationModal_db41ea { - min-height: 252px; - padding: 16px; - position: relative; - width: 440px -} - -.phoneVerificationModal_db41ea .animationContainer_db41ea { - position: absolute; - top: -150px; - inset-inline: 130px -} - -.phoneVerificationModal_db41ea .title_db41ea { - color: var(--text-strong); - font-size: 20px; - font-weight: var(--font-weight-bold); - margin-top: auto; - padding-top: 80px -} - -.phoneVerificationModal_db41ea .description_db41ea { - color: var(--text-default); - font-size: 16px; - line-height: 20px; - text-align: center -} - -.phoneVerificationModal_db41ea .description_db41ea.error_db41ea { - color: var(--text-feedback-critical) -} - -.phoneVerificationModal_db41ea .field_db41ea { - align-self: stretch -} - -.description_d286c5 { - margin-bottom: 16px -} - -.buttonContainer_d286c5 { - display: flex; - flex-direction: row -} - -.accountProfileCard__1fed1 { - background-color: var(--background-base-lowest); - border-radius: var(--radius-sm); - overflow: hidden; - position: relative -} - -.userInfo__1fed1 { - box-sizing: border-box; - display: flex; - justify-content: space-between; - padding-block:16px 0;padding-inline:120px 16px;width: 100% -} - -.accountProfileCard__1fed1 .avatar__1fed1 { - background-color: var(--background-base-lowest); - box-sizing: content-box; - inset-inline-start: 22px; - position: absolute; - top: 82px -} - -.profileCardUsernameRow__1fed1 { - display: flex -} - -.overflowMenuButton__1fed1 { - cursor: pointer; - margin-inline-start:8px} - -.overflowMenuIcon__1fed1 { - color: var(--interactive-text-default) -} - -.userTag__1fed1 { - color: var(--text-strong); - font-family: var(--font-display); - font-size: 20px; - font-weight: var(--font-weight-semibold); - line-height: 24px; - margin-bottom: 6px -} - -.discriminator__1fed1 { - color: var(--text-default) -} - -.background__1fed1 { - background-color: var(--background-base-lower); - border-radius: var(--radius-sm); - margin: 8px 16px 16px; - padding: 16px -} - -.profile__1fed1 { - display: flex; - flex-direction: row -} - -.avatarUploaderInner__1fed1 { - height: 80px; - width: 80px -} - -.details__1fed1 { - display: flex; - flex-direction: column; - margin-bottom: auto; - margin-top: auto; - margin-inline-start:16px;overflow: hidden -} - -.detailsInner__1fed1 { - color: var(--text-strong); - overflow: hidden; - text-overflow: ellipsis -} - -.badgeList__1fed1 { - background-color: var(--background-base-lower); - border: none; - border-radius: var(--radius-sm); - gap: 4px; - max-width: 228px; - padding: 2px 4px -} - -.badgeList__1fed1:empty { - height: 28px -} - -.badge__1fed1 { - height: 24px; - width: 24px -} - -.fieldList__1fed1 { - background-color: var(--background-base-lower); - border-radius: var(--radius-sm) -} - -.field__1fed1 { - display: flex; - flex-direction: row; - justify-content: space-between -} - -.fieldSpacer__1fed1 { - margin-top: 24px -} - -.fieldSpacerBottom__1fed1 { - margin-bottom: 24px -} - -.fieldTitle__1fed1 { - margin-bottom: 4px -} - -.fieldButtonList__1fed1 { - display: flex -} - -.fieldButton__1fed1,.pomeloWarning__1fed1 { - margin-bottom: auto; - margin-top: auto -} - -.pomeloWarning__1fed1 { - padding-inline-end:10px} - -.removeButton__1fed1 { - margin-inline-end:16px} - -.menu__1fed1,.uploadButton__1fed1 { - align-items: center; - display: flex; - flex-shrink: 0; - justify-content: center; - margin-inline-start:auto} - -.menu__1fed1 { - color: var(--interactive-text-default); - cursor: pointer; - margin-inline-end:16px} - -.avatarError__1fed1 { - margin-top: 8px -} - -.constrainedRow__1fed1 { - display: flex; - flex: 1 1 auto; - margin-inline-end:16px;overflow: hidden -} - -.usernameRow__1fed1 { - overflow: hidden -} - -.usernameInnerRow__1fed1 { - color: var(--text-strong); - overflow: hidden; - text-overflow: ellipsis -} - -.enable-forced-colors .overflowMenuIcon__1fed1 { - background-color: ButtonFace; - border: 1px solid ButtonFace; - color: ButtonText -} - -.enable-forced-colors .overflowMenuIcon__1fed1:focus,.enable-forced-colors .overflowMenuIcon__1fed1:hover { - border-color: ButtonText -} - -.enable-forced-colors .accountProfileCard__1fed1 { - border: 1px solid CanvasText -} - -.textRevealer__1fed1 { - align-items: center; - display: flex; - gap: 4px -} - -.guildRow__7db08 { - align-items: center; - display: flex; - gap: 16px -} - -.guildsList__7db08 { - display: flex; - flex-direction: column; - gap: 24px -} - -.guildRowTextContainer__7db08 { - flex: 1; - margin-inline-end:32px;overflow: hidden -} - -.memberDetailsContainer__7db08 { - align-items: center; - display: flex; - gap: 4px -} - -.infoDot__7db08 { - border-radius: 50%; - flex-shrink: 0; - height: 8px; - width: 8px -} - -.memberCountDot__7db08 { - background-color: var(--primary-300) -} - -.controlsContainer__7db08 { - align-items: center; - display: flex; - gap: var(--space-4); - justify-content: space-between -} - -.select__7db08 { - max-width: 180px -} - -.showAllButton__7db08 { - align-items: center; - cursor: pointer; - display: flex; - gap: 16px; - padding: 6px 0 -} - -.showAllIcon__7db08 { - margin: 0 12px -} - -.wrapper__7db08 { - display: flex; - flex-direction: column; - gap: 24px -} - -.wrapperCompact__7db08 { - gap: 16px -} - -.guildRowWrapper__7db08 { - margin: 0 -} - -.noResultsContainer__7db08 { - align-items: center; - display: flex; - height: 736px; - justify-content: center -} - -.noResultsText__7db08 { - color: var(--text-muted); - text-transform: capitalize -} - -.headerContainer__7db08 { - background-color: var(--background-base-low); - display: flex; - flex-direction: column; - gap: 16px; - padding-top: 16px; - position: sticky; - top: 0; - z-index: 1 -} - -.defaultSetting__6de8b { - margin-bottom: 16px -} - -.formTitle__6de8b { - margin-bottom: 8px -} - -.switchContainer__6de8b { - background-color: var(--background-surface-high); - border-radius: var(--radius-lg); - padding: 16px -} - -.myServersContainer__6de8b { - display: flex; - flex-direction: column -} - -.myServersHeaderContainer__6de8b { - align-items: center; - display: flex; - justify-content: space-between -} - -.myServersTitle__6de8b { - margin-bottom: 0 -} - -.myServersIgnoredWarning__6de8b { - width: -moz-fit-content; - width: fit-content -} - -.settingSwitch__6de8b { - display: flex; - gap: 16px -} - -.settingSwitchLabel__6de8b { - display: flex; - flex: 1; - flex-direction: column; - gap: 2px -} - -.privacyTermsCard__6de8b { - padding: var(--space-20) -} - -.inputWrapper__51422 { - flex-grow: 1; - margin-inline-end:10px;margin-bottom: 20px; - width: 100% -} - -.tryItOutButtons__6b52d { - display: flex; - gap: 12px -} - -.selectionGroup__6b52d { - padding: 0 -} - -.divider__6b52d { - margin-bottom: 24px; - margin-top: 32px -} - -.pageActions__2d021 { - border-radius: 3px; - display: flex; - flex: 0 0 auto; - justify-content: center; - padding: 20px 0 -} - -.buttons__2d021 { - align-items: center; - border: 1px solid var(--border-subtle); - border-radius: var(--radius-md); - display: flex; - gap: var(--space-8); - padding: var(--space-4) -} - -.payment_e9cb00 { - transition: background-color .2s ease; - width: 100% -} - -.payment_e9cb00:hover,.payment_e9cb00[data-expanded=true] { - background-color: var(--background-mod-muted) -} - -.expandInfo_e9cb00 { - padding: 20px -} - -.summaryInfo_e9cb00 { - color: var(--text-strong); - cursor: pointer; - padding: var(--space-16); - transition: background-color .2s ease -} - -.summaryInfo_e9cb00[data-expanded=true] { - background-color: var(--background-mod-muted) -} - -.summaryInfo_e9cb00:hover { - background-color: var(--background-mod-subtle) -} - -.expandedInfo_e9cb00 { - padding: var(--space-16) -} - -.paymentHeader_e9cb00 { - border-top: 1px solid; - border-color: var(--primary-500); - font-size: 16px; - font-weight: var(--font-weight-semibold); - margin-bottom: 8px; - margin-top: 20px; - padding-top: 20px -} - -.paymentHeader_e9cb00:first-child { - border-top: none; - margin-top: 0; - padding-top: 0 -} - -.paymentText_e9cb00 { - color: var(--text-default); - font-size: 14px; - line-height: 1.4 -} - -.paymentBreakdown_e9cb00 { - padding-top: 10px -} - -.paymentDetail_e9cb00 { - margin-top: 12px -} - -.paymentDetail_e9cb00:first-child { - margin-top: 20px -} - -.additionalInformationButtonContainer_e9cb00 { - margin-top: 16px -} - -.guildProductDetail_e9cb00 { -} - -.date_e9cb00 { - width: 20% -} - -.amount_e9cb00 { - align-content: center; - display: flex; - justify-content: flex-end -} - -.statusTag_e9cb00 { - border-radius: 3px; - color: var(--white); - display: block; - font-size: 11px; - font-weight: var(--font-weight-medium); - height: 14px; - letter-spacing: .4px; - line-height: 14px; - margin-inline-end:20px;max-width: 180px; - overflow: hidden; - padding: 0 4px; - text-overflow: ellipsis; - text-transform: uppercase; - white-space: nowrap -} - -.statusTagGreen_e9cb00 { - background-color: var(--green-360) -} - -.statusTagRed_e9cb00 { - background-color: var(--background-feedback-critical) -} - -.statusTagGrey_e9cb00 { - background-color: var(--primary-400) -} - -.statusTagYellow_e9cb00 { - background-color: var(--yellow-360) -} - -.price_e9cb00 { - white-space: nowrap -} - -.description_e9cb00 { - align-items: center; - display: flex; - flex: 1 1 50%; - margin-inline-end:8px} - -.descriptionIcon_e9cb00 { - background-position: 50%; - background-repeat: no-repeat; - flex-shrink: 0; - margin-inline-end:10px} - -.guildDescriptionIcon_e9cb00 { - background-color: var(--background-mod-muted) -} - -.shopIcon_e9cb00 { - height: 20px; - margin-inline-end:10px;width: 20px -} - -.expand_e9cb00 { - height: 24px; - margin-inline-start:20px;width: 24px -} - -.downloadInvoice_e9cb00 { - font-size: 14px; - margin-top: 12px -} - -.downloadRefundInvoice_e9cb00 { - margin-inline-start:14px} - -.refundSubHeader_e9cb00 { - font-size: 12px; - font-weight: var(--font-weight-semibold); - margin: 20px 0 8px; - text-transform: uppercase -} - -.refundSelect_e9cb00 { - flex: 1; - margin-inline-end:12px} - -.refundCriteria_e9cb00 { - margin-inline-end:32px} - -.refundCriteriaIcon_e9cb00 { - height: 16px; - margin-inline-end:8px;width: 16px -} - -.giftIcon_e9cb00 { - color: var(--text-default); - height: 16px; - margin-inline-start:10px;width: 16px -} - -@media (max-width: 800px) { - .refundCriteria_e9cb00 { - margin-inline-end:16px - } -} - -.compact_e9cb00 .amount_e9cb00 { - align-items: flex-end; - flex-direction: column-reverse; - justify-content: space-between; - width: 20% -} - -.compact_e9cb00 .summaryInfo_e9cb00 { - padding: 16px 20px -} - -.compact_e9cb00 .amount_e9cb00,.compact_e9cb00 .date_e9cb00,.compact_e9cb00 .description_e9cb00 { - text-transform: unset -} - -.compact_e9cb00 .date_e9cb00 { - opacity: .6 -} - -.compact_e9cb00 .descriptionIcon_e9cb00 { - height: 40px; - width: 40px -} - -.compact_e9cb00 .statusTag_e9cb00 { - margin: 8px 0 0 -} - -.compact_e9cb00 .expand_e9cb00 { - margin-inline-start:8px} - -.compact_e9cb00 .date_e9cb00 { - font-size: 12px; - line-height: 16px; - width: unset -} - -.compact_e9cb00 .description_e9cb00 { - flex: 1 1 55%; - text-transform: unset; - width: unset -} - -.refundActions_e9cb00 { - align-items: flex-end; - display: flex; - justify-content: space-between -} - -.refundIconContainer_e9cb00,.refundRules_e9cb00 { - display: flex -} - -.copiableContainer_e9cb00 { - cursor: pointer -} - -.paymentInfo_e9cb00 { - display: flex; - justify-content: space-between -} - -.divider_e9cb00 { - border-top: 1px solid var(--interactive-background-hover); - margin-bottom: 16px; - margin-top: 16px; - width: 100% -} - -.sectionDivider_e9cb00 { - margin-bottom: 20px; - margin-top: 20px -} - -.guildProductBenefits_e9cb00 { - background-color: var(--background-base-lower); - border-radius: 8px; - padding: 16px -} - -.guildProductBenefitLabel_e9cb00 { - margin-bottom: 12px; - text-transform: uppercase -} - -.warningBlock_e9cb00 { - align-items: center; - overflow: auto; - width: 100% -} - -.descriptionWrapper__43963 { - margin-inline-start:14px} - -.subText__43963 { - margin-top: 4px -} - -.indicator__43963 { - align-items: center; - border-radius: 3px; - display: flex; - font-size: 11px; - font-weight: var(--font-weight-bold); - height: 14px; - letter-spacing: .4px; - margin-inline-start:8px;padding: 0 4px; - text-align: center; - text-transform: uppercase -} - -.defaultIndicator__43963 { - background-color: var(--primary-500) -} - -.defaultIndicator__43963,.premiumIndicator__43963 { - color: var(--white) -} - -.premiumIndicator__43963 { - background-color: var(--brand-500) -} - -.invalidIndicator__43963 { - background-color: var(--red-400); - color: var(--white) -} - -.paymentPane__01014 { - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - color: var(--text-strong); - overflow: hidden -} - -.paymentRow__01014 { - cursor: default -} - -.paymentRowHeader__01014 { - font-size: 12px; - font-weight: var(--font-weight-semibold); - margin-inline-end:44px;padding: 20px -} - -.paymentRowHeaderDescription__01014 { - display: flex; - flex: 1 1 50%; - margin-inline-start:8px} - -.tab__01014 { - font-size: 14px; - font-weight: var(--font-weight-semibold); - margin-inline-start:12px;padding-bottom: 12px -} - -.tab__01014:first-child { - margin-inline-start:0} - -.externalRow__01014 { - border-radius: 5px; - display: flex; - flex-direction: column; - margin-bottom: 20px; - padding: 20px -} - -.externalRowHeader__01014 { - color: var(--text-default); - font-size: 16px -} - -.externalRowBody__01014 { - color: var(--text-muted); - font-size: 14px; - line-height: 18px; - margin-top: 8px -} - -.bottomDivider__01014 { - border-bottom: 1px solid var(--border-subtle) -} - -.bottomDivider__01014:last-child { - border-bottom: none -} - -.verticalFit__01014 { - display: flex; - flex: 1 1 auto; - flex-direction: column; - overflow: hidden -} - -.formError_f0c2ea { - margin-bottom: 20px -} - -.card_f0c2ea { - padding: 20px -} - -.formActions_f0c2ea { - -moz-column-gap: 2px; - column-gap: 2px; - display: flex; - flex-wrap: wrap; - row-gap: 10px -} - -.addressSection_f0c2ea,.formActions_f0c2ea { - margin-top: 20px -} - -.defaultSection_f0c2ea { - margin: var(--space-12) 0 -} - -.defaultCheckboxLabel_f0c2ea { - margin-inline-start:4px} - -.sectionHeader_f0c2ea { - color: var(--text-muted); - margin-bottom: 10px -} - -.subText_f0c2ea { - color: var(--text-muted); - font-size: 14px; - margin-top: 20px -} - -.errorSubText_f0c2ea { - color: var(--red-400) -} - -.disabledTooltipWrapper_f0c2ea { - position: relative -} - -.disabledTooltipTarget_f0c2ea { - cursor: not-allowed; - inset: 0; - position: absolute; - z-index: 1 -} - -.paymentSourceRow__0eeee { - justify-content: space-between; - margin: 16px 0 -} - -.header__0eeee,.paymentSourceRow__0eeee { - align-items: center; - display: flex -} - -.subText__0eeee { - color: var(--primary-400); - font-size: 14px; - margin-top: 4px -} - -.footer__0eeee { - display: flex; - justify-content: flex-end; - margin-top: 20px -} - -.footerContent__0eeee { - flex: 1 -} - -.titleContainer__0eeee { - align-items: center; - display: flex -} - -.lockIcon__0eeee { - margin-inline-end:var(--space-4)} - -.syncing_a706ba { - align-items: center; - display: flex; - height: 300px; - justify-content: center -} - -.paymentHistory_a706ba { - margin-top: 40px -} - -.codeRedemptionRedirect_a706ba { - margin-top: var(--space-32) -} - -.wrapper__02fc8 { - position: relative -} - -.inner__02fc8 { - background-color: transparent; - background-position: 50%; - background-repeat: no-repeat; - background-size: 28px 28px; - border-radius: var(--radius-sm); - height: 44px; - justify-content: center; - width: 44px -} - -.inner__02fc8:hover { - background-color: #99aab54d; - border-color: transparent; - cursor: pointer -} - -.inner__02fc8:disabled { - opacity: .3; - pointer-events: none -} - -.ellipsis__02fc8 { - align-items: center; - display: flex; - inset: 0; - justify-content: center; - position: absolute -} - -.spinnerItem__02fc8 { - background-color: #4f545c -} - -.enable-forced-colors .inner__02fc8 { - border: 1px solid ButtonFace -} - -.enable-forced-colors .inner__02fc8:focus,.enable-forced-colors .inner__02fc8:hover { - background-color: ButtonFace; - border-color: ButtonText -} - -.enable-forced-colors .inner__02fc8:disabled { - opacity: .3; - pointer-events: none -} - -.container__0d793 { - align-items: center; - background: var(--background-mod-normal); - border-radius: var(--radius-sm); - display: flex; - flex-direction: row; - padding: 24px 12px -} - -.textContainer__0d793 { - margin: 0 12px -} - -.headerText__0d793 { - align-items: center; - display: flex; - flex-direction: row -} - -.newBadge__0d793 { - margin-inline-end:8px} - -.banner_c7f964 { - filter: saturate(var(--saturation-factor,1)); - height: 180px; - margin-top: -32px; - width: 180px -} - -.connectionsContainer_c7f964 { - display: flex; - flex-wrap: wrap; - gap: var(--space-8); - padding: var(--space-8) 0 -} - -.accountButtonInner_c7f964 { - background-color: var(--control-secondary-background-default); - border: 1px solid var(--control-secondary-border-default) -} - -.accountButtonInner_c7f964:hover { - background-color: var(--control-secondary-background-hover); - border-color: var(--control-secondary-border-hover) -} - -.accountAddWrapper_c7f964 { - position: relative -} - -.accountAddInner_c7f964 { - align-items: center; - background-position: 50%; - background-repeat: no-repeat; - background-size: 28px 28px; - border-radius: var(--radius-sm); - color: var(--control-secondary-icon-default); - display: flex; - height: 44px; - justify-content: center; - width: 44px -} - -.accountAddInner_c7f964:hover { - background-color: var(--control-secondary-background-hover); - border-color: var(--control-secondary-border-hover); - cursor: pointer -} - -.accountAddInner_c7f964:disabled { - opacity: .5; - pointer-events: none -} - -.connectionList_c7f964 { - display: grid; - grid-gap: 24px -} - -.connection_c7f964 { - background-color: var(--background-mod-subtle); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - display: flex; - flex-direction: column; - gap: var(--space-20); - padding: var(--space-16); - width: 100% -} - -.connectionHeader_c7f964 { - align-items: center; - border-radius: 8px 8px 0 0; - display: grid; - grid-template-columns: auto minmax(0,1fr) auto; - position: relative -} - -.connectionHeader_c7f964 .connectionAccountValue_c7f964 { - color: var(--text-strong); - margin-inline-start:16px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.connectionAccountLabelContainer_c7f964 { - align-items: center; - display: flex; - flex-direction: row -} - -.connectionAccountLabelVerified_c7f964 { - height: 16px; - margin-inline-start:4px;width: 20px -} - -.connectionAccountLabel_c7f964 { - margin-inline-start:16px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.connectionIcon_c7f964 { - -webkit-user-drag: none; - height: 32px; - width: 32px -} - -.connectionOptions_c7f964 { - display: flex; - flex-direction: column; - gap: var(--space-20) -} - -.integrationsWrapper_c7f964 { - padding: 20px -} - -.integrationsWrapper_c7f964 a { - color: var(--text-default) -} - -.integrationWrapper_c7f964 { - background: var(--background-mod-normal); - border-radius: var(--radius-sm); - margin-top: 8px; - padding: 16px -} - -.integration_c7f964 { - align-items: center; - display: flex; - justify-content: space-between -} - -.integration_c7f964 .guildIcon_c7f964 { - margin-inline-end:8px} - -.integrationError_c7f964 { - display: flex; - justify-content: center -} - -.integrationInner_c7f964 { - display: flex; - flex: 1; - flex-direction: column -} - -.integrationInner_c7f964 .channelLink_c7f964 { - color: var(--text-default); - font-size: 12px -} - -.connectionDelete_c7f964 { - align-self: center; - color: var(--interactive-text-default); - cursor: pointer; - margin: 0 8px -} - -.connectionDelete_c7f964:hover { - color: var(--interactive-text-hover) -} - -.integrationRevoked_c7f964 { - font-weight: var(--font-weight-semibold) -} - -.integrationRevoked_c7f964 a { - color: var(--text-link) -} - -.metadataContainer_c7f964 { - align-content: flex-start; - background-color: var(--background-mod-muted); - border: 1px solid var(--border-muted); - border-radius: var(--radius-sm); - display: flex; - flex-direction: row; - padding: var(--space-8) -} - -.metadataItem_c7f964 { - align-self: center; - margin-top: 0!important -} - -.metadataRefreshButton_c7f964 { - align-self: center; - margin-inline-start:auto} - -.enable-forced-colors .connection_c7f964 { - border: 1px solid CanvasText -} - -.enable-forced-colors .connectionHeader_c7f964 { - border-bottom: 1px solid CanvasText -} - -.enable-forced-colors .connectionDelete_c7f964 { - background-color: ButtonFace; - border: 1px solid ButtonFace; - color: ButtonText -} - -.enable-forced-colors .connectionDelete_c7f964:focus,.enable-forced-colors .connectionDelete_c7f964:hover { - border-color: ButtonText -} - -.connectedAccountVanityMetadataCreatedAt_c7f964 { - align-items: center; - display: flex -} - -.connectionMetadataUpsellTag_c7f964 { - align-self: center; - padding: 0 6px -} - -.connectionMetadataUpsellDescription_c7f964 { - align-self: center; - padding: 0 4px -} - -.sparkleContainer_c7f964 { - margin-inline-end:8px;position: relative -} - -.sparkleTop_c7f964 { - inset-inline-start: -6px; - top: -8px -} - -.sparkleBottom_c7f964,.sparkleTop_c7f964 { - position: absolute; - z-index: 2 -} - -.sparkleBottom_c7f964 { - bottom: -6px; - inset-inline-end: -4px -} - -.infoBox_c7f964 { - margin-top: 16px -} - -.header_de8069 { - background-color: var(--background-surface-higher); - border-radius: var(--radius-sm) -} - -.gameNamesHeader_de8069 { - gap: 24px; - justify-content: space-between; - min-height: 36px; - padding: var(--space-8) var(--space-12) -} - -.gameIcons_de8069,.gameNamesHeader_de8069 { - align-items: center; - display: flex -} - -.gameIcons_de8069 { - flex: 0; - flex-direction: row; - gap: var(--space-8); - min-height: 56px; - padding: var(--space-12) -} - -.divider_de8069 { - background-color: var(--border-subtle); - height: 1px; - width: 100% -} - -.icon_de8069 { - border-radius: var(--radius-xs); - height: 32px; - width: 32px -} - -.moreIcon_de8069 { - background-color: var(--background-base-lowest); - justify-content: center -} - -.manageGamesLink_de8069,.moreIcon_de8069 { - align-items: center; - display: flex -} - -.manageGamesLink_de8069 { - cursor: pointer -} - -.manageGamesChevron_de8069 { - margin-top: 1px -} - -.emptyContainer_de8069 { - align-items: center; - background-color: var(--background-surface-higher); - border-radius: var(--radius-sm); - display: flex; - flex-direction: column; - gap: var(--space-8); - justify-content: center; - padding: var(--space-48) 0 -} - -.noGamesConnectedText_de8069 { - text-transform: uppercase -} - -.guildSelectOptionIcon_b476ba { - border-radius: var(--radius-sm) -} - -.searchableSelect_b476ba { - align-items: center -} - -.clydeIconContainer_b476ba { - align-items: center; - background-color: var(--background-brand); - border-radius: var(--radius-sm); - display: flex; - height: 24px; - justify-content: center; - width: 24px -} - -.card__5cea9 { - background-color: var(--background-base-lower); - border-radius: var(--radius-sm); - box-shadow: var(--elevation-low); - overflow: hidden -} - -.header__5cea9 { - align-items: center; - background-color: var(--background-mod-normal); - display: flex; - gap: var(--space-8); - height: 72px; - padding: var(--space-16) -} - -.text__5cea9 { - display: flex; - flex: 1; - flex-direction: column -} - -.iconContainer__5cea9 { - align-items: center; - background-color: var(--interactive-background-active); - border-radius: var(--radius-round); - display: flex; - padding: var(--space-8) -} - -.usersList__5cea9 { - padding-inline:var(--space-16)} - -.row__5cea9 { - align-items: center; - border-bottom: 1px solid var(--border-subtle); - display: flex; - flex-direction: row; - justify-content: space-between; - padding-bottom: var(--space-16); - padding-top: var(--space-16) -} - -.lastRow__5cea9 { - border-bottom: none -} - -.userInfo__5cea9 { - align-items: center; - display: flex; - gap: var(--space-8) -} - -.loadMoreContainer__5cea9 { - padding-bottom: var(--space-16) -} - -.loadMoreButton__5cea9,.loadMoreContainer__5cea9 { - display: flex; - justify-content: center -} - -.loadMoreButton__5cea9 { - align-items: center; - background-color: var(--background-mod-muted); - border-radius: 0 0 var(--radius-xs) var(--radius-xs); - cursor: pointer; - padding-bottom: var(--space-4); - padding-top: var(--space-4); - width: 40% -} - -.rowContainer__0e124 { - align-items: center; - display: flex; - gap: var(--space-8); - margin-inline-end:var(--space-4)} - -.select_b2fe93 { - min-width: 140px -} - -.selectItemRow_b2fe93 { - align-items: center; - border-bottom: 1px solid var(--border-subtle); - display: grid; - grid-template-columns: 1fr auto; - padding: var(--space-4) 0 -} - -.title_b2fe93 { - min-width: 0 -} - -.tabs__90767 { - margin-top: var(--space-12) -} - -.card__2023f { - padding: var(--space-16) -} - -.button__2023f { - display: flex -} - -.settingsItemHighlight__7cd2d { - border: 1px solid transparent; - border-radius: var(--radius-xs); - bottom: -8px; - position: absolute; - top: -8px; - inset-inline: -8px -} - -.settingsBackgroundFlashElement__7cd2d { - position: relative -} - -.container__6b700 { - gap: var(--space-16) -} - -.container__6b700,.header__6b700 { - display: flex; - flex-direction: column -} - -.header__6b700 { - gap: var(--space-4) -} - -.description__6b700.disabled__6b700,.title__6b700.disabled__6b700 { - cursor: not-allowed; - opacity: .6 -} - -.enable-forced-colors .title__6b700.disabled__6b700 { - opacity: 1 -} - -.enable-forced-colors .description__6b700.disabled__6b700 { - color: GrayText; - opacity: 1 -} - -.item__89d80 { - background-color: var(--background-mod-muted); - border-radius: 8px; - margin-bottom: 20px; - overflow: hidden -} - -.row__89d80 { - display: flex; - height: 68px; - padding: 16px -} - -.divider__89d80 { - background-color: var(--border-subtle); - height: 1px; - margin: 0 16px -} - -.section__89d80 { - align-items: center; - background-color: var(--background-mod-normal); - display: flex; - height: 72px; - padding: 16px -} - -.avatar__89d80 { - margin-inline-end:8px} - -.text__89d80 { - display: flex; - flex: 1; - flex-direction: column -} - -.icon__89d80,.username__89d80 { - cursor: pointer -} - -.icon__89d80 { - align-items: center; - border-radius: 16px; - display: flex; - height: 32px; - justify-content: center; - width: 32px -} - -.full-motion .icon__89d80 { - transition: background .1s ease-in-out -} - -.icon__89d80:hover { - background-color: var(--interactive-background-selected) -} - -.sectionIconContainer__89d80 { - background-color: var(--background-base-lower) -} - -.item__52575 { - background-color: var(--background-mod-muted); - border-radius: var(--radius-sm); - margin-bottom: 20px; - overflow: hidden -} - -.item__6be08 { - position: relative -} - -.removeBuildOverride__6be08 { - inset-inline-end: -31px; - opacity: 0; - position: absolute; - top: -12px -} - -.buildOverrideGroup__6be08 { - margin-top: -1px; - padding-bottom: 20px; - padding-top: 20px; - position: relative -} - -.buildOverrideGroup__6be08:hover .removeBuildOverride__6be08 { - opacity: 1 -} - -.divider__6be08 { - margin-bottom: 20px -} - -.buildOverrideList__6be08 { - display: grid; - gap: 16px; - grid-template-columns: 1fr 1fr; - padding-bottom: 16px -} - -.removeBuildOverrideDisabled__6be08 { - cursor: not-allowed; - filter: grayscale(100%); - opacity: .75 -} - -.row__6be08 { - border-bottom: 1px solid var(--border-subtle) -} - -.buttonsContainer__6be08 { - display: flex; - flex-direction: row; - flex-wrap: wrap; - gap: 8px -} - -.surveyOverride__6be08 { - display: flex -} - -.surveyOverrideInput__6be08 { - flex-grow: 1; - margin-inline-end:20px} - -.key__6be08 { - background-color: var(--background-mod-muted); - border: 1px solid var(--background-base-low); - border-radius: var(--radius-xs); - box-shadow: inset 0 -4px 0 var(--background-base-lowest); - display: inline-block; - font-size: 12px; - font-weight: var(--font-weight-semibold); - height: 23px; - line-height: 12px; - padding: 3px 6px 4px; - text-align: center; - text-transform: uppercase -} - -.codebox__6be08 { - background: none; - max-height: 380px -} - -.category_f06a86 { - display: flex; - flex-direction: column -} - -.category_f06a86:last-child .categoryDivider_f06a86 { - display: none -} - -.categoryHeader_f06a86 { - gap: var(--space-8); - margin-bottom: var(--space-16) -} - -.categoryContent_f06a86,.categoryHeader_f06a86 { - display: flex; - flex-direction: column -} - -.categoryContent_f06a86 { - gap: var(--space-32) -} - -.categoryDivider_f06a86 { - margin-bottom: var(--space-32); - margin-top: var(--space-32) -} - -.categorySubGroup__9f327 { - display: flex; - flex-direction: column; - gap: var(--space-16) -} - -.categorySubGroup__9f327:empty { - display: none -} - -.counter__90981 { - background-color: var(--background-surface-highest); - border: 1px solid var(--border-muted); - border-radius: var(--radius-md); - box-shadow: none; - flex-direction: column; - padding: 10px 20px 12px -} - -.counter__90981,.textRow__90981 { - align-items: center; - display: flex -} - -.info__90981 { - color: var(--text-default); - margin-inline-start:4px} - -.counterText__90981,.tooltip__90981 { - text-align: center -} - -.container__96cb9 { - width: -moz-fit-content; - width: fit-content -} - -.guildAvatar__96cb9 { - align-items: center; - background-color: var(--primary-600); - border-radius: var(--radius-md); - color: var(--white); - display: flex; - font-weight: 600; - height: 40px; - justify-content: center; - width: 40px -} - -.container__0d706 { - align-items: center; - border-bottom: 1px solid var(--border-subtle); - display: flex; - padding-bottom: 12px; - padding-top: 12px -} - -.descriptors__0d706 { - margin-inline-start:10px} - -.header__0d706 { - align-items: center; - display: flex -} - -.guildBadge__0d706 { - margin-inline-end:4px} - -.childWrapper__0d706 { - align-items: center; - background-color: var(--background-surface-highest); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-md); - color: var(--text-default); - display: flex; - height: 40px; - justify-content: center; - width: 40px -} - -.acronym__0d706 { - font-weight: var(--font-weight-medium); - line-height: 1.2em; - white-space: nowrap -} - -.container_c40875 { - align-items: center; - border-bottom: 1px solid var(--border-subtle); - display: flex; - gap: var(--space-12); - padding-bottom: var(--space-12); - padding-top: var(--space-12) -} - -.purchasePlaceholder_c40875 { - align-items: center; - background: var(--background-mod-normal,hsla(240,4%,61%,.16)); - border-radius: 4px; - display: flex; - justify-content: center -} - -.avatarDecorationPreview_c40875,.nameplatePreview_c40875,.purchasePlaceholder_c40875 { - height: 40px; - width: 40px -} - -.nameplatePreview_c40875 { - -o-object-fit: cover; - object-fit: cover; - -o-object-position: right; - object-position: right -} - -.avatarPairContainer__30f35 { - display: flex -} - -.avatarPair__30f35,.avatarPairContainer__30f35 { - align-items: center; - justify-content: center -} - -.avatarPair__30f35 { - background-color: var(--background-surface-higher); - border: 1px solid var(--border-muted); - border-radius: 100px; - box-shadow: none; - display: inline-flex; - gap: var(--space-12); - margin-bottom: var(--space-8); - padding: var(--space-8) -} - -.container__41445 { - align-items: center; - border-bottom: 1px solid var(--border-subtle); - display: flex; - padding-bottom: 12px; - padding-top: 12px -} - -.descriptors__41445 { - margin-inline-start:10px} - -.username__7b869 { - color: var(--text-strong) -} - -.discriminator__7b869 { - color: var(--text-muted) -} - -.fill__7b869 { - max-width: 100% -} - -.image__379fe { - width: 243px -} - -.empty__379fe { - text-align: center -} - -.container__339c4 { - width: -moz-fit-content; - width: fit-content -} - -.container__333b3 { - display: flex; - flex-wrap: wrap; - gap: var(--space-4) -} - -.container__333b3>div { - flex: 1 -} - -.sectionHeader__333b3 { - margin-bottom: var(--space-16) -} - -.avatarList__333b3 { - display: flex; - gap: var(--space-8) -} - -.actionSection__333b3 { - margin-bottom: var(--space-8) -} - -.guildRow__333b3,.userRow__333b3 { - border: 0; - padding-bottom: 0; - padding-top: 0 -} - -.container__38524 { - max-width: 800px -} - -.connectedCounter__38524 { - display: flex; - margin-bottom: var(--space-12) -} - -.icon__38524 { - color: var(--text-default); - margin-inline:var(--space-4)} - -.tooltip__38524 { - cursor: pointer -} - -.header__38524 { - align-items: center; - border-bottom: 1px solid var(--border-muted); - box-shadow: none; - display: flex; - padding: var(--space-12) -} - -.headerText__38524 { - margin-inline-start:var(--space-12);overflow: hidden; - width: 100% -} - -.activityCounterRow__38524 { - display: grid; - gap: var(--space-8); - grid-template-columns: repeat(12,1fr) -} - -.activityCounterRow__38524>div:first-child { - grid-column: 1/span 12 -} - -.activityCounterRow__38524>div:nth-child(n+2) { - grid-column: span 6 -} - -@media (min-width: 900px) { - .activityCounterRow__38524>div:first-child { - grid-column:1/span 4 - } - - .activityCounterRow__38524>div:nth-child(2) { - grid-column: 5/span 4 - } - - .activityCounterRow__38524>div:nth-child(3) { - grid-column: 9/span 4 - } - - .activityCounterRow__38524>div:nth-child(n+4) { - grid-column: span 3; - grid-row-start: 2 - } -} - -.activityOverview__38524 { - display: flex; - flex-direction: column; - gap: var(--space-32) -} - -.emptyActivity__38524 { - align-self: center; - margin-top: var(--space-32) -} - -.actionSection__38524 { - margin-bottom: var(--space-8) -} - -.actions__38524 { - max-height: 105px; - overflow: hidden; - transition: max-height .3s ease-in-out -} - -.actions__38524:last-of-type :last-child { - border-bottom: none; - padding-bottom: 0 -} - -.loadMoreBar__38524 { - align-items: center; - background-color: var(--background-mod-muted); - border-radius: 0 0 8px 8px; - cursor: pointer; - display: flex; - justify-content: center; - margin: 0 auto; - max-width: 30% -} - -.loadMore__38524 { - padding-bottom: var(--space-4); - padding-top: var(--space-4) -} - -.spinner__38524 { - padding-bottom: var(--space-12); - padding-top: var(--space-12) -} - -.sectionDescription__38524,.sectionHeader__38524 { - margin-bottom: var(--space-4) -} - -.accountRow__38524 { - align-items: center; - display: flex; - margin-inline-end:var(--space-4);overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: 100% -} - -.select__38524 { - border-radius: var(--radius-xs); - min-width: 240px -} - -.disabled__38524 { - cursor: not-allowed; - opacity: .6; - pointer-events: none -} - -.box_f8c98c { - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-md); - box-shadow: none -} - -.backButton__6fa7b { - align-items: center; - cursor: pointer; - display: flex; - gap: var(--space-4) -} - -.teenSelector__6fa7b { - display: flex; - justify-content: flex-end -} - -.titleContainer__6fa7b { - flex: 1; - flex-shrink: 0 -} - -.grow__6fa7b { - flex: 1 -} - -.tabs__3f3a2 { - margin-top: var(--space-12) -} - -.container__5dbf8 { - align-items: center; - border-radius: 12px; - box-shadow: var(--elevation-low); - display: flex; - flex-direction: column; - justify-content: space-between; - padding: 24px; - text-align: center -} - -@media (min-width: 485px) { - .container__5dbf8 { - align-items:flex-start; - flex-direction: row; - padding: 32px; - text-align: start - } -} - -.row__5dbf8 { - display: flex; - justify-content: space-between -} - -.headerText__5dbf8 { - max-width: 364px -} - -.headerText__5dbf8>* { - margin-bottom: 6px -} - -.headerText__5dbf8>:last-child { - margin-bottom: 0 -} - -.headerImage__5dbf8 { - align-self: center; - margin-top: 32px; - padding-inline-start:16px} - -@media (min-width: 900px) { - .headerImage__5dbf8 { - margin-top:0 - } -} - -.button__5dbf8 { - display: flex; - justify-content: center; - margin-top: 16px -} - -@media (min-width: 900px) { - .button__5dbf8 { - justify-content:flex-start - } -} - -.row__0624f { - align-items: center; - background-color: var(--background-base-lowest); - border: 1px solid var(--border-muted); - border-radius: var(--radius-md); - display: flex; - padding: var(--space-16) var(--space-12) -} - -.inModal__0624f { - background-color: var(--background-base-lower) -} - -.inModal__0624f .icon-container__0624f { - background-color: var(--background-base-lowest) -} - -.positive__0624f { - color: var(--text-feedback-positive) -} - -.negative__0624f { - color: var(--text-feedback-critical) -} - -.groupHeader__0624f { - margin-bottom: 10px -} - -.groupHeader__0624f:not(:first-child) { - margin-top: 28px -} - -.header__0624f { - margin-bottom: var(--space-4) -} - -.iconContainer__0624f { - align-items: center; - background-color: var(--background-mod-subtle); - border: 1px solid var(--border-subtle); - border-radius: 50%; - display: flex; - flex-shrink: 0; - height: 40px; - justify-content: center; - margin-inline-end:var(--space-8);width: 40px -} - -.max-width__5b321 { - max-width: auto -} - -@media (min-width: 900px) { - .max-width__5b321 { - max-width:800px - } -} - -.container__5b321 { - box-sizing: border-box; - flex-direction: column; - margin-top: var(--space-12) -} - -@media (min-width: 900px) { - .container__5b321 { - padding:var(--space-32) - } -} - -.container__8cc9a { - align-items: center; - display: flex; - flex-direction: row; - padding: 20px -} - -@media (min-width: 900px) { - .container__8cc9a { - align-items:flex-start; - flex-direction: column - } -} - -.box__8cc9a { - box-shadow: var(--elevation-low); - display: flex; - flex-direction: column; - padding: 20px -} - -.circle__8cc9a { - align-items: center; - background-color: var(--background-mod-subtle); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-round); - display: flex; - height: 36px; - justify-content: center; - margin-bottom: 10px; - margin-inline-end:12px;width: 36px -} - -.icon__8cc9a { - color: var(--text-strong) -} - -.header__8cc9a { - margin-bottom: 6px -} - -.container__9c880 { - max-width: 800px -} - -.parentalControlsTeenRow__9c880 { - margin-top: var(--space-24) -} - -.divider__9c880 { - margin: 0 -} - -.container__8e680 { - display: flex; - flex-direction: column; - gap: var(--space-32); - overflow: hidden -} - -.max-width__8e680 { - max-width: 800px -} - -.banner__8e680,.featureCards__8e680 { -} - -.featureCards__8e680 { - display: flex; - flex-direction: column; - gap: 10px; - margin-top: 12px -} - -@media (min-width: 900px) { - .featureCards__8e680 { - flex-direction:row - } -} - -.actionButton_d9752c { - align-items: center; - background-color: var(--background-surface-highest); - border: 1px solid var(--border-subtle); - border-radius: 50%; - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - height: 36px; - justify-content: center; - margin-inline-start:10px;width: 36px -} - -.actionButton_d9752c:first-child { - margin-inline-start:0} - -.actionButton_d9752c:hover { - color: var(--interactive-text-hover) -} - -.actionButton_d9752c:active { - background-color: var(--interactive-background-active); - color: var(--interactive-text-active) -} - -.actionButton_d9752c.disabled_d9752c { - opacity: .3; - pointer-events: none -} - -.actionButton_d9752c.actionAccept_d9752c:hover { - color: var(--icon-feedback-positive) -} - -.actionButton_d9752c.actionDeny_d9752c:hover { - color: var(--icon-feedback-critical) -} - -.icon_d9752c { - height: 20px; - width: 20px -} - -.enable-forced-colors .actionButton_d9752c { - background-color: ButtonFace; - border: 1px solid ButtonFace; - color: ButtonText; - forced-color-adjust: none -} - -.enable-forced-colors .actionButton_d9752c:hover { - border-color: ButtonText; - color: ButtonText -} - -.enable-forced-colors .actionButton_d9752c:active { - background-color: HighlightText; - border-color: Highlight; - color: Highlight -} - -.enable-forced-colors .actionButton_d9752c.disabled_d9752c { - background-color: Canvas; - border-color: GrayText; - color: GrayText; - opacity: 1 -} - -.avatar__1327d { - flex-shrink: 0; - margin-block:0;margin-inline:0 12px} - -.userPreview__1327d { - display: flex; - flex-direction: column; - margin-inline-end:16px;overflow: hidden -} - -.container__1327d { - align-items: center; - display: flex; - width: 100% -} - -.userContainerWithTimestamp__1327d { - align-items: flex-end; - display: flex; - justify-content: flex-start -} - -.tagContainer__1327d { - align-items: flex-end; - display: flex; - margin-top: 2px; - overflow: hidden -} - -.username__1327d { - color: var(--text-strong); - flex-grow: 0; - flex-shrink: 1; - font-weight: var(--font-weight-semibold); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.discriminator__1327d { - color: var(--text-default) -} - -.timestampWithPreview__1327d { - flex-grow: 1; - flex-shrink: 0; - margin-inline-start:8px} - -.container_e65d0c { - justify-content: space-between; - width: 100% -} - -.container_e65d0c,.details_e65d0c { - align-items: center; - display: flex -} - -.details_e65d0c { - overflow: hidden -} - -.actions_e65d0c { - align-items: center; - display: flex -} - -.rowItem_a0365b { - border-bottom: 1px solid var(--border-subtle); - box-sizing: border-box; - cursor: pointer; - display: flex; - flex-direction: row; - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px; - overflow: hidden; - padding: 10px -} - -.rowItem_a0365b.last_a0365b { - border-bottom: none -} - -.rowItem_a0365b.active_a0365b,.rowItem_a0365b:hover { - background: var(--interactive-background-hover); - border-color: transparent; - border-radius: var(--radius-md) -} - -.container_d9914b { - max-width: 800px -} - -.section_d9914b { - margin-bottom: 8px -} - -.box_d9914b { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - overflow: hidden; - padding: var(--space-24) -} - -@media (min-width: 900px) { - .box_d9914b { - padding:32px - } -} - -.linkingBanner_d9914b { - align-items: flex-start; - flex-direction: row; - justify-content: center; - margin-bottom: 24px; - padding: 32px -} - -.linkingBannerParent_d9914b { - align-items: flex-start; - flex-direction: column -} - -.linkingBannerUpper_d9914b { - display: flex; - flex-direction: row -} - -.step_d9914b { - align-items: center; - display: flex; - justify-content: center; - margin-bottom: 16px -} - -.steps_d9914b { - align-items: flex-start; - display: flex; - flex-direction: column -} - -.steps_d9914b :last-child { - margin-bottom: 0 -} - -.stepNumber_d9914b { - align-items: center; - background-color: var(--background-mod-subtle); - border: 1px solid var(--border-muted); - border-radius: var(--radius-round); - display: flex; - height: 32px; - justify-content: center; - margin-inline-end:var(--space-8);width: 32px -} - -.bannerArt_d9914b { - height: 110px; - width: 224px -} - -.maxConnectionInfo_d9914b { - border-top: 1px solid var(--border-subtle); - display: flex; - justify-content: center; - padding-top: 10px -} - -.supportHeader_d9914b { - margin-bottom: 4px -} - -.flexVertical__2d3fe { - display: flex; - flex-direction: column -} - -.flexVertical__2d3fe>.flexSpacer__2d3fe { - min-height: 1px -} - -.flexHorizontal__2d3fe { - display: flex; - flex-direction: row -} - -.flexHorizontal__2d3fe>.flexSpacer__2d3fe { - min-width: 1px -} - -.flexHorizontalReverse__2d3fe { - display: flex; - flex-direction: row-reverse -} - -.flexHorizontalReverse__2d3fe>.flexSpacer__2d3fe { - min-width: 1px -} - -.flexSpacer__2d3fe { - flex: 1; - overflow: hidden -} - -.flexCenter__2d3fe { - align-items: center; - display: flex; - justify-content: center -} - -.flexAlignStart__2d3fe { - align-items: flex-start -} - -.flexAlignEnd__2d3fe { - align-items: flex-end -} - -.flexAlignCenter__2d3fe { - align-items: center -} - -.flexAlignStretch__2d3fe { - align-items: stretch -} - -.flexJustifyStart__2d3fe { - justify-content: flex-start -} - -.flexJustifyEnd__2d3fe { - justify-content: flex-end -} - -.flexJustifyCenter__2d3fe { - justify-content: center -} - -.flexJustifyAround__2d3fe { - justify-content: space-around -} - -.flexJustifyBetween__2d3fe { - justify-content: space-between -} - -.flexNowrap__2d3fe { - flex-wrap: nowrap -} - -.flexWrap__2d3fe { - flex-wrap: wrap -} - -.flexWrapReverse__2d3fe { - flex-wrap: wrap-reverse -} - -.flex__48a9c { - box-sizing: border-box; - display: flex -} - -.flex__48a9c .flexChild__48a9c { - box-sizing: border-box; - flex-grow: 1 -} - -.flex__48a9c.flexGutterSmall__48a9c { - margin-left: -10px; - margin-right: -10px -} - -.flex__48a9c.flexGutterSmall__48a9c .flexChild__48a9c { - padding: 0 10px -} - -.flex__48a9c.flexGutterLarge__48a9c { - margin-left: -30px; - margin-right: -30px -} - -.flex__48a9c.flexGutterLarge__48a9c .flexChild__48a9c { - padding: 0 30px -} - -.container__74f90 { - display: flex; - flex-direction: column; - padding-top: 20px -} - -.enableCard__74f90 { - background-color: var(--background-base-lower); - border: 1px solid var(--border-muted); - color: var(--text-strong); - padding: 12px 16px -} - -.toggleSection__74f90 { - margin-bottom: 30px -} - -.arrow__74f90 { - color: var(--text-strong) -} - -.sideNavContent_dc2e0e { - max-width: 800px; - min-width: 375px; - padding: 24px -} - -@media (min-width: 900px) { - .sideNavContent_dc2e0e { - padding:40px - } -} - -.container_dc2e0e { - align-items: stretch; - display: flex; - flex: 1 1 auto; - flex-direction: column; - justify-content: stretch; - min-height: 0; - min-width: 0; - overflow: hidden; - position: relative -} - -.theme-dark.custom-theme-background .containerSidenav_dc2e0e,.theme-light.custom-theme-background .containerSidenav_dc2e0e { - background: inherit -} - -.sidebarTabBar_dc2e0e { - box-shadow: none -} - -.settingsTabBar_dc2e0e { - border-bottom: 1px solid var(--border-subtle); - margin-bottom: 24px -} - -.settingsTabBarItem_dc2e0e { - align-items: center; - display: flex; - padding-bottom: 14px -} - -.settingsTabBarItem_dc2e0e:first-child { - margin-inline-start:0} - -.contentPanel_dc2e0e { - align-items: stretch; - display: flex; - flex: 1 1 auto; - flex-direction: column; - justify-content: stretch; - overflow-x: hidden; - overflow-y: auto; - position: relative -} - -.userSettingsContentPanel_dc2e0e { - overflow-x: unset; - overflow-y: unset -} - -.item_dc2e0e { - align-items: center; - display: flex -} - -.badge_dc2e0e { - margin-inline-start:6px} - -.containerSidenav_dc2e0e { - background: var(--background-gradient-chat,var(--background-base-lower)); - border-top: 1px solid var(--app-frame-border) -} - -.loadingContainer_dc2e0e { - align-items: center; - display: flex; - justify-content: center; - min-height: calc(100vh - 100px) -} - -.notDetected_cc46f0,.nowPlaying_cc46f0 { - animation: none; - border-radius: 3px; - margin-top: 0; - padding: 16px; - width: 100% -} - -.notDetected_cc46f0 .overlayStatusText_cc46f0,.notDetected_cc46f0 .overlayWarningIcon_cc46f0,.nowPlaying_cc46f0 .overlayStatusText_cc46f0,.nowPlaying_cc46f0 .overlayWarningIcon_cc46f0 { - opacity: 1 -} - -.nowPlayingAdd_cc46f0 { - align-self: flex-start; - font-size: 14px; - margin-inline-start:0} - -.game_cc46f0 { - background-color: var(--background-mod-normal); - border-radius: 12px; - margin-bottom: 12px; - padding: 16px; - position: relative -} - -.game_cc46f0 .removeGame_cc46f0 { - cursor: pointer; - inset-inline-end: -15px; - opacity: 0; - position: absolute; - top: -12px -} - -.game_cc46f0:focus-within .overlayStatusText_cc46f0,.game_cc46f0:focus-within .overlayWarningIcon_cc46f0,.game_cc46f0:focus-within .removeGame_cc46f0,.game_cc46f0:hover .overlayStatusText_cc46f0,.game_cc46f0:hover .overlayWarningIcon_cc46f0,.game_cc46f0:hover .removeGame_cc46f0 { - opacity: 1 -} - -.toggleContainer_cc46f0 { - margin-inline-start:12px} - -.activeGame_cc46f0 { - border-radius: 12px; - position: relative -} - -.activeGame_cc46f0 .removeGame_cc46f0 { - inset-inline-end: -12px; - opacity: 0; - position: absolute; - top: -12px -} - -.activeGame_cc46f0:focus-within .removeGame_cc46f0,.activeGame_cc46f0:hover .removeGame_cc46f0 { - opacity: 1 -} - -.gameName_cc46f0,.gameNameInput_cc46f0,.gameVerifiedIcon_cc46f0,.lastPlayed_cc46f0,.overlayStatusText_cc46f0,.toggleIcon_cc46f0 { - z-index: 1 -} - -.gameNameLastPlayed_cc46f0 { - flex: 1 -} - -.gameVerifiedIcon_cc46f0 { - margin-inline-start:8px} - -.gameName_cc46f0 { - color: var(--text-strong); - font-size: 16px; - font-weight: var(--font-weight-semibold); - line-height: 22px -} - -.gameNameInput_cc46f0 { - background-color: transparent; - border: 1px solid transparent; - border-radius: 3px; - margin-inline-start:-5px;padding: 0 4px; - width: 240px -} - -.gameNameInput_cc46f0:focus,.gameNameInput_cc46f0:hover { - background-color: var(--background-mod-normal); - border-color: hsl(var(--primary-500-hsl)/.3) -} - -.lastPlayed_cc46f0 { - color: var(--primary-400); - font-size: 14px -} - -.overlayStatusText_cc46f0 { - color: var(--text-muted); - font-size: 14px; - margin-inline-end:4px;opacity: 0; - text-transform: uppercase; - width: 100px -} - -.toggleIcon_cc46f0 { - cursor: pointer -} - -.toggleIconOff_cc46f0 .fill_cc46f0 { - fill: var(--icon-feedback-critical) -} - -.toggleIconInactive_cc46f0 { - cursor: not-allowed -} - -.toggleIconInactive_cc46f0 .fill_cc46f0 { - fill: var(--icon-feedback-critical); - opacity: .5 -} - -.overlayWarningIcon_cc46f0 { - background: url(/assets/07e52d3b4c358536.svg) no-repeat; - height: 16px; - margin-inline-end:20px;opacity: 0; - width: 16px -} - -.addGamePopout_cc46f0 { - background-color: var(--background-surface-higher); - border: 1px solid var(--border-subtle); - border-radius: 3px; - padding: 10px; - width: 300px -} - -.addGamePopout_cc46f0 .actions_cc46f0 { - align-items: center; - gap: var(--space-16); - justify-content: flex-end -} - -.subgame_cc46f0 { - align-items: center; - background-color: var(--background-surface-high); - border-radius: 0; - display: flex; - margin-bottom: 0; - width: 100% -} - -.subgame_cc46f0:last-child { - border-radius: 0 0 12px 12px -} - -.border_cc46f0 { - border-bottom: 1px solid var(--border-subtle); - margin: 0 16px -} - -.detectedApplication_cc46f0,.nowPlayingAdd_cc46f0 { - align-items: center; - display: flex -} - -.nowPlayingAdd_cc46f0 { - color: var(--text-muted); - gap: 4px -} - -.nowPlaying_cc46f0 { - background-color: var(--status-positive-background) -} - -.nowPlaying_cc46f0 .gameName_cc46f0 { - color: var(--white) -} - -.nowPlaying_cc46f0 .gameNameInput_cc46f0:focus,.nowPlaying_cc46f0 .gameNameInput_cc46f0:hover { - background-color: hsl(var(--green-560-hsl)/.3); - border-color: var(--green-560) -} - -.nowPlaying_cc46f0 .lastPlayed_cc46f0,.nowPlaying_cc46f0 .overlayStatusText_cc46f0 { - color: var(--white) -} - -.nowPlaying_cc46f0 .toggleIconInactive_cc46f0 .fill_cc46f0,.nowPlaying_cc46f0 .toggleIconOff_cc46f0 .fill_cc46f0,.nowPlaying_cc46f0 .toggleIconOn_cc46f0 .fill_cc46f0 { - fill: var(--white) -} - -.notDetected_cc46f0 { - background-color: var(--background-base-lower) -} - -.notDetected_cc46f0 .gameName_cc46f0 { - color: var(--interactive-text-active) -} - -.notDetected_cc46f0 .lastPlayed_cc46f0 { - color: var(--text-muted) -} - -.toggleIconOn_cc46f0 .fill_cc46f0 { - fill: var(--interactive-text-default) -} - -.game_cc46f0.gameHasSubgame_cc46f0 { - border-end-end-radius: 0; - border-end-start-radius: 0; - margin-bottom: 0 -} - -.subgameContainer_cc46f0 { - background-color: var(--background-surface-high); - border-radius: 0 0 12px 12px; - margin-bottom: 20px -} - -.detectionToggleModal_cc46f0 { - width: var(--modal-width-small) -} - -.addedGamesTitle_cc46f0 { - margin-bottom: var(--space-xs) -} - -.addedGamesDescription_cc46f0 { - margin-bottom: var(--space-16) -} - -.container_c50183 { - background-position: bottom -80px right -330px; - background-size: auto 155%; - border-radius: 8px; - box-sizing: border-box; - height: 208px; - min-width: 660px; - padding: 24px 32px 32px; - position: relative -} - -.container_c50183.withMargin_c50183 { - margin: 32px 0 -} - -.theme-dark .container_c50183 { - background-color: #0b1320 -} - -.theme-light .container_c50183 { - background-color: #bec8ff -} - -.textContainer_c50183 { - height: 100%; - max-width: 343px -} - -.headerContainer_c50183 { - align-items: center; - display: flex; - flex-direction: row; - height: 32px; - margin-inline-start:-4px;max-width: 343px -} - -.icon_c50183 { - margin-inline-end:4px;width: 30px -} - -.loading__9ed46 { - margin-top: 48px -} - -.maybeLaterButton__9ed46 { - color: var(--text-strong); - margin-inline-end:8px} - -.maybeLaterButton__9ed46:hover { - text-decoration: underline -} - -.bodyText__9ed46 { - display: flex; - justify-content: center -} - -.formDivider__9ed46 { - margin: 16px 0 -} - -.formSection__9ed46 { - margin-bottom: 16px -} - -.confirmationText__9ed46 { - margin-top: 8px -} - -.headerContainer__9ed46 { - align-items: center; - display: flex; - flex-direction: column; - gap: 8px; - margin: calc(var(--custom-outbound-promotion-redemption-modal-art-height)/-1) auto auto; - padding: 0; - width: 90% -} - -.art__9ed46 { - background-image: url(/assets/1ab7d1a0b0204a75.svg); - height: var(--custom-outbound-promotion-redemption-modal-art-height); - margin-bottom: 16px; - width: 240px -} - -.errorBody__9ed46,.errorHeader__9ed46 { - display: flex; - flex-direction: column -} - -.errorBody__9ed46 { - align-items: center; - gap: 8px; - margin: 0 auto; - max-width: 90% -} - -.errorArt__9ed46 { - background-image: url(/assets/26eafe176b5b3454.svg); - height: 99px; - margin: 0 auto 32px; - width: 141px -} - -.card_fedacc { - border-radius: 5px; - box-sizing: border-box; - color: var(--text-strong); - cursor: default; - overflow: hidden; - transform: translateZ(0) -} - -.header_fedacc { - padding: 20px; - position: relative -} - -.headerButtonColor_fedacc { - background: var(--primary-500); - color: var(--white) -} - -.coverArtPosition_fedacc { - background-position-x: 50%; - background-position-y: 40%; - background-repeat: no-repeat; - background-size: cover; - border-radius: 5px; - bottom: 0; - inset-inline-start: 0; - overflow: hidden; - pointer-events: none; - position: absolute; - top: 0; - transform: translateZ(0) -} - -.splashArt_fedacc { - filter: grayscale(100%); - -webkit-mask: radial-gradient(100% 100% at top left,hsla(0,0%,100%,.6) 0,hsla(0,0%,100%,0) 100%); - mask: radial-gradient(100% 100% at top left,hsla(0,0%,100%,.6) 0,hsla(0,0%,100%,0) 100%); - opacity: .3; - width: 300px -} - -.body_fedacc { - background-color: var(--background-mod-normal) -} - -.header_fedacc { - background-color: var(--background-base-lowest); - color: var(--text-strong) -} - -.header_fedacc:hover { - background-color: var(--interactive-background-hover) -} - -.card__6bc46 { - cursor: pointer -} - -.subText__6bc46 { - font-size: 12px; - line-height: 16px -} - -.subTextRow__6bc46 { - color: var(--primary-300) -} - -.subtitleHeader__6bc46 { - align-items: center; - color: var(--text-muted); - display: flex; - flex-direction: row; - font-weight: var(--font-weight-semibold); - gap: 4px -} - -.applicationSubtitleHeader__6bc46 { - margin-top: 2px -} - -.applicationSubtitleIcon__6bc46 { - border-radius: 6px; - height: 20px; - width: 20px -} - -.codeText__6bc46 { - margin-bottom: 8px -} - -.headerText__6bc46 { - margin-inline-start:20px} - -.cardHeader__6bc46 { - align-items: center; - display: flex; - flex: 1 1 auto; - justify-content: space-between -} - -.giftCodeRow__6bc46 { - border-bottom: 1px solid transparent; - margin: 0 20px; - padding: 20px 0 19px -} - -.giftCodeRow__6bc46:last-child { - border-bottom: none -} - -.generateCodeRow__6bc46 { - padding: 22px 0 -} - -.gameName__6bc46 { - line-height: 19px -} - -.expandIcon__6bc46 { - cursor: pointer; - margin-inline-start:8px} - -.spinner__6bc46 { - padding: 20px -} - -.theme-dark .giftCodeRow__6bc46 { - border-color: hsl(var(--primary-500-hsl)/.6) -} - -.theme-dark .bodyButtonColor__6bc46 { - color: var(--white) -} - -.theme-dark .bodyButtonColor__6bc46:hover { - background: var(--primary-500) -} - -.theme-dark .bodyButtonColor__6bc46:active { - background: var(--primary-400) -} - -.theme-light .bodyButtonColor__6bc46:hover { - background: var(--primary-200) -} - -.theme-light .bodyButtonColor__6bc46:active { - background: var(--primary-300) -} - -.giftIcon__6bc46 { - height: 40px; - width: 40px -} - -.bodyButtonColor__6bc46 { - background: var(--background-base-lower) -} - -.marginContainer_d4883c { - margin-top: 20px -} - -.gradientContainer_d4883c { - border: 1px solid transparent -} - -.promoHeaderContainer_d4883c { - align-items: center; - display: flex; - flex-direction: row -} - -.promoDescription_d4883c { - margin-top: var(--space-4) -} - -.promoNitroButton_d4883c { - margin-inline-start:auto} - -.skuCard_d4883c { - margin-top: 24px -} - -.emptyState_d4883c { - align-items: center; - display: flex; - flex-direction: column -} - -.emptyStateHeader_d4883c { - color: var(--text-default); - font-size: 24px; - line-height: 1.17; - margin-top: 20px -} - -.emptyStateSubtext_d4883c { - color: var(--text-muted); - line-height: 1.25; - margin-top: 8px -} - -.emptyStateImage_d4883c { - height: 202px; - margin-top: 120px; - width: 404px -} - -.loading_d4883c { - margin-top: 40px -} - -.promotionCardDescriptionWithCode_d4883c { - cursor: text; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.dropsHeaderContainer_d4883c { - align-items: center; - display: flex -} - -.betaTagIcon_d4883c { - margin-inline-start:8px} - -.divider_d4883c { - margin: 8px 0 24px -} - -.promotionCard_d4883c { - align-items: flex-start; - background-color: var(--background-base-lowest); - border-radius: var(--radius-xs); - display: flex; - flex-direction: column; - padding: 20px -} - -.mainPromotionCardContainer_d4883c { - align-items: center; - display: flex; - justify-content: space-between; - width: 100% -} - -.promotionCardLeftContainer_d4883c { - display: flex -} - -.redeemTooltipContent_d4883c { - text-align: center -} - -.promotionIcon_d4883c { - align-items: center; - align-self: flex-start; - background-color: var(--background-mod-subtle); - border-radius: var(--radius-xs); - display: flex; - flex-shrink: 0; - height: 40px; - justify-content: center; - margin-inline-end:10px;width: 40px -} - -.promotionIconImage_d4883c { - max-height: 36px; - max-width: 36px -} - -.dropRewardImage_d4883c { - border-radius: var(--radius-xs); - width: 100% -} - -.dropCriteriaText_d4883c { - margin: 16px 0 -} - -.dropCriteriaText_d4883c>p { - margin-bottom: 2px; - margin-top: 2px -} - -.dropLearnMore_d4883c { - margin-bottom: 16px -} - -.promotionLegalese_d4883c { - border-top: 1px solid var(--border-subtle); - font-size: 12px; - line-height: 16px; - margin-top: 8px; - margin-inline-start:52px;margin-bottom: 0; - padding-top: 8px -} - -.promotionLegalese_d4883c em { - font-style: italic -} - -.dropContainer_d4883c { - border-radius: var(--radius-xs); - margin-bottom: 20px; - overflow: hidden -} - -.availableUntil_d4883c { - padding-top: 4px -} - -.dropCard_d4883c { - align-items: flex-start; - background-color: var(--background-base-lowest); - display: flex; - flex-direction: column; - padding: 20px -} - -.dropCriteria_d4883c { - align-items: center; - background-color: var(--background-mod-normal); - padding: 16px 16px 8px -} - -.feedback_d4883c { - background: var(--background-base-lowest); - padding: 8px -} - -.dropsHelpText_d4883c,.feedback_d4883c { - border-radius: var(--radius-xs); - color: var(--interactive-text-active); - font-size: 14px; - font-weight: var(--font-weight-medium); - margin-bottom: 20px; - margin-top: -12px -} - -.dropsHelpText_d4883c { - background: var(--background-base-lower); - line-height: 18px; - padding: 3px; - padding-inline:18px} - -.theme-dark .emptyStateImage_d4883c { - background: url(/assets/b584f76d3ff86700.svg) -} - -.theme-light .emptyStateImage_d4883c { - background: url(/assets/28fe27f3657e23ee.svg) -} - -.premiumIcon_d4883c { - height: 24px; - margin-inline-end:6px;position: relative; - top: 2px; - width: 24px -} - -.tier2Gradient_d4883c { - color: var(--premium-tier-2-pink) -} - -.container_f89b2c { - background-color: var(--input-background-default); - border: 1px solid; - border-color: var(--input-border-default); - border-radius: var(--radius-sm); - box-sizing: border-box; - cursor: pointer; - height: 40px; - min-height: 44px; - position: relative -} - -.layout_f89b2c { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.base_f89b2c { - background-color: transparent; - font-size: 16px; - line-height: 22px; - overflow: hidden; - padding-block:10px;padding-inline:10px 0;text-overflow: ellipsis; - transition: color .15s ease; - white-space: nowrap -} - -.hiddenMessage_f89b2c { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - z-index: 0 -} - -.input_f89b2c { - border: none; - box-sizing: border-box; - color: var(--text-strong); - flex: 1 1 auto; - min-width: 0; - position: relative; - width: 100%; - z-index: 1 -} - -.input_f89b2c::-moz-placeholder { - color: var(--input-placeholder-text-default) -} - -.input_f89b2c::placeholder { - color: var(--input-placeholder-text-default) -} - -.button_f89b2c { - border-radius: var(--radius-xs); - margin-block:4px;margin-inline:0 4px;padding: 2px 20px; - transition: background-color .2s ease-in-out,color .2s ease-in-out -} - -.disabled_f89b2c { - cursor: not-allowed; - opacity: .3 -} - -.disabled_f89b2c .button_f89b2c { - opacity: 1 -} - -.recorderContainer__2636e { - cursor: pointer; - transition: border .15s ease -} - -.recorderContainer__2636e:not(.containerDisabled__2636e):focus-within,.recorderContainer__2636e:not(.containerDisabled__2636e):hover { - border-color: hsl(var(--red-400-hsl)/.3) -} - -.containerDisabled__2636e { - cursor: not-allowed -} - -.recorderLayout__2636e { -} - -.addKeybindButton__2636e { - overflow: hidden; - position: relative; - transition: background-color .2s ease-in-out,color .2s ease-in-out,width .2s ease-in-out; - white-space: nowrap; - width: auto -} - -.recorderContainer__2636e.recording__2636e { - animation: shadowPulse__2636e 1s ease-in infinite; - border-color: hsl(var(--red-400-hsl)/.6); - box-shadow: 0 0 6px hsl(var(--red-400-hsl)/.3); - color: var(--text-feedback-critical) -} - -.recorderContainer__2636e.recording__2636e .addKeybindButton__2636e { - background-color: hsl(var(--red-400-hsl)/.1); - color: var(--text-feedback-critical) -} - -.recorderContainer__2636e.recording__2636e .keybindInput__2636e { - color: var(--text-feedback-critical) -} - -.recorderContainer__2636e.recording__2636e .keybindInput__2636e::-moz-placeholder { - color: transparent -} - -.recorderContainer__2636e.recording__2636e .keybindInput__2636e::placeholder { - color: transparent -} - -.keybindInput__2636e { - font-size: 14px; - font-weight: var(--font-weight-semibold); - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -@keyframes shadowPulse__2636e { - 0% { - box-shadow: 0 0 6px hsl(var(--red-400-hsl)/.3) - } - - 50% { - box-shadow: 0 0 10px hsl(var(--red-400-hsl)/.6) - } - - to { - box-shadow: 0 0 6px hsl(var(--red-400-hsl)/.3) - } -} - -.buttonContainer__2636e { - max-width: 128px; - padding: 4px -} - -.container__7700a { - align-items: center; - background-color: var(--background-base-lower); - border-radius: var(--radius-sm); - display: flex; - flex-direction: row; - gap: 12px; - padding: 16px -} - -.textContainer__7700a { - display: flex; - flex-direction: column; - flex-grow: 1; - gap: 2px -} - -.iconContainer__7700a { - align-items: center; - background-color: var(--background-feedback-warning); - border-radius: var(--radius-sm); - display: flex; - height: 40px; - justify-content: center; - width: 40px -} - -.gameIconContainer__7700a { - position: relative -} - -.warningCorner__7700a { - align-items: center; - background-color: var(--background-base-lower); - border-radius: var(--radius-round); - display: flex; - inset-inline-end: 0; - justify-content: center; - padding: 2px; - position: absolute; - top: 0 -} - -.channelIdSection__25eff { - margin-top: 8px -} - -.guildName__25eff { - flex-shrink: 1; - font-size: 12px; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: pre -} - -.selectedVoiceChannel__25eff { - align-items: stretch; - border: 1px solid var(--border-subtle); - border-radius: var(--radius-xs); - display: flex; - flex-direction: column; - flex-grow: 1; - flex-shrink: 1; - justify-content: center; - margin-inline-end:8px;min-height: 0; - min-width: 0; - pointer-events: none -} - -.noVoiceChannelSelected__25eff { - padding: 0 16px -} - -.voiceListSearchEmpty__25eff { - padding: 16px; - text-align: center -} - -.keybindMessage__740f2 { - position: relative -} - -.keybindMessage__740f2 a { - color: inherit; - text-decoration: underline -} - -.item__740f2 { - flex: 1; - margin-inline-end:16px;position: relative -} - -.item__740f2 a { - color: inherit; - text-decoration: underline -} - -.switch__740f2 { - align-items: center; - display: flex; - flex: 0; - margin-top: 28px; - margin-inline-start:32px} - -.removeKeybindFloating__740f2 { - inset-inline-end: -31px; - opacity: 0; - position: absolute; - top: -12px -} - -.removeKeybind__740f2 { - align-items: center; - cursor: pointer; - display: flex; - flex: 0; - margin-top: 28px -} - -.ghostPill__740f2 { - align-items: center; - background-color: var(--yellow-160); - border: 1px solid var(--yellow-300); - color: var(--primary-860); - display: inline-grid; - grid-template-columns: auto 1fr; - padding: 10px; - grid-gap: 10px; - border-radius: var(--radius-xs); - font-size: 14px -} - -.keybindGroup__740f2 { - margin-top: -1px; - padding-bottom: 20px; - padding-top: 20px; - position: relative -} - -.keybindGroup__740f2:hover .removeKeybindFloating__740f2 { - opacity: 1 -} - -.keybindGroupContent__740f2 { - align-items: center; - display: flex; - flex-direction: row; - margin-bottom: 8px -} - -.keyboard-mode .keybindGroup__740f2:focus-within .removeKeybindFloating__740f2 { - opacity: 1 -} - -.defaultKeybindGroup__740f2 { - background-color: var(--background-surface-higher); - border: 1px solid var(--border-muted); - border-radius: var(--radius-md); - display: flex; - flex-direction: column; - gap: var(--space-16); - padding: var(--space-16) 0 -} - -.defaultKeybind__740f2 { - align-items: center; - box-sizing: border-box; - display: grid; - font-weight: var(--font-weight-medium); - grid-template-columns: 1fr auto; - padding: 0 var(--space-16) -} - -.defaultKeybindShortcutGroup__740f2 { - display: grid; - grid-gap: 4px -} - -.shortcut__740f2 { - justify-content: end -} - -.defaultKeybindGroupHeader__740f2 { - margin-bottom: 24px; - text-transform: uppercase -} - -.defaultKeybindGroupWithDescription__740f2 { - margin-bottom: 4px -} - -.defaultKeybindGroupDescription__740f2 { - margin-bottom: 20px -} - -.browserNotice__740f2 a { - color: inherit; - text-decoration: underline -} - -.systemServiceUpsell__740f2 { - margin-bottom: 24px -} - -.ctaContainer__740f2 { - gap: 12px; - margin-bottom: 24px -} - -.ctaContainer__740f2,.warning__740f2 { - align-items: center; - display: flex; - flex-direction: row -} - -.warning__740f2 { - flex-grow: 1; - gap: 8px -} - -.addButton__740f2 { - flex-shrink: 0; - margin-inline-start:12px} - -.emptyWidgetContainer_cc1cc5 { - align-items: center; - display: flex; - flex-direction: column; - height: calc(100% - 32px); - justify-content: center; - padding: 16px; - width: calc(100% - 32px) -} - -.emptyWidgetContainer_cc1cc5.absolute_cc1cc5 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.emptyWidgetIcon_cc1cc5 { - opacity: .24 -} - -.effect_adebba { - position: absolute -} - -.username_adebba { - align-items: center; - background-color: rgba(19,19,24,.8); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-border) var(--shadow-high); - box-sizing: border-box; - cursor: pointer; - display: flex; - flex-shrink: 1; - gap: 8px; - height: 24px; - justify-content: center; - margin-inline-start:var(--space-4);max-width: 100%; - opacity: .8; - overflow: hidden; - padding: var(--space-4) var(--space-8); - text-overflow: ellipsis; - white-space: nowrap -} - -.username_adebba.streaming_adebba { - padding-inline-end:var(--space-4)} - -.voiceUserContainer_adebba { - display: flex; - flex-direction: column; - gap: var(--space-8) -} - -.voiceUser_adebba { - align-items: center; - cursor: pointer; - display: flex; - flex-direction: row; - opacity: .4; - position: relative; - transition: opacity .1s ease-in-out -} - -.voiceUser_adebba.flipped_adebba { - flex-direction: row; - justify-content: flex-end -} - -.voiceUser_adebba.flipped_adebba .avatar_adebba { - margin-inline:0} - -.voiceUser_adebba.flipped_adebba .username_adebba { - margin-inline:0 var(--space-4);padding-inline-start: var(--space-4) -} - -.voiceUser_adebba.flipped_adebba .voiceIcons_adebba { - margin-inline-end:var(--space-4)!important} - -.clanTag_adebba { - margin-inline-start: var(--space-4) -} - -.voiceIcons_adebba { - margin-inline:0!important} - -.voiceIcon_adebba { - color: var(--white)!important; - display: block; - flex-shrink: 0; - margin-inline-start:0!important;opacity: .5 -} - -.interactive_adebba { - opacity: 1!important -} - -.speaking_adebba { - opacity: .9 -} - -.speaking_adebba .avatarSpeakingOutline_adebba { - outline: 1px solid var(--green-300); - outline-offset: 2px -} - -.justConnected_adebba { - opacity: .6 -} - -.connectedAnimationContainer_adebba { - background-color: #131318; - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - bottom: 4px; - inset-inline-start: 36px; - overflow: hidden; - position: absolute; - top: 4px; - z-index: 100 -} - -.connectedAnimationContainer_adebba.right_adebba { - inset-inline-end: 36px; - inset-inline-start: unset -} - -.emptySpace_adebba { - height: 0; - width: 0 -} - -.connectedAnimationInnerContainer_adebba { - align-items: center; - display: flex; - gap: var(--space-4); - height: 100%; - justify-content: flex-start; - overflow: hidden; - white-space: nowrap; - width: -moz-fit-content; - width: fit-content -} - -.connectedAnimationInnerContainer_adebba.exiting_adebba { - max-width: 100% -} - -.connectedAnimationInnerContainer_adebba.left_adebba { - padding-inline-start:8px;text-align: start -} - -.connectedAnimationInnerContainer_adebba.right_adebba { - padding-inline-end:4px;text-align: end -} - -.animation_adebba { - height: 24px; - width: 24px -} - -.hiddenVoiceStates_adebba { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-4); - opacity: 1 -} - -.hiddenVoiceStates_adebba.locked_adebba { - opacity: .4 -} - -.hiddenVoiceStates_adebba.flipped_adebba { - flex-direction: row-reverse -} - -.hiddenVoiceStatesAvatar_adebba { - justify-content: center -} - -.hiddenVoiceStatesAvatar_adebba,.hiddenVoiceStatesText_adebba { - align-items: center; - display: flex; - flex-direction: row -} - -.hiddenVoiceStatesText_adebba { - background-color: rgba(19,19,24,.8); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-border) var(--shadow-high); - cursor: pointer; - flex-shrink: 1; - opacity: .8; - padding: var(--space-4) var(--space-8) -} - -.key__98feb { - background: var(--background-base-low); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-xs); - color: var(--text-muted); - font-family: var(--font-code); - font-size: 10px; - font-style: normal; - font-weight: 600; - height: 16px; - line-height: 14px; - text-transform: uppercase -} - -.key__98feb+.key__98feb { - margin-inline-start:0} - -.keySpan__98feb { - box-sizing: border-box; - display: inline-block; - margin-inline:var(--space-4);padding: 0 var(--space-4) -} - -.keyDiv__98feb { - align-items: center; - display: flex; - font-size: 10px; - height: 10px; - justify-content: center; - line-height: 12px; - padding: 2px 4px -} - -.bgShade__6d791 { - background-color: rgba(19,19,24,.9); - border: 1px solid hsla(0,0%,100%,.08); - box-sizing: border-box -} - -.keybind__6d791 { - background-color: var(--background-mod-strong); - border-color: var(--border-strong); - color: var(--text-strong) -} - -.titleWrapper__6d791 { - align-items: center; - border-radius: 12px; - display: flex; - flex-direction: row; - justify-content: flex-start; - max-width: 100%; - padding: 2px 8px -} - -.titleWrapperWithHint__6d791 { - border-radius: 8px -} - -.titleWrapperClickable__6d791 { - cursor: pointer; - height: auto -} - -.extrasEmptySpace__6d791 { - flex: 1; - height: 24px -} - -.button__6d791 { - align-items: center; - border-radius: 50%; - color: var(--text-strong); - cursor: pointer; - display: flex; - height: 24px; - justify-content: center; - width: 24px -} - -.button__6d791.active__6d791 { - border-color: var(--background-brand) -} - -.localizedName_e42467 { - font-size: 14px; - font-weight: var(--font-weight-normal); - line-height: 20px -} - -.flag_e42467 { - flex: 0 0 auto; - margin: 0 8px -} - -.flagImage_e42467 { - display: block; - filter: saturate(var(--saturation-factor,1)); - height: 18px; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - width: 27px -} - -.enableOverlayItemAction__9d98d { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px; - justify-content: flex-end -} - -.collapseable__9d98d { - background-color: var(--background-base-lower); - border: 1px solid var(--background-mod-muted); - border-radius: var(--radius-sm); - overflow: hidden -} - -.notificationSettingsContainer__9d98d { - display: flex; - flex-direction: column; - gap: 16px; - padding: 12px 0 -} - -.notificationSettingsContainer__9d98d .notificationItem__9d98d { - align-items: center; - border-bottom: 1px solid var(--background-mod-muted); - display: flex; - flex-direction: row; - gap: 12px; - justify-content: space-between; - padding: 8px 0 -} - -.notificationSettingsContainer__9d98d .notificationItem__9d98d .notificationDescriptionContainer__9d98d { - display: flex; - flex-direction: column; - gap: 2px -} - -.groupContainer__9d98d,.mainCollapseableContainer__9d98d { - background-color: var(--background-base-lower) -} - -.groupContainer__9d98d { - border-radius: var(--radius-sm); - flex: 1; - overflow: hidden; - transition: background-color .1s ease-in-out -} - -.groupWarning__9d98d { - cursor: default -} - -.standaloneContainer__9d98d { - background-color: var(--background-base-lower); - border: 1px solid var(--background-mod-muted); - border-radius: var(--radius-sm) -} - -.groupCollapsedContainer__9d98d { - align-items: stretch; - background-color: var(--background-base-lower); - display: flex; - flex: 1; - flex-direction: column; - gap: 8px; - padding: 0; - position: relative; - width: 100%; - z-index: 1 -} - -.groupCollapsed__9d98d,.groupCollapsedAction__9d98d { - align-items: center; - display: flex; - flex-direction: row; - justify-content: space-between -} - -.groupCollapsedAction__9d98d { - gap: 8px -} - -.emptySpacer__9d98d { - height: 20px; - width: 20px -} - -.playingDot__9d98d { - background-color: var(--text-feedback-positive); - border-radius: 50%; - height: 8px; - margin: 2px; - width: 8px -} - -.groupHeader__9d98d { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px; - justify-content: flex-start; - padding: 8px 16px -} - -.expandedContainer__9d98d,.groupHeader__9d98d { - background-color: var(--background-base-lowest) -} - -.groupContent__9d98d { - gap: 12px; - justify-content: space-between; - padding: 16px 16px 12px -} - -.groupContent__9d98d,.groupTitle__9d98d { - align-items: center; - display: flex; - flex-direction: row -} - -.groupTitle__9d98d { - gap: 4px; - justify-content: flex-start -} - -.groupIcon__9d98d { - align-items: center; - display: flex; - justify-content: center -} - -.groupMainContent__9d98d { - display: flex; - flex: 1; - flex-direction: column; - gap: 2px -} - -.subtitleContainer__9d98d { - padding: 12px 16px 4px -} - -.scroller__9d98d { - max-height: 30vh -} - -.groupAction__9d98d { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px; - justify-content: flex-end -} - -.gameIcon__9d98d { - height: 32px; - width: 32px -} - -.overlayEnabledSettingsContainer__9d98d { - display: flex; - flex-direction: column; - gap: 12px; - padding: 12px 0 -} - -.limitedInteractionOverrideContainer__9d98d { - display: flex; - flex: 1; - flex-direction: column; - gap: 0 -} - -.settingsDivider__9d98d { - background-color: var(--background-mod-muted); - height: 1px; - margin: 16px 0; - width: 100% -} - -.rowContainer__9d98d { - align-items: center; - display: flex; - flex: 1; - flex-direction: row; - gap: 12px; - justify-content: space-between -} - -.keybindRecorderContainer__9d98d { - flex-basis: 50%; - max-width: 280px -} - -.keybindHeaderContainer__9d98d { - flex-basis: auto -} - -.keybindMainContainer__9d98d { - align-items: flex-start; - display: flex; - flex: 1; - flex-direction: column; - gap: 4px; - justify-content: flex-start -} - -.keybindAdminWarning__9d98d { - padding-bottom: 8px; - padding-top: 4px -} - -.keybindContainer__9d98d { - align-items: center; - display: flex; - flex: 1; - flex-direction: row; - gap: 8px; - justify-content: space-between; - width: 100% -} - -.voiceSettingsContainer__9d98d { - display: grid; - gap: 24px; - grid-template-columns: calc(35% - 24px) calc(35% - 24px) 30%; - grid-template-rows: repeat(2,auto) -} - -.voiceSettingsContainer__9d98d>:nth-child(3) { - grid-column: 3; - grid-row: 1/span 2 -} - -.voiceWidgetContainer__9d98d { - align-items: flex-start; - background: rgba(0,0,0,.16); - border: 1px dashed hsla(0,0%,100%,.16); - border-radius: 12px; - display: flex; - flex-direction: column; - gap: 8px; - padding: 12px; - width: 100% -} - -.widgetSettingsContainer__9d98d { - align-items: center; - background-color: var(--background-mod-subtle); - border-radius: 8px; - display: flex; - flex-direction: column; - justify-content: center; - min-height: 270px; - padding: 32px; - width: 100% -} - -.widgetContainer__9d98d { - align-items: flex-start; - display: flex; - flex-direction: column; - gap: 4px; - padding-inline-start:24px} - -.select__9d98d { - width: 100% -} - -.widgetHeaderContainer__9d98d { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px -} - -.sliderContainer__9d98d { - padding-inline-start:4px;width: 100% -} - -.formItemTitle__9d98d { - margin-bottom: 4px -} - -.overlaySettingsNux__9d98d { - align-items: center; - display: flex; - flex-direction: column; - gap: 32px; - justify-content: center; - padding: 12px; - position: relative; - width: 100% -} - -.closeCircleButton__9d98d { - cursor: pointer; - inset-inline-end: 12px; - position: absolute; - top: 24px; - z-index: 1 -} - -.nuxFirstImage__9d98d { - height: 371px; - width: 660px -} - -.nuxUserSettingsImage__9d98d { - height: 188px; - width: 660px -} - -.mainTitleContainer__9d98d { - align-items: center; - display: flex; - flex-direction: column; - gap: 12px; - justify-content: center; - text-align: center; - width: 360px -} - -.nuxDivider__9d98d { - background-color: var(--background-mod-strong); - height: 1px; - margin: 16px 0; - width: 100% -} - -.systemServiceWarning__9d98d { - flex-shrink: 1; - margin-inline:16px;margin-bottom: 16px; - margin-top: 4px -} - -.imageWrapper__5ef8c { - align-items: center; - background-color: var(--white); - border-radius: 60px; - display: flex; - height: 60px; - justify-content: center; - overflow: hidden; - position: relative; - width: 60px; - z-index: 1 -} - -.image__5ef8c { - height: 80%; - width: 80%; - z-index: 1 -} - -.cooldown__5ef8c { - background-color: var(--blue-200) -} - -.cooldownImage__5ef8c { - height: 26px -} - -.boostIconContainer__8dbf5 { - border-radius: 100%; - box-shadow: var(--elevation-medium); - position: relative -} - -.boostIcon__8dbf5 { - height: 36px; - width: 36px -} - -.boostIconCanceled__8dbf5 { - display: block -} - -.snowflake__8dbf5 { - inset-inline-start: -16px; - position: absolute; - top: 2px; - z-index: 1 -} - -.guildContainer__5dba5 { - align-items: center; - background-color: var(--background-mod-normal); - display: flex; - flex-direction: row; - padding: 30px 18px; - position: relative -} - -.guildInfoContainer__5dba5 { - display: flex; - flex: 1; - flex-direction: column; - margin: 0 16px -} - -.guildBoostStatsContainer__5dba5 { - align-items: center; - display: flex; - flex-direction: row; - margin-top: 6px -} - -.separator__5dba5 { - border-inline-end:1px solid var(--text-muted);height: 18px; - margin: 0 10px; - opacity: .5 -} - -.guildBoostBadge__5dba5 { - color: var(--guild-boosting-pink); - height: 16px; - margin-inline-end:6px;width: 16px -} - -.sparkle__5dba5 { - color: var(--white); - position: absolute; - z-index: 1 -} - -.theme-light .sparkle__5dba5 { - color: var(--guild-boosting-pink) -} - -.sparkle1__5dba5 { - bottom: 9px; - inset-inline-start: 0 -} - -.sparkle2__5dba5 { - inset-inline-end: 9px; - top: -15px -} - -.sparkle3__5dba5 { - inset-inline-end: 0; - top: 24px -} - -.sparkle4__5dba5 { - inset-inline-end: -22px; - top: 28px -} - -.sideSparkleContainer__5dba5 { - background-image: radial-gradient(100% 100% at center,#fff 0,hsla(0,0%,100%,0) 50%); - position: absolute; - width: 1px; - z-index: 1 -} - -.theme-light .sideSparkleContainer__5dba5 { - background-image: radial-gradient(100% 100% at center,hsl(var(--guild-boosting-pink-hsl)/1) 0,hsl(var(--guild-boosting-pink-hsl)/0) 50%) -} - -.sideSparkle__5dba5 { - inset-inline-start: -8px -} - -.container_ceb06a,.wrapper_ceb06a { - display: flex; - flex-direction: column; - margin-bottom: 30px -} - -.container_ceb06a { - gap: 12px -} - -.appliedBoostContainer_ceb06a { - background-color: var(--background-mod-normal); - border-radius: var(--radius-sm); - display: flex; - flex-direction: column; - overflow: hidden -} - -.guildContainer_ceb06a { - align-items: center; - display: flex; - flex-direction: row; - padding: 18px -} - -.guildInfoContainer_ceb06a { - display: flex; - flex: 1; - flex-direction: column; - margin: 0 15px -} - -.guildBoostStatsContainer_ceb06a { - align-items: center; - display: flex; - flex-direction: row; - margin-top: 6px -} - -.separator_ceb06a { - border-inline-end:1px solid var(--text-muted);height: 18px; - margin: 0 10px -} - -.guildBoostBadge_ceb06a { - color: var(--guild-boosting-pink); - margin-inline-end:6px} - -.boostContainer_ceb06a { - background-color: var(--primary-800); - display: flex; - flex-direction: column; - padding: 0 20px -} - -.theme-light .boostContainer_ceb06a { - background-color: var(--primary-200) -} - -.boostInnerContainer_ceb06a { - align-items: center; - display: flex; - flex-direction: row; - padding: 6px 0 -} - -.boostContainerSeparator_ceb06a { - background-color: var(--primary-500); - height: 1px; - opacity: .48 -} - -.theme-light .boostContainerSeparator_ceb06a { - opacity: .2 -} - -.cancel_ceb06a { - color: var(--text-feedback-warning) -} - -.boostDescriptionContainer_ceb06a { - flex: 1; - margin: 0 10px; - position: relative -} - -.boostDescriptionInnerContainer_ceb06a { - position: absolute; - transform: translateY(-50%); - width: 100% -} - -.boostSlotMenuIcon_ceb06a { - color: var(--interactive-text-default); - cursor: pointer; - display: flex -} - -.mainSeparator_ceb06a { - background-color: var(--primary-500); - height: 1px; - opacity: .48 -} - -.theme-light .mainSeparator_ceb06a { - opacity: .3 -} - -.wrapper__8e08c { - isolation: isolate; - position: relative -} - -.gradient__8e08c { - background: center 15%/90% auto url(/assets/63ac51b66622ae3d.svg) no-repeat,linear-gradient(180deg,var(--premium-tier-0-header-gradient-2) 0,var(--premium-tier-0-header-gradient-3) 60%,var(--premium-tier-0-header-gradient-4) 120%); - border-radius: 8px 8px 0 0; - overflow: hidden; - padding: 76px 56px 36px; - position: relative; - z-index: 1 -} - -.guildBoostGemWrapper__8e08c { - background-color: var(--white); - border-radius: 34px; - box-shadow: var(--elevation-medium); - height: 68px; - overflow: hidden; - top: 0; - transform: translate(-50%,-25%); - width: 68px; - z-index: 2 -} - -.guildBoostGem__8e08c,.guildBoostGemWrapper__8e08c { - inset-inline-start: 50%; - position: absolute -} - -.guildBoostGem__8e08c { - height: 60px; - top: 50%; - transform: translate(-50%,-50%) -} - -.heading__8e08c { - color: var(--text-strong); - position: relative; - text-align: center; - z-index: 2 -} - -.heading__8e08c p { - margin: 0 -} - -.wave__8e08c { - bottom: -96px; - fill: var(--background-base-low); - inset-inline-start: 0; - position: absolute; - width: 100%; - z-index: 1 -} - -.wrapper__834f5 { - align-items: center; - background-color: var(--background-mod-normal); - border-radius: var(--radius-sm); - display: flex; - flex-direction: row; - margin-bottom: 50px; - padding: 30px 18px; - position: relative -} - -.icon__834f5 { - border-radius: 100%; - height: 60px; - width: 60px -} - -.info__834f5 { - display: flex; - flex: 1; - flex-direction: column; - margin: 0 16px -} - -.wrapper__37ae5 { - background-color: var(--background-mod-normal); - border-radius: var(--radius-sm); - padding: 16px 18px -} - -.copy__37ae5,.wrapper__37ae5 { - align-items: center; - display: flex -} - -.cta__37ae5 { - color: var(--text-link)!important; - display: inline; - height: auto; - margin-inline-start:4px;min-height: auto; - min-width: auto; - padding: 0 -} - -.boostIcon__37ae5 { - color: var(--guild-boosting-pink); - height: 16px; - margin-inline-end:4px;width: 16px -} - -.wrapper__51537 { - display: flex; - flex-direction: column; - gap: 10px; - margin-bottom: 50px -} - -.header__51537 { - margin-bottom: 20px; - margin-top: 30px -} - -.recommendedServerCard__51537 { - border-radius: var(--radius-sm); - position: relative -} - -.wrapper__686ce { - display: flex; - flex-direction: column; - margin-bottom: 40px; - padding: 0 70px; - text-align: center -} - -.subtitle__686ce { - margin-bottom: 6px -} - -.subscription_d970da { - align-items: center; - background-color: var(--background-base-lower); - border-radius: 8px; - display: flex; - padding: 16px -} - -.subscriptionInfo_d970da { - margin-inline-start:16px} - -.tierInfo_d970da { - align-items: center; - display: flex -} - -.tierPill_d970da { - align-items: center; - align-self: flex-start; - background-color: var(--background-mod-normal); - border-radius: 11px; - display: inline-flex; - font-size: 12px; - line-height: 14px; - padding-block:4px;padding-inline:4px 8px} - -.tierPill_d970da,.tierPillStar_d970da { - color: var(--text-default) -} - -.tierPillStar_d970da { - height: 16px; - margin-inline-end:5px;width: 16px -} - -.guildIcon_d970da { - background-color: var(--background-base-low) -} - -.guildName_d970da { - color: var(--text-strong); - flex: 1; - font-size: 16px; - font-weight: var(--font-weight-bold); - line-height: 20px; - margin-bottom: 8px -} - -.levelIcon_d970da { - height: 16px; - margin: 0 4px; - width: 16px -} - -.levelDownIcon_d970da { - color: var(--icon-feedback-critical) -} - -.levelUpIcon_d970da { - color: var(--green-360) -} - -.theme-light .boost_d970da { - background-color: var(--primary-230) -} - -.theme-light .guildName_d970da { - color: var(--primary-500) -} - -.tierPillGem_d970da { - color: var(--text-muted) -} - -.content__2cbe8 { - display: flex; - flex-direction: column; - padding-bottom: 16px -} - -.guildCard__2cbe8 { - margin: 16px 0 -} - -.image__2cbe8 { - align-self: center; - background-repeat: no-repeat; - background-size: 100%; - flex-shrink: 0; - margin: 20px 0 -} - -.transferGuildCardHeader__2cbe8 { - margin-top: 16px; - text-transform: uppercase -} - -.activeTransferGuildCardBorder__2cbe8 { - background: linear-gradient(to left,var(--guild-boosting-purple),var(--guild-boosting-blue)); - padding: 2px -} - -.activeTransferGuildCardBorder__2cbe8,.transferFromGuildCard__2cbe8 { - border-radius: 8px; - margin-top: 8px -} - -.transferToGuildCard__2cbe8 { - border-radius: 8px -} - -.error__2cbe8 { - margin: 16px -} - -.pendingCancellation__2cbe8 { - align-items: center; - background: var(--background-feedback-warning); - border: 1px solid var(--icon-feedback-warning); - border-radius: 4px; - display: flex; - margin-top: 16px; - padding: 8px 16px -} - -.pendingCancellationIcon__2cbe8 { - color: var(--text-feedback-warning); - flex-shrink: 0 -} - -.pendingCancellationMessage__2cbe8 { - color: var(--text-default); - margin-inline-start:10px} - -.footer__2cbe8 { - padding: 16px 24px -} - -.modal__39466 { - overflow: hidden -} - -.modalCloseButton__39466 { - inset-inline-end: 12px; - opacity: .8; - position: absolute; - top: 12px -} - -.modalCloseButton__39466:hover { - opacity: 1 -} - -.modalContent__39466 { - padding: 8px 0 -} - -.selectHeaderContainer__39466 { - align-items: flex-start; - display: flex; - flex-direction: column; - z-index: auto -} - -.selectHeader__39466 { - margin-bottom: 16px -} - -.selectGuild__39466 { - align-items: center; - border-radius: 4px; - cursor: pointer; - display: flex; - padding: 8px 6px -} - -.selectGuild__39466:hover { - background-color: var(--background-mod-normal) -} - -.selectGuildIcon__39466 { - flex: 0 0 auto -} - -.selectGuildName__39466 { - flex: 0 1 auto; - margin-inline-start:12px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.selectGuildLevel__39466 { - flex: 0 0 auto; - margin-inline-start:6px} - -.selectGuildCopy__39466 { - align-items: baseline; - display: flex; - flex: 1 1 auto; - overflow: hidden -} - -.selectGuildPseudoCta__39466 { - background: var(--brand-500); - border-radius: 4px; - flex: 0 0 auto; - margin-inline-start:6px;padding: 6px 16px -} - -.emptyStateWrapper__39466 { - padding: 8px 16px; - text-align: center -} - -.emptyStateWrapper__39466 p { - margin: 0 -} - -.emptyStateWrapper__39466 a:hover { - text-decoration: underline -} - -.animationContainer__884d1,.container__884d1 { - position: relative; - z-index: 0 -} - -.animationContainer__884d1 { - border-radius: 8px; - display: flex; - flex-direction: column; - overflow: hidden -} - -.svgBorder__884d1 { - height: calc(100% + 2px); - inset-inline-start: -1px; - position: absolute; - top: -1px; - width: calc(100% + 2px); - z-index: 1 -} - -.svgCopy__884d1 { - display: block -} - -.animationContainer__884d1 { - color: var(--text-strong) -} - -.shine__884d1 { - color: var(--opacity-white-48) -} - -.upsellFooter__5e07c { - align-items: center; - background-color: var(--background-base-lower); - border-radius: 8px; - color: var(--text-default); - display: flex; - font-size: 14px; - line-height: 18px; - margin-top: 16px; - padding: 8px -} - -.upsellFooterIcon__5e07c { - flex-shrink: 0; - height: 24px; - margin-inline-end:8px;width: 24px -} - -.reverseTrialUpsellContainer__5e07c { - border-radius: 4px; - box-shadow: 0 0 40px 0 rgba(229,85,211,.5); - position: relative -} - -.unlockIcon__5e07c { - height: 46px; - inset-inline-start: 7px; - pointer-events: none; - position: absolute; - top: -14px; - width: 46px -} - -.upsellText__5e07c { - padding-inline-start:48px} - -.bodyText__945b8 { - color: var(--text-default); - font-size: 16px; - line-height: 20px -} - -.planSelectText__945b8 { - margin-bottom: 16px -} - -.planSelectRow__945b8 { - align-items: center; - display: flex; - justify-content: space-between; - margin: 8px auto -} - -.planSelectRowALaCarte__945b8 { - justify-content: flex-start -} - -.planSelectorWrapper__945b8 { - align-items: center; - display: flex -} - -.planSelectorHeader__945b8 { - font-weight: var(--font-weight-semibold); - margin-bottom: 8px -} - -.planSelectorRadioBarALaCarte__945b8 { - align-items: flex-start -} - -.planSelectorRadioIconALaCarte__945b8 { - margin-top: 4px -} - -.planSelectorPlanName__945b8 { - margin-inline-end:4px} - -.planSelectorLabel__945b8 { - color: var(--text-default); - font-size: 16px; - line-height: 20px; - margin-inline-start:16px} - -.planSelectorPreviewPrice__945b8 { - color: var(--interactive-text-default); - font-size: 16px; - line-height: 20px -} - -.planSelectDivider__945b8 { - background-color: var(--border-subtle); - height: 1px; - margin: 16px 0 -} - -.planSelectorSubtotal__945b8 { - color: var(--interactive-text-active); - font-size: 16px; - font-weight: var(--font-weight-semibold); - line-height: 20px -} - -.invoiceTableBottom__945b8,.paymentSourceWrapper__945b8 { - margin: 16px 0 -} - -.taxInclusiveNote__945b8 { - margin-top: 12px -} - -.confirmationContainer__945b8 { - align-items: center; - display: flex; - flex-direction: column; - padding: 56px 32px 64px -} - -.confirmationAnimation__945b8 { - height: 140px; - margin-bottom: 32px; - width: 400px -} - -.confirmationText__945b8,.confirmationUpgradedBanner__945b8 { - margin-bottom: 16px -} - -.confirmationText__945b8 { - color: var(--interactive-text-default); - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px; - text-align: center -} - -.confirmationText__945b8 p { - margin: 0 -} - -.confirmationText__945b8 p+p { - margin-top: 8px -} - -.existingSlotNotice__945b8 { - align-items: center; - background-color: var(--background-base-lower); - border-radius: 8px; - color: var(--text-default); - display: flex; - font-size: 14px; - line-height: 18px; - margin-bottom: 16px; - margin-top: 16px; - padding: 8px -} - -.existingSlotIcon__945b8 { - flex-shrink: 0; - height: 24px; - margin-inline-end:8px;width: 24px -} - -.existingSlotTooltipWarningIcon__945b8 { - cursor: pointer; - height: 16px; - margin-inline-start:6px;vertical-align: text-bottom; - width: 14px -} - -.loadingSpinner__945b8 { - flex: 1; - max-width: 100px -} - -.reverseTrialContextInfoDivider__945b8 { - background-color: var(--border-subtle); - height: 1px -} - -.reverseTrialContextText__945b8 { - margin: 12px 0 -} - -.reverseTrialContextMarginBottom__945b8 { - margin-bottom: 8px -} - -.modal__0f8ce { - overflow: hidden -} - -.selectContent__0f8ce { - padding: 8px 0 -} - -.confirmImage__0f8ce { - height: 134px; - width: 264px -} - -.transferConfirmImage__0f8ce { - height: 128px; - width: 216px -} - -.successBody__0f8ce { - align-items: center; - display: flex; - flex-direction: column; - padding-bottom: 20px -} - -.successAnimation__0f8ce { - height: 200px; - width: 300px -} - -.quantitySelectorBody__0f8ce { - margin-top: 20px -} - -.quantitySelectorDescription__0f8ce,.quantitySelectorHeader__0f8ce { - margin-bottom: 16px -} - -.quantitySelectorWrapper__0f8ce { - align-items: center; - display: flex; - margin: 32px auto -} - -.quantitySelectorLabel__0f8ce { - line-height: 20px; - margin-inline-start:16px} - -.modalCloseButton__0f8ce { - inset-inline-end: 12px; - opacity: .8; - position: absolute; - top: 12px -} - -.modalCloseButton__0f8ce:hover { - opacity: 1 -} - -.images-light .confirmImage__0f8ce { - background-image: url(/assets/52c92d8492ebe041.svg) -} - -.images-light .transferConfirmImage__0f8ce { - background-image: url(/assets/2d416896e0bbb412.svg) -} - -.images-dark .confirmImage__0f8ce { - background-image: url(/assets/52c92d8492ebe041.svg) -} - -.images-dark .transferConfirmImage__0f8ce { - background-image: url(/assets/c278e895ac47b4af.svg) -} - -.tierPill__0f8ce { - background-color: var(--background-mod-normal); - color: var(--text-default) -} - -.header__7e090 { - background-image: url(/assets/cb4131669938b6ad.svg); - background-position: top; - background-repeat: no-repeat; - background-size: cover; - box-sizing: border-box; - height: 132px -} - -.animation__7e090 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.headerTitle__7e090 { - background-image: url(/assets/9f911879992ce2d1.svg); - height: 28px; - width: 60px -} - -.header__7e090 .closeButton__7e090 { - color: var(--white); - inset-inline-end: 12px; - opacity: .8; - position: absolute; - top: 12px -} - -.header__7e090 .closeButton__7e090:hover { - opacity: 1 -} - -.enable-forced-colors .header__7e090 .closeButton__7e090 { - opacity: 1 -} - -.body__968a4 { - color: var(--text-default); - font-size: 16px; - line-height: 20px; - padding-bottom: 16px -} - -.invoiceTable__968a4 { - margin: 16px 0 -} - -.cancelImage__968a4 { - background-image: url(/assets/14d412ef35277fe7.svg); - height: 128px; - margin: 0 auto 24px; - width: 270px -} - -html .invoiceCancelRow__968a4 { - color: var(--text-feedback-critical) -} - -.error__968a4 { - margin-bottom: 16px; - margin-top: 16px -} - -.textButton__968a4 { - margin-inline-end:12px} - -.body_fe75b7 { - color: var(--text-default); - font-size: 16px; - line-height: 20px; - padding-bottom: 16px -} - -.uncancelImage_fe75b7 { - background-image: url(/assets/d5c03099c5d632e3.svg); - height: 128px; - margin: 0 auto 24px; - width: 234px -} - -.error_fe75b7 { - margin-bottom: 16px -} - -.wrapper__99020 { - margin-bottom: 10px; - position: relative -} - -.specialHeader__99020 { - align-items: center; - background: linear-gradient(90deg,hsl(var(--premium-tier-2-purple-for-gradients-2-hsl)/.4) 0,hsl(var(--premium-tier-2-pink-for-gradients-hsl)/.3) 100%); - border-radius: var(--radius-sm) var(--radius-sm) 0 0; - display: flex; - height: 40px; - padding-inline-start:20px;position: relative -} - -.pill__99020 { - background-image: linear-gradient(95deg,var(--premium-tier-2-purple-for-gradients) 0,var(--premium-tier-2-purple-for-gradients-2) 49.96%,var(--premium-tier-2-pink-for-gradients) 95.93%); - border-radius: 10px; - color: var(--white); - inset-inline-start: 20px; - padding: 0 8px; - position: absolute; - top: -8px; - z-index: 30 -} - -.content__99020.headerWithoutSpecialHeader__99020 { - border-radius: var(--radius-sm) -} - -.content__99020.headerWithBoost__99020 { - border-radius: 0 0 var(--radius-sm) var(--radius-sm) -} - -.content__99020 { - position: relative; - z-index: 0 -} - -.content__99020:after,.content__99020:before { - border: 1px solid transparent; - border-radius: inherit; - box-sizing: border-box; - content: ""; - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.content__99020:after { - border-color: var(--guild-boosting-blue); - -webkit-mask: linear-gradient(45deg,#000,transparent); - mask: linear-gradient(45deg,#000,transparent) -} - -.content__99020:before { - border-color: var(--guild-boosting-purple) -} - -.border__99020 { - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100%; - z-index: 1 -} - -.header__99020 { - align-items: center; - background-color: var(--background-base-lower); - border-radius: var(--radius-sm); - display: flex; - padding: 30px 18px -} - -.headerHeading__99020 { - margin-bottom: 4px -} - -.headerContentPrimary__99020 { - align-items: center; - display: flex; - flex: 1 1 auto; - margin-inline-end:12px;width: 100% -} - -.headerBoostGems__99020 { - display: flex; - flex: 0 0 auto; - isolation: isolate; - margin-inline-end:16px} - -.headerBoostGem__99020 { - box-shadow: var(--elevation-medium) -} - -.headerBoostGem__99020+.headerBoostGem__99020 { - margin-inline-start:-30px} - -.headerLearnMoreLink__99020 { - color: var(--text-link); - cursor: pointer -} - -.headerLearnMoreLink__99020:hover { - text-decoration: underline -} - -.unappliedBoostSlots__99020 { - background-color: var(--background-mod-normal); - border-radius: 0 0 8px 8px; - padding: 0 18px -} - -.unappliedGuildBoostSlot__99020 { - align-items: center; - display: flex; - padding: 6px 0 -} - -.unappliedGuildBoostSlot__99020+.unappliedGuildBoostSlot__99020 { - border-top: 1px solid hsl(var(--primary-500-hsl)/.5) -} - -.theme-light .unappliedGuildBoostSlot__99020+.unappliedGuildBoostSlot__99020 { - border-top-color: hsl(var(--primary-500-hsl)/.2) -} - -.unappliedGuildBoostSlotContentPrimary__99020 { - align-items: center; - display: flex; - flex: 1 1 auto; - margin-inline-end:12px;width: 100% -} - -.unappliedGuildBoostSlotContentSecondary__99020 { - flex: 0 0 auto -} - -.unappliedGuildBoostSlotIcon__99020 { - flex: 0 0 auto; - margin-inline-end:10px} - -.unappliedGuildBoostSlotCta__99020 { - padding: 0 -} - -@media (max-width: 800px) { - .header__99020,.unappliedGuildBoostSlot__99020 { - flex-direction:column - } - - .headerContentPrimary__99020,.unappliedGuildBoostSlotContentPrimary__99020 { - margin-inline-end:0} - - .headerContentSecondary__99020,.unappliedGuildBoostSlotContentSecondary__99020 { - align-self: flex-end; - margin-top: 4px - } -} - -.tierComparisonTable__744ff { - margin-bottom: 64px -} - -.boosterRecognitionCard__744ff { - max-width: 140px; - min-width: 130px -} - -.faq__744ff { - padding: 36px 20px -} - -.blockedPaymentsWarning__744ff { - margin-bottom: 32px -} - -.FPContainer__744ff { - margin-bottom: 63px -} - -.FPPill__744ff { - background-color: var(--background-mod-muted) -} - -.guildBoostingSettings__744ff { - display: flex; - flex-direction: column -} - -.container__39fd0 { - box-sizing: border-box; - display: flex; - flex: 1; - flex-direction: column -} - -.loading__39fd0 { - align-items: center; - display: flex; - height: 520px; - justify-content: center -} - -.background__39fd0 { - background-repeat: no-repeat; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.content__39fd0 { - position: relative -} - -.divider__39fd0 { - background-color: var(--border-subtle); - height: 1px; - margin: 40px 0 -} - -.container_b1c93b { - align-self: center; - margin-top: var(--space-32) -} - -.emptySearchIcon_b1c93b { - height: 40px; - margin-bottom: 8px; - width: 100% -} - -.description__803f2 { - white-space: pre-wrap -} - -.description__803f2 p:first-child { - margin-top: 0 -} - -.description__803f2 p:last-child,.groupTitle__803f2 { - margin-bottom: 0 -} - -.sessions__803f2 { - margin: 32px 0 -} - -.session__803f2 { - display: flex; - flex-direction: column -} - -.session__803f2:first-child { - padding-top: 16px -} - -.currentSession__803f2 { - padding-bottom: 16px -} - -.sessionContent__803f2 { - align-items: center; - display: flex; - gap: 24px -} - -.sessionSeparator__803f2 { - margin-top: 24px -} - -.sessionIcon__803f2 { - align-items: center; - background: var(--interactive-text-default); - border-radius: 50%; - color: var(--background-base-lower); - display: flex; - height: 48px; - justify-content: center; - padding: 8px; - width: 48px -} - -.legacySession__803f2 .sessionIcon__803f2 { - background: var(--background-mod-muted) -} - -.sessionInfo__803f2 { - display: flex; - flex-direction: column; - gap: 8px -} - -.sessionInfoRow__803f2 { - display: flex; - flex-wrap: wrap; - gap: 4px -} - -.sessionMoreButton__803f2 { - color: var(--interactive-text-default); - cursor: pointer; - margin-inline-start:auto} - -.sessionMoreButton__803f2:hover { - color: var(--interactive-text-hover) -} - -.sessionCheckbox__803f2 { - margin-inline-start:auto} - -.sessionLogoutIcon__803f2 { - transform: rotate(180deg) -} - -.logOutAllButton__803f2 { - align-self: flex-start; - margin-top: 16px -} - -.loading__803f2 { - align-items: center; - display: flex; - height: 300px; - justify-content: center -} - -.tools__803f2 { - border-bottom: 1px solid var(--border-subtle); - border-top: 1px solid var(--border-subtle); - margin-top: 32px; - padding: 1rem 0 -} - -.toolsTitle__803f2 { - margin-bottom: .5rem -} - -.logOutAllButtonRedesign__803f2 { - margin-top: 24px -} - -.systemServiceContainer__79adc { - align-items: center; - display: flex; - flex-direction: row; - gap: 24px; - justify-content: space-between -} - -.systemServiceTextContainer__79adc { - display: flex; - flex-direction: column; - gap: var(--space-4) -} - -.group_bc4a3e { - margin-top: 20px -} - -.title_bc4a3e { - cursor: pointer; - display: flex; - justify-content: space-between; - margin-bottom: 8px -} - -.experimentDate_bc4a3e { - color: var(--channels-default) -} - -.emptyState_bc4a3e { - margin-top: 36px; - text-align: center -} - -.description_bc4a3e { - margin-top: 4px -} - -.divider_bc4a3e { - margin-top: 20px -} - -.debugTitle_bc4a3e { - margin-top: 16px -} - -.pre_bc4a3e { - white-space: pre -} - -.card_bc4a3e { - background-color: var(--background-surface-higher); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - padding: var(--space-16) -} - -.collapsible_bc4a3e { - background: none; - padding: var(--space-8) -} - -.collapsible_bc4a3e code { - background-color: var(--background-code); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - padding: var(--space-4) -} - -.heading__6f81c { - margin-bottom: 20px -} - -.tabBar__6f81c { - gap: 40px; - max-width: calc(100% - 60px) -} - -.tab__6f81c { - align-items: center; - display: flex; - flex-shrink: 1; - gap: 8px; - padding-bottom: 12px -} - -.tabIcon__6f81c { - flex-shrink: 0; - height: 24px; - width: 24px -} - -.iconTitle__6f81c { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.body__6f81c { - margin-top: 16px -} - -@media (max-width: 960px) { - .tabBar__6f81c { - gap:32px - } -} - -.poggermodeIcon__581ea { - height: 20px -} - -.premiumTab__581ea { - overflow: visible -} - -/*# sourceMappingURL=015ca14af3bac62c.css.map*/ diff --git a/discord-html-copy/css/076e97f00ed91b8f.css b/discord-html-copy/css/076e97f00ed91b8f.css deleted file mode 100644 index 97f43b3..0000000 --- a/discord-html-copy/css/076e97f00ed91b8f.css +++ /dev/null @@ -1,9110 +0,0 @@ -.left_caab99 { - transform: rotate(270deg) -} - -.right_caab99 { - transform: rotate(90deg) -} - -.down_caab99 { - transform: rotate(180deg) -} - -.downRight_caab99 { - transform: rotate(135deg) -} - -.upLeft_caab99 { - transform: rotate(315deg) -} - -.normalStylesDefault_a1443c { - --custom-poll-style-vote-background: var(--background-base-lowest); - --custom-poll-style-vote-percentage: var(--interactive-background-active); - --custom-poll-style-label: var(--text-default); - --custom-poll-style-border: transparent -} - -.normalStylesImageOnlyAnswers_a1443c { - --custom-poll-style-image-background: var(--polls-normal-image-background) -} - -.victorStyles_a1443c { - --custom-poll-style-vote-percentage: var(--polls-victor-fill); - --custom-poll-style-image-background: var(--background-feedback-warning); - --custom-poll-style-border: var(--status-positive) -} - -.votedStyles_a1443c { - --custom-poll-style-vote-percentage: var(--polls-voted-fill); - --custom-poll-style-image-background: var(--background-feedback-positive); - --custom-poll-style-border: var(--control-brand-foreground) -} - -.container__09ccc { - background-color: var(--background-surface-high); - border-radius: 4px; - box-shadow: var(--shadow-border),var(--shadow-high); - margin-top: 4px; - max-width: 250px; - overflow: hidden; - padding: 8px 8px 4px -} - -.search__09ccc { - margin: 0 8px 8px -} - -.roles__09ccc { - max-height: 300px; - overflow-y: scroll; - padding: 0 8px 8px -} - -.role__09ccc { - align-items: center; - border-radius: 4px; - cursor: pointer; - display: flex; - flex-grow: 0; - justify-content: space-between; - padding: 8px -} - -.role__09ccc:hover { - background-color: var(--background-base-lower) -} - -.role__09ccc.disabled__09ccc { - opacity: .5 -} - -.role__09ccc.disabled__09ccc:hover { - background-color: var(--background-surface-high) -} - -.label__09ccc { - color: var(--text-default); - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 18px; - margin-inline-end:8px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.iconContainer__09ccc { - border: 2px solid var(--interactive-text-default); - border-radius: 4px; - display: flex; - flex-shrink: 0; - height: 16px; - width: 16px -} - -.iconContainer__09ccc.checked__09ccc { - background-color: var(--brand-500); - border-color: var(--brand-500) -} - -.settingsWrapper_c9e42f { - background-color: var(--brand-600); - inset-inline-end: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 2000 -} - -.settingsWrapper_c9e42f.osx_c9e42f .backButton_c9e42f { - margin-inline-start:76px} - -.settingsWrapper_c9e42f.windows_c9e42f { - top: 22px -} - -.settingsWrapper_c9e42f.windows_c9e42f .backButton_c9e42f { - margin-inline-start:0} - -.backButton_c9e42f { - position: absolute -} - -.notice_c9e42f { - background-color: var(--brand-600); - color: var(--white); - height: 40px; - justify-content: space-between; - padding: 0 8px -} - -.notice_c9e42f,.noticeContents_c9e42f { - align-items: center; - display: flex -} - -.noticeContents_c9e42f { - flex: 1 1 auto; - justify-content: center -} - -.noticeText_c9e42f { - font-weight: var(--font-weight-semibold) -} - -.previewWarning_c9e42f { - line-height: 0; - margin-inline-start:8px} - -.button_c9e42f { - align-items: center; - font-size: 12px; - font-weight: var(--font-weight-semibold); - height: -moz-fit-content; - height: fit-content; - line-height: 16px; - margin-inline-start:16px;padding: 4px 8px -} - -.button_c9e42f:hover { - background-color: var(--brand-530) -} - -.buttonInner_c9e42f { - display: flex -} - -.backArrow_c9e42f { - margin-inline-end:8px} - -.selectCaret_c9e42f { - margin-inline-start:8px} - -.closeButton_c9e42f { - background: url(/assets/26a3dd69161dbf47.svg) no-repeat; - background-position: 50% 55%; - background-size: 10px 10px; - cursor: pointer; - height: 36px; - inset-inline-end: 0; - opacity: .5; - position: absolute; - top: 0; - transition: opacity .2s; - width: 36px; - -webkit-app-region: no-drag -} - -.closeButton_c9e42f:hover { - opacity: 1 -} - -.rolesList_c9e42f { - align-items: flex-start; - background-color: var(--background-surface-high); - border-radius: 4px; - display: flex; - flex-direction: column; - gap: 8px; - padding: 8px -} - -.badge__436c9 { - align-items: center; - border-radius: 4px; - color: var(--white); - display: flex; - gap: 6px; - justify-content: center; - padding: 4px 8px -} - -.badgeIcon__436c9 { - height: 16px; - width: 16px -} - -.productCard__8d9f6 { - position: relative -} - -.productCardClickable__8d9f6 { - background: var(--background-base-lower); - border-radius: 8px; - cursor: pointer; - overflow: hidden -} - -.productThumbnail__8d9f6 { - grid-area: 1/1/2/3; - height: 190px; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.productInfo__8d9f6 { - align-self: start; - display: flex; - flex-direction: row; - padding: 16px -} - -.productInfoContent__8d9f6 { - align-items: flex-start; - display: flex; - flex: 1; - flex-direction: column; - overflow: hidden -} - -.productName__8d9f6 { - max-width: 100%; - overflow: hidden; - padding-inline-end:16px;text-overflow: ellipsis; - white-space: nowrap -} - -.productDetails__8d9f6 { - align-items: center; - display: flex; - flex-wrap: wrap; - gap: 8px -} - -.dotSeparator__8d9f6 { - background-color: var(--text-muted); - border-radius: 50%; - flex-shrink: 0; - height: 4px; - width: 4px -} - -.productActionMenuButton__8d9f6 { - cursor: pointer; - height: 24px; - margin-inline-end:-8px;width: 24px; - z-index: 100 -} - -.productActionMenuButton__8d9f6 .productActionMenuIcon__8d9f6 { - color: var(--interactive-text-default) -} - -.productActionMenuButton__8d9f6:hover .productActionMenuIcon__8d9f6 { - color: var(--interactive-text-hover) -} - -.menuContainer__8d9f6 { - margin-top: 6px -} - -.disabled__8d9f6 { - cursor: not-allowed; - opacity: .5 -} - -.container__06f11 { - border-radius: 12px; - box-sizing: border-box; - -webkit-mask-image: radial-gradient(circle,#fff 100%,#000 0); - mask-image: radial-gradient(circle,#fff 100%,#000 0); - overflow: hidden; - padding: var(--custom-aspect-stable-image-container-padding); - width: 100% -} - -.container__06f11,.imageContainer__06f11 { - align-items: center; - display: flex; - justify-content: center; - position: relative -} - -.imageContainer__06f11 { - height: 100% -} - -.image__06f11 { - border-radius: 12px; - box-shadow: var(--shadow-high); - height: auto; - max-height: 100%; - max-width: 100%; - -o-object-fit: contain; - object-fit: contain; - position: relative; - width: auto -} - -.backgroundImage__06f11,.backgroundImageFilter__06f11 { - display: block; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.backgroundImage__06f11 { - -o-object-fit: cover; - object-fit: cover; - transform: scale(1.5) -} - -.backgroundImageFilter__06f11 { - -webkit-backdrop-filter: blur(4px); - backdrop-filter: blur(4px); - background: rgba(28,27,37,.9) -} - -.loader__06f11 { - position: absolute -} - -.root__48c1c { - flex-wrap: wrap; - margin-top: 2px; - position: relative -} - -.role__48c1c,.root__48c1c { - display: flex -} - -.role__48c1c { - align-items: center; - background: var(--background-mod-normal); - border-radius: 4px; - box-sizing: border-box; - font-size: 12px; - font-weight: var(--font-weight-medium); - height: 22px; - margin-block:0 4px;margin-inline:0 4px;padding: 4px -} - -.roleRemoveButton__48c1c { - position: relative -} - -.roleRemoveButtonCanRemove__48c1c { - cursor: pointer -} - -.roleDot__48c1c { - font-size: 1.34em -} - -.roleFlowerStar__48c1c { - margin: 0 4px -} - -.roleRemoveIcon__48c1c { - display: none; - height: 10px; - inset-inline-start: 50%; - margin-block:-5px 0;margin-inline:-5px 0;position: absolute; - top: 50%; - width: 10px -} - -.role__48c1c:focus .roleRemoveIcon__48c1c,.role__48c1c:hover .roleRemoveIcon__48c1c,.roleRemoveIconFocused__48c1c .roleRemoveIcon__48c1c { - display: block -} - -.role__48c1c:focus .roleVerifiedIcon__48c1c,.role__48c1c:hover .roleVerifiedIcon__48c1c,.roleRemoveIconFocused__48c1c .roleVerifiedIcon__48c1c { - display: none -} - -.roleName__48c1c { - margin-inline-end:4px;max-width: 200px; - overflow: hidden -} - -.roleNameOverflow__48c1c { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.actionButton__48c1c { - color: var(--interactive-text-active); - font-size: 11px; - justify-content: center; - line-height: 11px -} - -.overflowButton__48c1c { - padding: 4px 8px -} - -.addButton__48c1c { - padding: 4px 5px -} - -.addButtonIcon__48c1c { - cursor: pointer; - height: 14px; - width: 14px -} - -.overflowRolesPopout__48c1c { - background-color: var(--background-mod-normal); - border-radius: 3px; - box-shadow: var(--legacy-elevation-high),var(--legacy-elevation-border); - padding: 8px; - width: 200px -} - -.overflowRolesPopoutArrowWrapper__48c1c { - height: 12px; - margin-inline-start:-8px;overflow: hidden; - width: 16px -} - -.overflowRolesPopoutArrow__48c1c { - background-color: var(--background-mod-normal); - box-shadow: var(--legacy-elevation-high),var(--legacy-elevation-border); - height: 8px; - transform: rotate(45deg); - width: 8px -} - -.popoutBottom__48c1c .overflowRolesPopoutArrowWrapper__48c1c { - bottom: 100%; - inset-inline-start: 50%; - position: absolute -} - -.popoutBottom__48c1c .overflowRolesPopoutArrow__48c1c { - bottom: -4px; - inset-inline-start: 4px; - position: absolute -} - -.popoutTop__48c1c .overflowRolesPopoutArrowWrapper__48c1c { - inset-inline-start: 50%; - position: absolute; - top: 100% -} - -.popoutTop__48c1c .overflowRolesPopoutArrow__48c1c { - inset-inline-start: 4px; - position: absolute; - top: -4px -} - -.overflowRolesPopoutHeader__48c1c { - margin-bottom: 8px -} - -.overflowRolesPopoutHeaderIcon__48c1c { - height: 12px; - margin-inline-end:4px;width: 12px -} - -.overflowRolesPopoutHeaderText__48c1c { - color: var(--primary-400); - font-size: 12px; - font-weight: var(--font-weight-bold); - text-transform: uppercase -} - -.theme-dark .overflowRolesPopout__48c1c { - color: var(--opacity-white-80) -} - -.theme-light .overflowRolesPopout__48c1c { - color: hsl(var(--primary-500-hsl)/.8) -} - -.roleIcon__48c1c { - margin-inline-end:4px;vertical-align: middle -} - -.enable-forced-colors .role__48c1c { - border: 1px solid CanvasText -} - -.enable-forced-colors .actionButton__48c1c { - color: ButtonText -} - -.enable-forced-colors .roleRemoveIcon__48c1c { - background-color: ButtonFace; - border-radius: 100%; - color: ButtonText; - padding: 2px -} - -.enable-forced-colors .roleRemoveIcon__48c1c path { - fill: ButtonText!important -} - -.tile__72090 { - background-color: var(--background-surface-high); - border-radius: 5px; - box-shadow: 0 0 0 hsl(var(--primary-700-hsl)/.15); - display: flex; - flex-direction: column; - min-height: 370px; - overflow: hidden; - transform: translateZ(0); - transition: background-color .2s ease,box-shadow .2s ease -} - -.tile__72090:hover { - background-color: var(--background-surface-higher) -} - -.tileHorizontal__72090 { - align-items: stretch; - flex-direction: row-reverse; - min-height: 0; - width: 660px -} - -.title__72090 { - flex: 0 1 auto; - font-size: 18px; - font-weight: var(--font-weight-semibold); - line-height: 1.2 -} - -.tagline__72090 { - color: var(--text-default); - flex: 1 1 auto; - font-size: 15px; - line-height: 1.3; - margin-top: 8px; - overflow: hidden -} - -.actions__72090 { - flex: 0 1 auto; - justify-self: flex-end; - margin-top: 12px -} - -.media__72090 { - position: relative -} - -.mediaHorizontal__72090 { - align-items: center; - display: flex; - flex: 0 0 305px -} - -.description__72090 { - color: var(--text-strong); - display: flex; - flex: 1 1 auto; - flex-direction: column; - padding: 12px; - position: relative -} - -.embedHorizontal_a8b53f { - height: 173px -} - -.embedVertical_a8b53f { - height: 300px; - min-height: auto; - width: 240px -} - -@keyframes loading_a8b53f { - 0% { - transform: translate3d(calc(var(--custom-responsive-embed-tile-loading-background-width)*-1),0,0) - } - - 50% { - transform: translateZ(0) - } - - to { - transform: translate3d(calc(var(--custom-responsive-embed-tile-loading-background-width)*-1),0,0) - } -} - -.loadingDescription_a8b53f { - align-items: center; - display: flex; - flex: 1 -} - -.loadingBackgroundWrapper_a8b53f { - height: 165px; - margin-top: 16px; - -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='271' height='165'><path fill='#000000' fill-rule='evenodd' d='M5 0h120c2.761424 0 5 2.23857625 5 5v10c0 2.7614237-2.238576 5-5 5H5c-2.76142375 0-5-2.2385763-5-5V5c0-2.76142375 2.23857625-5 5-5zm0 30h244c2.761424 0 5 2.2385763 5 5v10c0 2.7614237-2.238576 5-5 5H5c-2.76142375 0-5-2.2385763-5-5V35c0-2.7614237 2.23857625-5 5-5zm0 30h180c2.761424 0 5 2.2385763 5 5v10c0 2.7614237-2.238576 5-5 5H5c-2.76142375 0-5-2.2385763-5-5V65c0-2.7614237 2.23857625-5 5-5zm-2 60h60c1.6568542 0 3 1.343146 3 3v26c0 1.656854-1.3431458 3-3 3H3c-1.65685425 0-3-1.343146-3-3v-26c0-1.656854 1.34314575-3 3-3z'/></svg>"); - mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='271' height='165'><path fill='#000000' fill-rule='evenodd' d='M5 0h120c2.761424 0 5 2.23857625 5 5v10c0 2.7614237-2.238576 5-5 5H5c-2.76142375 0-5-2.2385763-5-5V5c0-2.76142375 2.23857625-5 5-5zm0 30h244c2.761424 0 5 2.2385763 5 5v10c0 2.7614237-2.238576 5-5 5H5c-2.76142375 0-5-2.2385763-5-5V35c0-2.7614237 2.23857625-5 5-5zm0 30h180c2.761424 0 5 2.2385763 5 5v10c0 2.7614237-2.238576 5-5 5H5c-2.76142375 0-5-2.2385763-5-5V65c0-2.7614237 2.23857625-5 5-5zm-2 60h60c1.6568542 0 3 1.343146 3 3v26c0 1.656854-1.3431458 3-3 3H3c-1.65685425 0-3-1.343146-3-3v-26c0-1.656854 1.34314575-3 3-3z'/></svg>"); - -webkit-mask-size: 100%; - mask-size: 100%; - position: relative -} - -.loadingBackgroundWrapper_a8b53f,.loadingBackgroundWrapperHorizontal_a8b53f { - width: var(--custom-responsive-embed-tile-loading-background-width) -} - -.loadingBackgroundWrapperHorizontal_a8b53f { - height: 141px; - margin-top: 8px; - -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='271' height='141'><g fill='#000000' fill-rule='evenodd'><rect width='130' height='20' rx='5'/><rect width='254' height='20' y='30' rx='5'/><rect width='190' height='20' y='60' rx='5'/><rect width='66' height='32' y='102' rx='3'/></g></svg>"); - mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='271' height='141'><g fill='#000000' fill-rule='evenodd'><rect width='130' height='20' rx='5'/><rect width='254' height='20' y='30' rx='5'/><rect width='190' height='20' y='60' rx='5'/><rect width='66' height='32' y='102' rx='3'/></g></svg>") -} - -.loadingImage_a8b53f { - background-color: var(--background-base-lowest); - padding-top: 56%; - width: 100% -} - -.loadingImageHorizontal_a8b53f { - height: 100%; - padding: 0 -} - -.loadingBackground_a8b53f { - animation: loading_a8b53f 4s ease-in-out infinite; - bottom: 0; - inset-inline-start: 0; - position: absolute; - top: 0; - width: calc(var(--custom-responsive-embed-tile-loading-background-width)*2) -} - -.images-light .loadingBackground_a8b53f { - background-image: linear-gradient(to right,var(--primary-100),var(--primary-200),var(--primary-100)) -} - -.images-dark .loadingBackground_a8b53f { - background-image: linear-gradient(to right,var(--primary-600),var(--primary-500),var(--primary-600)) -} - -.embedHorizontal_a8b53f,.embedVertical_a8b53f { - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - padding: var(--space-4) -} - -.playButton__0cb56 { - padding-inline:0} - -.playButtonContents__0cb56 { - align-items: center; - display: flex; - flex-grow: 1 -} - -.disabledButtonWrapper__0cb56 { - position: relative -} - -.disabledButtonOverlay__0cb56 { - inset: 0; - position: absolute -} - -.disabledButtonColor__0cb56 { - background-color: hsl(var(--primary-800-hsl)/.5); - color: hsl(var(--primary-100-hsl)/.5); - opacity: 1 -} - -.buttonText__0cb56 { - flex-grow: 1; - padding: 2px 16px -} - -.dropdownArrowHitbox__0cb56 { - align-items: center; - display: flex; - height: 100%; - width: 32px; - z-index: 2 -} - -.arrowSeparator__0cb56 { - background-color: currentColor; - height: 24px; - opacity: .2; - width: 1px -} - -.dropdownArrow__0cb56 { - flex: 1; - height: 24px; - opacity: .2; - transition: opacity .2s ease; - width: 24px -} - -.dropdownArrowHitbox__0cb56:hover .dropdownArrow__0cb56 { - opacity: 1 -} - -.buttonWithProgress__61462 { - align-items: flex-start; - display: flex; - flex-direction: column; - gap: 4px -} - -.progress__61462 { - width: 100% -} - -.disabledButtonWrapper__61462 { - position: relative -} - -.disabledButtonOverlay__61462 { - inset: 0; - position: absolute -} - -.disabledButtonColor__61462 { - background-color: hsl(var(--primary-800-hsl)/.5); - color: var(--text-strong); - opacity: 1 -} - -.row_c7dbcb { - align-items: center; - display: flex -} - -.icon_c7dbcb { - height: 20px; - margin-inline-end:4px;width: 20px -} - -.salePercentage_c7dbcb { - background-color: var(--green-360); - border-radius: 3px; - color: var(--white); - padding: 4px 6px -} - -.originalAmount_c7dbcb { - color: var(--primary-300); - text-decoration: line-through -} - -.directoryTilePrice_c7dbcb { - font-size: 16px; - font-weight: var(--font-weight-medium) -} - -.directoryTilePrice_c7dbcb .originalAmount_c7dbcb,.directoryTilePrice_c7dbcb .salePercentage_c7dbcb { - margin-inline-end:6px} - -.directoryHeroPrice_c7dbcb { - font-size: 16px -} - -.directoryHeroPrice_c7dbcb .originalAmount_c7dbcb,.directoryHeroPrice_c7dbcb .salePercentage_c7dbcb { - margin-inline-end:6px} - -.directoryHeroPricePremium_c7dbcb { - flex-wrap: wrap; - font-size: 14px; - font-weight: var(--font-weight-medium) -} - -.directoryHeroPricePremium_c7dbcb .icon_c7dbcb { - flex-shrink: 0; - height: 24px; - width: 24px -} - -.directorySearchPrice_c7dbcb { - color: var(--text-muted); - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 16px -} - -.directorySearchPrice_c7dbcb .icon_c7dbcb { - height: 16px; - width: 16px -} - -.directorySearchPrice_c7dbcb .originalAmount_c7dbcb,.directorySearchPrice_c7dbcb .salePercentage_c7dbcb { - margin-inline-end:6px} - -.listingPrice_c7dbcb { - color: var(--primary-100); - font-size: 32px; - font-weight: var(--font-weight-medium); - line-height: 36px -} - -.listingPrice_c7dbcb .salePercentage_c7dbcb { - color: var(--white); - font-size: 16px; - margin-inline-start:10px} - -.listingPrice_c7dbcb .originalAmount_c7dbcb { - font-size: 15px -} - -.embedPrice_c7dbcb { - color: var(--text-default); - font-size: 16px; - font-weight: var(--font-weight-medium) -} - -.embedPrice_c7dbcb .originalAmount_c7dbcb,.embedPrice_c7dbcb .salePercentage_c7dbcb { - margin-inline-end:6px} - -.embedPrice_c7dbcb .salePercentage_c7dbcb { - background-color: var(--background-surface-high) -} - -.theme-light .directoryHeroPrice_c7dbcb .originalAmount_c7dbcb,.theme-light .directoryHeroPricePremium_c7dbcb .originalAmount_c7dbcb { - color: var(--primary-400) -} - -.directoryHeroPrice_c7dbcb,.directoryHeroPricePremium_c7dbcb { - color: var(--text-default) -} - -.OSSection_f28827 { - display: flex; - justify-content: flex-end -} - -.purchaseUnitOperatingSystem_f28827 { - color: var(--primary-500); - margin-inline-start:4px} - -.tag_d0ccaf { - padding: 6px 12px -} - -.tag_d0ccaf:before { - background-image: url(/assets/609367095f652a43.png); - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - content: ""; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.text_d0ccaf { - color: var(--white); - display: block; - font-size: 13px; - font-weight: var(--font-weight-semibold); - position: relative; - text-transform: uppercase -} - -.tag__6c02d { - padding: 6px 12px -} - -.tag__6c02d:before { - background-position: top; - background-repeat: no-repeat; - background-size: cover; - content: ""; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.text__6c02d { - color: var(--white); - display: block; - font-size: 13px; - font-weight: var(--font-weight-semibold); - position: relative; - text-transform: uppercase -} - -.tag__6c02d { - background-image: linear-gradient(to right,var(--premium-tier-1-blue),var(--premium-tier-1-purple)) -} - -.tag__6c02d:before { - background-image: url(/assets/df3df6cdf0dd1991.svg) -} - -.centeringContainer__0d97c { - align-items: center; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - display: flex; - justify-content: center; - min-height: 126px -} - -.titleContainer__0d97c { - background-image: url(/assets/df31f184a2abb737.png); - box-sizing: border-box; - color: var(--white); - font-size: 20px; - padding: 8px 10px; - text-align: center -} - -.loadingContainer__0d97c,.titleContainer__0d97c { -} - -.slideshowWrapper__0d97c { - height: 100% -} - -.spinner__0d97c { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.imageLoading__0d97c { - visibility: hidden -} - -.splash__0d97c { - -o-object-position: top; - object-position: top -} - -.splash__0d97c,.splashPlaceholder__0d97c { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - -o-object-fit: cover; - object-fit: cover; - vertical-align: middle; - width: 100% -} - -.splashPlaceholder__0d97c { - height: 100% -} - -.muteContainer__0d97c { - align-items: center; - background: var(--opacity-black-28); - border-radius: 50%; - bottom: 0; - display: flex; - height: 24px; - inset-inline-end: 0; - justify-content: center; - margin-block:0 10px;margin-inline:0 10px;opacity: 0; - padding: 4px; - pointer-events: all; - position: absolute; - transition: opacity .4s ease; - width: 24px; - z-index: 2 -} - -.muteContainerPlaying__0d97c { - opacity: 1 -} - -.muteContainerMediaOverlay__0d97c { - inset-inline-end: 0; - margin-block:0;margin-inline:0 10px;position: absolute; - top: 86px -} - -.mute__0d97c { - color: var(--white); - height: 20px; - width: 20px -} - -.theme-dark .loadingContainer__0d97c { - background-color: var(--opacity-black-40) -} - -.theme-light .loadingContainer__0d97c { - background-color: var(--opacity-white-40) -} - -.theme-light .spinnerItem__0d97c { - background-color: var(--primary-800) -} - -.splashContainer__0bef9 { - margin: 0; - max-height: 60%; - overflow: hidden; - padding-top: 56%; - pointer-events: none; - position: relative -} - -.splashContainerHorizontal__0bef9 { - flex: 1; - height: 100%; - max-height: none; - padding-top: 0 -} - -.priceOs__0bef9 { - align-items: center; - display: flex; - justify-content: space-between -} - -.platform__0bef9 { - opacity: 1; - transition: opacity .4s ease -} - -.exclusiveRegular__0bef9 { - inset-inline: 0; - bottom: 0; - position: absolute -} - -.exclusivePlaying__0bef9 { - opacity: 0; - transition: opacity .4s ease -} - -.splashPlaceholder__0bef9,.splashVideo__0bef9 { - height: 100% -} - -.tileBuyButton__0bef9 { - bottom: 12px; - inset-inline-start: 12px; - position: absolute; - transform: translate3d(0,12px,0) -} - -.full-motion .tileBuyButton__0bef9 { - transition: opacity .2s,transform .2s ease -} - -.tileBuyButtonVisible__0bef9 { - transform: translateZ(0) -} - -.tileBuyButton__0bef9,.tileBuyButton__0bef9:disabled { - opacity: 0 -} - -.tileBuyButtonVisible__0bef9,.tileBuyButtonVisible__0bef9:disabled { - opacity: 1 -} - -.tilePrice__0bef9 { - transition: opacity .2s -} - -.tilePriceWithVisibleBuyButton__0bef9 { - opacity: 0 -} - -.socialLayerStorefrontCardContainer__0bef9 { - border-radius: var(--radius-xs); - height: 100% -} - -.socialLayerStorefrontCardBackgroundImage__0bef9 { - background-size: cover; - height: 100% -} - -.socialLayerStorefrontCardImage__0bef9 { - height: 100%; - -o-object-position: center bottom; - object-position: center bottom -} - -.tileActions_bd7f32 { - align-items: center; - display: flex; - font-weight: var(--font-weight-medium) -} - -.actionButton_bd7f32 { - margin-inline-end:8px} - -.metadata__857bf { - font-size: 12px -} - -.libraryLink__857bf { - align-items: center; - display: flex; - margin-inline-start:10px} - -.libraryIcon__857bf { - margin-inline-end:6px} - -.invalidPoop__857bf { - background-position: 50% 50%; - background-repeat: no-repeat; - margin: 0; - max-height: 60%; - overflow: hidden; - padding-top: 56%; - position: relative -} - -.invalidPoopHorizontal__857bf { - flex: 1; - height: 100%; - max-height: none; - padding-top: 0 -} - -.theme-light .libraryLink__857bf,.theme-light .metadata__857bf { - color: var(--primary-400) -} - -.theme-light .invalidPoop__857bf { - background-color: hsl(var(--primary-400-hsl)/.3) -} - -.theme-dark .libraryLink__857bf { - color: var(--opacity-white-60) -} - -.theme-dark .invalidPoop__857bf { - background-color: hsl(var(--primary-500-hsl)/.3) -} - -.theme-dark .metadata__857bf { - color: var(--primary-400) -} - -.legacySeasonalGiftEmbedWrapper__857bf { - background: linear-gradient(102.85deg,#26569e 2.16%,#3584d5 46.11%,#5dbace 99.42%); - background-position: 50% 50% -} - -.customGiftEmbedWrapper__857bf { - height: 128px; - margin: 0; - overflow: hidden; - position: relative -} - -.giftEmbedWrapperHorizontal__857bf { - flex: 1; - height: 100% -} - -.snow__857bf { - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100%; - z-index: 0 -} - -.legacySeasonalGiftEmbedBox__857bf { - bottom: -18px; - height: 110%; - inset-inline-end: -8px; - position: absolute; - z-index: 1 -} - -.legacySeasonalGiftEmbedBoxHorizontal__857bf { - bottom: -24px -} - -.customGiftEmbedBox__857bf { - bottom: 18px; - inset-inline-start: 33px; - position: absolute; - width: 174px -} - -.customGiftEmbedBoxHorizontal__857bf { - bottom: 24px; - inset-inline-start: 36px; - width: 234px -} - -.headerIcon__857bf { - color: var(--white); - margin-bottom: auto; - margin-inline-start:16px;margin-top: 16px; - width: 92px -} - -.collectiblesEmbedWrapper__857bf { - pointer-events: none -} - -.collectiblesAcceptButton__857bf { - pointer-events: all -} - -.images-light .invalidPoop__857bf { - background-image: url(/assets/eb4644cbe2f3e90f.svg) -} - -.images-dark .invalidPoop__857bf { - background-image: url(/assets/e4a502bfc6589d65.svg) -} - -.embed__98ba8 { - align-self: start; - border-radius: var(--radius-md); - color: var(--white); - container-type: inline-size; - justify-self: start; - max-width: 343px; - overflow: hidden; - width: 100% -} - -.showVideoOnFocus__98ba8 { - --custom-ease: cubic-bezier(0.4,0,1,1) -} - -.showVideoOnFocus__98ba8 .staticBanner__98ba8 { - transition: opacity .3s var(--custom-ease) -} - -.showVideoOnFocus__98ba8 .videoBanner__98ba8 { - transition: visibility .3s var(--custom-ease); - visibility: hidden -} - -.showVideoOnFocus__98ba8:focus .staticBanner__98ba8,.showVideoOnFocus__98ba8:focus-within .staticBanner__98ba8,.showVideoOnFocus__98ba8:hover .staticBanner__98ba8 { - opacity: 0 -} - -.showVideoOnFocus__98ba8:focus .videoBanner__98ba8,.showVideoOnFocus__98ba8:focus-within .videoBanner__98ba8,.showVideoOnFocus__98ba8:hover .videoBanner__98ba8 { - visibility: visible -} - -.bannerWrapper__98ba8 { - height: 100%; - position: relative -} - -.bannerAspectRatioBot__98ba8 { - aspect-ratio: 17/6 -} - -.bannerAspectRatioActivity__98ba8 { - aspect-ratio: 16/9 -} - -.videoBanner__98ba8 { - -o-object-fit: cover!important; - object-fit: cover!important -} - -.staticBanner__98ba8,.videoBanner__98ba8 { - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - height: 100% -} - -.staticBanner__98ba8 { - position: absolute; - top: 0; - width: 100% -} - -.header__98ba8 { - color: var(--app-message-embed-secondary-text) -} - -.contentContainer__98ba8 { - display: flex; - flex-direction: column; - gap: 12px; - padding: 12px -} - -.contentWrapper__98ba8 { - --custom-content-size: 64px; - display: flex; - flex: 1; - gap: 12px; - min-width: 0 -} - -.cursorPointer__98ba8 { - cursor: pointer -} - -.contentWrapperClickable__98ba8:hover .contentTitle__98ba8 { - text-decoration: underline -} - -.img__98ba8 { - background-color: rgba(0,0,0,.64); - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - border-radius: var(--radius-md); - box-shadow: inset 0 0 0 1px rgba(0,0,0,.2); - box-sizing: border-box; - height: var(--custom-content-size); - width: var(--custom-content-size); - @container (max-width: 270px) { - display: none - } -} - -.content__98ba8 { - display: flex; - flex: 1; - flex-direction: column; - justify-content: center; - min-width: 0; - overflow: hidden -} - -.contentInfoWrapper__98ba8 { - color: var(--app-message-embed-secondary-text) -} - -.actionWrapper__98ba8 { - display: flex; - flex-direction: row-reverse; - gap: 8px; - @container (max-width: 300px) { - flex-direction: column - } -} - -.actionWrapper__98ba8>.buttonWithPossibleDisabledTextWrapper__98ba8 { - flex: 50% -} - -.actionWrapper__98ba8.actionWrapperPrimaryFirst__98ba8 { - flex-direction: row; - @container (max-width: 300px) { - flex-direction: column - } -} - -.buttonWithPossibleDisabledTextWrapper__98ba8 { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-4) -} - -.disabledReason__98ba8 { - color: var(--app-message-embed-secondary-text); - text-align: center -} - -.wrapper__748d7 { - display: flex; - gap: 4px -} - -:root { - --custom-app-message-embed-base-info-gap: 4px; - --custom-app-message-embed-base-info-top: calc(var(--custom-app-message-embed-base-info-gap) - 2px) -} - -.description__1f7ec { - margin-top: var(--custom-app-message-embed-base-info-top) -} - -.description__1f7ec strong { - font-weight: 500 -} - -.tagline__1f7ec { - align-items: center; - display: flex; - gap: 4px; - padding-top: var(--custom-app-message-embed-base-info-gap) -} - -.tag__1f7ec:not(:first-of-type):before { - content: "∙"; - padding: 0 3px -} - -.wrapper__727be { - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-sizing: border-box; - container-type: inline-size; - max-width: min(601px,100%); - overflow: hidden; - padding: 16px; - width: 100% -} - -.header__727be { - justify-content: space-between; - padding-bottom: 8px -} - -.header__727be,.headerTitle__727be { - align-items: center; - display: flex -} - -.headerTitle__727be { - gap: 4px -} - -.content__727be { - display: flex; - flex-wrap: wrap; - gap: 16px; - justify-content: space-between; - @container (max-width: 400px) { - flex-direction: column - } -} - -.contentTextWrapper__727be { - display: flex; - flex-basis: min-content; - flex-grow: 10; - flex-shrink: 10; - gap: 16px -} - -.contentText__727be { - display: flex; - flex-direction: column -} - -.appIcon__727be { - --custom-size: 44px; - border-radius: var(--custom-size); - height: var(--custom-size); - -o-object-fit: cover; - object-fit: cover; - width: var(--custom-size) -} - -.description__727be { - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - display: -webkit-box; - overflow: hidden -} - -.openStoreButton__727be { - width: 124px; - @container (max-width: 400px) { - width: 100% - } -} - -.skuPurchaseButtons__727be { - align-items: center; - display: flex; - flex-basis: min-content; - flex-grow: 1; - gap: 8px; - max-width: 100%; - overflow: hidden; - @container (max-width: 400px) { - flex-direction: column-reverse; - button { - width: 100% - } - } -} - -.skuPurchaseButtons__727be>button { - flex-grow: 1 -} - -.container_cacd02 { - flex-wrap: wrap; - justify-content: flex-start -} - -.bumpBox_cacd02,.container_cacd02 { - align-items: center; - display: flex -} - -.bumpBox_cacd02 { - background-color: var(--background-base-lower); - border-radius: 8px; - flex: 0 0 max-content; - margin-inline-end:16px;padding: 8px -} - -.icon_cacd02 { - color: var(--text-muted); - flex: 0 0 16px -} - -.icon_cacd02,.publish_cacd02,.tagline_cacd02 { - margin-inline-end:8px} - -.publish_cacd02 { - cursor: pointer -} - -.hidePermanently_cacd02 { - cursor: pointer; - line-height: 24px; - transition: .2s linear -} - -.hidePermanently_cacd02:focus,.hidePermanently_cacd02:hover { - color: var(--text-default) -} - -.closeIcon_cacd02 { - color: var(--icon-muted); - cursor: pointer; - display: block -} - -.closeIcon_cacd02:focus,.closeIcon_cacd02:hover { - color: var(--interactive-text-hover) -} - -.closeIcon_cacd02:active { - color: var(--interactive-text-active) -} - -@use postcss-pxtorem;.theme-dark.custom-theme-background .embedFull__623de { - --__spoiler-background-color--hidden: var(--primary-760); - --__spoiler-background-color--hidden--hover: hsl(var(--primary-760-hsl)/0.8) -} - -.embed__623de { - border-radius: 4px; - box-sizing: border-box; - display: grid; - max-width: -moz-max-content; - max-width: max-content; - position: relative -} - -.embed__623de .emoji { - height: 18px; - width: 18px -} - -.embed__623de :is(h1,h2,h3,h4,h5,h6) .emoji { - height: 1.375em; - width: 1.375em -} - -.embed__623de pre { - border: none; - max-width: 100% -} - -.embed__623de code { - background: var(--background-base-lowest); - border: none -} - -.embed__623de .no-webkit-scrollbar code { - scrollbar-color: var(--background-base-lower) var(--background-base-lowest) -} - -.embed__623de blockquote { - max-width: 100% -} - -.embedFull__623de { - background: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-inline-start:4px solid var(--border-normal)} - -.embedFull__623de.isHidden__623de { - border-inline-start: 0 -} - -.inlineMediaEmbed__623de { - max-width: 520px -} - -.embedAuthorName__623de,.embedAuthorNameLink__623de,.embedDescription__623de,.embedFieldName__623de,.embedFieldValue__623de,.embedFooterText__623de,.embedLink__623de,.embedProvider__623de,.embedTitle__623de,.embedTitleLink__623de { - text-align: start; - unicode-bidi: plaintext -} - -.gridContainer__623de { - max-width: 516px -} - -.grid__623de { - display: grid; - grid-template-columns: auto; - grid-template-rows: auto; - overflow: hidden; - padding-block:.5rem 1rem;padding-inline:.75rem 1rem;padding-top: .125rem -} - -.grid__623de.hasThumbnail__623de { - grid-template-columns: minmax(0,1fr) min-content -} - -.embedMargin__623de { - margin-top: 8px -} - -.embedLink__623de { - cursor: pointer; - text-decoration: none -} - -.embedLink__623de:hover { - text-decoration: underline -} - -.embedSuppressButton__623de { - color: var(--interactive-text-default); - cursor: pointer; - inset-inline-end: -20px; - opacity: 0; - padding: 2px; - position: absolute; - top: -2px -} - -.embed__623de:focus-within .embedSuppressButton__623de,.mouse-mode .embed__623de:hover .embedSuppressButton__623de { - opacity: 1 -} - -.embedSuppressButton__623de:hover { - color: var(--interactive-text-hover) -} - -.embedProvider__623de { - font-size: .75rem; - font-weight: var(--font-weight-normal); - grid-column: 1/1; - line-height: 1rem -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .embedProvider__623de { - font-weight:var(--font-weight-medium) - } -} - -.embedAuthor__623de { - align-items: center; - display: flex; - grid-column: 1/1 -} - -.embedAuthorName__623de { - font-size: .875rem; - font-weight: var(--font-weight-semibold) -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .embedAuthorName__623de { - font-weight:var(--font-weight-medium) - } -} - -.embedAuthorNameLink__623de { -} - -.embed__623de .embedAuthorNameLink__623de { - color: var(--text-strong) -} - -.embedAuthorIcon__623de { - border-radius: 50%; - height: 24px; - margin-inline-end:8px;-o-object-fit: contain; - object-fit: contain; - width: 24px -} - -.embedTitle__623de { - display: inline-block; - font-size: 1rem; - font-weight: var(--font-weight-semibold); - grid-column: 1/1 -} - -.embedTitleLink__623de { -} - -.embedDescription__623de { - font-size: .875rem; - font-weight: var(--font-weight-normal); - grid-column: 1/1; - line-height: 1.125rem; - white-space: pre-line -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .embedDescription__623de { - font-weight:var(--font-weight-medium) - } -} - -.embedImage__623de,.embedThumbnail__623de,.embedVideo__623de { - display: block; - -o-object-fit: fill; - object-fit: fill -} - -.embedImage__623de img,.embedImage__623de video,.embedThumbnail__623de img,.embedThumbnail__623de video,.embedVideo__623de img,.embedVideo__623de video { - border-radius: 4px; - display: block -} - -.embedGalleryImagesWrapper__623de { - border-radius: 4px; - -moz-column-gap: 4px; - column-gap: 4px; - display: grid; - grid-column: 1/2; - grid-template-columns: 1fr 1fr; - margin-top: 16px; - overflow: hidden -} - -.embedGallerySide__623de { - display: flex; - flex-direction: column; - overflow: hidden -} - -.embedGallerySide__623de>.galleryImage__623de { - border-radius: 0; - display: flex; - justify-content: center; - margin: 0; - max-width: 100%; - min-height: calc(50% - 2px); - min-width: 100% -} - -.embedGallerySide__623de>.galleryImage__623de:only-child { - min-height: 100% -} - -.embedGallerySide__623de>.galleryImage__623de:nth-child(2) { - margin-top: 4px -} - -.obscureVideoSpacing__623de { - bottom: 36px; - transition: bottom .1s ease-in-out -} - -.galleryImageContainer__623de { - max-height: 100% -} - -.embedGalleryImageElement__623de { - height: 100%; - max-height: 100%!important; - max-width: 100%!important; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.embedThumbnail__623de { - flex-shrink: 0; - grid-column: 2/2; - grid-row: 1/8; - justify-self: end; - margin-inline-start:16px;margin-top: 8px -} - -.embedVideo__623de { - display: flex; - position: relative -} - -.centerContent__623de,.embedVideoActions__623de,.embedVideoImageComponent__623de { - align-items: center; - display: flex; - justify-content: center -} - -.embedVideoImageComponent__623de { - border-radius: 0; - height: 100%!important; - max-height: 100%; - width: 100%!important -} - -.embedVideoImageComponentInner__623de { - height: 100%!important; - -o-object-fit: cover; - object-fit: cover; - width: 100%!important -} - -.embedVideoActions__623de { - inset: 0; - pointer-events: none; - position: absolute; - z-index: 1 -} - -.embedVideoAction__623de { - background: none; - background-size: 24px 24px; - display: inline-block; - height: 24px; - opacity: .6; - width: 24px -} - -.embedVideoAction__623de:hover { - opacity: 1 -} - -.embedIframe__623de { - display: block; - height: 100%; - width: 100% -} - -.embedGIFTag__623de { - inset-inline-end: 4px; - position: absolute; - top: 4px -} - -.embedFooter__623de { - align-items: center; - display: flex; - grid-column: 1/1; - grid-row: auto/auto -} - -.hasThumbnail__623de .embedFooter__623de { - grid-column: 1/3 -} - -.embedFooterText__623de { - color: var(--text-default); - font-size: .75rem; - font-weight: var(--font-weight-medium); - line-height: 1rem -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .embedFooterText__623de { - font-weight:var(--font-weight-medium) - } -} - -.embedFooterSeparator__623de { - color: var(--text-default); - display: inline-block; - font-weight: var(--font-weight-medium); - margin: 0 4px -} - -.embedFooterIcon__623de { - border-radius: 50%; - height: 20px; - margin-inline-end:8px;-o-object-fit: contain; - object-fit: contain; - width: 20px -} - -.embedFields__623de { - display: grid; - grid-column: 1/1; - margin-top: 8px; - grid-gap: 8px -} - -.embedField__623de { - font-weight: var(--font-weight-normal) -} - -.embedField__623de,.embedFieldName__623de { - font-size: .875rem; - line-height: 1.125rem; - min-width: 0 -} - -.embedFieldName__623de { - font-weight: var(--font-weight-semibold); - margin-bottom: 2px -} - -.embedFieldValue__623de { - font-size: .875rem; - font-weight: var(--font-weight-normal); - line-height: 1.125rem; - min-width: 0; - white-space: pre-line -} - -.embedMedia__623de { - border-radius: 4px; - contain: paint; - grid-column: 1/1 -} - -.hasThumbnail__623de .embedMedia__623de { - grid-column: 1/3 -} - -.embedFull__623de .embedMedia__623de { - margin-top: 16px -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .embedFieldName__623de { - font-weight:var(--font-weight-semibold) - } -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .embedFieldValue__623de { - font-weight:var(--font-weight-normal) - } -} - -.embedDescription__623de,.embedFieldValue__623de { - color: var(--text-default) -} - -.embedProvider__623de .embedLink__623de { - color: var(--interactive-text-default) -} - -.embedProvider__623de .embedLink__623de:hover { - color: var(--interactive-text-hover) -} - -.embedAuthorName__623de,.embedFieldName__623de,.embedTitle__623de { - color: var(--text-strong) -} - -.embedAuthor__623de,.embedDescription__623de,.embedFields__623de,.embedFooter__623de,.embedMedia__623de,.embedProvider__623de,.embedTitle__623de { - min-width: 0 -} - -.background-opacity-low .embedFull__623de { - background-color: hsl(var(--primary-630-hsl)/.2); - border-inline-start-color:rgba(32,34,37,.4)!important} - -.background-opacity-low .embedFull__623de code { - background: rgba(32,34,37,.4) -} - -.background-opacity-low .embedThumbnail__623de { - opacity: .4 -} - -.background-opacity-low .embedAmazonMusic__623de,.background-opacity-low .embedAppleMusic__623de,.background-opacity-low .embedSpotify__623de { - opacity: .8 -} - -.background-opacity-low .embedAmazonMusic__623de:hover,.background-opacity-low .embedAppleMusic__623de:hover,.background-opacity-low .embedSpotify__623de:hover { - opacity: 1 -} - -.background-opacity-low .embedPlaystation__623de { - opacity: .8 -} - -.background-opacity-low .embedPlaystation__623de:hover { - opacity: 1 -} - -.background-opacity-low .embedDescription__623de,.background-opacity-low .embedFieldValue__623de,.background-opacity-low .embedFooterText__623de,.background-opacity-low .embedProvider__623de { - color: var(--white)!important; - text-shadow: 0 0 1px var(--opacity-black-28) -} - -.background-opacity-low .embedTitleLink__623de { - color: var(--text-link)!important; - text-shadow: 0 0 1px var(--opacity-white-28),0 0 3px var(--opacity-black-60) -} - -.background-opacity-medium .embedFull__623de { - background-color: hsl(var(--primary-630-hsl)/.2); - border-inline-start-color:rgba(32,34,37,.4)!important} - -.background-opacity-medium .embedFull__623de code { - background: rgba(32,34,37,.4) -} - -.background-opacity-medium .embedThumbnail__623de { - opacity: .4 -} - -.background-opacity-medium .embedAmazonMusic__623de,.background-opacity-medium .embedAppleMusic__623de,.background-opacity-medium .embedSpotify__623de { - opacity: .8 -} - -.background-opacity-medium .embedAmazonMusic__623de:hover,.background-opacity-medium .embedAppleMusic__623de:hover,.background-opacity-medium .embedSpotify__623de:hover { - opacity: 1 -} - -.background-opacity-medium .embedDescription__623de,.background-opacity-medium .embedFieldValue__623de,.background-opacity-medium .embedFooterText__623de,.background-opacity-medium .embedProvider__623de { - color: var(--opacity-black-20)!important; - text-shadow: 0 0 1px rgba(0,0,0,.2) -} - -.background-opacity-medium .embedTitleLink__623de { - color: var(--text-link)!important; - text-shadow: 0 0 1px var(--opacity-white-20),0 0 3px var(--opacity-black-40) -} - -.background-opacity-high .embedFull__623de { - background-color: hsl(var(--primary-630-hsl)/.2); - border-inline-start-color:rgba(32,34,37,.4)!important} - -.background-opacity-high .embedFull__623de code { - background: rgba(32,34,37,.4) -} - -.background-opacity-high .embedThumbnail__623de { - opacity: .4 -} - -.background-opacity-high .embedAmazonMusic__623de,.background-opacity-high .embedAppleMusic__623de,.background-opacity-high .embedSpotify__623de { - opacity: .9 -} - -.background-opacity-high .embedAmazonMusic__623de:hover,.background-opacity-high .embedAppleMusic__623de:hover,.background-opacity-high .embedSpotify__623de:hover { - opacity: 1 -} - -.background-opacity-high .embedDescription__623de,.background-opacity-high .embedFieldValue__623de,.background-opacity-high .embedFooterText__623de,.background-opacity-high .embedProvider__623de { - text-shadow: 0 0 1px rgba(0,0,0,.2) -} - -.background-opacity-high .embedTitleLink__623de { - text-shadow: 0 0 1px hsla(0,0%,100%,.2),0 0 3px rgba(0,0,0,.4) -} - -.custom-theme-background .embedFull__623de { - background-color: var(--background-mod-subtle) -} - -.hiddenEmbed__623de.isHidden__623de { - overflow: hidden -} - -.hiddenEmbed__623de.isHidden__623de .grid__623de { - pointer-events: none; - transition: filter .1s ease-out -} - -.spoilerEmbed__623de { -} - -.spoilerEmbed__623de.isHidden__623de { - cursor: pointer -} - -.spoilerEmbed__623de.isHidden__623de .grid__623de { - filter: blur(var(--custom-embed-spoiler-blur-radius)) -} - -.hiddenExplicitEmbed__623de { -} - -.hiddenExplicitEmbed__623de.isHidden__623de .grid__623de { - filter: blur(var(--__obscured-background-blur-radius)) brightness(var(--__obscured-background-brightness)) -} - -.hiddenExplicitEmbed__623de.embedFull__623de { - border-inline-start:0} - -.justifyAuto__623de { - justify-self: auto!important -} - -.overlay-unlocked .embedAmazonMusic__623de,.overlay-unlocked .embedAppleMusic__623de,.overlay-unlocked .embedSpotify__623de,.overlay-unlocked .embedThumbnail__623de { - opacity: 1 -} - -.hiddenAttachment__623de.isHidden__623de { - pointer-events: none; - transition: filter .1s ease -} - -.spoilerAttachment__623de { -} - -.spoilerAttachment__623de.isHidden__623de { - filter: blur(var(--custom-embed-spoiler-blur-radius)) -} - -.hiddenExplicitAttachment__623de { -} - -.hiddenExplicitAttachment__623de.isHidden__623de { - filter: blur(var(--__obscured-background-blur-radius)) brightness(var(--__obscured-background-brightness)) -} - -.enable-forced-colors .embed__623de { - border: 1px solid CanvasText -} - -.enable-forced-colors .embedFull__623de { - border-left-width: 4px -} - -.enable-forced-colors .embedSuppressButton__623de { - background-color: ButtonFace; - border: 1px solid ButtonFace; - color: ButtonText; - opacity: 1 -} - -.enable-forced-colors .embedSuppressButton__623de:focus,.enable-forced-colors .embedSuppressButton__623de:hover { - border-color: ButtonText -} - -.contentPlaceholder__623de+.contentPlaceholder__623de { - margin-inline-start:0} - -.container_b7e1cb { - display: grid; - grid-auto-flow: row; - height: -moz-fit-content; - height: fit-content; - grid-row-gap: .25rem; - grid-template-columns: repeat(auto-fill,minmax(100%,1fr)); - min-height: 0; - min-width: 0; - padding-bottom: .125rem; - padding-top: .125rem; - position: relative; - text-indent: 0 -} - -.container_b7e1cb:empty { - display: none -} - -.gifFavoriteButton_b7e1cb { - box-sizing: border-box; - display: block; - margin-block:0;margin-inline:auto 0;opacity: 0; - transform: translateY(-16px) -} - -.full-motion .gifFavoriteButton_b7e1cb { - transition: transform .2s ease,opacity .1s ease -} - -.imageWrapper:focus-within .gifFavoriteButton_b7e1cb,.imageWrapper:hover .gifFavoriteButton_b7e1cb { - opacity: 1; - transform: translateY(0) -} - -.giftCodeContainer_b7e1cb { - width: 100% -} - -.referralContainer_b7e1cb { - justify-content: space-between; - width: 100% -} - -.pollContainer_b7e1cb { - margin: 4px 0 -} - -.confirmText_b7e1cb { - margin-bottom: 12px -} - -.threadRoleMentionFailure_b7e1cb { - color: var(--text-feedback-critical); - font-size: 12px; - line-height: 16px -} - -.ctaButtonContainer_b7e1cb { - margin-bottom: 6px; - margin-top: 6px -} - -.embedIFrame__49997 { - opacity: .8 -} - -.embedIFrame__49997:hover { - opacity: 1 -} - -.container__7473d { - background: var(--background-mod-subtle); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-sizing: border-box; - flex-wrap: wrap; - justify-content: space-between; - max-width: 432px; - padding: var(--space-12); - width: 100% -} - -.container__7473d,.themePreviewContainer__7473d { - align-items: center; - display: flex; - gap: var(--space-12) -} - -.themePreviewContainer__7473d { - flex-grow: 2; - justify-content: flex-start -} - -.colorPreview__7473d { - border-radius: var(--radius-sm); - flex-grow: 0; - flex-shrink: 0; - height: 60px; - overflow: hidden; - width: 60px -} - -.sharedByContainer__7473d { - display: flex; - flex-direction: column; - gap: var(--space-6) -} - -.sharedTitle__7473d { - align-items: center; - display: flex; - gap: var(--space-6) -} - -.sharedBy__7473d { - flex-wrap: wrap -} - -.avatarContainer__7473d,.sharedBy__7473d { - display: flex; - gap: var(--space-4) -} - -.avatarContainer__7473d { - flex-wrap: nowrap -} - -.container_adb50a { - background: var(--background-base-lower); - border: 1px solid var(--message-highlight-background-hover); - border-radius: 12px; - box-shadow: var(--shadow-low); - display: flex; - gap: 24px; - justify-content: space-between; - padding: 16px; - transition: background .3s -} - -@media (max-width: 1439px) { - .container_adb50a { - flex-direction:column-reverse; - max-width: var(--custom-guild-shop-content-width-reduced) - } - - .container_adb50a .coverImageContainer_adb50a,.container_adb50a .infoContainer_adb50a { - max-width: none - } -} - -.coverImageContainer_adb50a { - max-width: 655px; - width: 100% -} - -.coverImage_adb50a { - aspect-ratio: 4; - border-radius: 12px; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.infoContainer_adb50a { - display: flex; - flex: 1; - max-width: 500px -} - -.infoContainerNoDescription_adb50a,.showMoreButton_adb50a { - align-items: center -} - -.showMoreButton_adb50a { - border-bottom: 1px solid var(--interactive-text-active); - cursor: pointer; - display: inline-flex; - padding-bottom: 2px -} - -.showMoreButton_adb50a:hover { - opacity: .75; - transition: .3s ease -} - -.showMoreArrow_adb50a { - color: var(--interactive-text-active); - height: 16px; - margin-inline-end:-3px;width: 16px -} - -.guildShopEmbed__86fa6 { - border-radius: 12px; - max-width: 510px; - min-height: 300px; - min-width: 160px; - padding: 16px; - width: 100% -} - -.theme-dark .guildShopEmbed__86fa6 { - background-color: var(--background-base-lower) -} - -.theme-light .guildShopEmbed__86fa6 { - background-color: var(--background-mod-normal) -} - -.spinnerContainer__86fa6 { - align-items: center; - display: flex; - justify-content: center -} - -.serverShopLabel__86fa6 { - display: flex; - gap: 4px -} - -.serverShopLabelText__86fa6 { - text-transform: uppercase -} - -.guildShopEmbedFooter__86fa6 { - align-items: center; - display: flex; - flex-wrap: wrap; - gap: 12px; - width: 100% -} - -.guildShopSummary__86fa6 { - color: var(--text-muted); - display: flex; - flex-direction: column; - height: 42px; - justify-content: space-between; - list-style-type: disc; - padding-inline-start:20px} - -.guildShopEmbedCta__86fa6 { - margin-inline-start:auto} - -.guildShopEmbedCtaContent__86fa6 { - display: flex; - gap: 8px -} - -.accessory__09bc1 { - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - gap: var(--space-4); - justify-content: center -} - -.accessory__09bc1:hover,.accessory__09bc1:hover .accessoryText__09bc1 { - color: var(--interactive-text-hover) -} - -.accessoryText__09bc1 { - color: var(--interactive-text-default) -} - -.toast__1562f { - gap: var(--space-8) -} - -.image_d93468 { - border-radius: var(--radius-xs) -} - -.image_d93468,.video_d93468 { - width: 100% -} - -.media_d93468,.mediaContainer_d93468 { - border-radius: var(--radius-xs) -} - -.mediaContainer_d93468 { - background-color: var(--background-base-lower); - margin-bottom: var(--space-16); - margin-top: var(--space-12); - padding: var(--space-16) -} - -.container__122e4 { - display: flex; - width: 100% -} - -.quote__122e4 { - background-color: var(--border-subtle); - border-radius: 2px; - flex-shrink: 0; - margin-inline-end:12px;width: 4px -} - -.content__122e4 { - display: flex; - flex-direction: column; - flex-grow: 1; - max-width: 100% -} - -.headerContainer__122e4 { - align-items: center; - align-self: flex-start; - display: flex; - gap: 4px; - margin-bottom: 4px; - margin-top: 2px -} - -.headerIcon__122e4 { - flex-shrink: 0 -} - -.headerText__122e4 { - font-style: italic -} - -.footerContainer__122e4 { - align-items: center; - align-self: flex-start; - border-radius: 4px; - color: var(--text-muted); - cursor: pointer; - display: flex; - gap: 4px; - margin-top: 4px; - max-width: 100%; - padding-bottom: 1px; - padding-top: 1px -} - -.footerContainer__122e4:hover { - background-color: var(--interactive-background-hover) -} - -.footerContainer__122e4:hover .footerText__122e4 { - color: var(--interactive-text-hover) -} - -.footerText__122e4 { - overflow: hidden; - text-overflow: ellipsis; - white-space: pre -} - -.originIcon__122e4 { - border-radius: 4px; - height: 16px; - width: 16px -} - -.wrapper_d5f3cd { - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-sizing: border-box; - cursor: default; - display: flex; - flex-direction: column; - max-width: 432px; - min-width: 160px; - padding: 16px; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - width: 100% -} - -.header_d5f3cd { - color: var(--interactive-text-default); - font-weight: var(--font-weight-bold); - margin-bottom: 12px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.content_d5f3cd { - align-items: center; - display: flex; - flex-flow: row wrap; - gap: 16px -} - -.guildIcon_d5f3cd { - background-color: var(--background-mod-subtle); - flex: 0 0 auto -} - -.applicationIcon_d5f3cd { - height: 48px; - width: 48px -} - -.guildIconJoined_d5f3cd { - cursor: pointer -} - -.guildIconImage_d5f3cd { - background-color: var(--background-mod-subtle) -} - -.guildIconImageJoined_d5f3cd { - cursor: pointer -} - -.inviteDestination_d5f3cd { - margin-bottom: 2px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.inviteDestinationJoined_d5f3cd { - cursor: pointer -} - -.inviteDestinationJoined_d5f3cd:hover { - text-decoration: underline -} - -.channel_d5f3cd { - align-items: center; - display: flex -} - -.channelName_d5f3cd { - flex: 1 1 auto; - margin-inline-start:4px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.channelIcon_d5f3cd { - color: var(--text-muted); - flex: 0 0 auto -} - -.status_d5f3cd { - border-radius: 50%; - display: block; - flex: 0 0 auto; - height: 8px; - margin-inline-end:4px;width: 8px -} - -.statusWrapper_d5f3cd { - align-items: center; - display: flex; - flex: 0 1 auto; - flex-flow: nowrap; - min-width: 0 -} - -.statusOnline_d5f3cd { - background-color: var(--green-360) -} - -.statusOffline_d5f3cd { - background-color: var(--primary-400) -} - -.count_d5f3cd { - color: var(--interactive-text-default); - flex: 0 1 auto; - margin-inline-end:8px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.count_d5f3cd:last-child { - margin-inline-end:0} - -.guildNameWrapper_d5f3cd { - align-items: center; - display: flex; - flex: 0 1 auto; - max-width: 100%; - overflow: hidden -} - -.guildName_d5f3cd { - flex: 1 1 auto; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.guildBadge_d5f3cd { - flex: 0 0 auto; - margin-inline-end:8px} - -.guildInfo_d5f3cd { - min-width: 1px -} - -.guildDetail_d5f3cd { - color: var(--interactive-text-default); - font-size: 14px; - line-height: 16px -} - -.statusCounts_d5f3cd { - align-items: center; - -moz-column-gap: 12px; - column-gap: 12px; - display: flex; - flex-flow: row wrap -} - -.inviteSplash_d5f3cd { - background-color: var(--background-mod-subtle); - border-radius: 4px 4px 0 0; - height: 64px; - margin-top: -16px; - margin-inline:-16px;margin-bottom: 16px; - overflow: hidden; - position: relative -} - -.inviteSplashImage_d5f3cd { - display: block; - height: 100%; - -o-object-fit: cover; - object-fit: cover; - opacity: 0; - transition: opacity .125s; - width: 100% -} - -.inviteSplashBadge_d5f3cd { - inset-inline-end: 8px; - position: absolute; - top: 8px -} - -.inviteSplashImageLoaded_d5f3cd { - opacity: 1 -} - -.guildIconExpired_d5f3cd { - background-color: var(--background-base-lower); - background-position: 50%; - background-repeat: no-repeat; - background-size: 50px 26px; - border-radius: 12px; - height: 48px; - width: 48px -} - -.inviteDestinationExpired_d5f3cd { - color: var(--text-feedback-critical); - overflow: hidden; - text-overflow: ellipsis -} - -.buttonForNonMember_d5f3cd { - display: block; - margin-inline-start:0;margin-top: 16px; - width: 100% -} - -@keyframes invite-button-resolving_d5f3cd { - 0% { - transform: translate3d(calc(var(--custom-invite-button-resolving-background-width)*-1),0,0) - } - - 50% { - transform: translateZ(0) - } - - to { - transform: translate3d(calc(var(--custom-invite-button-resolving-background-width)*-1),0,0) - } -} - -.resolvingWrapper_d5f3cd { - align-items: center; - display: flex; - flex-flow: row wrap; - gap: 16px; - overflow: hidden; - width: 100% -} - -.resolving_d5f3cd { - flex: 1000 0 auto; - height: 50px; - -webkit-mask: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='259' height='50' viewBox='6.122 5.864 259 50'%3E%3Cpath fill-rule='evenodd' d='M68.122 11.864h124a3 3 0 0 1 3 3v14a3 3 0 0 1-3 3h-124a3 3 0 0 1-3-3v-14a3 3 0 0 1 3-3m0 24h194a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3h-194a3 3 0 0 1-3-3v-10a3 3 0 0 1 3-3m-42.77-30h11.54c6.687 0 9.112.696 11.556 2.003a13.63 13.63 0 0 1 5.67 5.67c1.31 2.447 2.004 4.87 2.004 11.557v11.54c0 6.687-.696 9.112-2.003 11.556a13.63 13.63 0 0 1-5.67 5.67c-2.447 1.31-4.87 2.004-11.557 2.004h-11.54c-6.687 0-9.112-.696-11.556-2.003a13.63 13.63 0 0 1-5.67-5.67c-1.31-2.447-2.004-4.87-2.004-11.557v-11.54c0-6.687.696-9.112 2.003-11.556a13.63 13.63 0 0 1 5.67-5.67c2.447-1.31 4.87-2.004 11.557-2.004'/%3E%3C/svg%3E"); - mask: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='259' height='50' viewBox='6.122 5.864 259 50'%3E%3Cpath fill-rule='evenodd' d='M68.122 11.864h124a3 3 0 0 1 3 3v14a3 3 0 0 1-3 3h-124a3 3 0 0 1-3-3v-14a3 3 0 0 1 3-3m0 24h194a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3h-194a3 3 0 0 1-3-3v-10a3 3 0 0 1 3-3m-42.77-30h11.54c6.687 0 9.112.696 11.556 2.003a13.63 13.63 0 0 1 5.67 5.67c1.31 2.447 2.004 4.87 2.004 11.557v11.54c0 6.687-.696 9.112-2.003 11.556a13.63 13.63 0 0 1-5.67 5.67c-2.447 1.31-4.87 2.004-11.557 2.004h-11.54c-6.687 0-9.112-.696-11.556-2.003a13.63 13.63 0 0 1-5.67-5.67c-1.31-2.447-2.004-4.87-2.004-11.557v-11.54c0-6.687.696-9.112 2.003-11.556a13.63 13.63 0 0 1 5.67-5.67c2.447-1.31 4.87-2.004 11.557-2.004'/%3E%3C/svg%3E"); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - mask-type: luminance; - position: relative; - width: 259px -} - -.resolvingBackground_d5f3cd { - animation: invite-button-resolving_d5f3cd 4s ease-in-out infinite; - bottom: 0; - inset-inline-start: 0; - position: absolute; - top: 0; - width: calc(var(--custom-invite-button-resolving-background-width)*2) -} - -.resolvingFakeButton_d5f3cd { - border-radius: 3px; - flex: 1 0 auto; - height: 38px; - -webkit-mask: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='66' height='38' viewBox='0 0 66 38'%3E%3Cpath d='M0 0h66v38H0z' style='fill:%23000;stroke:%23000'/%3E%3C/svg%3E"); - mask: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='66' height='38' viewBox='0 0 66 38'%3E%3Cpath d='M0 0h66v38H0z' style='fill:%23000;stroke:%23000'/%3E%3C/svg%3E"); - mask-type: luminance; - min-width: 66px; - position: relative -} - -.theme-dark .background-opacity-low .wrapper_d5f3cd { - background: hsl(var(--primary-630-hsl)/.3) -} - -.theme-dark .background-opacity-low .header_d5f3cd { - text-shadow: 0 0 1px var(--primary-700),1px 1px 0 var(--primary-700) -} - -.theme-dark .background-opacity-low .button_d5f3cd { - opacity: .8 -} - -.theme-dark .background-opacity-medium .wrapper_d5f3cd { - background: hsl(var(--primary-630-hsl)/.5) -} - -.theme-dark .background-opacity-medium .header_d5f3cd { - text-shadow: 0 0 1px var(--primary-600),1px 1px 0 var(--primary-600) -} - -.theme-dark .background-opacity-medium .guildIcon_d5f3cd { - opacity: .7 -} - -.theme-dark .background-opacity-medium .button_d5f3cd { - opacity: .9 -} - -.theme-dark .background-opacity-high .wrapper_d5f3cd { - background: hsl(var(--primary-630-hsl)/.6) -} - -.theme-dark .overlay-unlocked .wrapper_d5f3cd { - background-color: var(--background-base-lower) -} - -.theme-dark .overlay-unlocked .header_d5f3cd { - text-shadow: none -} - -.theme-dark .overlay-unlocked .button_d5f3cd,.theme-dark .overlay-unlocked .guildIcon_d5f3cd { - opacity: 1 -} - -.images-light .guildIconExpired_d5f3cd { - background-image: url(/assets/eb4644cbe2f3e90f.svg) -} - -.images-light .resolvingBackground_d5f3cd { - background-image: linear-gradient(to right,var(--primary-100),var(--primary-200),var(--primary-100)) -} - -.images-dark .guildIconExpired_d5f3cd { - background-image: url(/assets/e4a502bfc6589d65.svg) -} - -.images-dark .resolvingBackground_d5f3cd { - background-image: linear-gradient(to right,var(--primary-600),var(--primary-500),var(--primary-600)) -} - -.invalidBody__164c9 { - align-items: center; - display: flex; - gap: 16px; - max-width: 100% -} - -.lineClamp2Plus__38db5 { - white-space: pre-wrap -} - -.lineClamp1__38db5 { - white-space: nowrap -} - -.roleTag__9cd44,.roleTagContainer__9cd44 { - display: flex -} - -.roleTag__9cd44 { - align-items: center; - background: var(--interactive-background-hover); - border-radius: 4px; - padding: 8px -} - -.roleColor__9cd44 { - border-radius: 6px; - flex: none; - height: 12px; - width: 12px -} - -.productCard__79d38 { - border-radius: 8px; - overflow: hidden; - position: relative -} - -.solidBackground__79d38 { - background: var(--background-base-lower) -} - -.theme-dark .opaqueBackground__79d38 { - background: rgba(48,51,56,.85) -} - -.theme-light .opaqueBackground__79d38 { - background: var(--background-base-lower) -} - -.productThumbnailContainer__79d38 { - position: relative -} - -.productThumbnail__79d38 { - background: #2b2c31; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.purchaseToUnlockBadge__79d38 { - align-items: center; - background-color: rgba(43,45,49,.7); - border-radius: 30px; - color: var(--white); - display: flex; - gap: 6px; - inset-inline-start: 50%; - justify-content: center; - padding: 4px 12px; - position: absolute; - top: 50%; - transform: translate(-50%,-50%); - transition: background-color .2s ease-in-out; - white-space: nowrap -} - -.draftBadge__79d38 { - bottom: 10px; - inset-inline-end: 8px; - position: absolute -} - -.productThumbnailContainer__79d38:hover .purchaseToUnlockBadge__79d38 { - background-color: rgba(43,45,49,.45) -} - -.lockIcon__79d38 { - height: 16px; - width: 16px -} - -.productName__79d38 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.productDetails__79d38 { - align-content: flex-start; - border-bottom: 1px solid var(--border-subtle); - display: flex; - flex-direction: row; - flex-grow: 1; - padding: 16px 16px 0 -} - -.productDetailContent__79d38 { - display: flex; - flex: 1; - flex-direction: column; - gap: 4px; - margin-inline-end:24px;overflow: hidden -} - -.purchaseDetails__79d38 { - display: grid; - grid-template-columns: 1fr auto; - padding: 16px -} - -.productActionButton__79d38 { - grid-area: 1/2/3/3 -} - -.showMoreButton__79d38 { - align-items: center; - color: var(--interactive-text-hover); - display: flex; - gap: 4px; - padding-top: 2px -} - -.arrowIcon__79d38 { - margin-bottom: -2px -} - -.hasAction__79d38 { - color: var(--text-link); - cursor: pointer -} - -.cardClickableContainer__79d38 { - display: flex; - flex-direction: column; - height: 100%; - width: 100% -} - -.container__652b4 { - background-position: 50%; - background-size: cover; - border-radius: var(--radius-sm) var(--radius-sm) 0 0; - height: 164px; - margin: -17px -17px 16px -} - -.descriptionText__535f5 { - color: var(--text-default); - font-family: var(--font-primary); - font-size: 14px; - line-height: 18px; - margin-top: 4px -} - -.truncate__535f5 { - display: -webkit-box; - overflow: hidden; - text-overflow: ellipsis; - -webkit-line-clamp: 4; - -webkit-box-orient: vertical; - max-height: 72px -} - -.eventStatusContainer__29021 { - align-items: center; - display: flex; - flex-direction: row -} - -.isRecurring__29021 { - align-items: flex-start -} - -.eventStatusGreen__29021 { - color: var(--text-feedback-positive) -} - -.eventStatusBrand__29021 { - color: var(--text-brand) -} - -.eventStatusLabel__29021 { - margin-inline-start:8px} - -.liveEventEndTime__29021 { - display: inline-block -} - -.newBadge__29021 { - align-items: center; - background-color: var(--brand-260); - border-radius: 12px; - display: inline-flex; - flex-shrink: 0; - height: 20px; - justify-content: center; - margin-inline-end:8px;padding: 0 6px; - vertical-align: text-top -} - -.newBadgeText__29021 { - color: var(--brand-560); - text-transform: uppercase -} - -.container_b5010b { - display: flex; - flex-direction: column -} - -.eventName_b5010b { - max-width: 534px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.eventName_b5010b:hover { - text-decoration: underline -} - -.description_b5010b { - margin-top: 8px -} - -.descriptionWithThumbnail_b5010b { - max-width: 288px -} - -.spacer_b5010b { - margin-inline-start:auto} - -.rsvpCount_b5010b { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: 12px; - display: flex; - flex-shrink: 0; - height: 20px; - margin-bottom: auto; - padding-inline:8px} - -.rsvpIcon_b5010b { - color: var(--text-default); - margin-inline-end:4px} - -.creator_b5010b { - margin-inline-end:8px} - -.inline_b5010b { - align-items: center; - display: flex -} - -.eventInfoStatusContainer_b5010b { - margin-bottom: 12px -} - -.statusContainer_b5010b,.withThumbnail_b5010b { - display: flex; - flex-direction: row -} - -.withThumbnail_b5010b { - align-items: flex-start; - justify-content: space-between -} - -.thumbnailContainer_b5010b { - align-items: center; - height: auto -} - -.thumbnail_b5010b { - border-radius: var(--radius-sm) var(--radius-sm) 0 0; - height: 120px; - margin-block:0;margin-top: -16px; - margin-inline:-16px;margin-bottom: 16px; - width: calc(100% + 32px) -} - -.enable-forced-colors .eventName_b5010b { - color: ButtonText; - text-decoration: underline -} - -.responseOptions_d650db { - margin-top: 8px -} - -.confirmButton_d650db { - margin-inline-start:8px} - -.inviteDetailsContainer_da5e8d { - align-items: center; - display: flex; - flex: 1000 0 auto; - gap: 16px; - max-width: 100%; - min-width: 0 -} - -.clickable_da5e8d { - cursor: pointer -} - -.clickable_da5e8d:hover { - background-color: var(--background-mod-normal) -} - -.guildChannelInfoContainer_da5e8d { - align-items: center; - display: flex; - gap: 4px; - min-width: 0 -} - -.verticalContainer_da5e8d { - display: flex; - flex-direction: column; - justify-content: center; - min-width: 0 -} - -.footerContainer_da5e8d { - align-items: center; - display: flex; - flex-flow: row wrap; - gap: 16px; - margin-top: 12px; - overflow: hidden -} - -.eventDescription_da5e8d { - font-size: 14px; - line-height: 18px -} - -.channelDescription_da5e8d { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.guildBadge_da5e8d { - flex-shrink: 0 -} - -.guildName_da5e8d { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.guildName_da5e8d,.guildNameClickable_da5e8d { - min-width: 0 -} - -.guildNameLinkable_da5e8d { - cursor: pointer -} - -.guildNameLinkable_da5e8d:hover { - text-decoration: underline -} - -.channelIcon_da5e8d { - flex-shrink: 0 -} - -.channelInfoContainer_da5e8d { - align-items: center; - color: var(--text-default); - display: flex; - flex-direction: row; - margin-top: 4px -} - -.channelLocationLink_da5e8d { - color: var(--text-default); - cursor: pointer; - display: flex; - gap: 4px; - min-width: 0 -} - -.channelLocationLink_da5e8d:hover { - text-decoration: underline -} - -.banner_da5e8d { - border-radius: 4px 4px 0 0; - margin: -16px -16px 16px -} - -.icon__33e19 { - color: var(--interactive-text-default) -} - -.separator_ae2544 { - background-color: var(--border-subtle); - height: 1px; - margin-bottom: 16px; - margin-top: 16px -} - -.infoTitle_ae2544 { - align-items: center; - display: flex; - overflow: hidden -} - -.infoBadge_ae2544 { - margin-inline-start:4px} - -.headerLine_ae2544 { - align-items: center; - display: flex; - flex: 1000 0 auto; - gap: 16px; - max-width: 100% -} - -.tooltipContainer_ae2544 { - display: inline-block; - line-height: 0; - margin-inline-start:4px;vertical-align: sub -} - -.infoIcon_ae2544 { - height: 16px; - width: 16px -} - -.buttonContainer_ae2544 { - flex: 1 -} - -.container_bb8774 { - background-color: var(--card-background-default); - border: 1px solid var(--border-subtle); - border-radius: 8px; - flex-direction: column; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - width: 100% -} - -.container_bb8774,.image_bb8774 { - display: flex; - overflow: hidden -} - -.image_bb8774 { - align-items: center; - background-color: var(--brand-500); - border-radius: var(--radius-md); - flex-shrink: 0; - justify-content: center -} - -.image_bb8774,.image_bb8774 img { - height: 80px; - width: 80px -} - -.description_bb8774 { - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - display: -webkit-box; - max-height: 36px; - overflow: hidden; - text-align: center; - width: 100% -} - -.card_bb8774 { - background-color: var(--card-background-default); - gap: 10px; - padding: 16px -} - -.body_bb8774,.card_bb8774 { - align-items: center; - display: flex; - flex-direction: column -} - -.body_bb8774 { - align-self: stretch; - gap: 16px -} - -.purchaseBtn_bb8774 { - width: 100% -} - -.benefits_bb8774 { - background-color: var(--background-base-lower); - flex-grow: 1; - padding: 16px -} - -.benefitsContainer_bb8774 { - gap: 24px -} - -.benefitsContainer_bb8774,.benefitsList_bb8774 { - align-items: flex-start; - align-self: stretch; - display: flex; - flex-direction: column -} - -.benefitsList_bb8774 { - gap: 12px -} - -.benefitsBenefit_bb8774 { - align-items: center; - align-self: stretch; - display: flex; - gap: 16px -} - -.cardBenefit_bb8774 { - display: flex; - gap: 16px; - width: 100% -} - -@value iconSize: 24px;.cardBenefitIcon_bb8774 { - font-size: 24px; - height: 24px; - width: 24px -} - -.cardBenefitIcon_bb8774 img { - height: 24px; - -o-object-fit: cover; - object-fit: cover; - width: 24px -} - -.benefitsSummary_bb8774 { - align-items: center; - align-self: stretch; - background: var(--background-base-lowest); - display: flex; - flex-direction: column; - justify-content: space-between; - padding: 16px -} - -.container__96554 { - align-items: center; - background: var(--background-base-lowest); - border-radius: 12px; - display: inline-flex; - gap: 4px; - padding: 2px 8px -} - -.containerScrollGradient__956c6 { - background: linear-gradient(0deg,var(--background-base-low) 0,transparent 100%); - bottom: 52px; - height: 52px; - left: 0; - opacity: 0; - pointer-events: none; - position: absolute; - transition: opacity .12s ease-in-out; - width: 100% -} - -.containerScrollGradient__956c6[data-shown=true] { - opacity: 1 -} - -.text_f4d1ff { - display: flex; - gap: 4px -} - -.footer__24654 { - align-items: center; - display: flex; - flex-direction: row; - justify-content: space-between; - position: relative -} - -.footerButtons__24654 { - display: flex; - gap: 10px -} - -.appIcon__24654 { - --custom-size: 24px; - border-radius: var(--custom-size); - height: var(--custom-size); - margin-top: var(--custom-modal-padding-md); - margin-inline-start:var(--space-16);-o-object-fit: cover; - object-fit: cover; - width: var(--custom-size) -} - -.unavailableBody__24654 { - margin-bottom: 32px; - margin-top: 8px -} - -.container__8823a { - background: var(--background-base-lower); - box-sizing: border-box; - padding-bottom: 24px; - position: relative -} - -.header__8823a { - align-items: center; - background-color: var(--background-base-lowest); - display: flex; - height: 260px; - justify-content: center; - margin-bottom: 24px; - overflow: hidden; - position: relative -} - -.headerBackground__8823a { - --custom-overlay-color: hsl(var(--primary-730-hsl)/0.9); - background-image: linear-gradient(var(--custom-overlay-color),var(--custom-overlay-color)),var(--custom-background-url); - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - filter: blur(4px); - height: 120%; - left: 50%; - position: absolute; - top: 50%; - transform: translate3d(-50%,-50%,0); - width: 120% -} - -.theme-light .headerBackground__8823a { - --custom-overlay-color: hsl(var(--white-500-hsl)/0.3) -} - -.headerImage__8823a { - align-items: center; - border-radius: 8px; - display: flex; - height: 100%; - justify-content: center; - max-height: 85%; - max-width: 59.5%; - overflow: hidden; - width: 100%; - z-index: 1 -} - -.headerImage__8823a>img { - border-radius: 8px; - box-shadow: var(--shadow-high); - max-height: 100%; - max-width: 100% -} - -.scroller__8823a { - height: 100%; - scrollbar-width: thin -} - -.scrollContent__8823a { - display: flex; - flex-direction: column; - padding-bottom: 32px -} - -.content__8823a { - padding: 0 16px -} - -.details__8823a { - gap: 8px -} - -.benefits__8823a,.details__8823a { - display: flex; - flex-direction: column -} - -.benefits__8823a { - align-items: flex-start; - border-top: 1px solid var(--interactive-background-selected); - gap: 16px; - margin-top: 16px; - padding-top: 16px -} - -.description__8823a { - white-space: break-spaces -} - -.content__57f77 { - align-items: center; - display: flex; - flex-direction: row; - justify-content: center; - margin: auto; - min-width: 32px -} - -.loading__57f77 { - inset-inline: 0; - position: absolute; - top: 50%; - transform: translateY(-50%) -} - -.textEmoji__57f77 { - flex-shrink: 0; - margin-inline-end:4px} - -.premium__57f77 { - transition: width .3s -} - -.shopIcon__57f77 { - align-items: center; - display: flex; - margin-inline-end:4px} - -.launchIcon__57f77 { - flex-shrink: 0; - margin-inline-start:8px} - -.hidden__57f77 { - visibility: hidden -} - -.label__57f77 { - flex-shrink: 1; - overflow: hidden; - text-overflow: ellipsis -} - -.container__81a06 { - align-items: center; - display: flex; - width: 100% -} - -.select__81a06 { - max-width: 100%; - width: 400px -} - -.inModal__81a06 { - max-width: 100%; - width: 100% -} - -.iconContainer__81a06 { - align-items: center; - display: flex -} - -.hidden__81a06 { - opacity: 0; - width: 0 -} - -.container__28e94 { - align-items: center; - display: flex; - width: 100% -} - -.select__28e94 { - max-width: 100%; - width: 400px -} - -.inModal__28e94 { - max-width: 100%; - width: 100% -} - -.selectOption__28e94 { - padding: 8px -} - -.disabled__28e94 { - opacity: .6 -} - -.emoji__28e94,.smallEmoji__28e94 { - margin-inline-end:8px} - -.smallEmoji__28e94 { - height: 16px; - width: 16px -} - -.offset__28e94 { - margin-inline-start:27px} - -.optionTag__28e94 { - align-items: center; - background: var(--background-base-low); - border-radius: 3px; - box-sizing: border-box; - color: var(--interactive-text-active); - display: flex; - flex-shrink: 0; - max-width: 230px; - padding: 6px 8px -} - -.labelContainer__28e94 { - font-size: 14px; - line-height: 18px; - overflow: hidden -} - -.label__28e94 { - color: var(--text-strong); - margin-bottom: 4px -} - -.description__28e94 { - color: var(--interactive-text-default) -} - -.label__28e94,.singleValueLabel__28e94,.tag__28e94 { - display: block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.tag__28e94 { - align-items: center; - display: flex; - height: 20px -} - -.option__28e94 { - font-size: 16px -} - -.option__28e94,.value__28e94 { - align-items: center; - display: flex; - overflow: hidden -} - -.value__28e94 { - flex-wrap: wrap; - gap: 4px -} - -.singleValue__28e94 { - font-size: 16px; - gap: 0 -} - -.singleValueLabel__28e94 { - flex: 1 -} - -.error__28e94 { - align-items: center; - display: flex; - margin-top: 8px -} - -.errorIcon__28e94 { - color: var(--icon-feedback-critical); - display: block; - margin-inline-end:8px} - -.discriminator__78c91,.username__78c91 { - color: var(--text-muted) -} - -.tag__78c91 { - align-items: center -} - -.tag__78c91 .bot__78c91 { - margin-top: 0 -} - -.label__78c91 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px; - min-width: 100%; - overflow: hidden; - width: 0 -} - -.labelText__78c91 { - display: flex; - line-height: 1.2; - white-space: nowrap -} - -.roleCountContainer__78c91 { - align-items: center; - display: flex -} - -.roleCountIcon__78c91,.roleCountText__78c91 { - color: var(--text-muted) -} - -.roleCountText__78c91 { - margin: 0 4px -} - -.container_e21ed7 { - background-color: var(--input-background-default); - border: 1px solid var(--input-border-default); - border-radius: var(--radius-sm); - transition: background-color .1s ease-in-out -} - -.dropping_e21ed7 { - background-color: var(--background-surface-highest) -} - -.error_e21ed7 { - border-color: var(--border-feedback-critical) -} - -.mainWrapper_e21ed7 { - margin: var(--space-xl) var(--space-sm); - position: relative -} - -.mainBody_e21ed7 { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-xs); - justify-content: center -} - -.hidden_e21ed7 { - visibility: hidden -} - -.absolutelyPositioned_e21ed7 { - inset: 0; - position: absolute -} - -.files_e21ed7 { - display: grid; - gap: var(--space-xs); - grid-template-columns: 1fr 1fr; - margin: var(--space-sm) -} - -.file_e21ed7 { - align-items: center; - background-color: var(--background-surface-higher); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - display: flex; - gap: var(--space-sm); - padding: var(--space-xs); - position: relative -} - -.file_e21ed7:focus-within .miniPopover_e21ed7,.file_e21ed7:hover .miniPopover_e21ed7 { - opacity: 1 -} - -.singleFileInput_e21ed7 { - background-color: var(--background-surface-highest) -} - -.filename_e21ed7 { - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - overflow: hidden -} - -.miniPopover_e21ed7 { - background-color: var(--background-surface-high); - border-radius: var(--radius-sm); - height: unset; - inset-inline-end: -6px; - opacity: 0; - outline: 1px solid var(--border-subtle); - position: absolute; - top: -6px; - transition: opacity .1s ease-in-out -} - -.action_e21ed7 { - height: unset; - min-width: unset; - padding: var(--space-6) -} - -.action_e21ed7:active { - padding-bottom: 5px; - padding-top: 7px -} - -.actionIcon_e21ed7 { - height: unset; - width: unset -} - -.fileInput_e21ed7 { - display: none -} - -.clearButtonContainer__6d62c { - display: flex; - justify-content: flex-end; - margin-top: 4px; - max-height: 100px; - opacity: 1; - overflow: hidden -} - -.full-motion .clearButtonContainer__6d62c { - transition: all .2s ease-in-out -} - -.clearButtonContainerHidden__6d62c { - margin-top: 0; - max-height: 0; - opacity: 0 -} - -.formItem__1b11b { - width: 100% -} - -.noDrag_e9ca56 { - -webkit-user-drag: none; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.container_e9ca56 { - position: relative; - width: 380px -} - -.card_e9ca56 { - gap: 8px -} - -.card_e9ca56,.footer_e9ca56 { - display: flex; - padding: var(--space-8) -} - -.footer_e9ca56 { - justify-content: space-between -} - -.title_e9ca56 { - color: #000; - font-size: 16px; - text-transform: uppercase -} - -.asset_e9ca56,.title_e9ca56 { -} - -.asset_e9ca56 { - background-blend-mode: color-burn; - background-image: url(https://cdn.discordapp.com/assets/content/7f5bafad92c161f4e3f0aead83b84dfafeacea91a5eb442f49ed5711470b7887.png); - background-size: cover; - height: 120px; - padding-inline:var(--space-8);padding-bottom: var(--space-16); - padding-top: var(--space-16) -} - -.stats_e9ca56 { - flex-direction: column; - overflow: hidden -} - -.stat_e9ca56,.stats_e9ca56 { - display: flex; - gap: var(--space-8) -} - -.stat_e9ca56 { - align-items: center -} - -.statImage_e9ca56 { - background-color: #000; - border: 1px solid #000; - border-radius: 0; - flex-shrink: 0; - height: 14px; - width: 14px -} - -.statText_e9ca56 { - color: #000; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.cta_e9ca56 { - align-items: center; - cursor: pointer; - display: flex; - gap: var(--space-4) -} - -.ctaText_e9ca56 { - color: #000; - text-transform: uppercase -} - -.powerContainer_e9ca56 { - clip-path: polygon(0 0,100% 0,100% calc(100% - 12px),calc(100% - 12px) 100%,0 100%); - display: flex; - flex-direction: column; - gap: 2px; - inset-inline-start: 0; - padding-inline-end:var(--space-12);padding-bottom: var(--space-4); - padding-top: var(--space-8); - padding-inline-start:var(--space-8);position: absolute; - top: 0 -} - -.powerText_e9ca56 { - display: flex; - gap: 4px -} - -.powerBar_e9ca56 { - display: flex; - gap: 2px -} - -.inline__6c706 { - opacity: 1 -} - -.full-motion .inline__6c706 { - transition: all .1s ease -} - -.inline__6c706.hiddenMosaicItem__6c706 { - visibility: hidden -} - -.full-motion .obscured__6c706 { - transition: all .1s ease -} - -.obscured__6c706.hiddenMosaicItem__6c706 { - pointer-events: none -} - -.obscured__6c706.hiddenMosaicItem__6c706.hiddenSpoiler__6c706 { - filter: blur(var(--custom-message-attachment-spoiler-blur-radius)) -} - -.obscured__6c706.hiddenMosaicItem__6c706.hiddenExplicit__6c706 { - filter: blur(var(--__obscured-background-blur-radius)) brightness(var(--__obscured-background-brightness)) -} - -.obscureVideoSpacing__6c706 { - bottom: 36px; - transition: bottom .1s ease-in-out -} - -.mosaicItemContent__6c706 { - flex: auto -} - -.removeMosaicItemButton__6c706 { - align-self: flex-start; - cursor: pointer; - flex: 0 0 auto; - opacity: 0; - padding: 2px -} - -.spoilerRemoveMosaicItemButton__6c706 { - inset-inline-end: -20px; - position: absolute; - top: -2px -} - -.mosaicItem__6c706 { - align-items: start; - display: flex; - flex-flow: row nowrap; - height: 100%; - max-width: 100% -} - -.mosaicItemNoJustify__6c706 { - justify-self: auto!important -} - -.mosaicItemFullWidth__6c706 { - align-items: center; - width: 100% -} - -.mosaicItemMediaMosaic__6c706 { - border-radius: 2px; - max-height: inherit; - position: relative -} - -.hasFooter__6c706 { - max-width: 100% -} - -.hasFooter__6c706,.hasFooter__6c706 video { - border-end-end-radius: 0; - border-end-start-radius: 0 -} - -.mosaicItemWithFooter__6c706 { - flex-direction: column -} - -.hideOverflow__6c706 { - overflow: hidden -} - -.removeMosaicItemButton__6c706:focus-within,.removeMosaicItemButton__6c706:hover { - color: var(--interactive-text-hover) -} - -.nonMediaMosaicItem__6c706 { - inset-inline-end: -8px; - outline: 1px solid var(--background-base-lower); - top: -8px -} - -.removeMosaicItemHoverButton__6c706:hover,.keyboard-mode .removeMosaicItemHoverButton__6c706:focus { - background-color: var(--control-critical-primary-background-default)!important; - color: var(--control-critical-primary-text-default)!important -} - -.removeMosaicItemHoverButton__6c706:active { - background-color: var(--control-critical-primary-background-active); - color: var(--white) -} - -.downloadHoverButtonIcon__6c706 { - height: 20px; - width: 20px -} - -.enable-forced-colors .removeMosaicItemButton__6c706 { - background-color: ButtonFace; - border: 1px solid ButtonFace; - color: ButtonText; - opacity: 1 -} - -.enable-forced-colors .removeMosaicItemButton__6c706:focus,.enable-forced-colors .removeMosaicItemButton__6c706:hover { - border-color: ButtonText -} - -.hoverButtonGroup__06ab4:hover,.keyboard-mode .hoverButtonGroup__06ab4:focus-within,:hover+.hoverButtonGroup__06ab4 { - opacity: 1 -} - -.hoverButton__06ab4 { - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - padding: 6px -} - -.hoverButton__06ab4.selected__06ab4,.hoverButton__06ab4:hover,.keyboard-mode .hoverButton__06ab4:focus { - background-color: var(--interactive-background-hover); - color: var(--interactive-text-hover) -} - -.hoverButton__06ab4:active { - background-color: var(--interactive-background-active); - color: var(--interactive-text-active) -} - -.hoverButtonGroup__06ab4 { - background-color: var(--background-base-low); - border-radius: 5px; - display: flex; - inset-inline-end: 4px; - opacity: 0; - overflow: hidden; - position: absolute; - top: 4px; - z-index: 2 -} - -.nonMediaMosaicItem__06ab4 { - inset-inline-end: -8px; - outline: 1px solid var(--background-base-lower); - top: -8px -} - -.sizer__06ab4 { - height: 0; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.fileDisplayContainer__55ed7 { - width: -moz-min-content; - width: min-content -} - -.fileHidden__55ed7 { - opacity: 0; - pointer-events: none -} - -.notFoundPlaceholder_a3c1e1 { - align-items: center; - animation: fadeIn_a3c1e1 .7s; - background-color: var(--opacity-black-4); - display: flex; - height: 100%; - justify-content: center; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.brokenImageIcon_a3c1e1 { - color: var(--background-mod-strong); - height: auto; - -o-object-fit: contain; - object-fit: contain; - width: 32px -} - -.hiddenSpoilers_a3c1e1 { - opacity: 0; - pointer-events: none -} - -.oneByOneGrid_f4758a { - display: inline-block; - max-height: 350px; - width: -moz-fit-content; - width: fit-content -} - -.oneByOneGridSingle_f4758a { - border-radius: 8px; - max-width: 100%; - overflow: hidden -} - -.oneByOneGridMosaic_f4758a { - display: flex; - max-height: 280px; - width: 100% -} - -.oneByTwoGrid_f4758a { - display: flex; - flex-direction: row; - gap: 4px; - max-height: 280px -} - -.oneByTwoLayoutThreeGrid_f4758a { - max-height: 350px -} - -.oneByTwoGridItem_f4758a { - flex: 1; - min-width: 0 -} - -.oneByTwoGrid_f4758a .itemContentContainer_f4758a,.oneByTwoGrid_f4758a .lazyImg_f4758a { - height: 100% -} - -.oneByTwoSoloItem_f4758a { - flex: 2 -} - -.oneByTwoDuoItem_f4758a { - flex: 1 -} - -.twoByOneGrid_f4758a { - display: flex; - flex-direction: column; - gap: 4px; - height: 100% -} - -.twoByOneGridItem_f4758a { - flex: 1; - min-height: 0 -} - -.threeByThreeGrid_f4758a { - display: grid; - flex-direction: row; - grid-template-columns: repeat(3,minmax(0,1fr)); - grid-gap: 4px -} - -.threeByThreeGrid_f4758a .lazyImg_f4758a,.threeByThreeGrid_f4758a .lazyImgContainer_f4758a { - aspect-ratio: 1/1 -} - -.twoByTwoGrid_f4758a { - display: grid; - flex-direction: row; - grid-template-columns: repeat(2,minmax(0,1fr)); - grid-template-rows: repeat(2,minmax(0,1fr)); - grid-gap: 4px; - max-height: 350px -} - -.oneByOneGrid_f4758a+.threeByThreeGrid_f4758a,.oneByTwoGrid_f4758a+.threeByThreeGrid_f4758a { - margin-top: 4px -} - -.visualMediaItemContainer_f4758a { - border-radius: 8px; - height: 100%; - max-width: 550px; - overflow: hidden; - width: 100% -} - -.visualMediaItemContainer_f4758a.isInAppComponentsV2_f4758a { - max-width: 600px -} - -.nonVisualMediaItemContainer_f4758a { - display: flex; - flex-direction: column; - max-width: 100% -} - -.nonVisualMediaItem_f4758a { - max-width: 100%; - width: -moz-fit-content; - width: fit-content -} - -.nonVisualMediaItem_f4758a+.nonVisualMediaItem_f4758a,.nonVisualMediaItemContainer_f4758a { - margin-top: 8px -} - -.itemContentContainer_f4758a { - height: 100%; - width: 100% -} - -.hasFooter_f4758a { - max-height: none -} - -.imgContainer__8f9ad { - --__thumbnail-size: 85px; - border-radius: var(--radius-sm); - height: var(--__thumbnail-size); - max-height: var(--__thumbnail-size); - max-width: var(--__thumbnail-size); - overflow: auto; - width: var(--__thumbnail-size) -} - -.img__8f9ad { - -o-object-fit: cover; - object-fit: cover -} - -.hiddenSpoiler__8f9ad { - filter: blur(var(--custom-message-attachment-spoiler-blur-radius)); - pointer-events: none -} - -.container__6c21f { - border: 1px solid var(--status-warning); - border-radius: var(--radius-md); - padding: var(--space-16) -} - -.clickable__4337d { - cursor: pointer -} - -.container__4337d { - align-items: center; - display: flex -} - -.truncatedText__4337d { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.usersHeader__4337d { - display: flex; - white-space: pre -} - -.textPrimary__8e577 { - color: var(--content-inventory-overlay-text-primary) -} - -.textSecondary__8e577 { - color: var(--content-inventory-overlay-text-secondary) -} - -.headerIcons__8e577 { - inset-inline-end: 8px; - position: absolute; - top: 8px -} - -.container__8e577 { - align-items: center; - background-color: #000; - border-radius: var(--radius-sm); - display: flex; - gap: 12px; - margin: 4px 0 0; - max-width: 400px; - padding: 8px; - position: relative; - width: 100% -} - -.clickable__8e577 { - cursor: pointer -} - -.badges__8e577 { - margin-top: 4px -} - -.thumbnailImage__8e577 { - height: 0; - min-height: 100% -} - -.clickableText__8e577 { - max-width: -moz-fit-content; - max-width: fit-content -} - -.truncatedText__8e577 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.infoContainer__8e577 { - overflow: hidden; - position: relative; - width: 100% -} - -.users__8e577 { - overflow: hidden; - width: calc(100% - 30px) -} - -.thumbnailContainer__8e577 { - margin-bottom: -2.5px -} - -.container__1647d { - display: flex; - flex-direction: column -} - -.children__1647d { - display: flex; - flex-wrap: wrap; - gap: var(--space-8) -} - -.error__1647d { - margin: 4px 0 -} - -.container__60fa3 { - background: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - display: flex; - flex-direction: column; - gap: var(--space-8); - overflow: hidden; - padding: var(--space-16) -} - -.container__60fa3.isHidden__60fa3 { - filter: blur(var(--custom-embed-spoiler-blur-radius)); - pointer-events: none -} - -.container__60fa3.withAccentColor__60fa3 { - position: relative -} - -.container__60fa3.withAccentColor__60fa3:before { - background-color: var(--__accent-color); - content: ""; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 4px -} - -.custom-theme-background .container__60fa3 { - background-color: var(--background-mod-subtle) -} - -.description_cd2e37 { - margin-bottom: var(--space-8) -} - -.section_c3d3d9 { - display: flex; - flex-direction: column; - gap: var(--space-4) -} - -.children_c3d3d9 { - display: flex; - gap: var(--space-12); - justify-content: space-between -} - -.textChildren_c3d3d9 { - display: flex; - flex: 1; - flex-direction: column; - row-gap: var(--space-4) -} - -.textChildren_c3d3d9.verticallyCenterAlign_c3d3d9 { - justify-content: center -} - -.accessory_c3d3d9 { - align-items: flex-start; - display: flex; - min-width: 0 -} - -.accessory_c3d3d9.restrictWidth_c3d3d9 { - max-width: calc(50% - var(--space-12)) -} - -.container__5e208 { - display: flex; - flex-direction: column; - row-gap: 6px; - width: 100% -} - -.container__5e208.isComponentsV2__5e208 { - align-items: stretch; - max-width: min(600px,100%); - row-gap: var(--space-8); - width: -moz-fit-content; - width: fit-content -} - -.postPreviewContainer__419e3 { - border-radius: 10px; - max-width: 512px; - min-width: 160px; - overflow: hidden; - width: 100% -} - -.thumbnailContainer__419e3 { - background-color: var(--background-surface-high); - max-height: 288px; - overflow: hidden; - position: relative -} - -.thumbnail__419e3 { - overflow: hidden; - padding: 0; - width: 100% -} - -.thumbnail__419e3,.thumbnailImage__419e3 { - border-radius: 0; - height: 100% -} - -.thumbnailOverlay__419e3 { - align-items: center; - cursor: pointer; - display: flex; - height: 100%; - justify-content: center; - position: absolute; - top: 0; - transition-duration: var(--custom-media-post-embed-transition-duration); - width: 100% -} - -.thumbnailOverlay__419e3:hover { - background-color: var(--interactive-background-hover) -} - -.thumbnailOverlayCta__419e3 { - align-items: center; - background-color: rgba(49,51,56,.75); - border-radius: 64px; - display: flex; - gap: 8px; - padding: 16px -} - -.descriptionContainer__419e3 { - background-color: var(--background-base-lower); - position: relative -} - -.descriptionHeader__419e3 { - border-bottom: 1px solid var(--interactive-background-hover); - padding: 16px -} - -.descriptionHeaderText__419e3 { - line-height: 22px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: 100% -} - -.descriptionSubtitle__419e3 { - width: 360px -} - -.descriptionFooter__419e3 { - align-items: center; - display: flex; - flex-wrap: wrap; - gap: 16px; - padding: 16px -} - -.authorAvatar__419e3 { - border-radius: 50%; - height: 40px; - width: 40px -} - -.descriptionFooterContainer__419e3 { - flex-grow: 1 -} - -.descriptionFooterChannelName__419e3 { - align-items: center; - display: flex; - gap: 5px -} - -.descriptionFooterAuthorContainer__419e3 { - cursor: default; - display: flex -} - -.authorName__419e3 { - margin-inline-start:2px} - -.mediaChannelIcon__419e3 { - color: var(--text-muted); - height: 20px; - margin-inline-end:2px;width: 20px -} - -.channelName__419e3 { - cursor: pointer -} - -.channelName__419e3:hover { - color: var(--text-strong); - text-decoration: underline -} - -.spoiler__419e3 { - filter: blur(var(--custom-media-post-embed-spoiler-blur-radius)); - pointer-events: none -} - -.enabled_f4f0eb { - cursor: pointer -} - -.attachmentImage_f4f0eb { - -o-object-fit: cover; - object-fit: cover; - -o-object-position: center; - object-position: center -} - -.listItemButton_f4f0eb { - cursor: pointer -} - -.radioBackground_f4f0eb { - color: var(--text-default) -} - -.radioForeground_f4f0eb { - color: var(--control-brand-foreground) -} - -.checkbox_f4f0eb { - align-items: center; - background-color: transparent; - border: 2px solid var(--text-default); - border-radius: 3px; - box-sizing: border-box; - display: flex; - justify-content: center -} - -.checkboxSelected_f4f0eb { - background-color: var(--brand-500); - border: none -} - -.answersContainer__10758 { - display: grid; - gap: 16px; - grid-template-columns: repeat(2,1fr) -} - -.answer__10758 { - align-items: center; - aspect-ratio: 1/1; - background-color: var(--custom-poll-style-image-background); - border-radius: var(--radius-xs); - display: flex; - flex-direction: column; - justify-content: center; - position: relative -} - -.currentlyVoting__10758 { - transition: background-color var(--custom-button-transition-duration) ease -} - -.currentlyVoting__10758 .attachment__10758:after { - background-color: var(--white); - content: ""; - height: 100%; - inset-inline-start: 0; - opacity: 0; - position: absolute; - top: 0; - transition: opacity var(--custom-button-transition-duration) ease; - width: 100% -} - -.currentlyVoting__10758:hover { - box-shadow: var(--shadow-low) -} - -.currentlyVoting__10758:hover .attachment__10758:after { - opacity: .05 -} - -.theme-light .currentlyVoting__10758:hover .attachment__10758:after { - opacity: .1 -} - -.attachment__10758 { - border-radius: var(--radius-xs); - height: 100%; - position: absolute; - width: 100% -} - -.attachmentWithResults__10758 { - border-radius: var(--radius-xs) var(--radius-xs) var(--radius-sm) var(--radius-xs) -} - -.emoji__10758 { - height: 30%; - position: absolute; - width: 30% -} - -.mediaMissing__10758 { - background-color: var(--background-base-low) -} - -.selectedIcon__10758 { - position: relative -} - -.pollAnswerIcon__10758 { - height: 28px; - inset-inline-end: -9px; - position: absolute; - top: -9px; - width: 28px -} - -.votesData__10758 { - align-items: center; - align-self: flex-end; - background-color: var(--custom-poll-style-vote-percentage); - border-radius: var(--radius-xs) 0; - bottom: 0; - color: var(--custom-poll-style-label); - display: flex; - flex-direction: row; - gap: 4px; - inset-inline-end: 0; - padding: 4px 6px; - position: absolute -} - -.answersContainer__4c520 { - display: grid; - gap: 8px; - grid-auto-rows: 1fr; - grid-template-columns: 1fr; - margin: 8px 0 16px -} - -.answer__4c520 { - display: flex -} - -.answerInner__4c520 { - align-items: center; - background-color: var(--background-mod-muted); - border-radius: var(--radius-sm); - box-sizing: border-box; - color: var(--text-default); - display: flex; - gap: 8px; - min-height: 50px; - outline: 1px solid var(--custom-poll-style-border); - overflow: hidden; - padding: 8px 16px; - position: relative; - transition: background-color var(--custom-button-transition-duration) ease,outline-color var(--custom-button-transition-duration) ease; - width: 100%; - word-break: break-word; - z-index: 1 -} - -.answer__4c520:hover .answerInner__4c520.currentlyVoting__4c520 { - outline: 1px solid var(--border-strong) -} - -.answer__4c520 .answerInner__4c520.currentlyVoting__4c520.selected__4c520 { - outline: 1px solid var(--control-brand-foreground) -} - -.mediaContainer__4c520 { - align-items: center; - display: flex; - justify-content: center -} - -.emoji__4c520 { - margin-inline-end:2px} - -.attachment__4c520,.emoji__4c520 { - height: 24px; - width: 24px -} - -.label__4c520 { - margin-inline-end:auto} - -.avatarForSelected__4c520 { - height: 20px; - margin-inline-start:8px;width: 20px -} - -.votePercentageBar__4c520 { - background-color: var(--custom-poll-style-vote-percentage); - bottom: 0; - inset-inline-start: 0; - position: absolute; - top: 0; - z-index: -1 -} - -.votesData__4c520 { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px; - white-space: nowrap -} - -.answerSelectionIcon__4c520 { - flex-shrink: 0 -} - -.container__0be77 { - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-sizing: border-box; - display: flex; - flex-direction: column; - max-width: 472px; - min-width: 302px; - overflow: hidden; - padding: 16px; - position: relative; - width: 100% -} - -.header__0be77 { - display: grid; - gap: 0 8px; - grid-template-areas: "question infoIcon" "prompt infoIcon"; - grid-template-columns: 1fr 24px -} - -.question__0be77 { - grid-area: question; - word-break: break-word -} - -.prompt__0be77 { - grid-area: prompt; - margin: 4px 0 0 -} - -.detailsText__0be77 { - display: flex; - white-space: nowrap -} - -.detailsText__0be77>:not(:first-child):before { - content: "\2219"; - margin: 0 .5rem -} - -.bottomBar__0be77 { - align-items: center; - display: flex; - flex-direction: row; - flex-wrap: wrap; - gap: 16px; - min-height: var(--custom-button-button-md-height) -} - -.secondaryAction__0be77 { - margin-inline-end:auto} - -.tertiaryAction__0be77 { - cursor: pointer; - padding-inline:0} - -.tertiaryAction__0be77:hover { - text-decoration: underline -} - -.theme-light .container__0be77 { - border: 1px solid var(--border-subtle) -} - -.container__5301b { - box-sizing: border-box; - margin-bottom: 16px; - max-width: 660px; - min-width: 280px; - width: 100% -} - -.wide__5301b.container__5301b { - display: flex; - flex-shrink: 0; - height: 250px -} - -.tall__5301b.container__5301b { - display: flex; - flex-direction: column-reverse -} - -.contentContainer__5301b { - background: var(--background-base-lowest); - border-radius: 8px 0 0 8px; - display: flex; - flex: 1; - flex-direction: column; - padding: 38px 16px 16px -} - -.tall__5301b .contentContainer__5301b { - border-radius: 0 0 8px 8px; - padding: 16px -} - -.buttonContainer__5301b { - align-self: flex-start; - margin-top: auto -} - -.tall__5301b .buttonContainer__5301b { - margin-top: 50px; - width: 100% -} - -.imgContainer__5301b { - align-items: center; - background: var(--background-base-lower); - border-radius: 0 8px 8px 0; - display: flex; - flex: 1; - justify-content: center; - padding: 16px -} - -.missingQuestImage__5301b { - height: 180px; - -o-object-fit: contain; - object-fit: contain; - width: 100% -} - -.theme-light .contentContainer__5301b { - background: var(--background-base-lower) -} - -.theme-light .imgContainer__5301b { - background: var(--background-mod-normal) -} - -.theme-dark .contentContainer__5301b { - background: var(--background-base-lowest) -} - -.theme-dark .imgContainer__5301b { - background: var(--background-base-lower) -} - -.outerContainer_fbc6f7 { - align-items: center; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - gap: 16px; - padding: 16px -} - -.outerContainerSm_fbc6f7 { - flex-wrap: wrap; - gap: 12px; - max-height: none -} - -.outerContainerXs_fbc6f7 { - gap: 6px; - max-height: none; - position: relative -} - -.questRewardEmbed_fbc6f7 { - height: 80px; - width: 80px -} - -.questRewardEmbedSm_fbc6f7 { - height: 70px; - width: 70px -} - -.questRewardEmbedXs_fbc6f7 { - bottom: 0; - height: 50px; - position: absolute; - width: 50px -} - -.taskDetails_fbc6f7 { - flex: 1 1 180px; - min-width: 180px -} - -.taskInstructions_fbc6f7 { - margin-bottom: 4px -} - -.questEnrollmentBlockedButton_fbc6f7 { - flex: 1 0 auto -} - -.outerContainerXs_fbc6f7 .questEnrollmentBlockedButton_fbc6f7 { - width: 100% -} - -.learnMoreLink_fbc6f7 { - cursor: pointer -} - -.ctaTooltipText_fbc6f7 { - text-align: center -} - -.root_fbc6f7 { - background-color: var(--background-surface-high) -} - -.rewardTileWrapper_fbc6f7 { - position: relative -} - -.rewardTileExpired_fbc6f7 { - align-items: center; - background: rgba(0,0,0,.5); - bottom: 0; - display: flex; - inset-inline: 0; - justify-content: center; - position: absolute; - top: 0 -} - -.rewardTileExpirationTooltip_fbc6f7 { - text-align: center -} - -.removeStackWidth_fbc6f7 { - width: auto -} - -.heroAsset_c93f8c { - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - height: 100%; - transition: opacity .7s; - width: auto -} - -.heroAssetCont_c93f8c { - align-items: center; - background-color: var(--background-base-low); - display: flex; - justify-content: center; - overflow: hidden -} - -.heroAssetCont_c93f8c,.heroAssetVideo_c93f8c { - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.heroAssetVideo_c93f8c { - height: 100%; - -o-object-fit: cover; - object-fit: cover; - transition: opacity .4s -} - -.partnerBranding__83420 { - align-items: center; - display: flex; - flex: 0 1 auto -} - -.partnerBrandingGameTile__83420 { - display: block; - flex: 0 0 auto; - margin-inline-end:8px} - -.partnerBrandingLogotype__83420 { - height: 24px; - max-width: 92px; - -o-object-fit: contain; - object-fit: contain -} - -.partnerBrandingLogotypeWithCosponsor__83420 { - flex: 0 0 auto; - max-width: 44px -} - -.cosponsorBrandSeparatorWrapper__83420 { - align-items: center; - display: flex; - flex: 0 0 auto -} - -.cosponsorBrandSeparator__83420 { - height: 12px; - width: 12px -} - -.promotedTag__1d868 { - isolation: isolate; - padding: 3px 4px 2px; - position: relative; - text-transform: uppercase -} - -.promotedTag__1d868,.promotedTag__1d868 span { - display: block -} - -.promotedTagBackground__1d868 { - background-color: var(--black); - border-radius: 3px; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 0 -} - -.outerContainer__74dc6 { - display: flex; - flex-direction: column; - max-height: 185px; - overflow: hidden; - position: relative -} - -.outerContainerGiftInventory__74dc6 { - min-height: 185px -} - -.outerContainerEmbed__74dc6 { - min-height: 138px -} - -.questSplashContainer__74dc6 { - height: 185px -} - -.questSplashImage__74dc6 { - max-width: 660px; - -o-object-fit: cover; - object-fit: cover; - position: absolute; - width: 100% -} - -.header__74dc6 { - box-sizing: border-box; - flex: 1 1 auto; - height: 100%; - position: relative; - width: 100%; - z-index: 1 -} - -.outerContainerEmbed__74dc6.outerContainerXs__74dc6 .header__74dc6 { - padding-bottom: 40px -} - -.headerContent__74dc6 { - bottom: 0; - display: flex; - flex-direction: column; - justify-content: space-between; - position: absolute; - width: 100% -} - -.headerContentEmbed__74dc6 { - position: static -} - -.headerExpandedContent__74dc6 { - align-items: flex-start; - box-sizing: border-box; - display: flex; - flex-direction: column; - justify-content: space-between; - padding: 20px 16px -} - -.headerExpandedWrapper__74dc6 { - width: 100% -} - -.headerCollapsedContent__74dc6 { - height: 100px; - width: 100% -} - -.headerCollapsedClickableContainer__74dc6 { - align-items: center; - cursor: pointer; - display: flex; - flex-direction: row; - height: 100%; - padding: 8px; - width: 100% -} - -.headerCollapsedContentRewardWrapper__74dc6 { - align-items: center; - background-color: var(--background-mod-subtle); - border-radius: 8px; - display: flex; - flex-direction: column; - gap: 4px; - margin-inline-end:20px;padding: 4px 4px 6px -} - -.headerCollapsedRewardTile__74dc6 { - border-radius: 6px; - height: 60px; - width: 60px -} - -.headerCollapsedContentCopyWrapper__74dc6 { - align-items: flex-start; - display: flex; - flex-direction: column; - gap: 4px -} - -.headerCollapsedContentCopyLogos__74dc6,.iconLogotypeContainer__74dc6 { - align-items: center; - display: flex; - flex-direction: row -} - -.partnerBranding__74dc6 { - margin-inline-end:16px} - -.partnerBrandingLogotypes__74dc6 { - max-width: 92px -} - -.outerContainerXs__74dc6 .partnerBrandingLogotypes__74dc6 { - max-width: 60px -} - -.questInfo__74dc6 { - margin-top: 4px; - overflow: hidden -} - -.heading__74dc6 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.outerContainer__74dc6:after { - background: linear-gradient(180deg,transparent,25%,rgba(0,0,0,.9)); - content: ""; - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.iconsContainer__74dc6 { - flex-direction: row; - gap: 8px; - inset-inline-end: 16px; - position: absolute -} - -.iconWrapper__74dc6,.iconsContainer__74dc6 { - align-items: center; - display: flex -} - -.iconWrapper__74dc6 { - align-self: flex-start; - background-color: rgba(0,0,0,.4); - border-radius: 16px; - cursor: pointer; - height: 32px; - justify-content: center; - line-height: 32px; - opacity: .9; - transition: background-color .125s,opacity .125s; - width: 32px -} - -.iconWrapper__74dc6:focus,.iconWrapper__74dc6:hover { - background-color: rgba(0,0,0,.5) -} - -.iconWrapper__74dc6:active { - opacity: .7 -} - -.submenuIcon__74dc6 { - color: var(--white); - height: 24px; - inset-inline-start: 4px; - position: absolute; - top: 4px; - width: 24px -} - -.shareButton__74dc6 { - margin-top: 8px -} - -.questsCard__12e16 { - border-radius: 8px; - box-shadow: var(--shadow-low); - max-width: 660px; - min-width: 280px; - overflow: hidden; - width: 100% -} - -.questsCard__12e16+.questsCard__12e16 { - margin-top: 16px -} - -.spinner__255ba { - margin-top: 40px -} - -.popout_d5c2c4 { - background-color: var(--background-base-lower); - border-radius: 8px; - box-shadow: var(--legacy-elevation-high),var(--legacy-elevation-border); - box-sizing: border-box; - line-height: 20px; - overflow: hidden; - padding: 24px 16px; - text-align: center; - width: 240px -} - -.staffBadge_d5c2c4 { - background-image: url(/assets/a5041f91a8a7f1d5.svg); - background-position: 50%; - background-repeat: no-repeat; - background-size: contain; - height: 16px; - width: 16px -} - -.nameWrapper_d5c2c4 { - margin-top: 16px -} - -.header_d5c2c4 { - margin-inline-start:8px} - -.description_d5c2c4 { - margin-top: 8px -} - -.avatar_d5c2c4 { - border-radius: 16px; - height: 100px; - width: 100px -} - -.avatar__8c0e2 { - margin-inline-end:var(--space-4)} - -.username__8c0e2 a { - color: unset -} - -.timestampSeparator__8c0e2 { - margin: 0 var(--space-4) -} - -.timestamp__8c0e2 { - margin: 0 -} - -.container__0ed20 { - display: flex; - width: 100% -} - -.content__0ed20 { - display: flex; - flex-direction: column; - flex-grow: 1; - max-width: 100% -} - -.headerContainer__0ed20 { - align-items: center; - align-self: flex-start; - display: flex; - gap: var(--space-4); - margin-bottom: var(--space-4); - margin-top: 2px -} - -.message__0ed20 { - margin-inline-start:calc(var(--space-16)*-1)} - -.nestedAccessories__0ed20 { - margin-inline-start: 56px -} - -.headerIcon__0ed20 { - flex-shrink: 0 -} - -.headerText__0ed20 { - font-style: italic -} - -.footerContainer__0ed20 { - align-items: center; - align-self: flex-start; - border-radius: 4px; - color: var(--text-muted); - cursor: pointer; - display: flex; - gap: var(--space-4); - margin-top: var(--space-4); - max-width: 100%; - padding-bottom: 1px; - padding-top: 1px -} - -.footerContainer__0ed20:hover { - background-color: var(--interactive-background-hover) -} - -.footerContainer__0ed20:hover .footerText__0ed20 { - color: var(--interactive-text-hover) -} - -.footerText__0ed20 { - overflow: hidden; - text-overflow: ellipsis; - white-space: pre -} - -.originIcon__0ed20 { - border-radius: 4px; - height: 16px; - width: 16px -} - -.safetyPolicyNoticeContainer_e80b85 { - background: var(--background-base-lower); - border-radius: var(--radius-sm); - display: flex; - flex-direction: column -} - -.noticeContent_e80b85 { - align-items: flex-start; - display: flex; - flex-direction: column; - padding: var(--space-12) -} - -.headerRow_e80b85 { - align-items: center; - display: flex; - flex-direction: row -} - -.warningIcon_e80b85 { - color: var(--icon-feedback-critical); - height: 14px; - margin-inline-end:var(--space-4);width: 14px -} - -.incidentTiming_e80b85 { - color: var(--text-muted); - margin-top: var(--space-4) -} - -.noticeBody_e80b85 { - color: var(--text-muted); - margin-top: var(--space-8) -} - -.footerContainer_e80b85 { - align-items: flex-start; - background-color: var(--control-critical-primary-background-default); - border-radius: 0 0 var(--radius-sm) var(--radius-sm); - display: flex; - flex-direction: column; - justify-content: center; - padding: var(--space-8) var(--space-12) -} - -.footerContainer_e80b85:hover { - background-color: var(--control-critical-primary-background-hover) -} - -.safetyPolicyNoticeContainer__86361 { - background: var(--card-background-default); - border-radius: var(--radius-sm); - display: flex; - flex-direction: column -} - -.noticeContent__86361 { - align-items: flex-start; - display: flex; - flex-direction: column; - padding: var(--space-12) -} - -.headerRow__86361 { - align-items: center; - display: flex; - flex-direction: row -} - -.icon__86361 { - height: 14px; - margin-inline-end:var(--space-4);width: 14px -} - -.incidentTiming__86361 { - color: var(--text-muted); - margin-top: var(--space-4) -} - -.noticeBody__86361 { - color: var(--text-muted); - margin-top: var(--space-8) -} - -.detailsButton__86361 { - margin-inline:var(--space-8);padding: var(--space-8) 0 -} - -.defaultFooterContainer__86361 { - align-items: center; - background-color: var(--background-mod-normal); - border-radius: 0 0 var(--radius-sm) var(--radius-sm); - color: var(--white); - display: flex; - flex-direction: row; - justify-content: start; - width: 100% -} - -.defaultFooterContainer__86361:hover { - background-color: var(--control-secondary-background-hover) -} - -.dangerFooterContainer__86361 { - align-items: flex-start; - background-color: var(--control-critical-primary-background-default); - border-radius: 0 0 var(--radius-sm) var(--radius-sm); - color: var(--white); - display: flex; - flex-direction: row; - justify-content: center; - width: 100% -} - -.dangerfooterContainer__86361:hover { - background-color: var(--control-critical-primary-background-hover) -} - -.card_abfd90,.cardContainer_abfd90 { - overflow: hidden -} - -.card_abfd90 { - background: var(--background-base-low); - border: 2px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-sizing: border-box; - cursor: pointer; - display: flex; - flex-direction: column; - justify-content: flex-end; - position: relative; - transform: translateY(0); - width: 100% -} - -.cardMedium_abfd90 { - height: 270px -} - -.cardSmall_abfd90 { - height: 246px -} - -.cardImageContainer_abfd90 { - bottom: 0; - inset-inline-end: 0; - inset-inline-start: 0; - position: absolute; - top: 0; - z-index: 0 -} - -.cardEmbedded_abfd90 { - border: none; - border-radius: unset; - height: 254px -} - -.cardDark_abfd90 { - background-color: var(--background-surface-high) -} - -.cardHighlighted_abfd90 { - box-shadow: var(--shadow-border),var(--shadow-high) -} - -.cardDarkHighlighted_abfd90 { - box-shadow: 0 0 15px 1px var(--primary-700) -} - -.cardNotLoaded_abfd90 { - cursor: default; - pointer-events: none -} - -.cardAnimation_abfd90 { - transform: scale(100%) -} - -.cardAnimation_abfd90:focus-within,.cardAnimation_abfd90:hover { - transform: scale(105%) -} - -.full-motion .cardAnimation_abfd90 { - transition: transform .5s,box-shadow .5s -} - -@keyframes fadeIn_abfd90 { - 0% { - opacity: 0 - } - - to { - opacity: 1 - } -} - -.cardBackgroundImage_abfd90 { - background-color: var(--background-base-low); - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - transform: scale(110%) -} - -.full-motion .cardBackgroundImage_abfd90 { - transition: transform .5s -} - -.cardImage_abfd90 { - height: 100%; - -o-object-fit: cover; - object-fit: cover -} - -.full-motion .cardImage_abfd90 { - transition: transform .5s -} - -.ticketIconContainer_abfd90 { - background: var(--brand-400); - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.ticketIcon_abfd90 { - height: 50%; - inset-inline-start: 50%; - -o-object-fit: contain; - object-fit: contain; - -o-object-position: center center; - object-position: center center; - position: absolute; - top: 50%; - transform: translate(-50%,-50%); - width: 50% -} - -.details_abfd90 { - display: flex; - flex-direction: column; - gap: 4px; - padding: 16px; - z-index: 1 -} - -.titleContainer_abfd90 { - transform: translateY(0) -} - -.full-motion .titleContainer_abfd90 { - transition: transform .5s -} - -.descriptionContainer_abfd90 { - display: flex; - flex-direction: row; - gap: 4px; - opacity: 1 -} - -.full-motion .descriptionContainer_abfd90 { - transition: opacity .25s,transform .25s -} - -.buttonHover_abfd90 { - bottom: 16px; - inset-inline: 16px; - position: absolute; - transform: translateY(150%); - z-index: 1 -} - -.full-motion .buttonHover_abfd90 { - transition: all .5s -} - -.card_abfd90:focus-within .buttonHover_abfd90,.card_abfd90:hover .buttonHover_abfd90 { - transform: translateY(0) -} - -.card_abfd90:focus-within .descriptionContainer_abfd90,.card_abfd90:hover .descriptionContainer_abfd90 { - opacity: 0; - transform: translateY(50%) -} - -.card_abfd90:focus-within .titleContainer_abfd90,.card_abfd90:hover .titleContainer_abfd90 { - transform: translateY(-100%) -} - -.card_abfd90:focus-within .bottomGradient_abfd90,.card_abfd90:hover .bottomGradient_abfd90 { - transform: translateY(-15%) -} - -.card_abfd90:focus-within .cardBackgroundImage_abfd90,.card_abfd90:hover .cardBackgroundImage_abfd90 { - transform: scale(100%) -} - -.card_abfd90:focus-within .cardImage_abfd90,.card_abfd90:hover .cardImage_abfd90 { - transform: scale(105%) -} - -.bottomGradient_abfd90 { - bottom: -25%; - height: 100%; - inset-inline-end: 0; - position: absolute; - transform: translateY(0); - width: 100% -} - -.full-motion .bottomGradient_abfd90 { - transition: transform .5s -} - -.iconAsset_abfd90 { - height: 20px; - -o-object-fit: contain; - object-fit: contain; - -o-object-position: center; - object-position: center; - width: 20px -} - -.badge_abfd90 { - background-color: var(--white); - color: var(--black); - inset-inline-start: 16px; - position: absolute; - top: 16px; - z-index: 1 -} - -.cardButtonsContainer_abfd90 { - color: var(--primary-100); - display: flex; - flex-direction: row; - gap: 12px; - inset-inline-end: 8px; - position: absolute; - top: 8px; - z-index: 1 -} - -.wishlistButton_abfd90 { - -webkit-backdrop-filter: none; - backdrop-filter: none; - border-width: 0; - padding: 0 -} - -.forwardButton_abfd90 { - visibility: hidden -} - -.cardButtonContainer_abfd90 { - align-items: center; - background-color: color-mix(in srgb,var(--black) 40%,transparent); - border-radius: var(--radius-sm); - cursor: pointer; - display: flex; - height: 40px; - justify-content: center; - transition: background-color .2s ease-in-out; - width: 40px -} - -.cardButtonContainer_abfd90:hover { - background: color-mix(in srgb,var(--black) 60%,transparent) -} - -.cardButtonContainerHovered_abfd90 { - visibility: visible -} - -@keyframes pulse_abfd90 { - 0% { - background: var(--background-base-lowest) - } - - 30% { - background: var(--background-base-low) - } - - 70% { - background: var(--background-base-low) - } - - to { - background: var(--background-base-lowest) - } -} - -.placeholderCard_abfd90 { - cursor: default; - pointer-events: none -} - -.placeholderText_abfd90 { - background-color: var(--icon-muted); - border-radius: var(--radius-xs); - height: var(--space-24); - opacity: .1 -} - -.full-motion .placeholderCard_abfd90 { - animation: pulse_abfd90 2s ease-in-out infinite alternate; - animation-fill-mode: backwards -} - -.container_e95ba0 { - display: flex; - flex-direction: column; - height: 380px; - max-width: 512px; - overflow: hidden; - width: 100% -} - -.card_e95ba0,.container_e95ba0 { - border-radius: var(--radius-lg) var(--radius-lg) 0 0 -} - -.spinner_e95ba0 { - align-self: center; - flex: 1; - justify-self: center -} - -.details_e95ba0 { - background: var(--background-base-low); - border-bottom: 1px solid var(--border-subtle); - border-inline-end:1px solid var(--border-subtle);border-inline-start: 1px solid var(--border-subtle); - border-radius: 0 0 var(--radius-lg) var(--radius-lg); - gap: 16px; - padding: 16px -} - -.details_e95ba0,.detailsTitleDescription_e95ba0 { - display: flex; - flex-direction: column -} - -.detailsTitleDescription_e95ba0 { - gap: 8px -} - -.detailsDescription_e95ba0 { - display: flex; - flex-direction: row; - gap: 4px -} - -.iconAsset_e95ba0 { - height: 20px; - -o-object-fit: contain; - object-fit: contain; - -o-object-position: center; - object-position: center; - width: 20px -} - -.popoutContent_dfa983 { - display: flex; - flex-direction: column; - padding: var(--custom-message-helpers-popout-padding-width) -} - -.stickersList_dfa983 { - display: flex; - justify-content: space-between; - margin: 8px 0 -} - -.stickerWrapper_dfa983 { - cursor: pointer -} - -.packActions_dfa983 { - display: flex; - flex-direction: column; - gap: 8px -} - -.premiumButtonNitroWheel_dfa983 { - margin-inline-end:4px} - -.premiumButtonInner_dfa983 { - align-items: center; - display: flex -} - -.theme-dark .countdownIcon_dfa983 { - color: var(--yellow-260) -} - -.theme-light .popoutContent_dfa983,.theme-light .popoutLoader_dfa983 { - background-color: var(--background-surface-high); - box-shadow: var(--shadow-border),var(--shadow-high) -} - -.theme-light .countdownIcon_dfa983 { - color: #ff8a00 -} - -.nonInteractingSticker_dfa983 { - opacity: .5 -} - -.clickableSticker_abd7a8 { - cursor: pointer -} - -.stickerContainer_abd7a8 { - align-self: start; - justify-self: start -} - -.stickerName_abd7a8 { - display: inline-flex; - grid-gap: 4px -} - -.stickerIcon_abd7a8 { - height: 16px; - margin-inline-end:2px;width: 16px -} - -.background-opacity-low .clickableSticker_abd7a8 { - opacity: .6 -} - -.background-opacity-medium .clickableSticker_abd7a8 { - opacity: .7 -} - -.background-opacity-high .clickableSticker_abd7a8 { - opacity: .8 -} - -@use postcss-pxtorem;.container__235ca { - align-items: flex-start; - color: var(--channels-default); - display: flex; - flex: 0 0 auto; - padding-bottom: .125rem; - padding-top: .125rem; - position: relative -} - -.compact__235ca { - margin-inline-start:-4ch} - -.content__235ca { - font-size: 1rem; - font-weight: var(--font-weight-normal); - line-height: 1.375rem; - z-index: 1 -} - -.content__235ca a { - color: var(--interactive-text-active); - cursor: pointer; - font-weight: var(--font-weight-medium) -} - -.content__235ca .actionAnchor__235ca { - color: var(--text-link) -} - -.low-saturation .content__235ca a { - color: var(--interactive-text-active) -} - -.decorate-links .content__235ca a { - text-decoration: none -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .content__235ca { - font-weight:var(--font-weight-medium) - } - - .theme-light .content__235ca a { - font-weight: var(--font-weight-semibold) - } -} - -.iconContainer__235ca { - align-items: center; - display: flex; - inset-inline-end: 100%; - justify-content: center; - padding-top: .25rem; - position: absolute; - width: 2.5rem -} - -.compact__235ca .iconContainer__235ca { - margin-inline-end:1rem} - -.font-size-24 .cozy__235ca .iconContainer__235ca { - width: 3rem -} - -.font-size-20 .cozy__235ca .iconContainer__235ca { - width: 3.5rem -} - -.font-size-18 .cozy__235ca .iconContainer__235ca { - width: 4rem -} - -.font-size-12 .cozy__235ca .iconContainer__235ca,.font-size-14 .cozy__235ca .iconContainer__235ca,.font-size-15 .cozy__235ca .iconContainer__235ca,.font-size-16 .cozy__235ca .iconContainer__235ca { - width: 4.5rem -} - -.compact__235ca .iconContainer__235ca { - inset-inline-end: auto; - position: relative; - width: auto -} - -.icon__235ca { - background-repeat: no-repeat; - background-size: 1rem 1rem -} - -.iconSize__235ca { - height: 1rem; - width: 1rem -} - -.action__235ca,.actionAnchor__235ca { - margin-inline-start:3px} - -.actionAnchor__235ca { - cursor: pointer -} - -.timestamp__235ca { - color: var(--text-muted); - display: inline-block; - font-size: .75rem; - font-weight: var(--font-weight-normal); - letter-spacing: 0; - line-height: 1.375rem; - margin-inline-start:6px;text-transform: none -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .timestamp__235ca { - font-weight:var(--font-weight-medium) - } -} - -.background-opacity-low .content__235ca { - color: var(--white); - text-shadow: 0 0 1px var(--primary-700),1px 1px 0 var(--primary-700) -} - -.background-opacity-low .content__235ca a { - color: var(--interactive-text-active) -} - -.background-opacity-high .content__235ca,.background-opacity-medium .content__235ca { - color: var(--primary-100); - text-shadow: 0 0 1px var(--primary-600),1px 1px 0 var(--primary-600) -} - -.background-opacity-high .content__235ca a,.background-opacity-medium .content__235ca a { - color: var(--interactive-text-active) -} - -.background-opacity-low .timestamp__235ca,.background-opacity-medium .timestamp__235ca { - color: var(--primary-200) -} - -.background-opacity-high .timestamp__235ca { - color: var(--primary-300) -} - -.iconContainer__45eb7 { - color: var(--icon-muted); - padding-top: .125rem -} - -.content__45eb7 { - align-items: baseline; - display: inline-flex; - flex-direction: row; - flex-wrap: wrap; - gap: .25rem -} - -.clickableEmoji__45eb7 { - cursor: pointer; - display: inline-block -} - -.clickableEmoji__45eb7:hover { - text-decoration: underline -} - -.ctaReactionButton__45eb7 { - width: -moz-fit-content; - width: fit-content -} - -.ctaText__45eb7 { - align-items: center; - display: inline-flex; - gap: .3em; - height: 100% -} - -.ctaEmoji__45eb7 { - height: var(--custom-emoji-size-emoji); - vertical-align: baseline; - width: var(--custom-emoji-size-emoji) -} - -@use postcss-pxtorem;.spine__9271d { - align-self: auto!important; - border-bottom: 2px solid var(--spine-default); - border-end-start-radius: 8px; - border-inline-start:2px solid var(--spine-default);border-color: var(--border-subtle); - bottom: 29px; - inset-inline-start: -2.5rem; - position: absolute; - top: 0; - width: 2.25rem -} - -.spine__9271d.systemMessageSpine__9271d { - bottom: 32px; - inset-inline-start: -2rem; - width: 1.75rem -} - -.spine__9271d.cozy__9271d { - display: none -} - -.container__9271d { - align-self: start; - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: 4px; - cursor: pointer; - justify-self: start; - margin-top: 8px; - max-width: min(100%,480px); - min-width: 0; - padding: 8px -} - -.systemMessageContainer__9271d { - margin-top: 0 -} - -.topLine__9271d { - display: flex -} - -.name__9271d { - color: var(--text-strong); - margin-inline-end:8px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.cta__9271d,.name__9271d { - font-size: .875rem; - font-weight: var(--font-weight-semibold); - line-height: 1.125rem -} - -.cta__9271d { - color: var(--text-link); - flex-shrink: 0 -} - -.container__9271d:hover .cta__9271d { - text-decoration: underline -} - -.bottomLine__9271d { - align-items: center; - display: flex; - margin-top: 2px -} - -.timestamp__9271d { - color: var(--text-muted); - flex-shrink: 0; - font-size: .875rem; - line-height: 1.125rem; - margin-inline-start:8px} - -.clock__9271d { - margin-inline-start:6px} - -.enable-forced-colors .cta__9271d { - background-color: ButtonFace; - color: ButtonText; - text-decoration: underline -} - -.enable-forced-colors .container__9271d { - border: 1px solid ButtonText -} - -.custom-theme-background .container__9271d { - background-color: var(--background-mod-subtle) -} - -:root { - --custom-app-message-embed-base-info-gap: 4px; - --custom-app-message-embed-base-info-top: calc(var(--custom-app-message-embed-base-info-gap) - 2px) -} - -.infoWrapper_c44293 { - display: flex; - flex: 1; - flex-direction: column; - gap: var(--custom-app-message-embed-base-info-gap); - margin-top: var(--custom-app-message-embed-base-info-top) -} - -.activityPresenceDetailsContainer_c44293 { - align-items: center; - display: flex; - gap: 12px; - min-width: 0; - overflow: hidden -} - -.activityPresenceDetailsItemContainer_c44293 { - align-items: center; - display: flex; - gap: 4px -} - -.activityPresenceDetailsItemContainer_c44293.truncatesText_c44293 { - min-width: 0 -} - -.activityPresenceDetailsItemIconContainer_c44293 { - align-items: center; - display: flex -} - -.activePlayingWrapper_c44293 { - display: flex; - gap: 4px -} - -.ephemeralMessage__124d2 { - color: var(--text-muted); - font-size: 12px; - font-weight: var(--font-weight-normal); - margin-top: 4px -} - -.userAppsBetaContent__124d2 { - display: inline-flex; - flex-direction: column; - line-height: 1.33 -} - -.userAppsBetaContent__124d2 p { - margin: 0 -} - -.icon__124d2 { - margin-inline-end:4px;vertical-align: text-bottom -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .ephemeralMessage__124d2 { - font-weight:var(--font-weight-medium) - } -} - -:root { - --custom-app-message-embed-base-info-gap: 4px; - --custom-app-message-embed-base-info-top: calc(var(--custom-app-message-embed-base-info-gap) - 2px) -} - -.statusCounts_a4b24e { - align-items: center; - display: flex; - flex-flow: row wrap; - max-width: 160px; - padding-inline-end:16px} - -.statusCounts_a4b24e.large_a4b24e { - -moz-column-gap: 8px; - column-gap: 8px; - max-width: 304px -} - -.status_a4b24e { - border-radius: 50%; - display: block; - flex: 0 0 auto; - height: 8px; - margin-inline-end:4px;width: 8px -} - -.statusWrapper_a4b24e { - align-items: center; - display: flex; - flex: 0 1 auto; - flex-flow: nowrap; - min-width: 0 -} - -.statusOnline_a4b24e { - background-color: var(--green-360) -} - -.count_a4b24e { - color: var(--interactive-text-default); - flex: 0 1 auto; - margin-inline-end:8px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.statusOffline_a4b24e { - background-color: var(--primary-400) -} - -.channel_a4b24e { - align-items: center; - display: flex; - max-width: var(--custom-embedded-application-invite-image-width-large); - padding-inline-end:4px} - -.channel_a4b24e.ended_a4b24e { - max-width: var(--custom-embedded-application-invite-image-width-small) -} - -.channelIcon_a4b24e { - color: var(--text-muted); - flex: 0 0 auto; - padding-inline-end:2px} - -.infoWrapper_a4b24e { - display: flex; - flex: 1; - flex-direction: column; - gap: var(--custom-app-message-embed-base-info-gap); - margin-top: var(--custom-app-message-embed-base-info-top) -} - -.root__373b5 { - display: flex; - flex-direction: column -} - -.rolesHeading__373b5 { - margin-bottom: 4px -} - -.rolesList__373b5 { - display: flex; - flex-wrap: wrap; - gap: 4px -} - -.rolePill__373b5 { - background: var(--background-base-lowest); - border-radius: var(--radius-sm); - color: var(--interactive-text-active); - height: 24px; - margin: 0; - padding: 4px -} - -.rolePill__373b5>:first-child { - margin-inline-end:4px} - -.rolePill__373b5>div:last-child { - margin-inline-end:2px} - -.rolePill__373b5>div:last-child span { - font-weight: var(--font-weight-semibold) -} - -.guildInviteContainer__083ae { - background: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: 16px; - overflow: hidden; - position: relative; - width: 300px -} - -.clickable__083ae { - cursor: pointer -} - -.cardHeightMeasure__083ae { - align-items: flex-start; - border-radius: 16px; - display: flex; - flex-direction: column; - width: 100% -} - -.cardHeightMeasure__083ae .banner__083ae { - height: 72px -} - -.mainContent__083ae { - z-index: 0 -} - -.footer__083ae { - box-sizing: border-box; - width: 100% -} - -.floatingFooter__083ae { - bottom: 0; - inset-inline: 0; - position: absolute; - z-index: 1 -} - -.gradient__083ae { - background: linear-gradient(180deg,transparent 0,var(--background-surface-high) 100%); - height: 48px; - width: 100% -} - -.footerContent__083ae { - background: var(--background-surface-high) -} - -.collapsedFooterContent__083ae { - padding-top: 8px -} - -.buttonContainer__083ae { - display: flex; - padding: 0 16px 16px -} - -.hideDetailsButtonContainer__083ae { - margin: 0 16px -} - -.inviteTitle__083ae { - overflow: hidden; - padding: 8px 12px; - text-overflow: ellipsis -} - -.rolesList__083ae { - margin-bottom: 16px; - padding: 0 16px -} - -.clipPill_d762d7 { - align-items: center; - background-color: var(--brand-500); - border-radius: var(--radius-xs); - display: flex; - gap: 4px; - inset-inline-start: 4px; - justify-content: center; - padding: 4px 8px; - position: absolute; - text-transform: uppercase; - top: 4px; - z-index: 1 -} - -:root { - --custom-app-message-embed-base-info-gap: 4px; - --custom-app-message-embed-base-info-top: calc(var(--custom-app-message-embed-base-info-gap) - 2px) -} - -.description__403f1 { - margin-top: var(--custom-app-message-embed-base-info-top) -} - -.description__403f1 strong { - font-weight: var(--font-weight-medium) -} - -.info__403f1 { - display: flex; - flex-direction: column; - gap: var(--custom-app-message-embed-base-info-gap); - margin-top: var(--custom-app-message-embed-base-info-top) -} - -.tagline__403f1,.timestampContainer__403f1 { - align-items: center; - display: flex; - gap: 4px -} - -.timestampContainer__403f1 { - min-width: 54px -} - -.tag__403f1:not(:first-of-type):before { - content: "∙"; - padding: 0 3px -} - -.partyStatusWrapper__403f1 { - display: flex; - gap: 4px -} - -.footer__403f1 { - align-items: center; - background-color: var(--opacity-black-20); - color: var(--app-message-embed-secondary-text); - display: flex; - gap: 4px; - justify-content: center; - padding: 12px; - @container (max-width: 270px) { - flex-direction: column - } -} - -.footerSupportedPlatformIconsContainer__403f1 { - gap: 4px -} - -.footerSupportedPlatformIconContainer__403f1,.footerSupportedPlatformIconsContainer__403f1 { - align-items: center; - display: flex; - justify-content: center -} - -.footerSupportedPlatformText__403f1 { - @container (max-width: 270px) { - text-align: center - } -} - -.inlineApplicationText__403f1 { - align-items: flex-end; - display: inline-flex; - flex-direction: row; - gap: 4px; - vertical-align: text-bottom -} - -.reactions_f8896c { - align-items: center; - display: flex; - flex: 1 0 auto; - flex-wrap: wrap; - margin-bottom: -8px; - padding-bottom: 6px; - padding-top: 1px -} - -.icon_f8896c { - height: 18px; - width: 18px -} - -.reactionBtn_f8896c { - background-color: var(--background-mod-subtle); - color: var(--interactive-text-default); - cursor: pointer; - height: 18px; - margin-inline:4px;margin-bottom: 2px; - opacity: 0 -} - -.reactionBtn_f8896c:hover .icon_f8896c { - color: var(--interactive-text-hover) -} - -.reactionBtn_f8896c.active_f8896c .icon_f8896c,.reactionBtn_f8896c:active .icon_f8896c { - color: var(--interactive-text-active) -} - -.reactionBtn_f8896c:hover { - background-color: var(--background-mod-normal) -} - -.reactionBtn_f8896c.active_f8896c,.reactionBtn_f8896c.visible_f8896c,.reactions_f8896c:hover .reactionBtn_f8896c { - opacity: 1 -} - -.reactionBtn_f8896c.forceShow_f8896c { - background-color: var(--background-base-lower); - border: 1px solid transparent; - border-radius: 8px; - margin-inline-start:0;margin-bottom: 4px; - opacity: 1; - padding: 5px 7px -} - -.reactionBtn_f8896c.forceShow_f8896c:hover { - background-color: var(--background-base-low); - border-color: var(--opacity-white-20) -} - -.hideEmoji_f8896c { - opacity: 0 -} - -.reaction_f8896c { - background: var(--background-base-lower); - background-color: var(--background-mod-subtle); - border: 1px solid transparent; - border-radius: 8px; - box-sizing: border-box; - cursor: pointer; - flex-shrink: 0; - margin-inline-end:4px;margin-bottom: 4px; - transition: none .1s ease; - transition-property: background-color,border-color; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.full-motion .reaction_f8896c.shakeReaction_f8896c { - animation: shake_f8896c .5s; - animation-iteration-count: 1 -} - -.reaction_f8896c.reactionReadOnly_f8896c { - cursor: not-allowed -} - -.reaction_f8896c .emoji { - height: 20px; - margin: 2px 0; - min-height: auto; - min-width: auto; - width: 20px -} - -.reaction_f8896c .emoji-text { - color: var(--text-default); - display: inline-block; - font-size: 20px; - font-variant-emoji: emoji; - vertical-align: -2px -} - -.reaction_f8896c:hover { - background-color: var(--background-mod-normal); - border-color: var(--border-muted) -} - -.reaction_f8896c:hover .reactionCount_f8896c { - color: var(--text-strong) -} - -.reaction_f8896c:active { - background-color: var(--background-mod-strong); - border-color: var(--border-strong) -} - -.reaction_f8896c:active .reactionCount_f8896c { - color: var(--text-strong) -} - -.reaction_f8896c.reactionMe_f8896c { - background-color: var(--opacity-blurple-24); - border-color: var(--blurple-50) -} - -.reaction_f8896c.reactionMe_f8896c .reactionCount_f8896c { - color: var(--text-brand) -} - -.low-contrast .reaction_f8896c.reactionMe_f8896c { - background-color: var(--brand-15a); - border-color: var(--brand-50a) -} - -.low-contrast .reaction_f8896c.reactionMe_f8896c .reactionCount_f8896c { - color: var(--brand-300) -} - -.reactionInner_f8896c { - align-items: center; - display: flex; - padding: 2px 6px; - position: relative -} - -.reactionCount_f8896c { - color: var(--text-subtle); - font-size: 14px; - font-weight: var(--font-weight-semibold); - margin-inline-start:6px;text-align: center -} - -.remainingReactions_f8896c { - align-self: stretch; - display: flex -} - -.theme-light .reaction_f8896c:hover { - border-color: var(--opacity-black-20) -} - -.theme-light .reaction_f8896c.reactionMe_f8896c { - background-color: var(--brand-160); - border-color: var(--brand-500) -} - -.theme-light .reaction_f8896c.reactionMe_f8896c .reactionCount_f8896c { - color: var(--brand-560) -} - -.background-opacity-high .reaction_f8896c,.background-opacity-low .reaction_f8896c,.background-opacity-medium .reaction_f8896c { - background-color: hsl(var(--primary-660-hsl)/.2) -} - -.background-opacity-high .reactionCount_f8896c,.background-opacity-low .reactionCount_f8896c,.background-opacity-medium .reactionCount_f8896c { - color: var(--white) -} - -.background-opacity-high .reaction_f8896c.reactionMe_f8896c,.background-opacity-low .reaction_f8896c.reactionMe_f8896c,.background-opacity-medium .reaction_f8896c.reactionMe_f8896c { - background-color: var(--brand-50a) -} - -.enable-forced-colors .reaction_f8896c.reactionMe_f8896c { - border-color: Highlight; - outline: 2px solid Highlight -} - -.premiumIcon_f8896c { - margin-inline-end:4px} - -.sparkles_f8896c { - height: 32px; - inset-inline-start: -8px; - top: -8px; - width: 32px -} - -.burstGlow_f8896c { - border-radius: 8px; - inset-inline: 0; - bottom: 0; - opacity: .35; - position: absolute; - top: 0; - z-index: -1 -} - -@keyframes shake_f8896c { - 0% { - transform: translate(1px,1px) rotate(0deg) - } - - 10% { - transform: translate(-1px,-1px) rotate(-1deg) - } - - 20% { - transform: translate(-2px) rotate(1deg) - } - - 30% { - transform: translate(2px,1px) rotate(0deg) - } - - 40% { - transform: translate(1px,-1px) rotate(1deg) - } - - 50% { - transform: translate(-1px,1px) rotate(-1deg) - } - - 60% { - transform: translate(-2px,1px) rotate(0deg) - } - - 70% { - transform: translate(2px,1px) rotate(-1deg) - } - - 80% { - transform: translate(-1px,-1px) rotate(1deg) - } - - 90% { - transform: translate(1px,1px) rotate(0deg) - } - - to { - transform: translate(1px,-1px) rotate(-1deg) - } -} - -.reactions__23977 { - align-items: center; - display: flex; - flex: 1 0 auto; - flex-wrap: wrap; - margin-bottom: -.5rem; - padding-bottom: .375rem; - padding-top: .125rem -} - -.icon__23977 { - height: 1.125rem; - width: 1.125rem -} - -.reactionBtn__23977 { - background-color: var(--background-mod-subtle); - color: var(--interactive-text-default); - cursor: pointer; - height: 1.125rem; - margin-inline:.25rem;margin-bottom: .125rem; - opacity: 0 -} - -.reactionBtn__23977:hover .icon__23977 { - color: var(--interactive-text-hover) -} - -.reactionBtn__23977.active__23977 .icon__23977,.reactionBtn__23977:active .icon__23977 { - color: var(--interactive-text-active) -} - -.reactionBtn__23977:hover { - background-color: var(--background-mod-normal); - border: 1px solid var(--border-muted) -} - -.reactionBtn__23977.active__23977,.reactionBtn__23977.visible__23977,.reactions__23977:hover .reactionBtn__23977 { - opacity: 1 -} - -.reactionBtn__23977.forceShow__23977 { - border: .0625rem solid transparent; - border-radius: .5rem; - margin-inline-start:0;margin-bottom: .25rem; - opacity: 1; - padding: .3125rem .4375rem -} - -.high-contrast-mode .reactionBtn__23977.forceShow__23977 { - border-color: var(--border-subtle) -} - -.high-contrast-mode .reactionBtn__23977.forceShow__23977:hover { - border-color: var(--border-strong) -} - -.hideEmoji__23977 { - opacity: 0 -} - -.reaction__23977,.reactionBtn__23977 { - background-color: var(--background-mod-subtle) -} - -.reaction__23977:hover,.reactionBtn__23977:hover { - background-color: var(--background-mod-normal); - border-color: var(--border-muted) -} - -.reaction__23977:active,.reactionBtn__23977:active { - background-color: var(--background-mod-strong); - border-color: var(--border-strong) -} - -.reaction__23977:active .reactionCount__23977,.reactionBtn__23977:active .reactionCount__23977 { - color: var(--text-strong) -} - -.reaction__23977 { - background: var(--background-base-lower); - background-color: var(--background-mod-subtle); - border: .0625rem solid transparent; - border-radius: .5rem; - box-sizing: border-box; - cursor: pointer; - flex-shrink: 0; - margin-inline-end:.25rem;margin-bottom: .25rem; - transition: none .1s ease; - transition-property: background-color,border-color; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.high-contrast-mode .reaction__23977 { - border-color: var(--border-subtle) -} - -.high-contrast-mode .reaction__23977:hover { - border-color: var(--border-strong) -} - -.full-motion .reaction__23977.shakeReaction__23977 { - animation: shake__23977 .5s; - animation-iteration-count: 1 -} - -.reaction__23977.reactionReadOnly__23977 { - cursor: not-allowed -} - -.reaction__23977 .emoji { - height: 1.25rem; - margin: .125rem 0; - min-height: auto; - min-width: auto; - width: 1.25rem -} - -.reaction__23977 .emoji-text { - color: var(--text-default); - display: inline-block; - font-size: 1.25rem; - font-variant-emoji: emoji; - vertical-align: -.125rem -} - -.reaction__23977:hover .reactionCount__23977 { - color: var(--text-strong) -} - -.reaction__23977.reactionMe__23977 { - background-color: var(--message-reacted-background-default); - border-color: var(--blurple-50) -} - -.reaction__23977.reactionMe__23977 .reactionCount__23977 { - color: var(--message-reacted-text-default) -} - -.low-contrast .reaction__23977.reactionMe__23977 { - background-color: var(--brand-15a); - border-color: var(--brand-50a) -} - -.low-contrast .reaction__23977.reactionMe__23977 .reactionCount__23977 { - color: var(--brand-300) -} - -.reactionInner__23977 { - align-items: center; - display: flex; - padding: .125rem .375rem; - position: relative -} - -.reactionCount__23977 { - color: var(--text-subtle); - font-weight: var(--font-weight-semibold); - margin-inline-start:.375rem;text-align: center -} - -.remainingReactions__23977 { - align-self: stretch; - display: flex -} - -.theme-light .reaction__23977.reactionMe__23977 { - background-color: var(--brand-160); - border-color: var(--brand-500) -} - -.theme-light .reaction__23977.reactionMe__23977 .reactionCount__23977 { - color: var(--brand-560) -} - -.background-opacity-high .reaction__23977,.background-opacity-low .reaction__23977,.background-opacity-medium .reaction__23977 { - background-color: hsl(var(--primary-660-hsl)/.2) -} - -.background-opacity-high .reactionCount__23977,.background-opacity-low .reactionCount__23977,.background-opacity-medium .reactionCount__23977 { - color: var(--white) -} - -.background-opacity-high .reaction__23977.reactionMe__23977,.background-opacity-low .reaction__23977.reactionMe__23977,.background-opacity-medium .reaction__23977.reactionMe__23977 { - background-color: var(--brand-50a) -} - -.enable-forced-colors .reaction__23977.reactionMe__23977 { - border-color: Highlight; - outline: .125rem solid Highlight -} - -.premiumIcon__23977 { - margin-inline-end:.25rem} - -.sparkles__23977 { - height: 2rem; - inset-inline-start: -.5rem; - top: -.5rem; - width: 2rem -} - -.burstGlow__23977 { - border-radius: .5rem; - inset-inline: 0; - bottom: 0; - opacity: .35; - position: absolute; - top: 0; - z-index: -1 -} - -@keyframes shake__23977 { - 0% { - transform: translate(.0625rem,.0625rem) rotate(0deg) - } - - 10% { - transform: translate(-.0625rem,-.0625rem) rotate(-1deg) - } - - 20% { - transform: translate(-.125rem) rotate(1deg) - } - - 30% { - transform: translate(.125rem,.0625rem) rotate(0deg) - } - - 40% { - transform: translate(.0625rem,-.0625rem) rotate(1deg) - } - - 50% { - transform: translate(-.0625rem,.0625rem) rotate(-1deg) - } - - 60% { - transform: translate(-.125rem,.0625rem) rotate(0deg) - } - - 70% { - transform: translate(.125rem,.0625rem) rotate(-1deg) - } - - 80% { - transform: translate(-.0625rem,-.0625rem) rotate(1deg) - } - - 90% { - transform: translate(.0625rem,.0625rem) rotate(0deg) - } - - to { - transform: translate(.0625rem,-.0625rem) rotate(-1deg) - } -} - -.custom-theme-background .reaction__23977 { - background-color: var(--background-mod-subtle); - border-color: var(--border-strong) -} - -.custom-theme-background .reaction__23977.reactionMe__23977 { - background-color: hsla(var(--brand-500-hsl)/.15); - border-color: var(--brand-500) -} - -.custom-theme-background .reaction__23977.reactionMe__23977 .reactionCount__23977 { - color: var(--text-brand) -} - -.emojiContainer__0524a { - isolation: isolate; - position: absolute -} - -.emoji__0524a { - z-index: 1 -} - -.cannon__0524a { - height: 100%; - position: absolute; - width: 100% -} - -.container__7e919 { - align-items: flex-start; - background-color: var(--background-surface-high); - border-radius: 8px; - box-shadow: var(--elevation-high),var(--elevation-stroke); - display: flex; - margin-inline-start:var(--space-8);padding: 16px -} - -.content__7e919 { - margin-inline-start:24px;max-width: 320px -} - -.buttonContainer__7e919 { - display: flex; - margin-top: 16px -} - -.cancel__7e919 { - color: var(--interactive-text-default); - margin-inline-start:24px} - -.cancel__7e919:hover { - color: var(--interactive-text-hover) -} - -.cancel__7e919:active { - color: var(--interactive-text-active) -} - -.image__7e919 { - height: 64px; - width: 64px -} - -.artContainer_ade26d { - border-radius: 8px; - box-shadow: none; - filter: drop-shadow(0 8px 16px rgba(0,0,0,.24)); - overflow: hidden -} - -.modalContainer_ade26d { - width: 400px -} - -.bodyContainer_ade26d { - overflow: hidden -} - -.effect_a9ddb3 { - bottom: 0; - overflow: hidden; - pointer-events: none; - position: absolute -} - -.effectsWrapper_a9ddb3 { - display: flex; - height: 100%; - justify-content: center; - position: relative; - z-index: 1 -} - -.container_b7f4b4 { - display: flex; - flex-direction: row; - height: 100vh; - min-height: 1px; - padding-inline-start:var(--space-24)} - -.spacer_b7f4b4 { - border-bottom: 1px solid var(--border-subtle); - padding-top: var(--custom-modal-padding-md) -} - -.scroller_b7f4b4 { - border-inline-end:1px solid var(--border-subtle);flex: 0 0 auto; - margin-top: var(--custom-modal-padding-md); - padding-inline-end:var(--space-16);width: 98px -} - -.reactorsContainer_b7f4b4 { - display: flex; - flex-direction: column; - flex-grow: 1; - min-width: 1px -} - -.reactionDefault_b7f4b4,.reactionSelected_b7f4b4 { - align-items: center; - border-radius: 8px; - display: flex; - flex-direction: row; - margin-bottom: 8px; - overflow: hidden; - padding: 0 4px -} - -.reactionDefault_b7f4b4 { - cursor: pointer -} - -.reactionDefault_b7f4b4:hover { - background-color: var(--background-mod-subtle) -} - -.emoji_b7f4b4 { - height: 24px; - min-height: auto; - min-width: auto; - -o-object-fit: contain; - object-fit: contain; - padding-block:4px;padding-inline:4px 8px;vertical-align: middle; - width: 24px -} - -.emojiText_b7f4b4 { - display: inline-block; - vertical-align: -3px -} - -.burstEmojiSection_b7f4b4 { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - padding: 32px 0; - position: relative -} - -.hideEmoji_b7f4b4 { - opacity: 0 -} - -.burstEmoji_b7f4b4 { - height: 32px; - margin-bottom: 8px; - -o-object-fit: contain; - object-fit: contain; - width: 32px -} - -.spinner_b7f4b4 { - height: 52px; - width: 100% -} - -.reactor_b7f4b4 { - box-sizing: border-box; - height: 52px; - margin-inline:16px 2px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.reactorInfo_b7f4b4 { - margin-inline:10px;min-width: 0 -} - -.name_b7f4b4 { - display: flex; - flex: 1; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.ellipsized_b7f4b4,.name_b7f4b4 { - overflow: hidden; - text-overflow: ellipsis -} - -.ellipsized_b7f4b4 { - white-space: nowrap -} - -.username_b7f4b4 { - color: var(--text-muted); - font-size: 12px; - font-weight: var(--font-weight-normal); - line-height: 14px -} - -.tagFaded_b7f4b4 { - flex-shrink: 10 -} - -.discriminator_b7f4b4 { - color: var(--text-muted) -} - -.remove_b7f4b4 { - opacity: 0 -} - -.remove_b7f4b4:focus-within { - opacity: 1 -} - -.reactor_b7f4b4:hover .remove_b7f4b4 { - opacity: 1 -} - -.reactors_b7f4b4 { - --custom-list-scrollbar-top-padding: 12px; - border-radius: var(--radius-sm); - flex: 1; - padding-inline-end:var(--space-24);padding-top: var(--custom-list-scrollbar-top-padding) -} - -.reactors_b7f4b4::-webkit-scrollbar-thumb,.reactors_b7f4b4::-webkit-scrollbar-track { - margin-top: var(--custom-list-scrollbar-top-padding) -} - -.reactionSelected_b7f4b4 { - background-color: var(--background-mod-normal) -} - -.reactionSelected_b7f4b4:hover { - background-color: var(--background-mod-strong) -} - -.reactionDefault_b7f4b4:hover { - background-color: var(--interactive-background-hover) -} - -.effect__263e4 { - margin-bottom: -2px -} - -.effects__263e4 { - bottom: 0; - display: flex; - justify-content: center; - position: absolute -} - -.effect_e5f2cb { - margin-bottom: -.125rem -} - -.effects_e5f2cb { - bottom: 0; - display: flex; - justify-content: center; - position: absolute -} - -.reactionTooltip_b49891 { - background: var(--background-surface-high); - border-radius: var(--radius-xs); - box-shadow: var(--shadow-border),var(--shadow-high); - color: var(--text-default); - font-weight: var(--font-weight-medium); - max-width: 288px; - overflow: hidden; - padding: 16px -} - -.reactionTooltipEmoji_b49891 { - height: 32px; - width: 32px -} - -.reactionTooltipText_b49891 { - margin-inline-start:12px} - -.reactionTooltipInner_b49891 { - align-items: center; - display: flex; - -webkit-hyphens: auto; - hyphens: auto; - pointer-events: all; - word-break: break-word -} - -.reactionTooltipInner_b49891:hover { - cursor: pointer -} - -.reactionTooltipInner_b49891:hover a { - text-decoration: underline -} - -.burstReactionTooltipInner_b49891 { - pointer-events: all -} - -.burstReactionTooltipInner_b49891:hover { - cursor: pointer -} - -.burstReactionTooltipInner_b49891:hover a { - text-decoration: underline -} - -.burstReactionTooltipMessage_b49891 { - align-items: center; - display: flex; - flex-direction: row; - -webkit-hyphens: auto; - hyphens: auto; - pointer-events: all; - word-break: break-word -} - -.burstReactionTooltipSpacer_b49891 { - border-top: 1px solid var(--border-subtle); - margin-top: 8px; - padding-top: 8px -} - -.burstReactionTooltipPrompt_b49891 { - align-items: center; - display: flex; - margin: 12px 0 0; - width: 100% -} - -.burstReactionTooltipPromptClickable_b49891 { - pointer-events: all -} - -.burstReactionTooltipPromptClickable_b49891:hover { - cursor: pointer -} - -.burstReactionTooltipPromptClickable_b49891:hover a { - text-decoration: underline -} - -.burstReactionTooltipNitroIcon_b49891 { - margin-inline-end:5px} - -.burstReactionTooltipUpsellCta_b49891 { - background: linear-gradient(270deg,var(--premium-tier-2-pink-for-gradients) 0,var(--premium-tier-2-pink-for-gradients-2) 33.63%,var(--premium-tier-2-purple-for-gradients) 100%); - border-radius: 4px; - margin-top: 8px; - width: 100% -} - -.mainContainer__844a1.compact__844a1 { - margin-inline-start:-3.95rem;padding-inline-start:0} - -.thankYouCard_b851f5 { - border-radius: 4px; - display: flex; - flex-direction: row; - height: 200px; - justify-content: center; - margin: 12px 0; - position: relative; - width: 440px -} - -.thankYouText_b851f5 { - align-self: center; - min-width: 0; - position: absolute; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.userAvatar_b851f5 { - border-radius: 54px; - width: 100% -} - -.CTAMessage_e887d4 { - margin-bottom: 2px -} - -.badge__9ec1a { - margin-inline-start:16px} - -.userAvatar__9ec1a { - border-radius: 54px; - width: 100% -} - -.svg__9ec1a { - contain: paint -} - -.welcomeCard_d74b42 { - background: no-repeat 50% url(/assets/2f7179a17c81ca41.svg); - border-radius: 4px; - display: flex; - flex-direction: row; - height: 200px; - justify-content: center; - margin: 12px 0; - position: relative; - width: 440px -} - -.welcomeCardText_d74b42 { - align-self: center; - margin: 8px; - min-width: 0; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.welcomeCardBadge_d74b42 { - margin-inline-start:16px} - -.embed_f69538 { - margin-bottom: var(--custom-message-spacing-vertical-container-cozy); - margin-top: 8px -} - -.container_f69538 { - align-items: center; - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - display: flex; - gap: 0 8px; - max-width: 376px; - padding: 8px 16px -} - -.containerWithImage_f69538 { - padding-block:8px;padding-inline:8px 16px} - -.imageContainer_f69538 { - align-items: center; - background: var(--background-mod-subtle); - border-radius: var(--radius-sm); - display: flex; - flex-shrink: 0; - height: 40px; - justify-content: center; - width: 40px -} - -.description_f69538 { - display: flex; - flex-direction: column; - gap: 2px 0; - margin-inline-end:auto;overflow: hidden -} - -.victorAnswer_f69538 { - align-items: center; - display: flex; - gap: 0 4px -} - -.victorAnswerText_f69538 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.victorIcon_f69538 { - flex-shrink: 0 -} - -.systemMessageContainer_c0ec5e { - border-inline-start:2px solid #fff;display: block; - margin-inline-end:40px;padding: 12px 16px -} - -.embedContainer_c0ec5e { - background: var(--background-mod-subtle); - border-radius: 16px; - margin-top: 16px; - max-width: 800px; - padding: 28px 24px!important; - width: 100% -} - -.image_c0ec5e { - margin-inline-end:24px} - -.content_c0ec5e { - width: 100% -} - -.title_c0ec5e { - margin: 4px 0!important -} - -.buttonContainer_c0ec5e { - margin-top: 24px -} - -.container_c9b8e3 { - align-items: center; - align-self: stretch; - display: flex; - justify-content: space-between; - padding: 8px 0 -} - -.usernameContainer_c9b8e3 { - align-items: flex-start; - display: flex; - flex: 1 0 0; - flex-direction: column; - gap: 2px; - height: 32px; - justify-content: center -} - -.userName_c9b8e3 { - text-overflow: ellipsis -} - -.muted_c9b8e3 { - opacity: 10% -} - -.userAvatar_c9b8e3 { - aspect-ratio: 1/1; - justify-content: center; - margin-inline-end:12px} - -.checkbox_c9b8e3,.userAvatar_c9b8e3 { - align-items: center; - display: flex -} - -.checkbox_c9b8e3 { - height: 100% -} - -.clickable_c9b8e3 { - cursor: pointer -} - -.emptySearchResultsContainer__9eabf { - align-items: center; - display: flex; - flex-direction: column; - gap: 8px; - justify-content: center; - padding: 32px 16px; - text-align: center -} - -.trialRecipientContainer__2441a { - align-items: flex-start; - align-self: stretch; - display: flex; - flex-direction: column; - gap: var(--space-16) -} - -.trialRecipientRow__2441a { - align-items: center; - align-self: stretch; - border: 1px solid var(--border-muted); - border-radius: 12px; - display: flex; - gap: 12px; - overflow: hidden; - padding: 12px -} - -.trialRecipientRowDisplayName__2441a { - align-items: flex-start; - display: inline-block; - flex: 1 0 0; - flex-direction: column; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.trialRecipientRowDMContainer__2441a { - align-items: center; - background: var(--scoped-control-background-secondary-default,hsla(240,4%,61%,.12)); - border: 1px solid var(--scoped-control-border-secondary-default,hsla(240,4%,61%,.16)); - border-radius: 8px; - cursor: pointer; - display: flex; - gap: var(--space-4); - justify-content: center; - min-height: 32px; - min-width: 64px; - padding: var(--space-4) var(--space-12) -} - -.trialRecipientRowDMCTA__2441a { - font-family: var(--font-primary); - font-size: 14px; - font-style: normal; - font-weight: var(--font-weight-medium,500); - line-height: 18px; - text-align: center -} - -.errorDisplayName__2441a,.userName__2441a { - color: var(--text-muted) -} - -.erroredAvatar__2441a { - opacity: .5 -} - -.searchbar__45744 { - margin: 0 auto -} - -.list__45744 { - height: 260px; - padding: 0; - width: 100% -} - -.footer__45744 { - align-content: center; - align-items: center; - display: flex; - width: 100% -} - -.submit__45744 { - height: 40px; - margin: 20px 16px 21px; - width: 100% -} - -.theme-dark .footerSeparator__45744 { - box-shadow: 0 -1px 0 hsl(var(--primary-630-hsl)/.6) -} - -.theme-light .footerSeparator__45744 { - box-shadow: 0 -1px 0 hsl(var(--primary-100-hsl)/.6) -} - -.invalidWrapper__076c1 { - border-radius: 3px; - display: block; - overflow: hidden; - position: relative; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.invalidWrapper__076c1 img { - position: absolute -} - -.invalid__076c1 { - background-image: url(/assets/eb4644cbe2f3e90f.svg); - background-position: 50%; - background-repeat: no-repeat; - background-size: contain; - height: 102px; - width: 200px -} - -.buttonContainer__076c1 { - display: flex; - flex-direction: row; - justify-content: space-between -} - -.metadata__076c1 { - color: var(--primary-400); - display: flex; - flex-direction: column; - font-size: 12px; - font-weight: var(--font-weight-semibold); - justify-content: flex-end -} - -.container__076c1 { - align-items: stretch; - flex-direction: row-reverse; - margin: 0; - min-height: 0; - overflow: hidden; - position: relative; - width: 660px -} - -.systemMessageContainerDMEmbedXP__076c1 { - align-items: flex-start; - border-inline-start:2px solid var(--border-normal);display: flex; - gap: 16px; - padding: 12px 16px; - width: 1097px -} - -.systemMessageContainerIcon__076c1 { - padding-top: 0 -} - -.containerDMEmbedXP__076c1 { - align-items: center; - background: var(--background-mod-subtle,hsla(240,4%,61%,.12)); - border-radius: 16px; - display: flex; - gap: 24px; - height: 200px; - margin-top: 16px; - padding: 28px 24px; - width: 580px -} - -.contentDMEmbedXP__076c1 { - display: flex; - flex-direction: column; - gap: 24px -} - -.contentTextDMEmbedXP__076c1 { - display: flex; - flex-direction: column; - gap: 4px; - line-height: 20px -} - -.imgDMEmbedXP__076c1 { - overflow: visible -} - -.buttonContainerDMEmbedXP__076c1 { - align-items: center; - color: #000; - display: flex; - font-family: var(--font-primary); - font-size: 16px; - font-style: normal; - font-weight: var(--font-weight-medium,500); - gap: 24px; - line-height: 20px -} - -.tile__076c1 { - align-items: stretch; - background-color: var(--background-mod-normal); - border-radius: 5px; - box-shadow: 0 0 0 hsl(var(--primary-700-hsl)/.15); - display: flex; - flex-direction: row-reverse; - min-height: 0; - overflow: hidden; - transform: translateZ(0); - transition: background-color .2s ease,box-shadow .2s ease; - width: 660px -} - -.title__076c1 { - flex: 0 1 auto; - font-size: 18px; - font-weight: var(--font-weight-semibold); - line-height: 1.2 -} - -.tagline__076c1 { - color: var(--text-default); - flex: 1 1 auto; - font-size: 15px; - line-height: 1.3; - margin-top: 8px; - overflow: hidden -} - -.actions__076c1 { - flex: 0 1 auto; - justify-self: flex-end; - margin-top: 12px -} - -.media__076c1 { - align-items: center; - display: flex; - flex: 0 0 278px; - position: relative -} - -.birthdayReferral__076c1,.referral__076c1 { - flex: auto; - height: 100%; - margin: 0; - max-height: none; - overflow: hidden; - padding-top: 0; - position: relative -} - -.referral__076c1 { - background: url(/assets/e790daeb039ddb5a.svg) 100% no-repeat -} - -.birthdayReferral__076c1 { - background: url(/assets/eb40cc3e32c7e70f.svg) 100% no-repeat -} - -.description__076c1 { - color: var(--text-strong); - display: flex; - flex: 1 1 auto; - flex-direction: column; - padding: 12px; - position: relative -} - -.theme-dark .tile__076c1:hover { - background-color: var(--primary-800) -} - -.theme-dark .invalid__076c1 { - background-image: url(/assets/e4a502bfc6589d65.svg) -} - -.ticketIcon__0557e { - display: block; - height: 1rem; - width: 1rem -} - -.welcomeCTA_f5d1e2 { - margin-bottom: 2px -} - -.content__75e8e { - background: var(--background-mod-muted); - border: 1px solid var(--border-subtle); - border-radius: 12px; - max-height: 166px; - overflow: hidden -} - -.innerContent__75e8e { - flex-direction: row -} - -.innerContent__75e8e,.subContent__75e8e { - align-items: center; - display: flex -} - -.subContent__75e8e { - flex: 1; - margin: 16px; - min-width: 0 -} - -.avatars__75e8e { - flex-shrink: 0; - position: relative -} - -.recipientUserAvatar__75e8e { - background-color: var(--background-surface-high) -} - -.recipientAvatarMask__75e8e { - display: block -} - -.currentUserAvatar__75e8e { - background-color: var(--background-mod-muted); - bottom: -3px; - inset-inline-end: -2px; - position: absolute -} - -.textContainer__75e8e { - display: flex; - flex-direction: column; - margin-inline-start:12px;padding-top: var(--space-6) -} - -.subHeaderContainer__75e8e { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px -} - -.subHeaderText__75e8e { - color: #fff; - opacity: .8 -} - -.buttonContainer__75e8e { - align-items: center; - display: flex; - flex-shrink: 0; - gap: 16px; - padding-inline-end:16px} - -.messageContentContainer__4ea49 { - padding-bottom: 8px; - padding-top: 16px; - width: 100% -} - -.iconContainer__4ea49 { - height: 100% -} - -.icon__4ea49 { - fill: var(--brand-500) -} - -.container__85015 { - background: var(--background-mod-normal); - border-radius: var(--space-8); - color: var(--text-strong); - display: flex; - flex-grow: 1; - padding: var(--space-16) -} - -.theme-light .container__85015 { - background: var(--background-base-low) -} - -.buttonContainer__85015 { - margin-inline-start:var(--space-24);padding-top: 2px -} - -.messageContentContainer__85015 { - padding-top: var(--space-12) -} - -.iconContainer__85015 { - height: 100% -} - -.icon__85015 { - fill: var(--premium-nitro-pink-light) -} - -.nitroBadge__85015 { - align-items: center; - display: flex; - margin-top: 2px -} - -.nitroBadgeIcon__85015 { - fill: var(--premium-nitro-pink-light) -} - -.nitroBadgeText__85015 { - color: var(--premium-nitro-pink-light); - margin-inline-start:var(--space-4)} - -.premiumIcon__85015 { - height: 16px; - margin: 0 -} - -.embedCard__44c9a { - background-color: var(--background-base-lower); - border-radius: 8px; - padding: 12px -} - -.embedCard__44c9a.compact__44c9a { - padding: 4px -} - -.embedCard__44c9a .header__44c9a { - font-family: var(--font-primary)!important; - font-weight: 400!important; - margin: 0!important; - padding: 0!important -} - -.withFooter__44c9a { - border-radius: 8px 8px 0 0 -} - -.messageContainer__44c9a { - margin-inline-start:-8px} - -.mainContainer__44492 { - margin-bottom: 2px; - margin-top: 2px -} - -.mainContainer__44492.compact__44492 { - margin-inline-start:-3.95rem;padding-inline-start:0} - -.content__44492 { - font-size: 16px; - line-height: 24px; - white-space: pre-wrap; - word-wrap: break-word; - color: var(--text-default); - -webkit-user-select: text; - -moz-user-select: text; - user-select: text; - word-break: break-word -} - -.content__44492 a { - color: var(--interactive-text-active); - cursor: pointer; - font-weight: var(--font-weight-medium) -} - -.content__44492 .actionAnchor__44492 { - color: var(--text-link) -} - -.content__44492.compact__44492 { - padding-inline-start:0} - -.embedCard__44492 { - margin-top: 4px -} - -.embedCard__44492.compact__44492 { - margin-top: 0 -} - -.embedCard__44492.selected__44492 { - background-color: var(--background-mod-normal) -} - -.embedCard__44492.isClickable__44492 { - cursor: pointer -} - -.messageContent__44492.compact__44492 { - padding-inline-start:3.3rem} - -.messageContent__44492 .channelNameContainer__44492 { - display: flex -} - -.messageContent__44492 .channelNameContainer__44492 .channelName__44492 { - background-color: var(--background-base-lower); - color: var(--text-muted) -} - -.messageContent__44492 .channelName__44492 { - align-items: center; - color: var(--text-link); - display: flex; - flex-direction: row; - font-weight: var(--font-weight-medium); - justify-content: center -} - -.messageContent__44492 .channelIcon__44492 { - margin-inline-end:4px} - -.iconContainer__44492 { - align-items: center; - display: flex; - inset-inline-end: 100%; - justify-content: center; - padding-top: 0; - position: absolute; - width: 2.5rem -} - -.compact__44492 .iconContainer__44492 { - margin-inline-end:1rem} - -.font-size-24 .cozy__44492 .iconContainer__44492 { - width: 3rem -} - -.font-size-20 .cozy__44492 .iconContainer__44492 { - width: 3.5rem -} - -.font-size-18 .cozy__44492 .iconContainer__44492 { - width: 4rem -} - -.font-size-12 .cozy__44492 .iconContainer__44492,.font-size-14 .cozy__44492 .iconContainer__44492,.font-size-15 .cozy__44492 .iconContainer__44492,.font-size-16 .cozy__44492 .iconContainer__44492 { - width: 4.5rem -} - -.compact__44492 .iconContainer__44492 { - inset-inline-end: auto; - position: relative; - width: auto -} - -.avatarContainer__44492 { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: 50%; - display: flex; - height: 40px; - justify-content: center; - width: 40px -} - -.usernameContainer__44492 { - font-family: var(--font-primary)!important; - font-weight: 400!important; - margin: 0!important; - padding: 0!important -} - -.usernameContainer__44492 .username__44492 { - font-weight: var(--font-weight-semibold); - margin-inline-end:4px} - -.usernameContainer__44492 .systemTag__44492 { - margin-inline-end:4px;margin-top: 0 -} - -.usernameContainer__44492.compact__44492 { - margin-inline-start:-24px;padding-inline-start:0} - -.centeredRowContainer__44492 { - align-items: center; - display: flex; - flex-direction: row -} - -.dotMargin__44492 { - margin-inline:8px} - -.buttonContainer__44492 { - margin-inline-start:-8px} - -.footerIcon__44492 { - margin-inline-end:4px;vertical-align: center -} - -.footerAction__44492 { - display: flex; - padding: 8px -} - -.dot__44492 { - background-color: var(--background-mod-subtle); - border-radius: 50%; - height: 4px; - width: 4px -} - -.spanCorrection__44492 { - align-items: center; - flex-wrap: wrap; - white-space: pre -} - -.compact__44492 .spanCorrection__44492,.spanCorrection__44492 { - display: flex!important; - flex-direction: row -} - -.footerContainer__44492 { - background-color: var(--background-base-lowest); - border-radius: 0 0 8px 8px; - line-height: 20px; - padding: 2px 18px 4px -} - -.footerContainer__44492.compact__44492 { - margin-inline-start:3.3rem} - -.annotationRow__44492 { - background-color: var(--background-mod-normal); - border-radius: 4px; - line-height: 20px; - margin-top: 4px; - padding: 4px 8px 6px -} - -.alertActionIcon__44492 { - color: var(--text-muted); - padding-inline-start:6px;vertical-align: middle -} - -.alertActionSetCompletedIcon__44492 { - color: var(--text-feedback-positive); - vertical-align: middle -} - -.alertActionsIconContainer__44492 { - margin-inline-start:auto} - -.titleCase__44492 { - text-transform: capitalize -} - -.dropdownArrowHitbox_e8c23e { - align-items: center; - display: flex -} - -.dropdownArrowHitbox_e8c23e:hover .dropdownArrow_e8c23e { - opacity: 1 -} - -.dropdownArrow_e8c23e { - flex: 1; - opacity: .2; - transition: opacity .2s ease -} - -.arrowSeparator_e8c23e { - background-color: currentColor; - opacity: .2; - width: 1px -} - -.dropdownSmall_e8c23e { - width: var(--custom-dropdown-button-small-dropdown-size) + var(--custom-dropdown-button-hitbox-padding) -} - -.dropdownSmall_e8c23e .arrowSeparator_e8c23e { - height: var(--custom-dropdown-button-small-dropdown-size) - var(--custom-dropdown-button-separator-padding) -} - -.dropdownSmall_e8c23e .dropdownArrow_e8c23e { - height: var(--custom-dropdown-button-small-dropdown-size); - width: var(--custom-dropdown-button-small-dropdown-size) -} - -.dropdownMedium_e8c23e { - width: var(--custom-dropdown-button-medium-dropdown-size) + var(--custom-dropdown-button-hitbox-padding) -} - -.dropdownMedium_e8c23e .arrowSeparator_e8c23e { - height: var(--custom-dropdown-button-medium-dropdown-size) - var(--custom-dropdown-button-separator-padding) -} - -.dropdownMedium_e8c23e .dropdownArrow_e8c23e { - height: var(--custom-dropdown-button-medium-dropdown-size); - width: var(--custom-dropdown-button-medium-dropdown-size) -} - -.dropdownLarge_e8c23e { - width: var(--custom-dropdown-button-large-dropdown-size) + var(--custom-dropdown-button-hitbox-padding) -} - -.dropdownLarge_e8c23e .arrowSeparator_e8c23e { - height: var(--custom-dropdown-button-large-dropdown-size) - var(--custom-dropdown-button-separator-padding) -} - -.dropdownLarge_e8c23e .dropdownArrow_e8c23e { - height: var(--custom-dropdown-button-large-dropdown-size); - width: var(--custom-dropdown-button-large-dropdown-size) -} - -.optionContainer__14160 { - display: flex; - flex-direction: column; - gap: 4px -} - -.clickableRow__14160 { - align-items: center; - border-radius: 8px; - display: flex; - gap: 12px; - padding: 8px; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.clickableRow__14160:hover { - background: var(--background-mod-normal) -} - -.indicatorWrapper__14160 { - align-items: center; - display: flex; - flex-shrink: 0 -} - -.labelContainer__14160 { - display: flex; - flex: 1; - flex-direction: column; - gap: 4px -} - -.label__14160 { - align-items: center; - display: flex -} - -.textInputWrapper__14160 { - padding-inline-start:40px} - -.container_b5a371 { - gap: 12px -} - -.choicesContainer_b5a371,.container_b5a371 { - display: flex; - flex-direction: column -} - -.choicesContainer_b5a371 { - gap: 4px -} - -.choiceRow_b5a371 { - align-items: center; - display: flex -} - -.noChoices_b5a371 { - font-size: 14px -} - -.container__3cb11 { - display: flex; - flex-direction: column; - gap: 12px -} - -.container__18c16 { - display: flex; - flex-direction: column; - gap: 16px; - white-space: normal -} - -.container__18c16 ol,.container__18c16 ul { - margin-bottom: 24px; - margin-inline:20px 0} - -.header__2513e { - align-items: center; - justify-content: space-between -} - -.headerContent__2513e { - align-items: center; - display: flex; - gap: 12px -} - -.content__2513e { - display: flex; - flex-direction: column; - gap: 48px -} - -.loading__2513e { - padding: 24px; - text-align: center -} - -.mainContainer_b99f57.compact_b99f57 { - margin-inline-start:-3.95rem;padding-inline-start:0} - -.content_b99f57 { - font-size: 16px; - line-height: 24px; - white-space: pre-wrap; - word-wrap: break-word; - color: var(--text-default); - display: flex; - flex-direction: column; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text; - word-break: break-word -} - -.content_b99f57 a { - color: var(--interactive-text-active); - cursor: pointer; - font-weight: var(--font-weight-medium) -} - -.content_b99f57 .actionAnchor_b99f57 { - color: var(--text-link) -} - -.content_b99f57.compact_b99f57 { - align-items: center; - flex-direction: row; - flex-wrap: wrap; - padding-inline-start:0} - -.flexLineBreak_b99f57 { - flex-basis: 100% -} - -.embedCard_b99f57 { - background-color: var(--background-base-lower); - border-radius: 8px; - display: flex; - flex-direction: column; - margin-bottom: 12px; - margin-top: 4px; - margin-inline-start:0;overflow: hidden -} - -.embedCard_b99f57.compact_b99f57 { - margin-inline-start:3.3rem} - -.cardContent_b99f57 { - display: flex; - flex-direction: column; - gap: 8px; - padding: 12px -} - -.cardHeader_b99f57 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px -} - -.cardHeaderContianer_b99f57 { - align-items: flex-start; - display: flex; - flex-direction: column; - gap: 2px -} - -.subheader_b99f57 { - gap: 8px -} - -.cardFooter_b99f57,.subheader_b99f57 { - align-items: center; - display: flex; - flex-direction: row -} - -.cardFooter_b99f57 { - background-color: var(--background-mod-normal); - padding: 4px -} - -.iconContainer_b99f57 { - align-items: center; - display: flex; - inset-inline-end: 100%; - justify-content: center; - padding-top: 0; - position: absolute; - width: 2.5rem -} - -.compact_b99f57 .iconContainer_b99f57 { - margin-inline-end:1rem} - -.font-size-24 .cozy_b99f57 .iconContainer_b99f57 { - width: 3rem -} - -.font-size-20 .cozy_b99f57 .iconContainer_b99f57 { - width: 3.5rem -} - -.font-size-18 .cozy_b99f57 .iconContainer_b99f57 { - width: 4rem -} - -.font-size-12 .cozy_b99f57 .iconContainer_b99f57,.font-size-14 .cozy_b99f57 .iconContainer_b99f57,.font-size-15 .cozy_b99f57 .iconContainer_b99f57,.font-size-16 .cozy_b99f57 .iconContainer_b99f57 { - width: 4.5rem -} - -.compact_b99f57 .iconContainer_b99f57 { - inset-inline-end: auto; - position: relative; - width: auto -} - -.usernameContainer_b99f57 { - font-family: var(--font-primary)!important; - font-weight: 400!important; - margin: 0!important; - padding: 0!important -} - -.usernameContainer_b99f57 .username_b99f57 { - font-weight: var(--font-weight-semibold); - margin-inline-end:4px} - -.usernameContainer_b99f57 .systemTag_b99f57 { - margin-inline-end:2px;margin-top: 2px -} - -.usernameContainer_b99f57.compact_b99f57 { - margin-inline-start:-24px;padding-inline-start:0} - -.centeredRowContainer_b99f57 { - align-items: center; - display: flex; - flex-direction: row -} - -.dotMargin_b99f57 { - margin-inline:8px} - -.footerIcon_b99f57 { - margin-inline-end:4px;vertical-align: center -} - -.footerAction_b99f57 { - display: flex; - padding: 8px -} - -.spanCorrection_b99f57 { - flex-wrap: wrap; - white-space: pre -} - -.compact_b99f57 .spanCorrection_b99f57,.spanCorrection_b99f57 { - display: flex!important; - flex-direction: row -} - -.alertsEnabledSubHeader_b99f57 { - align-items: center; - display: flex; - gap: 4px -} - -.alertsEnabledSubHeaderAvatarUsername_b99f57 { - align-items: center; - cursor: pointer; - display: flex -} - -.dotSeparatedRow_b99f57 { - gap: 4px -} - -.dotSeparatedRow_b99f57,.footerRow_b99f57 { - align-items: center; - display: flex; - flex-direction: row -} - -.dot_b99f57 { - background-color: var(--background-mod-subtle); - border-radius: 50%; - height: 4px; - width: 4px -} - -.applicationName__9e50d { - display: inline-flex -} - -.systemMessage__9e50d>a:last-of-type { - color: var(--text-link) -} - -.messageContent__2facc { - display: inline-block -} - -.alertIcon__2facc { - color: var(--text-feedback-warning); - height: 16px; - width: 16px -} - -.content_e8c018 p { - margin-bottom: 0; - margin-top: 0 -} - -.content_e8c018 a:last-of-type { - color: var(--text-link) -} - -.gameContainer_e8c018 { - color: var(--text-default); - font-weight: var(--font-weight-medium) -} - -.inlineIcon_e8c018 { - margin-bottom: 2px; - vertical-align: middle -} - -.stageSystemMessage_e9ab2f { - color: var(--text-strong) -} - -.iconContainer_e9ab2f { - color: var(--text-muted); - padding-top: 0 -} - -.thumbnailStackGallery_c085c7 { - position: relative -} - -.gallery-height-1_c085c7 { - height: 102px; - width: 183px -} - -.gallery-height-2_c085c7 { - height: 114px; - width: 183px -} - -.gallery-height-3_c085c7 { - height: 123px; - width: 183px -} - -.gallery-height-max_c085c7 { - height: 131.5px; - width: 183px -} - -.thumbnailGallery_c085c7 { - border-radius: 4px; - inset-inline-start: 50%; - -o-object-fit: cover; - object-fit: cover; - position: absolute; - transform: translate(-50%) -} - -.thumbnailGallery_c085c7[data-index="3"],.thumbnailStackGallery_c085c7:not(.gallery-height-max_c085c7):not(.gallery-height-3_c085c7):not(.gallery-height-2_c085c7) .thumbnailGallery_c085c7[data-index="0"] { - bottom: 0; - height: 102px; - width: 183px -} - -.thumbnailGallery_c085c7[data-index="2"],.thumbnailStackGallery_c085c7.gallery-height-2_c085c7 .thumbnailGallery_c085c7[data-index="0"],.thumbnailStackGallery_c085c7.gallery-height-3_c085c7 .thumbnailGallery_c085c7[data-index="1"] { - bottom: 28px; - filter: brightness(80%); - height: 86px; - width: 154px -} - -.thumbnailGallery_c085c7[data-index="1"],.thumbnailStackGallery_c085c7.gallery-height-3_c085c7 .thumbnailGallery_c085c7[data-index="0"] { - bottom: 51px; - filter: brightness(60%); - height: 72px; - width: 128px -} - -.thumbnailGallery_c085c7[data-index="0"] { - bottom: 74.5px; - filter: brightness(50%); - height: 57px; - width: 101.333px -} - -.thumbnailStackStacked_c085c7 { - position: relative -} - -.stacked-1_c085c7 { - height: 112px; - width: 200px -} - -.stacked-2_c085c7 { - height: 127px; - width: 215.46px -} - -.thumbnailStacked_c085c7 { - border: 1px solid var(--black); - -o-object-fit: cover; - object-fit: cover; - position: absolute -} - -.thumbnailStacked_c085c7[data-index="0"] { - border-radius: 8px; - height: 80px; - inset-inline-start: 0; - opacity: .5; - top: 0; - transform: rotate(-2deg); - width: 142px -} - -.thumbnailStacked_c085c7[data-index="1"] { - border-radius: 12px; - height: 112px; - inset-inline-start: 15.46px; - opacity: 1; - top: 15px; - transform: rotate(2deg); - width: 200px -} - -.stacked-1_c085c7 .thumbnailStacked_c085c7[data-index="0"] { - border-radius: 12px; - height: 112px; - inset-inline-start: 0; - top: 0; - transform: rotate(0deg); - width: 200px -} - -.previewContainer_dd7e13 { - display: flex; - flex-direction: column; - gap: 12px; - margin-top: 8px -} - -.icon_b9588c { - height: 1.125rem; - width: 1.125rem -} - -.icon__80514 { - height: 1.125rem; - width: 1.125rem -} - -.iconWrapper__9f20a { - height: 1rem; - width: 1rem -} - -.icon__9f20a { - color: var(--guild-boosting-pink); - height: 100%; - width: 100% -} - -.icon__9f20a,.message__9f20a { - cursor: pointer -} - -.cannon__9f20a,.cannonWrapper__9f20a { - height: 100%; - position: absolute; - width: 100% -} - -.easterEggAnimationClickTarget__9f20a { - cursor: pointer; - pointer-events: all; - position: absolute -} - -.easterEggAnimationClickTargetTopLeft__9f20a { - inset-inline-start: 0; - top: 0; - transform: scale(-1) -} - -.easterEggAnimationClickTargetTopRight__9f20a { - inset-inline-end: 0; - top: 0; - transform: scaleY(-1) -} - -.easterEggAnimationClickTargetBottomLeft__9f20a { - bottom: 0; - inset-inline-start: 0; - transform: scaleX(-1) -} - -.easterEggAnimationClickTargetBottomRight__9f20a { - bottom: 0; - inset-inline-end: 0 -} - -.easterEggAnimation__9f20a { - height: var(--custom-user-premium-guild-subscription-easter-egg-size); - width: var(--custom-user-premium-guild-subscription-easter-egg-size) -} - -.easterEggAnimationHideLeaf__9f20a .userPremiumGuildSubscriptionLeaf,.easterEggAnimationHideLeaf__9f20a .userPremiumGuildSubscriptionLeafParent { - visibility: hidden -} - -.iconContainer_a1f71e { - padding-top: 0 -} - -.container_c530da { - max-width: 350px; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.tooltip_c530da { - background-color: var(--background-surface-high); - border-radius: 5px; - box-shadow: var(--shadow-border),var(--shadow-high); - color: var(--primary-200); - font-size: 16px; - line-height: 20px; - overflow-wrap: break-word; - padding: 8px 12px -} - -.tooltipPointer_c530da { - border: 5px solid transparent; - border-top: 5px solid var(--background-surface-high); - height: 0; - inset-inline-start: 50%; - margin-inline-start:-5px;pointer-events: none; - position: absolute; - top: 100%; - width: 0 -} - -.clickable_c530da { - cursor: pointer -} - -.mention_c530da { - background: none; - padding: 0 -} - -.spinner_c530da { - height: 16px; - width: 16px -} - -/*# sourceMappingURL=076e97f00ed91b8f.css.map*/ diff --git a/discord-html-copy/css/0fe7a252fcb6f0c6.css b/discord-html-copy/css/0fe7a252fcb6f0c6.css deleted file mode 100644 index 8ad5d38..0000000 --- a/discord-html-copy/css/0fe7a252fcb6f0c6.css +++ /dev/null @@ -1,9891 +0,0 @@ -.container__74da2 { - align-items: center; - display: flex; - justify-content: center; - min-height: 100vh; - position: relative; - width: 100vw -} - -.backgroundArtwork__74da2 { - height: 100%; - inset-inline-start: 0; - position: fixed; - top: 0; - width: 100% -} - -.appOpenedTitle__74da2 { - margin-bottom: 12px -} - -.container__44284 { - align-items: center; - background-color: var(--background-base-low); - border-radius: 8px; - box-shadow: 0 4px 12px rgba(0,0,0,.1); - display: flex; - flex-direction: column; - gap: 1rem; - inset-inline-start: 50%; - justify-content: center; - margin: 0 auto; - max-width: 80%; - padding: 2rem; - position: absolute; - text-align: center; - top: 50%; - transform: translate(-50%,-50%); - width: -moz-fit-content; - width: fit-content -} - -.divider_c87e85 { - border-bottom: 1px solid; - margin-bottom: 20px -} - -.theme-light .divider_c87e85 { - border-color: hsl(var(--primary-200-hsl)/.6) -} - -.theme-dark .divider_c87e85 { - border-color: hsl(var(--primary-630-hsl)/.6) -} - -.contentRating_f4dfaa { - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - flex-shrink: 0 -} - -.esrb_f4dfaa { - height: 54px; - width: 40px -} - -.pegi_f4dfaa { - height: 49px; - width: 40px -} - -.esrbEveryone_f4dfaa { - background-image: url(/assets/9daa18150ba7418f.svg) -} - -.esrbEveryoneTenPlus_f4dfaa { - background-image: url(/assets/27ff09832b8a5062.svg) -} - -.esrbTeen_f4dfaa { - background-image: url(/assets/0129c482e263a9bf.svg) -} - -.esrbMature_f4dfaa { - background-image: url(/assets/4613be06562668a7.svg) -} - -.esrbAdult_f4dfaa { - background-image: url(/assets/625f813befac2f1e.svg) -} - -.esrbRatingPending_f4dfaa { - background-image: url(/assets/fc451c7fd580b692.svg); - height: 60px; - width: 40px -} - -.pegiThree_f4dfaa { - background-image: url(/assets/555f2e7a9e2a3ba6.svg) -} - -.pegiSeven_f4dfaa { - background-image: url(/assets/838c728f09c1c921.svg) -} - -.pegiTwelve_f4dfaa { - background-image: url(/assets/23cd3d91b54c025b.svg) -} - -.pegiSixteen_f4dfaa { - background-image: url(/assets/3ad9c67e984da7ef.svg) -} - -.pegiEighteen_f4dfaa { - background-image: url(/assets/d71b4a11b638071e.svg) -} - -.content__6d178 { - color: var(--text-muted); - flex: 1; - font-size: 12px; - line-height: 1.5; - margin-inline-end:20px;-webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.rating__6d178 { - margin-inline-end:12px} - -.features_c85137 { - margin-bottom: 40px; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.row_c85137 { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: 3px; - color: var(--text-muted); - display: flex; - font-size: 15px; - line-height: 16px; - margin-bottom: 2px; - padding: 8px -} - -.row_c85137:last-child { - margin-bottom: 0 -} - -.checkmarkIcon_c85137,.featureIcon_c85137 { - flex: 0 0 auto; - height: 24px; - width: 24px -} - -.featureText_c85137 { - flex: 1; - margin: 0 8px -} - -.checkmarkIcon_c85137 { - color: var(--green-360) -} - -.featureIcon_c85137 { - color: var(--text-muted) -} - -.content__1688d { - color: var(--text-muted); - flex: 1; - font-size: 12px; - line-height: 1.5; - margin-inline-end:20px;-webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.matureListing__49e1d { - height: 100%; - padding-bottom: 60px; - position: relative -} - -.matureListing__49e1d:before { - box-shadow: var(--elevation-low); - content: ""; - display: block; - height: 1px; - position: absolute; - top: -1px; - inset-inline: 0; - pointer-events: none; - z-index: 1 -} - -.theme-light .matureListing__49e1d { - background-color: var(--white) -} - -.theme-dark .matureListing__49e1d { - background-color: transparent -} - -.recommendationReason__6efad { - align-items: center; - display: flex; - font-size: 15px -} - -.circle__6efad { - border-radius: 50%; - box-sizing: border-box; - color: var(--primary-100); - flex-shrink: 0; - height: 32px; - margin-inline-end:12px;padding: 8px; - width: 32px -} - -.icon__6efad { - height: 100%; - width: 100% -} - -.iconCircle__6efad { - align-items: center; - background-color: var(--primary-500); - display: flex; - justify-content: center -} - -.earlyAccess__6efad { - padding: 11px -} - -.premiumCircle__6efad { - background-image: linear-gradient(270deg,#d589c0,#bf7be3) -} - -.smallHeader__6efad { - color: var(--primary-300); - font-size: 12px; - margin-bottom: 2px -} - -.smallHeader__6efad,.text__6efad { - font-weight: var(--font-weight-medium) -} - -.text__6efad { - color: var(--text-default); - font-size: 14px; - line-height: 1.3 -} - -.description__6efad { - flex: 1 -} - -.staffIcon__6efad { - background-color: var(--brand-500) -} - -.nitroIcon__6efad { - background: linear-gradient(to left,var(--premium-tier-1-purple),var(--brand-500)) -} - -.recommendationActivity__450d3 { - display: flex -} - -.players__450d3 { - margin-inline-end:8px;margin-bottom: 12px -} - -.playerAvatar__450d3 { - cursor: pointer; - margin-inline-end:4px} - -.avatarMasked__450d3 { - margin-inline-end:-16px;-webkit-mask-image: url(/assets/66d9417d99573939.svg); - mask-image: url(/assets/66d9417d99573939.svg); - -webkit-mask-position: 0 0; - mask-position: 0 0; - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-size: 100% 100%; - mask-size: 100% 100% -} - -.playerOverflow__450d3 { - border-radius: 50%; - box-sizing: border-box; - color: var(--text-muted); - font-size: 12px; - font-weight: var(--font-weight-bold); - height: 32px; - line-height: 32px; - min-width: 32px; - padding: 0 8px; - position: relative -} - -.description__450d3 { - color: var(--text-muted); - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 20px -} - -.username__450d3:hover { - cursor: pointer; - text-decoration: underline -} - -.tooltip__450d3 { - text-align: center -} - -.tooltipTimestamp__450d3 { - opacity: .6 -} - -.theme-light .playerOverflow__450d3 { - background-color: var(--primary-100) -} - -.theme-light .description__450d3 strong,.theme-light .discriminator__450d3 strong,.theme-light .username__450d3 { - color: var(--primary-600) -} - -.theme-dark .playerOverflow__450d3 { - background-color: var(--primary-500) -} - -.theme-dark .description__450d3 strong,.theme-dark .username__450d3 { - color: var(--white) -} - -.header__74994 { - margin-bottom: 12px -} - -.staffReviewHeader__74994 { - align-items: center; - display: flex -} - -.headerText__74994 { - margin-inline-start:12px} - -.label__74994 { - color: var(--text-muted); - font-size: 12px; - font-weight: var(--font-weight-medium); - margin-bottom: 4px -} - -.username__74994 { - color: var(--text-strong); - font-size: 14px; - font-weight: var(--font-weight-semibold) -} - -.notes__74994 { - color: var(--text-muted); - font-size: 14px; - font-style: italic; - font-weight: var(--font-weight-medium); - line-height: 1.33 -} - -.notes__74994:before { - content: "\201c" -} - -.notes__74994:after { - content: "\201d" -} - -.root__26095 { - background-color: var(--background-base-lowest); - border-radius: 3px; - overflow: hidden; - padding: 20px 20px 0; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.header__26095 { - color: var(--text-default); - font-size: 20px; - font-weight: var(--font-weight-medium); - margin-bottom: 20px -} - -.section__26095:not(:last-child) { - border-bottom: 1px solid transparent; - margin-bottom: 20px -} - -.sectionContent__26095 { - display: flex; - flex-wrap: wrap; - margin-inline-end:-20px} - -.unit__26095 { - box-sizing: border-box; - flex: 1 1 50%; - margin-bottom: 20px; - min-width: 240px; - padding-inline-end:20px} - -.review__26095 { - padding-bottom: 20px -} - -.theme-dark .section__26095 { - border-bottom-color: var(--primary-600) -} - -.theme-light .section__26095 { - border-bottom-color: hsl(var(--primary-200-hsl)/.6) -} - -.breadcrumb__67607 { - cursor: pointer -} - -.breadcrumb__67607:hover { - color: var(--text-default); - text-decoration: underline -} - -.tabs_f1dca0 { - position: relative -} - -.separator_f1dca0 { - bottom: 0; - height: 1px; - inset-inline: 0; - position: absolute -} - -.tab_f1dca0 { - border-bottom: 2px solid transparent; - border-radius: 0; - margin-inline-start:8px;padding-bottom: 4px -} - -.tab_f1dca0:first-child { - margin-inline-start:0} - -.tabPageLarge_f1dca0 { - padding-inline:8px} - -.tabPageSmall_f1dca0 { -} - -.tabNotSelected_f1dca0 { - transition: color .17s -} - -.sectionTitle_f1dca0 { - color: var(--text-muted); - font-size: 12px; - font-weight: var(--font-weight-semibold); - margin-bottom: 8px; - text-transform: uppercase -} - -.requirementsContainer_f1dca0 { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - margin-top: 10px; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.requirements_f1dca0 { - box-sizing: border-box; - color: var(--text-default); - flex: 1 0 50%; - margin-bottom: 16px; - min-width: 260px; - padding-inline-end:20px} - -.requirement_f1dca0 { - font-size: 15px; - line-height: 18px; - margin: 6px 0 -} - -.requirementKey_f1dca0 { - color: var(--text-muted); - margin-inline-end:4px;white-space: nowrap -} - -.theme-dark .separator_f1dca0 { - background-color: hsl(var(--primary-630-hsl)/.6) -} - -.theme-dark .tabSelected_f1dca0 { - border-bottom-color: var(--white) -} - -.theme-light .separator_f1dca0 { - background-color: hsl(var(--primary-200-hsl)/.6) -} - -.theme-light .tabSelected_f1dca0 { - border-bottom-color: var(--primary-600) -} - -.tabNotSelectedColor_f1dca0 { - color: var(--text-muted) -} - -.tabNotSelectedColor_f1dca0:hover,.tabSelectedColor_f1dca0 { - color: var(--text-default) -} - -.inviteSmall_f1d99d .content_f1d99d { - align-items: flex-start -} - -.inviteSmall_f1d99d .info_f1d99d { - margin-bottom: 12px -} - -.inviteLarge_f1d99d .content_f1d99d { - align-items: center -} - -.content_f1d99d { - background-color: var(--background-base-lowest); - border-radius: 5px; - display: flex; - flex-direction: column; - padding: 16px -} - -.guildIcon_f1d99d { - flex-shrink: 0 -} - -.info_f1d99d { - display: flex; - max-width: 100% -} - -.infoText_f1d99d { - margin-inline-start:20px;min-width: 0 -} - -.name_f1d99d { - font-size: 16px; - line-height: 1.25 -} - -.memberInfo_f1d99d,.name_f1d99d { - color: var(--text-muted) -} - -.memberInfo_f1d99d { - font-size: 14px; - margin-top: 4px -} - -.dot_f1d99d { - border-radius: 50%; - flex-shrink: 0; - height: 8px; - margin-inline:8px 4px;width: 8px -} - -.dot_f1d99d:first-child { - margin-inline-start:0} - -.dotOnline_f1d99d { - background-color: var(--green-360) -} - -.dotOffline_f1d99d { - background-color: var(--primary-400) -} - -.memberText_f1d99d { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.actionButton_f1d99d { - margin-top: 16px -} - -.joinedCheckmark_f1d99d { - height: 18px; - margin-inline-start:8px;width: 18px -} - -.buttonColorInGuild_f1d99d { - color: var(--text-default) -} - -.listingWrapper__0f50c { - display: flex; - flex-direction: column; - height: 100% -} - -.outerScroller__0f50c:before { - box-shadow: var(--elevation-low); - content: ""; - display: block; - height: 1px; - position: absolute; - top: -1px; - inset-inline: 0; - pointer-events: none; - z-index: 1 -} - -.scroller__0f50c { - background-color: var(--background-base-low); - contain: layout size -} - -.noHeaderSpacer__0f50c { - padding-top: 20px -} - -.headerAssets__0f50c { - align-items: center; - display: flex; - height: 100px; - justify-content: center; - margin: 0 auto; - max-width: var(--custom-application-store-listing-body-max-width); - position: relative -} - -.headerBackground__0f50c { - background-position: 50%; - background-size: cover; - inset: 0; - -webkit-mask: radial-gradient(ellipse 50% 100% at 50% 0,#000,transparent); - mask: radial-gradient(ellipse 50% 100% at 50% 0,#000,transparent); - opacity: .3; - position: absolute -} - -.headerLogo__0f50c { - background-size: contain; - height: 60px; - -o-object-fit: contain; - object-fit: contain; - position: relative -} - -.link__0f50c { - color: var(--text-strong); - cursor: pointer; - margin-inline:6px;opacity: .6 -} - -.link__0f50c:hover { - opacity: .8 -} - -.link__0f50c:disabled { - opacity: .2 -} - -.linkIcon__0f50c { - display: block; - height: 24px; - width: 24px -} - -.spinner__0f50c { - margin-bottom: 40px; - margin-top: 40px -} - -.listing__0f50c { - color: var(--text-strong); - margin: 0 auto -} - -.gatedListing__0f50c { - position: absolute; - visibility: hidden; - z-index: -2 -} - -.contentRating__0f50c,.description__0f50c,.guildInvite__0f50c,.legalInfo__0f50c,.news__0f50c,.premiumPerks__0f50c,.purchaseUnit__0f50c,.whyYouMightLikeIt__0f50c { - margin-bottom: 40px -} - -.systemRequirements__0f50c { - margin-bottom: 20px -} - -.marketingHeader__0f50c { - background-color: var(--primary-700) -} - -.headerBarListing__0f50c { - flex: 1; - justify-content: space-between -} - -.headerBarListing__0f50c,.headerPurchase__0f50c,.headerSection__0f50c { - align-items: center; - display: flex -} - -.headerPurchase__0f50c { - font-size: 14px; - opacity: 0; - transform: translateY(45px); - transition-duration: .4s; - transition-timing-function: ease -} - -.full-motion .headerPurchase__0f50c { - transition-property: transform,opacity -} - -.headerPurchase__0f50c.active__0f50c { - opacity: 1; - transform: translateY(0) -} - -.testModeSelectButton__0f50c { - margin-inline-start:16px;padding-block:4px;padding-inline:8px 4px} - -@media (max-width: 900px) { - .headerPurchase__0f50c { - display:none - } -} - -.headerActionButton__0f50c { - margin-inline:12px 6px} - -.listingLarge__0f50c { - margin: 0 auto; - max-width: var(--custom-application-store-listing-body-max-width); - padding: 0 20px -} - -.listingLarge__0f50c .body__0f50c { - display: flex -} - -.listingLarge__0f50c .leftColumn__0f50c { - width: 66.67% -} - -.listingLarge__0f50c .rightColumn__0f50c { - margin-inline-start:40px;width: 33.33% -} - -.listingSmall__0f50c { - padding-inline:20px} - -.listingSmall__0f50c .headerAssets__0f50c { - height: 80px -} - -.listingSmall__0f50c .headerLogo__0f50c { - height: 40px -} - -.listingSmall__0f50c .purchaseUnit__0f50c { - margin-bottom: 20px; - margin-top: 20px -} - -.purchaseError__0f50c { - color: var(--text-feedback-critical); - font-size: 14px; - margin-inline-start:8px} - -.applicationStore_f07d62 { - display: flex; - flex: 1 1 auto; - flex-direction: column; - overflow-x: hidden; - overflow-y: auto; - -webkit-overflow-scrolling: touch; - background-color: var(--background-base-low) -} - -.navigation_f07d62 { - margin: 40px 35px -} - -.marketingHeader_f07d62 { - background-color: var(--primary-700) -} - -.marketingFooter_f07d62 { - overflow: visible!important -} - -.libraryFilter__3fb48 { - align-items: center; - display: flex; - margin-inline-end:8px;overflow: hidden; - position: relative; - width: 144px -} - -.libraryFilter__3fb48.focused__3fb48 { - width: 240px -} - -.searchBar__3fb48 { - flex: 1 1 auto; - width: 100% -} - -.libraryHeader__5a895 { - z-index: 3 -} - -.libraryHeader__5a895:before { - box-shadow: var(--elevation-low); - contain: paint; - content: ""; - display: block; - height: 1px; - position: absolute; - top: 47px; - inset-inline: 0; - pointer-events: none; - z-index: 1 -} - -.row__5fe04 { - display: flex -} - -.clickable__5fe04 { - color: var(--interactive-text-default); - cursor: pointer -} - -.clickable__5fe04:hover { - color: var(--interactive-text-hover) -} - -.headerCell__5fe04 { - display: flex -} - -.headerCellContent__5fe04 { - align-items: center; - display: flex; - min-height: 18px -} - -.stickyHeader__5fe04 { - inset-inline-start: 0; - position: sticky; - top: 0; - z-index: 2 -} - -.sortIcon__5fe04 { - height: 18px; - margin-inline-start:4px;width: 18px -} - -.headerCellSorted__5fe04 { - color: var(--text-default) -} - -.table_adb41f { - width: 100% -} - -.table_adb41f:only-child { - margin-bottom: 20px -} - -.header_adb41f { - border-bottom: 1px solid var(--border-subtle); - box-sizing: border-box; - font-size: 12px; - font-weight: var(--font-weight-semibold); - padding: 20px 34px 8px -} - -.nameCell_adb41f { - width: 30% -} - -.nameCellText_adb41f { - color: var(--text-strong); - font-weight: var(--font-weight-semibold); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.icon_adb41f { - color: var(--icon-muted); - height: 20px; - width: 20px -} - -.nitroIcon_adb41f { - flex-shrink: 0; - margin-inline-start:8px} - -.lastPlayedCell_adb41f,.platformCell_adb41f { - color: var(--text-muted); - width: 20% -} - -.actionsCell_adb41f { - align-items: center; - display: flex; - justify-content: flex-end; - min-width: 0; - width: 30% -} - -.stickyHeader_adb41f { - width: calc(100% - 16px) -} - -.headerCell_adb41f { - border-inline-start:1px solid var(--border-subtle);box-sizing: border-box; - flex: 1 1 auto; - padding: 0 12px; - text-transform: uppercase -} - -.headerCell_adb41f:first-child { - border-inline-start:none;padding-inline-start:0} - -.headerCellSorted_adb41f,.headerCellSorted_adb41f:hover { - color: var(--interactive-text-active) -} - -.rowWrapper_adb41f { - border-radius: 8px; - cursor: pointer; - margin: 0 18px; - position: relative -} - -.rowWrapper_adb41f+.rowWrapper_adb41f .row_adb41f { - border-top: 1px solid var(--border-subtle) -} - -.rowWrapperActive_adb41f { - background-color: var(--interactive-background-hover); - margin-bottom: -1px; - padding-bottom: 1px; - z-index: 1 -} - -.rowWrapperActive_adb41f,.rowWrapperGlow_adb41f { -} - -@keyframes glow_adb41f { - 0% { - opacity: 1 - } - - 20% { - opacity: 1 - } - - to { - opacity: 0 - } -} - -.row_adb41f { - align-items: center; - color: var(--text-default); - display: flex; - margin: 0 8px; - min-height: var(--custom-game-list-row-min-height); - position: relative -} - -.rowBackground_adb41f { - background-repeat: no-repeat; - background-size: cover; - border-radius: 8px; - bottom: 0; - filter: grayscale(100%); - inset-inline-start: 0; - -webkit-mask: radial-gradient(100% 100% at top left,hsla(0,0%,100%,.3) 0,hsla(0,0%,100%,0) 100%); - mask: radial-gradient(100% 100% at top left,hsla(0,0%,100%,.3) 0,hsla(0,0%,100%,0) 100%); - position: absolute; - top: 0; - width: 272px -} - -.bodyCell_adb41f { - box-sizing: border-box; - flex: 1 1 auto; - line-height: 1.25; - padding-block:8px;padding-inline:15px 8px} - -.textCell_adb41f { - align-self: stretch; - color: var(--text-muted); - display: flex -} - -.nameBodyCell_adb41f { - font-weight: var(--font-weight-medium); - padding-inline-start:8px} - -.nameCellInfo_adb41f { - align-items: center; - display: flex; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.lastPlayedCellNew_adb41f { - color: var(--text-feedback-warning); - font-weight: var(--font-weight-semibold); - text-transform: uppercase -} - -.gameIcon_adb41f { - margin-inline-end:20px} - -.actionButtonSize_adb41f { - height: 32px; - min-width: 120px -} - -.hidden_adb41f { - visibility: hidden -} - -.settingIcons_adb41f { - display: flex; - margin-inline-end:26px;min-width: 56px -} - -.settingIcon_adb41f { - margin-inline-end:20px;min-width: 20px -} - -.settingIcon_adb41f:last-child { - margin-inline-end:0} - -.emptyState_adb41f { - align-items: center; - display: flex; - justify-content: center; - padding-bottom: 20px -} - -.emptyStateText_adb41f { - margin: 20px 20px 0; - text-align: center -} - -.emptyStateHeader_adb41f { - color: var(--text-strong); - font-size: 24px; - margin-bottom: 8px -} - -.emptyStateDescription_adb41f { - color: var(--text-default); - font-size: 16px; - line-height: 1.25 -} - -.emptyStateButtons_adb41f { - display: flex; - flex: 0 0 auto; - justify-content: center; - width: 100% -} - -.emptyStateButton_adb41f { - margin-top: 20px; - margin-inline-end:20px;min-width: 125px -} - -.emptyStateButton_adb41f:last-child { - margin-inline-end:0} - -.emptyWumpus_adb41f { - flex-shrink: 0; - height: 202px; - width: 404px -} - -.emptyStateLarge_adb41f { - box-sizing: border-box; - flex-direction: column; - height: 100% -} - -.emptyStateSmall_adb41f { - border-top: 1px solid var(--border-subtle); - padding-top: 39px -} - -.emptyStateSmall_adb41f .emptyStateText_adb41f { - margin-inline-start:0;text-align: start -} - -.emptyStateSmall_adb41f .emptyWumpus_adb41f { - margin-inline:50px 40px} - -.emptyStateSmall_adb41f .emptyStateButtons_adb41f { - justify-content: flex-start -} - -.rowWrapperDim_adb41f .nameBodyCell_adb41f,.rowWrapperDim_adb41f .settingIcon_adb41f,.rowWrapperDim_adb41f .textCell_adb41f { - opacity: .2 -} - -.rowWrapper_adb41f+.rowWrapperActive_adb41f .row_adb41f,.rowWrapperActive_adb41f+.rowWrapper_adb41f .row_adb41f { - border-top-color: transparent -} - -.theme-dark .emptyWumpus_adb41f { - background: url(/assets/b584f76d3ff86700.svg) -} - -.theme-light .emptyWumpus_adb41f { - background: url(/assets/28fe27f3657e23ee.svg) -} - -@media (max-width: 1200px) { - .settingIcons_adb41f { - display:none - } - - .emptyStateSmall_adb41f { - flex-direction: column - } - - .emptyStateSmall_adb41f .emptyStateText_adb41f { - margin-inline-start:20px;text-align: center - } - - .emptyStateSmall_adb41f .emptyWumpus_adb41f { - margin-inline:0} - - .emptyStateSmall_adb41f .emptyStateButtons_adb41f { - justify-content: center - } -} - -@media (max-width: 980px) { - .platformCell_adb41f { - display:none - } -} - -@media (max-width: 860px) { - .lastPlayedCell_adb41f { - display:none - } -} - -@media (max-width: 780px) { - .rowWrapper_adb41f { - margin:0 8px - } - - .header_adb41f { - padding: 0 24px 8px - } -} - -.canvas__5dc9c { - height: 100%; - width: 100% -} - -.monitor__7eeec { - height: 24px; - width: 100% -} - -.overflowContainer__7eeec { - -webkit-mask: linear-gradient(90deg,transparent,#000 50%,#000 97%,transparent); - mask: linear-gradient(90deg,transparent,#000 50%,#000 97%,transparent); - overflow: hidden; - width: 100% -} - -.text__7eeec { - font-size: 10px; - line-height: 12px; - margin-inline-end:0;text-transform: uppercase; - white-space: nowrap; - width: 70px -} - -.title__7eeec { - color: currentColor; - font-weight: var(--font-weight-semibold) -} - -.rate__7eeec { - font-weight: var(--font-weight-medium) -} - -.sparkChart__7eeec { - height: 24px -} - -.theme-dark .rate__7eeec { - color: var(--primary-400) -} - -.table__49035 { - width: 100% -} - -.gameUpdates__49035 { - background-color: var(--background-base-lowest); - transform: translateZ(0) -} - -.headerRow__49035 { - display: flex; - padding: 20px 28px 12px -} - -.headerCell__49035 { - box-sizing: border-box; - margin-inline-end:40px} - -.headerCell__49035:last-child { - margin-inline-end:0} - -.diskProgress__49035 { - color: var(--text-link) -} - -.networkProgress__49035 { - color: var(--text-feedback-positive) -} - -.row__49035 { - border-bottom: 1px solid var(--border-subtle); - margin: 0 28px; - padding: 16px 0 10px -} - -.row__49035:last-child { - border-bottom: none -} - -.cell__49035 { - box-sizing: border-box; - padding-block:8px;padding-inline:20px 8px} - -.nameCell__49035 { - cursor: pointer; - flex-shrink: 0; - padding-inline-start:8px;width: 30% -} - -.progressCell__49035 { - width: 60% -} - -.actionsCell__49035 { - min-width: 98px; - padding-inline:12px;width: 10% -} - -.nameCellHeader__49035 { - font-size: 18px -} - -.nameCellText__49035 { - color: var(--text-strong); - font-weight: var(--font-weight-semibold); - line-height: 1.25; - margin-inline-start:20px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.progressCellHeader__49035 { - display: block; - padding-block:8px;padding-inline:20px 8px} - -.progressCellBody__49035 { - align-items: center; - display: flex; - padding-inline-start:13px} - -.progressCellText__49035 { - color: var(--text-muted); - font-size: 12px; - font-weight: var(--font-weight-medium); - margin-top: 4px; - text-transform: uppercase -} - -.infoIcon__49035 { - height: 24px; - opacity: .3; - width: 24px -} - -.actionButton__49035 { - margin-inline-start:8px} - -.actionButton__49035:first-child { - margin-inline-start:0} - -.actionButton__49035 .actionButtonContents__49035 { - display: flex -} - -.gameActionButton__49035 { - min-width: 72px -} - -.actionIcon__49035 { - height: 24px; - opacity: .6; - width: 24px -} - -.container__4a84a { - position: relative -} - -.scroller__4a84a { - background: var(--background-gradient-chat,var(--background-base-low)); - contain: layout size -} - -.wrapper__27106 { - flex-shrink: 0; - height: 50px; - position: relative; - width: 50px -} - -.svg__27106 { - transform: rotate(-86deg) -} - -.path__27106 { - fill: none; - stroke-width: 4px -} - -.background__27106 { - stroke: var(--text-muted) -} - -.foreground__27106 { - stroke-dashoffset: 0; - stroke-miterlimit: 10; - stroke-linecap: round; - transition: stroke .3s ease,stroke-dasharray .3s ease-out -} - -.usageInfo__27106 { - color: var(--text-default); - font-size: 12px; - font-weight: var(--font-weight-semibold); - inset-inline-start: 11px; - line-height: 12px; - overflow: hidden; - position: absolute; - text-align: center; - top: 19px; - width: 30px -} - -.wrapper_a6f654 { - margin-top: 60px -} - -.installationPath_a6f654 { - padding: var(--custom-game-install-locations-item-padding) 0 -} - -.installationPathEditing_a6f654 { - margin-inline:calc(var(--custom-game-install-locations-item-padding)*-1);padding: calc(var(--custom-game-install-locations-item-padding) - 1px) -} - -.separator_a6f654 { - margin-bottom: 20px; - margin-top: 20px -} - -.defaultIndicator_a6f654 { - align-items: center; - background-color: var(--primary-500); - border-radius: 3px; - color: var(--white); - display: flex; - font-size: 11px; - font-weight: var(--font-weight-bold); - height: 14px; - letter-spacing: .4px; - margin-inline-start:8px;padding: 0 4px; - text-align: center; - text-transform: uppercase -} - -.rowBody_a6f654 { - font-size: 14px; - height: 16px; - margin-top: 4px -} - -.buttonRowWrapper_a6f654 { - display: flex; - justify-content: flex-end; - margin-top: 20px -} - -.defaultLocationCheckbox_a6f654 { - margin-top: var(--space-12) -} - -.theme-dark .installationPath_a6f654 { - box-shadow: 0 1px 0 0 var(--primary-500) -} - -.theme-dark .rowBody_a6f654 { - color: var(--primary-400) -} - -.theme-light .installationPath_a6f654 { - box-shadow: 0 1px 0 0 var(--primary-200) -} - -.theme-light .rowBody_a6f654 { - color: hsl(var(--primary-400-hsl)/.6) -} - -.rowTitle_a6f654 { - color: var(--text-default) -} - -.scroller_fb04e1 { - background: var(--background-gradient-chat,var(--background-base-low)); - contain: layout size -} - -.body_fb04e1 { - margin: 0 auto; - max-width: 660px; - padding: 40px 0 -} - -.hiddenLibraryApplications_fb04e1 { - margin-top: 60px -} - -.hiddenLibraryApplications_fb04e1:first-child { - margin-top: 20px -} - -.hiddenLibraryApplicationContent_fb04e1 { - z-index: 1 -} - -.hiddenLibraryApplicationsTitle_fb04e1 { - margin-bottom: 8px -} - -.hiddenLibraryApplication_fb04e1 { - align-items: center; - border-top: 1px solid; - display: flex; - justify-content: space-between; - padding: 20px 0; - position: relative -} - -.hiddenLibraryApplication_fb04e1:last-of-type { - border-bottom: 1px solid -} - -.hiddenLibraryApplication_fb04e1:last-of-type:hover { - border-bottom-color: transparent -} - -.hiddenLibraryApplication_fb04e1:first-of-type,.hiddenLibraryApplication_fb04e1:hover,.hiddenLibraryApplication_fb04e1:hover+.hiddenLibraryApplication_fb04e1 { - border-top-color: transparent -} - -.hiddenLibraryApplication_fb04e1:hover .restoreButton_fb04e1 { - opacity: 1 -} - -.restoreButton_fb04e1 { - align-items: center; - background: var(--background-mod-normal); - border-radius: 50%; - cursor: pointer; - display: flex; - height: 24px; - inset-inline-end: -31px; - justify-content: center; - min-height: 0; - min-width: 0; - opacity: 0; - padding: 0; - position: absolute; - top: -12px; - transition: opacity .1s ease; - width: 24px -} - -.restoreButton_fb04e1:hover { - background: var(--background-base-lower) -} - -.restoreIcon_fb04e1 { - color: var(--icon-feedback-critical); - height: 11px; - width: 11px -} - -.applicationName_fb04e1 { - color: var(--text-strong); - font-size: 16px; - font-weight: var(--font-weight-semibold); - line-height: 22px -} - -.applicationSubText_fb04e1 { - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 16px; - margin-top: 2px -} - -.theme-dark .applicationSubText_fb04e1 { - color: var(--primary-400) -} - -.theme-dark .hiddenLibraryApplication_fb04e1 { - border-color: var(--primary-500) -} - -.theme-dark .hiddenLibraryApplication_fb04e1:before { - background: hsl(var(--primary-700-hsl)/.3); - border-color: var(--primary-700) -} - -.theme-dark .restoreButton_fb04e1 { - box-shadow: 0 0 0 1px hsl(var(--primary-700-hsl)/.6),0 1px 5px 0 var(--opacity-black-28) -} - -.theme-dark .restoreButton_fb04e1:hover { - box-shadow: 0 0 0 1px hsl(var(--primary-700-hsl)/.6),0 2px 10px 0 var(--opacity-black-20) -} - -.theme-light .applicationSubText_fb04e1 { - color: hsl(var(--primary-400-hsl)/.6) -} - -.theme-light .hiddenLibraryApplication_fb04e1 { - border-color: hsl(var(--primary-300-hsl)/.3) -} - -.theme-light .hiddenLibraryApplication_fb04e1:before { - background-color: hsl(var(--primary-100-hsl)/.6); - border-color: var(--primary-200) -} - -.theme-light .restoreButton_fb04e1 { - box-shadow: 0 0 0 1px hsl(var(--primary-300-hsl)/.3),0 2px 5px 0 var(--opacity-black-20) -} - -.theme-light .restoreButton_fb04e1:hover { - box-shadow: 0 0 0 1px hsl(var(--primary-300-hsl)/.3),0 2px 10px 0 var(--opacity-black-8) -} - -.container_ad5cdf { - display: flex; - flex: 1 1 auto; - flex-direction: column; - overflow: hidden -} - -.container__251d4 { - position: absolute -} - -@keyframes fadeOut__251d4 { - 0% { - opacity: 1; - visibility: visible - } - - 99% { - opacity: .01; - visibility: visible - } - - to { - opacity: 0; - visibility: hidden - } -} - -.upsell__251d4 { - opacity: 1; - position: absolute; - z-index: 1000 -} - -.upsell__251d4.hidden__251d4 { - animation: fadeOut__251d4 .3s forwards -} - -.backForwardButtons__63abb { - --custom-bf-button-radius: var(--custom-app-top-bar-item-radius); - --custom-bf-outer-padding: var(--space-6); - --custom-bf-inner-padding: 2px; - align-items: center; - display: flex; - flex-direction: row; - gap: 2px; - justify-content: center; - min-width: var(--custom-guild-list-width) -} - -.platform-osx .backForwardButtons__63abb { - margin-inline-start:calc(var(--custom-bf-outer-padding)*-1);min-width: unset -} - -.platform-win.density-compact .backForwardButtons__63abb { - --custom-bf-button-radius: 5px -} - -.backForwardButtons__63abb .button__63abb { - align-items: center; - color: var(--interactive-text-default); - display: flex; - height: var(--space-24); - justify-content: center -} - -.backForwardButtons__63abb .button__63abb.back__63abb { - border-end-start-radius: var(--custom-bf-button-radius); - border-start-start-radius: var(--custom-bf-button-radius); - padding-inline:var(--custom-bf-outer-padding) var(--custom-bf-inner-padding)} - -.backForwardButtons__63abb .button__63abb.forward__63abb { - border-end-end-radius: var(--custom-bf-button-radius); - border-start-end-radius: var(--custom-bf-button-radius); - padding-inline:var(--custom-bf-inner-padding) var(--custom-bf-outer-padding)} - -.backForwardButtons__63abb .button__63abb: not(.disabled__63abb):hover { - background-color:var(--background-mod-normal); - color: var(--interactive-text-hover); - cursor: pointer -} - -.backForwardButtons__63abb .button__63abb:active { - opacity: .7 -} - -.backForwardButtons__63abb .button__63abb.disabled__63abb { - opacity: .3 -} - -.navigationTooltip__63abb { - align-items: center; - display: flex; - flex-direction: column; - gap: 4px; - justify-content: center; - text-align: center -} - -.fastTravelButton_bbe3de { - align-items: center; - background-color: var(--control-secondary-background-default); - border: 1px solid var(--control-secondary-border-default); - border-radius: var(--radius-sm); - color: var(--control-secondary-text-default); - display: flex; - gap: var(--space-8); - height: 24px; - padding: 0 var(--space-12); - -webkit-app-region: no-drag; - cursor: pointer -} - -.fastTravelButton_bbe3de:hover { - background-color: var(--control-secondary-background-hover); - border-color: var(--control-secondary-border-hover); - color: var(--control-secondary-text-hover) -} - -.fastTravelButton_bbe3de:active { - background-color: var(--control-secondary-background-active); - border-color: var(--control-secondary-border-active); - color: var(--control-secondary-text-active) -} - -.navigationTooltip_bbe3de { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-4); - justify-content: center; - text-align: center -} - -.row__771da { - box-sizing: border-box; - cursor: pointer; - height: 50px; - margin-inline-start:8px;position: relative -} - -.leftIndicatorContainer__771da { - align-items: center; - display: flex; - height: 8px; - inset-inline-start: -8px; - position: absolute; - top: 18px; - width: 4px -} - -.channelIcon__771da { - flex-shrink: 0; - height: 12px; - width: 12px -} - -.offlineRow__771da { - opacity: .6 -} - -.offlineRow__771da:active,.offlineRow__771da:focus-within,.offlineRow__771da:hover { - opacity: 1 -} - -.rowInner__771da { - box-sizing: border-box; - height: 42px; - padding-block:var(--space-xxs) var(--space-xxs);padding-inline: 0; - width: 100%; - z-index: 0 -} - -.rowRecentlyAdded__771da { - animation: friendsWidgetRowRecentlyAdded__771da .95s ease-out 1 -} - -@keyframes friendsWidgetRowRecentlyAdded__771da { - 0% { - background: color-mix(in srgb,var(--brand-500) 28%,transparent) - } - - to { - background: transparent - } -} - -.avatar__771da { - margin-inline-end:12px} - -.username__771da { - color: var(--text-default) -} - -.username__771da,.usernameContainer__771da { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.usernameContainer__771da { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px -} - -.activityIconContainer__771da { - align-items: center; - display: flex; - flex-shrink: 0; - height: 32px; - justify-content: center; - width: 32px -} - -.rowActions__771da { - align-items: center; - display: inline-flex; - gap: var(--space-xxs) -} - -.badgesContainer__771da { - display: flex; - flex-wrap: wrap; - gap: 4px; - margin-top: 2px -} - -.friendVoiceStatus__771da { - gap: 8px -} - -.friendVoiceStatus__771da,.voiceStatusContainer__771da { - align-items: center; - display: flex; - flex-direction: row -} - -.voiceStatusContainer__771da { - gap: 4px -} - -.voiceStatusContainer__771da .voiceText__771da { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.friendVoiceStatusGuildContainer__771da { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px; - justify-content: flex-start -} - -.guildName__771da { - max-width: 100px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.voiceStatusIcon__771da { - height: 12px; - width: 12px -} - -.iconOnlyButton__068c6 { - align-items: center; - border: 1px solid transparent; - border-radius: var(--radius-sm); - cursor: pointer; - display: flex; - height: 30px; - justify-content: center; - width: 30px -} - -.iconOnlyButton__068c6:hover { - background-color: var(--control-icon-only-background-hover); - border: 1px solid var(--control-icon-only-border-hover) -} - -.iconOnlyButton__068c6:active { - background-color: var(--background-mod-muted) -} - -.iconOnlyButton__068c6 svg { - pointer-events: none -} - -.container__606e9 { - display: flex; - flex: 1; - flex-direction: column -} - -.container__606e9,.list__606e9 { - min-height: 0; - min-width: 0 -} - -.list__606e9 { - border-top: 1px solid hsla(240,4%,61%,.035); - flex: 1 1 auto; - overflow: auto -} - -.searchContainer__606e9 { - margin-top: -1px; - margin-inline-end:-1px;padding: 0 -} - -.searchContainer__606e9>div>div>div>div { - background: hsla(240,4%,61%,.04); - border: none!important; - border-radius: 0; - outline: none!important; - padding: 8px 12px -} - -.searchContainer__606e9>div>div>div>div:has(input:focus,textarea:focus) { - background: hsla(240,4%,61%,.08) -} - -.section__606e9 { - height: auto; - padding-block:var(--space-lg) var(--space-xxs);padding-inline: var(--space-md) var(--space-xxs) -} - -.chevronIcon__606e9 { - height: 12px; - margin-inline-start:var(--space-xxs);width: 12px -} - -.sectionCollapsible__606e9 { - align-items: center; - cursor: pointer; - display: flex -} - -.sectionCollapsible__606e9:hover { - color: var(--text-strong) -} - -.nonGroupSectionHeaderRow__606e9 { - align-items: center; - display: flex; - gap: var(--space-xxs); - justify-content: space-between -} - -.nonGroupSectionHeaderHideButton__606e9 { - align-items: center; - color: var(--text-muted); - display: flex; - justify-content: center; - margin-inline-end:8px;padding: var(--space-xxs) -} - -.nonGroupSectionHeaderHideButton__606e9:hover { - color: var(--text-strong) -} - -.emptyStateContainer__606e9 { - align-items: center; - display: flex; - height: 300px; - justify-content: center; - padding: 16px -} - -.container__672fc { - background-color: var(--background-surface-higher); - border: 1px solid hsla(240,4%,61%,.035); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-border),var(--shadow-high); - width: 350px -} - -.header__672fc { - align-items: center; - display: flex; - justify-content: space-between; - padding: 16px 16px 0 -} - -.activityText__672fc { - color: var(--text-subtle) -} - -.controlButtons__672fc { - display: flex; - gap: 8px; - margin-inline-start:8px} - -.searchContainer__672fc { - margin-top: -1px; - margin-inline-end:-1px;padding: 0 -} - -.searchContainer__672fc>div>div>div>div { - background: hsla(240,4%,61%,.04); - border: none!important; - border-radius: 0; - outline: none!important; - padding: 8px 12px -} - -.searchContainer__672fc>div>div>div>div:has(input:focus,textarea:focus) { - background: hsla(240,4%,61%,.08) -} - -.searchInput__672fc { - padding: 0 -} - -.list__672fc { - height: 70vh -} - -.section__672fc { - height: auto; - padding-block:var(--space-lg) var(--space-xxs);padding-inline: var(--space-md) var(--space-xxs) -} - -.reorderableGroupSection__672fc { - cursor: grab; - position: relative -} - -.reorderableGroupSection__672fc:active { - cursor: grabbing -} - -.draggingGroupSection__672fc { - opacity: .5 -} - -.dropTargetGroupSection__672fc { - background-color: var(--interactive-background-hover) -} - -.dropBeforeGroupSection__672fc:before { - inset-block-start: 0; - inset-inline: 0; - inset-inline-end: 0 -} - -.dropAfterGroupSection__672fc:after,.dropBeforeGroupSection__672fc:before { - background-color: var(--text-brand); - content: ""; - height: 2px; - position: absolute -} - -.dropAfterGroupSection__672fc:after { - inset-block-end: 0; - inset-inline: 0; - inset-inline-end: 0 -} - -.chevronIcon__672fc { - height: 12px; - margin-inline-start:var(--space-xxs);width: 12px -} - -.sectionCollapsible__672fc { - align-items: center; - cursor: pointer; - display: flex -} - -.sectionCollapsible__672fc:hover { - color: var(--text-strong) -} - -.emptyStateContainer__672fc { - align-items: center; - display: flex; - height: 300px; - justify-content: center; - padding: 16px -} - -.container__49ef0 { - border-bottom: 1px solid hsla(240,4%,61%,.035); - display: flex; - flex-direction: column; - gap: 0; - padding: 0 -} - -.profileBanner__49ef0 { - background-position: 50%; - background-size: cover; - height: 160px; - position: relative; - z-index: 1 -} - -.userBannerContainer__49ef0 { - align-items: center; - background-color: #131318; - border-bottom: 1px solid hsla(240,4%,61%,.035); - bottom: 0; - display: flex; - flex: 1; - justify-content: space-between; - min-width: 0; - padding-block-end:16px;padding-block-start:0;padding-inline:12px;position: absolute; - width: calc(100% - 24px); - z-index: 2 -} - -.userBannerContainer__49ef0:after { - background: linear-gradient(180deg,rgba(19,19,24,0),#131318 98.03%); - content: ""; - inset-inline: 0; - bottom: calc(100% - 1px); - height: 80px; - pointer-events: none; - position: absolute; - z-index: -1 -} - -.userBanner__49ef0 { - align-items: center; - display: flex; - flex: 1; - flex-direction: row; - gap: 12px; - justify-content: flex-start; - margin-top: -20px; - width: 100% -} - -.bannerContainer__49ef0 { - display: flex; - flex-direction: column; - height: 160px; - overflow: hidden; - position: relative -} - -.userBannerLeft__49ef0 { - align-items: center; - display: flex; - gap: var(--space-12); - min-width: 0 -} - -.avatar__49ef0 { - flex-shrink: 0 -} - -.userTextContainer__49ef0 { - display: flex; - flex-direction: column; - gap: var(--space-4); - min-width: 0 -} - -.activityText__49ef0,.secondaryLine__49ef0,.username__49ef0 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.secondaryLine__49ef0 { - align-items: center; - display: flex; - flex: 1; - gap: var(--space-4); - max-width: 216px; - min-width: 0 -} - -.secondaryIcon__49ef0 { - color: var(--green-360); - flex-shrink: 0 -} - -.secondaryText__49ef0 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.customStatusContainer__49ef0 { - align-items: center; - display: flex; - min-width: 0; - overflow: hidden -} - -.customStatusEmoji__49ef0 { - flex-shrink: 0; - height: 14px; - margin-inline-end:var(--space-4);width: 14px -} - -.duration__49ef0 { - color: var(--green-360); - flex-shrink: 0; - font-size: 12px; - font-weight: 400 -} - -.tabs__49ef0 { - border-top: none; - display: flex; - gap: 4px; - padding: 8px -} - -.tab__49ef0 { - background-color: transparent; - border: 1px solid transparent; - border-radius: 6px; - color: var(--text-muted); - cursor: pointer; - flex: 1; - min-width: 0; - padding: 6px 12px; - position: relative; - text-align: center -} - -.tab__49ef0:hover { - background-color: var(--background-mod-strong); - color: var(--interactive-text-hover) -} - -.tabSelected__49ef0 { - background-color: var(--background-mod-subtle); - color: var(--interactive-text-active) -} - -.unreadFavoritesDot__49ef0 { - background: var(--text-muted); - border: 2px solid #121214; - border-radius: var(--radius-round); - height: 6px; - inset-inline-end: -4px; - pointer-events: none; - position: absolute; - top: -4px; - width: 6px -} - -.channelNameContainer__711d3,.label__711d3 { - align-items: center; - display: flex; - gap: 4px -} - -.channelName__711d3 { - max-width: 140px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.recentChannelsMenu__711d3 { - background: #121214; - border: 1px solid hsla(240,4%,61%,.035); - border-radius: 16px; - box-shadow: var(--shadow-high); - max-height: 500px; - overflow: hidden; - padding: 8px; - width: 360px -} - -.recentChannelsMenuHeader__711d3 { - align-items: center; - display: flex; - flex: 1; - flex-direction: row; - margin-bottom: 2px; - padding: 0 12px -} - -.channelMenuSubContainer__711d3 { - align-items: center; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - gap: 8px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.icon__711d3 { - width: auto -} - -.guildIconWrapper__711d3 { - align-items: center; - background: var(--background-surface-highest); - display: flex; - height: 32px; - justify-content: center; - width: 32px -} - -.overflowCount__711d3 { - max-height: 22px; - max-width: 24px -} - -.channelItemContainer__711d3 { - align-items: center; - background-color: transparent; - border-radius: 8px; - cursor: pointer; - display: flex; - flex-direction: row; - gap: 12px; - height: 48px; - justify-content: space-between; - margin-bottom: 2px; - min-width: 0; - overflow: hidden; - padding: 6px; - padding-inline-start:0;transition: border-color .1s ease-in-out; - width: calc(100% - 16px) -} - -.channelItemContainer__711d3.channelItemHighlighted__711d3,.channelItemContainer__711d3:hover { - background-color: var(--background-mod-strong) -} - -.searchBarContainer__711d3 { - padding-bottom: 16px -} - -.emptyChannelItem__711d3 { - border-radius: 8px; - padding: 12px; - text-align: center -} - -.channelItemIcon__711d3,.emptyChannelItem__711d3 { - align-items: center; - display: flex; - justify-content: center -} - -.channelItemNameContainer__711d3 { - align-items: flex-start; - display: flex; - flex: 1; - flex-direction: column; - gap: 0; - min-width: 0 -} - -.channelItemName__711d3 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px; - max-width: 100% -} - -.channelIcon__711d3 { - min-width: 16px -} - -.channelItemNameText__711d3 { - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.channelItemAvatars__711d3 { - align-items: center; - display: flex; - gap: 4px; - padding-inline-end:4px} - -.channelList__711d3 { - max-height: 360px -} - -.channelListWrapper__711d3 { - display: flex; - flex-direction: column; - gap: 12px -} - -.avatarWrapper__69074 { - height: 100%; - width: 100% -} - -.root_df7f81 { - align-items: flex-start; - display: flex; - justify-content: flex-start; - overflow: visible; - position: relative -} - -.mainGuildImage_df7f81,.mainIcon_df7f81 { - border-radius: var(--radius-sm) -} - -.mainGuildImage_df7f81 { - display: block -} - -.acronym_df7f81 { - background-color: rgba(19,19,24,.95); - color: var(--white); - font-weight: var(--font-weight-medium); - line-height: .8em; - white-space: nowrap -} - -.acronym_df7f81,.guildIconContainer_df7f81 { - align-items: center; - display: flex; - justify-content: center -} - -.guildIconContainer_df7f81 { - bottom: 0; - inset-inline-end: 0; - overflow: visible; - position: absolute -} - -.guildIcon40_df7f81 { - height: 20px; - width: 20px -} - -.guildIcon32_df7f81 { - height: 16px; - width: 16px -} - -.guildIcon24_df7f81 { - height: 12px; - width: 12px -} - -.typingMaskWrapper_df7f81 { - overflow: visible; - position: relative -} - -.typingMaskSvg_df7f81 { - inset: 0; - overflow: visible; - position: absolute -} - -.typingIndicator_df7f81 { - pointer-events: none; - position: absolute -} - -.channelTypingStatusFill_df7f81 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - line-height: 0; - width: 100% -} - -.messagePreviewLine__7c4a2 { - align-items: center; - display: flex; - gap: var(--space-4); - overflow: hidden -} - -.messageContent__7c4a2 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.messageContent__7c4a2>strong { - font-weight: var(--font-weight-medium) -} - -.messageContentTrailingIcon__7c4a2 { - flex-shrink: 0 -} - -.colorTextFeedbackPositive__7c4a2 { - color: var(--text-feedback-positive) -} - -.expandedUsersContainer_e72588 { - display: flex; - flex-direction: column; - gap: 2px; - padding-inline-end:12px;padding-bottom: 8px; - padding-inline-start:56px} - -.expandedUsersRow_e72588 { - border-radius: var(--radius-sm); - height: 24px; - padding: 4px -} - -.expandedUsersRow_e72588:hover { - background-color: var(--interactive-background-hover) -} - -.expandedUsersRow_e72588:active { - background-color: var(--interactive-background-active) -} - -.facepileContainer_e72588 { - justify-content: center; - min-width: 24px; - position: relative -} - -.expandedUserRow_e72588,.facepileContainer_e72588 { - align-items: center; - display: flex; - min-height: 24px -} - -.expandedUserRow_e72588 { - gap: 6px -} - -.expandedUserName_e72588 { - flex-shrink: 0 -} - -.expandedUserName_e72588,.expandedUserNameContainer_e72588 { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.expandedUserNameContainer_e72588 { - flex: 1 -} - -.actionsRow_e72588,.expandedUserNameContainer_e72588 { - align-items: center; - display: flex; - gap: 4px -} - -.actionsRow_e72588 { - flex-direction: row -} - -.voiceIcons_e72588 { - align-items: center; - display: inline-flex; - flex-shrink: 0; - gap: 4px -} - -.voiceIcon_e72588 { - height: 14px; - width: 14px -} - -.container__50c27 { - background-color: #121214; - border: 1px solid hsla(240,4%,61%,.035); - border-radius: 12px; - box-shadow: var(--shadow-border) var(--shadow-high); - box-sizing: border-box; - display: flex; - flex: 1; - flex-direction: column; - height: 100%; - overflow: hidden; - position: relative; - width: 100%; - z-index: 2 -} - -.content__50c27 { - display: flex -} - -.content__50c27,.list__50c27 { - flex: 1 1 auto; - min-height: 0; - min-width: 0 -} - -.list__50c27 { - overflow: auto -} - -.container_e73f2a { - background-color: var(--background-surface-higher); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-border),var(--shadow-high); - height: 80vh; - width: 350px -} - -.gameCover__1a3d6 { - border-radius: 8px; - box-sizing: border-box; - height: var(--custom-game-cover-height,120px); - width: var(--custom-game-cover-width,90px) -} - -.coverContainer__1a3d6 { - background-color: var(--background-mod-muted); - box-shadow: 0 3px 12px 0 rgba(0,0,0,.16); - overflow: hidden; - position: relative; - z-index: 0 -} - -.coverContainer__1a3d6:before { - background: linear-gradient(150deg,hsla(0,0%,100%,.2),hsla(0,0%,100%,0) 40%); - border-radius: 8px; - box-shadow: inset 0 0 13px 0 hsla(0,0%,100%,.06),inset 0 0 0 1px var(--border-muted); - content: ""; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - transition: background 50ms ease-in; - width: 100%; - z-index: 1 -} - -.gameCoverImage__1a3d6 { - -o-object-fit: cover; - object-fit: cover; - -o-object-position: top; - object-position: top -} - -.fallback__1a3d6 { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - padding: 4px; - text-align: center; - word-break: break-word -} - -.clickable__1a3d6 { - cursor: pointer -} - -.clickable__1a3d6:hover:before { - background: linear-gradient(150deg,hsla(0,0%,100%,.2),hsla(0,0%,100%,0) 40%),linear-gradient(rgba(0,0,0,.2),rgba(0,0,0,.2)); - transition: background .15s ease-out -} - -.loadingCover__1a3d6 { -} - -.full-motion .hoverActiveEffect__1a3d6 { - transition: transform .15s ease-in-out; - will-change: transform -} - -.full-motion .hoverActiveEffect__1a3d6:hover { - transform: scale(1.0225) -} - -.full-motion .hoverActiveEffect__1a3d6:active { - transform: scale(.9775) -} - -.premiumLabel_e681d1 { - align-items: center; - display: flex; - justify-content: space-between; - pointer-events: none; - position: relative; - z-index: 3 -} - -.selected_e681d1 { - color: var(--white) -} - -.background_e681d1 { - border-radius: var(--radius-xs); - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 1 -} - -.selectedBackground_e681d1 { - background-color: var(--brand-500) -} - -.enable-forced-colors .selected_e681d1 { - color: Highlight -} - -.enable-forced-colors .selectedIcon_e681d1 { - fill: Highlight -} - -.enable-forced-colors .selectedBackground_e681d1 { - background-color: HighlightText -} - -.container__8a969 { - background-color: var(--background-mod-normal); - border-radius: var(--radius-sm); - flex-direction: row; - justify-content: flex-start; - padding: 12px -} - -.container__8a969,.iconContainer__8a969 { - align-items: center; - display: flex; - position: relative -} - -.iconContainer__8a969 { - flex-grow: 0; - flex-shrink: 0; - height: 40px; - justify-content: center; - margin-inline-end:12px;width: 40px -} - -.actionable__8a969 { - cursor: pointer -} - -.name__8a969 { - color: var(--text-strong); - font-size: 16px; - font-weight: var(--font-weight-bold); - line-height: 20px -} - -.description__8a969 { - color: var(--text-default); - font-size: 12px; - font-weight: var(--font-weight-medium); - line-height: 16px -} - -.flair__8a969 { - bottom: -16px; - inset-inline-start: calc(50% - 24px); - position: absolute -} - -.safetySettingsNotice__9536c { - align-items: center; - background: var(--background-base-lowest); - border-radius: var(--radius-xs); - display: flex; - gap: var(--space-8); - padding: var(--space-16) -} - -.safetySettingsNotice__9536c .closeButton__9536c { - cursor: pointer; - margin-inline-start:auto} - -.keybindFlexboxLayout_cbf20c { - align-items: baseline; - display: flex; - flex: 0 0 auto; - flex-direction: row; - flex-wrap: wrap; - gap: var(--space-4); - justify-content: flex-start; - max-width: 100%; - overflow: visible; - white-space: normal; - width: -moz-fit-content; - width: fit-content -} - -.keyCombo_cbf20c { - vertical-align: middle -} - -.keyCombo_cbf20c,.keyComboKey_cbf20c { - display: inline-block -} - -.container__4a98c { - gap: var(--space-32) -} - -.container__4a98c,.contentWrapper__4a98c { - display: flex; - flex-direction: column -} - -.contentWrapper__4a98c { - flex-grow: 1; - justify-content: space-between -} - -.header__4a98c { - margin-bottom: var(--space-16) -} - -.platformsWrap__4a98c { - display: flex; - flex-direction: column -} - -.platforms__4a98c { - align-items: start; - display: grid; - flex-wrap: wrap; - gap: var(--space-12); - justify-content: center -} - -.platformsDesktop__4a98c { - grid-template-columns: 1fr 1fr 1fr -} - -.platformsMobile__4a98c { - grid-template-columns: 1fr 1fr -} - -.platform__4a98c { - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - border: 1px solid var(--border-normal); - border-radius: var(--radius-md); - box-sizing: border-box; - display: flex; - flex-direction: column; - flex-shrink: 0; - height: auto; - min-width: 0; - padding: var(--space-12); - position: relative; - width: auto -} - -.full-motion .platform__4a98c { - transition: all .3s ease-in-out -} - -.platform__4a98c.active__4a98c { - background-color: var(--opacity-blurple-12); - border-color: var(--text-brand); - transform: none -} - -.platform__4a98c.active__4a98c .icon__4a98c { - opacity: 0 -} - -.platform__4a98c.active__4a98c .icon__4a98c.active__4a98c { - opacity: 1 -} - -.platformName__4a98c { - text-align: center -} - -.iconWrap__4a98c { - height: 100px; - position: relative; - width: 100px -} - -.icon__4a98c,.iconWrap__4a98c { - align-self: center -} - -.icon__4a98c { - background-color: var(--icon-subtle); - background-repeat: no-repeat; - background-size: contain; - height: 100%; - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - position: absolute; - top: 0; - transition: background-color .2s ease; - width: 100% -} - -.icon__4a98c,.icon__4a98c.active__4a98c { - background-image: none -} - -.icon__4a98c.active__4a98c { - background-color: var(--text-brand); - opacity: 0 -} - -.icon__4a98c.apple__4a98c { - -webkit-mask-image: url(/assets/3bacc5415aa9a561.svg); - mask-image: url(/assets/3bacc5415aa9a561.svg); - -webkit-mask-position: center; - mask-position: center -} - -.icon__4a98c.android__4a98c { - -webkit-mask-image: url(/assets/8d4fb350d405ae2d.svg); - mask-image: url(/assets/8d4fb350d405ae2d.svg); - -webkit-mask-position: bottom; - mask-position: bottom -} - -.icon__4a98c.windows__4a98c { - -webkit-mask-image: url(/assets/f80d470870ccfd01.svg); - mask-image: url(/assets/f80d470870ccfd01.svg) -} - -.icon__4a98c.linux__4a98c { - -webkit-mask-image: url(/assets/64dcefc70846922d.svg); - mask-image: url(/assets/64dcefc70846922d.svg) -} - -.icon__4a98c.ios__4a98c { - -webkit-mask-image: url(/assets/81672d3df281d7de.svg); - mask-image: url(/assets/81672d3df281d7de.svg) -} - -.downloadButton__4a98c { - padding-top: var(--space-4) -} - -@media (max-width: 486px) { - .platformsDesktop__4a98c,.platformsMobile__4a98c { - grid-template-columns:1fr - } -} - -.title_edbb22 { - align-items: center; - display: inline-flex; - gap: var(--space-8); - justify-content: center; - max-width: 50vw -} - -.title_edbb22.fastTravel_edbb22 { - align-items: center; - border-radius: var(--custom-app-top-bar-item-radius); - display: flex; - height: 24px; - padding: 0 8px; - -webkit-app-region: no-drag; - cursor: pointer -} - -.title_edbb22.fastTravel_edbb22:hover { - background-color: var(--background-mod-normal) -} - -.title_edbb22.fastTravel_edbb22 .fastTravelChevron_edbb22 { - margin-inline:-4px} - -.navigationTooltip_edbb22 { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-4); - justify-content: center; - text-align: center -} - -.icon_edbb22 { - height: 16px; - width: 16px -} - -.appIcon_edbb22 { - border-radius: 6px; - height: 20px; - width: 20px -} - -.guildIcon_edbb22 { - border-radius: var(--radius-xs) -} - -.iconWrapper_ede711 { - flex: 0 0 auto; - margin-inline-start:-2px;position: relative -} - -.clickableContainer_ede711,.iconWrapper_ede711 { - align-items: center; - display: flex; - justify-content: center -} - -.clickableContainer_ede711 { - cursor: pointer; - flex-direction: row; - gap: 4px; - height: 24px; - margin: 0 -6px; - padding: 0 6px -} - -.clickableContainer_ede711.withHoverHighlight_ede711:hover { - background-color: var(--background-mod-normal); - border-radius: var(--custom-app-top-bar-item-radius) -} - -.icon_ede711 { - cursor: pointer -} - -.unreadDot_ede711 { - background-color: var(--icon-strong); - border-radius: 100%; - bottom: 3px; - inset-inline-end: 3px; - position: absolute -} - -.unreadDot_ede711.refresh_sm_ede711 { - height: 5px; - width: 5px -} - -.unreadDot_ede711.sm_ede711 { - height: 4px; - width: 4px -} - -.clickable_c99c29 { - --custom-horizontal-padding: 4px; - align-items: center; - color: var(--icon-muted); - cursor: pointer; - display: flex; - height: 24px; - margin: 0 calc(var(--space-32)/2 - 10px - var(--custom-horizontal-padding)*2/2); - padding: 0 var(--custom-horizontal-padding); - position: relative -} - -.clickable_c99c29:hover { - color: var(--interactive-text-hover) -} - -.clickable_c99c29:active,.clickable_c99c29[aria-expanded=true] { - color: var(--interactive-text-active) -} - -.clickable_c99c29.withHighlight_c99c29:hover { - background-color: var(--background-mod-normal); - border-radius: var(--custom-app-top-bar-item-radius) -} - -.badge_c99c29 { - background-color: var(--background-feedback-notification); - border-radius: 8px; - bottom: 2px; - height: 8px; - position: absolute; - width: 8px -} - -.badge_c99c29,.badge_c99c29.smol_c99c29 { - inset-inline-end: 6px -} - -.badge_c99c29.smol_c99c29 { - bottom: 4px; - height: 6px; - width: 6px -} - -.button__85643 { - align-items: center; - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - height: var(--space-32); - justify-content: center; - margin: 0; - width: var(--space-32) -} - -.button__85643:hover { - color: var(--interactive-text-hover) -} - -.button__85643 svg { - height: var(--chat-input-icon-size); - width: var(--chat-input-icon-size) -} - -.button__85643.smallButton__85643 svg { - height: 18px; - padding: 3px 4px; - width: 18px -} - -.button__85643.smallButton__85643:hover svg { - background-color: var(--background-mod-normal); - border-radius: var(--custom-app-top-bar-item-radius) -} - -&.enable-forced-colors .button__85643 { - background-color: ButtonFace; - color: ButtonText -} - -.iframe_a62174 { - height: 100%; - width: 100% -} - -.wrapper_a62174 { - background-color: #000; - display: flex; - flex-direction: column; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - height: 100%; - z-index: 2 -} - -.wrapper_a62174.resizable_a62174 { - height: 65% -} - -.wrapper_a62174.noChat_a62174 { - height: 100%; - max-height: 100% -} - -.activityPanelContainer_a62174 { - display: flex; - flex: 1; - flex-direction: column -} - -.footer_a62174 { - align-items: center; - display: flex; - flex-direction: row; - gap: 16px; - justify-content: space-between; - padding: 12px 24px -} - -.activityContainer_a62174 { - align-items: center; - flex: 1; - flex-direction: row; - justify-content: center; - margin: 24px -} - -.activityContainerNoMargin_a62174 { - margin: 0 -} - -.footerButtons_a62174 { - flex: 1; - flex-direction: row; - gap: 16px; - justify-content: center -} - -.buttonSection_a62174,.footerButtons_a62174 { - align-items: center; - display: flex -} - -.buttonSection_a62174 { - background: var(--plum-23); - border: 1px solid hsl(var(--plum-11-hsl)/.06); - border-radius: 12px; - justify-content: space-between; - padding: 4px -} - -.inviteButtonIcon_a62174 { - height: 16px; - width: 16px -} - -.icon__402f3 { - align-items: center; - border-radius: 4px; - color: var(--interactive-text-default); - display: flex; - height: 24px; - justify-content: center; - padding: 4px; - width: 24px -} - -.icon__402f3:hover { - color: var(--interactive-text-active) -} - -.theme-dark .icon__402f3 { - background-color: var(--background-scrim) -} - -.theme-light .icon__402f3 { - background-color: var(--background-base-lower) -} - -@value cardHeight 320px;@value bannerImageHeight 146px;.container__4cb8a { - flex: 1; - position: relative -} - -.card__4cb8a { - display: flex; - flex-direction: column; - height: 320px -} - -.card__4cb8a:focus .iconMask__4cb8a,.card__4cb8a:hover .iconMask__4cb8a { - background-color: var(--background-base-lowest) -} - -.card__4cb8a:focus .contextMenu__4cb8a,.card__4cb8a:hover .contextMenu__4cb8a { - display: flex -} - -.card__4cb8a:focus .maximizeIcon__4cb8a,.card__4cb8a:hover .maximizeIcon__4cb8a { - opacity: 1 -} - -.header__4cb8a { - height: 146px; - position: relative -} - -.full-motion .loaded__4cb8a { - transition: opacity .2s,transform .2s ease-out -} - -.banner__4cb8a { - display: block; - height: 146px; - inset-inline-start: 0; - overflow: hidden; - position: absolute; - top: 0; - width: 100% -} - -.banner__4cb8a.loaded__4cb8a { - opacity: 1 -} - -.bannerImage__4cb8a { - height: 100%; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.icon__4cb8a { - bottom: -26px; - inset-inline-start: 12px; - position: absolute -} - -.iconMask__4cb8a { - padding: 4px -} - -.full-motion .iconMask__4cb8a { - transition: box-shadow .2s ease-out,transform .2s ease-out,background .2s ease-out -} - -.avatar__4cb8a { - height: 100%; - width: 100% -} - -.guildDetails__4cb8a { - display: flex; - flex: 1; - flex-direction: column; - padding: 28px 16px 16px -} - -.title__4cb8a { - align-items: center; - display: flex; - gap: 4px -} - -.guildBadge__4cb8a { - flex: 0 0 16px; - height: 16px; - margin-inline-start:-2px;width: 16px -} - -.guildName__4cb8a { - white-space: nowrap -} - -.description__4cb8a,.guildName__4cb8a { - overflow: hidden; - text-overflow: ellipsis -} - -.description__4cb8a { - display: -webkit-box; - margin: 4px 0 16px; - min-height: 0; - -webkit-line-clamp: 3; - -webkit-box-orient: vertical -} - -.memberDetails__4cb8a { - align-items: flex-end; - display: flex; - flex: 1; - gap: 16px -} - -.memberDetailsCount__4cb8a { - align-items: center; - display: flex; - gap: 4px; - min-width: 0 -} - -.memberDetailsText__4cb8a { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.infoDot__4cb8a { - border-radius: 50%; - flex-shrink: 0; - height: 8px; - width: 8px -} - -.presenceCountDot__4cb8a { - background-color: var(--green-360) -} - -.memberCountDot__4cb8a { - background-color: var(--primary-300) -} - -.spinnerContainer__4cb8a { - align-items: center; - bottom: 0; - display: flex; - justify-content: center; - position: absolute; - top: 0; - inset-inline: 0; - z-index: 1 -} - -.spinner__4cb8a { - background-color: var(--primary-800); - border-radius: 8px; - height: 48px; - width: 48px -} - -.contextMenu__4cb8a { - align-items: center; - display: none; - inset-inline-end: 16px; - justify-content: center; - position: absolute; - top: 130px -} - -.maximizeIcon__4cb8a { - inset-inline-end: 8px; - opacity: 0; - position: absolute; - top: 8px; - transition: opacity .1s ease-in-out -} - -.maximizeIcon__4cb8a:focus { - opacity: 1 -} - -.theme-dark .iconMask__4cb8a { - background-color: var(--background-mod-normal) -} - -.theme-light .iconMask__4cb8a { - background-color: var(--background-base-lower) -} - -.placeholder__4cb8a,.transitionGroup__4cb8a { - height: 320px; - width: 100% -} - -.transitionGroup__4cb8a { - display: flex; - position: relative -} - -&.theme-dark .iconMask__4cb8a,&.theme-light .iconMask__4cb8a { - background-color: var(--background-base-low) -} - -.card__4cb8a:hover .iconMask__4cb8a { - background-color: var(--background-surface-high) -} - -.sectionTitle_f0d60d { - padding-top: 24px; - padding-inline-start:32px;z-index: 1 -} - -.content_f0d60d { - -moz-column-gap: var(--space-md); - column-gap: var(--space-md); - display: grid; - grid-template-columns: repeat(auto-fill,minmax(280px,1fr)); - padding: var(--space-md) var(--space-lg); - row-gap: var(--space-md) -} - -.container_a76561 { - background-color: var(--card-background-default); - border: 1px solid var(--border-subtle); - border-radius: 8px; - box-sizing: border-box; - display: flex; - flex-direction: column; - height: var(--custom-hub-discovery-add-hub-card-card-height); - padding: 16px -} - -.iconContainer_a76561 { - align-items: center; - background-color: var(--green-360); - border-radius: 48px; - display: flex; - height: 48px; - justify-content: center; - width: 48px -} - -.header_a76561,.iconContainer_a76561 { - margin-bottom: 16px -} - -.gap_a76561 { - flex: 1 -} - -.enable-forced-colors .container_a76561 { - border: 1px solid CanvasText -} - -.disclaimer__04d5b { - display: flex; - flex-direction: column; - gap: 4px; - padding: 24px 32px; - white-space: pre-wrap -} - -.text__04d5b { - max-width: 400px -} - -.divider__04d5b { - background-color: var(--border-subtle); - height: 1px -} - -.categories_fe77d6 { - display: flex; - flex-direction: column; - gap: 2px -} - -.category_fe77d6 { - align-items: center; - border-radius: 4px; - cursor: pointer; - display: flex; - justify-content: space-between; - padding: 8px -} - -.category_fe77d6:hover { - background-color: var(--interactive-background-hover) -} - -.name_fe77d6 { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.count_fe77d6 { - margin-inline-start:8px} - -.selected_fe77d6 { - background-color: var(--interactive-background-active) -} - -.container__757e8 { - align-items: center; - display: flex; - gap: 8px -} - -.container__74f3e { - align-items: center; - background: var(--background-gradient-low,var(--background-base-lower)); - border-radius: 8px; - display: flex; - flex-direction: column; - height: 400px; - justify-content: center; - padding: 16px -} - -.image__74f3e { - background-repeat: no-repeat; - background-size: contain; - height: 145px; - margin-bottom: 16px; - width: 314px -} - -.header__74f3e { - margin-bottom: 8px -} - -.container__4d63b { - width: 180px -} - -@value sectionHeaderHeight 72px;@value sectionHeaderFiltersHeight 44px;@value sidebarWidth 170px;.heading_cbd375 { - display: flex; - flex-direction: column; - gap: 16px; - margin-top: 32px -} - -.headingTopbar_cbd375 { - margin-top: 24px -} - -.headingFilters_cbd375 { - align-items: center; - display: flex; - flex: 1; - gap: 8px; - height: 44px; - justify-content: space-between -} - -.masonryList_cbd375 { - display: grid; - grid-template-columns: 1fr auto -} - -.container_cbd375 { - background: var(--background-gradient-chat,var(--background-base-low)); - display: flex; - flex: 1; - min-height: 0; - position: relative -} - -.spinner_cbd375 { - align-items: center; - display: flex; - height: 120px -} - -.spinnerWithSidebar_cbd375 { - margin-inline-end:170px} - -.sidebar_cbd375 { - height: 100%; - inset-inline-end: 0; - margin-top: 0; - padding-inline-end:8px;position: absolute; - top: 0; - width: 170px; - z-index: 1 -} - -.sidebarContent_cbd375 { - position: sticky; - top: 0 -} - -.sidebarLanguageSelect_cbd375 { - align-items: center; - display: flex; - height: 50px; - justify-content: flex-end; - margin-bottom: 18px -} - -.container_d08938 { - display: flex; - flex: 1; - flex-direction: column; - height: 100%; - position: relative -} - -.clickable_d08938 { - cursor: pointer -} - -.search_d08938 { - background: var(--background-gradient-chat,var(--background-base-low)) -} - -.searchResultsHeader_d08938 { - flex: 1; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.clanDiscoveryToolbar_d08938 { - padding: 0 -} - -.rightAlignedToolbar_d08938 { - gap: 4px; - justify-content: flex-end -} - -.themeSection_f7cedc,.themeSelector_f7cedc { - align-items: flex-start; - display: flex -} - -.themeSelector_f7cedc { - flex-direction: row; - gap: var(--space-md) -} - -.themeOption_f7cedc { - align-items: center; - background: none; - border: none; - cursor: pointer; - display: flex; - flex-direction: column; - gap: var(--space-xs); - padding: 0; - transition: opacity .2s -} - -.themeOption_f7cedc:hover { - opacity: .8 -} - -.themeCircle_f7cedc { - border: 2px solid var(--border-normal); - border-radius: 50%; - height: 32px; - transition: border-color .2s; - width: 32px -} - -.themeCircleSelected_f7cedc { - border-color: var(--brand-500) -} - -.themeLabel_f7cedc { - color: var(--text-muted); - font-size: 11px; - font-weight: 500 -} - -.controlBar__04a02 { - flex-direction: column; - width: 100% -} - -.controlBar__04a02,.questInput__04a02 { - display: flex; - gap: var(--space-sm) -} - -.questInput__04a02 { - align-items: flex-start; - flex: 1; - flex-direction: row; - min-width: 600px -} - -.currentQuestInfo__04a02 { - flex: 1; - gap: var(--space-xs); - justify-content: flex-end -} - -.currentQuestInfo__04a02,.questName__04a02 { - align-items: center; - display: flex -} - -.questName__04a02 { - border: 1px solid var(--interactive-background-selected); - border-radius: var(--radius-sm); - gap: var(--space-sm); - padding: var(--space-sm) var(--space-sm) -} - -.questLabel__04a02 { - color: var(--interactive-text-default); - font-weight: 500 -} - -.questTitle__04a02 { - color: var(--interactive-text-active); - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - max-width: 400px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.divider__04a02 { - background: var(--interactive-background-selected); - height: 1px; - margin-block:var(--space-sm) var(--space-sm);margin-inline: 0; - width: 100% -} - -.controlsSection__04a02 { - display: flex; - flex-direction: column; - width: 100% -} - -.controlsHeading__04a02 { - color: var(--text-strong); - margin: 0 -} - -.controlButtons__04a02 { - display: flex; - flex-wrap: wrap; - gap: var(--space-sm); - justify-content: center -} - -.questSelectorContainer__04a02 { - align-items: flex-start; - display: flex; - flex: 1; - flex-direction: column; - gap: var(--space-sm); - justify-content: flex-end -} - -.questSelectorWrapper__04a02 { - display: flex; - flex-direction: column; - gap: 8px -} - -.copyInput__04a02 { - width: 100% -} - -.sharePopover__04a02 { - background: var(--background-surface-high); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-high); - padding: var(--space-sm); - width: 400px -} - -.divider__3b806 { - background: var(--interactive-background-selected); - height: 1px; - margin-block:var(--space-sm) var(--space-sm);margin-inline: 0; - width: 100% -} - -.wrapper__11054 { - background-color: var(--background-base-low); - border-radius: 0 0 8px 8px; - cursor: default; - padding: 12px -} - -.wrapper__11054:only-child { - border-radius: 8px -} - -.submenuIcon__11054 { - color: var(--interactive-text-default); - cursor: pointer -} - -.submenuIcon__11054:focus,.submenuIcon__11054:hover { - color: var(--interactive-text-hover) -} - -.submenuIcon__11054:active { - color: var(--interactive-text-active) -} - -.utils__11054 { - align-items: center; - display: flex; - justify-content: space-between; - margin-bottom: 4px -} - -.wrapperQuestAccepted__11054 .utils__11054 { - align-items: flex-start -} - -.heading__11054 { - align-items: center; - display: flex; - margin-bottom: 10px -} - -.headingGameTile__11054 { - border-radius: 4px; - flex: 0 0 auto; - height: 32px; - margin-inline-end:8px;width: 32px -} - -.headingCopy__11054 { - flex: 1 1 auto -} - -.instructions__11054 { - margin-bottom: 12px -} - -.ctas__11054 { - align-items: center; - display: flex; - gap: 12px -} - -.rewardTileWithInstructions__11054 { - margin-inline:-8px} - -.previewPage__8d6f9 { - align-items: center; - display: flex; - flex-direction: column; - gap: 12px; - padding: 0 20px; - place-content: center -} - -.errorIcon__8d6f9,.questsIcon__8d6f9 { - color: var(--text-strong); - height: 24px; - width: 24px -} - -.messageWrapper__8d6f9 { - margin-bottom: 12px; - padding: 12px -} - -.message__8d6f9 { - color: var(--text-default); - text-align: start; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.previewCard__8d6f9 { - gap: var(--space-sm); - padding: var(--space-md); - width: 450px -} - -.previewBackground__8d6f9,.previewCard__8d6f9 { - display: flex; - flex-direction: column -} - -.previewBackground__8d6f9 { - align-items: center; - background-color: var(--background-mod-normal); - border-radius: var(--radius-md); - justify-content: center; - padding-bottom: var(--space-md); - padding-top: var(--space-md); - width: 100% -} - -.heading__8d6f9 { - grid-row: 1/span 1 -} - -.heading__8d6f9,.questTile__8d6f9 { - grid-column: 2/span 1 -} - -.questTile__8d6f9 { - grid-row: 2/span 1 -} - -.questChannelCallHeaderWrapper__8d6f9>div { - inset-inline-start: auto; - margin-inline-start:0;position: relative; - top: auto -} - -.embedCard__8d6f9 { - align-self: flex-start; - min-width: 100%; - padding: 32px; - width: -moz-fit-content; - width: fit-content -} - -.barCard_d8a230 { - display: flex; - flex-direction: column; - max-width: 1200px; - position: relative; - z-index: 10 -} - -.barHeading_d8a230 { - margin-bottom: var(--space-8) -} - -.fixedBarContainer_d8a230 { - display: flex; - position: relative; - width: 100%; - z-index: 10 -} - -.fixedBarWrapper_d8a230 { - background-color: var(--background-base-lower); - border-radius: var(--radius-md); - height: 275px; - position: relative; - width: var(--custom-guild-sidebar-width); - z-index: 10 -} - -.container__5e434 { - display: flex; - height: 100%; - overflow: hidden; - position: relative; - width: 100%; - --custom-chat-input-margin-bottom: var(--space-xs) -} - -.downloadProgressCircle__5e434 { - height: 24px; - opacity: 1; - transition: opacity .2s ease; - width: 24px -} - -.guilds__5e434 { - flex-shrink: 0; - min-width: 0; - position: relative; - width: var(--custom-guild-list-width) -} - -.base__5e434 { - flex-grow: 1 -} - -.base__5e434,.sidebar__5e434 { - display: flex; - flex-direction: column; - overflow: hidden; - position: relative -} - -.sidebar__5e434 { - background: var(--background-base-lower); - background-color: unset; - flex: 0 0 auto; - min-height: 0; - min-width: 0; - width: var(--custom-guild-sidebar-width) -} - -.sidebar__5e434.hidden__5e434 { - display: none; - visibility: hidden; - width: 0 -} - -.sidebar__5e434:after { - background: var(--background-base-lowest); - content: ""; - inset-inline: 0 1px; - bottom: 0; - height: var(--space-8); - position: absolute; - z-index: 1 -} - -.sidebar__5e434.channelListHidden__5e434 { - width: var(--custom-guild-list-width)!important -} - -.sidebar__5e434.channelListHidden__5e434 .guilds__5e434 { - margin-bottom: 0 -} - -.sidebar__5e434.channelListHidden__5e434:after { - display: none -} - -.fullWidth__5e434 { - width: 100% -} - -.panels__5e434 { - flex: 0 0 auto; - z-index: 10; - --custom-panels-spacing: var(--space-xs); - background: var(--background-gradient-highest,var(--background-base-low)); - border: 1px solid var(--border-muted); - border-radius: var(--radius-sm); - bottom: var(--custom-panels-spacing); - box-sizing: border-box; - inset-inline-start: var(--custom-panels-spacing); - justify-content: space-between; - position: absolute; - width: calc(100% - var(--custom-panels-spacing)*2) -} - -.panels__5e434.disablePointersWhileSorting__5e434 { - pointer-events: none -} - -.content__5e434 { - align-items: stretch; - display: flex; - flex: 1 1 auto; - justify-content: flex-start; - min-height: 0; - min-width: 0 -} - -.activityPanel__5e434 { - border-bottom: 1px solid var(--border-subtle); - padding: calc(var(--custom-guild-list-padding) - var(--custom-panels-spacing) + 4px); - position: relative; - z-index: 2 -} - -.loader__5e434 { - align-items: center; - display: flex; - justify-content: center; - width: 100% -} - -.sidebarResizeHandle__5e434 { - background-color: transparent; - bottom: 0; - cursor: ew-resize; - display: initial; - inset-inline-end: 0; - position: absolute; - top: 0; - transform: scaleX(var(--custom-overdrag,1)); - transform-origin: right; - transition: background-color .2s ease; - transition-delay: .1s; - width: 4px; - z-index: 101 -} - -.sidebarResizeHandle__5e434:active,.sidebarResizeHandle__5e434:hover { - background-color: var(--background-mod-strong) -} - -.high-contrast-mode .sidebarResizeHandle__5e434:active,.high-contrast-mode .sidebarResizeHandle__5e434:hover { - background-color: var(--border-normal) -} - -@supports not ((grid-template-columns: subgrid) and (white-space-collapse:collapse)) { - .sidebar__5e434 { - display:flex; - flex-direction: row - } -} - -@supports (grid-template-columns: subgrid) and (white-space-collapse:collapse) { - .base__5e434 { - display:grid; - grid-template-areas: "titleBar titleBar titleBar" "guildsList notice notice" "guildsList channelsList page"; - grid-template-columns: [start] min-content [guildsEnd] min-content [channelsEnd] 1fr [end]; - grid-template-rows: [top] var(--custom-app-top-bar-height) [titleBarEnd] min-content [noticeEnd] 1fr [end] - } - - .base__5e434[data-fullscreen=true] { - grid-template-rows: [top] min-content [titleBarEnd] min-content [noticeEnd] 1fr [end] - } - - .platform-osx .base__5e434 { - padding-top: 1px - } - - .content__5e434 { - display: grid; - grid-column: start/end; - grid-row: titleBarEnd/end; - grid-template-columns: subgrid; - grid-template-rows: subgrid - } - - .page__5e434 { - grid-area: page; - overflow: auto - } - - .sidebar__5e434 { - display: grid; - grid-column: start/channelsEnd; - grid-row: titleBarEnd/end; - grid-template-columns: subgrid; - grid-template-rows: subgrid - } - - .sidebarList__5e434 { - grid-area: channelsList - } -} - -&.custom-theme-background .sidebar__5e434:after { - display: none -} - -.sidebarList__5e434 { - border-inline-start:1px solid var(--app-frame-border);border-top: 1px solid var(--app-frame-border); - container-type: inline-size; - display: flex; - flex: 0 0 auto; - flex-direction: column; - min-height: 0; - min-width: 0; - overflow: hidden; - width: calc(var(--custom-guild-sidebar-width) - var(--custom-guild-list-width) - 1px) -} - -.sidebarList__5e434:has([data-has-banner=true][data-banner-visible=true]) { - border-top: none -} - -.sidebarListRounded__5e434 { - -webkit-backdrop-filter: blur(0); - backdrop-filter: blur(0); - border-start-start-radius: var(--radius-md) -} - -.sidebar__5e434[data-collapsed=true] { - width: calc(var(--custom-guild-list-width) + 4px)!important -} - -.sidebar__5e434[data-collapsed=true] .sidebarList__5e434 { - border-inline-start:none;border-top: none -} - -.sidebar__5e434[data-collapsed=true] .sidebarResizeHandle__5e434 { - border-start-start-radius: var(--radius-lg); - margin-top: 12px -} - -.dragging__5e434 { - cursor: ew-resize!important -} - -.dragging__5e434 * { - pointer-events: none!important -} - -.draggingMin__5e434 { - cursor: e-resize!important -} - -.collapsing__5e434,.draggingMax__5e434 { - cursor: w-resize!important -} - -.page__5e434 { - box-sizing: border-box; - display: flex; - height: 100%; - min-width: 0; - position: relative; - width: 100% -} - -.high-contrast-mode .page__5e434,.theme-midnight .page__5e434 { - border-inline-start:1px solid var(--border-subtle)} - -.refresh-fast-follow-avatars .panels__5e434 { - --custom-panels-spacing: min(var(--space-xs),var(--space-8)) -} - -.custom-theme-background .panels__5e434 { - border-color: var(--app-frame-border) -} - -.enable-forced-colors .sidebar__5e434 { - border-inline-end:2px solid CanvasText} - -.enable-forced-colors .panels__5e434 { - border-top: 2px solid CanvasText -} - -.enable-forced-colors .sidebarResizeHandle__5e434:active,.enable-forced-colors .sidebarResizeHandle__5e434:focus,.enable-forced-colors .sidebarResizeHandle__5e434:hover { - background-color: ButtonText -} - -@container (max-width: 100px) { - .sidebarList__5e434>* { - display: none - } -} - -.embedCard_b5ecb2 { - align-self: center; - gap: 24px; - min-width: min(100%,687px); - padding: 32px; - width: -moz-fit-content; - width: fit-content -} - -.embedCard_b5ecb2,.embedSections_b5ecb2 { - display: flex; - flex-direction: column -} - -.embedSections_b5ecb2 { - gap: var(--space-sm) -} - -.embedHeading_b5ecb2,.embedHelper_b5ecb2 { - margin-bottom: 8px -} - -.helperText_b5ecb2 { - color: var(--text-muted); - font-size: 12px; - font-style: italic -} - -.embedWrapper_b5ecb2 { - display: flex; - justify-content: center -} - -.embedSection_b5ecb2 { - display: flex; - flex-direction: column; - gap: var(--space-sm) -} - -.embedSection_b5ecb2:hover { - border-color: var(--border-subtle) -} - -.fixedEmbed_b5ecb2 { - border-radius: 8px; - box-shadow: 0 2px 8px rgba(0,0,0,.1); - display: flex; - height: 100%; - overflow: hidden; - position: relative -} - -.desktopEmbed_b5ecb2 { - width: 687px -} - -.tabletEmbed_b5ecb2 { - width: 450px -} - -.mobileEmbed_b5ecb2 { - width: 300px -} - -.sizeLabel_b5ecb2 { - color: var(--text-muted); - font-size: 12px; - font-style: italic; - margin-inline-start:8px} - -.questsEmbed_b5ecb2 { - border-radius: 8px; - box-shadow: 0 2px 8px rgba(0,0,0,.1); - flex-shrink: 0; - height: 100%; - max-width: 687px; - min-width: 300px; - overflow: auto; - position: relative; - resize: horizontal; - width: 451px -} - -.heading_a10b43 { - color: var(--text-strong); - margin-bottom: var(--space-12) -} - -.previewDescription_a10b43 { - color: var(--text-muted); - font-size: 12px; - font-style: italic; - margin-bottom: var(--space-16) -} - -.memberListContainer_a10b43 { - border-radius: var(--radius-md); - display: flex; - margin-top: var(--space-16); - padding: var(--space-8) 0; - width: 100% -} - -.minimalMemberItem_a10b43 { - border-radius: var(--radius-sm); - transition: background-color .1s ease; - width: 240px -} - -.minimalMemberItem_a10b43:hover { - background-color: var(--interactive-background-hover) -} - -.minimalMemberItem_a10b43.selected_a10b43 { - background-color: var(--interactive-background-selected) -} - -.minimalMemberItem_a10b43>* { - cursor: pointer; - padding: var(--space-8) var(--space-12); - width: 100% -} - -.memberItemContent_a10b43 { - align-items: center; - display: flex; - gap: var(--space-12); - width: 100% -} - -.memberInfo_a10b43 { - display: flex; - flex: 1; - flex-direction: column; - min-width: 0 -} - -.container_ba8a2f { - display: flex; - flex-direction: column; - gap: var(--space-lg); - height: 100%; - padding: var(--space-md); - width: 790px -} - -.controlsBarContainer_ba8a2f { - margin-bottom: var(--space-sm); - width: 90% -} - -.progressBar_ba8a2f { - border-radius: 4px; - height: 8px; - overflow: hidden; - position: relative -} - -.progressBarFill_ba8a2f { - background: linear-gradient(90deg,var(--brand-500) 0,var(--brand-360) 100%); - border-radius: 4px; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.contentArea_ba8a2f { - border-radius: 8px; - flex: 1; - z-index: 1 -} - -.content_ba8a2f,.contentArea_ba8a2f { - overflow: visible; - position: relative -} - -.content_ba8a2f { - display: flex; - flex-direction: column; - gap: 16px; - height: 100% -} - -.popout__9047f { - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-high); - box-sizing: border-box; - padding: var(--space-16); - width: 242px -} - -.popoutHeading__9047f { - margin-bottom: var(--space-16) -} - -.groupDivider__61c5c { - background-color: var(--border-subtle); - border: none; - height: 1px; - margin: var(--space-12) calc(var(--space-8)*-1) var(--space-16) calc(var(--space-8)*-1) -} - -.submitWrapper__61c5c { - margin: calc(var(--space-8)*-1) -} - -@value twoColumnBreakpoint: 1340px;@value threeColumnBreakpoint: 1610px;.container__60f82 { - box-sizing: border-box; - display: grid; - gap: var(--space-lg); - justify-content: center; - padding: 20px; - width: 100% -} - -@media (min-width: 0) { - .container__60f82 { - grid-template-columns:repeat(1,minmax(336px,608px)) - } -} - -@media (min-width: 1340px) { - .container__60f82 { - grid-template-columns:repeat(2,minmax(406px,1fr)) - } -} - -@media (min-width: 1610px) { - .container__60f82 { - grid-template-columns:repeat(3,minmax(336px,1fr)); - max-width: 1310px - } -} - -@keyframes fadeIn__60f82 { - 0% { - opacity: .4 - } - - 50% { - opacity: .4 - } - - to { - opacity: 1 - } -} - -@keyframes glow__60f82 { - 0% { - box-shadow: 0 0 0 0 transparent - } - - 10% { - box-shadow: 0 0 0 0 transparent - } - - 33% { - box-shadow: 0 0 24.6px 0 #5865f2 - } - - 50% { - box-shadow: 0 0 24.6px 0 #5865f2 - } - - to { - box-shadow: 0 0 0 0 transparent - } -} - -@keyframes reflect__60f82 { - 0% { - opacity: 0; - transform: scale(0) rotate(45deg) - } - - 10% { - opacity: .25; - transform: scale(0) rotate(45deg) - } - - 11% { - opacity: .5; - transform: scale(4) rotate(45deg) - } - - 28% { - opacity: 0; - transform: scale(50) rotate(45deg) - } -} - -.questTile__60f82 { - overflow: hidden; - position: relative -} - -.questTile__60f82.selected__60f82 { - animation: glow__60f82 4s ease both; - opacity: 1 -} - -.questTile__60f82.selected__60f82:after { - background-color: #fff; - content: ""; - display: block; - height: 100%; - inset-inline-start: 0; - opacity: 0; - pointer-events: none; - position: absolute; - top: -300px; - transform: rotate(-45deg); - width: 30px; - z-index: 999 -} - -.full-motion .questTile__60f82.selected__60f82:after { - animation: reflect__60f82 4s ease-in-out -} - -.questTile__60f82.unselected__60f82 { - animation: fadeIn__60f82 4s ease both -} - -.spinner__60f82 { - align-items: center; - display: flex; - height: 100%; - justify-content: center -} - -.emptyStateContainer__60f82 { - display: flex; - flex-direction: column; - gap: 16px; - margin-inline:auto;max-width: 575px; - padding: 88px; - text-align: center -} - -.headingWrapper__57454 { - align-items: center; - display: flex; - gap: var(--space-16); - justify-content: space-between; - margin-bottom: 4px; - padding: var(--space-24) var(--space-20); - padding-bottom: 0 -} - -.headingControls__57454 { - align-items: center; - display: flex; - gap: var(--space-8) -} - -.filterSortOption__57454 { - border-radius: var(--radius-sm); - cursor: pointer; - margin-bottom: 0; - padding: var(--space-8)!important -} - -.filterSortOption__57454:hover { - background-color: var(--background-mod-subtle) -} - -.container__06199 { - align-items: center; - background-image: linear-gradient(0deg,#15193c,#000 33%); - display: flex; - height: 100%; - justify-content: center -} - -.asset__06199 { - aspect-ratio: 16/10; - inset: 0; - -o-object-fit: contain; - object-fit: contain; - position: absolute; - width: 100% -} - -.full-motion .asset__06199 { - transition-duration: transitionDuration; - transition-property: transform,opacity,filter; - transition-timing-function: transitionTimingFunction -} - -@value detailsOffset 20px;@value transitionDuration 0.2s;@value transitionTimingFunction ease-in-out;@value borderRadius 16px;.container__9192b { - aspect-ratio: 1; - border-radius: 16px; - max-width: 330px; - overflow: hidden; - position: relative; - width: 100% -} - -.image__9192b { - border-radius: 16px 16px 0 0; - height: 100%; - inset: 0; - -o-object-fit: cover; - object-fit: cover; - position: absolute; - width: 100% -} - -.full-motion .image__9192b { - transition-duration: .2s; - transition-property: transform,opacity,filter; - transition-timing-function: ease-in-out -} - -.assetBlurred__9192b { - border-radius: 16px 16px 0 0; - filter: blur(30px); - height: 100%; - inset: 0; - -o-object-fit: cover; - object-fit: cover; - position: absolute; - width: 100% -} - -.decoWrapper__9192b { - inset-inline-start: 50%; - position: absolute; - top: 24px; - transform: translateX(-50%); - transform-origin: top; - z-index: 2 -} - -.full-motion .decoWrapper__9192b { - transition-duration: .2s; - transition-property: transform,opacity,filter; - transition-timing-function: ease-in-out -} - -.overlay__9192b { - position: absolute; - top: 50%; - inset-inline: 0; - bottom: -50%; - z-index: 1 -} - -.full-motion .overlay__9192b { - transition: transform .2s ease-in-out -} - -.darkThemeGradient__9192b { - background: linear-gradient(to top,var(--primary-660) 0,hsl(var(--primary-660-hsl)/.5) 50%,hsl(var(--primary-660-hsl)/0) 100%) -} - -.lightThemeGradient__9192b { - background: linear-gradient(to top,hsl(var(--black-hsl)/.7) 0,hsl(var(--black-hsl)/.5) 50%,hsl(var(--black-hsl)/0) 100%) -} - -.details__9192b { - align-items: center; - inset-inline: 0; - bottom: 0; - display: flex; - flex-direction: column; - padding: 0 16px; - position: absolute; - text-align: center; - text-wrap: pretty; - transform: translateY(100%); - z-index: 10 -} - -.full-motion .details__9192b { - transition: transform .2s ease-in-out -} - -.logoContainer__9192b { - bottom: 20px; - display: flex; - inset-inline: 0; - justify-content: center; - position: absolute; - z-index: 5 -} - -.full-motion .logoContainer__9192b { - transition: transform .2s ease-in-out -} - -.logo__9192b { - height: auto; - max-height: 45px; - max-width: 140px; - -o-object-position: center; - object-position: center -} - -.logoWithCosponsor__9192b { - max-width: 70px -} - -.title__9192b { - margin-bottom: 4px -} - -.button__9192b { - margin-top: 16px; - width: 100% -} - -.hovered__9192b .details__9192b { - transform: translateY(-20px) -} - -.hovered__9192b .image__9192b { - filter: blur(8px); - opacity: .8 -} - -.hovered__9192b .image__9192b,.hovered__9192b .overlay__9192b { - transform: translateY(-80px) -} - -.hovered__9192b .decoWrapper__9192b { - transform: translateX(-50%) scale(.53) -} - -@value padding 24px;.gridContainer_f3e8a7 { - display: grid; - gap: 24px; - grid-template-columns: repeat(auto-fill,minmax(230px,1fr)); - justify-content: center; - padding: 24px -} - -.spinner_f3e8a7 { - align-items: center; - display: flex; - height: 100%; - justify-content: center -} - -.wrapper__42d45 { - box-sizing: border-box; - container-name: wrapper; - container-type: inline-size; - margin: var(--space-24) auto 0; - max-width: 1300px; - padding: 0 var(--space-20); - width: 100% -} - -.wrapperWithOldDiscoveryHeader__42d45 { - margin-top: var(--space-64) -} - -.contentWrapper__42d45 { - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - box-sizing: border-box; - display: flex; - isolation: isolate; - min-height: 500px; - overflow: hidden; - padding: var(--space-32); - position: relative -} - -@container wrapper (width < 725px) { - .contentWrapper__42d45 { - min-height: 580px - } -} - -.contentWrapperLoading__42d45 { - align-items: center; - border: none; - justify-content: center -} - -.content__42d45 { - position: relative; - z-index: 2 -} - -.heroBackground__42d45 { - z-index: 0 -} - -.backgroundOverlay__42d45,.backgroundOverlay__42d45:after,.backgroundOverlay__42d45:before,.heroAsset__42d45,.heroBackground__42d45 { - bottom: 0; - inset-inline-end: 0; - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.asset__42d45 { - bottom: 0; - height: auto; - inset-inline-end: 0; - inset-inline-start: auto; - max-width: 100%; - min-height: 100%; - min-width: 1234px; - -o-object-fit: cover; - object-fit: cover; - position: absolute; - top: 50%; - transform: translateY(-50%); - width: auto -} - -.heroAssetBlurred__42d45 .asset__42d45 { - filter: blur(var(--custom-image-blur-amount)) brightness(.5) saturate(120%) -} - -.heroAssetBlurredA__42d45 { - --custom-image-blur-amount: 10px; - -webkit-mask: linear-gradient(90deg,#000,transparent 30%); - mask: linear-gradient(90deg,#000,transparent 30%) -} - -.heroAssetBlurredB__42d45 { - --custom-image-blur-amount: 5px; - -webkit-mask: linear-gradient(90deg,#000 30%,transparent 70%); - mask: linear-gradient(90deg,#000 30%,transparent 70%) -} - -.backgroundOverlay__42d45 { - overflow: hidden; - z-index: 1 -} - -.backgroundOverlay__42d45:after { - inset-inline-end: auto; - -webkit-mask: linear-gradient(270deg,transparent,rgba(0,0,0,.83) 68%,#000); - mask: linear-gradient(270deg,transparent,rgba(0,0,0,.83) 68%,#000); - width: 600px -} - -.backgroundOverlay__42d45:after,.backgroundOverlay__42d45:before { - background-color: var(--background-base-lower); - content: "" -} - -.backgroundOverlay__42d45:before { - display: none; - height: 500px; - -webkit-mask: linear-gradient(180deg,transparent,#000); - mask: linear-gradient(180deg,transparent,#000); - top: auto -} - -@container wrapper (width < 725px) { - .heroAssetBlurred__42d45 { - display: none - } - - .backgroundOverlay__42d45:after { - -webkit-mask: linear-gradient(135deg,#000,rgba(0,0,0,.83) 15%,transparent 30%); - mask: linear-gradient(135deg,#000,rgba(0,0,0,.83) 15%,transparent 30%) - } - - .backgroundOverlay__42d45:before { - display: block - } -} - -.contentBody__42d45 { - max-width: 540px -} - -.logo__42d45 { - max-height: 56px; - max-width: 132px; - -o-object-fit: contain; - object-fit: contain; - width: 100% -} - -.title__42d45 { - text-transform: uppercase -} - -.subtitle__42d45 { - opacity: .8 -} - -.sponsoredTag__42d45 { - background-color: rgba(0,0,0,.5); - border-radius: 8px; - margin-inline-end:-8px;margin-top: -8px; - padding: 4px 8px -} - -@value headerBarHeight 60px;.container__955a3 { - border-top: 1px solid var(--app-frame-border); - box-sizing: border-box; - color: rgba(64,74,123,.7); - display: flex; - flex: 1; - flex-direction: column; - height: 100%; - position: relative -} - -.withoutTopBorder__955a3 { - border-top: none -} - -.dragRegion__955a3 { - display: none; - height: 60px; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.bannerAsset__955a3 { - grid-area: layer; - height: 100%; - max-height: 348px; - max-width: 1300px; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.orbsBannerAsset__955a3 { - -o-object-position: 75% 50%; - object-position: 75% 50% -} - -.oversizedImg__955a3 { - transform: scale(1.5) translate(-40px,20px) -} - -.bannerGradient__955a3 { - grid-area: layer; - height: 348px; - width: 100% -} - -.tabs__955a3 { - padding: 24px 20px 4px -} - -.tabs__955a3,.tabsExperimental__955a3 { - display: flex; - flex-flow: row nowrap; - justify-content: space-between -} - -.tabsExperimental__955a3 { - align-items: center; - flex: 1; - height: 100% -} - -.orbsTermsButton__955a3 { - margin-inline-start:10px} - -.orbsTermsButton__955a3:hover { - text-decoration: underline -} - -.theme-dark .bannerGradient__955a3 { - background: linear-gradient(180deg,transparent 49.93%,rgba(0,0,0,.2) 84.02%) -} - -.theme-dark .orbsGradient__955a3 { - background: linear-gradient(0deg,var(--brand-500) 0,transparent 33%); - opacity: .25 -} - -.theme-dark .bannerContainer__955a3 { - background-color: #0c1522 -} - -.theme-dark .orbsContainer__955a3 { - background-color: #000 -} - -.theme-dark .orbsTermsButton__955a3 { - color: #fff -} - -.theme-light .bannerGradient__955a3 { - background: linear-gradient(180deg,transparent 68%,#f2f3f5),radial-gradient(circle at center,transparent 45.68%,rgba(171,183,249,.61) 69.72%,#bec8ff 100%) -} - -.theme-light .orbsGradient__955a3 { - background: radial-gradient(circle at center,transparent 45.68%,rgba(171,183,249,.61) 69.72%,#bec8ff 100%) -} - -.theme-light .bannerContainer__955a3 { - background-color: #909ff1 -} - -.theme-light .orbsContainer__955a3 { - background-color: #fff -} - -.theme-light .orbsTermsButton__955a3 { - color: #000 -} - -.redirectNoticeBannerAsset__955a3 { - -o-object-position: bottom center; - object-position: bottom center -} - -.redirectNoticeBannerContainer__955a3 { - background-image: linear-gradient(180deg,#26007c,#00006a) -} - -.redirectNoticeContainer__955a3 { - align-items: center; - display: flex; - flex-direction: column; - gap: 12px; - margin: 0 auto; - max-width: 560px; - padding: 24px; - text-align: center -} - -.content__955a3 { - height: 100% -} - -.contentWithExtraPadding__955a3 { - padding-top: var(--space-48) -} - -.container_a592e1 { - border-top: 1px solid var(--app-frame-border); - display: flex; - flex-direction: column; - width: 100% -} - -.dragRegion_a592e1 { - display: none; - height: 60px; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.container__551b0 { - background: var(--background-gradient-high,var(--background-base-lowest)); - display: flex; - flex-direction: column; - height: 100% -} - -.header__551b0 { - align-items: center; - border-bottom: 1px solid var(--border-subtle); - box-shadow: none; - box-sizing: border-box; - display: flex; - height: var(--custom-channel-header-height); - padding: 16px -} - -.nav__551b0 { - display: flex; - flex: 1; - flex-direction: column; - gap: 2px; - padding: 8px -} - -.navItemIcon__551b0 { - height: 24px; - width: 24px -} - -.navItem__551b0,.navItemIcon__551b0 { - color: var(--interactive-text-default) -} - -.navItem__551b0 { - align-items: center; - border-radius: var(--radius-sm); - cursor: pointer; - display: flex; - gap: 12px; - padding: var(--space-sm) var(--space-xs) -} - -.navItem__551b0:focus-within,.navItem__551b0:hover { - background: var(--interactive-background-hover); - color: var(--interactive-text-hover) -} - -.navItem__551b0:focus-within .navItemIcon__551b0,.navItem__551b0:hover .navItemIcon__551b0 { - color: var(--interactive-text-hover) -} - -.navItem__551b0.selected__551b0,.navItem__551b0:active { - background: var(--interactive-background-active) -} - -.navItem__551b0.selected__551b0,.navItem__551b0.selected__551b0 .navItemIcon__551b0,.navItem__551b0:active { - color: var(--interactive-text-active) -} - -.refresh-fast-follow-distinct-borders .header__551b0 { - border-bottom-color: var(--app-frame-border) -} - -.image__5901e { - inset-inline-start: 0; - opacity: 0; - position: absolute; - top: 0; - transition: opacity .2s linear .2s -} - -.image__5901e.loaded__5901e { - opacity: 1 -} - -.confirmation__1051d { - align-items: center; - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: 16px; - display: flex; - flex-direction: column; - gap: 24px; - max-width: 400px; - padding: 24px; - text-align: center -} - -.iconWrapper__1051d { - background: var(--background-base-lowest); - border-radius: 50%; - padding: 12px -} - -.statusTextContainer__1051d { - display: flex; - flex-direction: column; - gap: 8px; - max-width: 100%; - overflow: hidden; - overflow-wrap: break-word -} - -.incompleteButtonsContainer__1051d { - display: flex; - flex-direction: column; - gap: 8px; - width: 100% -} - -.confirmationButtonRow__1051d { - display: flex; - flex-direction: row; - gap: 24px; - width: 100% -} - -.confirmationButton__1051d { - display: flex; - flex: 1; - width: auto -} - -.rejectionReasonLabel__1051d { - margin-inline-end:4px} - -.confirmationTooltipContents__1051d { - width: 100% -} - -.page__99a7e { - background-color: var(--background-surface-high); - box-sizing: border-box -} - -.contentWrapper__99a7e,.page__99a7e { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.contentWrapper__99a7e { - border-inline-start:1px solid var(--border-subtle);border-start-start-radius: var(--radius-md); - border-top: 1px solid var(--border-subtle); - position: relative -} - -.refresh-fast-follow-distinct-borders .contentWrapper__99a7e { - border-color: var(--app-frame-border) -} - -.dragRegion__99a7e { - display: none; - height: 100px -} - -.dragRegion__99a7e,.splash__99a7e { - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.splash__99a7e { - height: 100% -} - -.defaultGradient__99a7e { - background: linear-gradient(112deg,var(--background-base-lower) 0,var(--background-base-lowest) 100%) -} - -.defaultGradient__99a7e,.splashGradient__99a7e { - height: 100%; - position: absolute; - width: 100% -} - -.splashGradient__99a7e { - background: linear-gradient(180deg,hsl(var(--primary-800-hsl)/.5) 0,hsl(var(--primary-800-hsl)/1) 100%) -} - -.cover__99a7e { - -o-object-fit: cover; - object-fit: cover -} - -.container_cd1bf7 { - justify-content: center; - width: 100% -} - -.container_cd1bf7,.explainerContainer_cd1bf7 { - align-items: center; - display: flex -} - -.explainerContainer_cd1bf7 { - background-color: var(--background-base-low); - border-radius: 8px; - flex-direction: column; - gap: var(--space-32); - margin: 32px; - max-width: 500px; - padding: 32px; - position: relative -} - -.transparentBackground_cd1bf7 { - background-color: var(--transparent) -} - -.header_cd1bf7 { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-12) -} - -.outerWrapping_cd1bf7 { - border-start-end-radius: var(--radius-lg); - border-start-start-radius: var(--radius-lg); - margin: var(--space-24) -} - -.main_c08b38 { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - overflow: hidden; - padding: 80px 0; - position: relative; - width: 100% -} - -.fullBorderWithGradient_c08b38 { - background-color: var(--bg-animated-gradient-background-not-black); - border-radius: var(--radius-lg) -} - -.artwork_c08b38 { - inset-inline-start: 0; - top: 0 -} - -.artwork_c08b38,.gradient_c08b38 { - height: 100%; - position: absolute; - width: 100% -} - -.gradient_c08b38 { - background: linear-gradient(180deg,hsl(var(--primary-800-hsl)/.5) 0,hsl(var(--primary-800-hsl)/1) 100%) -} - -.content_c08b38 { - background-color: var(--modal-background); - border-radius: 12px; - flex-direction: column; - height: 640px; - max-height: 640px; - padding: 0 24px; - top: 50%; - transform: translate(-50%,-50%); - width: 800px -} - -.bottomCenterContent_c08b38,.content_c08b38 { - display: flex; - position: absolute; - z-index: 1 -} - -.bottomCenterContent_c08b38 { - align-items: center; - bottom: 16px; - text-align: center -} - -.cover_c08b38 { - -o-object-fit: cover; - object-fit: cover -} - -.container__7e486 { - align-items: center; - display: flex; - flex-direction: column; - margin-bottom: 32px -} - -.coverImageContainer__7e486 { - padding-bottom: calc(100%/var(--custom-guild-role-subscription-style-constants-cover-image-aspect-ratio)); - position: relative; - width: 100% -} - -.coverImage__7e486 { - height: 100%; - inset-inline-start: 0; - -o-object-fit: cover; - object-fit: cover; - position: absolute; - top: 0; - width: 100% -} - -.guildIconContainer__7e486 { - background-color: var(--background-base-low); - border-radius: 50%; - margin-bottom: 24px; - margin-top: -58px; - padding: 8px; - z-index: 1 -} - -.ctaTitle__7e486 { - color: var(--text-default); - margin-bottom: 8px; - margin-inline:16px;max-width: 640px; - text-align: center -} - -.ctaTitle__7e486 strong { - color: var(--text-strong); - font-weight: var(--font-weight-bold) -} - -.ctaSubtitle__7e486 { - margin-inline:16px;max-width: 640px; - text-align: center -} - -.pendingPlanChangeNotice__7e486 { - margin-top: 16px -} - -@media (max-width: 485px) { - .guildIconContainer__7e486 { - margin-bottom:8px - } -} - -.pendingPlanChangeNotice_f75db8 { - margin-top: 16px -} - -.backButtonInner_f75db8 { - align-items: center; - color: var(--text-link); - display: flex; - font-size: 16px -} - -.backButton_f75db8 { - margin: auto -} - -.errorPageContainer__5a176 { - background-color: var(--background-base-low); - display: flex; - flex-direction: column; - justify-content: stretch; - width: 100% -} - -.errorPageContent__5a176 { - display: flex; - flex: 1; - flex-direction: column; - justify-content: center; - padding: 16px -} - -.errorPageIllo__5a176 { - margin-bottom: 24px -} - -.errorPageTitle__5a176 { - margin-bottom: 8px -} - -.errorPagSubtitle__5a176,.errorPageTitle__5a176 { - text-align: center -} - -.container_e66c4d { - align-items: center; - background-color: var(--background-base-low); - display: flex; - flex-direction: column; - height: 100%; - justify-content: center; - padding: 0 32px; - text-align: center -} - -.content_e66c4d { - padding-bottom: 10% -} - -.header_e66c4d { - font-weight: var(--font-weight-bold) -} - -.container__808a1 { - display: flex; - flex: 1; - flex-direction: column -} - -.content__808a1 { - flex: 1; - min-height: 0; - position: relative; - width: 100% -} - -.content__808a1:before { - box-shadow: var(--elevation-low); - content: ""; - display: block; - height: 1px; - position: absolute; - top: -1px; - inset-inline: 0; - pointer-events: none; - z-index: 1 -} - -.scroller__808a1 { - align-items: center; - display: flex; - flex-direction: column; - height: 100% -} - -.scrollerContent__808a1 { - max-width: var(--custom-guild-role-subscriptions-overview-page-page-max-width); - position: relative; - width: 100% -} - -@media (max-width: 485px) { - #guild-role-subscription-overview-notice__808a1,.headerBar__808a1 { - display:none - } -} - -.container_ea45f4 { - background-color: var(--brand-500); - border-radius: 8px; - box-sizing: border-box; - padding: 16px; - width: 282px -} - -.container_ea45f4 .linkButtonContainer_ea45f4 { - padding-top: 16px -} - -.container_ea45f4 .linkButtonContainer_ea45f4 .linkButton_ea45f4 { - font-weight: var(--font-weight-bold); - margin: 0 auto -} - -.container_ea45f4 .content_ea45f4 { - color: var(--white); - margin: 8px 0 16px; - text-align: center -} - -.container_ea45f4 .pointer_ea45f4 { - border: 12px solid transparent; - border-top: 12px solid var(--brand-500); - height: 0; - inset-inline-start: 127px; - pointer-events: none; - position: absolute; - top: -24px; - transform: rotate(180deg); - width: 0 -} - -.container_e88f3f { - background-color: var(--background-base-lowest); - border-radius: var(--radius-sm); - box-shadow: var(--elevation-medium); - margin: var(--custom-channel-notice-padding) var(--custom-channel-notice-padding) 0; - padding: var(--custom-channel-notice-padding); - position: relative -} - -.close_e88f3f,.container_e88f3f a { - cursor: pointer -} - -.close_e88f3f { - color: var(--interactive-text-default); - height: var(--custom-channel-notice-icon-size); - inset-inline-end: var(--custom-channel-notice-padding); - position: absolute; - top: var(--custom-channel-notice-padding); - width: var(--custom-channel-notice-icon-size) -} - -.close_e88f3f:hover { - color: var(--interactive-text-hover) -} - -.closeIcon_e88f3f { - height: var(--custom-channel-notice-icon-size); - width: var(--custom-channel-notice-icon-size) -} - -.imageContainer_e88f3f { - display: flex; - margin-bottom: var(--custom-channel-notice-padding) -} - -.image_e88f3f { - width: 100% -} - -.message_e88f3f { - white-space: pre-wrap -} - -.title_e88f3f { - margin-bottom: 4px -} - -.title_e88f3f.live_e88f3f { - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - text-overflow: ellipsis -} - -.center_e88f3f { - text-align: center -} - -.noImageTitle_e88f3f { - margin-inline-end:20px} - -.button_e88f3f { - margin-top: 8px -} - -.header_e88f3f { - text-transform: uppercase -} - -.header_e88f3f,.participants_e88f3f { - margin-bottom: 8px -} - -.header_e88f3f,.participants_e88f3f,.pill_e88f3f { - align-items: center; - display: flex; - gap: 4px -} - -.pill_e88f3f { - background-color: var(--background-base-lower); - border-radius: var(--radius-md); - flex-grow: 0; - overflow: hidden; - padding: 4px 8px -} - -.pillIcon_e88f3f { - color: var(--text-default); - flex-shrink: 0 -} - -.pillLabel_e88f3f { - overflow: hidden; - text-overflow: ellipsis -} - -.locationContainer_e88f3f { - align-items: center; - display: flex; - flex-direction: row; - justify-content: flex-start; - white-space: nowrap -} - -.location_e88f3f { - overflow: hidden; - text-overflow: ellipsis -} - -.locationIcon_e88f3f { - color: var(--text-muted); - flex-shrink: 0; - margin-inline-end:4px} - -.avatar_e88f3f { - flex-shrink: 0 -} - -.key_e88f3f { - background-color: var(--control-overlay-primary-background-default); - color: var(--control-overlay-primary-text-default) -} - -.channelNotice__4c43d { - box-shadow: inset 0 -1px 0 var(--border-subtle); - padding: 16px; - position: relative; - text-align: center -} - -.channelNotice__4c43d a,.close__4c43d { - cursor: pointer -} - -.close__4c43d { - color: var(--interactive-text-default); - height: 18px; - inset-inline-end: 16px; - position: absolute; - top: 12px; - width: 18px -} - -.close__4c43d:hover { - color: var(--interactive-text-hover) -} - -.closeIcon__4c43d { - height: 18px; - width: 18px -} - -.title__4c43d { - margin-bottom: 8px -} - -.message__4c43d { - color: var(--text-default); - font-size: 14px; - line-height: 18px; - margin-top: 16px; - white-space: pre-wrap -} - -.message__4c43d .btn__4c43d { - margin: 16px auto 0 -} - -.imageContainer__4c43d { - display: flex -} - -.image__4c43d { - width: 100% -} - -.textBlock_e2d7b8 { - align-items: center; - display: flex; - flex-direction: row; - justify-content: flex-start; - margin: 0 auto -} - -.joinButton_e2d7b8 { - margin: 0 -} - -.eventName_e2d7b8 { - display: -webkit-box; - overflow: hidden; - text-overflow: ellipsis; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - margin: 0 -} - -.channelNotice_e2d7b8 { - border-bottom: 1px solid var(--border-subtle); - margin: 0; - padding: var(--space-xs) var(--space-md) -} - -.channelNotice_e2d7b8>*+* { - margin-top: 8px -} - -.singleLine_e2d7b8 { - white-space: nowrap -} - -.liveNowText_e2d7b8 { - margin-inline-start:4px;text-transform: capitalize -} - -.eventNameClickable_e2d7b8 { - cursor: pointer -} - -.eventNameClickable_e2d7b8:hover { - text-decoration: underline; - text-underline-position: under -} - -.channelName_e2d7b8 { - overflow: hidden; - text-overflow: ellipsis -} - -.dotDivider_e2d7b8 { - margin-inline:4px} - -.liveIndicator_e2d7b8 { - background-color: var(--green-360); - border-radius: 8px; - height: 8px; - width: 8px -} - -.startTime_e2d7b8 { - overflow: hidden; - text-overflow: ellipsis -} - -.stageIcon_e2d7b8 { - margin-inline-end:4px} - -.stageIcon_e2d7b8,.userNames_e2d7b8 { - color: var(--text-default) -} - -.userNames_e2d7b8 { - margin-inline-start:4px;overflow: hidden; - text-overflow: ellipsis -} - -.rsvpButton_e2d7b8 { - display: flex; - justify-content: center -} - -.buttonIcon_e2d7b8 { - margin-inline-end:4px} - -.closeIcon_e2d7b8 { - align-items: center; - color: var(--icon-muted); - cursor: pointer; - display: flex; - justify-content: center; - margin-inline-start:auto;width: 20px -} - -.eventDetails_e2d7b8 { - display: flex; - flex-direction: column; - gap: 4px -} - -.channelName_e2d7b8,.details_e2d7b8,.dotDivider_e2d7b8,.stageIcon_e2d7b8,.userNames_e2d7b8 { - color: var(--text-muted) -} - -.icon__58921 { - color: var(--text-default); - margin-inline-end:4px} - -.enable-forced-colors .container__6e9be { - background-color: Canvas -} - -.container__2637a { - align-items: stretch; - background: var(--background-gradient-high,var(--background-base-lowest)); - display: flex; - flex: 1 1 auto; - flex-direction: column; - justify-content: flex-start; - margin-inline-start:-1px;min-height: 0; - padding-bottom: calc(var(--custom-app-panels-height, 0) + var(--space-xs) - 16px); - position: relative -} - -.varUpsellTooltip__2637a { - max-width: 220px -} - -.varUpsell__2637a { - padding: 8px 0 -} - -.varUpsellTitle__2637a { - font-weight: var(--font-weight-bold); - margin-bottom: 8px -} - -.varUpsellBody__2637a { - text-align: center -} - -.tooltip__2637a p { - margin: 12px 0 -} - -.tooltip__2637a p:first-of-type { - margin-top: 0 -} - -.tooltip__2637a p:last-of-type { - margin-bottom: 0 -} - -.tooltipAsset__2637a { - display: block; - height: auto; - margin: 0 auto 16px -} - -.stageUpsellInline__2637a { - background-color: var(--opacity-white-8); - border-radius: 12px; - color: var(--white); - display: inline; - margin: 0 2px; - padding: 2px 6px -} - -.stageDescription__2637a { - color: var(--white); - margin: 8px 0 16px -} - -.hubContainer__2637a { - list-style: none; - margin-block:14px 0;margin-inline:0 8px} - -.lowerPriceBoostingTooltip__2637a { - width: 248px -} - -.lowerPriceBoostingTooltipHeader__2637a { - line-height: 0 -} - -.lowerPriceBoostingImg__2637a { - align-content: center; - display: block; - height: auto; - margin: 0 auto 16px -} - -.viewAllChannelsButton__2637a>div { - color: var(--text-muted) -} - -.botTag__2637a { - align-self: center; - margin-inline-start:4px} - -.clydeTooltipHeaderContainer__2637a { - align-items: center; - display: flex; - flex-direction: row; - justify-content: center; - margin-top: 12px -} - -.clydeTooltipAsset__2637a { - display: block; - height: 48px; - margin: 0 auto; - width: 48px -} - -.dropdownWrapperHighlighted__2637a { - position: relative -} - -.dropdownWrapperHighlighted__2637a:before { - background-image: var(--custom-premium-colors-premium-gradient-tier-2); - border-radius: 2px; - content: ""; - height: 24px; - inset-inline-end: -3px; - opacity: .4; - position: absolute; - top: -3px; - width: 24px; - z-index: 1 -} - -.theme-light .dropdownWrapperHighlighted__2637a:before { - opacity: .2 -} - -.dropdownButton__2637a { - color: var(--icon-subtle); - position: relative; - z-index: 2 -} - -.visibilityHidden__2637a { - visibility: hidden -} - -.dropdownButtonBannerVisible__2637a { - color: var(--white) -} - -.enable-forced-colors .container__2637a { - background-color: ButtonFace -} - -.container_a08a9d { - background-color: var(--card-background-default); - border-radius: 12px; - padding: 16px -} - -.card_a08a9d { - border-radius: 12px; - min-height: 80px; - padding: 16px -} - -.card_a08a9d,.content_a08a9d { - align-items: center; - display: flex; - justify-content: center -} - -.content_a08a9d { - flex-direction: row; - flex-wrap: wrap; - gap: 12px -} - -.emojiContainer_a08a9d { - flex-shrink: 0; - font-size: 32px; - line-height: 32px -} - -.statusText_a08a9d { - flex: 1; - text-align: center; - word-break: break-word -} - -.container__0919a { - background-color: var(--card-background-default); - padding: 16px -} - -.card__0919a,.container__0919a { - border-radius: 12px -} - -.card__0919a { - align-items: center; - display: flex; - flex-direction: row; - gap: 16px -} - -.gameIcon__0919a { - border-radius: 8px; - flex-shrink: 0; - height: 72px; - -o-object-fit: cover; - object-fit: cover; - width: 72px -} - -.gameInfo__0919a { - display: flex; - flex: 1; - flex-direction: column; - gap: 8px; - overflow: hidden -} - -.badge__0919a { - align-items: center; - background-color: var(--brand-experiment-15a); - border-radius: 4px; - display: inline-flex; - padding: 4px 8px; - width: -moz-fit-content; - width: fit-content -} - -.container__935b3 { - border-radius: 12px; - display: flex; - flex-direction: column; - overflow: hidden; - padding-bottom: 12px -} - -.titleContainer__935b3 { - background-color: var(--card-secondary-bg); - padding: 16px -} - -.title__935b3 { - color: var(--text-strong); - font-size: 18px; - font-weight: 600; - line-height: 24px -} - -.messagesContainer__935b3 { - padding-top: 16px -} - -.eventContainer__935b3,.messagesContainer__935b3 { - background-color: var(--card-background-default) -} - -.eventContainer__935b3 { - border-radius: 12px; - padding: 20px -} - -.eventTimeAndUser__935b3 { - align-items: center; - display: flex; - justify-content: space-between; - margin-bottom: 8px -} - -.eventTitle__935b3 { - margin-bottom: 4px -} - -.eventDescription__935b3 { - display: -webkit-box; - -webkit-line-clamp: 5; - -webkit-box-orient: vertical; - overflow: hidden; - text-overflow: ellipsis -} - -.eventDivider__935b3 { - background-color: var(--border-subtle); - border: none; - height: 1px; - margin: 12px 0; - width: 100% -} - -.eventInfoContainer__935b3 { - display: flex; - flex-direction: column; - gap: 4px -} - -.eventLocationRow__935b3 { - align-items: center; - display: flex; - gap: 8px -} - -.eventIcon__935b3 { - flex-shrink: 0 -} - -.eventLocationText__935b3 { - display: -webkit-box; - overflow: hidden; - text-overflow: ellipsis; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical -} - -.icymiMessageWrapper__7d14e { - padding-inline-start:64px} - -.icymiMessageWrapper__7d14e .avatar { - inset-inline-start: 16px -} - -.icymiMessageWrapper__7d14e .avatarDecoration { - inset-inline-start: 16px; - transform: translate(-10%,-10%) -} - -.container_df91b4 { - cursor: pointer -} - -.container_df91b4,.header_df91b4 { - display: flex; - flex-direction: row -} - -.header_df91b4 { - flex-grow: 1; - gap: 12px -} - -.avatar_df91b4 { - border-radius: 50%; - flex-shrink: 0; - height: 50px; - width: 50px -} - -.headerInfo_df91b4 { - display: flex; - flex-direction: column; - flex-grow: 1; - flex-shrink: 1; - gap: 4px; - justify-content: center -} - -.titleRow_df91b4 { - justify-content: space-between -} - -.titleLeft_df91b4,.titleRow_df91b4 { - align-items: center; - display: flex; - flex-direction: row -} - -.titleLeft_df91b4 { - flex-grow: 0; - flex-shrink: 1; - gap: 8px -} - -.title_df91b4 { - color: var(--text-strong); - font-size: 16px; - font-weight: 600; - line-height: 20px; - overflow: hidden; - text-overflow: ellipsis -} - -.timestamp_df91b4,.title_df91b4 { - white-space: nowrap -} - -.timestamp_df91b4 { - color: var(--text-muted); - flex-shrink: 0; - font-size: 12px; - font-weight: 400; - line-height: 16px -} - -.optionsButton_df91b4 { - align-items: center; - background: none; - border: none; - color: var(--icon-muted); - cursor: pointer; - display: flex; - flex-shrink: 0; - font-size: 20px; - justify-content: center; - line-height: 20px; - padding: 4px -} - -.optionsButton_df91b4:hover { - color: var(--interactive-text-hover) -} - -.subtitle_df91b4 { - flex-direction: row; - flex-wrap: wrap -} - -.subtitle_df91b4,.subtitleText_df91b4 { - align-items: center; - display: flex -} - -.subtitleText_df91b4 { - color: var(--text-subtle); - font-size: 14px; - font-weight: 500; - gap: 4px; - line-height: 18px -} - -.subtitleText_df91b4:not(:last-child) { - margin-inline-end:4px} - -.channelIcon_df91b4 { - color: var(--text-subtle); - flex-shrink: 0; - margin-inline-end:-3px} - -.container_a03ecd { - border-radius: 16px; - color: var(--text-strong); - cursor: pointer; - display: flex; - flex: 1; - flex-direction: column; - padding: 16px; - transition: background-color .1s ease -} - -.messageContentContainer_a03ecd { - background-color: var(--card-background-default); - border-radius: 16px; - margin-top: 16px -} - -.container_a03ecd:hover { - background-color: var(--interactive-background-active) -} - -.container_fb95a5 { - max-width: 720px; - padding: 16px; - position: relative; - width: 100% -} - -.container_fb95a5,.scrollContainer_fb95a5 { - display: flex; - flex-direction: column -} - -.container__78320 { - height: 100% -} - -.container__78320,.content__78320 { - display: flex; - flex-direction: column -} - -.content__78320 { - align-items: center; - flex: 1; - overflow-x: hidden; - overflow-y: auto -} - -.preAlphaText__78320 { - color: var(--text-brand); - font-size: var(--size-12); - font-weight: var(--font-weight-semibold) -} - -.chat_a44415 { - align-items: stretch; - display: flex; - flex: 1 1 auto; - flex-direction: column; - justify-content: stretch; - min-height: 0; - min-width: 0; - position: relative -} - -.chat_a44415:before { - box-shadow: var(--elevation-low); - content: ""; - display: block; - height: 1px; - position: absolute; - top: -1px; - inset-inline: 0; - pointer-events: none; - z-index: 1 -} - -.hamBanner__6a41e { - align-items: center; - background-color: var(--background-mod-normal); - display: flex; - padding: 10px 16px -} - -.hamBannerButton__6a41e { - flex-shrink: 0; - margin-inline-start:12px} - -.emptyStateContainer__65428 { - align-items: center; - bottom: 0; - display: flex; - position: absolute; - top: 0; - inset-inline: 0; - justify-content: center -} - -.messageRequestItem_abb9ad { - box-sizing: border-box; - cursor: pointer; - display: flex; - flex-direction: row; - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px; - overflow: hidden; - padding: 16px 10px -} - -.messageRequestItem_abb9ad:not(.firstItem_abb9ad) { - border-top: 1px solid var(--border-subtle) -} - -.messageRequestItem_abb9ad.active_abb9ad+.messageRequestItem_abb9ad,.messageRequestItem_abb9ad:hover+.messageRequestItem_abb9ad { - border-color: transparent -} - -.messageRequestItem_abb9ad.active_abb9ad,.messageRequestItem_abb9ad:hover { - background: var(--interactive-background-hover); - border-color: transparent; - border-radius: 8px; - box-shadow: none -} - -.messageContent_dbf24f { - color: unset; - font-size: 16px!important; - height: 18px; - line-height: 18px!important; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - word-break: break-word -} - -.messageContent_dbf24f .emoji { - height: 18px -} - -.messageReplacement_dbf24f { - font-style: italic -} - -.messageContentIcon_dbf24f { - flex-shrink: 0; - margin-inline-start:4px} - -.messageFocusBlock_dbf24f { - min-width: 0 -} - -.avatar_b8dbac { - flex-shrink: 0; - margin-block:0;margin-inline:0 12px} - -.userPreview_b8dbac { - display: flex; - flex-direction: column; - margin-inline-end:var(--space-16);min-width: 25%; - overflow: hidden -} - -.container_b8dbac { - align-items: center; - display: flex; - justify-content: space-between; - width: 100% -} - -.userContainerWithPreview_b8dbac { - align-items: center; - display: flex; - gap: var(--space-8); - justify-content: flex-start -} - -.tagContainer_b8dbac { - align-items: flex-end; - display: flex; - flex-shrink: 1; - margin-top: 2px; - overflow: hidden -} - -.username_b8dbac { - color: var(--text-strong); - flex-grow: 1; - flex-shrink: 0; - font-weight: var(--font-weight-semibold); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.discriminator_b8dbac { - display: none -} - -.globalName_b8dbac { - color: var(--text-muted) -} - -.messagePreview_b8dbac { - display: flex; - padding-top: var(--space-4) -} - -.timestampWithPreview_b8dbac { - flex-shrink: 1; - white-space: nowrap -} - -.mutualGuildsContainer_b8dbac { - align-items: center; - display: flex; - gap: var(--space-4); - padding-top: var(--space-4) -} - -.mutualGuilds_b8dbac { - flex-shrink: 1 -} - -@media (min-width: 900px) { - .mutualGuilds_b8dbac { - align-items:center; - display: flex - } -} - -.container__24368 { - align-items: center; - display: flex; - justify-content: space-between; - width: 100% -} - -.userPreview__24368 { - display: flex; - flex-direction: column; - margin-inline-end:16px;overflow: hidden -} - -.messagePreview__24368 { - display: flex; - padding-top: 4px -} - -.avatar__24368 { - flex-shrink: 0; - margin-block:0;margin-inline:0 12px} - -.userContainer__24368 { - display: flex; - flex-direction: column -} - -.userContainerWithPreview__24368 { - align-items: flex-end; - display: flex; - justify-content: flex-start -} - -.tagContainer__24368 { - align-items: flex-end; - display: flex; - margin-top: 2px; - overflow: hidden -} - -.username__24368 { - color: var(--text-strong); - font-weight: var(--font-weight-semibold); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.discriminator__24368 { - display: none -} - -.timestampWithPreview__24368 { - margin-inline-start:8px} - -.timestamp__24368 { - margin-inline-start:0} - -.actions__24368 { - display: flex; - flex: 0 0 auto; - margin-inline-start:auto} - -.button__24368:not(:last-child) { - margin-inline-end:16px} - -.container__0b7fc { - align-items: center; - display: flex; - justify-content: space-between; - width: 100% -} - -.userPreview__0b7fc { - display: flex; - flex-direction: column; - margin-inline-end:16px;overflow: hidden -} - -.messagePreview__0b7fc { - display: flex; - padding-top: 4px -} - -.avatar__0b7fc { - flex-shrink: 0; - margin-block:0;margin-inline:0 12px} - -.userContainerWithPreview__0b7fc { - align-items: flex-end; - display: flex; - justify-content: flex-start -} - -.tagContainer__0b7fc { - align-items: flex-end; - display: flex; - margin-top: 2px; - overflow: hidden -} - -.username__0b7fc { - color: var(--text-strong); - flex-grow: 0; - flex-shrink: 1; - font-weight: var(--font-weight-semibold); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.discriminator__0b7fc { - display: none -} - -.timestampWithPreview__0b7fc { - flex-grow: 1; - flex-shrink: 0; - margin-inline-start:8px} - -.actions__0b7fc { - display: flex; - flex: 0 0 auto; - gap: var(--space-8); - margin-inline-start:auto} - -.selected_dcc822 { - background-color: var(--interactive-background-selected); - border-radius: 8px -} - -.selected_dcc822,.siblingSelected_dcc822 { - border-bottom: none -} - -.list_dcc822 { - padding: 0 24px -} - -.sectionTitle_dcc822 { - align-items: center; - display: flex; - margin-inline-start:10px;margin-bottom: 10px -} - -.titleDivider_dcc822 { - margin: 0 8px -} - -.container_f391e3 { - align-items: stretch; - background-color: var(--background-gradient-chat,var(--background-base-lower)); - border-top: 1px solid var(--app-frame-border); - display: flex; - flex: 1 1 auto; - flex-direction: column; - justify-content: stretch; - min-height: 0; - min-width: 0; - overflow: hidden; - position: relative -} - -.container_f391e3:before { - display: none -} - -.item_f391e3 { - align-items: center; - display: flex; - justify-content: center; - text-align: center; - -webkit-app-region: no-drag -} - -.content_f391e3 { - display: flex; - flex-direction: column; - height: 100%; - overflow: hidden; - padding-bottom: 0; - position: relative -} - -.content_f391e3:before { - box-shadow: var(--elevation-low); - content: ""; - display: block; - height: 1px; - position: absolute; - top: -1px; - inset-inline: 0; - pointer-events: none; - z-index: 1 -} - -.list_f391e3 { - padding: 0 24px -} - -.sectionTitle_f391e3 { - align-items: center; - display: flex; - margin-inline-start:10px;margin-bottom: 10px -} - -.sectionBody_f391e3 { - margin-top: 10px -} - -.selected_f391e3 { - background-color: var(--interactive-background-selected); - border-radius: 8px -} - -.selected_f391e3,.siblingSelected_f391e3 { - border-bottom: none -} - -.messageRequestCoachmark_f391e3 { - text-align: center -} - -.titleDivider_f391e3 { - margin: 0 8px -} - -.clearAllButton_f391e3 { - display: inline-block; - margin: 0; - min-width: 48px; - padding: 0 -} - -.wrapper__123e1 { - align-items: center; - background-color: var(--background-base-low); - display: flex; - flex-direction: column; - height: 100%; - justify-content: center; - width: 100% -} - -.textContainer__123e1 { - box-sizing: border-box; - margin-inline:auto;max-width: 800px; - width: 100%; - z-index: 1 -} - -.heading__123e1 { - text-wrap: balance -} - -.heading__123e1,.text__123e1 { - text-align: center -} - -.inset_bf1984,.outer_bf1984 { - border-radius: 4px -} - -.outer_bf1984 { - background-color: var(--background-surface-high) -} - -.padded_bf1984 { - padding: 16px -} - -.padded_bf1984.inset_bf1984 { - padding: 12px -} - -.interactive_bf1984 { - cursor: pointer -} - -.inset_bf1984 { - background-color: var(--background-mod-subtle) -} - -.outer_bf1984.active_bf1984,.outer_bf1984.interactive_bf1984:hover { - background-color: var(--background-surface-higher) -} - -.noBackground_bf1984 { - background-color: unset -} - -.card__2eab1 { - text-align: center -} - -.header__2eab1 { - margin-bottom: 4px -} - -.buttonContainer__2eab1 { - display: flex; - flex-direction: column; - margin-top: 16px -} - -.buttonContainer__2eab1>* { - margin-bottom: 8px -} - -.buttonContainer__2eab1>:last-child { - margin-bottom: 0 -} - -.outer_ac6414 { - position: relative -} - -.inner_ac6414 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.wrapper_e45a82 { - align-items: center; - justify-content: flex-end -} - -.partyMembers_e45a82,.wrapper_e45a82 { - display: flex -} - -.partyMember_e45a82,.partyMembers_e45a82,.wrapper_e45a82 { - height: var(--custom-party-avatars-avatar-diameter) -} - -.partyMember_e45a82 { - display: inline-block; - margin-inline-start:-2px;-webkit-mask: url(/assets/eae6388e2d5a721a.svg); - mask: url(/assets/eae6388e2d5a721a.svg); - -webkit-mask-size: 100%; - mask-size: 100%; - mask-type: luminance; - width: var(--custom-party-avatars-avatar-diameter) -} - -.partyMemberKnown_e45a82 { - cursor: pointer -} - -.partyMember_e45a82:first-child { - margin-inline-start:0} - -.partyMember_e45a82:last-child,.partyMember_e45a82:last-child>.partyMember_e45a82 { - -webkit-mask: none; - mask: none -} - -.partyMemberEmpty_e45a82,.partyMemberUnknown_e45a82 { -} - -.partyMemberUnknown_e45a82 { - align-items: center; - border-radius: 50%; - display: inline-flex; - height: 24px; - justify-content: center; - width: 24px -} - -.partyMemberUnknownIcon_e45a82 { - border-radius: 50%; - color: var(--text-muted); - height: 16px; - width: 16px -} - -.partyMemberOverflow_e45a82 { - align-items: center; - background-color: var(--background-base-lower); - border-radius: 12px; - color: var(--text-default); - display: flex; - font-size: 14px; - font-weight: var(--font-weight-semibold); - height: var(--custom-party-avatars-avatar-diameter); - justify-content: center; - line-height: 16px; - margin-inline-start:-2px;min-width: var(--custom-party-avatars-avatar-diameter); - padding: 0 4px -} - -.theme-dark .partyMemberBackground_e45a82 { - background-color: var(--primary-500) -} - -.theme-dark .partyMemberUnknown_e45a82 { - background-color: var(--primary-500) -} - -.theme-light .partyMemberBackground_e45a82 { - background-color: var(--primary-160) -} - -.theme-light .partyMemberUnknown_e45a82 { - background-color: var(--primary-160) -} - -.memberItem__5017b { - align-items: center; - display: flex; - min-height: 24px; - padding: 6px 8px -} - -.avatar__5017b { - flex: 0 0 auto; - margin-inline-end:8px} - -.avatar__5017b,.unknown__5017b { - height: 24px; - width: 24px -} - -.unknown__5017b { - background-size: 24px 24px -} - -.images-light .unknown__5017b { - background-image: url(/assets/95e823d78e994238.svg) -} - -.images-dark .unknown__5017b { - background-image: url(/assets/148cf7732ca0f3d8.svg) -} - -.itemCard__7e549 { - border-radius: 8px; - margin-top: 8px -} - -.itemCard__7e549:first-of-type { - margin-top: 0 -} - -.homepagePadding__7e549 { - font-weight: var(--font-weight-bold); - padding-inline-start:16px;padding-top: 16px -} - -.emptyCard__7e549 { - align-items: center; - background: var(--background-base-low); - border-radius: 8px; - display: flex; - flex-direction: column; - justify-content: center; - padding: 16px; - text-align: center -} - -.emptyHeader__7e549 { - margin-bottom: 4px -} - -.emptyText__7e549 { - color: var(--interactive-text-default) -} - -.refresh-active-now .itemCard__7e549 { - border-radius: 16px; - box-shadow: 0 1px 4px 0 rgba(0,0,0,.14); - margin-top: 12px; - overflow: hidden -} - -.scroller__7d20c { - border-inline-start:1px solid var(--border-subtle);height: 100%; - margin-inline-start:0;padding: 6px 16px 16px -} - -.scroller__7d20c.standalone__7d20c { - border-inline-start:none;display: flex; - justify-content: center -} - -.container__7d20c { - background: none; - height: 100% -} - -.header__7d20c { - font-family: var(--font-display); - font-weight: var(--font-weight-semibold); - margin: 8px 0 16px; - margin-bottom: calc(var(--space-8) + 1px) -} - -.addFriendInputWrapper__72ba7 { - align-items: center; - background-color: var(--input-background-default); - border: 1px solid var(--input-border-default); - border-radius: 8px; - display: flex; - margin-top: 16px; - padding: 0 12px; - position: relative -} - -.addFriendInputWrapper__72ba7:focus-within { - border-color: var(--text-link) -} - -.addFriendInputWrapper__72ba7.error__72ba7 { - border-color: var(--border-feedback-critical) -} - -.addFriendInputWrapper__72ba7.success__72ba7 { - border-color: var(--green-360) -} - -.addFriendWumpusWrapper__72ba7 { - align-items: flex-start; - display: flex; - justify-content: space-between; - padding-inline-end:var(--space-128);position: relative -} - -.addFriendWumpusWrapper__72ba7 img { - bottom: calc(var(--space-16)*-1); - inset-inline-end: var(--space-16); - position: absolute -} - -.inputText__72ba7 { - font-size: 16px; - font-weight: var(--font-weight-medium); - letter-spacing: .04em; - line-height: 20px; - white-space: pre -} - -.addFriendInput__72ba7 { - background-color: transparent; - border: none; - box-sizing: border-box; - color: var(--text-default); - flex: 1 1 auto; - margin-inline-end:16px;padding: 4px 0; - z-index: 1 -} - -.addFriendInput__72ba7::-moz-placeholder { - color: var(--text-muted); - -moz-user-select: none; - user-select: none -} - -.addFriendInput__72ba7::placeholder { - color: var(--text-muted); - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.input__72ba7 { - background-color: unset; - border: none; - padding: 0 -} - -.input__72ba7:focus,.input__72ba7:focus-within,.input__72ba7:hover { - border: none -} - -.addFriendHint__72ba7 { - box-sizing: border-box; - color: var(--text-muted); - cursor: text; - pointer-events: none; - position: absolute; - top: 15px -} - -.ring__72ba7 { - border-radius: 8px -} - -.enable-forced-colors .addFriendInputWrapper__72ba7 { - border-color: ButtonText -} - -.enable-forced-colors .addFriendInputWrapper__72ba7:focus-within { - border-color: Highlight -} - -.header_a14595 { - display: flex; - flex-direction: column; - flex-shrink: 0; - gap: var(--space-8); - padding: 20px 30px -} - -.title_a14595 { - margin: 0; - margin-bottom: var(--space-8) -} - -.grid_a14595 { - -moz-column-gap: 32px; - column-gap: 32px; - display: grid; - grid-template-columns: 1fr 1fr; - padding: 0 20px; - place-items: center; - row-gap: 20px -} - -.container_a14595 { - align-items: center; - background-color: var(--background-base-lower); - border: 1px solid var(--border-subtle); - border-radius: 8px; - cursor: pointer; - display: flex; - width: 100% -} - -.container_a14595:hover { - background-color: var(--interactive-background-hover) -} - -.container_a14595:hover .arrow_a14595 { - color: var(--interactive-text-hover) -} - -.keyboard-mode .container_a14595:focus { - background-color: var(--interactive-background-selected) -} - -.container_a14595:active { - background-color: var(--interactive-background-active) -} - -.icon_a14595 { - align-items: center; - background-color: var(--green-360); - border-radius: 8px; - display: flex; - filter: saturate(var(--saturation-factor,1)); - flex-shrink: 0; - height: 36px; - justify-content: center; - margin: 8px; - width: 36px -} - -.text_a14595 { - overflow: hidden; - text-overflow: ellipsis -} - -.arrow_a14595 { - color: var(--interactive-text-default); - margin-inline:auto 16px} - -.header__94b08 { - border-bottom: 1px solid var(--border-subtle); - flex-shrink: 0; - padding: 20px 30px -} - -.title__94b08 { - margin-bottom: var(--space-8) -} - -.emptyState__94b08 { - flex: 1 1 auto; - position: relative -} - -.peopleListItem_cc6179 { - border-bottom: 1px solid transparent; - border-top: 1px solid var(--border-subtle); - box-sizing: border-box; - cursor: pointer; - display: flex; - flex-direction: row; - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px; - margin-inline:var(--space-24) 20px;overflow: hidden -} - -.peopleListItem_cc6179.active_cc6179+.peopleListItem_cc6179,.peopleListItem_cc6179:hover+.peopleListItem_cc6179 { - border-color: transparent -} - -.peopleListItem_cc6179.active_cc6179,.peopleListItem_cc6179:hover { - background: var(--interactive-background-hover); - border-color: transparent; - border-radius: 8px; - box-shadow: none; - margin-block:0;margin-inline:20px 10px;margin-inline-start:var(--space-16);padding: 16px 10px; - padding-inline-start:var(--space-8)} - -.peopleListItem_cc6179: hover { - background-color:var(--background-mod-subtle) -} - -.peopleListItem_cc6179.active_cc6179 { - background-color: var(--background-mod-normal) -} - -.full-motion .peopleListItem_cc6179 { - transition: background-color .2s ease -} - -.statusText__19b6d,.text__19b6d { - font-size: 14px -} - -.statusText__19b6d { - color: var(--text-muted) -} - -.userInfo__0a06e { - display: flex; - overflow: hidden -} - -.avatar__0a06e { - flex-shrink: 0; - margin-block:0;margin-inline:0 12px} - -.text__0a06e { - display: flex; - flex-direction: column; - overflow: hidden -} - -.account__0a06e { - display: flex; - flex-direction: row -} - -.accountIdentifier__0a06e { - margin-inline-start:8px} - -.subtext__0a06e { - color: var(--text-muted); - font-size: 12px -} - -.discordTag__0a06e { - align-items: flex-end; - flex-grow: 1; - overflow: hidden -} - -.botTag__0a06e,.discordTag__0a06e { - display: flex -} - -.username__0a06e { - color: var(--text-strong); - font-weight: var(--font-weight-semibold); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.discriminator__0a06e { - color: var(--text-default); - font-size: 14px; - line-height: 16px; - visibility: hidden -} - -.hovered__0a06e .discriminator__0a06e { - visibility: visible -} - -.alignPomelo__0a06e { - align-items: center -} - -.listItemContents_fc004c { - align-items: center; - display: flex; - flex-grow: 1; - justify-content: space-between; - max-width: 100% -} - -.friendAnniversaryRow_fc004c { - padding: var(--space-8) 0 -} - -.actions_fc004c { - display: flex; - gap: 10px -} - -.anniversarySubtext_fc004c { - align-items: center; - display: flex; - gap: var(--space-4) -} - -.userInfoCentered_fc004c { - align-items: center -} - -.actionButton_f8fa06 { - align-items: center; - background-color: var(--background-base-lower); - border-radius: 50%; - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - height: 36px; - justify-content: center; - width: 36px -} - -.actionButton_f8fa06.highlight_f8fa06 { - background-color: var(--background-base-lowest) -} - -.actionButton_f8fa06:first-child { - margin-inline-start:0} - -.actionButton_f8fa06:hover { - color: var(--interactive-text-hover) -} - -.actionButton_f8fa06:active { - background-color: var(--interactive-background-active); - color: var(--interactive-text-active) -} - -.actionButton_f8fa06.disabled_f8fa06 { - opacity: .3; - pointer-events: none -} - -.actionButton_f8fa06.actionAccept_f8fa06:hover { - color: var(--icon-feedback-positive) -} - -.actionButton_f8fa06.actionDeny_f8fa06:hover { - color: var(--icon-feedback-critical) -} - -.icon_f8fa06 { - height: 20px; - width: 20px -} - -.enable-forced-colors .actionButton_f8fa06 { - background-color: ButtonFace; - border: 1px solid ButtonFace; - color: ButtonText; - forced-color-adjust: none -} - -.enable-forced-colors .actionButton_f8fa06:hover { - border-color: ButtonText; - color: ButtonText -} - -.enable-forced-colors .actionButton_f8fa06:active { - background-color: HighlightText; - border-color: Highlight; - color: Highlight -} - -.enable-forced-colors .actionButton_f8fa06.disabled_f8fa06 { - background-color: Canvas; - border-color: GrayText; - color: GrayText; - opacity: 1 -} - -.friendsEmpty_c7ea19 { - align-items: center; - display: flex; - flex-grow: 1; - justify-content: center -} - -.button_c7ea19 { - margin-top: 20px -} - -.emptyStateContainer_c7ea19 { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-24) -} - -.listItemContents_e1ecd3 { - flex-grow: 1; - justify-content: space-between; - max-width: 100% -} - -.listItemContents_e1ecd3,.tableHeader_e1ecd3 { - align-items: center; - display: flex -} - -.tableHeader_e1ecd3 { - border-bottom: 1px solid var(--background-base-lower); - flex-direction: row; - flex-shrink: 0; - padding: 16px 30px 8px -} - -.tableHeaderText_e1ecd3 { - color: var(--text-default) -} - -.mutualGuilds_e1ecd3 { - display: flex; - margin-inline-start:13px;width: 200px -} - -@media (max-width: 1040px) { - .mutualGuilds_e1ecd3 { - display:none - } -} - -.moreMutualGuildsBtn_e1ecd3,.mutualGuilds_e1ecd3 a+a { - margin-inline-start:6px} - -.moreMutualGuildsBtn_e1ecd3 { - align-items: center; - border-radius: 50%; - box-sizing: border-box; - color: var(--text-default); - cursor: pointer; - display: flex; - flex-shrink: 0; - font-size: 11px; - font-weight: var(--font-weight-bold); - height: 30px; - justify-content: center; - width: 30px -} - -.moreMutualGuildsBtn_e1ecd3:hover { - color: var(--text-strong) -} - -.actions_e1ecd3 { - display: flex; - gap: 10px; - margin-inline-start:8px} - -.staffIndicator_e1ecd3 { - align-items: center; - display: flex -} - -.theme-dark .moreMutualGuildsBtn_e1ecd3:hover { - border-color: var(--opacity-white-60) -} - -.theme-light .moreMutualGuildsBtn_e1ecd3:hover { - border-color: var(--opacity-black-60) -} - -.applicationSublabel_e1ecd3 { - align-items: center; - display: flex; - gap: 4px -} - -.applicationIcon_e1ecd3 { - border-radius: 2px; - height: 14px; - width: 14px -} - -.emptyStateContainer__5ec2f { - display: flex; - flex-direction: column; - height: 100%; - position: relative; - width: 100% -} - -.peopleList__5ec2f { - flex-grow: 0; - padding-bottom: 8px -} - -.searchBar__5ec2f { - margin-block:var(--space-12) var(--space-8);margin-inline: var(--space-24) var(--space-16) -} - -.sectionTitle__5ec2f { - align-items: center; - display: flex; - flex-direction: row; - justify-content: space-between -} - -.clearButton__5ec2f { - margin-inline-end:var(--space-24)} - -.viewSpamButton__5ec2f { - align-self: start; - margin: 0 var(--space-24); - padding: var(--space-4) var(--space-8); - padding-inline-start:0} - -.sectionFooter__5ec2f { - display: flex; - justify-content: flex-end; - padding-top: var(--space-8); - padding-inline:var(--space-24);padding-bottom: var(--space-8); - padding-inline-start:0} - -.blockedIgnoredSettingsNotice__6811a { - margin-block:16px 8px;margin-inline:30px 20px} - -.title__1472a { - color: var(--text-default); - margin-block:16px;margin-inline:30px 20px;margin-inline-start:var(--space-24)} - -.listItemContents_c72b78 { - align-items: center; - display: flex; - flex: 0 1 100%; - justify-content: space-between -} - -.actions_c72b78 { - display: flex; - gap: 10px; - margin-inline-start:auto} - -.container__133bf { - background: var(--background-gradient-chat,var(--background-base-low)); - background-color: var(--background-base-lowest); - border-top: 1px solid var(--app-frame-border); - display: flex; - flex-direction: column; - overflow: hidden; - width: 100% -} - -[data-collapsed=true]~.container__133bf { - border-inline-start:1px solid var(--border-subtle);border-start-start-radius: var(--radius-md) -} - -.inviteToolbar__133bf { - display: flex; - padding-inline-end:var(--space-8)} - -@media (max-width: 940px) { - .inviteToolbar__133bf { - display:none - } -} - -.peopleColumn__133bf { - display: flex; - flex: 1 1 auto; - flex-direction: column; - overflow: hidden -} - -.nowPlayingColumn__133bf { - background: var(--background-base-lower); - flex: 0 1 30%; - max-width: 420px; - min-width: 360px -} - -.nowPlayingScroller__133bf { - max-height: 100%; - padding: 16px -} - -.consentCard__133bf { - margin-bottom: 16px -} - -.tabBar__133bf .badge__133bf { - margin-inline-start:8px} - -.tabBar__133bf .item__133bf { - align-items: center; - display: flex; - justify-content: center; - min-width: 40px; - text-align: center; - -webkit-app-region: no-drag -} - -.tabBar__133bf .addFriend__133bf.addFriend__133bf.addFriend__133bf { - background-color: var(--control-primary-background-default); - border-color: var(--control-primary-border-default); - color: var(--white) -} - -.tabBar__133bf .addFriend__133bf.addFriend__133bf.addFriend__133bf:hover { - background-color: var(--control-primary-background-hover); - color: var(--status-positive-text) -} - -.tabBar__133bf .addFriend__133bf.addFriend__133bf.addFriend__133bf[aria-selected=true] { - background-color: var(--opacity-blurple-16); - color: var(--text-brand) -} - -.tabBar__133bf .addFriend__133bf.addFriend__133bf.addFriend__133bf:active { - background-color: var(--control-primary-background-active) -} - -.tabBody__133bf { - background-color: var(--background-gradient-chat,var(--background-base-lower)); - display: flex; - flex-direction: row; - height: 100%; - overflow: hidden; - position: relative; - transform: translateZ(0) -} - -.tabBody__133bf:before { - box-shadow: var(--elevation-low); - content: ""; - display: none; - height: 1px; - position: absolute; - top: -1px; - inset-inline: 0; - pointer-events: none; - z-index: 1 -} - -@media (max-width: 1200px) { - .nowPlayingColumn__133bf { - display:none - } -} - -.nowPlayingColumnFullWidth__133bf { - flex: 1 1 auto; - max-width: unset; - min-width: 0; - width: 100% -} - -.heading-sm\/normal__4ed1a { - font-family: var(--font-display); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.heading-sm\/normal__4ed1a.fontScaling__4ed1a { - font-size: .875rem -} - -.heading-sm\/medium__4ed1a { - font-family: var(--font-display); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.heading-sm\/medium__4ed1a.fontScaling__4ed1a { - font-size: .875rem -} - -.heading-sm\/semibold__4ed1a { - font-family: var(--font-display); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.heading-sm\/semibold__4ed1a.fontScaling__4ed1a { - font-size: .875rem -} - -.heading-sm\/bold__4ed1a { - font-family: var(--font-display); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.heading-sm\/bold__4ed1a.fontScaling__4ed1a { - font-size: .875rem -} - -.heading-sm\/extrabold__4ed1a { - font-family: var(--font-display); - font-size: 14px; - font-weight: 800; - line-height: 1.2857142857142858 -} - -.heading-sm\/extrabold__4ed1a.fontScaling__4ed1a { - font-size: .875rem -} - -.heading-md\/normal__4ed1a { - font-family: var(--font-display); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.heading-md\/normal__4ed1a.fontScaling__4ed1a { - font-size: 1rem -} - -.heading-md\/medium__4ed1a { - font-family: var(--font-display); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.heading-md\/medium__4ed1a.fontScaling__4ed1a { - font-size: 1rem -} - -.heading-md\/semibold__4ed1a { - font-family: var(--font-display); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.heading-md\/semibold__4ed1a.fontScaling__4ed1a { - font-size: 1rem -} - -.heading-md\/bold__4ed1a { - font-family: var(--font-display); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.heading-md\/bold__4ed1a.fontScaling__4ed1a { - font-size: 1rem -} - -.heading-md\/extrabold__4ed1a { - font-family: var(--font-display); - font-size: 16px; - font-weight: 800; - line-height: 1.25 -} - -.heading-md\/extrabold__4ed1a.fontScaling__4ed1a { - font-size: 1rem -} - -.heading-lg\/normal__4ed1a { - font-family: var(--font-display); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.heading-lg\/normal__4ed1a.fontScaling__4ed1a { - font-size: 1.25rem -} - -.heading-lg\/medium__4ed1a { - font-family: var(--font-display); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.heading-lg\/medium__4ed1a.fontScaling__4ed1a { - font-size: 1.25rem -} - -.heading-lg\/semibold__4ed1a { - font-family: var(--font-display); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.heading-lg\/semibold__4ed1a.fontScaling__4ed1a { - font-size: 1.25rem -} - -.heading-lg\/bold__4ed1a { - font-family: var(--font-display); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.heading-lg\/bold__4ed1a.fontScaling__4ed1a { - font-size: 1.25rem -} - -.heading-lg\/extrabold__4ed1a { - font-family: var(--font-display); - font-size: 20px; - font-weight: 800; - line-height: 1.2 -} - -.heading-lg\/extrabold__4ed1a.fontScaling__4ed1a { - font-size: 1.25rem -} - -.heading-xl\/normal__4ed1a { - font-family: var(--font-display); - font-size: 24px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xl\/normal__4ed1a.fontScaling__4ed1a { - font-size: 1.5rem -} - -.heading-xl\/medium__4ed1a { - font-family: var(--font-display); - font-size: 24px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xl\/medium__4ed1a.fontScaling__4ed1a { - font-size: 1.5rem -} - -.heading-xl\/semibold__4ed1a { - font-family: var(--font-display); - font-size: 24px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xl\/semibold__4ed1a.fontScaling__4ed1a { - font-size: 1.5rem -} - -.heading-xl\/bold__4ed1a { - font-family: var(--font-display); - font-size: 24px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xl\/bold__4ed1a.fontScaling__4ed1a { - font-size: 1.5rem -} - -.heading-xl\/extrabold__4ed1a { - font-family: var(--font-display); - font-size: 24px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xl\/extrabold__4ed1a.fontScaling__4ed1a { - font-size: 1.5rem -} - -.heading-xxl\/normal__4ed1a { - font-family: var(--font-display); - font-size: 32px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xxl\/normal__4ed1a.fontScaling__4ed1a { - font-size: 2rem -} - -.heading-xxl\/medium__4ed1a { - font-family: var(--font-display); - font-size: 32px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xxl\/medium__4ed1a.fontScaling__4ed1a { - font-size: 2rem -} - -.heading-xxl\/semibold__4ed1a { - font-family: var(--font-display); - font-size: 32px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xxl\/semibold__4ed1a.fontScaling__4ed1a { - font-size: 2rem -} - -.heading-xxl\/bold__4ed1a { - font-family: var(--font-display); - font-size: 32px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xxl\/bold__4ed1a.fontScaling__4ed1a { - font-size: 2rem -} - -.heading-xxl\/extrabold__4ed1a { - font-family: var(--font-display); - font-size: 32px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xxl\/extrabold__4ed1a.fontScaling__4ed1a { - font-size: 2rem -} - -.eyebrow__4ed1a { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - letter-spacing: .02em; - line-height: 1.3333333333333333; - text-transform: uppercase -} - -.eyebrow__4ed1a.fontScaling__4ed1a { - font-size: .75rem -} - -.heading-deprecated-12\/normal__4ed1a { - font-family: var(--font-display); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/normal__4ed1a.fontScaling__4ed1a { - font-size: .75rem -} - -.heading-deprecated-12\/medium__4ed1a { - font-family: var(--font-display); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/medium__4ed1a.fontScaling__4ed1a { - font-size: .75rem -} - -.heading-deprecated-12\/semibold__4ed1a { - font-family: var(--font-display); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/semibold__4ed1a.fontScaling__4ed1a { - font-size: .75rem -} - -.heading-deprecated-12\/bold__4ed1a { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/bold__4ed1a.fontScaling__4ed1a { - font-size: .75rem -} - -.heading-deprecated-12\/extrabold__4ed1a { - font-family: var(--font-display); - font-size: 12px; - font-weight: 800; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/extrabold__4ed1a.fontScaling__4ed1a { - font-size: .75rem -} - -.redesign\/heading-18\/bold__4ed1a { - font-family: var(--font-display); - font-size: 18px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/heading-18\/bold__4ed1a.fontScaling__4ed1a { - font-size: 1.125rem -} - -.text-xxs\/normal__4ed1a { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 400; - line-height: 1.2 -} - -.text-xxs\/normal__4ed1a.fontScaling__4ed1a { - font-size: .625rem -} - -.text-xxs\/medium__4ed1a { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 500; - line-height: 1.2 -} - -.text-xxs\/medium__4ed1a.fontScaling__4ed1a { - font-size: .625rem -} - -.text-xxs\/semibold__4ed1a { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 600; - line-height: 1.2 -} - -.text-xxs\/semibold__4ed1a.fontScaling__4ed1a { - font-size: .625rem -} - -.text-xxs\/bold__4ed1a { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 700; - line-height: 1.2 -} - -.text-xxs\/bold__4ed1a.fontScaling__4ed1a { - font-size: .625rem -} - -.text-xs\/normal__4ed1a { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.text-xs\/normal__4ed1a.fontScaling__4ed1a { - font-size: .75rem -} - -.text-xs\/medium__4ed1a { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.text-xs\/medium__4ed1a.fontScaling__4ed1a { - font-size: .75rem -} - -.text-xs\/semibold__4ed1a { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.text-xs\/semibold__4ed1a.fontScaling__4ed1a { - font-size: .75rem -} - -.text-xs\/bold__4ed1a { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.text-xs\/bold__4ed1a.fontScaling__4ed1a { - font-size: .75rem -} - -.text-sm\/normal__4ed1a { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.text-sm\/normal__4ed1a.fontScaling__4ed1a { - font-size: .875rem -} - -.text-sm\/medium__4ed1a { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.text-sm\/medium__4ed1a.fontScaling__4ed1a { - font-size: .875rem -} - -.text-sm\/semibold__4ed1a { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.text-sm\/semibold__4ed1a.fontScaling__4ed1a { - font-size: .875rem -} - -.text-sm\/bold__4ed1a { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.text-sm\/bold__4ed1a.fontScaling__4ed1a { - font-size: .875rem -} - -.text-md\/normal__4ed1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.text-md\/normal__4ed1a.fontScaling__4ed1a { - font-size: 1rem -} - -.text-md\/medium__4ed1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.text-md\/medium__4ed1a.fontScaling__4ed1a { - font-size: 1rem -} - -.text-md\/semibold__4ed1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.text-md\/semibold__4ed1a.fontScaling__4ed1a { - font-size: 1rem -} - -.text-md\/bold__4ed1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.text-md\/bold__4ed1a.fontScaling__4ed1a { - font-size: 1rem -} - -.text-lg\/normal__4ed1a { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.text-lg\/normal__4ed1a.fontScaling__4ed1a { - font-size: 1.25rem -} - -.text-lg\/medium__4ed1a { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.text-lg\/medium__4ed1a.fontScaling__4ed1a { - font-size: 1.25rem -} - -.text-lg\/semibold__4ed1a { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.text-lg\/semibold__4ed1a.fontScaling__4ed1a { - font-size: 1.25rem -} - -.text-lg\/bold__4ed1a { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.text-lg\/bold__4ed1a.fontScaling__4ed1a { - font-size: 1.25rem -} - -.redesign\/message-preview\/normal__4ed1a { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/normal__4ed1a.fontScaling__4ed1a { - font-size: .9375rem -} - -.redesign\/message-preview\/medium__4ed1a { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/medium__4ed1a.fontScaling__4ed1a { - font-size: .9375rem -} - -.redesign\/message-preview\/semibold__4ed1a { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/semibold__4ed1a.fontScaling__4ed1a { - font-size: .9375rem -} - -.redesign\/message-preview\/bold__4ed1a { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/bold__4ed1a.fontScaling__4ed1a { - font-size: .9375rem -} - -.redesign\/channel-title\/normal__4ed1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.375 -} - -.redesign\/channel-title\/normal__4ed1a.fontScaling__4ed1a { - font-size: 1rem -} - -.redesign\/channel-title\/medium__4ed1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.375 -} - -.redesign\/channel-title\/medium__4ed1a.fontScaling__4ed1a { - font-size: 1rem -} - -.redesign\/channel-title\/semibold__4ed1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.375 -} - -.redesign\/channel-title\/semibold__4ed1a.fontScaling__4ed1a { - font-size: 1rem -} - -.redesign\/channel-title\/bold__4ed1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.375 -} - -.redesign\/channel-title\/bold__4ed1a.fontScaling__4ed1a { - font-size: 1rem -} - -.display-sm__4ed1a { - font-family: var(--font-headline); - font-size: 20px; - font-weight: 800; - line-height: 1 -} - -.display-sm__4ed1a.fontScaling__4ed1a { - font-size: 1.25rem -} - -.display-md__4ed1a { - font-family: var(--font-headline); - font-size: 34px; - font-weight: 800; - line-height: 1.0588235294117647 -} - -.display-md__4ed1a.fontScaling__4ed1a { - font-size: 2.125rem -} - -.display-lg__4ed1a { - font-family: var(--font-headline); - font-size: 44px; - font-weight: 800; - line-height: .9545454545454546 -} - -.display-lg__4ed1a.fontScaling__4ed1a { - font-size: 2.75rem -} - -.code__4ed1a { - font-family: var(--font-code); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.code__4ed1a.fontScaling__4ed1a { - font-size: .75rem -} - -.marketingBadgeTooltip__4ed1a { - margin-inline-start:10px;max-width: 100%; - position: relative -} - -.marketingBadgeTooltipContent__4ed1a { - display: flex; - flex-direction: row; - gap: 4px -} - -.shopMarketingTooltipContent__4ed1a { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px -} - -.shopMarketingTooltipText__4ed1a { - max-width: 165px -} - -.avatarContainer__4ed1a { - position: relative; - width: 92px -} - -.avatar__4ed1a { - inset-inline-start: 0; - position: absolute; - top: -40px -} - -.newBadge__4ed1a { - align-items: center; - display: inline-flex; - z-index: 1 -} - -.newBadge__4ed1a svg { - height: revert!important; - width: revert!important -} - -.marketingButtonBackgroundNew__4ed1a { - border-radius: inherit; - bottom: 0; - height: 100%; - inset-inline-end: 0; - pointer-events: none; - position: absolute -} - -.marketingButtonBackgroundImage__4ed1a { - border-radius: inherit; - height: 100%; - max-width: 100%; - -o-object-fit: cover; - object-fit: cover; - -o-object-position: bottom right; - object-position: bottom right -} - -.marketingButtonBackgroundImageFaded__4ed1a { - mask-image: linear-gradient(90deg,transparent 0,transparent 0,rgba(0,0,0,.1) 50%,rgba(0,0,0,.3) 60%,rgba(0,0,0,.6) 70%,#000 85%,#000); - -webkit-mask-image: linear-gradient(90deg,transparent 0,transparent 0,rgba(0,0,0,.1) 50%,rgba(0,0,0,.3) 60%,rgba(0,0,0,.6) 70%,#000 85%,#000) -} - -.frequentFriendsRow__1fc18 { - padding: 0 var(--space-12) -} - -.frequentFriendsHeader__1fc18 { - align-items: center; - display: flex; - margin-bottom: var(--space-8) -} - -.frequentFriendsTitle__1fc18 { - color: var(--channels-default); - font-size: 14px; - font-weight: var(--font-weight-medium); - margin: 0 var(--space-4) -} - -.frequentFriendsInfoIcon__1fc18 { - color: var(--text-muted); - height: var(--space-12); - width: var(--space-12) -} - -.frequentFriendsAvatars__1fc18 { - display: flex; - gap: var(--space-8); - padding-inline-start:2px} - -.scrollMaskLeft__1fc18 { - inset-inline-start: 0; - -webkit-mask: linear-gradient(90deg,transparent,#000 20px); - mask: linear-gradient(90deg,transparent,#000 20px) -} - -.scrollMaskRight__1fc18 { - inset-inline-end: 0; - -webkit-mask: linear-gradient(270deg,transparent,#000 20px); - mask: linear-gradient(270deg,transparent,#000 20px) -} - -.scrollMaskLeft__1fc18.scrollMaskRight__1fc18 { - -webkit-mask: linear-gradient(90deg,transparent,#000 20px,#000 calc(100% - 20px),transparent); - mask: linear-gradient(90deg,transparent,#000 20px,#000 calc(100% - 20px),transparent) -} - -.frequentFriendAvatarButton__1fc18 { - align-items: center; - cursor: pointer; - display: flex; - justify-content: center; - padding: 2px; - position: relative -} - -.countDown__61bcd { - display: flex -} - -.sparkleStar1__61bcd { - height: 10px; - margin-top: 9px; - margin-inline-end:4px;width: 10px -} - -.sparkleStar2__61bcd { - margin-inline-end:2px} - -.premiumBadge__6d7da { - align-items: center; - border-radius: var(--radius-md); - display: flex; - margin-inline-start:auto;padding: 0 6px; - pointer-events: all; - text-transform: uppercase -} - -.premiumAcknowledgedBadge__6d7da { - background-color: var(--background-mod-strong) -} - -.premiumUnacknowledgedBadge__6d7da { - background-color: var(--badge-expressive-background-default) -} - -.premiumUnacknowledgedBadgeCopy__6d7da { - color: var(--badge-expressive-text-default) -} - -.BadgeClockIcon__6d7da { - color: var(--icon-subtle); - margin-inline-end:4px} - -.churnDiscountBadge_b65b20 { - align-items: center; - border-radius: 12px; - display: flex; - margin-inline-start:auto;padding: 0 6px; - pointer-events: all; - text-transform: uppercase -} - -.churnDiscountAcknowledgedBadge_b65b20 { - background-color: var(--background-mod-strong) -} - -.churnDiscountBadgeIcon_b65b20 { - color: var(--icon-subtle); - height: 14px; - margin-inline-end:4px;width: 14px -} - -.badgeContainer__98c7b { - display: flex; - flex-direction: row; - position: relative -} - -.newBadge__98c7b { - align-items: center; - background-image: linear-gradient(to right,var(--premium-tier-2-purple-for-gradients),var(--premium-tier-2-pink-for-gradients)); - border-radius: 19px; - display: flex; - justify-content: center; - margin-inline-start:auto;padding: 0 6px; - pointer-events: all; - text-transform: uppercase -} - -.newBadge__98c7b,.star__98c7b { - color: var(--white) -} - -.star__98c7b { - position: relative -} - -.starLeft__98c7b { - bottom: -6px; - inset-inline-start: -6px -} - -.starRight__98c7b { - bottom: 0; - inset-inline-start: -4px -} - -.theme-light .starLeft__98c7b { - color: var(--premium-tier-2-purple-for-gradients) -} - -.theme-light .starRight__98c7b { - color: var(--premium-tier-2-pink-for-gradients) -} - -.theme-light .customCircleAnimation__83ef9 { - animation: glow__83ef9 5s ease-in-out infinite,pulse__83ef9 5s ease-in-out infinite; - transition: stroke-dashoffset .5s ease-out -} - -.theme-dark .customCircleAnimation__83ef9 { - animation: extra-glow__83ef9 5s ease-in-out infinite,pulse__83ef9 5s ease-in-out infinite; - transition: stroke-dashoffset .5s ease-out -} - -@keyframes glow__83ef9 { - 0%,50%,to { - filter: none - } - - 25%,75% { - filter: drop-shadow(0 0 5px var(--premium-tier-2-pink-for-gradients-2)) - } -} - -@keyframes extra-glow__83ef9 { - 0%,50%,to { - filter: none - } - - 25%,75% { - filter: drop-shadow(0 0 2px var(--premium-tier-2-pink-for-gradients-2)) drop-shadow(0 0 2px var(--premium-tier-2-pink-for-gradients-2)) drop-shadow(0 0 5px var(--premium-tier-2-pink-for-gradients-2)) - } -} - -@keyframes pulse__83ef9 { - 0%,50%,to { - stroke-width: 2 - } - - 25%,75% { - stroke-width: 4 - } -} - -.iconContainer__83ef9 { - margin-inline:10px} - -.iconContainer__83ef9,.incentiveIconContainer__83ef9 { - position: relative; - width: 92px -} - -.coachtipContent__83ef9 { - max-width: 160px -} - -.marketingBadgeTooltip__83ef9 { - margin-inline-start:10px;max-width: 100%; - position: relative -} - -.nitroTabCoachtipContainer__83ef9 { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px -} - -[data-mana-component=tooltip]:has(.nitroTabCoachtipContainer__83ef9) { - max-width: none -} - -[data-mana-component=tooltip]:has(.nitroTabCoachtipContainer__83ef9)>div { - overflow: visible -} - -.icon__83ef9 { - scale: 1.6 -} - -.trialBadgeBackground__83ef9 { - background-image: linear-gradient(to right,var(--premium-tier-2-purple-for-gradients),var(--premium-tier-2-pink-for-gradients)); - z-index: 2 -} - -.premiumTabTooltipContainer__83ef9 { - align-items: center; - display: flex; - flex-direction: row; - gap: 12px -} - -[data-mana-component=tooltip]:has(.premiumTabTooltipContainer__83ef9) { - max-width: none -} - -.premiumTabTooltipImage__83ef9 { - flex-shrink: 0; - height: 60px; - -o-object-fit: contain; - object-fit: contain; - width: 60px -} - -.premiumTabTooltipContent__83ef9 { - display: flex; - flex: 1; - flex-direction: column -} - -.premiumTrialBadge__94f65 { - align-items: center; - border-radius: 12px; - display: flex; - margin-inline-start:auto;padding: 0 6px; - pointer-events: all; - text-transform: uppercase -} - -.premiumTrialAcknowledgedBadge__94f65 { - background-color: var(--background-mod-strong) -} - -.reverseTrialBadgeIcon__94f65 { - color: var(--icon-subtle); - height: 14px; - margin-inline-end:4px;width: 14px -} - -.wrapper__553bf { - isolation: isolate; - position: relative -} - -.withGradientAndBadge__553bf { - overflow: hidden; - position: relative -} - -.withGradientAndBadge__553bf a { - color: var(--text-strong)!important -} - -.withGradientAndBadge__553bf svg { - color: var(--icon-strong)!important -} - -.withGradientAndBadge__553bf:after { - background-image: linear-gradient(90deg,#394aff,#e901d9); - border-radius: inherit; - bottom: 1px; - content: ""; - inset-inline: 0; - opacity: .16; - position: absolute; - top: 1px; - transition: opacity .125s; - z-index: 0 -} - -.withGradientAndBadge__553bf:hover:after { - opacity: .2 -} - -.badge__553bf { - position: relative; - z-index: 2 -} - -.shine__553bf { - background-image: linear-gradient(90deg,hsla(0,0%,100%,0) 25%,hsla(0,0%,100%,.7) 50%,hsla(0,0%,100%,0) 75%); - height: var(--custom-shine-dimensions); - inset-inline-end: calc(100% + var(--custom-shine-rotated-dimensions-delta)); - transform-origin: 50%; - width: var(--custom-shine-dimensions); - z-index: 3 -} - -.glow__553bf,.shine__553bf { - pointer-events: none; - position: absolute; - top: 50% -} - -.glow__553bf { - background-image: radial-gradient(rgba(221,0,241,.5) 0,rgba(221,0,241,0) 60%); - height: 150px; - inset-inline-end: 32px; - transform: translate(50%,-50%); - width: 150px; - z-index: 1 -} - -.glow__553bf:after { - background-image: radial-gradient(rgba(241,0,4,.2) 0,rgba(241,0,4,0) 60%); - content: ""; - display: block; - height: 100%; - width: 100% -} - -html .tabBadge_e6b769 { - background-color: var(--background-feedback-notification) -} - -.downloadProgressCircle_e6b769 { - height: 24px; - margin-inline-start:8px;opacity: 1; - transition: opacity .2s ease; - width: 24px -} - -.hideGameUpdateProgressIndicator_e6b769 { - opacity: 0; - pointer-events: none -} - -.privateChannels_e6b769 { - background: var(--background-gradient-high,var(--background-base-lowest)); - box-sizing: border-box; - display: flex; - flex: 1; - flex-direction: column; - overflow: hidden; - position: relative; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.sectionDivider_e6b769 { - background-color: var(--border-subtle); - border: 0; - box-shadow: border-box; - height: 1px; - margin: var(--space-12) var(--space-8); - width: 100% -} - -.searchBar_e6b769 { - align-items: center; - border-bottom: 1px solid var(--border-subtle); - box-shadow: none; - box-sizing: border-box; - display: flex; - flex: 0 0 auto; - height: var(--custom-channel-header-height); - padding: 0 var(--space-xs); - position: relative; - z-index: 2 -} - -.searchBar_e6b769 .searchBarComponent_e6b769 { - background-color: var(--input-background-default); - border: 1px solid var(--input-border-default); - border-radius: var(--radius-sm); - box-shadow: none; - color: var(--text-muted); - font-size: 14px; - font-weight: var(--font-weight-medium); - height: auto; - line-height: unset; - overflow: hidden; - padding: 10px 12px; - text-align: start; - text-overflow: ellipsis; - white-space: nowrap; - width: 100% -} - -.custom-client-theme .searchBar_e6b769 { - border-bottom: 1px solid var(--app-frame-border) -} - -.searchBarComponent_e6b769 { - background: var(--background-gradient-high,var(--background-base-lowest)) -} - -.familyCenterLinkButton_e6b769 { - padding-inline-end:0!important} - -.friendsButtonContainer_e6b769 { - position: relative -} - -.friendsBadge_e6b769 { - background: linear-gradient(170deg,hsl(var(--black-hsl)/0) 45%,var(--brand-500) 100%) -} - -.friendsBadge_e6b769:focus-within,.friendsBadge_e6b769:hover { - background: linear-gradient(170deg,hsl(var(--black-hsl)/0) 45%,var(--brand-500) 100%) -} - -.confetti_e6b769 { - height: 100%; - inset-inline-start: var(--space-8); - pointer-events: none; - position: absolute; - top: 0; - width: 100%; - z-index: 1 -} - -.theme-dark.custom-theme-background .themedHeaderMobile_e6b769 { - background: var(--background-gradient-high,var(--background-base-lower)) -} - -.theme-dark.custom-theme-background .themedSearchBarMobile_e6b769 { - background: var(--background-gradient-low,var(--background-base-lower)) -} - -.theme-light.custom-theme-background .themedHeaderMobile_e6b769 { - background: var(--background-gradient-low,var(--background-base-lower)) -} - -.theme-light.custom-theme-background .themedSearchBarMobile_e6b769 { - background: var(--background-gradient-lower,var(--background-base-lower)) -} - -.refresh-fast-follow-distinct-borders .searchBar_e6b769 { - border-bottom-color: var(--app-frame-border) -} - -.refresh-fast-follow-distinct-borders .sectionDivider_e6b769 { - background-color: var(--app-frame-border) -} - -.enable-forced-colors .searchBar_e6b769 { - border-bottom: 2px solid CanvasText -} - -.enable-forced-colors .privateChannels_e6b769 { - background-color: ButtonFace -} - -.familyCenterButtonContainer_e6b769 { - align-items: center; - display: flex; - height: var(--size-24); - position: relative -} - -.familyCenterButtonContainer_e6b769.withPadding_e6b769 { - padding-inline-end:var(--space-8)} - -.directMessagesHeader_e6b769 { - align-items: center; - border-bottom: 1px solid var(--border-subtle); - box-sizing: border-box; - display: flex; - height: 48px; - justify-content: space-between; - padding: 8px 16px; - padding-inline-end:8px} - -.directMessagesButton_e6b769 { - border-radius: var(--radius-sm) -} - -.directMessagesButton_e6b769:hover { - background-color: var(--background-mod-subtle) -} - -.preAlphaText_e6b769 { - color: var(--text-brand); - font-size: var(--size-12); - font-weight: var(--font-weight-semibold); - margin-inline-start:var(--space-8)} - -.devBanner__56d75 { - flex-grow: 0; - flex-shrink: 0; - font-size: 12px; - font-weight: var(--font-weight-medium); - line-height: 24px; - padding: 0 4px; - position: relative; - text-align: center; - white-space: no-wrap -} - -.devBanner__56d75 strong { - font-weight: var(--font-weight-semibold) -} - -.local__56d75 { - background-color: var(--background-feedback-warning) -} - -.local__56d75,.staging__56d75 { - color: var(--text-default) -} - -.staging__56d75 { - background-color: var(--background-feedback-critical) -} - -.buildOverride__56d75 { - background-color: var(--status-positive-background); - color: var(--white) -} - -.icon__56d75 { - display: inline-block; - height: auto; - margin-inline-end:4px;vertical-align: middle; - width: 12px -} - -.closeButton__56d75 { - background-position: 50% 55%; - background-size: 10px 10px; - cursor: pointer; - height: 24px; - inset-inline-end: 0; - opacity: .5; - position: absolute; - top: 3px; - transition: opacity .2s; - width: 24px; - -webkit-app-region: no-drag -} - -.closeButton__56d75:hover { - opacity: 1 -} - -.closeIcon__56d75 { - height: 14px; - width: 14px -} - -.listeningAlong_e0cf27 { - align-items: center; - background: var(--background-gradient-lowest,var(--background-base-low)); - border-radius: 4px 4px 0 0; - box-sizing: border-box; - display: flex; - flex-shrink: 0; - padding: 8px; - width: 100% -} - -.party_e0cf27 { - margin-inline-start:8px} - -.overflow_e0cf27 { - cursor: pointer -} - -.icons_e0cf27 { - display: flex; - margin-inline-start:4px} - -.avatar_e0cf27 { - cursor: pointer; - height: 20px!important; - width: 20px!important -} - -.userList__24091 { - border: 1px solid; - border-radius: 3px; - min-width: 250px; - position: relative -} - -.userList__24091:before { - border: 1px solid; - box-shadow: 2px 2px 4px var(--opacity-black-20); - content: ""; - height: 8px; - inset-inline-start: 50%; - margin-inline-start:-8px;position: absolute; - top: 100%; - transform: translate3d(4px,-4px,0) rotate(45deg); - width: 8px -} - -.header__24091 { - background-color: var(--background-base-lower); - color: var(--text-default); - font-size: 14px; - font-weight: var(--font-weight-semibold); - padding: 12px; - text-align: center; - text-transform: uppercase -} - -.content__24091 { - background-color: var(--background-base-lowest); - max-height: 260px -} - -.user__24091 { - font-size: 14px; - padding: 10px -} - -.discriminator__24091 { - color: var(--text-muted); - font-size: 14px -} - -.tag__24091 { - margin-inline-start:8px} - -.avatar__24091 { - height: 20px!important; - width: 20px!important -} - -.theme-dark .userList__24091 { - border-color: var(--primary-700) -} - -.theme-dark .userList__24091:before { - border-bottom-color: var(--primary-700); - border-inline-end-color:var(--primary-700)} - -.theme-light .userList__24091 { - border-color: var(--primary-100) -} - -.theme-light .userList__24091:before { - border-bottom-color: var(--primary-100); - border-inline-end-color:var(--primary-100)} - -.userList__24091: before { - background-color:var(--background-base-lowest); - border-color: transparent -} - -.username__24091 { - color: var(--text-default) -} - -.root_e373d2 { - border-start-end-radius: 8px; - border-start-start-radius: 8px; - box-sizing: border-box; - display: flex; - height: 32px; - justify-content: center; - padding: 8px -} - -.root_e373d2.connected_e373d2 { - background-color: var(--status-positive-background) -} - -.root_e373d2.connected_e373d2 .icon_e373d2 { - color: var(--white) -} - -.root_e373d2.connecting_e373d2 { - background-color: var(--background-mod-normal); - border-bottom: 1px solid var(--border-subtle) -} - -.icon_e373d2 { - color: var(--interactive-text-active); - height: 16px; - margin-inline-end:8px;width: 16px -} - -.scroller__68617 { - margin-inline:-3px -7px;max-height: 132px; - min-height: 24px; - padding-block:4px;padding-inline:4px 0;padding-bottom: 0; - padding-top: 0 -} - -.scroller__68617.expanded__68617 { - margin-bottom: 4px -} - -.voiceUsers__68617 { - align-items: center; - box-sizing: border-box; - display: flex; - flex-wrap: wrap; - gap: 4px; - width: 100% -} - -.voiceUsers__68617.collapsed__68617 { - flex-wrap: nowrap -} - -.avatarContainer__68617 { - border-radius: 50%; - flex-shrink: 0; - overflow: hidden -} - -.avatar__68617 { - background-size: 100%; - border-radius: 50%; - cursor: pointer; - display: block; - height: 24px; - width: 24px -} - -.avatarIconOverlay__68617 { - background-color: var(--opacity-black-28); - color: var(--white); - padding: 4px -} - -.avatarIconRed__68617 { - color: var(--red-400) -} - -.layers__960e4 { - overflow: hidden; - position: relative -} - -.layers__960e4>.layer__960e4 { - display: flex; - flex-direction: column; - inset: 0; - overflow: hidden; - position: absolute -} - -.layers__960e4>.layer__960e4.animating__960e4 { - pointer-events: none -} - -.layers__960e4>.layer__960e4.animating__960e4 * { - pointer-events: none!important -} - -.bg__960e4 { - background: var(--background-gradient-app-frame,var(--background-base-lowest)); - inset: 0; - position: absolute -} - -.refresh-fast-follow-guild-bg .bg__960e4 { - background: var(--background-gradient-app-frame,var(--app-frame-background)) -} - -.platform-win .layer__960e4 { - padding-top: 22px; - top: -22px -} - -.platform-win .bg__960e4 { - top: -22px -} - -.stop-animations * { - animation: none!important__960e4; - transition-property: none!important -} - -.layer__960e4 { - background-color: var(--background-base-low) -} - -.layer__960e4.baseLayer__960e4 { - background-color: transparent -} - -.app__160d8 { - background-color: var(--background-base-lowest); - display: flex; - flex-direction: column; - inset: 0; - position: absolute -} - -.app__160d8 button { - cursor: pointer -} - -.layers__160d8 { - display: flex; - flex: 1 1 auto; - flex-direction: column; - min-height: 0; - min-width: 0; - overflow: hidden -} - -.verifiedRole_e59759 { - align-items: center; - border: 2px solid var(--border-subtle); - border-radius: 12px; - cursor: pointer; - display: flex; - flex-direction: row; - gap: 12px; - padding: 20px 16px; - position: relative -} - -.verifiedRole_e59759:hover { - border-color: var(--border-muted) -} - -.verifiedRoleNameDescriptionContainer_e59759 { - flex: 2; - overflow: hidden -} - -.verifiedRoleDescription_e59759,.verifiedRoleName_e59759 { - overflow: hidden; - text-overflow: ellipsis -} - -.verifiedRoleDescription_e59759 { - margin-top: 4px -} - -.verifiedRoleHasRole_e59759 { - background-color: var(--background-base-lowest); - border: 2px solid var(--background-base-lowest); - cursor: unset -} - -.verifiedRoleHasRole_e59759:hover { - border-color: var(--background-base-lowest) -} - -.roleCheckmark_e59759 { - background-color: var(--brand-500); - border-radius: 50%; - color: var(--white); - inset-inline-end: -8px; - position: absolute; - top: -8px -} - -.botAvatar_e59759,.roleCheckmark_e59759,.roleCheckmark_e59759 svg { - height: 20px; - width: 20px -} - -.botAvatar_e59759 { - padding: 2px -} - -.firefoxFixScrollFlex_efbae7 { - min-height: 0 -} - -.fixClipping_efbae7 { - transform: translateZ(0) -} - -.full-motion .spinner_a2f514 { - transition: all var(--connecting-content-fade-duration,.15s) ease-out; - transition-property: opacity,transform -} - -.container_a2f514 { - align-items: center; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - display: flex; - justify-content: center; - z-index: 3000; - background: var(--background-gradient-lower,var(--background-base-lowest)); - opacity: 1; - overflow: hidden; - transition: opacity var(--connecting-container-fade-duration,.2s) ease-out -} - -.container_a2f514[data-fade=true],.container_a2f514[data-fade=true] .text_a2f514 { - opacity: 0 -} - -.container_a2f514 img,.container_a2f514 video { - height: 200px; - width: 200px -} - -.container_a2f514 video { - visibility: hidden -} - -.container_a2f514 video.ready_a2f514 { - visibility: visible -} - -.full-motion .container_a2f514[data-fade=true] .spinner_a2f514 { - opacity: 0; - transform: scale(.5) -} - -.platform-win .container_a2f514 { - padding-top: 22px; - top: -22px -} - -.content_a2f514 { - text-align: center -} - -.problems_a2f514 { - align-items: center; - color: var(--text-strong); - inset-inline: 0; - bottom: 0; - display: flex; - flex-direction: column; - padding-bottom: 32px; - position: absolute; - transform: translate3d(0,100%,0) -} - -.full-motion .problems_a2f514 { - transition: transform .2s ease-out -} - -.problems_a2f514.slideIn_a2f514 { - transform: translateZ(0) -} - -.problemsText_a2f514 { - color: var(--text-default); - font-size: 14px; - margin-bottom: 8px -} - -.links_a2f514 { - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 16px -} - -.links_a2f514:hover { - opacity: .8 -} - -.links_a2f514:before { - content: ""; - display: inline-block; - margin-inline-end:6px;vertical-align: bottom -} - -.statusLink_a2f514,.twitterLink_a2f514 { -} - -.statusLink_a2f514 { - margin-inline-start:20px} - -.icon_a2f514 { - color: var(--text-link); - display: inline-block; - height: auto; - margin-inline-end:4px;vertical-align: middle; - width: 16px -} - -.contentBase_a2f514 { - color: var(--text-default); - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 16px; - max-width: 600px -} - -.text_a2f514 { - position: relative; - top: -20px; - transition: opacity var(--connecting-content-fade-duration,.2s) ease-out -} - -.tipTitle_a2f514 { - color: var(--text-strong); - font-size: 12px; - font-weight: var(--font-weight-semibold); - line-height: 16px; - margin-bottom: 8px; - text-transform: uppercase -} - -.tip_a2f514,.tipTitle_a2f514 { - max-width: 300px -} - -.tip_a2f514 { - color: var(--text-default); - font-size: 16px; - line-height: 20px -} - -.keybind_a2f514 { - display: inline-flex; - margin-inline-end:-2px;margin-bottom: 0; - transform: translateY(-1px) -} - -.body_a2f514,.title_a2f514 { -} - -.title_a2f514 { - color: var(--text-strong); - font-size: 16px; - font-style: italic; - line-height: 20px; - margin-bottom: 10px; - text-transform: uppercase -} - -.channelName__7c7a6 { - margin-block:0 8px;margin-inline:8px 0} - -.username__7c7a6 { - padding-inline-start:8px} - -.kvContainer__7c7a6 { - flex-wrap: wrap -} - -.item__7c7a6 { - color: var(--text-strong); - margin-bottom: 20px -} - -.item__7c7a6,.itemValue__7c7a6 { - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.itemValue__7c7a6 { - color: var(--text-muted); - display: inline-block; - line-height: 24px; - max-width: 310px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.valueIcon__7c7a6 { - float: inline-start; - position: relative; - top: -1px -} - -.title__7c7a6 { - cursor: default; - flex: 1; - font-size: 16px; - font-weight: var(--font-weight-semibold); - line-height: 24px; - margin-bottom: 0; - margin-top: 0; - overflow: hidden; - word-wrap: break-word -} - -.divider__7c7a6,.graph__7c7a6 { - margin-top: 4px -} - -.videoWrapper__7c7a6 { - height: 180px; - width: 320px -} - -.sectionHeader__7c7a6 { - font-size: 24px; - margin-bottom: 8px -} - -.allowSelection__7c7a6 { - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.channelItem__7c7a6 { - margin-inline-start:-8px;margin-bottom: 8px -} - -.avatar__7c7a6 { - height: 20px!important; - top: -2px; - width: 20px!important -} - -.theme-dark .videoPreview__7c7a6 { - border: 1px solid var(--primary-700) -} - -.theme-light .videoPreview__7c7a6 { - border: 1px solid var(--primary-200) -} - -.videoPreview__7c7a6 { - background-color: var(--background-base-lower) -} - -.tabBar__7c7a6 { - margin-bottom: 16px -} - -.button__95fb7 { - min-height: 40px; - min-width: 137px -} - -.innerButton__95fb7 { - align-items: center; - display: flex -} - -.nitroIconSubHeader__95fb7 { - height: 16px; - margin-inline-end:4px;width: 16px -} - -.termsApplyAnchor__95fb7 { - color: var(--text-link); - margin-inline-start:2px;text-decoration: underline; - text-underline-offset: 2px -} - -.termsApplyAnchor__95fb7:hover { - text-decoration: none -} - -.modalTopPill__95fb7 { - inset-inline-start: 50%; - position: absolute; - top: 0; - transform: translate(-50%,-50%) -} - -.tier2Gradient__95fb7 { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color); - color: var(--white) -} - -.root_d06335 { - background: var(--background-surface-high); - max-height: -moz-fit-content; - max-height: fit-content; - overflow: auto; - position: relative; - width: 716px -} - -.root_d06335.largeBorderRadius_d06335 { - border-radius: var(--radius-lg) -} - -.splash_d06335 { - position: absolute; - top: 31% -} - -.headerText_d06335 { - color: var(--text-strong); - font-size: 32px; - font-style: italic; - font-weight: 900; - letter-spacing: -.64px; - line-height: 100%; - text-align: center; - text-transform: uppercase; - width: 620px -} - -.closeButtonContainer_d06335 { - color: var(--interactive-text-default); - cursor: pointer; - inset-inline-end: var(--space-8); - position: absolute; - top: var(--space-8); - z-index: 5 -} - -.video_d06335 { - border-radius: 16px; - height: 349px; - width: 620px -} - -.content_d06335 { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-24); - padding: 48px 44px; - word-break: break-word -} - -.subHeader_d06335 { - font-size: 18px; - font-style: normal; - font-weight: 500; - line-height: 24px; - max-width: 480px -} - -.bodyText_d06335,.subHeader_d06335 { - color: var(--text-strong); - text-align: center -} - -.bodyText_d06335 { - margin-bottom: -8px; - max-width: 500px -} - -.buttonContainer_d06335 { - margin-top: var(--space-8) -} - -.featureCardGroup_d06335 { - color: var(--text-strong); - -moz-column-gap: 8px; - column-gap: 8px; - display: flex; - flex-wrap: wrap; - position: relative -} - -.featureCardGroup_d06335.wideStyle_d06335 { - -moz-column-gap: 10px; - column-gap: 10px; - margin-inline-start:16px;row-gap: 10px -} - -.featureCard_d06335 { - align-items: center; - background: var(--background-surface-highest); - border-radius: var(--radius-lg); - display: flex; - flex-direction: column; - min-height: 140px; - padding: var(--space-20); - position: relative; - width: 159px -} - -.featureCard_d06335.wideStyle_d06335 { - flex-direction: row; - justify-content: center; - width: 317px -} - -.featureCardImg_d06335 { - height: 80px; - width: 120px -} - -.featureCardImg_d06335.wideStyle_d06335 { - height: 111px; - padding: 0 16px; - width: 88px -} - -.featureCardTextGroup_d06335 { - display: flex; - flex-direction: column; - margin-top: var(--space-16); - text-align: start; - width: 100% -} - -.featureCardTextHeader_d06335 { - padding-bottom: 4px -} - -.featureCardTag_d06335 { - background: var(--custom-premium-colors-premium-gradient-tier-2); - border-radius: 10px; - color: #fff; - inset-inline-start: 16px; - position: absolute; - top: -8px -} - -.tag_d06335 { - background-color: var(--brand-500); - font-size: 12px; - line-height: 16px; - padding: 0 6px -} - -.container__577c3 { - background-color: var(--background-mod-muted); - border-radius: var(--radius-sm); - padding: var(--space-20) -} - -.links__577c3 { - display: flex; - flex-direction: column; - gap: var(--space-16); - list-style: none -} - -.link__577c3 { - align-items: center; - color: var(--text-link); - display: flex; - gap: var(--space-4) -} - -.description__577c3 { - text-align: center -} - -/*# sourceMappingURL=0fe7a252fcb6f0c6.css.map*/ diff --git a/discord-html-copy/css/1803f89f7fb95846.css b/discord-html-copy/css/1803f89f7fb95846.css deleted file mode 100644 index 44dbf72..0000000 --- a/discord-html-copy/css/1803f89f7fb95846.css +++ /dev/null @@ -1,5316 +0,0 @@ -.wrapper__48b20 { - background-color: var(--black); - overflow: hidden; - position: relative -} - -.video__48b20,.wrapper__48b20 { - height: 100%; - width: 100% -} - -.video__48b20 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.videoCover__48b20,.videoCover__48b20 video { - -o-object-fit: cover; - object-fit: cover -} - -.videoContain__48b20,.videoContain__48b20 video { - -o-object-fit: contain; - object-fit: contain -} - -.mirror__48b20 { - transform: scaleX(-1) -} - -.previewWrapper__48b20 { - align-content: center; - display: flex; - opacity: 0; - pointer-events: none; - transition: opacity .5s ease -} - -.previewWrapper__48b20.loading__48b20 { - opacity: 1 -} - -.previewImage__48b20 { - filter: blur(10px); - -o-object-fit: contain; - object-fit: contain; - width: 100% -} - -.emptyPreviewWrapper__48b20 { - aspect-ratio: 16/9; - margin: auto; - width: 100% -} - -.emptyPreview__48b20 { - background: linear-gradient(120deg,var(--primary-730) 0,var(--primary-660) 100%); - height: 100%; - opacity: .6; - width: 100% -} - -.spinner__48b20 { - inset-inline-start: 50%; - position: absolute; - top: 50%; - transform: translate(-50%,-50%) -} - -.qualityIndicator__30845 { - align-items: center; - display: flex -} - -.qualityIndicatorHighlightWrapper__30845 { - background-color: var(--primary-400); - border-radius: 16px; - display: flex; - padding: 4px -} - -.clickable__30845 { - cursor: pointer -} - -.qualityIndicatorFullQuality__30845 { - color: var(--white) -} - -.qualityIndicatorLowQuality__30845 { - color: var(--primary-300) -} - -.premiumStreamIcon__30845 { - color: var(--white); - height: 14px; - margin-inline-end:4px;width: 14px -} - -.premiumStreamIconLarge__30845 { - color: var(--white); - height: 16px; - margin-inline-end:4px;width: 16px -} - -.qualityResolution__30845 { - margin-inline-end:4px} - -.qualityIndicatorBadge__30845 { - text-transform: none -} - -.qualityIndicatorBadgePremium__30845 { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color) -} - -.streamQualityIndicator__30845 { - align-items: center; - display: flex; - overflow: hidden -} - -.streamGiftingPopout__30845 { - background-color: var(--brand-500); - border-radius: 8px; - margin-top: 6px; - padding: 16px 16px 12px; - position: relative; - width: 280px -} - -.streamGiftingPopout__30845:after { - border-block-color:transparent var(--brand-500);border-inline-color: transparent; - border-style: solid; - border-width: 8px; - bottom: 100%; - content: ""; - inset-inline-start: 50%; - margin-top: 6px; - margin-inline-start:-8px;position: absolute -} - -.streamGiftingPopoutHeader__30845 { - align-items: center; - display: flex; - justify-content: center -} - -.streamGiftingPopoutCTA__30845 { - display: flex; - justify-content: center; - margin-top: 16px -} - -.streamGiftingPopoutButton__30845 { - display: flex; - justify-content: space-between -} - -.streamGiftingPopoutButtonText__30845 { - color: var(--brand-500) -} - -.streamGiftingPopoutText__30845 { - margin-top: 4px; - text-align: center -} - -.liveIndicator__30845 { - overflow: hidden; - position: relative -} - -.liveQualityIndicator__30845 { - margin-inline-end:2px} - -.giftIcon__30845,.giftIconBrand__30845 { - height: 16px; - width: 16px -} - -.giftIcon__30845 { - color: var(--white); - margin-inline:4px} - -.giftIconBrand__30845 { - color: var(--brand-500); - margin-inline-end:4px} - -.giftButton__30845 { - align-items: center; - display: flex -} - -.wrapper__1505a { - background-color: var(--black); - height: 100%; - overflow: hidden; - position: relative; - width: 100% -} - -.videoContainer__1505a { - bottom: 0; - contain: layout style paint; - height: 100%; - position: absolute; - top: 0; - inset-inline: 0; - width: 100%; - will-change: transform,transform-origin -} - -.videoContainer__1505a.zoomed__1505a { - transform: translate(var(--custom-pan-x),var(--custom-pan-y)) scale(var(--custom-zoom-scale)); - transform-origin: center -} - -.full-motion .videoContainer__1505a.zoomed__1505a { - transition: var(--custom-zoom-transition) -} - -.zoomEnabled__1505a { - cursor: grab -} - -.zoomDragging__1505a { - cursor: grabbing -} - -.minimap__1505a { - border-radius: 8px; - cursor: grab; - height: var(--custom-zoom-minimap-height); - opacity: 1; - overflow: hidden; - position: relative; - transition: opacity .2s ease-in-out; - width: var(--custom-zoom-minimap-width) -} - -.minimap__1505a:active { - cursor: grabbing -} - -.minimap__1505a.fadeOut__1505a { - opacity: 0; - pointer-events: none -} - -.minimapVideo__1505a { - -o-object-fit: contain; - object-fit: contain -} - -.minimapIndicator__1505a,.minimapVideo__1505a { - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.minimapIndicator__1505a:after { - background: var(--opacity-blurple-16); - border: 2px solid var(--opacity-blurple-64); - border-radius: 8px; - box-sizing: border-box; - content: ""; - height: var(--custom-zoom-indicator-height); - inset-inline-start: var(--custom-zoom-indicator-left); - pointer-events: none; - position: absolute; - top: var(--custom-zoom-indicator-top); - width: var(--custom-zoom-indicator-width) -} - -.full-motion .minimapIndicator__1505a:after { - transition: var(--custom-zoom-indicator-transition) -} - -.zoomControlsContainer__1505a { - inset-inline-end: 8px; - pointer-events: none; - position: absolute; - transition: opacity .2s ease-in-out; - z-index: 10 -} - -.zoomControlsContainer__1505a.idle__1505a { - opacity: 0 -} - -.zoomControls__1505a { - align-items: center; - background: var(--background-scrim); - border-radius: 8px; - cursor: default; - display: flex; - flex-direction: column; - gap: 8px; - justify-content: space-between; - margin: 4px; - pointer-events: all -} - -.zoomControlsWithChildren__1505a { - border-radius: 12px; - margin: 0; - padding: 4px -} - -.zoomControlsRow__1505a { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px; - justify-content: space-between; - width: 100% -} - -.wrapper_fc8177 { - box-sizing: border-box; - position: relative; - word-wrap: break-word; - background: var(--background-gradient-chat,var(--background-base-lower)); - contain: paint layout; - flex: 0 0 auto; - overflow: hidden; - padding-inline-end:1rem;-webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.compact_fc8177.wrapper_fc8177 { - padding-bottom: .125rem; - padding-top: .125rem -} - -.cozy_fc8177.wrapper_fc8177 { - padding-inline-start:4.5rem} - -.a11y-font-scaled-up .cozy_fc8177.wrapper_fc8177 { - padding-inline-start:72px} - -.compact_fc8177 .contents_fc8177 { - display: flex; - flex-wrap: wrap; - height: 1.375rem; - overflow: hidden; - padding-inline-start:5rem} - -.compact_fc8177 .contents_fc8177 .content_fc8177 { - display: contents -} - -.cozy_fc8177 .content_fc8177 { - align-items: center; - display: flex; - flex-wrap: wrap; - height: 1.375rem; - overflow: hidden; - text-indent: 0 -} - -.compact_fc8177 .content_fc8177 { - display: inline; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.blob_fc8177 { - flex: 0 0 auto; - height: 1rem; - line-height: 1.375rem; - margin-top: .1875rem; - vertical-align: middle -} - -.cozy_fc8177 .blob_fc8177 { - display: block -} - -.hidden_fc8177 { - visibility: hidden -} - -.compact_fc8177 .blob_fc8177 { - display: inline-block -} - -.cozy_fc8177 .header_fc8177 { - align-items: center; - display: flex; - flex-wrap: wrap; - height: 1.375rem; - line-height: 1.375rem; - overflow: hidden; - position: relative -} - -.compact_fc8177 .header_fc8177 { - margin-inline-end:.5rem} - -.attachmentContainer_fc8177 { - padding-bottom: .125rem; - padding-top: .125rem -} - -.compact_fc8177 .attachmentContainer_fc8177 { - margin-top: .25rem -} - -.attachment_fc8177 { - height: 120px; - margin-top: .125rem; - width: 240px -} - -.compact_fc8177 .attachment_fc8177 { - margin-inline-start:5rem} - -.blob_fc8177+.blob_fc8177 { - margin-inline-start:.25rem} - -.header_fc8177 .blob_fc8177+.blob_fc8177 { - margin-inline-start:.5rem} - -.blob_fc8177 { - border-radius: .5rem -} - -.attachment_fc8177 { - border-radius: 6px -} - -.attachment_fc8177,.avatar_fc8177,.blob_fc8177 { - background-color: var(--text-default) -} - -.avatar_fc8177 { - border-radius: 50%; - flex: 0 0 auto; - height: 2.5rem; - inset-inline-start: 1rem; - max-height: 40px; - max-width: 40px; - position: absolute; - top: .25rem; - width: 2.5rem -} - -.a11y-font-scaled-up .avatar_fc8177 { - inset-inline-start: 16px; - top: 4px -} - -.compactTimestamp_fc8177 { - margin-inline:-4rem .25rem} - -.divider__1fcac { - border-bottom: 1px solid var(--background-mod-strong) -} - -.spacingLarge__1fcac { - margin: var(--space-8) 0 -} - -.root_da9de7.root_da9de7.sizeReduced_da9de7 { - font-size: .875rem; - line-height: calc(var(--chat-markup-line-height)*.875) -} - -.root_da9de7.root_da9de7.sizeReduced_da9de7 small { - font-size: .75rem; - line-height: calc(var(--chat-markup-line-height)*.75) -} - -.root_da9de7.root_da9de7.colorMuted_da9de7 { - color: var(--text-subtle) -} - -.root_da9de7.root_da9de7.colorMuted_da9de7 small { - color: var(--text-muted) -} - -.root_da9de7.root_da9de7.weightReduced_da9de7 { - font-weight: var(--font-weight-normal) -} - -.root_da9de7.root_da9de7.weightReduced_da9de7 h1,.root_da9de7.root_da9de7.weightReduced_da9de7 h2,.root_da9de7.root_da9de7.weightReduced_da9de7 h3,.root_da9de7.root_da9de7.weightReduced_da9de7 h4,.root_da9de7.root_da9de7.weightReduced_da9de7 h5,.root_da9de7.root_da9de7.weightReduced_da9de7 h6,.root_da9de7.root_da9de7.weightReduced_da9de7 strong { - font-weight: var(--font-weight-medium) -} - -.markdownContainer__48344 { - overflow-wrap: anywhere -} - -.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344>p:first-child { - margin-top: 0 -} - -.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344>p:last-child { - margin-bottom: 0 -} - -.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 h1:first-child,.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 h2:first-child,.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 h3:first-child { - margin-top: 0 -} - -.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 h1:not(:has(+*)),.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 h2:not(:has(+*)),.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 h3:not(:has(+*)) { - margin-bottom: 0 -} - -.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 blockquote,.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 pre { - max-width: unset -} - -.slowModeIcon_b21699 { - display: block; - margin-inline-start:4px} - -.cooldownWrapper_b21699 { - gap: 4px; - margin-inline-start:auto;overflow: hidden; - padding: 3px 8px; - padding-inline-end:16px;white-space: nowrap -} - -.cooldownText_b21699,.cooldownWrapper_b21699 { - align-items: center; - display: flex -} - -.cooldownText_b21699 { - border-start-end-radius: 8px; - border-start-start-radius: 8px; - gap: var(--space-4); - transition: color .2s ease -} - -.menu__43313 { - max-height: 70vh; - width: 222px -} - -.customMenuItem__43313 { - margin: -4px 6px 16px -} - -.menu__43313 .customMenuItem__43313 .customNotches__43313 { - color: var(--background-surface-higher) -} - -.wrapper__06283 { - height: calc(var(--custom-gradient-progress-notch-height) + var(--custom-gradient-progress-notch-margin)*2); - min-width: 0; - position: relative; - width: 100% -} - -.container__06283 { - display: flex; - height: 100%; - overflow: hidden; - position: relative -} - -.progress__06283 { - background-color: var(--gradient-progress-pill-background) -} - -.notches__06283,.progress__06283 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.notches__06283 { - width: 100% -} - -.notches__06283.gray__06283 { - color: var(--background-base-low) -} - -.notches__06283.black__06283 { - color: var(--background-surface-high) -} - -.container__011b7 { - display: flex; - flex-direction: column -} - -.micTest__011b7 { - align-items: center; - display: flex; - margin-top: 4px; - position: relative -} - -.title__011b7 { - margin-top: 8px -} - -.description__011b7,.title__011b7 { - margin-bottom: 4px -} - -.micTestCaption__011b7 { - inset-inline-start: 0; - position: absolute; - top: calc(100% + 4px) -} - -.buttonSizer__011b7 { - align-items: center; - display: flex; - margin-top: 4px; - position: absolute; - visibility: hidden; - width: 100% -} - -.buttonSizerSpacer__011b7 { - min-width: 0; - width: 100% -} - -.buttonWrapper__011b7 { - margin-inline-end:var(--space-8)} - -.singleSelectOption__12eef { - -moz-column-gap: 8px; - column-gap: 8px; - display: flex -} - -.deviceContainer__12eef { - align-items: center; - display: grid; - gap: 4px; - grid-template-areas: "label subLabel certifiedPill"; - grid-template-columns: auto 1fr min-content; - width: 100% -} - -.deviceContainer__12eef.withIcon__12eef { - grid-template-areas: "icon label subLabel certifiedPill"; - grid-template-columns: min-content auto 1fr min-content -} - -.deviceContainer__12eef.multiLine__12eef { - grid-template-areas: "label certifiedPill" "subLabel certifiedPill"; - grid-template-columns: 1fr min-content -} - -.deviceContainer__12eef.multiLine__12eef.withIcon__12eef { - grid-template-areas: "icon label certifiedPill" "icon subLabel certifiedPill"; - grid-template-columns: min-content 1fr min-content -} - -.deviceContainer__12eef.multiLine__12eef .deviceSubLabel__12eef:after,.deviceContainer__12eef.multiLine__12eef .deviceSubLabel__12eef:before { - content: "" -} - -.deviceContainer__12eef .deviceIcon__12eef { - align-items: center; - display: flex; - grid-area: icon -} - -.deviceContainer__12eef .deviceLabel__12eef { - grid-area: label -} - -.deviceContainer__12eef .deviceSubLabel__12eef { - grid-area: subLabel; - text-overflow: ellipsis -} - -.deviceContainer__12eef .deviceSubLabel__12eef:before { - content: "(" -} - -.deviceContainer__12eef .deviceSubLabel__12eef:after { - content: ")" -} - -.deviceContainer__12eef .deviceCertifiedPill__12eef { - align-items: center; - display: flex; - grid-area: certifiedPill -} - -.sharedCanvas_c4eba0 { - height: 100%; - position: absolute; - width: 100%; - z-index: 1 -} - -.hidden_d2e74b { - visibility: hidden -} - -.hidden_d2e74b,.whiteFlash_d2e74b { - height: 100%; - position: absolute; - width: 100% -} - -.whiteFlash_d2e74b { - background-color: #fff -} - -.movingImage_d2e74b { - border-radius: var(--space-4); - height: 0; - opacity: 0; - position: fixed; - visibility: hidden; - width: 0 -} - -@keyframes outgoing-call-pulse__2f4f7 { - 0% { - opacity: 0; - transform: scaleX(1) - } - - 11.56% { - opacity: 1 - } - - 39.02% { - opacity: 0; - transform: scale3d(1.025,1.025,1) - } - - 39.03% { - transform: scaleX(1) - } - - 39.04% { - opacity: 1 - } - - 66.48% { - opacity: 0; - transform: scale3d(1.025,1.025,1) - } - - to { - opacity: 0; - transform: scaleX(1) - } -} - -.wrapper__2f4f7 { - display: flex; - position: relative -} - -.wrapper__2f4f7.ringing__2f4f7 { - opacity: .3 -} - -.wrapper__2f4f7.ringing__2f4f7:after { - animation: outgoing-call-pulse__2f4f7 2.5s ease-out infinite; - background: transparent; - border: 3px solid var(--interactive-text-active); - border-radius: 12px; - content: ""; - height: 100%; - inset-inline-start: -3px; - pointer-events: none; - position: absolute; - top: -3px; - transform-origin: center; - width: 100% -} - -.ripple__2f4f7 { - animation: outgoing-call-pulse__2f4f7 2.429s ease-out infinite; - background-color: var(--interactive-text-active); - border-radius: var(--custom-base-tile-border-radius); - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - opacity: 0 -} - -.ripple0__2f4f7 { - animation-delay: 20ms -} - -.ripple1__2f4f7 { - animation-delay: 145ms -} - -.ripple2__2f4f7 { - animation-delay: .27s -} - -.tile__2f4f7 { - cursor: pointer; - display: flex; - flex: 1 1 100%; - position: relative -} - -.tile__2f4f7.idle__2f4f7,.tile__2f4f7.noInteraction__2f4f7 { - cursor: inherit -} - -.tileChild__2f4f7 { - display: flex; - flex: 1 1 100% -} - -.border__2f4f7 { - border-radius: var(--custom-base-tile-border-radius); - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - pointer-events: none -} - -.border__2f4f7.voiceChannelEffect__2f4f7 { - box-shadow: inset 0 0 0 2px var(--brand-500),inset 0 0 0 3px var(--black) -} - -.border__2f4f7.latchedNotSpeaking__2f4f7 { - box-shadow: inset 0 0 0 2px var(--yellow-360),inset 0 0 0 3px var(--black) -} - -.app-focused .border__2f4f7 { - transition: box-shadow .1s ease-out -} - -.noBorder__2f4f7 video { - border-radius: 0 -} - -.noBorder__2f4f7 .border__2f4f7 { - border-radius: 0; - display: none -} - -.overlayContainer__2f4f7 { - inset-inline: 0; - bottom: 0; - margin: 8px; - position: absolute; - top: 0; - transition: opacity .2s ease-in-out -} - -.overlayContainer__2f4f7.compact__2f4f7 { - margin: 4px -} - -.overlayContainer__2f4f7.noPointerEvents__2f4f7 { - pointer-events: none -} - -.overlayTop__2f4f7 { - display: inline-block; - inset-inline-start: 0 -} - -.overlay__2f4f7,.overlayBottom__2f4f7 { - align-items: center; - bottom: 0; - display: flex; - inset-inline: 0; - justify-content: space-between; - line-height: 0; - pointer-events: none; - position: absolute -} - -.overlay__2f4f7 { - padding: 8px; - transition: opacity .2s ease-in-out -} - -.overlayTitle__2f4f7 { - align-items: center; - background: var(--control-overlay-secondary-background-default); - border-radius: var(--custom-base-tile-border-radius); - color: var(--control-overlay-secondary-text-default); - display: flex; - flex: 0 1 auto; - gap: var(--space-4); - margin-inline-end:8px;min-width: 0; - padding: 6px 12px -} - -.overlayTitle__2f4f7.idle__2f4f7 { - opacity: 0 -} - -.experimentOverlayTitle__2f4f7 { - align-items: center; - background: var(--control-overlay-secondary-background-default); - border-radius: var(--custom-base-tile-border-radius); - color: var(--control-overlay-secondary-text-default); - display: flex; - flex: 0 1 auto; - gap: var(--space-4); - min-height: 20px; - min-width: 0; - padding: 6px 8px -} - -.experimentOverlayTitle__2f4f7.idle__2f4f7,.experimentOverlayTitle__2f4f7:empty { - opacity: 0 -} - -.overlayTitleText__2f4f7 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.overlayButton__2f4f7 { - margin-inline-start:var(--space-8);pointer-events: all -} - -.overlayButton__2f4f7.compact__2f4f7 { - margin-inline-start:var(--space-4)} - -.overlayButtonInner__2f4f7 { - align-items: center; - display: flex; - justify-content: center -} - -.overlayButtonContainer__2f4f7 { - display: flex -} - -.overlayContainer__2f4f7:not(:hover,:focus-within) .overlayButton__2f4f7.hideWhenInactive__2f4f7 { - border: none; - margin-inline-start:0;opacity: 0; - padding: 0; - pointer-events: none; - width: 0 -} - -.titleIcon__2f4f7 { - flex: none; - height: 20px; - width: 20px -} - -.experimentTitleIcon__2f4f7 { - flex: none; - height: 16px; - width: 16px -} - -.secureFramesIcon__2f4f7 { - margin-inline-start:4px;pointer-events: all -} - -.statusContainer__2f4f7 { - align-items: center; - display: flex; - flex: 0 0 auto -} - -.status__2f4f7 { - background: var(--control-overlay-secondary-background-default); - border-radius: 50%; - color: var(--control-overlay-secondary-text-default); - height: 20px; - margin-inline-start:8px;padding: 6px; - width: 20px -} - -.status__2f4f7 svg { - height: 100%; - width: 100% -} - -.status__2f4f7.interactive__2f4f7 { - pointer-events: all -} - -.status__2f4f7.interactive__2f4f7:hover { - background-color: var(--opacity-black-80) -} - -.gameIconContainer__2f4f7 { - align-items: center; - background-color: var(--opacity-black-56); - border-radius: 50%; - display: flex; - height: 40px; - justify-content: center; - margin-inline-start:8px;margin-top: -12px; - overflow: hidden; - padding: 4px; - pointer-events: all; - width: 40px -} - -.gameIcon__2f4f7 { - height: 32px; - width: 32px -} - -.gameIconSmall__2f4f7.gameIconContainer__2f4f7 { - height: 32px; - margin-top: 0; - padding: 0; - width: 32px -} - -.gameIconSmall__2f4f7 .gameIcon__2f4f7 { - height: 24px; - width: 24px -} - -.videoDisabledTitle__2f4f7 { - padding-block:0;padding-inline:0 12px} - -.videoDisabledTitle__2f4f7 .status__2f4f7 { - background-color: transparent -} - -.content__2f4f7 { - align-items: center; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - display: flex; - justify-content: center; - overflow: hidden -} - -.streamHidden__2f4f7 { - background: var(--primary-600); - flex-direction: column -} - -.streamHiddenEmptyState__2f4f7 { - margin: 0; - padding: 0 -} - -.streamHiddenCTA__2f4f7 { - align-items: center; - display: flex; - justify-content: center; - padding-top: 4px -} - -.streamPreview__2f4f7 { - background: var(--primary-700) -} - -.streamPreviewOpacity__2f4f7 { - opacity: .15 -} - -.indicators__2f4f7 { - inset-inline-end: 8px; - top: 8px; - z-index: 1 -} - -.indicators__2f4f7,.selectedScreen__2f4f7 { - align-items: center; - display: flex; - position: absolute -} - -.selectedScreen__2f4f7 { - background: var(--opacity-black-60); - justify-content: center; - top: 0; - inset-inline: 0; - bottom: 0; - pointer-events: none -} - -.toggleMute__2f4f7 { - opacity: 1; - transition: opacity .2s ease -} - -.toggleMute__2f4f7.interactive__2f4f7 { - pointer-events: all -} - -.toggleMute__2f4f7.interactive__2f4f7:hover { - background-color: var(--opacity-black-80) -} - -.hidden__2f4f7 { - opacity: 0 -} - -.selectedIcon__2f4f7 { - color: var(--white); - height: 32px; - width: 32px -} - -.localMuteStrikethrough__2f4f7 { - color: currentColor -} - -.liveIndicator__2f4f7 { - margin-inline-start:8px} - -.addStreamIcon__2f4f7 { - height: 24px; - width: 24px -} - -.cta__2f4f7 { - z-index: 1 -} - -.addCta__2f4f7 { - margin-inline-start:8px} - -.absoluteFill__2f4f7 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.videoWrapper__2f4f7 { - background-color: var(--primary-700) -} - -.blocked__2f4f7 { - background-color: var(--white); - border-radius: var(--radius-round) -} - -.blocked__2f4f7,.ignored__2f4f7 { - height: 16px; - width: 16px -} - -.blockedIcon__2f4f7 { - height: 12px; - margin: 2px; - width: 12px -} - -.blockedAvatar__2f4f7 { - opacity: .5 -} - -.small__2f4f7 .status__2f4f7 { - margin-inline-start:4px;padding: 4px -} - -.small__2f4f7 .addStreamIcon__2f4f7,.small__2f4f7 .status__2f4f7,.small__2f4f7 .stopStreamIcon__2f4f7 { - height: 16px; - width: 16px -} - -.small__2f4f7 .darkCircle__2f4f7 { - padding: 8px -} - -.streamUnavailable__2f4f7 { - flex-direction: column -} - -.streamUnavailableText__2f4f7 { - padding-inline:8px;text-align: center -} - -.darkCircle__2f4f7 { - align-items: center; - background: rgba(0,0,0,.3); - border-radius: 50%; - display: flex; - justify-content: center; - margin-top: 8px; - padding: 16px; - z-index: 1 -} - -.voiceChannelEffectsContainer__2f4f7 { - height: 100%; - position: absolute -} - -.stopStreamIcon__2f4f7 { - height: 24px; - width: 24px -} - -.stopStreamForeground__2f4f7 { - color: var(--red-400) -} - -.enable-forced-colors .border__2f4f7 { - box-shadow: inset 0 0 0 1px ButtonText; - forced-color-adjust: none -} - -.enable-forced-colors .border__2f4f7.voiceChannelEffect__2f4f7 { - box-shadow: inset 0 0 0 2px Highlight,inset 0 0 0 3px var(--black) -} - -.root__14990 { - line-height: 0 -} - -.warningIcon__14990 { - color: var(--yellow-300); - height: 20px; - width: 20px -} - -.errorCodeMessage__14990 { - margin-top: var(--space-8) -} - -.popout__14990 { - background-color: var(--background-surface-high); - border-radius: var(--radius-sm); - box-shadow: inset 0 0 0 1px var(--border-subtle),var(--shadow-high); - color: var(--text-default); - padding: var(--space-8) var(--space-12); - width: 200px -} - -.graphContainer_effb26 { - background-color: var(--background-base-lower); - margin-bottom: 8px; - padding-bottom: 2px -} - -.separator_effb26 { - border: none; - border-top: 1px solid var(--border-subtle); - margin: 16px 0 -} - -.popoutText_effb26 { - color: var(--text-default); - font-size: 14px; - line-height: 18px; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.popoutText_effb26 strong { - font-weight: var(--font-weight-bold) -} - -.popoutText_effb26.popoutTextDetails_effb26 { - color: var(--text-muted); - margin-top: 8px -} - -.popoutBottom_effb26 { - display: flex -} - -.debugButton_effb26 { - color: var(--text-link); - cursor: pointer; - font-size: 12px; - font-weight: var(--font-weight-medium); - text-decoration: none -} - -.debugButton_effb26:hover { - text-decoration: underline -} - -.copyStatsButton_effb26 { - color: var(--text-link); - cursor: pointer; - font-size: 12px; - font-weight: var(--font-weight-medium); - margin-inline-start:8px;text-decoration: none -} - -.copyStatsButton_effb26:hover { - text-decoration: underline -} - -.textWithIcon_effb26 { - align-items: center; - display: flex; - gap: var(--space-4) -} - -.secured_effb26 { - color: var(--text-feedback-positive); - display: flex; - flex: 1; - font-size: 12px; - font-weight: var(--font-weight-medium) -} - -.secured_effb26 .lockIcon_effb26 { - margin-inline-end:4px} - -.enable-forced-colors .container_effb26 { - border: 2px solid CanvasText -} - -.custom-theme-background .container_effb26 { - border: 1px solid var(--border-strong) -} - -.code__1a1f5 { - background-color: var(--background-code); - display: grid; - grid-template-columns: repeat(var(--secure-frames-columns),1fr); - overflow: hidden; - padding: 12px; - row-gap: 16px -} - -.chunk__1a1f5 { - text-align: center -} - -.divider__1a1f5 { - position: relative -} - -.divider__1a1f5:after { - background-color: var(--border-subtle); - content: ""; - height: 1px; - inset-inline-start: 0; - position: absolute; - top: -8px; - width: 100%; - z-index: 1 -} - -.codeText__1a1f5 { - font-weight: 500 -} - -.loading__1a1f5 { - align-items: center; - display: flex; - justify-content: center; - min-height: 104px -} - -.container_e03deb { - cursor: pointer; - padding: 2px -} - -.container__9351a { - display: flex; - flex-direction: column -} - -.tag__9351a { - align-self: flex-start; - background-color: var(--background-base-low); - border-radius: 4px; - padding: 4px -} - -.header__9351a,.tag__9351a { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px; - margin-bottom: 4px -} - -.header__9351a { - margin-top: 16px -} - -.code__9351a { - border-radius: 8px -} - -.container__50387 { - background: var(--background-surface-high); - border-radius: 4px; - box-shadow: var(--shadow-border),var(--shadow-high); - box-sizing: border-box; - padding: 16px; - width: 300px -} - -.tabs__50387,.title__50387 { - margin-bottom: 16px -} - -.tabs__50387 { - position: relative -} - -.tabs__50387:after { - background-color: var(--border-subtle); - bottom: 0; - content: ""; - height: 1px; - inset-inline-start: 0; - position: absolute; - width: 100%; - z-index: 0 -} - -.tabBarItem__50387 { - padding-bottom: 8px -} - -.debugPanelStandalone__50387 { - background: var(--background-surface-high); - border-radius: 4px; - box-shadow: var(--shadow-border),var(--shadow-high); - width: 290px -} - -.debugPanelStandalone__50387>.debugPanelSection__50387 { - padding: 16px -} - -.hoverableStatus_cdcd10 { - cursor: pointer -} - -.ping__06d62 { - color: var(--text-muted); - display: block; - flex: 0 0 auto; - height: 16px; - margin-inline:0 4px;width: 16px -} - -.ping__06d62.largePing__06d62 { - background: var(--background-mod-muted); - border-radius: 8px; - box-shadow: inset 0 0 0 1px var(--border-muted); - height: 18px; - margin-inline-end:8px;padding: 7px; - width: 18px -} - -.clickablePing__06d62 { - cursor: pointer -} - -.rtcConnectionStatus__06d62 { - align-items: center; - display: flex; - height: 32px; - overflow: hidden; - padding-bottom: 0; - text-overflow: ellipsis; - white-space: nowrap -} - -.rtcConnectionStatusLabel__06d62 { - font-weight: var(--font-weight-semibold); - height: 20px; - margin-top: -1px -} - -.rtcConnectionStatusConnected__06d62 { - color: var(--text-feedback-positive) -} - -.rtcConnectionStatusConnecting__06d62 { - color: var(--text-feedback-warning) -} - -.rtcConnectionStatusError__06d62 { - color: var(--text-feedback-critical) -} - -.rtcConnectionQualityFine__06d62 .largePing__06d62 { - background: var(--background-feedback-positive) -} - -.rtcConnectionQualityAverage__06d62 .largePing__06d62 { - background: var(--background-feedback-warning) -} - -.rtcConnectionQualityBad__06d62 .largePing__06d62 { - background: var(--background-feedback-critical) -} - -.labelWrapper__06d62 { - overflow: hidden -} - -.rtcConnectionStatusWrapper__06d62 { - display: flex; - flex-direction: column; - padding-bottom: 0 -} - -.base_b88801 { - align-items: center; - bottom: 1px; - color: var(--text-subtle); - display: flex; - font-size: 12px; - font-weight: 500; - height: 24px; - inset-inline: var(--space-16) 16px; - line-height: 16px; - overflow-y: hidden; - position: absolute; - resize: none -} - -.base_b88801 strong { - font-weight: var(--font-weight-bold) -} - -.base_b88801 .text_b88801 { - display: block; - margin-inline-start:4px;min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.base_b88801 .ellipsis_b88801 { - display: block; - flex: 0 0 auto; - margin-inline-start:0} - -.base_b88801 .typingDots_b88801 { - align-items: center; - display: flex; - flex: 1 1 auto; - overflow: hidden; - text-overflow: ellipsis -} - -.inTextChannel_b88801.base_b88801 { - bottom: unset; - top: -24px; - inset-inline: 0; - padding-inline-start:var(--space-md);pointer-events: all -} - -.inTextChannel_b88801 .typingDots_b88801 { - margin-inline-start:8px} - -.inTextChannel_b88801 .text_b88801 { - margin-inline-start:calc(var(--space-md) + var(--space-8));z-index: 1 -} - -.inTextChannel_b88801 .text_b88801>strong { - color: var(--text-default); - font-weight: var(--font-weight-semibold) -} - -.typing_b88801 { - overflow: visible -} - -.activityInviteEducation_b88801 { - bottom: unset; - margin-inline-start:-8px;opacity: 0; - top: -24px; - inset-inline: 0; - padding-inline-start:var(--space-md);pointer-events: all; - z-index: 1 -} - -.activityInviteEducationFadeIn_b88801 { - opacity: 1 -} - -.activityInviteEducationArrow_b88801 { - background-repeat: no-repeat; - background-size: 12px 10px; - display: inline-block; - height: 15px; - margin-block:0;margin-inline:24px 2px;transform: scaleY(-1); - width: 15px -} - -.images-light .activityInviteEducationArrow_b88801 { - background-image: url(/assets/3b11031474fb64a0.svg) -} - -.images-dark .activityInviteEducationArrow_b88801 { - background-image: url(/assets/6ad5446be34bb27a.svg) -} - -@use postcss-pxtorem;.expanded__7a70a { - background-color: var(--message-background-hover) -} - -.blockedSystemMessage__7a70a { - font-size: 1rem; - line-height: 1.375rem; - text-indent: 0; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.blockedIcon__7a70a { - color: var(--text-muted); - display: block; - height: 1rem; - width: 1rem -} - -.container__7a70a:hover .blockedIcon__7a70a { - color: var(--icon-feedback-critical) -} - -.blockedIconClickable__7a70a { - cursor: pointer -} - -.blockedIconClickable__7a70a:hover .blockedIcon__7a70a { - color: var(--interactive-text-active) -} - -.blockedMessageText__7a70a { - color: var(--text-default) -} - -.blockedAction__7a70a { - color: var(--text-muted); - cursor: pointer -} - -.blockedSystemMessage__7a70a:hover .blockedAction__7a70a { - color: var(--text-link) -} - -.blockedAction__7a70a:hover { - text-decoration: underline -} - -.wrapper__63f6b { - margin-bottom: var(--space-16); - position: relative -} - -.column__63f6b { - flex: 1 1 50% -} - -.modalContent__46773 { - display: flex; - flex-direction: column; - gap: var(--custom-modal-padding) -} - -.iconSection__46773 { - flex-direction: column; - gap: 12px -} - -.iconContainer__46773,.iconSection__46773 { - align-items: center; - display: flex; - justify-content: center -} - -.iconContainer__46773 { - cursor: pointer; - height: 120px; - margin: 0 auto; - position: relative; - width: 120px -} - -.iconContainer__46773.petite__46773 { - height: 80px; - width: 80px -} - -.iconContainer__46773 .pencilIconWrapper__46773 { - align-items: center; - background: var(--background-mod-subtle); - border: 4px solid var(--modal-background); - border-radius: 50%; - color: var(--interactive-text-default); - display: flex; - height: 32px; - inset-inline-end: -4px; - justify-content: center; - position: absolute; - top: -4px; - width: 32px -} - -.theme-light .iconContainer__46773 .pencilIconWrapper__46773 { - background: var(--background-mod-normal) -} - -.iconContainer__46773.petite__46773 .pencilIconWrapper__46773 { - height: 24px; - width: 24px -} - -.iconContainer__46773:hover .pencilIconWrapper__46773 { - color: var(--interactive-text-active) -} - -.iconPlaceholder__46773 { - align-items: center; - background-color: var(--background-mod-subtle); - border-radius: 50%; - color: var(--background-mod-normal); - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.iconContainer__46773:hover .iconPlaceholder__46773 { - background-color: var(--background-mod-normal); - outline: 1px solid var(--border-muted) -} - -.iconImage__46773 { - border-radius: 50%; - height: 100%; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.friendWrapper_bbd192 { - cursor: pointer -} - -.friendWrapper_bbd192.disabled_bbd192 { - cursor: not-allowed -} - -.disabled_bbd192 .avatar_bbd192 { - opacity: 50% -} - -.disabled_bbd192 .nickname_bbd192,.friend_bbd192 { - color: var(--text-muted) -} - -.friend_bbd192 { - border-radius: var(--radius-sm); - box-sizing: border-box; - font-size: 14px; - font-weight: var(--font-weight-normal); - gap: 8px; - height: 48px; - overflow: hidden; - padding: 8px; - text-overflow: ellipsis; - white-space: nowrap -} - -.friend_bbd192 .avatar_bbd192 { - flex-shrink: 0; - margin: 0 -} - -.friend_bbd192 .checkbox_bbd192 { - flex: 0 0 auto -} - -.friend_bbd192 .match_bbd192 { - align-items: baseline; - display: flex; - flex: 1 1 auto; - flex-direction: column; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.friend_bbd192 .discordTag_bbd192 { - color: var(--text-muted) -} - -.theme-dark .friendSelected_bbd192 { - background-color: hsl(var(--primary-500-hsl)/.3) -} - -.theme-light .friendSelected_bbd192 { - background-color: hsl(var(--primary-200-hsl)/.3) -} - -.full-motion .friend_bbd192 { - transition: background-color .2s ease -} - -.enable-forced-colors .friendSelected_bbd192 { - outline: 2px solid Highlight -} - -.friendSelected_bbd192 { - background-color: var(--background-mod-subtle); - color: var(--text-strong) -} - -.searchBar_cba592 { - align-items: flex-start; - display: flex; - flex-direction: row; - gap: 8px -} - -.customizationContainer_cba592 { - display: grid; - gap: 8px; - grid-template-columns: auto 1fr; - margin-bottom: var(--space-16) -} - -.iconSelector_cba592 { - grid-column: 1; - grid-row: 1/3; - padding-inline-end:8px} - -.channelNameLabel_cba592 { - display: flex; - flex-direction: column; - grid-column: 2; - justify-content: flex-end -} - -.scroller_cba592 { - flex: 1 1 auto; - margin-top: 4px; - padding: 0 12px -} - -.scrollerInner_cba592 { - margin-inline:-8px} - -.mobileToolsContainer_cba592 { - inset-inline-end: 16px; - position: absolute; - top: 16px -} - -.mobileToolsCloseIcon_cba592>div { - flex: 0 0 24px; - height: 24px; - width: 24px -} - -.noResults_cba592 { - padding: 20px; - text-align: center -} - -.selectExistingFormHeader_cba592 { - margin-bottom: 8px; - margin-top: 16px -} - -.confirmChannelItemContainer_cba592 { - align-items: center; - border-radius: 4px; - display: flex; - flex-direction: row; - padding: 8px -} - -.confirmChannelName_cba592 { - color: var(--text-strong); - font-size: 14px; - font-weight: var(--font-weight-medium); - margin-inline-start:8px} - -.lastActiveTimestamp_cba592 { - color: var(--text-muted); - font-size: 12px; - margin-inline-start:8px;margin-top: 2px -} - -.confirmChannelItemContainer_cba592:hover { - background-color: var(--interactive-background-hover); - cursor: pointer -} - -@media (max-width: 485px) { - .noResults_cba592 { - max-height:100% - } -} - -.sectionHeader_cba592 { - align-items: center; - color: var(--text-muted); - cursor: pointer; - display: flex; - gap: var(--space-xxs); - height: calc(var(--custom-invite-section-header-height)*1px); - padding-inline:12px;padding-top: calc(var(--custom-invite-section-header-gap)*1px) -} - -.sectionSubheader_cba592 { - padding-inline:12px;padding-bottom: 8px -} - -.sectionToggleIcon_cba592 { - transform: rotate(var(--custom-icon-collapse-rotate)); - transition: transform .1s linear -} - -.sectionInfoIcon_cba592,.sectionToggleIcon_cba592 { - height: 12px; - width: 12px -} - -.floaterWrapper__1836e { - bottom: 80px; - inset-inline-start: 0; - position: absolute; - width: 100% -} - -.visibleFloater__1836e { - background: var(--background-surface-highest); - border: 1px solid var(--border-muted); - border-radius: 8px; - box-shadow: 0 12px 32px 0 rgba(0,0,0,.24); - display: flex; - flex-direction: column; - gap: 8px; - margin: 0 8px; - padding: 8px -} - -.progressText__1836e { - display: flex; - gap: 8px; - justify-content: space-between -} - -.floaterWrapper__729b7 { - bottom: 80px; - inset-inline-start: 0; - position: absolute; - width: 100% -} - -.visibleFloater__729b7 { - background: var(--background-feedback-critical); - border-radius: 8px; - box-shadow: 0 12px 32px 0 rgba(0,0,0,.24); - margin: 0 8px; - padding: 8px -} - -.voiceFilterHero_cb3698 { - box-shadow: var(--shadow-top-high); - display: flex; - flex-direction: row; - flex-shrink: 0; - height: 72px; - padding: 0 16px; - position: relative -} - -.voiceFilterHero_cb3698 .bgGradient_cb3698 { - inset: 0; - pointer-events: none; - position: absolute -} - -.voiceFilterHero_cb3698 .bgGradientHighlight_cb3698 { - background: radial-gradient(circle at 11% 100%,hsla(0,0%,100%,.17) 0,hsla(0,0%,100%,.1) 15%,hsla(0,0%,100%,.05) 30%,hsla(0,0%,100%,.03) 45%,hsla(0,0%,100%,.01) 60%,hsla(0,0%,100%,0) 75%) -} - -.voiceFilterHero_cb3698:after,.voiceFilterHero_cb3698:before { - background: linear-gradient(90deg,hsla(0,0%,100%,.2),hsla(0,0%,100%,0)); - content: ""; - height: 1px; - inset: unset 0; - pointer-events: none; - position: absolute -} - -.voiceFilterHero_cb3698:before { - top: 0 -} - -.voiceFilterHero_cb3698:after { - bottom: 0 -} - -.voiceFilterHero_cb3698 .iconWrapper_cb3698 { - align-items: center; - display: flex; - justify-content: center; - position: relative; - width: 88px -} - -.voiceFilterHero_cb3698 .activeVoiceIcon_cb3698 { - bottom: 0; - height: 88px; - inset-inline-start: 0; - position: absolute; - transform-origin: center bottom; - width: 88px -} - -.tooltip_cb3698 { - max-width: 240px -} - -.tooltipContent_cb3698 { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - text-align: center -} - -.tooltipContent_cb3698 .tooltipHeader_cb3698 { - padding-bottom: 2px -} - -.buttonWrapper_cb3698 { - align-self: center; - display: flex; - flex-direction: row; - gap: 8px -} - -.descriptionWrapper_cb3698 { - flex-grow: 1; - overflow: hidden; - padding-inline-start:16px;position: relative -} - -.description_cb3698 { - display: flex; - flex-direction: column; - gap: 2px; - height: 100%; - justify-content: center; - position: absolute; - transform-origin: left center -} - -.button_cb3698,.offWhiteText_cb3698 { - opacity: .7 -} - -.button_cb3698 { - align-items: center; - align-self: center; - border-radius: 8px; - cursor: pointer; - display: flex; - justify-content: center; - padding: 10px -} - -.button_cb3698.loopbackEnabled_cb3698 { - background: hsla(0,0%,100%,.08) -} - -.button_cb3698:hover { - background: hsla(0,0%,100%,.1); - opacity: 1 -} - -.countdownContainer__8fece { - align-items: center; - background: var(--background-surface-highest); - border-radius: 8px; - box-shadow: 0 12px 32px 0 rgba(0,0,0,.24); - display: flex; - flex: 1; - justify-content: space-between; - margin: var(--space-8); - padding: 8px -} - -.iconContainer__8fece { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px; - justify-content: center -} - -.upsellText__8fece { - flex: 1; - margin-block:auto;margin-inline:8px 16px} - -.clockIcon__8fece { - color: var(--white) -} - -.clockIconWrapper__8fece { - background-color: var(--brand-500); - border-radius: 50%; - width: 24px -} - -.clockIconWrapper__8fece,.digitContainer__8fece { - align-items: center; - display: flex; - height: 24px; - justify-content: center -} - -.digitContainer__8fece { - background-color: var(--background-mod-subtle); - border-radius: 4px; - color: var(--text-strong); - width: 16px -} - -.digitsContainer__8fece { - display: flex; - flex-direction: row; - gap: 2px -} - -.colonContainer__8fece { - display: flex; - flex-direction: column; - gap: 3px; - justify-content: center; - padding: 0 4px -} - -.animatedDigit__8fece { - height: 1.1rem; - overflow: hidden; - position: relative; - width: 1rem -} - -.tinyDot__8fece { - background-color: var(--text-muted); - border-radius: 50%; - height: 2px; - width: 2px -} - -.isScrolled__8fece { - background: var(--background-surface-higher); - box-shadow: none -} - -.profile_ed0705 { - border-radius: 16px; - display: flex; - height: 80px; - overflow: hidden; - position: relative; - width: 80px -} - -.profile_ed0705.underDevelopment_ed0705 { - filter: grayscale(100%) -} - -.insetBorder_ed0705 { - border: 1px solid var(--border-subtle); - border-radius: 16px; - box-sizing: border-box; - inset-inline-start: 0; - pointer-events: none; - top: 0 -} - -.insetBorder_ed0705,.thumbnail_ed0705 { - height: 80px; - position: absolute; - width: 80px -} - -.filterName_ed0705 { - width: 88px -} - -.downloadRequiredContent_ed0705,.filterName_ed0705 { - align-items: center; - display: flex; - gap: 2px; - justify-content: center -} - -.downloadRequiredContent_ed0705 { - flex-direction: column -} - -.iconCircle_ed0705 { - background: var(--background-base-lowest); - border-radius: 20px; - bottom: -7px; - display: flex; - height: -moz-fit-content; - height: fit-content; - inset-inline-end: -7px; - padding: 2px; - position: absolute; - width: -moz-fit-content; - width: fit-content -} - -.lockedCircle_ed0705 { - background: linear-gradient(0deg,var(--background-mod-subtle) 0,var(--background-mod-subtle) 100%),var(--background-mod-normal); - border: 3px solid var(--background-base-lowest); - border-radius: 20px; - height: 18px; - width: 18px -} - -.clockCircle_ed0705,.lockedCircle_ed0705 { - align-items: center; - display: flex; - flex-shrink: 0; - justify-content: center -} - -.clockCircle_ed0705 { - background: var(--brand-500); - border-radius: 20px; - height: 24px; - width: 24px -} - -.iconBorder_ed0705 { - padding: 3px -} - -.lockedIcon_ed0705 { - color: var(--text-muted) -} - -.clockIcon_ed0705 { - color: var(--white) -} - -.hoverButtonCircle_ed0705 { - align-items: center; - background: var(--opacity-black-48); - border-radius: 20px; - display: flex; - height: 24px; - justify-content: center; - transition-duration: 80ms; - width: 24px -} - -.hoverButtonCircle_ed0705 svg { - opacity: .8 -} - -.previewButton_ed0705 { - inset-inline-start: 12px; - position: absolute; - top: 12px -} - -.filter_ed0705,.selector_ed0705 { - height: 104px; - width: 96px -} - -.selector_ed0705 { - align-items: center; - display: flex; - flex-direction: column; - gap: 8px; - justify-content: flex-start; - text-align: center -} - -.filter_ed0705 { - border-radius: 8px; - cursor: pointer; - flex-shrink: 0; - padding: 8px 0; - position: relative; - transition: background-color; - transition-duration: 80ms -} - -.filter_ed0705:hover { - background-color: var(--interactive-background-hover) -} - -.filter_ed0705:hover .iconCircle_ed0705 { - background: linear-gradient(0deg,var(--interactive-background-hover),var(--interactive-background-hover)),linear-gradient(0deg,var(--background-base-lowest),var(--background-base-lowest)) -} - -.filter_ed0705.selected_ed0705 .profile_ed0705 { - outline: 2px solid var(--brand-500); - outline-offset: 2px -} - -.filter_ed0705.locked_ed0705 .profile_ed0705 { - filter: grayscale(100%) -} - -.filter_ed0705 .profile_ed0705 { - background: radial-gradient(100% 100% at 50% 100%,color-mix(in srgb,var(--custom-voice-filter-icon-bg-color-two) 50%,transparent) 0,color-mix(in srgb,var(--custom-voice-filter-icon-bg-color-two) 5%,transparent) 100%) -} - -.filter_ed0705:hover .profile_ed0705 { - background: radial-gradient(100% 100% at 0 50%,color-mix(in srgb,var(--custom-voice-filter-icon-bg-color-one) 20%,transparent) 0,transparent 100%),radial-gradient(100% 100% at 50% 100%,color-mix(in srgb,var(--custom-voice-filter-icon-bg-color-two) 50%,transparent) 0,color-mix(in srgb,var(--custom-voice-filter-icon-bg-color-two) 5%,transparent) 100%) -} - -.filter_ed0705.selected_ed0705 .profile_ed0705 { - background: radial-gradient(100% 100% at 0 50%,color-mix(in srgb,var(--custom-voice-filter-icon-bg-color-one) 40%,transparent) 0,transparent 100%),radial-gradient(100% 100% at 50% 100%,var(--custom-voice-filter-icon-bg-color-two) 0,color-mix(in srgb,var(--custom-voice-filter-icon-bg-color-two) 20%,transparent) 100%) -} - -.filter_ed0705 .hoverButtonCircle_ed0705 { - opacity: 0; - transform: translateY(-4px) -} - -.filter_ed0705 .hoverButtonCircle_ed0705.visible_ed0705,.filter_ed0705:hover .hoverButtonCircle_ed0705,.keyboard-mode .filter_ed0705:focus-within .hoverButtonCircle_ed0705 { - opacity: 1; - transform: translateY(0) -} - -.iconTreatmentsWrapper_ed0705 { - align-items: center; - display: flex; - height: -moz-fit-content; - height: fit-content; - justify-content: center; - position: relative; - width: -moz-fit-content; - width: fit-content -} - -.checkmark_ed0705 { - color: var(--brand-500) -} - -.spinner_ed0705 { - height: 40px; - width: 40px -} - -.spinner_ed0705 span { - animation-duration: 1s -} - -.spinnerWrapper_ed0705 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - position: absolute; - width: 100% -} - -.skye_ed0705 { - --custom-voice-filter-icon-bg-color-one: #35ed7e; - --custom-voice-filter-icon-bg-color-two: #f260c3 -} - -.skye_ed0705 .thumbnail_ed0705 { - height: 80px; - inset-inline-start: -8px; - top: 8px; - width: 80px -} - -.quinn_ed0705 { - --custom-voice-filter-icon-bg-color-one: #68bcfe; - --custom-voice-filter-icon-bg-color-two: #5f549d -} - -.quinn_ed0705 .thumbnail_ed0705 { - height: 88px; - inset-inline-start: -14px; - top: 8px; - width: 88px -} - -.axel_ed0705 { - --custom-voice-filter-icon-bg-color-one: #68bdff; - --custom-voice-filter-icon-bg-color-two: #285436 -} - -.axel_ed0705 .thumbnail_ed0705 { - height: 88px; - inset-inline-start: -10px; - top: 8px; - width: 88px -} - -.sebastien_ed0705 { - --custom-voice-filter-icon-bg-color-one: #68bcfe; - --custom-voice-filter-icon-bg-color-two: #5c771f -} - -.sebastien_ed0705 .thumbnail_ed0705 { - height: 80px; - inset-inline-start: -8px; - top: 8px; - width: 80px -} - -.megaphone_ed0705 { - --custom-voice-filter-icon-bg-color-one: #7fe986; - --custom-voice-filter-icon-bg-color-two: #e65acd -} - -.megaphone_ed0705 .thumbnail_ed0705 { - height: 64px; - inset-inline-start: 9px; - top: 7px; - width: 64px -} - -.robot_ed0705 { - --custom-voice-filter-icon-bg-color-one: #35ec7d; - --custom-voice-filter-icon-bg-color-two: #3d484f -} - -.robot_ed0705 .thumbnail_ed0705 { - height: 80px; - inset-inline-start: -8px; - top: 12px; - width: 80px -} - -.tunes_ed0705 { - --custom-voice-filter-icon-bg-color-one: #ff4cd2; - --custom-voice-filter-icon-bg-color-two: #6f86fd -} - -.tunes_ed0705 .thumbnail_ed0705 { - height: 88px; - inset-inline-start: -8px; - top: 7px; - width: 88px -} - -.ghost_ed0705 { - --custom-voice-filter-icon-bg-color-one: #ff4cd2; - --custom-voice-filter-icon-bg-color-two: #6e499c -} - -.ghost_ed0705 .thumbnail_ed0705 { - height: 64px; - inset-inline-start: 10px; - top: 8px; - width: 64px -} - -.spacebunny_ed0705 { - --custom-voice-filter-icon-bg-color-one: #76ea89; - --custom-voice-filter-icon-bg-color-two: #6e3cdf -} - -.spacebunny_ed0705 .thumbnail_ed0705 { - height: 72px; - inset-inline-start: 2px; - top: 8px; - width: 72px -} - -.justus_ed0705 { - --custom-voice-filter-icon-bg-color-one: #68bdff; - --custom-voice-filter-icon-bg-color-two: #0556f8 -} - -.justus_ed0705 .thumbnail_ed0705 { - height: 80px; - inset-inline-start: -4px; - top: 10px; - width: 80px -} - -.harper_ed0705 { - --custom-voice-filter-icon-bg-color-one: #ff4cd2; - --custom-voice-filter-icon-bg-color-two: #7d57b3 -} - -.harper_ed0705 .thumbnail_ed0705 { - height: 80px; - inset-inline-start: -2px; - top: 8px; - width: 80px -} - -.villain_ed0705 { - --custom-voice-filter-icon-bg-color-one: #35ed7e; - --custom-voice-filter-icon-bg-color-two: #db0222 -} - -.villain_ed0705 .thumbnail_ed0705 { - height: 80px; - inset-inline-start: -6px; - top: 8px; - width: 80px -} - -.solara_ed0705 { - --custom-voice-filter-icon-bg-color-one: #ff4cd2; - --custom-voice-filter-icon-bg-color-two: #d69b38 -} - -.solara_ed0705 .thumbnail_ed0705 { - height: 80px; - inset-inline-start: -6px; - top: 8px; - width: 80px -} - -.cave_ed0705 { - --custom-voice-filter-icon-bg-color-one: #7cbcf9; - --custom-voice-filter-icon-bg-color-two: #cf7a7c -} - -.cave_ed0705 .thumbnail_ed0705 { - height: 80px; - inset-inline-start: 0; - top: 8px; - width: 80px -} - -.deepfried_ed0705 { - --custom-voice-filter-icon-bg-color-one: #7fe987; - --custom-voice-filter-icon-bg-color-two: #fdb12d -} - -.deepfried_ed0705 .profile_ed0705 { - overflow: visible -} - -.deepfried_ed0705 .thumbnail_ed0705 { - height: 64px; - inset-inline-start: 6px; - top: 10px; - width: 64px -} - -.row__6500b { - display: flex; - height: 120px; - position: relative -} - -.container__6500b { - margin-inline-start:8px} - -.spacer__6500b { - align-self: stretch; - background: var(--border-subtle); - height: 1px -} - -.header__6500b { - border-top: 1px solid var(--border-subtle); - padding: 16px 0 -} - -.header__6500b:first-child { - border-top: 0 -} - -.loading__6500b { - padding: 16px 16px 80px -} - -.iconMessage__6500b,.loading__6500b { - align-items: center; - box-sizing: border-box; - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.iconMessage__6500b { - color: var(--text-muted); - flex-direction: column; - padding: 16px; - row-gap: 16px -} - -.voiceFiltersPopout_e2f668 { - background: var(--background-base-lowest); - border-radius: 16px; - box-shadow: var(--shadow-high); - display: flex; - flex-direction: column; - flex-shrink: 0; - outline: 1px solid var(--border-subtle); - position: relative; - width: 400px -} - -.voiceFiltersPopout_e2f668.wide_e2f668 { - width: 504px -} - -.voiceFiltersPopout_e2f668.notResizable_e2f668 { - padding-top: 16px -} - -.voiceFiltersFooter_e2f668 { - background-color: var(--background-mod-normal); - border-radius: 0 0 16px 16px; - border-top: 1px solid transparent; - display: flex; - flex-direction: row; - gap: 8px; - padding: 16px; - z-index: 1 -} - -.voiceFiltersFooter_e2f668:not(.hasActiveVoice_e2f668) { - border-top: 1px solid var(--border-subtle) -} - -.voiceFiltersFooter_e2f668,.voiceFiltersPopout_e2f668 { - background: var(--background-surface-high) -} - -.resizeHandle_e2f668 { - align-items: center; - cursor: ns-resize; - display: flex; - justify-content: center; - margin-top: -2px; - padding: 8px 0 -} - -.resizePill_e2f668 { - background: var(--background-mod-subtle); - border-radius: 2px; - height: 4px; - width: 48px -} - -.upsell_e2f668 { - background: var(--background-surface-highest); - border: 1px solid var(--border-muted); - border-radius: 8px; - box-shadow: 0 12px 32px 0 rgba(0,0,0,.24); - height: 22px; - margin: 8px; - padding: 8px -} - -.upsellCountdownContainer_e2f668 { - bottom: -50px; - margin-top: -104px; - position: relative -} - -.upsellContainer_e2f668 { - bottom: 50px; - position: relative -} - -.spacing_fd14e0 { - margin-bottom: 20px -} - -.spacingTop_fd14e0 { - margin-top: 20px -} - -.message_fd14e0 { - background-color: var(--background-base-low); - border-radius: 3px; - box-shadow: var(--legacy-elevation-border),var(--legacy-elevation-high); - overflow: hidden; - padding-bottom: 10px; - padding-top: 10px; - pointer-events: none; - position: relative -} - -.closeButton_fd14e0 { - justify-content: flex-end -} - -.inner__36c1b { - padding: 0 16px -} - -.body__36c1b,.inner__36c1b { - display: flex; - flex-direction: column -} - -.body__36c1b { - flex-grow: 1 -} - -.headerInput__36c1b { - align-items: center; - gap: 16px; - overflow: hidden -} - -.inputWrapper__36c1b { - flex: 1 1 auto; - padding-inline-end:8px} - -.titleInput__36c1b { - background-color: transparent; - border: none!important; - color: var(--text-strong); - font-family: var(--font-display); - font-size: 20px; - font-weight: 700; - height: 26px; - line-height: 24px; - margin-top: 16px; - padding-bottom: 0; - padding-inline-start:0;width: 100% -} - -.titleInput__36c1b::-moz-placeholder { - font-family: var(--font-display); - font-size: 20px; - font-weight: 700; - line-height: 24px -} - -.titleInput__36c1b::placeholder { - font-family: var(--font-display); - font-size: 20px; - font-weight: 700; - line-height: 24px -} - -.attachmentsContainer__36c1b { - margin-inline-start:-16px} - -.divider__36c1b { - background-color: var(--border-subtle); - height: 1px -} - -.footer__36c1b { - justify-content: space-between; - padding: 0 16px -} - -.footer__36c1b,.footerPart__36c1b { - align-items: center; - display: flex -} - -.footerPart__36c1b { - gap: 8px; - margin-inline-start:-8px} - -.threadToggle__36c1b { - align-items: center; - display: flex; - flex-direction: row; - gap: 6px -} - -.heroImageWrapper__36c1b { - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - position: relative -} - -.heroImage__36c1b,.heroImageWrapper__36c1b { - border-radius: var(--radius-sm); - height: var(--custom-channel-attachment-upload-mini-attachment-size); - width: var(--custom-channel-attachment-upload-mini-attachment-size) -} - -.heroImage__36c1b { - -o-object-fit: cover; - object-fit: cover; - -o-object-position: center; - object-position: center -} - -.heroOverlay__36c1b { - cursor: pointer; - inset-inline-end: 8px; - position: absolute; - top: 8px -} - -.attachButton__36c1b { - border: none; - border-radius: 8px; - color: var(--interactive-text-default); - line-height: 0; - padding: 6px; - position: relative; - transition: background-color .2s -} - -.attachButton__36c1b:hover { - background-color: var(--interactive-background-selected); - color: var(--interactive-text-active) -} - -.editModeButton__36c1b { - color: var(--interactive-text-default); - cursor: pointer -} - -.editModeButton__36c1b:hover { - color: var(--interactive-text-active) -} - -.heroImageButton__36c1b { - align-items: center; - background-color: var(--background-mod-normal); - border-radius: var(--radius-sm); - cursor: pointer; - display: flex; - height: var(--custom-channel-attachment-upload-mini-attachment-size); - justify-content: center; - position: relative; - width: var(--custom-channel-attachment-upload-mini-attachment-size) -} - -.sendButtonContainer__36c1b:hover .subIcon__36c1b { - background-color: var(--control-primary-background-hover) -} - -.sendButton__36c1b { - padding: 0 8px -} - -.sendButton__36c1b,.sendButtonContents__36c1b { - align-items: center; - display: flex; - gap: 4px -} - -.sendButtonContents__36c1b { - padding: 4px -} - -.sendButtonDivider__36c1b { - background: var(--border-subtle); - height: 24px; - width: 1px -} - -.sendButtonIcons__36c1b { - align-items: center; - display: flex; - justify-content: center; - overflow: visible; - position: relative -} - -.subIcon__36c1b { - background-color: var(--control-primary-background-default); - border-radius: 8px; - bottom: -4px; - height: 10px; - inset-inline-end: -4px; - padding: 1px; - position: absolute; - transition: background-color var(--custom-button-transition-duration) ease; - width: 10px -} - -.wrapper__4d3a9 { - align-self: stretch; - flex: 0 0 auto -} - -.icon__4d3a9,.wrapper__4d3a9 { - position: sticky -} - -.icon__4d3a9 { - padding-block:15px;padding-inline:16px 12px;top: 0 -} - -.voiceClipThumbnail__60cfd { - align-items: center; - background: var(--background-mod-subtle); - border-radius: inherit; - display: flex; - flex-direction: row; - gap: var(--space-4); - height: 100%; - justify-content: center; - width: 100% -} - -.thumbnailStack__15798 { - position: relative -} - -.height-1__15798 { - height: 102px -} - -.height-2__15798 { - height: 114px -} - -.height-3__15798 { - height: 123px -} - -.height-max__15798 { - height: 131.5px -} - -.thumbnail__15798 { - border-radius: 4px; - -o-object-fit: cover; - object-fit: cover; - position: absolute; - transform: translate(-50%) -} - -.thumbnail__15798:nth-child(4) { - bottom: 0; - height: 102px; - width: 183px -} - -.thumbnail__15798:nth-child(3) { - bottom: 28px; - filter: brightness(80%); - height: 86px; - width: 154px -} - -.thumbnail__15798:nth-child(2) { - bottom: 51px; - filter: brightness(60%); - height: 72px; - width: 128px -} - -.thumbnail__15798:first-child { - bottom: 74.5px; - filter: brightness(50%); - height: 57px; - width: 101.333px -} - -.recentClipsPopout__20c92 { - background-color: var(--background-surface-high); - border-radius: 8px; - box-shadow: var(--elevation-medium); - box-sizing: border-box; - max-width: 280px; - padding: var(--space-16); - text-align: center -} - -.recentClipsPopout__20c92:after { - border: 8px solid transparent; - border-top: 8px solid var(--background-surface-high); - content: " "; - inset-inline-start: 50%; - margin-inline-start:-8px;position: absolute; - top: 100% -} - -.header__20c92 { - margin-bottom: 8px -} - -.thumbnailStackContainer__20c92 { - display: flex; - flex-direction: column; - height: 51px; - margin-bottom: var(--space-16) -} - -.buttonsContainer__20c92 { - display: flex; - justify-content: center; - margin: 16px auto 0 -} - -.buttonsContainer__20c92 :nth-child(2) { - margin-inline-start:8px} - -.menu__77820 { - max-width: none; - min-width: 200px -} - -.optionLabel__77820 { - align-items: center; - display: flex; - font-size: 14px; - width: -moz-min-content; - width: min-content -} - -.optionIcon__77820 { - height: 24px; - width: 24px -} - -.badge__77820,.optionName__77820 { - margin-inline-start:8px} - -@use postcss-pxtorem;.attachWrapper__0923f { - align-self: stretch; - flex: 0 0 auto; - padding: var(--space-12) calc(var(--space-md) - 6px); - position: sticky -} - -.attachButton__0923f { - box-sizing: border-box; - color: var(--interactive-text-default); - height: unset; - padding: 0; - position: sticky; - top: 0 -} - -.attachButton__0923f:hover { - color: var(--icon-strong) -} - -.attachButton__0923f .attachButtonPlus__0923f { - fill: currentColor!important -} - -.attachButtonInner__0923f { - border-radius: 8px; - height: var(--chat-input-icon-size); - padding: 6px; - transition-duration: .2s -} - -.attachButtonInner__0923f:hover { - background-color: var(--interactive-background-selected) -} - -.attachButtonPlus__0923f,.attachButtonPlusGradient__0923f { - height: var(--chat-input-icon-size); - transform-origin: 50% 50%; - width: var(--chat-input-icon-size) -} - -.attachButtonPlus__0923f { - fill: var(--interactive-text-default) -} - -.attachButton__0923f:hover .attachButtonPlus__0923f { - fill: var(--interactive-text-hover) -} - -.attachButtonClip__0923f { - fill: var(--brand-500) -} - -.attachButtonPlay__0923f { - fill: var(--green-360) -} - -.attachPopout__0923f { - background-color: var(--background-surface-high); - border-radius: 8px; - box-shadow: var(--shadow-border),var(--shadow-high); - font-size: .75rem; - font-weight: var(--font-weight-medium); - min-width: 200px; - padding: 8px -} - -.attachPopoutRow__0923f { - align-items: center; - border-radius: 3px; - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - padding: 8px; - transition: background-color .2s ease -} - -.attachPopoutRow__0923f:hover { - background-color: var(--interactive-background-hover); - color: var(--interactive-text-hover) -} - -.attachPopoutRowText__0923f { - margin-inline-start:8px} - -.attachPopoutIcon__0923f { - height: var(--chat-input-icon-size); - width: var(--chat-input-icon-size) -} - -.uploadInput__0923f { - height: 0; - pointer-events: none; - position: relative; - width: 0 -} - -.buttonAnimation__0923f { - height: 44px; - width: 40px -} - -.buttonAnimationGlow__0923f { - inset-inline-start: -16px; - position: absolute; - top: -6px; - z-index: 1 -} - -.buttonAnimationTrinkets__0923f { - inset-inline-start: -10px -} - -.spamBanner_a2eac3 { - background: var(--background-base-lowest); - border-radius: var(--radius-xs); - box-shadow: 0 8px 16px var(--elevation-high); - display: flex; - flex-wrap: nowrap; - justify-content: space-between; - margin: 0 16px 16px; - overflow: hidden; - padding: 12px -} - -.tooltip_a2eac3 { - max-width: 165px; - text-align: center -} - -.bannerTextContainer_a2eac3 { - align-items: center; - display: flex; - flex-shrink: 1 -} - -.bannerText_a2eac3 { - display: flex; - flex: 5 2 auto; - flex-direction: column; - flex-wrap: wrap; - gap: var(--space-4); - margin: auto 0 -} - -.bannerIcon_a2eac3 { - height: 45px; - margin-inline-end:1rem;width: 45px -} - -.bannerHeader_a2eac3 { - color: var(--text-strong); - font-size: 16px; - font-weight: var(--font-weight-semibold) -} - -.bannerSubtext_a2eac3 { - color: var(--text-default); - font-size: 14px -} - -.actionButtons_a2eac3 { - align-items: center; - display: flex; - justify-content: flex-end; - text-align: end -} - -.actionButtons_a2eac3>button { - flex-shrink: 0 -} - -.smallButton_a2eac3 { - margin-inline-start:.5rem} - -.largeButton_a2eac3 { - margin-inline-start:16px} - -.bannerIcon__1d97f { - align-self: center; - color: var(--icon-feedback-critical); - height: 32px; - margin-inline-end:.5rem;width: 32px -} - -.bannerContainer__1d97f { - min-width: 100px -} - -.bannerHeader__1d97f { - line-height: 20px; - margin-bottom: 0 -} - -.container__65338 { - align-items: flex-start; - background-color: var(--background-surface-high); - border-radius: 8px; - bottom: 84px; - box-shadow: var(--elevation-high),var(--elevation-stroke); - display: flex; - margin-inline-start:16px;max-width: 408px; - padding: 16px; - position: absolute -} - -.iconContainer__65338 { - background-color: var(--background-base-low); - border-radius: 8px; - max-height: 20px; - padding: 8px -} - -.icon__65338 { - box-sizing: border-box; - color: var(--icon-feedback-critical) -} - -.header__65338 { - margin-inline-start:16px} - -.headerText__65338 { - margin-bottom: 4px -} - -.button__65338 { - margin-top: 16px -} - -.bannerIcon_cbe8c4 { - align-self: center; - color: var(--icon-feedback-critical); - height: 24px; - margin-inline-end:1rem;width: 24px -} - -.bannerContainer_cbe8c4 { - min-width: 100px -} - -.bannerHeader_cbe8c4 { - line-height: 20px; - margin-bottom: 0 -} - -.contentWarningPopout_d2eed6 { - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-md); - box-shadow: var(--shadow-high); - display: flex; - flex-direction: column; - min-height: 198px; - width: 440px -} - -.header_d2eed6 { - color: var(--text-default); - font-weight: var(--font-weight-bold); - letter-spacing: .6px; - margin-bottom: 4px; - text-transform: uppercase -} - -.body_d2eed6 { - color: var(--text-muted); - display: flex; - min-height: 128px; - padding: 20px -} - -.body_d2eed6 strong { - font-weight: var(--font-weight-semibold) -} - -.animation_d2eed6 { - width: 210px -} - -.buttonWrapper_d2eed6 { - display: flex; - margin-top: 20px -} - -.buttonContainer_d2eed6:last-child { - margin-inline-start:8px} - -.button_d2eed6 { - max-width: 130px -} - -.buttonHint_d2eed6 { - color: var(--primary-400); - margin-top: 4px -} - -.buttonHint_d2eed6 strong { - color: var(--text-muted); - text-transform: uppercase -} - -.content_d2eed6 { - display: flex; - flex-direction: column; - font-size: 14px; - line-height: 20px; - margin-inline-start:20px} - -.footer_d2eed6 { - align-items: center; - color: var(--primary-400); - display: flex; - font-size: 12px; - min-height: 14px; - padding: 8px -} - -.footer_d2eed6 strong { - color: var(--text-muted); - font-weight: var(--font-weight-bold) -} - -.icon_d2eed6 { - margin-inline-end:4px} - -.theme-dark .footer_d2eed6 { - background-color: hsl(var(--primary-700-hsl)/.4) -} - -.theme-light .footer_d2eed6 { - background-color: var(--primary-100); - border-radius: 0 0 5px 5px -} - -.wrapper_d852db { - background: var(--background-gradient-chat,var(--background-base-lower)); - contain: strict; - overflow: hidden -} - -.flash__03436 { - background-color: transparent; - border-end-end-radius: var(--radius-xs); - border-start-end-radius: var(--radius-xs) -} - -.flash__03436[data-flash=true] { - background-color: var(--message-highlight-background-default) -} - -.full-motion .flash__03436 { - transition: background-color .2s ease -} - -.blockedEdit_b7ab2c,.blockedSend_b7ab2c { - color: var(--text-default) -} - -.shieldIcon_b7ab2c { - color: var(--text-feedback-critical) -} - -.blockedNoticeIcon_b7ab2c { - flex: 0 -} - -.blockedNotice_b7ab2c { - flex: 1 -} - -.blockedNoticeContainer_b7ab2c { - align-items: center; - display: flex; - flex: 1; - flex-direction: row; - gap: 4px; - margin-top: 8px -} - -.blockedNoticeContainer_b7ab2c.compact_b7ab2c { - margin-inline-start:-8px} - -.ephemeralAccessories_b7ab2c { - margin-top: -4px -} - -.ephemeralAccessories_b7ab2c.compact_b7ab2c { - margin-inline-start:-8px} - -.learnMore_b7ab2c { - margin-inline-start:3px} - -.loadingWrapper__5a143 { - align-items: center; - border-radius: 2px; - display: flex; - flex-direction: row; - height: 16px; - justify-content: center; - margin: 2px 0; - padding: 8px 4px -} - -.list_c47777 { - max-height: 500px -} - -.emoji_ab6c65 { - -o-object-fit: contain; - object-fit: contain -} - -.wrapper_f563df { - display: grid; - gap: 4px; - grid-template-columns: repeat(4,1fr); - grid-template-rows: 1fr; - justify-items: center; - padding: 8px -} - -.button_f563df,.wrapper_f563df { - align-items: center -} - -.button_f563df { - background-color: var(--background-mod-subtle); - border-radius: 8px; - cursor: pointer; - display: flex; - flex: 0 0 auto; - height: 44px; - justify-content: center; - padding: 0; - width: 44px -} - -.button_f563df:hover { - background-color: var(--background-mod-strong) -} - -.button_f563df:active { - background-color: var(--background-mod-muted) -} - -.button_f563df:hover { - background-color: var(--background-mod-normal) -} - -.keyboard-mode .button_f563df.focused_f563df { - background-color: var(--background-mod-normal); - box-shadow: 0 0 0 2px var(--blue-345) -} - -.icon_f563df { - display: block; - height: 20px; - -o-object-fit: contain; - object-fit: contain; - width: 20px -} - -.flagIcon__45b6e { - height: 12px; - width: 16px -} - -.container__040f0 { - inset-inline-end: 0; - padding-block:0;padding-inline:32px 14px;position: absolute; - top: -25px; - z-index: 1 -} - -.isHeader__040f0:not(.isReply__040f0) { - top: -16px -} - -.emojiTooltipText__040f0 { - text-align: center -} - -.emoji__040f0 { - height: 20px; - width: 20px -} - -.operations_bab751 { - color: var(--text-default); - font-size: 12px; - font-weight: var(--font-weight-normal); - padding: 7px 0; - text-indent: 0 -} - -.operations_bab751>a { - color: var(--text-link); - cursor: pointer; - text-decoration: none; - transition: .05s -} - -.operations_bab751>a:hover { - text-decoration: underline -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .operations_bab751 { - font-weight:var(--font-weight-medium) - } -} - -.cell_f70307 { - align-items: center; - border-bottom: 1px solid var(--border-subtle); - border-color: var(--border-muted); - display: flex; - gap: var(--space-16); - padding: var(--space-16) 0 -} - -.cell_f70307:last-child { - border-bottom: none -} - -.iconContainer_f70307 { - display: flex; - justify-content: center -} - -.icon_f70307,.iconContainer_f70307 { - height: var(--space-24); - width: var(--space-24) -} - -.icon_f70307 { - color: var(--icon-subtle) -} - -.textContainer_f70307 { - gap: 2px -} - -.number_f70307,.textContainer_f70307 { - display: flex; - flex-direction: column -} - -.number_f70307 { - align-items: center; - background-color: var(--background-mod-subtle); - border-radius: var(--radius-round); - height: 32px; - justify-content: center; - min-width: 32px -} - -.cellGroup_f70307 { - display: flex; - flex-direction: column; - flex-shrink: 0; - gap: var(--space-8) -} - -.cellGroup_f70307 .content_f70307 { - border-radius: var(--radius-sm); - flex-grow: 1; - flex-shrink: 0; - overflow: hidden; - padding: 0 -} - -.completedText_f70307 { - text-decoration: line-through -} - -.safetyBanner__51e4d { - align-items: center; - background: var(--background-surface-high); - border-radius: 6px; - box-shadow: var(--elevation-low); - display: flex; - flex-wrap: nowrap; - gap: var(--space-16); - margin: var(--space-16) var(--space-16) calc(var(--space-8)*-1); - padding-block:var(--space-8) var(--space-8);padding-inline: var(--space-16) var(--space-12); - z-index: 2 -} - -.safetyShieldIcon__51e4d { - height: 32px; - width: 27px -} - -.buttons__51e4d { - align-items: center; - display: flex; - flex-wrap: nowrap; - gap: 8px; - justify-content: flex-end -} - -@media (max-width: 500px) { - .buttons__51e4d { - flex-grow:1 - } -} - -.shieldAndHeading__51e4d { - align-items: center; - display: flex; - flex: 1 1 auto; - flex-wrap: nowrap; - gap: var(--space-16) -} - -.closeButton__51e4d { - cursor: pointer; - opacity: .5; - transition: opacity .2s; - -webkit-app-region: no-drag; - color: var(--text-default) -} - -.closeButton__51e4d:hover { - opacity: 1 -} - -@media (max-width: 860px) { - .safetyBanner__51e4d { - flex-basis:min-content; - flex-wrap: wrap; - padding: var(--space-16) - } - - .safetyShieldIcon__51e4d { - height: 51px; - width: 43px - } - - .shieldAndHeading__51e4d { - flex-basis: min-content - } - - .closeButton__51e4d { - align-self: flex-start; - margin-inline-start:auto} - - .buttons__51e4d { - order: 4; - width: 100% - } - - .ctaButton__51e4d { - width: 50% - } -} - -.avatar_b50d96 { - border-radius: 50% -} - -.avatar_b50d96,.avatarMask_b50d96 { - height: 24px; - width: 24px -} - -.avatarMask_b50d96 { - margin-inline-end:-4px} - -.avatarContainer_b50d96 { - display: flex; - margin-inline-end:8px} - -.container_b50d96 { - align-items: center -} - -.container_b50d96,.mobileContainer_b50d96 { - display: flex; - margin-top: 16px -} - -.mobileContainer_b50d96 { - align-items: flex-start; - flex-direction: column; - gap: 8px -} - -.inline_b50d96 { - align-items: center; - display: flex; - gap: var(--space-8) -} - -.wrap_b50d96 { - flex-wrap: wrap -} - -.divider_b50d96 { - background-color: var(--background-mod-muted); - border-radius: 50%; - height: 4px; - margin: 0 16px; - width: 4px -} - -.mutualGuilds_b50d96 { - color: var(--interactive-text-default); - cursor: pointer -} - -.mutualGuilds_b50d96:hover { - color: var(--interactive-text-hover) -} - -.mutualGuilds_b50d96:active { - color: var(--interactive-text-active) -} - -@media (max-width: 980px) { - .container_b50d96 { - align-items:flex-start; - flex-direction: column; - gap: 8px - } - - .divider_b50d96 { - display: none - } - - .inline_b50d96 { - flex-wrap: wrap - } -} - -.container__00de6 { - display: flex; - flex: 0 0 auto; - flex-direction: column; - justify-content: flex-end; - margin: var(--custom-message-margin-horizontal) -} - -.header__00de6 { - font-weight: var(--font-weight-bold); - margin: 8px 0 -} - -.description__00de6 { - color: var(--text-default) -} - -.emptyChannelIcon__00de6 { - background-color: var(--background-mod-muted); - border-radius: 50%; - height: 68px; - margin-top: 16px; - width: 68px -} - -.emptyChannelIconComponent__00de6 { - align-items: center; - display: flex; - justify-content: center -} - -.emptyChannelIconSvg__00de6 { - background-image: url(/assets/4ab68153d28748ee.svg); - background-position: 50%; - background-repeat: no-repeat -} - -.emptyChannelIconSvg__00de6.locked__00de6 { - background-image: url(/assets/00e45deca221a6ee.svg) -} - -.emptyChannelIconSvg__00de6.voiceChat__00de6 { - background-image: url(/assets/f8169a374e27b270.svg) -} - -.tags_e5a45e { - align-items: center; - gap: var(--space-8); - justify-content: center; - overflow: hidden -} - -.tags_e5a45e,.wrap_e5a45e { - display: flex -} - -.wrap_e5a45e { - flex-wrap: wrap; - justify-content: flex-start; - overflow: auto -} - -.container__7e6bb { - align-items: flex-start; - margin-bottom: 24px -} - -.iconWrapper__7e6bb { - align-items: center; - background-color: var(--background-base-lower); - border-radius: 50%; - display: flex; - height: 64px; - justify-content: center; - margin-top: 16px; - width: 64px -} - -.icon__7e6bb { - color: var(--text-strong); - height: 40px; - width: 40px -} - -.header__7e6bb { - font-weight: var(--font-weight-medium); - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.buttons_bb2295 { - align-items: center; - display: flex; - gap: 8px -} - -.container__143a4 { - align-items: flex-start; - max-width: 650px -} - -.formContainer__143a4 { - max-width: 100%; - overflow: hidden; - overflow-wrap: break-word -} - -.formContainer__143a4,.summaryContainer__143a4 { - display: flex; - flex-direction: column; - gap: 16px -} - -.summaryContainer__143a4 { - background-color: var(--background-base-lower); - border: 1px solid var(--border-subtle); - border-radius: 8px; - padding: 16px -} - -.summaryHeader__143a4 { - display: flex; - flex-direction: column; - gap: 4px -} - -.summaryHeaderClanInfo__143a4 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px; - justify-content: flex-start -} - -.guildIcon__143a4 { - border-radius: 16px -} - -.summarySeparator__143a4 { - background-color: var(--border-subtle); - border: none; - height: 1px; - margin: 0 -} - -.formResponseContainer__143a4 { - display: flex; - flex-direction: column; - gap: 4px -} - -.editableGdmIcon_ec5bef { - align-items: center; - cursor: pointer; - display: flex; - justify-content: center; - position: relative; - width: -moz-max-content; - width: max-content -} - -.editableGdmIcon_ec5bef .editableGdmIconIndicator_ec5bef { - display: none -} - -.editableGdmIcon_ec5bef:hover>:not(.editableGdmIconIndicator_ec5bef) { - opacity: .2 -} - -.editableGdmIcon_ec5bef:hover .editableGdmIconIndicator_ec5bef { - align-items: center; - color: var(--interactive-text-active); - display: flex; - inset: 0; - justify-content: center; - position: absolute -} - -.buttonContainer__6ca73 { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px; - margin-top: 16px -} - -.alignCenter__9ecf6 { - align-items: center; - display: flex -} - -.searchBox__9ecf6 { - padding-bottom: var(--space-12) -} - -.hintText__9ecf6 { - margin-top: var(--space-4) -} - -.roleMemberList__9ecf6 { - margin: 0 -} - -.clickableRow__9ecf6 { - align-items: center; - border-radius: var(--radius-xs); - cursor: pointer; - display: flex; - justify-content: space-between; - margin-inline:0 var(--space-4);padding: var(--space-8) var(--space-6) -} - -.clickableRow__9ecf6.selectedRow__9ecf6 { - background-color: var(--interactive-background-selected) -} - -.rowBody__9ecf6 { - align-items: center; - display: flex -} - -.checkbox__9ecf6 { - padding-inline-start:8px} - -.rowHeight__9ecf6 { - height: 24px -} - -.rowLabel__9ecf6 { - margin-inline:8px 4px} - -.rowLabelSubText__9ecf6 { - flex: 1; - margin-inline-start:4px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.sectionTitle__9ecf6 { - color: var(--text-default); - padding-top: var(--space-8) -} - -.noResults__9ecf6 { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - margin: var(--space-16) 0 -} - -.noResultIcon__9ecf6 { - height: 85px; - margin-bottom: var(--space-16); - width: 85px -} - -.theme-dark .scrollSeparator__9ecf6 { - box-shadow: 0 1px 0 0 hsl(var(--primary-800-hsl)/.3),0 1px 2px 0 hsl(var(--primary-800-hsl)/.3) -} - -.theme-light .scrollSeparator__9ecf6 { - box-shadow: 0 1px 0 0 hsl(var(--primary-300-hsl)/.3) -} - -.scrollSeparator__9ecf6 { - border-bottom: 1px solid var(--border-subtle); - box-shadow: none -} - -.channelName__01dab { - align-items: center; - display: flex; - justify-self: center; - margin-bottom: var(--space-8) -} - -.channelIcon__01dab { - padding-inline-end:var(--space-4)} - -.description__01dab { - margin-bottom: var(--space-12) -} - -.subtext__01dab { - margin-top: var(--space-4) -} - -.role_b4b2c3 { - align-items: center; - background-color: color-mix(in oklch,var(--custom-role-label-color,var(--background-base-low)) 10%,var(--background-base-low)); - border: 1px solid color-mix(in oklch,var(--custom-role-label-color,var(--border-subtle)) 20%,var(--border-subtle)); - border-radius: var(--radius-sm); - color: var(--interactive-text-active); - display: inline-flex; - font-size: 12px; - line-height: 16px; - padding: 4px 8px; - width: auto -} - -.roleColor_b4b2c3 { - border: 1px solid color-mix(in oklch,var(--custom-role-label-color,var(--border-subtle)) 20%,var(--border-muted)); - border-radius: 50%; - display: inline-block; - filter: saturate(var(--saturation-factor,1)); - height: 8px; - width: 8px -} - -.linkedRoleColor_b4b2c3,.roleColor_b4b2c3 { - margin-inline-end:4px} - -.button_afdfcc { - cursor: pointer; - display: inline-flex -} - -.button_afdfcc.disabled_afdfcc { - cursor: not-allowed; - opacity: .5 -} - -.role_afdfcc:hover { - background-color: color-mix(in oklch,var(--custom-role-label-color,var(--background-base-low)) 30%,var(--background-base-low)) -} - -.full-motion .role_afdfcc { - transition: background-color .2s ease -} - -.removeRole_afdfcc { - margin-inline-start:6px} - -.removeRoleIcon_afdfcc { - fill: var(--interactive-text-default); - height: 6px; - width: 6px -} - -.role_afdfcc:hover .removeRoleIcon_afdfcc { - fill: var(--interactive-text-hover) -} - -.members__1ee8c { - display: flex; - flex-wrap: wrap; - margin-top: 16px -} - -.avatars__1ee8c { - align-items: center; - display: flex; - margin-inline-end:12px;margin-bottom: 6px -} - -.singleUserName__1ee8c { - margin-inline-start:8px} - -.role__1ee8c { - margin-inline-end:6px;margin-bottom: 6px -} - -.role__1ee8c.last__1ee8c { - margin-inline-end:12px} - -.editRoleButton__1ee8c { - margin-bottom: 6px -} - -.editRoleButtonInner__1ee8c { - align-items: center; - color: var(--interactive-text-default); - display: flex; - height: 16px -} - -.channelSettingButtons__1ee8c { - display: flex; - gap: var(--space-8); - margin-top: var(--space-12) -} - -.buttonContainer_ffab0d { - margin-top: var(--space-12) -} - -.showHistory_ffab0d { - margin-top: 16px; - width: -moz-max-content; - width: max-content -} - -.iconWrapper__54b20 { - align-items: center; - background: var(--background-mod-strong); - border-radius: 50%; - display: flex; - height: 64px; - justify-content: center; - margin-top: 16px; - width: 64px -} - -.icon__54b20 { - color: var(--icon-subtle); - height: 32px; - width: 32px -} - -.subtitle__54b20 { - margin-bottom: 2px -} - -.threadCreatorName__54b20 { - color: var(--text-strong); - font-weight: var(--font-weight-semibold) -} - -.threadCreatorName__54b20:hover { - cursor: pointer; - text-decoration: underline -} - -.unknownCreatorName__54b20 { - pointer-events: none -} - -.autoArchiveDuration__54b20 { - color: var(--text-strong) -} - -.textDivider__54b20 { - background-color: var(--background-mod-muted); - border-radius: 50%; - display: inline-block; - height: 4px; - margin: 0 8px; - vertical-align: middle; - width: 4px -} - -.container_aae012 { - padding: 16px 16px 0 -} - -.container_aae012,.inner_aae012 { - align-items: center; - display: flex; - flex-direction: column -} - -.inner_aae012 { - max-width: 400px -} - -.titleName_aae012 { - text-align: center -} - -.titleName_aae012 p { - margin: 0 -} - -.subtitle_aae012 { - margin-bottom: 12px; - margin-top: 8px; - text-align: center -} - -.subtitle_aae012.noChildren_aae012 { - margin-bottom: 0 -} - -.card_aae012,.cardWrapper_aae012 { - align-self: stretch -} - -.card_aae012 { - align-items: center; - background-color: var(--background-mod-muted); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - cursor: pointer; - display: flex; - margin-top: 8px; - padding: var(--space-16); - transition: background-color .1s ease-in-out -} - -.card_aae012:hover { - background-color: var(--background-mod-subtle) -} - -.completed_aae012 .cardTextContainer_aae012,.completed_aae012 .checkmark_aae012,.completed_aae012 .icon_aae012,.completed_aae012 .playCircleWrapper_aae012 { - opacity: .6 -} - -.icon_aae012 { - background-repeat: no-repeat; - background-size: contain; - filter: saturate(var(--saturation-factor,1)); - height: var(--space-24); - min-height: var(--space-24); - min-width: var(--space-24); - width: var(--space-24) -} - -.playCircleWrapper_aae012 { - align-items: center; - background-color: var(--brand-500); - border-radius: 50%; - display: flex; - height: 24px; - justify-content: center; - margin: 2px; - padding: 6px; - padding-inline:7px 5px;width: 24px -} - -.playCircleIcon_aae012 { - height: 26px; - width: 26px -} - -.cardHeader_aae012 { - font-weight: var(--font-weight-semibold) -} - -.cardTextContainer_aae012 { - flex-grow: 1; - margin-inline:var(--space-12) 16px} - -.checkmark_aae012 { - background-color: var(--status-positive); - border-radius: 50%; - color: var(--white); - height: 16px; - padding: 4px; - width: 16px -} - -.checkmark_aae012.animate_aae012 { - animation: completed_aae012 .8s -} - -&.full-motion .card_aae012 { - transition: background-color .2s ease -} - -@keyframes completed_aae012 { - 0% { - background-color: var(--background-mod-muted); - transform: scale(.8) - } - - 20% { - box-shadow: 0 0 0 0 hsl(var(--green-360-hsl)/.5) - } - - 60% { - background-color: var(--green-230); - transform: scale(1.2) - } - - to { - box-shadow: 0 0 0 8px hsl(var(--green-360-hsl)/0); - transform: scale(1) - } -} - -.arrow_aae012 { - color: var(--icon-muted); - justify-self: flex-end; - min-width: var(--space-20) -} - -.sharePromptContainer__56d6c { - background-color: var(--message-highlight-background-default); - display: flex; - gap: 24px; - margin-bottom: 10px; - padding: 20px -} - -.sharePromptContent__56d6c { - display: flex; - flex-direction: column; - flex-grow: 1; - gap: 4px -} - -.closeButton__56d6c { - color: var(--interactive-text-default); - cursor: pointer; - height: -moz-fit-content; - height: fit-content -} - -.closeButton__56d6c:hover { - color: var(--interactive-text-hover) -} - -.box_ee23ac { - background-color: var(--background-mod-normal); - border-radius: 12px; - box-sizing: border-box; - display: flex; - flex-direction: row; - margin: var(--space-16); - max-width: 448px; - padding: 12px; - position: relative -} - -.box_ee23ac:after,.box_ee23ac:before { - content: ""; - height: 0; - position: absolute; - top: 64px; - width: 0 -} - -.box_ee23ac:after { - border-inline-end:10px solid transparent;border-inline-start:10px solid transparent;border-top-color: var(--background-base-lower); - border-top: 10px solid var(--background-mod-normal); - bottom: -10px; - inset-inline-start: 100px; - top: unset -} - -.textContainer_ee23ac { - display: flex; - flex-direction: column; - margin-inline-start:16px} - -.divider_ee23ac { - background-color: var(--border-subtle); - height: 8px; - margin-bottom: 16px; - width: 100% -} - -.animation_ee23ac { - height: 40px; - width: 40px -} - -.container__34c2c { - align-items: center; - border-top: 1px solid var(--border-subtle); - display: flex; - justify-content: space-between; - margin-top: 8px; - padding: 6px 0; - padding-inline:var(--space-sm);position: sticky; - top: -1px; - z-index: 2 -} - -.container__34c2c,.header__34c2c { - background: var(--background-gradient-chat,var(--background-base-lower)) -} - -.header__34c2c { - border-top-color: transparent; - box-shadow: var(--elevation-low) -} - -.reactButtons__34c2c { - align-items: center; - display: flex; - margin-inline-end:20px;overflow-y: visible -} - -.reactButtons__34c2c.loading__34c2c { - height: 26px; - overflow-y: hidden -} - -.reportedMessageActions__34c2c { - align-items: center; - display: flex; - flex-shrink: 0; - gap: var(--space-4); - margin-inline-end:var(--space-16)} - -.addReactButton__34c2c { - align-items: center; - background-color: var(--control-secondary-background-default); - border: 1px solid transparent; - border-color: var(--control-secondary-border-default); - border-radius: var(--radius-sm); - color: var(--control-secondary-text-default); - display: flex; - font-size: 14px; - font-weight: var(--font-weight-medium); - gap: 8px; - height: 24px; - justify-content: center; - line-height: 16px; - margin: 0 2px; - min-width: 32px; - opacity: 1; - padding: 2px 0; - width: auto -} - -.addReactButton__34c2c.hasNoReactions__34c2c { - padding: 2px 8px -} - -.addReactButton__34c2c:hover { - background-color: var(--control-secondary-background-hover); - border-color: var(--control-secondary-border-hover); - color: var(--control-secondary-text-hover) -} - -.addReactButton__34c2c:active { - background-color: var(--control-secondary-background-active); - border-color: var(--control-secondary-border-active); - color: var(--control-secondary-text-active) -} - -.reactions__34c2c { - align-items: center; - flex-shrink: 1; - flex-wrap: wrap -} - -.buttons__34c2c { - align-items: center; - display: flex; - flex-shrink: 0; - gap: 4px -} - -.summaryDivider__3aab5 { - border-top: thin solid var(--text-brand); - justify-content: flex-start; - pointer-events: auto -} - -.summaryDividerStart__3aab5 { - margin-bottom: 1rem!important; - margin-top: 2rem!important -} - -.summaryDividerEnd__3aab5 { - margin-bottom: 2rem!important; - margin-top: 1rem!important -} - -.summaryStartContent__3aab5 { - align-items: center; - color: var(--text-brand); - display: flex; - font-size: 14px; - font-weight: 500; - line-height: 18px; - margin-inline-start:8px;padding-inline:4px 14px;text-transform: capitalize -} - -.summaryEndContent__3aab5 { - background: none; - display: flex; - height: 13px; - justify-content: flex-end; - line-height: 9px; - padding: 0; - width: 100% -} - -.summaryStartIcon__3aab5 { - color: var(--text-brand); - padding-inline-end:4px} - -.summaryEndIcon__3aab5 { - align-self: flex-end; - color: var(--text-brand); - padding-inline-start:26px} - -.summaryEndIcon__3aab5,.summaryFeedbackWrapper__3aab5 { - background: var(--background-gradient-chat,var(--background-base-low)) -} - -.summaryFeedbackWrapper__3aab5 { - display: flex; - margin-inline:calc(3.5rem - 16px) auto;padding-inline:16px} - -.summaryFeedback__3aab5 { - align-items: center; - display: flex; - gap: 8px -} - -.thumbIcon__3aab5 { - background: var(--background-mod-normal); - border-radius: 16px; - color: var(--interactive-text-default); - cursor: pointer; - padding: 8px -} - -.thumbIcon__3aab5:active,.thumbIcon__3aab5:hover { - color: var(--interactive-text-active) -} - -.containerExpanded__7ff28 { - align-items: center; - display: flex; - flex-direction: column; - margin: 20px; - width: 232px -} - -.stickerExpanded__7ff28 { - margin-bottom: 24px -} - -.containerCompact__7ff28 { - margin: 20px 16px 0; - text-align: center; - width: -moz-fit-content; - width: fit-content -} - -.compactButton__7ff28,.compactButtonDisabled__7ff28 { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: 32px; - color: var(--text-strong); - display: flex; - padding: 12px; - width: -moz-fit-content; - width: fit-content -} - -.compactButton__7ff28:hover { - background-color: var(--background-mod-normal); - cursor: pointer -} - -.compactButtonDisabled__7ff28 { - cursor: not-allowed; - opacity: .6 -} - -.text__7ff28 { - margin-inline:8px} - -.error__7ff28 { - margin-top: 8px -} - -.messagesWrapper__36d07 { - display: flex; - flex: 1 1 auto; - min-height: 0; - min-width: 0; - position: relative; - z-index: 0 -} - -.scrollerContent__36d07 { - align-items: stretch; - display: flex; - flex-direction: column; - justify-content: flex-end; - min-height: 100%; - overflow-anchor: none -} - -.scroller__36d07 { - bottom: 0; - position: absolute; - top: 0; - inset-inline: 0 -} - -.scroller__36d07::-webkit-scrollbar-track { - margin-bottom: 36px -} - -.scroller__36d07:not(:hover)::-webkit-scrollbar-thumb { - background: transparent -} - -.overlay .scroller__36d07::-webkit-scrollbar-track { - margin-bottom: 0 -} - -.scrollerInner__36d07 { - min-height: 0; - overflow: hidden -} - -.scrollerInner__36d07.scrollerAllowSticky__36d07 { - overflow: unset -} - -.scrollerInner__36d07:focus { - outline: none -} - -.scrollerSpacer__36d07 { - display: block; - flex: 0 0 auto; - height: 40px; - pointer-events: none; - width: 1px -} - -.scrollerSpacer__36d07.empty__36d07 { - height: 36px -} - -.scrollerSpacer__36d07.emptyForum__36d07 { - height: 32px -} - -.navigationDescription__36d07 { - display: none -} - -.overlay .scroller__36d07:after { - height: 20px -} - -.messages__36d07 { - background-color: var(--background-base-low); - contain: size; - margin: 0; - overflow-x: hidden -} - -.scrollerWrap__36d07 { - height: auto; - overflow: hidden -} - -.divider__36d07+.messageGroupBlocked__36d07 { - margin-top: 12px -} - -.highlight { - background: hsl(var(--yellow-300-hsl)/.3) -} - -.chatGradientBase__36d07 { - bottom: 0; - inset-inline: 0 16px; - pointer-events: none; - position: absolute; - transition: background .5s ease-in-out; - z-index: 1 -} - -.chatGradient__36d07 { - background: linear-gradient(to bottom,transparent,var(--background-base-lower)); - height: 16px -} - -.chatTypingGradientAtBottom__36d07 { - background: linear-gradient(to bottom,transparent,var(--background-base-lower) 8px,var(--background-base-lower) 100%); - height: 32px -} - -.chatTypingGradientNotAtBottom__36d07 { - background: linear-gradient(to bottom,transparent,var(--background-base-lower) 72px,var(--background-base-lower) 100%); - height: 96px -} - -.gradientDefault__36d07 { - -webkit-mask: linear-gradient(180deg,#000,transparent) bottom /100% 16px no-repeat,linear-gradient(#000,#000) top /100% calc(100% - 15px) no-repeat; - mask: linear-gradient(180deg,#000,transparent) bottom /100% 16px no-repeat,linear-gradient(#000,#000) top /100% calc(100% - 15px) no-repeat -} - -.typingGradientNotAtBottom__36d07 { - -webkit-mask: linear-gradient(180deg,#000,transparent 72px,transparent) bottom /100% 96px no-repeat,linear-gradient(#000,#000) top /100% calc(100% - 95px) no-repeat; - mask: linear-gradient(180deg,#000,transparent 72px,transparent) bottom /100% 96px no-repeat,linear-gradient(#000,#000) top /100% calc(100% - 95px) no-repeat -} - -.typingGradientAtBottom__36d07 { - -webkit-mask: linear-gradient(180deg,#000,transparent 8px,transparent) bottom /100% 32px no-repeat,linear-gradient(#000,#000) top /100% calc(100% - 31px) no-repeat; - mask: linear-gradient(180deg,#000,transparent 8px,transparent) bottom /100% 32px no-repeat,linear-gradient(#000,#000) top /100% calc(100% - 31px) no-repeat -} - -.combo__257af { - align-items: center; - border-radius: var(--radius-sm); - display: flex; - flex-direction: column; - inset-inline-end: 16px; - justify-content: center; - padding: 4px 8px; - position: absolute; - text-shadow: var(--background-base-lowest); - top: -90px; - transform: rotate(6deg); - z-index: 1 -} - -.comboValue__257af { - font-size: 50px; - line-height: 32px; - paint-order: stroke fill; - position: relative; - width: auto; - z-index: 2; - -webkit-text-stroke: 3px var(--background-base-lowest) -} - -.comboNameplate__257af { - background-color: var(--background-base-lowest); - border-radius: 5px; - font-size: 16px; - font-weight: var(--font-weight-bold); - line-height: 20px; - padding: 8px 12px; - position: relative; - z-index: 1 -} - -.comboMultiplier__257af { - display: inline-block; - font-size: 16px; - line-height: 20px; - margin-inline-start:4px} - -.comboSquare__257af { - height: 6px; - outline: 4px solid var(--background-base-lowest); - transform: rotate(-45deg); - width: 6px; - z-index: 4 -} - -.left__257af { - inset-inline-start: -3px -} - -.left__257af,.right__257af { - bottom: 42%; - position: absolute -} - -.right__257af { - inset-inline-end: -3px -} - -.confettiIcon__257af.left__257af { - bottom: 20%; - inset-inline-start: -14px; - transform: scaleX(-1) -} - -.confettiIcon__257af.right__257af { - bottom: 20%; - inset-inline-end: -14px; - transform: scaleX(1) -} - -.tip__257af { - bottom: -6px; - color: var(--text-strong); - font-size: 10px; - inset-inline-start: 0; - line-height: 12px; - position: absolute; - text-align: center; - width: 100%; - z-index: 2 -} - -.messageComboScore__257af { - align-items: center; - border-radius: var(--radius-sm); - display: flex; - flex-direction: column; - inset-inline-end: 8px; - justify-content: center; - padding: 4px 8px; - position: absolute; - text-shadow: var(--brand-700); - top: -150px; - transform: rotate(6deg) -} - -.comboScore__257af { - font-size: 28px; - line-height: 32px; - paint-order: stroke fill; - position: relative; - width: auto; - z-index: 2; - -webkit-text-stroke: 2px var(--brand-700) -} - -.bannerContainer__362cd { - background: var(--chat-background-default); - border: 1px solid var(--border-muted); - border-radius: var(--radius-sm) -} - -.bannerHeader__362cd { - align-items: center; - display: flex -} - -.notice__51057 { - align-items: center; - background-color: var(--background-mod-normal); - display: flex; - justify-content: space-between; - padding: 8px 16px -} - -.info__51057 { - flex: 1 -} - -.chatHeaderBar_dc83f5 { - align-items: center; - background-color: var(--background-surface-high); - border-bottom: 1px solid var(--border-muted); - display: flex; - justify-content: space-between; - padding: 8px 16px; - padding-inline-start:28px;position: relative -} - -.chatHeaderBar_dc83f5:after { - bottom: -1px; - box-shadow: var(--elevation-low); - content: ""; - display: none; - display: block; - height: 1px; - inset-inline: 0; - pointer-events: none; - position: absolute; - z-index: 1 -} - -.chatHeaderBarText_dc83f5 { - flex: 1; - margin-inline-end:16px} - -.narrow_dc83f5 .nudgeBarText_dc83f5 { - max-width: 325px -} - -.nudgeAction_dc83f5:hover { - cursor: pointer; - text-decoration: underline -} - -.chatHeaderBar_a5700d { - align-items: center; - background-color: var(--background-mod-normal); - display: flex; - gap: 16px; - justify-content: space-between; - padding: 8px 16px -} - -.narrow_a5700d { - padding: 12px 16px -} - -.chatHeaderBarText_a5700d { - flex: 1 -} - -.narrow_a5700d .nudgeBarText_a5700d { - max-width: 325px -} - -.nudgeAction_a5700d:hover { - cursor: pointer; - text-decoration: underline -} - -.container__87dda { - display: flex; - flex-direction: row; - height: 44px; - margin-bottom: 24px; - padding: 0 16px; - width: 100% -} - -.placeholderItem__87dda { - background-color: var(--background-mod-muted); - border-radius: var(--radius-sm); - height: 100% -} - -.chatTextAreaPlaceholder__87dda { - flex: 1; - margin-inline-end:8px} - -.appLauncherPlaceholder__87dda { - width: 44px -} - -.wrapper__44df5 { - background-color: var(--background-base-low); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - height: var(--space-32); - margin-inline:0;margin-bottom: var(--space-8); - margin-top: 0; - padding: 12px 0; - position: relative; - width: auto; - z-index: 1 -} - -.content__44df5,.wrapper__44df5 { - align-items: center; - display: flex; - justify-content: space-between -} - -.content__44df5 { - height: 20px; - margin-inline-start:16px} - -.image__44df5 { - align-self: center; - border-radius: var(--radius-sm); - height: 40px; - margin-inline:-8px 16px;width: 40px -} - -.animation__44df5 { - align-content: center; - align-self: center; - display: flex; - height: 50px; - justify-content: center; - margin-top: 4px; - margin-inline:-8px 4px;width: 50px -} - -.text__44df5 { - max-height: 40px -} - -.text__44df5,.title__44df5 { - overflow: hidden -} - -.title__44df5 { - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical -} - -.button__44df5 { - align-items: center; - display: flex; - flex-shrink: 0; - margin-inline-start:12px} - -.buttonContainer__44df5 { - display: flex; - flex-direction: row; - margin-inline-end:12px} - -.innerButton__44df5 { - align-items: center; - display: flex; - gap: 8px -} - -.countdown__44df5 { - color: var(--text-muted); - margin-inline-end:16px} - -.animationMiddle__8177b { - height: 262px; - inset-inline-start: calc(50% - 128px); - position: absolute; - top: -240px; - width: 256px -} - -.animationContainer__8177b { - position: relative; - width: 100% -} - -.header__8177b { - margin-bottom: 8px; - max-width: 100%; - overflow: hidden; - overflow-wrap: break-word -} - -.header__8177b p { - margin: 0 -} - -.manaContainer__8177b { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - margin-top: 28px; - text-align: center -} - -@media (max-height: 500px) { - .animationContainer__8177b { - display:none - } -} - -.clickableChannelTextArea_d8b277 { - cursor: pointer -} - -.voiceChannelEffectEmojiContainer__66db8 { - background: var(--background-surface-high); - border-radius: 50%; - box-shadow: 0 0 16px rgba(0,0,0,.24); - inset-inline-end: 8px; - position: absolute; - top: 8px; - z-index: 2 -} - -.voiceChannelEffectEmoji__66db8 { - height: 32px; - padding: 12px; - width: 32px -} - -.streamInfoContainer__0f85c { - background-color: rgba(0,0,0,.5); - border-radius: 8px; - color: var(--text-default); - display: flex; - flex-direction: column; - font-size: 14px; - line-height: 18px; - margin: 16px; - padding: 8px; - pointer-events: none; - position: absolute; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text; - z-index: 100 -} - -.streamInfoContainer__0f85c strong { - font-weight: var(--font-weight-bold) -} - -.infoRow__0f85c { - display: flex; - justify-content: space-between; - margin-bottom: 4px -} - -.infoRow__0f85c span { - margin-inline-end:8px} - -.container_ecf309 { - position: relative -} - -.container_ecf309,.iframe_ecf309 { - height: 100%; - width: 100% -} - -.clickShield_ecf309,.splashImage_ecf309 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.splashImage_ecf309 { - height: 100%; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.splash_ecf309 { - align-items: center; - background: var(--opacity-black-60); - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - display: flex; - flex-direction: column; - justify-content: center -} - -.avatar_ecf309 { - border-radius: 50%; - width: 100% -} - -.subheader_ecf309 { - color: var(--text-default); - font-size: 14px; - line-height: 18px; - margin-top: 4px -} - -.subheader_ecf309.medium_ecf309 { - font-size: 12px; - line-height: 16px -} - -.subheader_ecf309.small_ecf309 { - display: none -} - -.header_ecf309 { - color: var(--white); - font-size: 32px; - font-weight: var(--font-weight-bold); - line-height: 40px; - margin: 4px 0 -} - -.header_ecf309.medium_ecf309 { - font-size: 20px; - line-height: 24px -} - -.header_ecf309.small_ecf309 { - font-size: 14px; - line-height: 18px -} - -.buttons_ecf309 { - display: flex; - margin-top: 4px -} - -.loadingLottie_ecf309 { - height: 100%; - max-height: 100%; - max-width: 100%; - width: 100% -} - -.chatWheelMouseInput_adfa30 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.chatWheel_adfa30 { - position: relative -} - -.chatWheelBackground_adfa30 { - height: 100%; - width: 100% -} - -.chatWheelDeadZone_adfa30 { - fill: var(--background-surface-high); - opacity: 0 -} - -.chatWheelDeadZone_adfa30:hover { - opacity: .3 -} - -.chatWheelCenter_adfa30 { - fill: var(--background-surface-high); - opacity: .6 -} - -.innerContent_adfa30 { - align-items: center; - bottom: 0; - display: flex; - flex-direction: column; - position: absolute; - top: calc(50% - 16px); - inset-inline: 0; - pointer-events: none -} - -.chatWheelDeadZoneIcon_adfa30 { - color: var(--white); - cursor: pointer; - height: 48px; - position: relative; - width: 48px; - z-index: 9999 -} - -.paginationHint_adfa30 { - background-color: var(--opacity-black-28); - border-radius: var(--radius-sm); - color: var(--white); - font-size: 12px; - line-height: 16px; - margin-top: 8px; - padding: 4px -} - -.chatWheelBackground_adfa30 { - fill: none; - stroke: var(--background-surface-high); - opacity: .9 -} - -.chatWheelItem_adfa30 { - align-items: center; - display: flex; - justify-content: center; - position: absolute -} - -.soundButton_d9cf5f { - box-shadow: var(--shadow-border),var(--shadow-high); - height: 52px; - width: 96px -} - -/*# sourceMappingURL=1803f89f7fb95846.css.map*/ diff --git a/discord-html-copy/css/1f7b87510348dd0c.css b/discord-html-copy/css/1f7b87510348dd0c.css deleted file mode 100644 index 0cf8d83..0000000 --- a/discord-html-copy/css/1f7b87510348dd0c.css +++ /dev/null @@ -1,2000 +0,0 @@ -.pill_a2c9e8 { - align-items: center; - background-color: var(--control-secondary-background-default); - border: 1px solid var(--border-subtle); - border-radius: 20px; - box-sizing: border-box; - color: var(--control-secondary-text-default); - display: inline-flex; - height: 32px; - overflow: hidden; - padding: 0 12px -} - -.pill_a2c9e8.clickable_a2c9e8:not(.disabled_a2c9e8):hover { - background-color: var(--control-secondary-background-hover); - cursor: pointer -} - -.pill_a2c9e8.disabled_a2c9e8 { - cursor: not-allowed; - opacity: .5 -} - -.pill_a2c9e8.small_a2c9e8 { - height: 24px; - padding: 0 8px -} - -.pill_a2c9e8.clickable_a2c9e8:not(.disabled_a2c9e8):active { - background-color: var(--control-secondary-background-active); - cursor: pointer -} - -.pill_a2c9e8.selected_a2c9e8 { - background-color: var(--message-reacted-background-default); - color: var(--message-reacted-text-default) -} - -.pill_a2c9e8.selected_a2c9e8:not(.disabled_a2c9e8):hover { - background-color: var(--opacity-blurple-32) -} - -.pill_a2c9e8.selected_a2c9e8:not(.disabled_a2c9e8):active { - background-color: var(--opacity-blurple-12) -} - -.pill_a2c9e8.danger_a2c9e8 { - background-color: var(--background-feedback-critical); - border-color: var(--border-feedback-critical); - color: var(--text-feedback-critical) -} - -.pill_a2c9e8.success_a2c9e8 { - background-color: var(--background-feedback-positive); - color: var(--text-feedback-positive) -} - -.pill_a2c9e8.success_a2c9e8,.pill_a2c9e8.warning_a2c9e8 { - border-color: var(--border-subtle) -} - -.pill_a2c9e8.warning_a2c9e8 { - background-color: var(--background-feedback-warning); - color: var(--text-feedback-warning) -} - -.theme-light .pill_a2c9e8.selected_a2c9e8 { - background-color: var(--brand-160); - border-color: var(--brand-500) -} - -.theme-dark .pill_a2c9e8.selected_a2c9e8 { - background-color: var(--brand-15a); - border-color: var(--brand-500) -} - -.emoji_a2c9e8 { - height: 14px; - margin-inline-end:6px;width: 14px -} - -.emoji_a2c9e8.small_a2c9e8 { - height: 12px; - width: 12px -} - -.closeCircle_a2c9e8 { - align-items: center; - background-color: var(--background-surface-high); - border-radius: 50%; - cursor: pointer; - display: flex; - flex-shrink: 0; - justify-content: center -} - -.close_a2c9e8 { - color: var(--text-strong); - height: 12px; - width: 12px -} - -.tooltipPill_a2c9e8 { - margin: 4px 2px -} - -.full-motion .pill_a2c9e8 { - transition: background-color .2s ease -} - -.actionContainer_bc4513 { - background-color: var(--background-mod-normal); - border-radius: 80px; - flex-direction: row; - padding-inline:6px 8px;padding-bottom: 4px; - padding-top: 4px -} - -.actionContainer_bc4513,.actionIconContainer_bc4513 { - align-items: center; - display: flex; - justify-content: center -} - -.actionIconContainer_bc4513 { - height: 16px; - margin-inline-end:4px;width: 16px -} - -.actionIcon_bc4513 { - color: var(--text-muted) -} - -.actionTextContainer_bc4513 { - flex: 1 -} - -.actionTextHeader_bc4513 { - word-wrap: normal; - text-transform: lowercase -} - -.actionTextHelper_bc4513 { - margin-inline-start:4px;text-transform: lowercase -} - -.container__0e476 { - background-color: var(--input-background-default); - border: 1px solid var(--input-border-default); - border-radius: var(--radius-sm); - flex-shrink: 0; - overflow: hidden -} - -.container__0e476,.inner__0e476 { - box-sizing: border-box; - display: flex -} - -.inner__0e476 { - flex: 1 1 auto; - flex-direction: row; - flex-wrap: wrap; - padding: 1px; - position: relative -} - -.disabled__0e476 .inner__0e476 { - opacity: .3 -} - -.input__0e476 { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background: transparent; - border: none; - box-sizing: border-box; - color: var(--text-default); - flex: 1; - margin: 1px; - min-width: 48px; - resize: none -} - -.input__0e476::-webkit-input-placeholder { - color: var(--input-placeholder-text-default); - opacity: 1 -} - -.disabled__0e476 .input__0e476 { - cursor: not-allowed -} - -.tag__0e476 { - align-items: center; - background-color: var(--background-mod-normal); - border: 1px solid var(--border-subtle); - border-radius: 5px; - box-sizing: border-box; - color: var(--text-default); - cursor: pointer; - display: flex; - flex-direction: row; - margin: 1px; - text-align: center; - transition: none -} - -.tag__0e476:hover { - background-color: var(--background-mod-strong); - opacity: 1; - text-decoration: none -} - -.small__0e476 .input__0e476,.small__0e476 .tag__0e476 { - font-size: 14px; - font-weight: var(--font-weight-medium); - height: 20px; - line-height: 20px; - padding: 0 4px -} - -.medium__0e476 .input__0e476,.medium__0e476 .tag__0e476 { - font-size: 16px; - height: 30px; - line-height: 32px; - padding: 0 8px -} - -.large__0e476 .input__0e476,.large__0e476 .tag__0e476 { - font-size: 20px; - height: 38px; - line-height: 40px; - padding: 0 16px -} - -.richTag__0e476 { - background-color: var(--background-mod-normal); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.medium__0e476 .richTag__0e476,.medium__0e476 .richTagInput__0e476 { - height: 26px; - line-height: 24px; - margin: 3px 0; - margin-inline-start:3px} - -.large__0e476 .richTag__0e476,.large__0e476 .richTagInput__0e476 { - height: 32px; - line-height: 30px; - margin: 4px 0; - margin-inline-start:4px} - -.tagLabel__0e476 { - overflow: hidden; - padding-inline-start:6px;text-overflow: ellipsis -} - -.tagRoleColor__0e476 { - border-radius: 50%; - flex-shrink: 0; - height: 8px; - width: 8px -} - -.close__0e476 { - height: 12px; - margin-inline-start:4px;width: 12px -} - -.close__0e476,.iconLayout__0e476 { - box-sizing: border-box -} - -.iconLayout__0e476 { - align-items: center; - cursor: text; - display: flex; - height: 22px; - justify-content: center; - width: 22px -} - -.medium__0e476.iconLayout__0e476 { - height: 32px; - width: 32px -} - -.large__0e476.iconLayout__0e476 { - height: 40px; - width: 40px -} - -.iconContainer__0e476 { - box-sizing: border-box; - height: 16px; - position: relative; - width: 16px -} - -.medium__0e476 .iconContainer__0e476 { - height: 20px; - width: 20px -} - -.large__0e476 .iconContainer__0e476 { - height: 24px; - width: 24px -} - -.icon__0e476 { - box-sizing: border-box; - color: var(--text-muted); - height: 100%; - inset-inline-start: 0; - opacity: 0; - position: absolute; - top: 0; - transform: rotate(90deg); - transition: transform .1s ease-out,opacity .1s ease-out; - width: 100%; - z-index: 2 -} - -.icon__0e476.visible__0e476 { - opacity: 1; - transform: rotate(0deg) -} - -.clear__0e476 { - box-sizing: border-box; - cursor: pointer -} - -.clear__0e476 .icon__0e476 { - color: var(--interactive-text-default) -} - -.clear__0e476.iconLayout__0e476:hover .icon__0e476 { - color: var(--interactive-text-hover) -} - -.enable-forced-colors .input__0e476 { - border: 1px solid CanvasText; - border-radius: 4px -} - -.enable-forced-colors .input__0e476::-webkit-input-placeholder { - color: GrayText -} - -.enable-forced-colors .icon__0e476 { - background-color: Canvas; - border: 1px solid Canvas; - color: GrayText -} - -.enable-forced-colors .clear__0e476 .icon__0e476 { - background-color: ButtonFace; - border-color: CanvasText; - color: ButtonText -} - -.enable-forced-colors .clear__0e476.iconLayout__0e476:focus .icon__0e476,.enable-forced-colors .clear__0e476.iconLayout__0e476:hover .icon__0e476 { - border-color: ButtonText; - color: ButtonText -} - -.auditLog__43dab { - background-color: var(--card-background-default); - border: 1px solid; - border-color: var(--border-subtle); - border-radius: var(--radius-sm); - color: var(--text-default); - display: flex; - flex-direction: column; - padding: var(--space-4) -} - -.auditLog__43dab strong { - color: var(--text-strong); - font-weight: var(--font-weight-medium) -} - -.divider__43dab { - background-color: unset; - height: 1px; - width: 100% -} - -.header__43dab { - align-items: center; - display: flex; - padding-block:10px;padding-inline:10px 20px;transition: background-color .2s ease; - width: 100% -} - -.headerDefault__43dab { - cursor: default -} - -.headerClickable__43dab,.headerExpanded__43dab { - cursor: pointer -} - -.headerExpanded__43dab { - background-color: var(--card-secondary-bg); - border-radius: var(--radius-xs) -} - -.headerClickable__43dab { -} - -.timeWrap__43dab { - display: flex; - flex-direction: column; - margin: 0 10px; - max-width: calc(100% - 118px) -} - -.timeWrap__43dab,.title__43dab { - flex-grow: 1 -} - -.avatar__43dab { - margin-inline-start:10px} - -.expand__43dab .expandForeground__43dab { - transition: d .2s ease,stroke .2s ease -} - -.colorsHook__43dab { - font-weight: var(--font-weight-medium) -} - -.colorHook__43dab { - border-radius: 50%; - display: inline-block; - height: 10px; - position: relative; - top: -1px; - width: 10px -} - -.userHook__43dab { - align-items: baseline; - color: var(--text-strong); - display: inline-flex; - font-weight: var(--font-weight-medium) -} - -.changeDetails__43dab { - background-color: unset; - display: flex; - flex-direction: column; - padding: 0 8px 10px -} - -.detail__43dab { - align-items: baseline; - display: flex; - margin-top: 8px; - margin-inline-start:40px} - -.prefix__43dab { - flex-grow: 0 -} - -.typeCreate__43dab { - color: var(--green-230) -} - -.typeDelete__43dab { - color: var(--red-360) -} - -.typeUpdate__43dab { - color: var(--yellow-260) -} - -.dash__43dab { - margin: 0 10px -} - -.change__43dab { - display: flex; - flex-direction: column; - margin: 0; - overflow: hidden -} - -.changeStr__43dab,.overflowEllipsis__43dab { - overflow: hidden; - text-overflow: ellipsis -} - -.overflowEllipsis__43dab { - align-items: baseline; - white-space: nowrap -} - -.subListItem__43dab { - margin: 4px 0 -} - -.subListItem__43dab:last-child { - margin-bottom: 0 -} - -.onboardingChangeLogContainer__43dab { - align-items: baseline; - display: flex; - flex-direction: column; - gap: 8px; - margin: 12px 0 -} - -.onboardingChangeLogItemTitle__43dab { - align-items: flex-end; - display: flex; - flex-direction: row; - gap: 8px -} - -.onboardingChangeLogItemChanges__43dab { - margin-inline-start:40px;overflow: hidden -} - -.icon__43dab { - filter: saturate(var(--saturation-factor,1)); - height: 24px; - min-width: 24px; - position: relative; - width: 24px -} - -.icon__43dab:after { - content: " "; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.typeCreate__43dab:after { - background: url(/assets/9dabbd95e9e8a278.svg) -} - -.typeDelete__43dab:after { - background: url(/assets/273a82a28d0e1160.svg) -} - -.typeUpdate__43dab:after { - background: url(/assets/f50ede2a8b3701be.svg) -} - -.timestamp__43dab { - color: var(--text-muted) -} - -.theme-light .auditLog__43dab { - border-color: var(--primary-300) -} - -.theme-light .auditLog__43dab:hover .expandForeground__43dab { - stroke: hsl(var(--primary-400-hsl)/1) -} - -.theme-light .headerClickable__43dab,.theme-light .headerDefault__43dab { - background-color: hsl(var(--primary-100-hsl)/.3) -} - -.theme-light .headerExpanded__43dab { - background-color: var(--primary-100) -} - -.theme-light .discrim__43dab { - opacity: .6 -} - -.theme-light .expandForeground__43dab { - stroke: hsl(var(--primary-400-hsl)/.6) -} - -.theme-light .changeDetails__43dab { - background-color: hsl(var(--primary-100-hsl)/.3) -} - -.themeOverrideLight__43dab.icon__43dab.targetAll__43dab,.theme-light .icon__43dab.targetAll__43dab { - background: url(/assets/573976ea56ebddcd.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetBan__43dab,.theme-light .icon__43dab.targetBan__43dab { - background: url(/assets/24243230825c2c4e.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetChannel__43dab,.theme-light .icon__43dab.targetChannel__43dab { - background: url(/assets/39fa213330e56d4a.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetGuild__43dab,.theme-light .icon__43dab.targetGuild__43dab { - background: url(/assets/4161f29c69130cdd.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetEmoji__43dab,.theme-light .icon__43dab.targetEmoji__43dab { - background: url(/assets/b59ebc1822da2a0e.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetSticker__43dab,.theme-light .icon__43dab.targetSticker__43dab { - background: url(/assets/d6e3f94f5910677c.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetIntegration__43dab,.theme-light .icon__43dab.targetIntegration__43dab { - background: url(/assets/90a2a4f8d8fa159f.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetInvite__43dab,.theme-light .icon__43dab.targetInvite__43dab { - background: url(/assets/440d208fccbaba65.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetMemberRole__43dab,.theme-light .icon__43dab.targetMemberRole__43dab { - background: url(/assets/0119c42e7eb9c848.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetMember__43dab,.theme-light .icon__43dab.targetMember__43dab { - background: url(/assets/e168c3c034c1d0cb.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetPermission__43dab,.theme-light .icon__43dab.targetPermission__43dab { - background: url(/assets/f0d99535c0dceb52.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetRole__43dab,.theme-light .icon__43dab.targetRole__43dab { - background: url(/assets/e850e1e3d9500baa.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetOnboarding__43dab,.theme-light .icon__43dab.targetOnboarding__43dab { - background: url(/assets/0f665f281fbc0180.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetVanityUrl__43dab,.theme-light .icon__43dab.targetVanityUrl__43dab { - background: url(/assets/c7b5dc58ffc4e595.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetWebhook__43dab,.theme-light .icon__43dab.targetWebhook__43dab { - background: url(/assets/10d3479da12f9040.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetWidget__43dab,.theme-light .icon__43dab.targetWidget__43dab { - background: url(/assets/bdf6c4d043e4e45c.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetMessage__43dab,.theme-light .icon__43dab.targetMessage__43dab { - background: url(/assets/feaccc58464f03a4.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetStageInstance__43dab,.theme-light .icon__43dab.targetStageInstance__43dab { - background: url(/assets/c7cf6befa3c699d9.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetGuildScheduledEvent__43dab,.theme-light .icon__43dab.targetGuildScheduledEvent__43dab { - background: url(/assets/83d40d9ba3a9db6f.svg) -} - -.themeOverrideLight__43dab.icon__43dab.thread__43dab,.theme-light .icon__43dab.thread__43dab { - background: url(/assets/608c1103a1687613.svg) -} - -.themeOverrideLight__43dab.icon__43dab.applicationCommand__43dab,.theme-light .icon__43dab.applicationCommand__43dab { - background: url(/assets/c54e976df2bc84cb.svg) -} - -.themeOverrideLight__43dab.icon__43dab.autoModerationRule__43dab,.theme-light .icon__43dab.autoModerationRule__43dab { - background: url(/assets/10d3479da12f9040.svg) -} - -.themeOverrideLight__43dab.icon__43dab.autoModerationBlockMessage__43dab,.theme-light .icon__43dab.autoModerationBlockMessage__43dab { - background: url(/assets/10d3479da12f9040.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetGuildHome__43dab,.theme-light .icon__43dab.targetGuildHome__43dab { - background: url(/assets/69869694b4135993.svg) -} - -.themeOverrideLight__43dab.icon__43dab.targetGuildSoundboard__43dab,.theme-light .icon__43dab.targetGuildSoundboard__43dab { - background: url(/assets/f21dd7f12034e6cb.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetAll__43dab,.theme-dark .icon__43dab.targetAll__43dab { - background: url(/assets/df1be54a7d6a8857.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetBan__43dab,.theme-dark .icon__43dab.targetBan__43dab { - background: url(/assets/798d305c92bcebf6.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetChannel__43dab,.theme-dark .icon__43dab.targetChannel__43dab { - background: url(/assets/b120d96b118bf2c1.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetGuild__43dab,.theme-dark .icon__43dab.targetGuild__43dab { - background: url(/assets/ac84ae8206a6f5e1.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetEmoji__43dab,.theme-dark .icon__43dab.targetEmoji__43dab { - background: url(/assets/e8e77b2ca603a44a.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetSticker__43dab,.theme-dark .icon__43dab.targetSticker__43dab { - background: url(/assets/6d6c4298fbda978b.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetIntegration__43dab,.theme-dark .icon__43dab.targetIntegration__43dab { - background: url(/assets/0f213e7334ec406b.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetInvite__43dab,.theme-dark .icon__43dab.targetInvite__43dab { - background: url(/assets/83713b6ddb3aaba9.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetMemberRole__43dab,.theme-dark .icon__43dab.targetMemberRole__43dab { - background: url(/assets/e50ed58cbe31755a.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetMember__43dab,.theme-dark .icon__43dab.targetMember__43dab { - background: url(/assets/69a57bf1542db6d0.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetPermission__43dab,.theme-dark .icon__43dab.targetPermission__43dab { - background: url(/assets/02df409dfd89ffbc.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetRole__43dab,.theme-dark .icon__43dab.targetRole__43dab { - background: url(/assets/eea18d5e3a85428c.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetOnboarding__43dab,.theme-dark .icon__43dab.targetOnboarding__43dab { - background: url(/assets/e35fcd676e720a43.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetVanityUrl__43dab,.theme-dark .icon__43dab.targetVanityUrl__43dab { - background: url(/assets/b74a77e98b76ceb0.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetWebhook__43dab,.theme-dark .icon__43dab.targetWebhook__43dab { - background: url(/assets/91bd7f1643fe02ff.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetWidget__43dab,.theme-dark .icon__43dab.targetWidget__43dab { - background: url(/assets/a0ba681b0596ef90.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetMessage__43dab,.theme-dark .icon__43dab.targetMessage__43dab { - background: url(/assets/14bedd367511ce89.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetStageInstance__43dab,.theme-dark .icon__43dab.targetStageInstance__43dab { - background: url(/assets/b81e4773a96fb05d.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetGuildScheduledEvent__43dab,.theme-dark .icon__43dab.targetGuildScheduledEvent__43dab { - background: url(/assets/06e0c61b48f72f6d.svg) -} - -.themeOverrideDark__43dab.icon__43dab.thread__43dab,.theme-dark .icon__43dab.thread__43dab { - background: url(/assets/f31aad3177a6c7c8.svg) -} - -.themeOverrideDark__43dab.icon__43dab.applicationCommand__43dab,.theme-dark .icon__43dab.applicationCommand__43dab { - background: url(/assets/3934256e60927112.svg) -} - -.themeOverrideDark__43dab.icon__43dab.autoModerationRule__43dab,.theme-dark .icon__43dab.autoModerationRule__43dab { - background: url(/assets/91bd7f1643fe02ff.svg) -} - -.themeOverrideDark__43dab.icon__43dab.autoModerationBlockMessage__43dab,.theme-dark .icon__43dab.autoModerationBlockMessage__43dab { - background: url(/assets/91bd7f1643fe02ff.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetGuildHome__43dab,.theme-dark .icon__43dab.targetGuildHome__43dab { - background: url(/assets/606a039f64780e30.svg) -} - -.themeOverrideDark__43dab.icon__43dab.targetGuildSoundboard__43dab,.theme-dark .icon__43dab.targetGuildSoundboard__43dab { - background: url(/assets/547b254784ef1abb.svg) -} - -.headerClickable__43dab,.headerDefault__43dab { - background-color: unset; - color: var(--text-default) -} - -.inviteChipletContainer__23437 { - display: flex; - flex-direction: row -} - -.inviteChiplet__23437 { - color: var(--text-muted); - flex: 0 -} - -.inviteContainer__23437 { - background-color: var(--background-mod-subtle); - border-radius: var(--radius-xs); - cursor: pointer; - overflow: hidden; - padding: 0 4px; - white-space: nowrap; - width: -moz-fit-content; - width: fit-content -} - -.inviteContainer__23437,.inviterTooltipContainer__23437,.linkedChannelContainer__23437 { - align-items: center; - display: flex; - gap: 4px -} - -.linkedChannelContainer__23437 { - cursor: pointer; - max-width: 250px -} - -.linkedChannelApplicationName__23437 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.robot__23437 { - color: var(--text-link) -} - -.integrationIcon__23437 { - background-size: contain -} - -.unknownInvite__23437 { - flex: 0; - width: -moz-fit-content; - width: fit-content -} - -.inviterFooter__23437 { - margin-top: 4px -} - -.inviterUserContainer__23437 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px -} - -.clickable__23437 { - cursor: pointer -} - -.footerAlignment__23437 { - align-items: flex-end; - display: flex; - flex-direction: column -} - -.modInfoItemContainer__96c0b { - border-radius: var(--radius-sm); - flex-direction: column; - gap: 0; - overflow: hidden -} - -.modInfoItem__96c0b,.modInfoItemContainer__96c0b { - box-shadow: var(--elevation-stroke); - display: flex -} - -.modInfoItem__96c0b { - align-items: center; - background-color: var(--background-base-lower); - flex: 1; - flex-direction: row; - gap: 8px; - padding: 12px -} - -.modInfoItem__96c0b:not(:last-child) { - margin-bottom: 1px -} - -.modInfoItemIcon__96c0b { - flex: 0 0 auto; - height: 16px; - width: 16px -} - -.modInfoItemName__96c0b { - flex: 1 1 auto -} - -.modInfoItemDescription__96c0b { - display: block; - text-align: end -} - -.modInfoAction__96c0b { - box-shadow: var(--elevation-stroke); - color: var(--text-muted); - cursor: pointer; - transition: background-color .2s ease-in-out,color .2s ease-in-out -} - -.modInfoAction__96c0b:hover { - background-color: var(--interactive-background-active); - color: var(--interactive-text-hover) -} - -.modInfoItemActionIcon__96c0b { - flex: 0 0 auto; - height: 16px; - width: 16px -} - -.container__3a5a1 { - background-color: color-mix(in oklab,var(--background-surface-high),transparent 20%); - border-radius: var(--radius-sm); - box-shadow: var(--elevation-low); - overflow: hidden -} - -.container__3a5a1,.innerContainer__3a5a1 { - display: flex; - flex: 1; - flex-direction: column -} - -.innerContainer__3a5a1 { - gap: 20px; - padding: 12px 16px -} - -.keyComboInner__3a5a1 { - border: 1px solid var(--interactive-background-hover)!important; - box-shadow: inset 0 -4px 0 var(--interactive-background-selected)!important; - color: var(--text-default)!important -} - -.keyComboContainer__3a5a1,.keyComboInner__3a5a1 { - background-color: var(--interactive-background-selected) -} - -.keyComboContainer__3a5a1 { - border-radius: var(--radius-sm) -} - -.header__3a5a1 { - background-color: color-mix(in oklab,var(--background-base-lower),transparent 20%); - box-shadow: var(--elevation-stroke),var(--elevation-low); - padding: 12px 16px; - z-index: 1 -} - -.header__3a5a1,.headerTitle__3a5a1 { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px -} - -.headerTitle__3a5a1 { - flex: 1 -} - -.infoTitle__3a5a1 { - padding-bottom: 4px -} - -.roleContainer__3a5a1 { - align-items: center; - display: flex; - flex-direction: row; - flex-wrap: wrap; - gap: 4px -} - -.highestRole__3a5a1 { - box-shadow: var(--elevation-stroke),var(--custom-guild-shop-channel-row-glow) -} - -.otherRoles__3a5a1 { - align-items: center; - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: center -} - -.addRoleIcon__3a5a1 { - color: var(--interactive-text-default); - transition: color 80ms cubic-bezier(.48,1.93,1,.68) -} - -.roleTooltip__3a5a1 { - padding-inline-end:0;padding: 4px 4px 0 -} - -.roleTooltipItemContainer__3a5a1 { - display: flex; - flex-direction: column; - gap: 4px; - padding: 8px -} - -.roleTooltipItem__3a5a1 { - overflow: hidden -} - -.addRoleContainer__3a5a1 { - cursor: pointer -} - -.guildIcon__3a5a1 { - border-radius: var(--radius-sm); - overflow: hidden -} - -.loadingSpinner__3a5a1 { - flex: 0; - height: 16px; - margin: 0; - width: 16px -} - -.verifiedIcon__3a5a1 { - color: var(--text-feedback-positive) -} - -.unverifiedIcon__3a5a1 { - color: var(--text-feedback-critical) -} - -.noRoles__3a5a1 { - background-color: var(--background-mod-subtle); - border-radius: 4px; - padding: 4px -} - -.auditLogItem_b2f52f { - align-items: flex-start; - display: flex; - flex: 1; - flex-direction: column; - gap: 8px; - justify-content: space-between; - padding: 12px -} - -.auditLogItemTitleContainer_b2f52f { - display: flex; - flex: 1; - flex-direction: row; - gap: 4px; - width: 100% -} - -.auditLogItemTitle_b2f52f { - display: flex; - flex: 1; - flex-direction: row; - gap: 4px -} - -.username_b2f52f { - cursor: pointer; - margin-inline-end:4px} - -.auditLogItemDate_b2f52f { - align-self: flex-end -} - -.auditLogReason_b2f52f { - display: block -} - -.auditLogSecondaryContainer_b2f52f { - display: flex; - flex-direction: row; - gap: 4px -} - -.auditLogSecondary_b2f52f { - flex: 1 -} - -.auditLogExpandedChangeDetails_b2f52f { - background-color: transparent!important; - padding: 0 -} - -.auditLogExpandedChangeDetails_b2f52f>div { - margin: 0 -} - -.permissionTooltip__1ef77 { - padding-inline-end:0;padding: 4px 4px 0 -} - -.roleTooltipContainer__1ef77 { - display: block; - max-width: 300px; - width: auto -} - -.roleTooltipContentContainer__1ef77 { - align-items: center; - display: flex; - flex-direction: row; - flex-wrap: wrap; - gap: 4px; - justify-content: center; - width: auto -} - -.roleTooltipContentContainer__1ef77,.roleTooltipItem__1ef77 { - align-self: center; - flex-basis: auto; - flex-grow: 0; - flex-shrink: 1 -} - -.roleTooltipItem__1ef77 { - overflow: hidden -} - -.permissionsContainer__1ef77 { - display: flex; - flex-direction: row; - flex-wrap: wrap; - gap: 8px -} - -.permissionChiplet__1ef77 { - align-items: center; - background-color: var(--background-mod-subtle); - border-radius: 4px; - cursor: normal; - display: flex; - gap: 4px; - padding: 4px -} - -.noModPerms__1ef77 { - cursor: not-allowed -} - -.elevatedPermission__1ef77 { - background-color: var(--background-mod-strong) -} - -.headerContainer__1ef77 { - gap: 8px; - justify-content: space-between -} - -.headerContainer__1ef77,.viewAllPermissions__1ef77 { - align-items: center; - display: flex; - flex-direction: row -} - -.viewAllPermissions__1ef77 { - cursor: pointer -} - -.wrapper__2ea32 { - margin-inline-start:var(--space-xs);overflow: visible; - padding: 1px 0; - position: relative -} - -.icon__2ea32 { - color: var(--channel-icon) -} - -.typeThread__2ea32.wrapper__2ea32 { - margin-inline-start:calc(28px + var(--space-xs) + var(--space-xs) - var(--space-xs))} - -.typeThread__2ea32 .name__2ea32 { - font-size: 14px -} - -.typeThread__2ea32 .link__2ea32 { - padding-block:4px;padding-inline:var(--space-xs) var(--space-xs)} - -.typeThread__2ea32 .unread__2ea32 { - inset-inline-start: calc((16px + var(--space-md))*-1 + 1px) -} - -.typeThread__2ea32.withGuildIcon__2ea32 { - margin-inline-start:42px} - -.typeThread__2ea32.withGuildIcon__2ea32 .unread__2ea32 { - inset-inline-start: -42px -} - -.modeUnreadImportant__2ea32 .name__2ea32,.modeUnreadImportant__2ea32:hover .name__2ea32 { - color: var(--text-strong) -} - -.wrapper__2ea32.modeSelected__2ea32 { - cursor: default -} - -.wrapper__2ea32.modeLocked__2ea32 { - cursor: not-allowed -} - -.icon__2ea32 { - display: block; - flex: 0 0 auto -} - -.icon__2ea32,.iconContainer__2ea32 { - height: 20px; - width: 20px -} - -.iconContainer__2ea32 { - margin-inline-end:0;position: relative -} - -.iconContainerWithGuildIcon__2ea32 { - margin-bottom: -2px; - margin-top: -2px -} - -.favoritesSuggestion__2ea32 { - border: 2px dashed var(--border-subtle); - border-radius: var(--radius-md); - box-sizing: border-box -} - -.hdStreamed__2ea32 { - background: linear-gradient(90deg,rgba(255,89,120,.15),rgba(111,74,255,.15)); - border-radius: var(--radius-xs) -} - -.link__2ea32 { - border-radius: var(--radius-sm); - box-sizing: border-box; - cursor: pointer; - display: flex; - flex-direction: column; - padding: 6px 8px; - padding-top: var(--space-xxs); - padding-inline:var(--space-xs);padding-bottom: var(--space-xxs); - position: relative -} - -.link__2ea32:before { - content: ""; - display: block; - position: absolute; - top: -1px; - inset-inline: 0; - bottom: -1px -} - -.basicChannelRowLink__2ea32 { - min-height: 24px -} - -.linkTop__2ea32 { - gap: var(--space-xs); - justify-content: center -} - -.linkBottom__2ea32,.linkTop__2ea32 { - align-items: center; - display: flex; - position: relative -} - -.linkBottom__2ea32 { - margin-inline-start:calc(20px + var(--space-xs))} - -.linkBottom__2ea32.withGuildIcon__2ea32 { - margin-inline-start: 38px -} - -.name__2ea32 { - flex: 1 1 auto; - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: var(--channels-name-line-height) -} - -.name__2ea32,.subtitle__2ea32 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.unread__2ea32 { - background-color: var(--channels-default); - border-radius: 0 var(--radius-xs) var(--radius-xs) 0; - height: 8px; - inset-inline-start: calc(var(--space-xs)*-1 + 1px); - margin-top: -4px; - position: absolute; - top: 50%; - width: 4px -} - -.unread__2ea32.unreadWithHangStatus__2ea32 { - top: 25% -} - -.children__2ea32 { - align-items: center; - display: flex; - flex: 0 0 auto; - justify-content: center -} - -.modeMuted__2ea32 .icon__2ea32 { - color: var(--icon-muted); - opacity: .5 -} - -.modeMuted__2ea32 .name__2ea32 { - color: var(--text-muted); - opacity: .5 -} - -.modeMuted__2ea32 .acronym__2ea32,.modeMuted__2ea32 .iconWithGuildIcon__2ea32 { - background: var(--background-mod-muted); - color: var(--text-muted) -} - -.theme-dark.custom-theme-background.custom-client-theme .modeMuted__2ea32 .icon__2ea32,.theme-dark.custom-theme-background.custom-client-theme .modeMuted__2ea32 .name__2ea32 { - color: color-mix(in oklab,var(--text-muted) 40%,transparent) -} - -.theme-dark.custom-theme-background.custom-client-theme .modeMuted__2ea32:hover .icon__2ea32,.theme-dark.custom-theme-background.custom-client-theme .modeMuted__2ea32:hover .name__2ea32 { - color: var(--interactive-text-hover) -} - -.modeSelected__2ea32 .icon__2ea32,.modeSelected__2ea32:hover .icon__2ea32 { - color: var(--channel-icon) -} - -.modeUnreadImportant__2ea32 .icon__2ea32,.modeUnreadImportant__2ea32:hover .icon__2ea32 { - color: var(--icon-subtle) -} - -.wrapper__2ea32:hover .link__2ea32 { - background: var(--interactive-background-hover) -} - -.wrapper__2ea32:active .link__2ea32 { - background: var(--background-modifier-active,var(--interactive-background-active)) -} - -.modeUnreadImportant__2ea32 .iconWithGuildIcon__2ea32,.modeUnreadImportant__2ea32:hover .iconWithGuildIcon__2ea32 { - color: var(--text-subtle) -} - -.modeSelected__2ea32 .iconWithGuildIcon__2ea32,.modeSelected__2ea32:hover .iconWithGuildIcon__2ea32 { - background: var(--interactive-text-default); - color: var(--background-base-lower) -} - -.modeSelected__2ea32 .acronym__2ea32,.modeSelected__2ea32:hover .acronym__2ea32 { - background: var(--brand-500); - color: var(--white) -} - -.modeSelected__2ea32 .link__2ea32,.modeSelected__2ea32:hover .link__2ea32 { - background: var(--interactive-background-selected) -} - -.name__2ea32 { - color: var(--channels-default) -} - -.modeMuted__2ea32:hover .name__2ea32,.wrapper__2ea32:hover .name__2ea32 { - color: var(--interactive-text-hover) -} - -.modeConnected__2ea32 .name__2ea32,.modeConnected__2ea32:hover .name__2ea32,.modeSelected__2ea32 .name__2ea32,.modeSelected__2ea32:hover .name__2ea32,.modeUnreadImportant__2ea32 .name__2ea32,.modeUnreadImportant__2ea32:hover .name__2ea32,.notInteractive__2ea32.modeConnected__2ea32 .name__2ea32,.notInteractive__2ea32.modeSelected__2ea32 .name__2ea32 { - color: var(--interactive-text-active); - font-weight: var(--font-weight-semibold-1x-light-theme,500) -} - -.high-contrast-mode .modeConnected__2ea32 .name__2ea32,.high-contrast-mode .modeConnected__2ea32:hover .name__2ea32,.high-contrast-mode .modeSelected__2ea32 .name__2ea32,.high-contrast-mode .modeSelected__2ea32:hover .name__2ea32,.high-contrast-mode .modeUnreadImportant__2ea32 .name__2ea32,.high-contrast-mode .modeUnreadImportant__2ea32:hover .name__2ea32 { - font-weight: var(--font-weight-semibold) -} - -.unreadImportant__2ea32 { - background-color: var(--interactive-text-active) -} - -.numberBadge__2ea32 { - background-color: var(--background-mod-strong) -} - -.newBadge__2ea32 { - color: var(--brand-560) -} - -@media (max-width: 485px) { - .modeSelected__2ea32 .link__2ea32 { - background-color:unset - } -} - -.modeConnected__2ea32 .name__2ea32,.modeConnected__2ea32:hover .name__2ea32,.modeSelected__2ea32 .name__2ea32,.modeSelected__2ea32:hover .name__2ea32 { - color: var(--text-strong) -} - -.modeConnected__2ea32 .icon__2ea32,.modeConnected__2ea32:hover .icon__2ea32,.modeSelected__2ea32 .icon__2ea32,.modeSelected__2ea32:hover .icon__2ea32 { - color: var(--icon-strong) -} - -.activeEvent__2ea32 { - color: var(--text-feedback-positive)!important -} - -[data-favorites=true] .iconContainer__2ea32:has(.iconWithGuildIcon__2ea32) { - height: auto; - width: auto -} - -.children__2ea32:empty { - display: none -} - -:where(.refresh-fast-follow-avatars) .wrapper__2ea32 { - margin-inline-start:min(var(--space-xs),var(--space-8))} - -.enable-forced-colors .unread__2ea32 { - background-color: Highlight; - height: 10px; - margin-top: -5px; - outline: 1px solid Canvas; - width: 6px -} - -.enable-forced-colors .icon__2ea32,.enable-forced-colors .name__2ea32 { - color: inherit -} - -.enable-forced-colors .link__2ea32 { - background-color: ButtonFace; - border: 1px solid ButtonFace -} - -.enable-forced-colors .icon__2ea32,.enable-forced-colors .name__2ea32,.enable-forced-colors .topContent__2ea32 { - color: ButtonText; - forced-color-adjust: none -} - -.enable-forced-colors .wrapper__2ea32:hover .link__2ea32 { - background-color: ButtonFace; - border-color: ButtonText -} - -.enable-forced-colors .wrapper__2ea32:hover .icon__2ea32,.enable-forced-colors .wrapper__2ea32:hover .name__2ea32,.enable-forced-colors .wrapper__2ea32:hover .topContent__2ea32 { - color: ButtonText -} - -.enable-forced-colors .modeSelected__2ea32 .link__2ea32,.enable-forced-colors .modeSelected__2ea32:hover .link__2ea32 { - background-color: Highlight; - border-color: HighlightText -} - -.enable-forced-colors .modeSelected__2ea32 .icon__2ea32,.enable-forced-colors .modeSelected__2ea32 .name__2ea32,.enable-forced-colors .modeSelected__2ea32 .topContent__2ea32,.enable-forced-colors .modeSelected__2ea32:hover .icon__2ea32,.enable-forced-colors .modeSelected__2ea32:hover .name__2ea32,.enable-forced-colors .modeSelected__2ea32:hover .topContent__2ea32 { - color: HighlightText -} - -.enable-forced-colors .modeMuted__2ea32 { - opacity: .4 -} - -.enable-forced-colors .modeMuted__2ea32:focus,.enable-forced-colors .modeMuted__2ea32:hover { - opacity: 1 -} - -.barsRoot__70b7b { - height: 100%; - pointer-events: none; - position: relative; - width: 100% -} - -.animatedIconContainer__70b7b { - display: inline-flex -} - -.barsGroup__70b7b { - align-items: center; - display: flex; - isolation: isolate; - overflow: hidden; - pointer-events: none; - position: absolute; - transform: translateZ(0) -} - -.bar__70b7b,.barsGroup__70b7b { - will-change: transform -} - -.bar__70b7b { - border-radius: 1px; - display: block; - flex: 0 0 auto; - transform-origin: center center -} - -.icon__2894c { - display: block; - flex: 0 0 auto; - height: 20px; - width: 20px -} - -.iconWithGuildIcon__2894c { - background: var(--background-mod-strong); - border-radius: var(--radius-round); - color: var(--text-subtle); - height: 12px; - margin-inline-start:12px;margin-top: -15px; - overflow: visible; - padding: 4px; - position: relative; - width: 12px -} - -.channelGuildIcon__2894c { - border-radius: var(--radius-sm) -} - -.acronym__2894c { - align-items: center; - background-color: var(--background-mod-strong); - color: var(--text-default); - display: flex; - font-weight: var(--font-weight-medium); - justify-content: center; - line-height: 1.2em; - white-space: nowrap -} - -.container_b82a71 { - color: var(--text-strong) -} - -.container__80bf8 { - outline: none; - position: relative -} - -.searchResult__80bf8 { - background-color: var(--background-base-lower); - border: 1px solid var(--border-subtle); - border-radius: 8px; - box-sizing: border-box; - cursor: pointer; - margin-bottom: 8px; - overflow: hidden -} - -.message__80bf8 { - padding: 6px 0 -} - -.buttonsContainer__80bf8 { - display: none; - inset-inline-end: 8px; - position: absolute; - top: 8px -} - -.container__80bf8:focus-within .buttonsContainer__80bf8,.container__80bf8:hover .buttonsContainer__80bf8 { - display: flex -} - -.button__80bf8 { - background-color: var(--background-base-lowest); - border-radius: 3px; - box-sizing: border-box; - color: var(--text-default); - cursor: pointer; - font-size: 12px; - font-weight: var(--font-weight-medium); - height: 24px; - line-height: 16px; - margin-inline-start:6px;padding: 4px; - text-align: center; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - z-index: 1 -} - -.button__80bf8:hover { - color: var(--interactive-text-hover) -} - -.button__80bf8:active { - color: var(--interactive-text-active); - transform: translate3d(0,1px,0) -} - -.searchResultGroup_a7e67f { - margin-bottom: 24px -} - -.channelNameContainer_a7e67f { - align-items: center; - cursor: pointer; - display: flex; - margin-bottom: 8px -} - -.channelNameText_a7e67f { - color: var(--text-strong); - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.channelNameText_a7e67f:hover { - text-decoration: underline -} - -.parentChannelName_a7e67f { - align-items: center; - display: flex -} - -.parentChannelNameClickable_a7e67f .parentChannelNameText_a7e67f:hover { - text-decoration: underline -} - -.parentChannelNameText_a7e67f { - white-space: nowrap -} - -.channelNameIcon_a7e67f { - color: var(--text-strong); - flex-shrink: 0; - padding-inline-end:4px} - -.parentChannelNameIcon_a7e67f { - color: var(--text-default); - flex-shrink: 0; - margin-inline-start:4px;padding-inline-end:4px} - -.resultsBlocked_a7e67f { - align-items: center; - background-color: var(--background-mod-normal); - border: 1px solid var(--background-base-lowest); - border-radius: 3px; - color: var(--text-muted); - cursor: pointer; - display: flex; - font-size: 14px; - padding: 6px 18px -} - -.resultsBlocked_a7e67f:hover { - color: var(--text-default) -} - -.resultsBlockedImage_a7e67f { - height: 32px; - margin-inline-end:20px;width: 32px -} - -.images-light .resultsBlockedImage_a7e67f { - background-image: url(/assets/3f044af39abb8630.svg) -} - -.images-dark .resultsBlockedImage_a7e67f { - background-image: url(/assets/abbc14555963e723.svg) -} - -.keyComboInner__15c82 { - background-color: var(--background-mod-subtle); - border: 1px solid var(--interactive-background-hover)!important; - box-shadow: inset 0 -4px 0 var(--interactive-background-selected)!important; - color: var(--text-default)!important -} - -.keyComboContainer__15c82 { - background-color: var(--interactive-background-selected); - border-radius: var(--radius-sm) -} - -.backButton__15c82 { - align-items: center; - cursor: pointer; - display: flex; - flex-direction: row; - gap: 4px -} - -.innerContainer__39de8 { - display: flex; - flex: 1; - flex-direction: column; - gap: 16px; - margin-top: 1px; - overflow: hidden; - padding: 12px 16px -} - -.innerContainer__39de8 li>div { - background-color: var(--background-base-lower)!important; - box-shadow: var(--elevation-stroke) -} - -.permissionsGroupContainer__3f11f { - padding: 16px -} - -.permissionItemContainer__3f11f,.permissionsItemContainer__3f11f { - display: flex; - flex-direction: column; - gap: 12px -} - -.permissionItemContainer__3f11f { - background-color: var(--background-base-lower); - border-radius: 8px; - box-shadow: none; - padding: 12px -} - -.permissionItemContainer__3f11f.elevatedPermission__3f11f { - box-shadow: var(--elevation-stroke) -} - -.permissionItemHeader__3f11f { - align-items: flex-start; - display: flex; - flex-direction: row; - justify-content: space-between -} - -.permissionItemSubheader__3f11f { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px -} - -.elevatedPermissionContainer__3f11f { - align-items: center; - display: flex; - flex-direction: row; - gap: 2px -} - -.permissionItemDescription__3f11f { - flex: 1 -} - -.permissionItemRoleContainer__3f11f { - align-items: center; - display: flex; - flex-direction: row; - flex-wrap: wrap; - gap: 8px -} - -.permissionTitle__3f11f { - overflow: hidden; - text-overflow: ellipsis; - vertical-align: top -} - -.roleTooltipItem__3f11f { - align-items: center; - display: flex; - flex-direction: row; - margin: 0; - padding: 0 -} - -.roleTooltipItem__3f11f.editable__3f11f { - cursor: pointer -} - -.roleChiplet__3f11f { - background-color: var(--background-mod-strong); - border-radius: 4px; - height: auto; - margin: 0; - padding: 4px -} - -.container__34940 { - background-color: var(--background-mod-subtle); - border-radius: var(--radius-sm); - box-shadow: var(--elevation-low); - display: flex; - flex-direction: column; - overflow: hidden -} - -.topRow__34940 { - align-items: center; - background-color: color-mix(in oklab,var(--background-base-lower),transparent 20%); - display: flex; - flex-direction: row; - justify-content: space-between; - padding: 12px 8px -} - -.memberNameAndTagContainer__34940 { - display: flex; - flex-direction: column -} - -.memberNameContainer__34940 { - display: flex; - flex-direction: row; - gap: 8px -} - -.memberName__34940 { - gap: 4px -} - -.memberName__34940,.memberNameGlobal__34940 { - align-items: center; - display: flex; - flex-direction: row; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.memberNameTextContainer__34940 { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px -} - -.memberClanTag__34940 { - padding: 4px -} - -.memberClanTagContainer__34940 { - flex-shrink: 0 -} - -.memberAvatar__34940 { - align-self: center; - grid-row: span 2 -} - -.closeAction__34940 { - color: var(--text-default); - cursor: pointer; - padding: 4px -} - -.bottomRow__34940 { - align-items: center; - display: flex; - flex-direction: row; - gap: 1px; - justify-content: space-between; - margin-top: 1px -} - -.bottomRowAction__34940 { - background-color: color-mix(in oklab,var(--background-base-lower),transparent 20%); - color: var(--interactive-text-default); - cursor: pointer; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.bottomRowAction__34940,.bottomRowAction__34940 .innerBottomRowAction__34940 { - align-items: center; - display: flex; - flex: 1; - flex-direction: column; - justify-content: center -} - -.bottomRowAction__34940 .innerBottomRowAction__34940 { - gap: 4px; - height: 52px; - padding: 12px 8px; - width: calc(100% - 16px) -} - -.bottomRowAction__34940 .innerBottomRowAction__34940:hover { - background-color: var(--interactive-background-selected); - color: var(--interactive-text-hover) -} - -.bottomRowActionDisabled__34940 { - cursor: not-allowed -} - -.bottomRowActionDisabled__34940 .innerBottomRowAction__34940 { - opacity: .5 -} - -.bottomRowActionDisabled__34940 .innerBottomRowAction__34940,.bottomRowActionDisabled__34940 .innerBottomRowAction__34940:hover { - background-color: var(--background-mod-subtle); - color: var(--text-muted) -} - -.sidebarContainer__656be { - margin: 0; - margin-inline-start:8px} - -.loadingContainer__656be,.sidebarContainer__656be { - display: flex; - flex-direction: column; - gap: 8px; - height: 100% -} - -.loadingContainer__656be { - align-items: center; - justify-content: center -} - -div.profileThemedContainer__656be { - border-radius: 8px 0 0 8px; - box-shadow: none; - max-height: auto; - min-height: auto; - width: auto -} - -.innerContainer__656be,div.profileThemedContainer__656be { - display: flex; - flex: 1; - flex-direction: column; - gap: 8px -} - -.innerContainer__656be { - color: var(--text-default); - padding: 8px; - position: relative -} - -.searchIndexAnimation__8a209 { - height: 280px; - position: relative; - width: 320px -} - -.searchIndexBackground__8a209,.searchIndexForeground__8a209 { - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.background__8a209,.magnifyingGlass__8a209,.mask__8a209 { - transform-origin: center -} - -.mask__8a209 { - height: 80px; - width: 80px -} - -.images-light .searchIndexBackground__8a209 { - background-image: url(/assets/e409bdbc405200da.svg) -} - -.images-dark .searchIndexBackground__8a209 { - background-image: url(/assets/436b5a86429f186a.svg) -} - -/*# sourceMappingURL=1f7b87510348dd0c.css.map*/ diff --git a/discord-html-copy/css/3ec8cc660f6f6173.css b/discord-html-copy/css/3ec8cc660f6f6173.css deleted file mode 100644 index 07ba5a4..0000000 --- a/discord-html-copy/css/3ec8cc660f6f6173.css +++ /dev/null @@ -1,3083 +0,0 @@ -.key__98feb { - background: var(--background-base-low); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-xs); - color: var(--text-muted); - font-family: var(--font-code); - font-size: 10px; - font-style: normal; - font-weight: 600; - height: 16px; - line-height: 14px; - text-transform: uppercase; -} - -.key__98feb + .key__98feb { - margin-inline-start: 0px; } - -.keySpan__98feb { - box-sizing: border-box; - display: inline-block; - margin-inline: var(--space-4); padding: 0 var(--space-4); -} - -.keyDiv__98feb { - align-items: center; - display: flex; - font-size: 10px; - height: 10px; - justify-content: center; - line-height: 12px; - padding: 2px 4px; -} - -.keybindFlexboxLayout_cbf20c { - align-items: baseline; - display: flex; - flex: 0 0 auto; - flex-flow: wrap; - gap: var(--space-4); - justify-content: flex-start; - max-width: 100%; - overflow: visible; - white-space: normal; - width: fit-content; -} - -.keyCombo_cbf20c { - vertical-align: middle; -} - -.keyCombo_cbf20c, .keyComboKey_cbf20c { - display: inline-block; -} - -.bgShade__6d791 { - background-color: rgba(19, 19, 24, 0.9); - border: 1px solid rgba(255, 255, 255, 0.08); - box-sizing: border-box; -} - -.keybind__6d791 { - background-color: var(--background-mod-strong); - border-color: var(--border-strong); - color: var(--text-strong); -} - -.titleWrapper__6d791 { - align-items: center; - border-radius: 12px; - display: flex; - flex-direction: row; - justify-content: flex-start; - max-width: 100%; - padding: 2px 8px; -} - -.titleWrapperWithHint__6d791 { - border-radius: 8px; -} - -.titleWrapperClickable__6d791 { - cursor: pointer; - height: auto; -} - -.extrasEmptySpace__6d791 { - flex: 1 1 0%; - height: 24px; -} - -.button__6d791 { - align-items: center; - border-radius: 50%; - color: var(--text-strong); - cursor: pointer; - display: flex; - height: 24px; - justify-content: center; - width: 24px; -} - -.button__6d791.active__6d791 { - border-color: var(--background-brand); -} - -.row__771da { - box-sizing: border-box; - cursor: pointer; - height: 50px; - margin-inline-start: 8px; position: relative; -} - -.leftIndicatorContainer__771da { - align-items: center; - display: flex; - height: 8px; - inset-inline-start: -8px; - position: absolute; - top: 18px; - width: 4px; -} - -.channelIcon__771da { - flex-shrink: 0; - height: 12px; - width: 12px; -} - -.offlineRow__771da { - opacity: 0.6; -} - -.offlineRow__771da:active, .offlineRow__771da:focus-within, .offlineRow__771da:hover { - opacity: 1; -} - -.rowInner__771da { - box-sizing: border-box; - height: 42px; - padding-block: var(--space-xxs) var(--space-xxs); padding-inline: 0px; - width: 100%; - z-index: 0; -} - -.rowRecentlyAdded__771da { - animation: 0.95s ease-out 0s 1 normal none running friendsWidgetRowRecentlyAdded__771da; -} - -@keyframes friendsWidgetRowRecentlyAdded__771da { - 0% { - background: color-mix(in srgb,var(--brand-500) 28%,transparent); - } - - 100% { - background: transparent; - } -} - -.avatar__771da { - margin-inline-end: 12px; } - -.username__771da { - color: var(--text-default); -} - -.username__771da, .usernameContainer__771da { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.usernameContainer__771da { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px; -} - -.activityIconContainer__771da { - align-items: center; - display: flex; - flex-shrink: 0; - height: 32px; - justify-content: center; - width: 32px; -} - -.rowActions__771da { - align-items: center; - display: inline-flex; - gap: var(--space-xxs); -} - -.badgesContainer__771da { - display: flex; - flex-wrap: wrap; - gap: 4px; - margin-top: 2px; -} - -.friendVoiceStatus__771da { - gap: 8px; -} - -.friendVoiceStatus__771da, .voiceStatusContainer__771da { - align-items: center; - display: flex; - flex-direction: row; -} - -.voiceStatusContainer__771da { - gap: 4px; -} - -.voiceStatusContainer__771da .voiceText__771da { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.friendVoiceStatusGuildContainer__771da { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px; - justify-content: flex-start; -} - -.guildName__771da { - max-width: 100px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.voiceStatusIcon__771da { - height: 12px; - width: 12px; -} - -.iconOnlyButton__068c6 { - align-items: center; - border: 1px solid transparent; - border-radius: var(--radius-sm); - cursor: pointer; - display: flex; - height: 30px; - justify-content: center; - width: 30px; -} - -.iconOnlyButton__068c6:hover { - background-color: var(--control-icon-only-background-hover); - border: 1px solid var(--control-icon-only-border-hover); -} - -.iconOnlyButton__068c6:active { - background-color: var(--background-mod-muted); -} - -.iconOnlyButton__068c6 svg { - pointer-events: none; -} - -.container__606e9 { - display: flex; - flex: 1 1 0%; - flex-direction: column; -} - -.container__606e9, .list__606e9 { - min-height: 0px; - min-width: 0px; -} - -.list__606e9 { - border-top: 1px solid rgba(152, 152, 160, 0.035); - flex: 1 1 auto; - overflow: auto; -} - -.searchContainer__606e9 { - margin-top: -1px; - margin-inline-end: -1px; padding: 0px; -} - -.searchContainer__606e9 > div > div > div > div { - background: rgba(152, 152, 160, 0.04); - border-radius: 0px; - padding: 8px 12px; - border: none !important; - outline: none !important; -} - -.searchContainer__606e9 > div > div > div > div:has(input:focus, textarea:focus) { - background: rgba(152, 152, 160, 0.08); -} - -.section__606e9 { - height: auto; - padding-block: var(--space-lg) var(--space-xxs); padding-inline: var(--space-md) var(--space-xxs); -} - -.chevronIcon__606e9 { - height: 12px; - margin-inline-start: var(--space-xxs); width: 12px; -} - -.sectionCollapsible__606e9 { - align-items: center; - cursor: pointer; - display: flex; -} - -.sectionCollapsible__606e9:hover { - color: var(--text-strong); -} - -.nonGroupSectionHeaderRow__606e9 { - align-items: center; - display: flex; - gap: var(--space-xxs); - justify-content: space-between; -} - -.nonGroupSectionHeaderHideButton__606e9 { - align-items: center; - color: var(--text-muted); - display: flex; - justify-content: center; - margin-inline-end: 8px; padding: var(--space-xxs); -} - -.nonGroupSectionHeaderHideButton__606e9:hover { - color: var(--text-strong); -} - -.emptyStateContainer__606e9 { - align-items: center; - display: flex; - height: 300px; - justify-content: center; - padding: 16px; -} - -.container__672fc { - background-color: var(--background-surface-higher); - border: 1px solid rgba(152, 152, 160, 0.035); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-border),var(--shadow-high); - width: 350px; -} - -.header__672fc { - align-items: center; - display: flex; - justify-content: space-between; - padding: 16px 16px 0px; -} - -.activityText__672fc { - color: var(--text-subtle); -} - -.controlButtons__672fc { - display: flex; - gap: 8px; - margin-inline-start: 8px; } - -.searchContainer__672fc { - margin-top: -1px; - margin-inline-end: -1px; padding: 0px; -} - -.searchContainer__672fc > div > div > div > div { - background: rgba(152, 152, 160, 0.04); - border-radius: 0px; - padding: 8px 12px; - border: none !important; - outline: none !important; -} - -.searchContainer__672fc > div > div > div > div:has(input:focus, textarea:focus) { - background: rgba(152, 152, 160, 0.08); -} - -.searchInput__672fc { - padding: 0px; -} - -.list__672fc { - height: 70vh; -} - -.section__672fc { - height: auto; - padding-block: var(--space-lg) var(--space-xxs); padding-inline: var(--space-md) var(--space-xxs); -} - -.reorderableGroupSection__672fc { - cursor: grab; - position: relative; -} - -.reorderableGroupSection__672fc:active { - cursor: grabbing; -} - -.draggingGroupSection__672fc { - opacity: 0.5; -} - -.dropTargetGroupSection__672fc { - background-color: var(--interactive-background-hover); -} - -.dropBeforeGroupSection__672fc::before { - inset-block-start: 0px; - inset-inline: 0px; -} - -.dropAfterGroupSection__672fc::after, .dropBeforeGroupSection__672fc::before { - background-color: var(--text-brand); - content: ""; - height: 2px; - position: absolute; -} - -.dropAfterGroupSection__672fc::after { - inset-block-end: 0px; - inset-inline: 0px; -} - -.chevronIcon__672fc { - height: 12px; - margin-inline-start: var(--space-xxs); width: 12px; -} - -.sectionCollapsible__672fc { - align-items: center; - cursor: pointer; - display: flex; -} - -.sectionCollapsible__672fc:hover { - color: var(--text-strong); -} - -.emptyStateContainer__672fc { - align-items: center; - display: flex; - height: 300px; - justify-content: center; - padding: 16px; -} - -.emoji_ce5b39 { - flex-shrink: 0; -} - -.container__49ef0 { - border-bottom: 1px solid rgba(152, 152, 160, 0.035); - display: flex; - flex-direction: column; - gap: 0px; - padding: 0px; -} - -.profileBanner__49ef0 { - background-position: 50% center; - background-size: cover; - height: 160px; - position: relative; - z-index: 1; -} - -.userBannerContainer__49ef0 { - align-items: center; - background-color: rgb(19, 19, 24); - border-bottom: 1px solid rgba(152, 152, 160, 0.035); - bottom: 0px; - display: flex; - flex: 1 1 0%; - justify-content: space-between; - min-width: 0px; - padding-block: 0px 16px; padding-inline: 12px; position: absolute; - width: calc(100% - 24px); - z-index: 2; -} - -.userBannerContainer__49ef0::after { - background: linear-gradient(rgba(19, 19, 24, 0), rgb(19, 19, 24) 98.03%); - content: ""; - inset-inline: 0px; - bottom: calc(100% - 1px); - height: 80px; - pointer-events: none; - position: absolute; - z-index: -1; -} - -.userBanner__49ef0 { - align-items: center; - display: flex; - flex: 1 1 0%; - flex-direction: row; - gap: 12px; - justify-content: flex-start; - margin-top: -20px; - width: 100%; -} - -.bannerContainer__49ef0 { - display: flex; - flex-direction: column; - height: 160px; - overflow: hidden; - position: relative; -} - -.userBannerLeft__49ef0 { - align-items: center; - display: flex; - gap: var(--space-12); - min-width: 0px; -} - -.avatar__49ef0 { - flex-shrink: 0; -} - -.userTextContainer__49ef0 { - display: flex; - flex-direction: column; - gap: var(--space-4); - min-width: 0px; -} - -.activityText__49ef0, .secondaryLine__49ef0, .username__49ef0 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.secondaryLine__49ef0 { - align-items: center; - display: flex; - flex: 1 1 0%; - gap: var(--space-4); - max-width: 216px; - min-width: 0px; -} - -.secondaryIcon__49ef0 { - color: var(--green-360); - flex-shrink: 0; -} - -.secondaryText__49ef0 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.customStatusContainer__49ef0 { - align-items: center; - display: flex; - min-width: 0px; - overflow: hidden; -} - -.customStatusEmoji__49ef0 { - flex-shrink: 0; - height: 14px; - margin-inline-end: var(--space-4); width: 14px; -} - -.duration__49ef0 { - color: var(--green-360); - flex-shrink: 0; - font-size: 12px; - font-weight: 400; -} - -.tabs__49ef0 { - border-top: none; - display: flex; - gap: 4px; - padding: 8px; -} - -.tab__49ef0 { - background-color: transparent; - border: 1px solid transparent; - border-radius: 6px; - color: var(--text-muted); - cursor: pointer; - flex: 1 1 0%; - min-width: 0px; - padding: 6px 12px; - position: relative; - text-align: center; -} - -.tab__49ef0:hover { - background-color: var(--background-mod-strong); - color: var(--interactive-text-hover); -} - -.tabSelected__49ef0 { - background-color: var(--background-mod-subtle); - color: var(--interactive-text-active); -} - -.unreadFavoritesDot__49ef0 { - background: var(--text-muted); - border: 2px solid rgb(18, 18, 20); - border-radius: var(--radius-round); - height: 6px; - inset-inline-end: -4px; - pointer-events: none; - position: absolute; - top: -4px; - width: 6px; -} - -.barsRoot__70b7b { - height: 100%; - pointer-events: none; - position: relative; - width: 100%; -} - -.animatedIconContainer__70b7b { - display: inline-flex; -} - -.barsGroup__70b7b { - align-items: center; - display: flex; - isolation: isolate; - overflow: hidden; - pointer-events: none; - position: absolute; - transform: translateZ(0px); -} - -.bar__70b7b, .barsGroup__70b7b { - will-change: transform; -} - -.bar__70b7b { - border-radius: 1px; - display: block; - flex: 0 0 auto; - transform-origin: center center; -} - -.icon__2894c { - display: block; - flex: 0 0 auto; - height: 20px; - width: 20px; -} - -.iconWithGuildIcon__2894c { - background: var(--background-mod-strong); - border-radius: var(--radius-round); - color: var(--text-subtle); - height: 12px; - margin-inline-start: 12px; margin-top: -15px; - overflow: visible; - padding: 4px; - position: relative; - width: 12px; -} - -.channelGuildIcon__2894c { - border-radius: var(--radius-sm); -} - -.acronym__2894c { - align-items: center; - background-color: var(--background-mod-strong); - color: var(--text-default); - display: flex; - font-weight: var(--font-weight-medium); - justify-content: center; - line-height: 1.2em; - white-space: nowrap; -} - -.clickable_c69b6d { - cursor: pointer; -} - -.containerDefault_c69b6d, .containerDragAfter_c69b6d, .containerDragBefore_c69b6d, .containerUserOver_c69b6d { - position: relative; - transition: opacity 0.2s ease-in-out; -} - -.containerDragAfter_c69b6d::after, .containerDragBefore_c69b6d::before { - background-color: var(--green-360); - border-radius: 2px; - box-shadow: 0 0 3px var(--opacity-black-40); - content: ""; - height: 4px; - inset-inline: 8px 0px; - position: absolute; - z-index: 1; -} - -.containerDragBefore_c69b6d::before { - top: -2px; -} - -.containerDragAfter_c69b6d::after { - bottom: -2px; -} - -.containerUserOver_c69b6d::after { - background-color: hsl(var(--green-360-hsl)/.1); - border: 2px solid hsl(var(--green-360-hsl)/.5); - border-radius: 4px; - box-sizing: border-box; - content: ""; - position: absolute; - top: 0px; - inset-inline: 8px 0px; - bottom: 0px; - z-index: 1; -} - -.iconBase_c69b6d { - cursor: pointer; - line-height: 0; - position: relative; -} - -.iconItem_c69b6d { - display: none; - margin-inline-start: 4px; } - -.iconItem_c69b6d.alwaysShown_c69b6d { - display: block; -} - -.containerDefault_c69b6d.selected_c69b6d .iconNoChannelInfo_c69b6d, .iconVisibility_c69b6d:focus .iconItem_c69b6d, .iconVisibility_c69b6d:focus-within .iconItem_c69b6d { - display: block; -} - -.containerDefault_c69b6d.selected_c69b6d .iconWithChannelInfo_c69b6d { - display: none; -} - -.iconItem_c69b6d:focus, .iconItem_c69b6d:focus-within { - display: block; - position: relative; -} - -.disableClick_c69b6d { - pointer-events: none; -} - -.disabled_c69b6d > div { - opacity: 0.3; -} - -.actionIcon_c69b6d { - display: block; - height: 16px; - width: 16px; -} - -.actionIcon_c69b6d:active { - transform: translateY(0.5px); -} - -.actionIcon_c69b6d, .actionIcon_c69b6d:hover { - color: var(--icon-muted); -} - -.channelInfo_c69b6d { - display: block; - margin-inline-start: 12px; } - -.iconVisibility_c69b6d:focus .channelInfo_c69b6d, .iconVisibility_c69b6d:focus-within .channelInfo_c69b6d { - display: none; -} - -.actionIcon_c69b6d { - color: var(--interactive-text-default); -} - -.iconItem_c69b6d:hover .actionIcon_c69b6d { - color: var(--interactive-text-hover); -} - -.actionIcon_c69b6d:active { - color: var(--interactive-text-active); -} - -.iconLive_c69b6d { - color: var(--text-feedback-positive) !important; -} - -.summary_c69b6d { - margin-inline-start: 40px; } - -.subtitleHasThreads_c69b6d { - color: var(--text-brand); -} - -@media (hover: hover) { - .iconVisibility_c69b6d:hover .iconItem_c69b6d { - display: block; - } - - .iconVisibility_c69b6d:hover .channelInfo_c69b6d { - display: none; - } -} - -.selectedChannel_c69b6d .actionIcon_c69b6d, .selectedChannel_c69b6d .actionIcon_c69b6d:hover { - color: var(--icon-strong); -} - -.voiceChannelHighlightContainer_c69b6d { - position: relative; -} - -.voiceChannelHighlightBorder_c69b6d { - background-color: var(--icon-feedback-positive); - bottom: 4px; - inset-inline-start: 0px; - position: absolute; - top: 2px; - width: 2px; -} - -.voiceChannelHighlightGlow_c69b6d { - background: radial-gradient(40px 50% at left center, rgba(0, 158, 66, 0.125) 0px, rgba(0, 158, 66, 0) 100%); - bottom: -10px; - inset-inline-start: 0px; - position: absolute; - top: -12px; - width: 40px; -} - -.enable-forced-colors .actionIcon_c69b6d { - color: buttontext; -} - -.enable-forced-colors .iconBase_c69b6d { - background-color: buttonface; - border: 1px solid buttonface; - border-radius: 4px; - color: buttontext; -} - -.enable-forced-colors .iconBase_c69b6d:hover { - border-color: buttontext; -} - -.enable-forced-colors .iconBase_c69b6d:hover .actionIcon_c69b6d { - color: buttontext; -} - -.enable-forced-colors .containerDragAfter_c69b6d::after, .enable-forced-colors .containerDragBefore_c69b6d::before, .enable-forced-colors .containerUserOver_c69b6d::after { - background-color: highlight; - outline: canvas solid 1px; -} - -.wrapper__2ea32 { - margin-inline-start: var(--space-xs); overflow: visible; - padding: 1px 0px; - position: relative; -} - -.icon__2ea32 { - color: var(--channel-icon); -} - -.typeThread__2ea32.wrapper__2ea32 { - margin-inline-start: calc(28px + var(--space-xs) + var(--space-xs) - var(--space-xs)); } - -.typeThread__2ea32 .name__2ea32 { - font-size: 14px; -} - -.typeThread__2ea32 .link__2ea32 { - padding-block: 4px; padding-inline: var(--space-xs) var(--space-xs); } - -.typeThread__2ea32 .unread__2ea32 { - inset-inline-start: calc((16px + var(--space-md))*-1 + 1px); -} - -.typeThread__2ea32.withGuildIcon__2ea32 { - margin-inline-start: 42px; } - -.typeThread__2ea32.withGuildIcon__2ea32 .unread__2ea32 { - inset-inline-start: -42px; -} - -.modeUnreadImportant__2ea32 .name__2ea32, .modeUnreadImportant__2ea32:hover .name__2ea32 { - color: var(--text-strong); -} - -.wrapper__2ea32.modeSelected__2ea32 { - cursor: default; -} - -.wrapper__2ea32.modeLocked__2ea32 { - cursor: not-allowed; -} - -.icon__2ea32 { - display: block; - flex: 0 0 auto; -} - -.icon__2ea32, .iconContainer__2ea32 { - height: 20px; - width: 20px; -} - -.iconContainer__2ea32 { - margin-inline-end: 0px; position: relative; -} - -.iconContainerWithGuildIcon__2ea32 { - margin-bottom: -2px; - margin-top: -2px; -} - -.favoritesSuggestion__2ea32 { - border: 2px dashed var(--border-subtle); - border-radius: var(--radius-md); - box-sizing: border-box; -} - -.hdStreamed__2ea32 { - background: linear-gradient(90deg, rgba(255, 89, 120, 0.15), rgba(111, 74, 255, 0.15)); - border-radius: var(--radius-xs); -} - -.link__2ea32 { - border-radius: var(--radius-sm); - box-sizing: border-box; - cursor: pointer; - display: flex; - flex-direction: column; - padding-right: 8px; - padding-left: 8px; - padding-top: var(--space-xxs); - padding-inline: var(--space-xs); padding-bottom: var(--space-xxs); - position: relative; -} - -.link__2ea32::before { - content: ""; - display: block; - position: absolute; - top: -1px; - inset-inline: 0px; - bottom: -1px; -} - -.basicChannelRowLink__2ea32 { - min-height: 24px; -} - -.linkTop__2ea32 { - gap: var(--space-xs); - justify-content: center; -} - -.linkBottom__2ea32, .linkTop__2ea32 { - align-items: center; - display: flex; - position: relative; -} - -.linkBottom__2ea32 { - margin-inline-start: calc(20px + var(--space-xs)); } - -.linkBottom__2ea32.withGuildIcon__2ea32 { - margin-inline-start: 38px; -} - -.name__2ea32 { - flex: 1 1 auto; - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: var(--channels-name-line-height); -} - -.name__2ea32, .subtitle__2ea32 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.unread__2ea32 { - background-color: var(--channels-default); - border-radius: 0 var(--radius-xs) var(--radius-xs) 0; - height: 8px; - inset-inline-start: calc(var(--space-xs)*-1 + 1px); - margin-top: -4px; - position: absolute; - top: 50%; - width: 4px; -} - -.unread__2ea32.unreadWithHangStatus__2ea32 { - top: 25%; -} - -.children__2ea32 { - align-items: center; - display: flex; - flex: 0 0 auto; - justify-content: center; -} - -.modeMuted__2ea32 .icon__2ea32 { - color: var(--icon-muted); - opacity: 0.5; -} - -.modeMuted__2ea32 .name__2ea32 { - color: var(--text-muted); - opacity: 0.5; -} - -.modeMuted__2ea32 .acronym__2ea32, .modeMuted__2ea32 .iconWithGuildIcon__2ea32 { - background: var(--background-mod-muted); - color: var(--text-muted); -} - -.theme-dark.custom-theme-background.custom-client-theme .modeMuted__2ea32 .icon__2ea32, .theme-dark.custom-theme-background.custom-client-theme .modeMuted__2ea32 .name__2ea32 { - color: color-mix(in oklab,var(--text-muted) 40%,transparent); -} - -.theme-dark.custom-theme-background.custom-client-theme .modeMuted__2ea32:hover .icon__2ea32, .theme-dark.custom-theme-background.custom-client-theme .modeMuted__2ea32:hover .name__2ea32 { - color: var(--interactive-text-hover); -} - -.modeSelected__2ea32 .icon__2ea32, .modeSelected__2ea32:hover .icon__2ea32 { - color: var(--channel-icon); -} - -.modeUnreadImportant__2ea32 .icon__2ea32, .modeUnreadImportant__2ea32:hover .icon__2ea32 { - color: var(--icon-subtle); -} - -.wrapper__2ea32:hover .link__2ea32 { - background: var(--interactive-background-hover); -} - -.wrapper__2ea32:active .link__2ea32 { - background: var(--background-modifier-active,var(--interactive-background-active)); -} - -.modeUnreadImportant__2ea32 .iconWithGuildIcon__2ea32, .modeUnreadImportant__2ea32:hover .iconWithGuildIcon__2ea32 { - color: var(--text-subtle); -} - -.modeSelected__2ea32 .iconWithGuildIcon__2ea32, .modeSelected__2ea32:hover .iconWithGuildIcon__2ea32 { - background: var(--interactive-text-default); - color: var(--background-base-lower); -} - -.modeSelected__2ea32 .acronym__2ea32, .modeSelected__2ea32:hover .acronym__2ea32 { - background: var(--brand-500); - color: var(--white); -} - -.modeSelected__2ea32 .link__2ea32, .modeSelected__2ea32:hover .link__2ea32 { - background: var(--interactive-background-selected); -} - -.name__2ea32 { - color: var(--channels-default); -} - -.modeMuted__2ea32:hover .name__2ea32, .wrapper__2ea32:hover .name__2ea32 { - color: var(--interactive-text-hover); -} - -.modeConnected__2ea32 .name__2ea32, .modeConnected__2ea32:hover .name__2ea32, .modeSelected__2ea32 .name__2ea32, .modeSelected__2ea32:hover .name__2ea32, .modeUnreadImportant__2ea32 .name__2ea32, .modeUnreadImportant__2ea32:hover .name__2ea32, .notInteractive__2ea32.modeConnected__2ea32 .name__2ea32, .notInteractive__2ea32.modeSelected__2ea32 .name__2ea32 { - color: var(--interactive-text-active); - font-weight: var(--font-weight-semibold-1x-light-theme,500); -} - -.high-contrast-mode .modeConnected__2ea32 .name__2ea32, .high-contrast-mode .modeConnected__2ea32:hover .name__2ea32, .high-contrast-mode .modeSelected__2ea32 .name__2ea32, .high-contrast-mode .modeSelected__2ea32:hover .name__2ea32, .high-contrast-mode .modeUnreadImportant__2ea32 .name__2ea32, .high-contrast-mode .modeUnreadImportant__2ea32:hover .name__2ea32 { - font-weight: var(--font-weight-semibold); -} - -.unreadImportant__2ea32 { - background-color: var(--interactive-text-active); -} - -.numberBadge__2ea32 { - background-color: var(--background-mod-strong); -} - -.newBadge__2ea32 { - color: var(--brand-560); -} - -@media (max-width: 485px) { - .modeSelected__2ea32 .link__2ea32 { - background-color: unset; - } -} - -.modeConnected__2ea32 .name__2ea32, .modeConnected__2ea32:hover .name__2ea32, .modeSelected__2ea32 .name__2ea32, .modeSelected__2ea32:hover .name__2ea32 { - color: var(--text-strong); -} - -.modeConnected__2ea32 .icon__2ea32, .modeConnected__2ea32:hover .icon__2ea32, .modeSelected__2ea32 .icon__2ea32, .modeSelected__2ea32:hover .icon__2ea32 { - color: var(--icon-strong); -} - -.activeEvent__2ea32 { - color: var(--text-feedback-positive) !important; -} - -[data-favorites="true"] .iconContainer__2ea32:has(.iconWithGuildIcon__2ea32) { - height: auto; - width: auto; -} - -.children__2ea32:empty { - display: none; -} - -:where(.refresh-fast-follow-avatars) .wrapper__2ea32 { - margin-inline-start: min(var(--space-xs),var(--space-8)); } - -.enable-forced-colors .unread__2ea32 { - background-color: highlight; - height: 10px; - margin-top: -5px; - outline: canvas solid 1px; - width: 6px; -} - -.enable-forced-colors .icon__2ea32, .enable-forced-colors .name__2ea32 { - color: inherit; -} - -.enable-forced-colors .link__2ea32 { - background-color: buttonface; - border: 1px solid buttonface; -} - -.enable-forced-colors .icon__2ea32, .enable-forced-colors .name__2ea32, .enable-forced-colors .topContent__2ea32 { - color: buttontext; - forced-color-adjust: none; -} - -.enable-forced-colors .wrapper__2ea32:hover .link__2ea32 { - background-color: buttonface; - border-color: buttontext; -} - -.enable-forced-colors .wrapper__2ea32:hover .icon__2ea32, .enable-forced-colors .wrapper__2ea32:hover .name__2ea32, .enable-forced-colors .wrapper__2ea32:hover .topContent__2ea32 { - color: buttontext; -} - -.enable-forced-colors .modeSelected__2ea32 .link__2ea32, .enable-forced-colors .modeSelected__2ea32:hover .link__2ea32 { - background-color: highlight; - border-color: highlighttext; -} - -.enable-forced-colors .modeSelected__2ea32 .icon__2ea32, .enable-forced-colors .modeSelected__2ea32 .name__2ea32, .enable-forced-colors .modeSelected__2ea32 .topContent__2ea32, .enable-forced-colors .modeSelected__2ea32:hover .icon__2ea32, .enable-forced-colors .modeSelected__2ea32:hover .name__2ea32, .enable-forced-colors .modeSelected__2ea32:hover .topContent__2ea32 { - color: highlighttext; -} - -.enable-forced-colors .modeMuted__2ea32 { - opacity: 0.4; -} - -.enable-forced-colors .modeMuted__2ea32:focus, .enable-forced-colors .modeMuted__2ea32:hover { - opacity: 1; -} - -.channelNameContainer__711d3, .label__711d3 { - align-items: center; - display: flex; - gap: 4px; -} - -.channelName__711d3 { - max-width: 140px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.recentChannelsMenu__711d3 { - background: rgb(18, 18, 20); - border: 1px solid rgba(152, 152, 160, 0.035); - border-radius: 16px; - box-shadow: var(--shadow-high); - max-height: 500px; - overflow: hidden; - padding: 8px; - width: 360px; -} - -.recentChannelsMenuHeader__711d3 { - align-items: center; - display: flex; - flex: 1 1 0%; - flex-direction: row; - margin-bottom: 2px; - padding: 0px 12px; -} - -.channelMenuSubContainer__711d3 { - align-items: center; - display: flex; - flex-flow: row; - gap: 8px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.icon__711d3 { - width: auto; -} - -.guildIconWrapper__711d3 { - align-items: center; - background: var(--background-surface-highest); - display: flex; - height: 32px; - justify-content: center; - width: 32px; -} - -.overflowCount__711d3 { - max-height: 22px; - max-width: 24px; -} - -.channelItemContainer__711d3 { - align-items: center; - background-color: transparent; - border-radius: 8px; - cursor: pointer; - display: flex; - flex-direction: row; - gap: 12px; - height: 48px; - justify-content: space-between; - margin-bottom: 2px; - min-width: 0px; - overflow: hidden; - padding: 6px; - padding-inline-start: 0px; transition: border-color 0.1s ease-in-out; - width: calc(100% - 16px); -} - -.channelItemContainer__711d3.channelItemHighlighted__711d3, .channelItemContainer__711d3:hover { - background-color: var(--background-mod-strong); -} - -.searchBarContainer__711d3 { - padding-bottom: 16px; -} - -.emptyChannelItem__711d3 { - border-radius: 8px; - padding: 12px; - text-align: center; -} - -.channelItemIcon__711d3, .emptyChannelItem__711d3 { - align-items: center; - display: flex; - justify-content: center; -} - -.channelItemNameContainer__711d3 { - align-items: flex-start; - display: flex; - flex: 1 1 0%; - flex-direction: column; - gap: 0px; - min-width: 0px; -} - -.channelItemName__711d3 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px; - max-width: 100%; -} - -.channelIcon__711d3 { - min-width: 16px; -} - -.channelItemNameText__711d3 { - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.channelItemAvatars__711d3 { - align-items: center; - display: flex; - gap: 4px; - padding-inline-end: 4px; } - -.channelList__711d3 { - max-height: 360px; -} - -.channelListWrapper__711d3 { - display: flex; - flex-direction: column; - gap: 12px; -} - -.avatarWrapper__69074 { - height: 100%; - width: 100%; -} - -.root_df7f81 { - align-items: flex-start; - display: flex; - justify-content: flex-start; - overflow: visible; - position: relative; -} - -.mainGuildImage_df7f81, .mainIcon_df7f81 { - border-radius: var(--radius-sm); -} - -.mainGuildImage_df7f81 { - display: block; -} - -.acronym_df7f81 { - background-color: rgba(19, 19, 24, 0.95); - color: var(--white); - font-weight: var(--font-weight-medium); - line-height: 0.8em; - white-space: nowrap; -} - -.acronym_df7f81, .guildIconContainer_df7f81 { - align-items: center; - display: flex; - justify-content: center; -} - -.guildIconContainer_df7f81 { - bottom: 0px; - inset-inline-end: 0px; - overflow: visible; - position: absolute; -} - -.guildIcon40_df7f81 { - height: 20px; - width: 20px; -} - -.guildIcon32_df7f81 { - height: 16px; - width: 16px; -} - -.guildIcon24_df7f81 { - height: 12px; - width: 12px; -} - -.typingMaskWrapper_df7f81 { - overflow: visible; - position: relative; -} - -.typingMaskSvg_df7f81 { - inset: 0px; - overflow: visible; - position: absolute; -} - -.typingIndicator_df7f81 { - pointer-events: none; - position: absolute; -} - -.channelTypingStatusFill_df7f81 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - line-height: 0; - width: 100%; -} - -.messagePreviewLine__7c4a2 { - align-items: center; - display: flex; - gap: var(--space-4); - overflow: hidden; -} - -.messageContent__7c4a2 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.messageContent__7c4a2 > strong { - font-weight: var(--font-weight-medium); -} - -.messageContentTrailingIcon__7c4a2 { - flex-shrink: 0; -} - -.colorTextFeedbackPositive__7c4a2 { - color: var(--text-feedback-positive); -} - -.container__394db { - align-items: center; - display: block; - flex: 1 1 auto; - overflow: hidden; - position: relative; - vertical-align: middle; - white-space: nowrap; -} - -.container__394db .chipletContainer__394db { - display: inline-block; - overflow: hidden; -} - -.container__394db .chipletParent__394db { - display: inline; - overflow: hidden; - padding-inline-start: 4px; position: relative; -} - -.container__394db .usernameContainer__394db { - display: inline-block; - flex: 1 1 0%; - margin-top: 2px; - max-width: 100%; - overflow: hidden; - white-space: nowrap; -} - -.container__394db.isOverlayContainer__394db { - align-content: center; - background: var(--background-base-low); - border-radius: 4px; - display: flex; - flex: 0 0 auto; - flex-direction: row; - height: 20px; -} - -.container__394db.isOverlayContainer__394db .usernameContainer__394db { - margin-top: 0px; -} - -.container__394db.isOverlayContainer__394db .chipletContainer__394db, .container__394db.isOverlayContainer__394db .chipletParent__394db { - align-content: center; - align-items: center; - display: flex; -} - -.container__394db.isOverlayContainer__394db .chipletContainer__394db { - margin: 0px; - padding: 1.5px; -} - -.container__394db.isOverlayContainer__394db .chipletContainer__394db.noPadding__394db { - padding: 0px; -} - -.isOverlayTag__394db { - margin: 0px; -} - -.avatarWrapper_d8a370 { - align-items: center; - display: inline-flex; - flex: 0 0 auto; - justify-content: center; - position: relative; -} - -.ring_d8a370 { - border-radius: 50%; - inset: -3px; - mask: radial-gradient(farthest-side, transparent calc(100% - 2px), rgb(0, 0, 0) calc(100% - 1px)); - pointer-events: none; - position: absolute; - transform: rotate(-90deg); -} - -.expandedUsersContainer_e72588 { - display: flex; - flex-direction: column; - gap: 2px; - padding-inline: 56px 12px; padding-bottom: 8px; -} - -.expandedUsersRow_e72588 { - border-radius: var(--radius-sm); - height: 24px; - padding: 4px; -} - -.expandedUsersRow_e72588:hover { - background-color: var(--interactive-background-hover); -} - -.expandedUsersRow_e72588:active { - background-color: var(--interactive-background-active); -} - -.facepileContainer_e72588 { - justify-content: center; - min-width: 24px; - position: relative; -} - -.expandedUserRow_e72588, .facepileContainer_e72588 { - align-items: center; - display: flex; - min-height: 24px; -} - -.expandedUserRow_e72588 { - gap: 6px; -} - -.expandedUserName_e72588 { - flex-shrink: 0; -} - -.expandedUserName_e72588, .expandedUserNameContainer_e72588 { - min-width: 0px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.expandedUserNameContainer_e72588 { - flex: 1 1 0%; -} - -.actionsRow_e72588, .expandedUserNameContainer_e72588 { - align-items: center; - display: flex; - gap: 4px; -} - -.actionsRow_e72588 { - flex-direction: row; -} - -.voiceIcons_e72588 { - align-items: center; - display: inline-flex; - flex-shrink: 0; - gap: 4px; -} - -.voiceIcon_e72588 { - height: 14px; - width: 14px; -} - -.container__50c27 { - background-color: rgb(18, 18, 20); - border: 1px solid rgba(152, 152, 160, 0.035); - border-radius: 12px; - box-shadow: var(--shadow-border) var(--shadow-high); - box-sizing: border-box; - display: flex; - flex: 1 1 0%; - flex-direction: column; - height: 100%; - overflow: hidden; - position: relative; - width: 100%; - z-index: 2; -} - -.content__50c27 { - display: flex; -} - -.content__50c27, .list__50c27 { - flex: 1 1 auto; - min-height: 0px; - min-width: 0px; -} - -.list__50c27 { - overflow: auto; -} - -.emptyWidgetContainer_cc1cc5 { - align-items: center; - display: flex; - flex-direction: column; - height: calc(100% - 32px); - justify-content: center; - padding: 16px; - width: calc(100% - 32px); -} - -.emptyWidgetContainer_cc1cc5.absolute_cc1cc5 { - position: absolute; - top: 0px; - inset-inline: 0px; - bottom: 0px; -} - -.emptyWidgetIcon_cc1cc5 { - opacity: 0.24; -} - -.panel_ef5082 { - gap: 16px; - padding: 8px; -} - -.panel_ef5082, .panelGroup_ef5082 { - display: flex; - flex-direction: column; -} - -.panelGroup_ef5082 { - background: var(--background-base-lower); - border-radius: 8px; - gap: 4px; - padding: 12px; -} - -.panelGroup_ef5082 span { - transition: color 0.4s ease-in-out; -} - -.bottomPanelButton_ef5082 { - padding-top: 12px; -} - -.topPanelToggle_ef5082 { - padding-bottom: 12px; -} - -.secondaryInfoText_ef5082 { - margin-inline-start: 8px; } - -.panel_fe7ab2 { - flex: 1 1 0%; - min-height: 0px; -} - -.header_fe7ab2 { - margin: 16px 0px 8px; -} - -.headerBar_fe7ab2 { - box-shadow: var(--elevation-low); -} - -.headerTitle_fe7ab2 { - flex: 1 1 0%; -} - -.dispatcherHeader_fe7ab2 { - max-width: calc(100% - 90px); -} - -.headerTitleText_fe7ab2 { - display: block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.inspectorWrapper_fe7ab2 { - font-family: var(--font-code); - font-size: 14px; -} - -.tabBarContainer_fe7ab2 { - align-items: center; - display: flex; - width: 100%; -} - -.tabBar_fe7ab2 { - background: var(--background-base-lower); - border-bottom: 1px solid var(--background-base-lowest); - flex-shrink: 0; - height: 36px; - overflow: hidden; - white-space: nowrap; - width: 95%; -} - -.tabContent_fe7ab2 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px; -} - -.tabMeasurer_fe7ab2 { - height: 0px; - inset-inline-start: 0px; - pointer-events: none; - position: absolute; - top: 0px; - visibility: hidden; - width: 0px; -} - -.tabItem_fe7ab2 { - border-bottom: 2px solid transparent; - color: var(--interactive-text-default); - cursor: pointer; - display: inline-block; - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 22px; - overflow: hidden; - padding: 6px 12px; - text-overflow: ellipsis; - white-space: nowrap; -} - -.tabItem_fe7ab2:hover { - border-bottom-color: var(--brand-500); -} - -.tabItem_fe7ab2.selected_fe7ab2, .tabItem_fe7ab2:active { - border-bottom-color: var(--control-brand-foreground); -} - -.menu_fe7ab2 { - display: flex; - height: 100%; - justify-content: flex-end; - width: 5%; -} - -.overflowChevron_fe7ab2 { - height: 100%; - position: relative; -} - -.table_fe7ab2, .tableContainer_fe7ab2 { - display: flex; - flex: 1 1 0%; - flex-direction: column; - min-height: 0px; -} - -.tableHeader_fe7ab2 { - background: var(--background-base-lower); - height: 24px; - text-transform: uppercase; -} - -.tableHeader_fe7ab2, .tableRow_fe7ab2 { - align-items: center; - border-bottom: 1px solid rgba(255, 255, 255, 0.06); - display: flex; - flex-direction: row; - padding: 8px; -} - -.tableRow_fe7ab2 { - color: var(--interactive-text-default); - cursor: pointer; - font-size: 16px; - line-height: 22px; - min-height: 24px; -} - -.tableRow_fe7ab2 > div { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.tableRow_fe7ab2:hover { - background: var(--interactive-background-hover); -} - -.tableRow_fe7ab2:last-child { - border-bottom: none; -} - -.selectedTableRow_fe7ab2 { - background: var(--interactive-background-selected); -} - -.properties_fe7ab2 { - align-items: baseline; - background: var(--background-base-lower); - box-shadow: var(--elevation-low); - display: grid; - gap: 16px 8px; - grid-template-columns: max-content auto; - padding: 16px; -} - -.propertyName_fe7ab2 { - color: var(--text-default); - flex: 0 0 auto; - font-family: var(--font-display); - font-size: 14px; - font-weight: var(--font-weight-semibold); - text-align: end; - text-transform: uppercase; -} - -.propertyValue_fe7ab2 { - align-items: center; - display: flex; -} - -.propertyValue_fe7ab2:hover > .copyPropertyButton_fe7ab2 { - opacity: 1; -} - -.propertyValue_fe7ab2 .copyPropertyButton_fe7ab2 { - color: var(--interactive-text-default); - height: 18px; - margin-inline-start: 4px; opacity: 0; - transition: opacity 0.1s ease-in-out; -} - -.propertyValue_fe7ab2 .copyPropertyButton_fe7ab2:hover { - color: var(--interactive-text-hover); - cursor: pointer; -} - -.toolbar_fe7ab2 { - align-items: center; - border-bottom: 1px solid var(--background-base-lowest); - display: flex; - flex-direction: row; - height: 34px; -} - -.toolbarGroup_fe7ab2 { - align-items: center; - display: inline-flex; - margin: 0px 8px; -} - -.toolbarGroupLabel_fe7ab2 { - font-size: 14px; - margin-inline-end: 8px; } - -.toolbarButton_fe7ab2 { - flex: 0 0 auto; -} - -.toolbarDivider_fe7ab2 { - border-inline-start: 1px solid var(--text-muted); height: 18px; - margin: 8px 0px; -} - -.premiumStreakOverride_fe7ab2 { - margin-top: 12px; -} - -.lineClamp__0b48b { - display: -webkit-box; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.hoverCard_fdda30 { - cursor: pointer; - transition: box-shadow, transform 0.2s; -} - -.hoverCard_fdda30:focus-within, .hoverCard_fdda30:hover { - box-shadow: var(--shadow-high); - transform: translateY(-4px); -} - -.notice__29487 { - align-items: center; - background: linear-gradient(90deg,var(--color-scoped-expressive-background-nitro-1-start,rgba(179,38,156,.7)) 0,var(--color-scoped-expressive-background-nitro-1-end,rgba(20,20,203,.7)) 100%),var(--background-base-lowest); - display: flex; - height: 54px; - position: relative; - z-index: 101; -} - -@supports not ((grid-template-columns: subgrid) and (white-space-collapse:collapse)) { - .notice__29487 { - border-start-start-radius: 0px; - } -} - -@supports (grid-template-columns: subgrid) and (white-space-collapse:collapse) { - .notice__29487 { - grid-area: notice; - } -} - -.noticeContent__29487 { - align-items: center; - display: flex; - flex: 1 1 0%; - gap: var(--space-16); - justify-content: center; -} - -.noticeText__29487 strong { - font-weight: var(--font-weight-semibold); -} - -.closeButton__29487 { - margin-inline: auto var(--space-12); } - -.premiumIcon_b68a35 { - height: 24px; - margin-inline-end: 8px; position: relative; - top: 6px; - width: 24px; -} - -.platformIcon_b68a35 { - display: inline-block; - height: 28px; - margin-top: -4px; - margin-inline-end: 10px; position: relative; - vertical-align: middle; - width: 28px; -} - -.platformIcon_b68a35 + .platformIcon_b68a35 { - margin-inline-start: -10px; } - -.giftIcon_b68a35 { - margin-inline-end: 4px; vertical-align: text-bottom; -} - -.icon_b68a35 { - display: inline-block; - height: 20px; - margin-inline-start: 10px; margin-top: -3px; - position: relative; - vertical-align: middle; - width: 20px; -} - -.icon_b68a35 + .icon_b68a35 { - margin-inline-start: 6px; } - -.iconWindows_b68a35 { - mask-image: url("/assets/20dd0e244b9a7065.svg"); -} - -.iconApple_b68a35, .iconWindows_b68a35 { - background-color: currentcolor; -} - -.iconApple_b68a35 { - mask-image: url("/assets/d11dc1928431aa27.svg"); -} - -.iconAndroid_b68a35 { - background-color: currentcolor; - mask-image: url("/assets/f625814fc53c325e.svg"); -} - -.iconUSFlag_b68a35 { - background-image: url("/assets/423c2b95bd0fdafb.png"); - background-position: center top; - background-repeat: no-repeat; - background-size: 85%; - margin-inline-end: 7px; margin-top: 0px; -} - -.icon_b68a35 + .btn_b68a35 { - margin-inline-start: 20px; } - -.textLink_b68a35 { - color: var(--white); - padding-inline-start: 10px; text-decoration: underline; - app-region: no-drag; -} - -.textLinkSmall_b68a35 { - font-size: 12px; -} - -.testModeSKUSelector_b68a35 { - height: 24px; - margin-inline-start: 16px; } - -.premiumLogo_b68a35 { - background-image: url("/assets/a3541a1173e706be.svg"); - background-repeat: no-repeat; - background-size: 100%; - display: inline-block; - height: 13px; - margin-inline-end: 20px; position: relative; - top: 2px; - width: 51px; -} - -.premiumText_b68a35 { - font-weight: var(--font-weight-medium); -} - -.premiumAction_b68a35 { - margin-inline-start: 20px; } - -.ellipsis_b68a35 { - margin-inline-start: 7px; } - -.quarantineLearnMoreLink_b68a35 { - margin-inline-start: 10px; text-decoration: underline; -} - -.errorCodeNoticeText_b68a35 { - margin-inline-start: var(--space-8); } - -.errorCodeNoticeClickable_b68a35 { - cursor: pointer; -} - -.text__7b750 { - display: inline-block; -} - -.text__7b750, .text__7b750 a { - color: currentcolor; -} - -.text__7b750 a { - text-decoration: underline; -} - -.premiumIcon__7b750 { - height: 24px; - margin-inline-end: 8px; position: relative; - top: 6px; - width: 24px; -} - -.notice__36c3e { - align-items: center; - display: flex; - flex-direction: row; - justify-content: center; -} - -.guildIcon__36c3e { - margin-inline-end: 8px; } - -.guildName__36c3e { - color: inherit; -} - -.guildName__36c3e:hover { - text-decoration: underline; -} - -.actionButton__36c3e { - margin-inline-start: 12px; top: 0px; -} - -.actionButtonInner__36c3e { - align-items: center; - display: flex; - gap: 4px; -} - -.notice__30f28 { - align-items: center; - background-color: var(--brand-600); - box-shadow: none; - color: var(--white); - display: flex; - height: 40px; - justify-content: center; - padding: 0px 8px; -} - -.notice__30f28.error__30f28 { - background-color: var(--background-feedback-critical); -} - -.button__30f28 { - align-items: center; - font-weight: var(--font-weight-semibold); - height: fit-content; - padding: 4px 8px; -} - -.button__30f28:hover { - background-color: var(--brand-530); -} - -.error__30f28 > .button__30f28:hover { - background-color: var(--red-430); -} - -.header__30f28 { - color: var(--white); - display: inline; - margin-inline-end: 16px; } - -.backButtonInner__84419 { - display: flex; - gap: 8px; - padding: 2px 0px; -} - -.backButton__84419:hover { - background-color: var(--brand-530); -} - -.backNotice__84419 { - background-color: var(--brand-600); - border-radius: 0px; - padding: 8px; -} - -.closeButton__84419 { - height: 100%; -} - -.notice_c5cd6a { - background-color: var(--brand-600); - color: var(--white); - display: flex; - height: 40px; - justify-content: center; - padding: 0px 8px; -} - -.button_c5cd6a, .notice_c5cd6a { - align-items: center; -} - -.button_c5cd6a { - font-weight: var(--font-weight-semibold); - height: fit-content; - padding: 4px 8px; -} - -.button_c5cd6a:hover { - background-color: var(--brand-530); -} - -.back_c5cd6a { - bottom: 0px; - height: 24px; - inset-inline-start: 8px; - margin: auto; - position: absolute; - top: 0px; -} - -.iconButton_c5cd6a { - align-items: center; - display: flex; -} - -.arrow_c5cd6a { - margin-inline-end: 8px; } - -.header_c5cd6a { - color: var(--white); - display: inline; - margin-inline-end: 16px; } - -.container__477aa { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-12); - justify-content: center; -} - -.buttonGroup__477aa { - margin: var(--space-4) 0; - width: unset; -} - -.blocked__477aa { - align-items: center; - display: flex; -} - -.blockedIcon__477aa { - margin-inline: var(--space-12) var(--space-4); } - -.blockedText__477aa { - font-size: 12px; - font-weight: var(--font-weight-normal); -} - -.noIcon__477aa { - margin-inline-start: var(--space-12); } - -.actionContainer_bc4513 { - background-color: var(--background-mod-normal); - border-radius: 80px; - flex-direction: row; - padding-inline: 6px 8px; padding-bottom: 4px; - padding-top: 4px; -} - -.actionContainer_bc4513, .actionIconContainer_bc4513 { - align-items: center; - display: flex; - justify-content: center; -} - -.actionIconContainer_bc4513 { - height: 16px; - margin-inline-end: 4px; width: 16px; -} - -.actionIcon_bc4513 { - color: var(--text-muted); -} - -.actionTextContainer_bc4513 { - flex: 1 1 0%; -} - -.actionTextHeader_bc4513 { - overflow-wrap: normal; - text-transform: lowercase; -} - -.actionTextHelper_bc4513 { - margin-inline-start: 4px; text-transform: lowercase; -} - -.pill_a2c9e8 { - align-items: center; - background-color: var(--control-secondary-background-default); - border: 1px solid var(--border-subtle); - border-radius: 20px; - box-sizing: border-box; - color: var(--control-secondary-text-default); - display: inline-flex; - height: 32px; - overflow: hidden; - padding: 0px 12px; -} - -.pill_a2c9e8.clickable_a2c9e8:not(.disabled_a2c9e8):hover { - background-color: var(--control-secondary-background-hover); - cursor: pointer; -} - -.pill_a2c9e8.disabled_a2c9e8 { - cursor: not-allowed; - opacity: 0.5; -} - -.pill_a2c9e8.small_a2c9e8 { - height: 24px; - padding: 0px 8px; -} - -.pill_a2c9e8.clickable_a2c9e8:not(.disabled_a2c9e8):active { - background-color: var(--control-secondary-background-active); - cursor: pointer; -} - -.pill_a2c9e8.selected_a2c9e8 { - background-color: var(--message-reacted-background-default); - color: var(--message-reacted-text-default); -} - -.pill_a2c9e8.selected_a2c9e8:not(.disabled_a2c9e8):hover { - background-color: var(--opacity-blurple-32); -} - -.pill_a2c9e8.selected_a2c9e8:not(.disabled_a2c9e8):active { - background-color: var(--opacity-blurple-12); -} - -.pill_a2c9e8.danger_a2c9e8 { - background-color: var(--background-feedback-critical); - border-color: var(--border-feedback-critical); - color: var(--text-feedback-critical); -} - -.pill_a2c9e8.success_a2c9e8 { - background-color: var(--background-feedback-positive); - color: var(--text-feedback-positive); -} - -.pill_a2c9e8.success_a2c9e8, .pill_a2c9e8.warning_a2c9e8 { - border-color: var(--border-subtle); -} - -.pill_a2c9e8.warning_a2c9e8 { - background-color: var(--background-feedback-warning); - color: var(--text-feedback-warning); -} - -.theme-light .pill_a2c9e8.selected_a2c9e8 { - background-color: var(--brand-160); - border-color: var(--brand-500); -} - -.theme-dark .pill_a2c9e8.selected_a2c9e8 { - background-color: var(--brand-15a); - border-color: var(--brand-500); -} - -.emoji_a2c9e8 { - height: 14px; - margin-inline-end: 6px; width: 14px; -} - -.emoji_a2c9e8.small_a2c9e8 { - height: 12px; - width: 12px; -} - -.closeCircle_a2c9e8 { - align-items: center; - background-color: var(--background-surface-high); - border-radius: 50%; - cursor: pointer; - display: flex; - flex-shrink: 0; - justify-content: center; -} - -.close_a2c9e8 { - color: var(--text-strong); - height: 12px; - width: 12px; -} - -.tooltipPill_a2c9e8 { - margin: 4px 2px; -} - -.full-motion .pill_a2c9e8 { - transition: background-color 0.2s; -} - -.container__0e476 { - background-color: var(--input-background-default); - border: 1px solid var(--input-border-default); - border-radius: var(--radius-sm); - flex-shrink: 0; - overflow: hidden; -} - -.container__0e476, .inner__0e476 { - box-sizing: border-box; - display: flex; -} - -.inner__0e476 { - flex: 1 1 auto; - flex-flow: wrap; - padding: 1px; - position: relative; -} - -.disabled__0e476 .inner__0e476 { - opacity: 0.3; -} - -.input__0e476 { - appearance: none; - background: transparent; - border: none; - box-sizing: border-box; - color: var(--text-default); - flex: 1 1 0%; - margin: 1px; - min-width: 48px; - resize: none; -} - -.input__0e476::-webkit-input-placeholder { - color: var(--input-placeholder-text-default); - opacity: 1; -} - -.disabled__0e476 .input__0e476 { - cursor: not-allowed; -} - -.tag__0e476 { - align-items: center; - background-color: var(--background-mod-normal); - border: 1px solid var(--border-subtle); - border-radius: 5px; - box-sizing: border-box; - color: var(--text-default); - cursor: pointer; - display: flex; - flex-direction: row; - margin: 1px; - text-align: center; - transition: none; -} - -.tag__0e476:hover { - background-color: var(--background-mod-strong); - opacity: 1; - text-decoration: none; -} - -.small__0e476 .input__0e476, .small__0e476 .tag__0e476 { - font-size: 14px; - font-weight: var(--font-weight-medium); - height: 20px; - line-height: 20px; - padding: 0px 4px; -} - -.medium__0e476 .input__0e476, .medium__0e476 .tag__0e476 { - font-size: 16px; - height: 30px; - line-height: 32px; - padding: 0px 8px; -} - -.large__0e476 .input__0e476, .large__0e476 .tag__0e476 { - font-size: 20px; - height: 38px; - line-height: 40px; - padding: 0px 16px; -} - -.richTag__0e476 { - background-color: var(--background-mod-normal); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.medium__0e476 .richTag__0e476, .medium__0e476 .richTagInput__0e476 { - height: 26px; - line-height: 24px; - margin: 3px 0px; - margin-inline-start: 3px; } - -.large__0e476 .richTag__0e476, .large__0e476 .richTagInput__0e476 { - height: 32px; - line-height: 30px; - margin: 4px 0px; - margin-inline-start: 4px; } - -.tagLabel__0e476 { - overflow: hidden; - padding-inline-start: 6px; text-overflow: ellipsis; -} - -.tagRoleColor__0e476 { - border-radius: 50%; - flex-shrink: 0; - height: 8px; - width: 8px; -} - -.close__0e476 { - height: 12px; - margin-inline-start: 4px; width: 12px; -} - -.close__0e476, .iconLayout__0e476 { - box-sizing: border-box; -} - -.iconLayout__0e476 { - align-items: center; - cursor: text; - display: flex; - height: 22px; - justify-content: center; - width: 22px; -} - -.medium__0e476.iconLayout__0e476 { - height: 32px; - width: 32px; -} - -.large__0e476.iconLayout__0e476 { - height: 40px; - width: 40px; -} - -.iconContainer__0e476 { - box-sizing: border-box; - height: 16px; - position: relative; - width: 16px; -} - -.medium__0e476 .iconContainer__0e476 { - height: 20px; - width: 20px; -} - -.large__0e476 .iconContainer__0e476 { - height: 24px; - width: 24px; -} - -.icon__0e476 { - box-sizing: border-box; - color: var(--text-muted); - height: 100%; - inset-inline-start: 0px; - opacity: 0; - position: absolute; - top: 0px; - transform: rotate(90deg); - transition: transform 0.1s ease-out, opacity 0.1s ease-out; - width: 100%; - z-index: 2; -} - -.icon__0e476.visible__0e476 { - opacity: 1; - transform: rotate(0deg); -} - -.clear__0e476 { - box-sizing: border-box; - cursor: pointer; -} - -.clear__0e476 .icon__0e476 { - color: var(--interactive-text-default); -} - -.clear__0e476.iconLayout__0e476:hover .icon__0e476 { - color: var(--interactive-text-hover); -} - -.enable-forced-colors .input__0e476 { - border: 1px solid canvastext; - border-radius: 4px; -} - -.enable-forced-colors .input__0e476::-webkit-input-placeholder { - color: graytext; -} - -.enable-forced-colors .icon__0e476 { - background-color: canvas; - border: 1px solid canvas; - color: graytext; -} - -.enable-forced-colors .clear__0e476 .icon__0e476 { - background-color: buttonface; - border-color: canvastext; - color: buttontext; -} - -.enable-forced-colors .clear__0e476.iconLayout__0e476:focus .icon__0e476, .enable-forced-colors .clear__0e476.iconLayout__0e476:hover .icon__0e476 { - border-color: buttontext; - color: buttontext; -} - -.chat_f75fb0 { - background: var(--background-gradient-chat,var(--background-base-lowest)); - display: flex; - flex-direction: column; - min-height: 0px; - min-width: 0px; - overflow: hidden; - position: relative; -} - -.chat_f75fb0[data-has-border="true"] { - border-top: 1px solid var(--app-frame-border); -} - -[data-collapsed="true"] > .chat_f75fb0 { - border-inline-start: 1px solid var(--border-subtle); border-start-start-radius: var(--radius-md); -} - -.refresh-fast-follow-distinct-borders .chat_f75fb0.threadSidebarOpen_f75fb0 { - border-inline-end-color: var(--app-frame-border); } - -.chat_f75fb0 { - flex: 1 1 auto; -} - -.chat_f75fb0 .uploadArea_f75fb0 { - position: fixed; -} - -.chat_f75fb0.threadSidebarOpen_f75fb0 { - border-inline-end: 1px solid var(--border-subtle); border-radius: 0px 8px 8px 0px; -} - -.chat_f75fb0.threadSidebarOpen_f75fb0 .uploadArea_f75fb0 { - border-radius: 0px 8px 8px 0px; - margin-inline-end: 8px; width: auto; -} - -.chat_f75fb0.threadSidebarOpen_f75fb0.threadSidebarFloating_f75fb0 .uploadArea_f75fb0 { - border-radius: 0px; - margin-inline-end: 0px; } - -.typing_f75fb0 { - display: flex; -} - -.form_f75fb0 { - flex-shrink: 0; - margin-top: -16px; - position: relative; -} - -.form_f75fb0, .formWithLoadedChatInput_f75fb0 { - padding-inline: var(--space-xs); } - -.formWithLoadedChatInput_f75fb0: :before { - content: ""; - height: 0.5rem; - position: absolute; -} - -.formWithLoadedChatInput_f75fb0 ::before { - top: 0px; - inset-inline: 0px; -} - -.formWithLoadedChatInput_f75fb0::before { - background: linear-gradient(to bottom,transparent,var(--background-base-low) 100%); -} - -.custom-theme-background .formWithLoadedChatInput_f75fb0::before { - content: unset; -} - -.chatContent_f75fb0 { - background: var(--background-gradient-chat,var(--background-base-lower)); - display: flex; - flex: 1 1 auto; - flex-direction: column; - min-height: 0px; - min-width: 0px; - position: relative; -} - -.cursorPointer_f75fb0 { - cursor: pointer; -} - -.content_f75fb0 { - align-items: stretch; - display: flex; - flex: 1 1 auto; - flex-direction: row; - justify-content: stretch; - min-height: 0px; - min-width: 0px; - position: relative; -} - -.content_f75fb0.noChat_f75fb0 { - overflow: hidden; -} - -.content_f75fb0::before { - box-shadow: var(--shadow-ledge); - content: ""; - display: none; - height: 1px; - position: absolute; - top: -1px; - inset-inline: 0px; - pointer-events: none; - z-index: 1; -} - -.channelBottomBarArea_f75fb0 { - display: flex; - flex-direction: row; -} - -.channelTextArea_f75fb0 { - background: var(--background-gradient-highest,var(--chat-background-default)); - margin-bottom: var(--custom-chat-input-margin-bottom); -} - -.refresh-fast-follow-avatars .form_f75fb0 { - padding-inline: min(var(--space-xs),var(--space-8)); } - -.titleWrapper_f75fb0 { - align-items: center; - display: flex; - flex: 1 1 auto; - overflow: hidden; -} - -.editPartyIcon_f75fb0 { - cursor: pointer; - margin-inline-end: 4px; opacity: 0.6; - transition: opacity 0.2s; -} - -.editPartyIcon_f75fb0:hover { - opacity: 1; -} - -.channelName_f75fb0 { - margin: 0px 8px; -} - -.avatar_f75fb0 { - margin-inline-end: var(--space-8); } - -.parentChannelName_f75fb0 { - color: var(--text-default); -} - -.parentChannelName_f75fb0:hover { - color: var(--text-strong); -} - -.title_f75fb0 { - flex: 0 0 auto; - padding-inline-end: var(--space-xs); padding-top: var(--space-8); - position: relative; - z-index: 100; -} - -.gdm_f75fb0 { - padding-inline-start: calc(var(--custom-message-margin-horizontal)/2); } - -.followButton_f75fb0 { - margin-inline-end: 8px; - padding: 4px 8px; -} - -.status_f75fb0 { - align-items: center; - display: flex; - flex: 0 0 auto; - margin-inline-end: 8px; } - -.stop-animations .title_f75fb0 { - app-region: no-drag; -} - -.theme-light.custom-client-theme .title_f75fb0 { - --background-gradient-lowest: var(--background-gradient-chat); -} - -.guildBreadcrumbContainer_f75fb0 { - align-items: center; - cursor: pointer; - display: flex; - flex-shrink: 0; -} - -.guildSidebar_f75fb0 { - flex-shrink: 0; -} - -.guildBreadcrumbIcon_f75fb0 { - margin-inline-end: var(--space-4); } - -.loader_f75fb0 { - align-items: center; - display: flex; - justify-content: center; - width: 100%; -} - -.enable-forced-colors .cursorPointer_f75fb0 { - background-color: buttonface; - color: buttontext; - forced-color-adjust: none; - text-decoration: underline; -} - -.forumPostTitle_f75fb0 { - margin-inline-start: 8px; } - -.forumPostSidebarTitle_f75fb0 { - margin-inline-start: 0px; } - -.subtitleContainer_f75fb0 { - display: flex; - flex-direction: column; - position: relative; -} - -.secureFramesIcon_f75fb0 { - margin-inline-start: 4px; } - -.shaker_f75fb0 { - flex: 1 1 0%; - width: 100%; -} - -.linkedLobbyTooltip_f75fb0 { - margin-inline-end: 8px; } - -.linkedLobby_f75fb0, .linkedLobbyTooltip_f75fb0 { - align-items: center; - display: flex; -} - -.linkedLobbyApplicationIcon_f75fb0 { - border-radius: var(--radius-sm); - height: 14px; - margin-inline: 8px 4px; width: 14px; -} - -.linkedLobbyEducationTooltipWrapper_f75fb0 { - max-width: 288px; - pointer-events: all; -} - -.linkedLobbyEducationTooltip_f75fb0 { - align-items: center; - display: flex; - flex-direction: column; - gap: 4px; - padding: 8px 4px; - text-align: center; -} - -.linkedLobbyEducationTooltip_f75fb0 p { - margin: 0px; -} - -.linkedLobbyEducationTooltipCloseClickContainer_f75fb0 { - cursor: pointer; - inset-inline-end: 8px; - position: absolute; - top: 8px; -} - -.linkedLobbyEducationTooltipCloseClickContainer_f75fb0:hover .linkedLobbyEducationTooltipCloseIcon_f75fb0 { - color: var(--interactive-text-active); -} - -.linkedLobbyEducationTooltipCloseIcon_f75fb0 { - color: var(--icon-muted); - height: 24px; - width: 24px; -} - -.tile_ba65b0 { - height: 100%; - width: 100%; -} - -.tileSizer_ba65b0 { - height: 112px; - width: 195px; -} - -.tileSizer_ba65b0:not(:first-child) { - margin-inline-start: 8px; } - -.root_ba65b0 { - box-sizing: border-box; - display: flex; - height: 112px; - justify-content: center; - width: 100%; -} - -.effect_adebba { - position: absolute; -} - -.username_adebba { - align-items: center; - background-color: rgba(19, 19, 24, 0.8); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-border) var(--shadow-high); - box-sizing: border-box; - cursor: pointer; - display: flex; - flex-shrink: 1; - gap: 8px; - height: 24px; - justify-content: center; - margin-inline-start: var(--space-4); max-width: 100%; - opacity: 0.8; - overflow: hidden; - padding: var(--space-4) var(--space-8); - text-overflow: ellipsis; - white-space: nowrap; -} - -.username_adebba.streaming_adebba { - padding-inline-end: var(--space-4); } - -.voiceUserContainer_adebba { - display: flex; - flex-direction: column; - gap: var(--space-8); -} - -.voiceUser_adebba { - align-items: center; - cursor: pointer; - display: flex; - flex-direction: row; - opacity: 0.4; - position: relative; - transition: opacity 0.1s ease-in-out; -} - -.voiceUser_adebba.flipped_adebba { - flex-direction: row; - justify-content: flex-end; -} - -.voiceUser_adebba.flipped_adebba .avatar_adebba { - margin-inline: 0px; } - -.voiceUser_adebba.flipped_adebba .username_adebba { - margin-inline: 0 var(--space-4); padding-inline-start: var(--space-4); -} - -.voiceUser_adebba.flipped_adebba .voiceIcons_adebba { - margin-inline-end: var(--space-4) !important; } - -.clanTag_adebba { - margin-inline-start: var(--space-4); -} - -.voiceIcons_adebba { - margin-inline: 0px !important; } - -.voiceIcon_adebba { - display: block; - flex-shrink: 0; - opacity: 0.5; - color: var(--white) !important; - margin-inline-start: 0px !important; } - -.interactive_adebba { - opacity: 1 !important; -} - -.speaking_adebba { - opacity: 0.9; -} - -.speaking_adebba .avatarSpeakingOutline_adebba { - outline: 1px solid var(--green-300); - outline-offset: 2px; -} - -.justConnected_adebba { - opacity: 0.6; -} - -.connectedAnimationContainer_adebba { - background-color: rgb(19, 19, 24); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - bottom: 4px; - inset-inline-start: 36px; - overflow: hidden; - position: absolute; - top: 4px; - z-index: 100; -} - -.connectedAnimationContainer_adebba.right_adebba { - inset-inline-end: 36px; - inset-inline-start: unset; -} - -.emptySpace_adebba { - height: 0px; - width: 0px; -} - -.connectedAnimationInnerContainer_adebba { - align-items: center; - display: flex; - gap: var(--space-4); - height: 100%; - justify-content: flex-start; - overflow: hidden; - white-space: nowrap; - width: fit-content; -} - -.connectedAnimationInnerContainer_adebba.exiting_adebba { - max-width: 100%; -} - -.connectedAnimationInnerContainer_adebba.left_adebba { - padding-inline-start: 8px; text-align: start; -} - -.connectedAnimationInnerContainer_adebba.right_adebba { - padding-inline-end: 4px; text-align: end; -} - -.animation_adebba { - height: 24px; - width: 24px; -} - -.hiddenVoiceStates_adebba { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-4); - opacity: 1; -} - -.hiddenVoiceStates_adebba.locked_adebba { - opacity: 0.4; -} - -.hiddenVoiceStates_adebba.flipped_adebba { - flex-direction: row-reverse; -} - -.hiddenVoiceStatesAvatar_adebba { - justify-content: center; -} - -.hiddenVoiceStatesAvatar_adebba, .hiddenVoiceStatesText_adebba { - align-items: center; - display: flex; - flex-direction: row; -} - -.hiddenVoiceStatesText_adebba { - background-color: rgba(19, 19, 24, 0.8); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-border) var(--shadow-high); - cursor: pointer; - flex-shrink: 1; - opacity: 0.8; - padding: var(--space-4) var(--space-8); -} diff --git a/discord-html-copy/css/4845f272d76c596f.css b/discord-html-copy/css/4845f272d76c596f.css deleted file mode 100644 index 2baa532..0000000 --- a/discord-html-copy/css/4845f272d76c596f.css +++ /dev/null @@ -1,2016 +0,0 @@ -.flex_aa3ffd { - display: flex -} - -.alignStart_aa3ffd { - align-items: flex-start -} - -.alignEnd_aa3ffd { - align-items: flex-end -} - -.alignCenter_aa3ffd { - align-items: center -} - -.alignStretch_aa3ffd { - align-items: stretch -} - -.alignBaseline_aa3ffd { - align-items: baseline -} - -.justifyStart_aa3ffd { - justify-content: flex-start -} - -.justifyEnd_aa3ffd { - justify-content: flex-end -} - -.justifyCenter_aa3ffd { - justify-content: center -} - -.justifyAround_aa3ffd { - justify-content: space-around -} - -.justifyBetween_aa3ffd { - justify-content: space-between -} - -.noWrap_aa3ffd { - flex-wrap: nowrap -} - -.wrap_aa3ffd { - flex-wrap: wrap -} - -.wrapReverse_aa3ffd { - flex-wrap: wrap-reverse -} - -.directionRow_aa3ffd { - flex-direction: row -} - -.directionRowReverse_aa3ffd { - flex-direction: row-reverse -} - -.directionColumn_aa3ffd { - flex-direction: column -} - -.spacer_aa3ffd { - flex: 1; - overflow: hidden -} - -.vertical_aa3ffd { - display: flex; - flex-direction: column -} - -.horizontal_aa3ffd { - display: flex; - flex-direction: row -} - -.horizontalReverse_aa3ffd { - display: flex; - flex-direction: row-reverse -} - -.horizontal_aa3ffd>.spacer_aa3ffd,.horizontalReverse_aa3ffd>.spacer_aa3ffd,.vertical_aa3ffd>.spacer_aa3ffd { - min-height: 1px -} - -.flexCenter_aa3ffd,.streamerModeEnabled_aa3ffd { - align-items: center; - display: flex; - justify-content: center -} - -.streamerModeEnabled_aa3ffd { - flex: 1; - flex-direction: column -} - -.streamerModeEnabledImage_aa3ffd { - background-size: 100% 100%; - height: 220px; - width: 440px -} - -.streamerModeEnabledBtn_aa3ffd { - background-color: var(--twitch); - border-radius: 3px; - color: var(--white); - cursor: pointer; - font-size: 18px; - font-weight: var(--font-weight-bold); - line-height: 29px; - margin-top: 20px; - padding: 10px 20px; - text-transform: uppercase -} - -.streamerModeEnabledBtn_aa3ffd:hover { - background-color: var(--twitch-secondary) -} - -.streamerModeEnabledBtn_aa3ffd.disabled_aa3ffd { - background-color: var(--twitch); - cursor: default; - opacity: .5 -} - -.images-light .streamerModeEnabledImage_aa3ffd { - background-image: url(/assets/9a898919395a3f13.svg) -} - -.images-dark .streamerModeEnabledImage_aa3ffd { - background-image: url(/assets/a105cf85c9c42968.svg) -} - -.premiumTrialBadge_e4ef22 { - align-items: center; - border-radius: var(--radius-md); - display: flex; - margin-inline-start:auto;padding: 0 6px; - pointer-events: all; - text-transform: uppercase -} - -.backgroundGradient_e4ef22 { - background: linear-gradient(90deg,#db00a4,#5968f0) -} - -.premiumTrialAcknowledgedBadge_e4ef22 { - background-color: var(--background-mod-strong) -} - -.BogoBadgeClockIcon_e4ef22 { - color: var(--icon-subtle); - height: 14px; - margin-inline-end:4px;width: 14px -} - -.premiumTrialBadge__4c1e4 { - align-items: center; - border-radius: var(--radius-md); - color: var(--text-strong); - display: flex; - gap: 4px; - margin-inline-start:auto;padding: 1px 6px; - pointer-events: all; - text-transform: uppercase -} - -.premiumOfferUnackedBadge__4c1e4 { - background: linear-gradient(90deg,#db00a4,#5968f0) -} - -.premiumTrialBadge__4c1e4.premiumOfferUnackedBadge__4c1e4 .premiumTrialBadgeIcon__4c1e4,.premiumTrialBadge__4c1e4.premiumOfferWithTimerAcknowledgedBadge__4c1e4 .premiumTrialBadgeIcon__4c1e4 { - height: 12px; - width: 12px -} - -.premiumOfferWithTimerUnacknowledgedBadge__4c1e4 { - background-image: linear-gradient(to right,var(--premium-tier-2-purple-for-gradients),var(--premium-tier-2-pink-for-gradients)) -} - -.premiumOfferWithTimerAcknowledgedBadge__4c1e4 { - background-color: var(--background-mod-strong) -} - -.premiumTrialBadgeSelected__4c1e4 { - background-color: var(--background-base-lower) -} - -.premiumOfferBadgeCopy__4c1e4 { - color: var(--white) -} - -.theme-light .premiumTrialBadge__4c1e4 { - background-color: var(--background-base-lowest) -} - -.theme-light .premiumTrialBadgeSelected__4c1e4 { - background-color: var(--background-base-lower) -} - -@value minColumnWidth: 460px;.standardSidebarView__23e6b { - background: var(--background-base-lowest); - display: flex; - inset: 0; - position: absolute; - z-index: 101 -} - -.standardSidebarView__23e6b,.standardSidebarView__23e6b * { - box-sizing: border-box -} - -.standardSidebarView__23e6b.withUpsell__23e6b { - top: 44px -} - -.platform-win .standardSidebarView__23e6b { - top: 22px -} - -.sidebarRegion__23e6b { - display: flex; - flex: 1 0 var(--custom-standard-sidebar-view-sidebar-total-width); - justify-content: flex-end; - min-height: 100%; - z-index: 1 -} - -.sidebarRegionScroller__23e6b { - align-items: flex-start; - background: var(--background-base-lowest); - display: flex; - flex: 1 0 auto; - flex-direction: row; - justify-content: flex-end -} - -.sidebar__23e6b { - padding-block:60px;width: var(--custom-standard-sidebar-view-sidebar-total-width) -} - -.mobileSidebar__23e6b,.sidebar__23e6b { - padding-inline:var(--custom-standard-sidebar-view-standard-padding) var(--custom-standard-sidebar-view-sidebar-content-scrollbar-padding)} - -.mobileSidebar__23e6b { - padding-block: 10px 60px; - width: 100% -} - -.contentTransitionWrap__23e6b { - flex: 1; - height: 100% -} - -.contentRegionScroller__23e6b { - align-items: flex-start; - background-color: var(--background-base-low); - display: flex; - flex-direction: row; - height: 100%; - overflow-x: hidden; - position: static -} - -.contentRegionScroller__23e6b .toolsContainer__23e6b { - margin-inline-end:21px} - -.contentRegionScroller__23e6b .tools__23e6b { - position: fixed -} - -.contentRegionHiddenSidebar__23e6b { - justify-content: center -} - -.contentRegionShownSidebar__23e6b { - justify-content: flex-start -} - -.contentRegion__23e6b { - align-items: flex-start; - background: var(--background-base-low); - display: flex; - flex: 1 1 800px; - position: relative -} - -.contentColumn__23e6b,.customColumn__23e6b { - flex: 1 1 auto; - max-width: 740px; - min-height: 100%; - min-width: 460px -} - -.contentColumnWide__23e6b { - max-width: 740px; - padding-block:60px 80px;padding-inline:40px 10px} - -.contentColumnDefault__23e6b { - padding: 60px 40px 80px; - position: relative -} - -.contentColumnMinimal__23e6b { - padding-inline-end:40px} - -.contentColumnScrollable__23e6b { - flex: 1 1 auto; - min-width: 460px; - position: relative -} - -.customHeader__23e6b { - padding-top: 60px -} - -.customColumn__23e6b { - padding-bottom: 0 -} - -.customContainer__23e6b { - inset: 0; - overflow: hidden; - position: absolute -} - -.customScroller__23e6b { - box-sizing: border-box; - display: block; - position: static -} - -.customScroller__23e6b>div { - margin-inline:40px 97px;max-width: 660px; - min-width: 404px -} - -.noticeRegion__23e6b { - bottom: 0; - inset-inline: 0 71px; - max-width: 740px; - padding: 0 var(--custom-standard-sidebar-view-standard-padding) var(--custom-standard-sidebar-view-standard-padding); - position: absolute; - z-index: 2 -} - -.noticeRegionHiddenSidebar__23e6b { - margin: 0 auto -} - -.toolsContainer__23e6b { - flex: 0 0 36px; - margin-inline-end:35px;padding-top: 60px; - position: relative; - width: 60px -} - -.mobileSidebarTools__23e6b { - margin-inline-end:0} - -.theme-light .customContainer__23e6b:before { - border-bottom: 1px solid var(--opacity-black-8); - box-shadow: 0 1px 4px var(--opacity-black-8) -} - -.theme-dark .customContainer__23e6b:before { - border-bottom: 1px solid var(--opacity-black-20); - box-shadow: 0 1px 4px var(--opacity-black-20) -} - -.flexFullWidth__23e6b { - flex: 1 0 100% -} - -.hidden__23e6b { - display: none -} - -.mobileContent__23e6b { - box-sizing: border-box; - min-width: unset; - padding-top: 10px -} - -.mobileContentHeader__23e6b { - display: flex; - justify-content: space-between; - padding: 10px 20px -} - -.mobileContentHeader__23e6b.hideHamburger__23e6b { - justify-content: flex-end -} - -.mobileToolsContainer__23e6b { - padding-top: 10px -} - -.mobileToolsContainer__23e6b.closeIconOnly__23e6b { - margin-inline-end:0} - -.mobileToolsContainer__23e6b.isMobileAndroid__23e6b { - position: absolute; - z-index: 1000 -} - -.mobileToolsCloseIcon__23e6b>div { - flex: 0 0 24px; - height: 24px; - width: 24px -} - -.mobileSidebarHeader__23e6b { - display: flex; - justify-content: flex-end -} - -&.platform-win .contentRegionScroller__23e6b { - margin-top: var(--custom-app-top-bar-height) -} - -.tabBarItemContainer_c7e907 { - align-items: center; - display: flex; - justify-content: space-between -} - -.searchBar_c7e907 { - margin-bottom: var(--space-12) -} - -.right__89d2b { - transform: rotate(90deg) -} - -.down__89d2b { - transform: rotate(180deg) -} - -.left__89d2b { - transform: rotate(-90deg) -} - -:root { - --legacy-elevation-low: 0 1px 5px var(--opacity-black-20); - --legacy-elevation-high: 0 2px 10px 0 var(--opacity-black-8); - --legacy-elevation-border: 0 0 0 1px hsl(var(--primary-300-hsl)/0.3) -} - -.theme-dark { - --legacy-elevation-low: 0 1px 5px 0 var(--opacity-black-28); - --legacy-elevation-high: 0 2px 10px 0 var(--opacity-black-20); - --legacy-elevation-border: 0 0 0 1px hsl(var(--primary-700-hsl)/0.6) -} - -.elevationLow__9a9f9 { - box-shadow: var(--legacy-elevation-low) -} - -.elevationHigh__9a9f9 { - box-shadow: var(--legacy-elevation-high) -} - -.elevationBorderLow__9a9f9 { - box-shadow: var(--legacy-elevation-border),var(--legacy-elevation-low) -} - -.darkElevationBorderHigh__9a9f9 { - box-shadow: var(--legacy-elevation-border),var(--legacy-elevation-high) -} - -.lightElevationLow__9a9f9 { - box-shadow: var(--legacy-elevation-low) -} - -.lightElevationHigh__9a9f9 { - box-shadow: var(--legacy-elevation-high) -} - -.lightElevationBorderLow__9a9f9 { - box-shadow: var(--legacy-elevation-border),var(--legacy-elevation-low) -} - -.elevationBorderHigh__9a9f9,.elevationBorderLow__9a9f9,.elevationHigh__9a9f9,.elevationLow__9a9f9,.lightElevationBorderHigh__9a9f9 { - box-shadow: var(--legacy-elevation-border),var(--legacy-elevation-high) -} - -.container__9a9f9 { - background-color: var(--background-surface-highest); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-shadow: var(--legacy-elevation-high); - overflow: hidden; - padding-block:10px;padding-inline:16px 10px;transition-duration: .2s; - transition-property: background-color,color,border-color; - transition-timing-function: ease -} - -.container__9a9f9[data-emphasized=true] { - background-color: var(--notice-background-critical); - border-color: var(--border-feedback-critical); - color: var(--notice-text-critical) -} - -.flexContainer__9a9f9 { - align-items: center; - display: flex; - justify-content: space-between; - position: relative -} - -.shrinkingContainer__9a9f9 { - overflow: hidden -} - -.actions__9a9f9 { - display: flex; - flex-grow: 0; - gap: 0 10px; - justify-content: end -} - -.resetButton__9a9f9 { - padding-inline:0} - -.message__9a9f9 { - color: var(--text-default); - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.theme-light .notice__9a9f9 { - background-color: hsl(var(--primary-100-hsl)/.95) -} - -.theme-dark .notice__9a9f9 { - background: hsl(var(--primary-700-hsl)/.95) -} - -.theme-dark .message__9a9f9 { - color: var(--white) -} - -.enable-forced-colors .container__9a9f9 { - border: 2px solid CanvasText -} - -.featureBorder__65c15 { - border-radius: 8px -} - -.premiumFeatureBorder__65c15 { - background: linear-gradient(var(--background-base-low),var(--background-base-low)) padding-box,var(--custom-premium-colors-premium-gradient-tier-2-diagonal) border-box; - border: 2px solid transparent -} - -.limitedFeatureBorder__65c15 { - background: linear-gradient(var(--background-base-low),var(--background-base-low)) padding-box,linear-gradient(var(--blue-345),var(--blue-345),var(--premium-perk-light-blue)) border-box; - border: 1px solid transparent -} - -.background__65c15 { - padding: 24px -} - -.premiumBackground__65c15 { - background: linear-gradient(45deg,hsl(var(--premium-tier-2-purple-for-gradients-hsl)/.05) 0,hsl(var(--premium-tier-2-pink-for-gradients-hsl)/.05) 100%) border-box -} - -.limitedBackground__65c15 { - background: linear-gradient(78.43deg,hsl(var(--blue-345-hsl)/.1),hsl(var(--blue-345-hsl)/.1),hsl(var(--premium-perk-light-blue-hsl)/.1)) padding-box -} - -.imageInputContainer__4cc0e { - height: 0; - position: relative; - width: 0 -} - -.channelHeader__06fb5 { - align-items: center; - border-bottom: var(--border-subtle) 1px; - border-style: solid; - display: flex; - gap: 6px; - padding: 12px -} - -.icon__06fb5 { - border-radius: 6px; - box-sizing: border-box; - flex-shrink: 0; - height: 24px; - width: 24px -} - -.dmIcon__06fb5 { - background: var(--background-base-lowest); - justify-content: center; - padding: 6px -} - -.channelNameSection__06fb5,.dmIcon__06fb5 { - align-items: center; - display: flex -} - -.channelNameSection__06fb5 { - flex-grow: 1; - overflow: hidden -} - -.channelTypeIcon__06fb5 { - margin-inline-end:4px} - -.emptyContainer__4a7f0 { - box-sizing: border-box; - height: 480px; - padding: 16px -} - -.emptyContainer__4a7f0,.emptyInner__4a7f0 { - display: flex; - flex-direction: column -} - -.emptyInner__4a7f0 { - align-items: center; - flex-grow: 1; - justify-content: center; - padding: 0 40px; - pointer-events: none; - text-align: center -} - -.image__4a7f0 { - margin-bottom: 24px; - width: 140px -} - -.header__4a7f0 { - margin-bottom: 8px -} - -.upsellButton__4a7f0 { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color); - background-clip: padding-box; - border-radius: 3px; - flex-shrink: 0; - margin-inline-start:auto} - -.upsellContainer__4a7f0 { - align-items: center; - background: var(--background-base-lowest); - border-radius: 4px; - display: flex; - padding: 12px -} - -.upsellText__4a7f0 { - margin-inline-start:8px} - -.container_ea9851 { - background: var(--background-base-lowest); - flex-grow: 1; - gap: 6px; - padding: 12px -} - -.container_ea9851,.icon_ea9851 { - align-items: center; - display: flex -} - -.icon_ea9851 { - background: var(--background-base-lower); - border-radius: 6px; - height: 24px; - justify-content: center; - width: 24px -} - -.popoutContainer__4ad69 { - background-color: var(--background-surface-high); - border-radius: 8px; - box-shadow: var(--shadow-border),var(--shadow-high); - box-sizing: border-box; - display: flex; - flex-direction: column; - max-height: 80vh; - max-width: 507px; - min-width: 480px; - overflow: hidden; - position: relative; - width: 35vw; - z-index: 0 -} - -.messagesScroller__4ad69 { - display: flex; - flex-direction: column; - gap: 16px; - padding: 16px -} - -.message__4ad69 { - margin: 12px 0; - overflow: hidden -} - -.messageContainer__4ad69 { - background: var(--background-surface-higher); - border: 1px solid var(--border-subtle); - border-radius: 8px; - display: flex; - flex-direction: column; - flex-shrink: 0; - overflow: hidden; - position: relative; - transition: box-shadow .1s ease-in-out -} - -.messageContainer__4ad69:focus-within,.messageContainer__4ad69:hover { - box-shadow: var(--shadow-high) -} - -.messageContainer__4ad69:focus-within .hoverBar__4ad69,.messageContainer__4ad69:hover .hoverBar__4ad69 { - opacity: 1 -} - -.clickableMessageBackground__4ad69 { - inset-inline: 0; - bottom: 0; - cursor: pointer; - position: absolute; - top: 0 -} - -.deletedMessage__4ad69 { - flex-direction: row; - gap: 6px; - padding: 12px -} - -.deleteIcon__4ad69,.deletedMessage__4ad69 { - align-items: center; - display: flex -} - -.deleteIcon__4ad69 { - background: var(--background-base-lowest); - border-radius: 6px; - height: 24px; - justify-content: center; - width: 24px -} - -.hoverBar__4ad69 { - inset-inline-end: 6px; - opacity: 0; - position: absolute; - top: 6px -} - -.searchToken_bd8186 { - background-color: var(--background-surface-highest); - border: 1px solid var(--border-muted); - border-radius: var(--radius-xs); - color: var(--text-strong); - display: inline-block; - height: 22px; - margin-inline-start:unset;padding: 1px var(--space-4) -} - -.searchAnswer_bd8186 { - border-end-start-radius: 0; - border-start-start-radius: 0; - margin-inline:-2px 2px} - -.searchFilter_bd8186 { - border-end-end-radius: 0; - border-start-end-radius: 0; - font-weight: var(--font-weight-semibold) -} - -.searchFilter_bd8186+.searchFilter_bd8186 { - padding-inline-start:2px} - -.searchFilter_bd8186:has(+.searchAnswer_bd8186) { - border-inline-end:none} - -.searchFilter_bd8186+.searchAnswer_bd8186 { - border-inline-start:none;padding-inline-start:0} - -.container__16eb0 { - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-high); - box-sizing: border-box; - max-height: calc(100vh - var(--custom-app-top-bar-height) - var(--custom-channel-textarea-text-area-height) - var(--custom-channel-header-height)); - overflow: hidden; - padding: var(--space-12) var(--space-8); - width: 356px -} - -.datePickerContainer__16eb0 { - padding: 0 -} - -.calendar__16eb0 { - display: flex; - justify-content: center; - width: 314px -} - -.searchResultDMChannelIcon__16eb0 { - border-radius: 50%; - height: 20px; - width: 20px -} - -.searchResultDMChannelIcon__16eb0,.searchResultGDMChannelIcon__16eb0 { - display: inline-block; - flex-grow: 0; - flex-shrink: 0; - margin-inline-end:4px;vertical-align: top -} - -.searchResultGDMChannelIcon__16eb0 { - line-height: 0 -} - -.searchResultDMChannelName__16eb0 { - color: var(--search-popout-option-user-nickname); - font-weight: 500 -} - -.searchResultDMUserName__16eb0 { - color: var(--search-popout-option-user-username); - font-weight: 500; - margin: 0 5px -} - -.searchResultChannelCategory__16eb0 { - flex-shrink: 1; - font-size: 10px; - font-weight: var(--font-weight-semibold); - margin-inline-start:4px;overflow: hidden; - position: relative; - text-overflow: ellipsis; - text-transform: uppercase; - top: 2px; - white-space: nowrap -} - -.resultChannel__16eb0 { - align-items: center; - display: flex; - flex-wrap: nowrap -} - -.itemContainer__971b5 { - border-radius: var(--radius-sm); - cursor: pointer; - overflow: hidden -} - -.itemContainer__971b5:active,.itemContainer__971b5:hover,.itemContainer__971b5[aria-selected=true] { - background-color: var(--background-mod-subtle); - color: var(--text-strong) -} - -.item__971b5 { - align-items: center; - display: flex; - -webkit-mask-image: linear-gradient(to right,var(--background-surface-high) 90%,transparent 100%); - mask-image: linear-gradient(to right,var(--background-surface-high) 90%,transparent 100%); - padding: var(--space-4) var(--space-8) -} - -.itemIcon__971b5 { - color: var(--text-muted); - flex-shrink: 0; - height: 20px; - margin-inline-end:8px;width: 20px -} - -.filterSublabelTextContainer__971b5,.labelWithElements__971b5 { - align-items: center; - display: flex; - flex-shrink: 0; - gap: 4px -} - -.channelNameContainer__971b5 { - align-items: center; - display: flex -} - -.channelName__971b5 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.channelNameIcon__971b5 { - color: var(--text-default); - height: 14px; - margin-inline-end:4px;width: 14px -} - -.divider__971b5 { - background-color: var(--border-subtle); - height: 1px; - margin: var(--space-8) calc(var(--space-8)*-1) -} - -.groupWrapper__971b5 { - display: grid; - gap: var(--space-4) -} - -.groupHeader__971b5 { - align-items: center; - display: flex; - justify-content: space-between; - padding: var(--space-4) var(--space-8) -} - -.clearSearchHistory__971b5:hover .clearSearchHistoryIcon__971b5 { - color: var(--text-strong) -} - -.clearSearchHistoryIcon__971b5 { - color: var(--text-subtle); - cursor: pointer -} - -.autocompletePillContainer__971b5 { - align-items: center; - display: flex; - flex-shrink: 0 -} - -.nonText__971b5 { - margin-inline-end:8px} - -.filterPill__971b5 { - border-radius: var(--radius-xs) 0 0 var(--radius-xs); - padding: 3px 6px; - padding-inline-end:2px} - -.answerPill__971b5,.filterPill__971b5 { - background-color: var(--background-mod-strong) -} - -.answerPill__971b5 { - align-items: center; - border-radius: 0 var(--radius-xs) var(--radius-xs) 0; - display: flex; - gap: 4px; - margin-inline-end:8px;padding: 3px 6px; - padding-inline-start:2px;white-space: nowrap -} - -.channelAnswerPill__971b5 { - gap: 0 -} - -.channelPillIcon__971b5 { - color: var(--text-default); - margin-inline-end:2px} - -.search_c322aa { - overflow: visible; - position: relative; - z-index: 100; - -webkit-app-region: no-drag -} - -.search_c322aa .DraftEditor-root { - flex: 1; - font-size: 14px; - font-weight: var(--font-weight-medium); - height: 100%; - line-height: 26px; - overflow: hidden; - padding: 0 var(--space-4) -} - -.search_c322aa .DraftEditor-root .DraftEditor-editorContainer { - border: none; - border-radius: 2px; - height: 28px; - overflow: hidden -} - -.search_c322aa .DraftEditor-root .public-DraftEditor-content,.search_c322aa .DraftEditor-root .public-DraftEditorPlaceholder-root { - box-sizing: content-box; - padding-bottom: 20px; - padding-inline:2px} - -.search_c322aa .DraftEditor-root .public-DraftEditorPlaceholder-root { - color: var(--input-placeholder-text-default); - padding-inline-start:4px;width: 100% -} - -.search_c322aa .DraftEditor-root .public-DraftEditorPlaceholder-inner { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap!important; - width: 94% -} - -.search_c322aa .DraftEditor-root .public-DraftEditor-content { - overflow-x: auto; - overflow-y: hidden -} - -.search_c322aa .DraftEditor-root .public-DraftStyleDefault-block { - display: inline-block; - min-width: 1px; - white-space: pre -} - -.search_c322aa .DraftEditor-root .public-DraftStyleDefault-block span { - min-width: 1px -} - -.searchBar_c322aa { - align-items: center; - background: var(--background-gradient-low,var(--input-background-default)); - border: 1px solid var(--input-border-default); - border-radius: var(--radius-sm); - box-shadow: none; - box-sizing: border-box; - color: var(--text-default); - cursor: text; - display: flex; - height: 32px; - overflow: hidden; - padding: 2px 0; - width: 244px -} - -.searchBar_c322aa .icon_c322aa { - height: 100%; - margin-inline-end:var(--space-4)} - -.density-compact .searchBar_c322aa { - height: 28px -} - -.density-compact .search_c322aa .DraftEditor-root { - height: 100%; - line-height: 22px; - padding: 0 var(--space-4) -} - -.density-compact .search_c322aa .DraftEditor-editorContainer { - height: 24px -} - -.full-motion .searchBar_c322aa { - transition: width .25s ease -} - -.enable-forced-colors .focused_c322aa,.enable-forced-colors .open_c322aa { - border-color: Highlight; - color: CanvasText -} - -.enable-forced-colors .search_c322aa .public-DraftEditorPlaceholder-root { - color: GrayText -} - -.root__85d05.comboIcon__85d05 { - align-items: center; - display: flex; - flex-direction: row; - height: 24px; - width: auto -} - -.count__85d05 { - line-height: 24px; - margin-inline-start:4px} - -.icon__85d05 { - flex-shrink: 0; - height: 24px; - width: 24px -} - -@keyframes arrowBounce__49676 { - 0% { - transform: translate3d(0,-2px,0) - } - - 50% { - transform: translateZ(0) - } - - to { - transform: translate3d(0,-2px,0) - } -} - -.children__49676 { - flex: 0 1 auto -} - -.back__49676 { - color: var(--text-default); - margin-inline-end:12px;opacity: .6 -} - -.back__49676:active,.back__49676:hover { - opacity: .8 -} - -.back__49676:disabled { - opacity: .2 -} - -.updateIconForeground__49676 { - fill: var(--green-330) -} - -.search__49676 { - margin: 0 4px -} - -.toolbar__49676 { - align-items: center; - display: flex; - flex: 0 0 auto -} - -.downloadArrow__49676 { - animation: arrowBounce__49676 2s ease-in-out infinite; - color: var(--green-360) -} - -@media (min-width: 849px) { - .headerBarLoggedOut__49676 { - padding:0 30px - } -} - -.cloud__49676 { - color: var(--text-muted) -} - -.controlButtonWrapper__49676 { - display: flex; - gap: 8px; - margin-inline-end:8px} - -.iconContainer__0ecc4 { - color: var(--text-default) -} - -.iconContainer__0ecc4,.iconContainer__0ecc4>foreignObject { - align-items: center; - display: flex; - justify-content: center -} - -.iconContainer__0ecc4>foreignObject { - background: linear-gradient(0deg,var(--background-mod-strong,hsla(240,4%,61%,.24)) 0,var(--background-mod-strong,hsla(240,4%,61%,.24)) 100%),var(--background-surface-high) -} - -.smallText__0ecc4 { - transform: scale(.42) -} - -.textContainer__0ecc4 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.button_e18686 { - align-items: center; - color: var(--icon-subtle); - display: flex; - justify-content: center; - transition: color .2s ease -} - -.button_e18686:hover { - color: var(--icon-strong) -} - -.enable-forced-colors .button_e18686 { - background-color: ButtonFace; - border: 1px solid ButtonText -} - -.card_b846e5 { - position: relative -} - -.card_b846e5:before { - background-color: var(--interactive-background-hover); - border: 1px solid var(--border-subtle); - border-radius: 5px; - content: ""; - position: absolute; - top: 0; - inset-inline: -20px; - bottom: -1px; - opacity: 0; - transition: opacity .1s ease -} - -.card_b846e5.active_b846e5:before,.card_b846e5:focus-within:before,.card_b846e5:hover:before { - opacity: 1 -} - -.wrapper__8b9fc { - background-clip: padding-box; - background-color: var(--background-feedback-notification); - border-radius: 3px; - box-shadow: 0 1px 0 var(--opacity-black-24),inset 0 1px 0 var(--opacity-white-16); - color: var(--white); - display: inline-block; - font-size: 12px; - font-weight: var(--font-weight-medium); - line-height: 12px; - padding: 3px 6px; - text-shadow: 0 1px 0 var(--opacity-black-24); - text-transform: uppercase -} - -.lowImportance__8b9fc { - background-color: var(--background-mod-strong) -} - -.result__71961 { - border-radius: 3px; - cursor: pointer; - font-size: 16px; - font-weight: var(--font-weight-medium); - height: 34px; - overflow: hidden; - position: relative -} - -.result__71961[aria-selected=true] { - background: var(--interactive-background-selected) -} - -.content__71961 { - align-items: baseline; - display: flex; - flex-direction: row; - height: 34px; - line-height: 34px; - overflow: hidden; - padding: 0 10px -} - -.contentDefault__71961,.contentUnread__71961 { -} - -.contentDefault__71961 { - color: var(--interactive-text-default) -} - -.contentUnread__71961 { - color: var(--text-strong) -} - -.voiceSummaryContainer__71961 { - align-self: center -} - -.iconContainer__71961 { - align-items: center; - align-self: center; - display: flex; - justify-content: center; - margin-inline-end:5px;width: 20px -} - -.gameIcon__71961,.iconContainer__71961 { - flex-grow: 0; - flex-shrink: 0 -} - -.gameIconSize__71961 { - height: 20px; - width: 20px -} - -.icon__71961 { - color: var(--interactive-text-default); - display: block -} - -.name__71961 { - align-items: baseline; - flex: 1 1 auto; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.misc__71961,.name__71961 { - display: flex -} - -.misc__71961 { - align-items: center; - color: var(--interactive-text-default); - flex: 0 1 auto; - margin-inline-start:4px;max-width: 140px -} - -.match__71961 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.badge__71961,.match__71961 { - margin-inline-end:4px} - -.note__71961 { - color: var(--text-muted); - font-size: 10px; - font-weight: var(--font-weight-semibold); - line-height: 14px; - margin-top: 1px; - overflow: hidden; - text-overflow: ellipsis; - text-transform: uppercase; - white-space: nowrap -} - -.username__71961 { - font-weight: var(--font-weight-normal); - opacity: .6 -} - -.header__71961 { - color: var(--interactive-text-default); - font-size: 12px; - font-weight: var(--font-weight-semibold); - letter-spacing: .025em; - line-height: 30px; - margin-top: 4px; - text-transform: uppercase -} - -.dmIconContainer__71961 .guildIcon__71961,.guildIconContainer__71961 .guildIcon__71961 { - background-size: 100% 100%; - border-radius: 3px; - display: block; - flex-grow: 0; - flex-shrink: 0; - line-height: 20px; - margin: 0; - overflow: hidden; - text-align: center -} - -.dmIconContainer__71961,.guildIconContainer__71961 { -} - -.score__71961 { - align-items: center; - background-color: var(--opacity-black-20); - border-radius: 2px; - bottom: 2px; - display: flex; - font-family: var(--font-code); - inset-inline-end: 2px; - justify-content: center; - padding-inline:8px;position: absolute; - top: 2px -} - -.enable-forced-colors .result__71961[aria-selected=true] { - outline: 2px solid Highlight; - outline-offset: -2px -} - -.upsellOuter__50e68 { - border-radius: var(--radius-sm); - margin: 8px 12px; - position: relative -} - -.upsellInner__50e68 { - background: linear-gradient(to right,hsl(var(--premium-tier-2-purple-for-gradients-hsl)/.1) 0,hsl(var(--premium-tier-2-pink-for-gradients-hsl)/.1) 100%) border-box; - border-radius: calc(var(--radius-sm) - 2px); - display: flex; - flex-direction: column; - gap: 8px; - padding: 12px -} - -.upsellTitle__50e68 { - display: flex; - gap: 4px -} - -.nitroWheel__50e68 { - color: var(--text-strong) -} - -.close__50e68 { - cursor: pointer; - inset-inline-end: 8px; - position: absolute; - top: 8px -} - -.closeIcon__50e68 { - color: var(--interactive-text-default) -} - -.betaBadge__62691 { - color: var(--badge-expressive-text-default); - padding: 0 var(--space-8); - text-transform: uppercase -} - -.title__094ae { - padding-bottom: var(--space-24) -} - -.poggermodeIcon_bc2dd4 { - height: 20px; - width: 20px -} - -.connectedStatus_e56446 { - align-items: flex-start; - display: flex; - gap: var(--space-6); - justify-content: flex-start -} - -.spinner_e56446 { - height: 20px; - width: 20px -} - -.settingsContainer_ff76ae { - background: var(--background-base-lowest); - border: 1px solid var(--border-normal); - border-radius: var(--radius-md); - overflow: hidden; - position: relative -} - -.headerContainer_ff76ae { - box-sizing: border-box; - display: flex; - flex-direction: column; - padding: var(--space-24); - padding-bottom: 0 -} - -.header_ff76ae { - border-bottom: 1px solid var(--border-muted); - justify-content: space-between; - padding-bottom: var(--space-16) -} - -.header_ff76ae,.headerLogos_ff76ae { - align-items: center; - display: flex; - flex-direction: row -} - -.headerLogos_ff76ae { - gap: var(--space-10) -} - -.headerDivider_ff76ae { - background: var(--border-muted); - height: 13px; - width: 2px -} - -.contentContainer_ff76ae { - display: flex; - gap: var(--space-12); - padding: var(--space-24); - padding-top: var(--space-12) -} - -.contentSpacer_ff76ae { - flex: 1 -} - -.content_ff76ae { - display: flex; - flex-direction: column; - gap: var(--space-4); - max-width: 400px -} - -.ctaContainer_ff76ae { - align-items: center; - display: flex -} - -.spinner_ff76ae { - width: auto -} - -.subscribeBtnContainer_ff76ae { - display: flex; - flex-direction: column; - gap: var(--space-8) -} - -.spacingTop24__53965 { - margin-top: 24px -} - -.permissionWarning__53965 { - margin-top: 8px -} - -.previewToggle__53965 { - margin-top: 24px -} - -.cameraPreviewTitle__53965 { - align-items: center; - display: flex -} - -.selector__53965 { - margin-top: 16px; - width: 100% -} - -.selector__53965.selectorNoHeader__53965 { - margin-top: 24px -} - -.imageInput__53965 { - display: none -} - -.backgroundIconOptionIcon__53965 { - height: 24px -} - -.backgroundOptions__53965 { - display: grid; - gap: var(--space-16); - grid-auto-rows: 1fr; - grid-template-columns: repeat(auto-fill,minmax(132px,1fr)); - margin-top: var(--space-16) -} - -.backgroundImageOption__53965 { - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - height: 100%; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.full-motion .backgroundImageOption__53965 { - transition: transform .2s ease-out -} - -.backgroundOptionBackgroundBlurred__53965 { - filter: blur(14px); - position: absolute -} - -.backgroundOptionBlurBackground__53965 { - background-image: url(https://cdn.discordapp.com/assets/content/9b1bb92a5685e3f1d67378826197687bb24f8cf1319a4ab2375fbc8a3fb7d0fe.png) -} - -.backgroundOption__53965 { - box-sizing: border-box; - cursor: pointer; - height: 100%; - position: relative; - width: 100% -} - -.full-motion .backgroundOption__53965 { - transition: box-shadow .2s ease-out,transform .2s ease-out,background .2s ease-out,opacity .2s ease-in -} - -.backgroundOption__53965:hover:not(.backgroundOption__53965.backgroundOptionDisabled__53965):not(.backgroundOptionSelected__53965) { - box-shadow: var(--elevation-medium); - transform: translateY(-1px) -} - -.backgroundOption__53965:hover:not(.backgroundOption__53965.backgroundOptionDisabled__53965):not(.backgroundOptionSelected__53965) .backgroundImageOption__53965 { - transform: scale(1.05) translateZ(0) -} - -.backgroundOptionInner__53965 { - background-color: var(--primary-700); - border-radius: 4px; - height: 100%; - overflow: hidden; - position: relative; - width: 100% -} - -.playIcon__53965 { - align-items: center; - background-color: rgba(0,0,0,.4); - border-radius: 50%; - color: var(--white); - display: flex; - height: 16px; - inset-inline-end: 4px; - justify-content: center; - position: absolute; - top: 4px; - width: 16px -} - -.newTextBadge__53965 { - border: 3px solid var(--background-base-low); - border-radius: 10px; - box-sizing: content-box; - inset-inline-start: -6px; - position: absolute; - top: -3px -} - -.backgroundOptionRing__53965 { - border: 2px solid var(--brand-500); - border-radius: 4px; - position: absolute; - top: -4px; - inset-inline: -4px; - bottom: -4px -} - -.backgroundOptionDisabled__53965 { - cursor: not-allowed -} - -.backgroundOptionDisabled__53965 .backgroundCustomInlineUpsellBackgroundDarkener__53965 { - opacity: .04 -} - -.backgroundOptionContent__53965 { - align-items: center; - color: var(--white); - display: flex; - flex-direction: column; - height: 100%; - justify-content: center; - position: relative; - width: 100% -} - -.overflowEllipsis__53965 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.backgroundOptionText__53965 { - box-sizing: border-box; - max-width: 100%; - padding: 0 4px -} - -.newBackgroundTooltipContainer__53965 { - position: relative -} - -.backgroundCustomInlineUpsellBackground__53965 { - background-image: linear-gradient(90deg,var(--premium-tier-2-purple-for-gradients) 0,var(--premium-tier-2-purple-for-gradients-2) 50.24%,var(--premium-tier-2-pink-for-gradients) 100%); - position: absolute -} - -.backgroundCustomInlineUpsellBackgroundDarkener__53965 { - background-color: var(--neutral-45); - filter: blur(12px); - height: 100%; - opacity: .2; - position: absolute; - width: 100% -} - -.backgroundCustomInlineUpsell__53965 { - align-items: center; - display: flex -} - -.backgroundCustomInlineUpsellIcon__53965 { - flex-shrink: 0; - height: 14px; - margin-inline-end:4px;width: 14px -} - -.customBackgroundTooltip__53965 { - align-items: center; - color: var(--interactive-text-default); - display: flex -} - -.customBackgroundTooltipIcon__53965 { - height: 16px; - margin-inline-end:4px;width: 16px -} - -.theme-dark .backgroundOptionDisabled__53965 .backgroundCustomInlineUpsellBackground__53965,.theme-dark .backgroundOptionDisabled__53965 .backgroundOptionContent__53965 { - opacity: .2 -} - -.theme-light .backgroundOptionDisabled__53965 .backgroundCustomInlineUpsellBackground__53965,.theme-light .backgroundOptionDisabled__53965 .backgroundOptionContent__53965 { - opacity: .5 -} - -.spacingTop24_f22a74 { - margin-top: 24px -} - -.permissionWarning_f22a74 { - margin-top: 8px -} - -.selector_f22a74 { - margin-top: 16px; - width: 100% -} - -.selector_f22a74.selectorNoHeader_f22a74 { - margin-top: 24px -} - -.tooltip_f22a74 { - max-width: 244px; - text-align: center -} - -.tooltipWrapper_f22a74 { - height: 100% -} - -.previewToggle_f22a74 { - margin-bottom: 0 -} - -.filterLoadingIndicator_f22a74 { - background-color: var(--opacity-black-68); - bottom: 0; - height: 100%; - justify-content: center; - position: absolute; - width: 100% -} - -.cameraPreviewTitle_f22a74,.filterLoadingIndicator_f22a74 { - align-items: center; - display: flex -} - -.cameraDeeplink_f22a74 { - margin-top: var(--space-8) -} - -.cameraWrapper__11e1f { - align-items: center; - background-color: var(--background-base-lower); - border: 1px solid var(--background-base-lowest); - border-radius: var(--radius-xs); - display: flex; - flex-direction: column; - justify-content: center; - min-height: 220px; - position: relative; - width: 100% -} - -.camera__11e1f { - position: relative; - transform: scaleX(-1) -} - -.autoThresholdSlider__39fa9 { - height: auto -} - -.noInputDevicesDetectedWarning__39fa9 { - margin-top: 8px -} - -.theme-light .sliderBar__39fa9 { - background-color: var(--primary-200) -} - -.theme-dark .sliderBar__39fa9 { - background-color: var(--primary-400) -} - -.sliderBar__39fa9.disabled__39fa9 { - opacity: .6 -} - -.sliderBar__39fa9.speaking__39fa9 { - background-color: var(--green-230) -} - -.inputSensitivityToggle__39fa9 .sliderBar__39fa9 { - transition: background-color .2s linear -} - -.inputSensitivityToggle__39fa9.manual__39fa9 .microphone__39fa9 { - position: absolute; - width: 100% -} - -.inputSensitivityToggle__39fa9.manual__39fa9 .microphone__39fa9 .fill__39fa9 { - background-color: var(--opacity-black-24); - height: 100%; - transition: width 35ms ease -} - -.inputSensitivitySlider__39fa9 { - height: 40px; - position: relative; - width: 100% -} - -.inputSensitivityBar__39fa9 { - border-radius: var(--radius-xs); - display: block; - height: 8px; - overflow: hidden; - position: relative -} - -.inputSensitivityBarFill__39fa9 { - background: var(--brand-500); - height: 100% -} - -.inputSensitivityToggle__39fa9.manual__39fa9 .microphone__39fa9 { - background-color: transparent -} - -.inputSensitivityToggle__39fa9:not(.manual__39fa9) { - padding-top: 8px -} - -.keybindContainer_eedc51 { - display: grid; - min-width: 0; - width: clamp(160px,30vw,300px) -} - -.soundButtonSettingContainer__84bad { - align-items: center; - background: var(--input-background-default); - border: 1px solid var(--input-border-default); - border-radius: var(--radius-sm); - box-sizing: border-box; - display: flex; - flex-direction: row; - font-weight: var(--font-weight-semibold); - height: 40px; - justify-content: space-between; - overflow: hidden; - padding: 0 12px; - position: relative; - text-align: center; - width: 50% -} - -.emoji__84bad { - flex-shrink: 0; - margin-inline-end:8px} - -.emoji__84bad,.secondaryIcon__84bad { - height: 20px; - width: 20px -} - -.secondaryIcon__84bad { - margin: 0 4px -} - -.secondaryIconActive__84bad { - cursor: pointer -} - -.secondaryIconDisabled__84bad { - color: var(--icon-muted) -} - -.container__84bad { - align-items: center; - display: flex -} - -.soundText__84bad { - max-width: 190px; - overflow: hidden; - text-overflow: ellipsis -} - -.guildSelector__673eb { - margin-bottom: 0; - margin-top: 0 -} - -.nitroWheel__673eb { - height: 16px; - margin-inline-start:4px;width: 16px -} - -.container__673eb { - margin-bottom: 16px -} - -.callSoundsTitle__673eb { - margin-bottom: 8px -} - -.callSoundsDivider__673eb { - margin-bottom: 24px; - margin-top: 4px -} - -.notice__673eb { - margin-top: 8px -} - -.pill__673eb { - align-items: center; - background: var(--background-mod-normal); - border-radius: 8px; - display: inline-flex; - gap: 4px; - padding: 4px 8px -} - -.pillText__673eb { - max-width: 150px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.pillIcon__673eb { - width: 16px -} - -.micTest__169b3 { - margin-bottom: 16px -} - -.micTestHelpLink__169b3 { - margin-inline-start:4px} - -.profileCustomizationTab__99464 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-12); - max-width: 220px -} - -.textContainer__99464 { - display: flex; - flex: 1; - flex-direction: column; - gap: 2px; - min-width: 0; - overflow: hidden; - word-wrap: break-word; - white-space: normal; - word-break: break-word -} - -.editProfilesRow__99464 { - align-items: center; - display: flex; - gap: var(--space-4) -} - -.keyRecorder__7c484 { - min-width: 220px -} - -/*# sourceMappingURL=4845f272d76c596f.css.map*/ diff --git a/discord-html-copy/css/4851cc625502983d.css b/discord-html-copy/css/4851cc625502983d.css deleted file mode 100644 index dc5d1b8..0000000 --- a/discord-html-copy/css/4851cc625502983d.css +++ /dev/null @@ -1,2482 +0,0 @@ -.collapsed_d77896 { - -webkit-mask: linear-gradient(#000 240px,transparent); - mask: linear-gradient(#000 240px,transparent); - max-height: 300px; - overflow: hidden -} - -.blurb_d77896 { - color: var(--text-default); - font-size: 16px; - line-height: 20px; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.description_d77896 { - color: var(--text-muted); - font-size: 15px; - line-height: 1.4; - margin-top: 20px -} - -.toggleCollapseButton_d77896 { - margin-top: 20px; - width: 100% -} - -.assetWrapper_d77896 { - border-radius: 3px; - margin: 20px 0; - overflow: hidden; - transform: translateZ(0) -} - -.assetWrapper_d77896 img { - border-radius: 0 -} - -.asset_d77896 { - display: block; - margin: 0 auto; - max-width: 100% -} - -@media (min-width: 640px) { - .toggleCollapseButton_d77896 { - width:inherit - } -} - -@value headerBarHeight 60px;.headerBar__8a7fc { - align-items: center; - box-sizing: border-box; - display: flex; - gap: 20px; - height: var(--custom-channel-header-height); - padding: 0 16px; - width: 100%; - z-index: 100 -} - -.overlay__8a7fc,.overlay__8a7fc:after { - position: absolute -} - -.overlay__8a7fc:after { - background-color: var(--border-subtle); - bottom: 0; - content: ""; - height: 1px; - inset-inline-start: 0; - width: 100%; - z-index: 0 -} - -.relative__8a7fc { - background: var(--background-gradient-chat,var(--background-base-low)); - box-shadow: var(--shadow-low); - clip-path: inset(0 0 -4px 0); - position: relative -} - -.backdrop__8a7fc { - background: var(--background-gradient-low,var(--background-base-lower)); - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100%; - z-index: -1 -} - -.background__8a7fc { - background: var(--background-gradient-lower,var(--background-base-lower)) -} - -.icon__8a7fc { - height: 24px; - width: 24px -} - -.iconButton__8a7fc { - align-items: center; - background-color: var(--control-secondary-background-default); - border-radius: 4px; - color: var(--control-secondary-text-default); - cursor: pointer; - display: flex; - height: 32px; - justify-content: center; - width: 32px -} - -.iconButton__8a7fc:hover { - background-color: var(--control-secondary-background-hover) -} - -.iconButton__8a7fc:active { - background-color: var(--control-secondary-background-active) -} - -.floatingSearchTabsMask__8a7fc { - -webkit-mask-image: linear-gradient(90deg,#000 calc(100% - 340px),transparent calc(100% - 240px)); - mask-image: linear-gradient(90deg,#000 calc(100% - 340px),transparent calc(100% - 240px)) -} - -.refresh-fast-follow-distinct-borders .overlay__8a7fc:after { - background-color: var(--app-frame-border); - mix-blend-mode: luminosity -} - -.container__26669 { - display: flex; - flex: 1; - height: 100%; - margin-inline-start:44px;position: relative -} - -.measurements__26669 { - inset-inline: 0; - bottom: 0; - pointer-events: none; - position: absolute; - top: 60px; - visibility: hidden -} - -.measurements__26669,.tabs__26669 { - align-items: center; - display: flex; - gap: 24px -} - -.tabs__26669 { - flex: 1; - flex-direction: row; - height: 100% -} - -.tabWrapper__26669 { - justify-content: center -} - -.tab__26669,.tabWrapper__26669 { - align-items: center; - display: flex; - height: 100% -} - -.tab__26669 { - box-sizing: border-box; - cursor: pointer; - margin-top: 0 -} - -.tab__26669:hover { - border-bottom: 2px solid var(--brand-500); - margin-bottom: -2px -} - -.more__26669 { - cursor: pointer!important; - display: flex -} - -.selected__26669 { - border-bottom: 2px solid var(--icon-strong)!important; - margin-bottom: -2px -} - -.headerBar__1a9ce { - background-color: var(--background-base-lower); - border-bottom: 1px solid var(--border-subtle); - height: var(--custom-channel-header-height); - padding-bottom: 0; - padding-inline-end:var(--space-12);padding-top: 0; - position: sticky; - top: 0 -} - -.headerBarInner__1a9ce { - overflow: visible -} - -.fullscreenHeaderBar__1a9ce { - border: none; - box-sizing: border-box; - display: flex; - height: calc(64px + var(--custom-app-top-bar-height)); - justify-content: center; - margin: 0 auto; - padding: 0 72px; - padding-top: var(--custom-app-top-bar-height) -} - -.headerBarContent__1a9ce { - align-items: center; - display: flex; - gap: 24px; - justify-content: end; - padding-inline-end:36px} - -.discordLogoContainer__1a9ce { - align-items: center; - display: flex; - margin-top: 12px -} - -.discordLogo__1a9ce { - color: var(--text-default); - height: 20px; - margin-inline-end:-6px} - -.headerBarSearchContent__1a9ce { - align-items: center; - display: flex; - flex: 1; - flex-direction: row; - z-index: 1 -} - -.closeIcon__1a9ce { - margin-top: 24px -} - -.back__1a9ce { - cursor: pointer -} - -@value headerBarHeight 60px;.container__65d41 { - display: flex; - flex: 1; - height: 60px; - position: relative -} - -.measurements__65d41 { - align-items: center; - inset-inline: 0; - bottom: 0; - display: flex; - gap: 20px; - pointer-events: none; - position: absolute; - top: 60px; - visibility: hidden -} - -.tabs__65d41 { - border-bottom: none; - flex: 1; - flex-direction: row; - gap: 0; - height: 60px -} - -.tab__65d41,.tabs__65d41 { - display: flex -} - -.tab__65d41 { - align-items: center; - box-sizing: border-box; - cursor: pointer; - height: var(--custom-channel-header-height); - margin-top: 0; - padding-top: 16px; - --selected-tab-item-color: var(--text-default)!important -} - -.tab__65d41:hover,.tab__65d41[aria-selected=true] { - --selected-tab-item-color: var(--text-default) -} - -.container__65d41,.tabs__65d41 { - height: var(--custom-channel-header-height) -} - -.more__65d41 { - cursor: pointer!important; - display: flex; - gap: 4px -} - -.selected__65d41 { - border-bottom-color: var(--brand-500)!important -} - -@value searchBarWidth 240px;@value searchBarIcon 24px;.search__1ac1c { - display: flex; - justify-content: flex-end -} - -.searchBar__1ac1c { - flex: 1 1 240px; - max-width: 240px; - min-width: 240px -} - -.platform-osx .searchBar__1ac1c { - -webkit-app-region: no-drag -} - -.searchIcon__1ac1c { - cursor: pointer; - flex: 0 1 24px; - max-width: 24px; - min-width: 24px -} - -.searchFloating__1ac1c { - inset-inline-end: 16px; - position: absolute -} - -@value headerBarHeight 60px;@value imageHeight 288px;.container_e9ef78 { - display: grid; - grid-template-areas: "layer"; - grid-template-columns: 100%; - grid-template-rows: 348px; - justify-items: center -} - -.content_e9ef78 { - align-items: center; - display: flex; - grid-area: layer; - margin-top: 60px; - max-width: 1300px; - width: 100%; - z-index: 1 -} - -.textContainer_e9ef78 { - display: flex; - flex-direction: column; - justify-content: center; - padding-inline-start:32px} - -.buttonContainer_e9ef78 { - display: flex; - margin-top: 16px; - max-height: 44px; - min-height: 44px -} - -.title_e9ef78 { - max-width: 740px -} - -.title_e9ef78 p { - margin: 0 -} - -.uppercase_e9ef78 { - text-transform: uppercase -} - -.description_e9ef78 { - margin-top: 16px; - max-width: 600px -} - -.description_e9ef78 p { - margin: 0 -} - -.betaTag_e9ef78 { - display: inline-block; - margin-inline-start:8px;vertical-align: middle -} - -.theme-dark .gradient_e9ef78 { - background: radial-gradient(130.66% 324.98% at -3.95% 0,#2f72c1 0,rgba(37,34,154,.8) 35.73%,rgba(25,23,92,0) 100%),#000 -} - -.theme-dark .gradient_e9ef78.clientThemes_e9ef78 { - background: none -} - -.theme-dark .gradient_e9ef78.clientThemes_e9ef78:after { - background-color: var(--border-strong); - bottom: 0; - content: ""; - height: 1px; - position: absolute; - width: 100% -} - -.theme-dark .gradientBackground_e9ef78 { - opacity: .7 -} - -.theme-dark .gradientBackground_e9ef78.clientThemes_e9ef78 { - opacity: 1 -} - -.theme-light .gradient_e9ef78 { - background: radial-gradient(122.23% 351.84% at -4.04% 0,#eff8ff 0,#d7dcff 30.28%,#fdfdff 100%),#fff -} - -.theme-light .gradient_e9ef78.clientThemes_e9ef78 { - background: none -} - -.theme-light .gradient_e9ef78.clientThemes_e9ef78:after { - background-color: var(--border-strong); - bottom: 0; - content: ""; - height: 1px; - position: absolute; - width: 100% -} - -.theme-light .gradientBackground_e9ef78 { - opacity: .8 -} - -.theme-light .gradientBackground_e9ef78.clientThemes_e9ef78 { - opacity: 1 -} - -.gradient_e9ef78,.gradientBackground_e9ef78 { - height: 100%; - width: 100% -} - -.gradientBackground_e9ef78 { - grid-area: layer -} - -.contentSection_b6bcee { - align-items: center; - display: flex; - flex: 1; - flex-direction: column; - height: 100%; - width: 100%; - z-index: 1 -} - -.content_b6bcee { - height: 100%; - max-width: 1300px; - width: 100% -} - -.scroller__23746 { - background: var(--background-gradient-chat,var(--background-base-lower)); - display: flex; - flex: 1; - flex-direction: column -} - -.container__2abba { - align-items: center; - flex: 1; - flex-direction: column -} - -.container__2abba,.imageContainer__2abba { - display: flex; - justify-content: center -} - -.imageContainer__2abba { - margin-bottom: 16px; - width: 100% -} - -.image__2abba { - max-width: 500px; - min-width: min(100%,300px); - width: 40%; - -webkit-user-drag: none -} - -.full-motion .image__2abba { - animation: wiggling__2abba 2s linear infinite; - animation-play-state: paused -} - -.full-motion .image__2abba:hover { - animation-play-state: running -} - -.header__2abba { - margin-bottom: 8px; - text-align: center -} - -@keyframes wiggling__2abba { - 0% { - animation-timing-function: linear; - transform: rotate(0deg) - } - - 7% { - animation-timing-function: linear; - transform: rotate(-16deg) - } - - 20% { - animation-timing-function: ease-in-out; - transform: rotate(14deg) - } - - 32% { - animation-timing-function: ease-in-out; - transform: rotate(-16deg) - } - - 44% { - animation-timing-function: ease-in-out; - transform: rotate(12deg) - } - - 56% { - animation-timing-function: ease-in-out; - transform: rotate(-10deg) - } - - 68% { - animation-timing-function: ease-in-out; - transform: rotate(14deg) - } - - 81% { - animation-timing-function: ease-in-out; - transform: rotate(-16deg) - } - - 93% { - animation-timing-function: linear; - transform: rotate(14deg) - } - - to { - animation-timing-function: linear; - transform: rotate(0deg) - } -} - -.container__00b64 { - min-height: 150px; - min-width: 150px; - position: relative -} - -.spinnerContainer__00b64 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - display: flex -} - -.spinner__00b64 { - align-self: center; - justify-self: center -} - -.loading__00b64 { - opacity: .2 -} - -.card__84e3e { - border-radius: 8px; - overflow: hidden -} - -.full-motion .card__84e3e { - transition: box-shadow .2s ease-out,transform .2s ease-out,background .2s ease-out,border .2s ease-out,opacity .2s ease-in -} - -.full-motion .card__84e3e:focus,.full-motion .card__84e3e:hover { - box-shadow: var(--shadow-border),var(--shadow-high); - transform: translateY(-1px) -} - -.enable-forced-colors .card__84e3e { - border: 1px solid CanvasText -} - -.theme-dark .card__84e3e { - background-color: var(--card-background-default); - border: 1px solid var(--border-subtle) -} - -.theme-dark .card__84e3e:hover { - background-color: var(--background-base-lowest) -} - -.theme-light .card__84e3e { - background-color: var(--background-base-lower); - box-shadow: var(--legacy-elevation-border),var(--elevation-medium) -} - -.theme-light .card__84e3e:focus,.theme-light .card__84e3e:hover { - background-color: var(--background-base-lowest); - box-shadow: var(--shadow-border),var(--shadow-high) -} - -&.theme-dark .card__84e3e,&.theme-light .card__84e3e { - background-color: var(--background-base-low); - border: 1px solid transparent; - border-color: var(--border-subtle); - box-shadow: none -} - -&.theme-dark .card__84e3e:focus,&.theme-dark .card__84e3e:hover,&.theme-light .card__84e3e:focus,&.theme-light .card__84e3e:hover { - background-color: var(--background-surface-high); - border-color: var(--border-strong); - box-shadow: var(--shadow-low); - transform: none -} - -.clickable__84e3e { - cursor: pointer -} - -.disabled__84e3e { - cursor: auto; - opacity: .5; - pointer-events: none -} - -@keyframes placeholderPulse__84e3e { - 0% { - opacity: .6 - } - - 50% { - opacity: .8 - } - - to { - opacity: .6 - } -} - -.cardPlaceholder__84e3e { - animation: placeholderPulse__84e3e 1.8s ease-in-out infinite; - background-color: var(--background-base-lowest) -} - -.transitionItem__84e3e { - bottom: 0; - position: absolute; - top: 0; - inset-inline: 0 -} - -.bannerContainer_b76d57 { - display: block; - overflow: hidden; - position: relative; - width: 100% -} - -.bannerContent_b76d57 { - background-color: transparent; - height: 100%; - -o-object-fit: cover; - object-fit: cover; - -o-object-position: center; - object-position: center; - width: 100%; - -webkit-user-drag: none -} - -.bannerGradient_b76d57.loaded_b76d57 { - animation: fadeIn_b76d57 .7s forwards -} - -.bannerImage_b76d57 { - -webkit-user-drag: none; - opacity: 0 -} - -.bannerImage_b76d57.loaded_b76d57 { - animation: fadeIn_b76d57 .7s forwards -} - -.bannerAnimatedContainer_b76d57 { - height: 100%; - opacity: 0; - position: absolute; - top: 0; - width: 100% -} - -.bannerAnimatedContainer_b76d57.videoFadeIn_b76d57 { - animation: fadeIn_b76d57 .5s forwards -} - -.bannerAnimatedContainer_b76d57.videoFadeOut_b76d57 { - animation: fadeOut_b76d57 .5s; - animation-fill-mode: forwards -} - -@keyframes fadeIn_b76d57 { - 0% { - opacity: 0 - } - - to { - opacity: 1 - } -} - -@keyframes fadeOut_b76d57 { - 0% { - opacity: 1 - } - - to { - opacity: 0 - } -} - -.container_f215b9 { - min-width: 0; - position: relative -} - -.card_f215b9 { - flex-direction: column; - height: 100% -} - -.card_f215b9,.card_f215b9:hover .contextMenu_f215b9 { - display: flex -} - -.avatarContainer_f215b9,.header_f215b9 { - position: relative -} - -.avatar_f215b9 { - -webkit-user-drag: none; - background: var(--background-base-low); - border: solid; - border-color: var(--background-base-low); - border-radius: var(--radius-md); - transform: translateY(-50%) -} - -.appDetails_f215b9 { - flex: 1; - flex-direction: column; - padding: 4px 16px 16px -} - -.appDetails_f215b9,.titleContainer_f215b9 { - display: flex -} - -.title_f215b9 { - align-items: center; - display: flex; - gap: 4px; - overflow: hidden; - text-overflow: ellipsis -} - -.infoContainer_f215b9 { - display: flex; - gap: 4px; - margin-top: 4px -} - -.appName_f215b9 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.appCategory_f215b9 { - white-space: pre -} - -.description_f215b9 { - margin-top: 8px; - min-height: 0; - overflow: hidden; - text-overflow: ellipsis -} - -.memberDetails_f215b9 { - align-items: flex-end; - display: flex; - flex: 1 -} - -.theme-dark .avatar_f215b9 { - background: var(--background-base-lowest); - border-color: var(--background-base-low) -} - -.theme-light .avatar_f215b9 { - background: var(--background-base-lower); - border-color: var(--background-base-lower) -} - -@value minCardWidth 254px;.content__87ce6 { - -moz-column-gap: 16px; - column-gap: 16px; - display: grid; - grid-auto-rows: 1fr; - grid-template-columns: repeat(auto-fill,minmax(254px,1fr)); - padding: 16px 32px; - row-gap: 16px -} - -.errorContainer__87ce6 { - display: flex; - height: 100% -} - -.error__87ce6 { - padding: 16px 32px -} - -.paginationInput__87ce6 { - margin: 0; - padding: 16px 0 -} - -.errorContainer__7b60c { - display: flex; - height: 100% -} - -.error__7b60c { - padding: 16px 32px -} - -.firstChildSpacingFix_c9a59b { - margin-inline-start:0!important} - -.tabItem_c9a59b { - align-items: center; - display: flex; - flex-direction: row -} - -.alignCenter__31873 { - margin: 0 auto -} - -.alignLeft__31873 { - margin: 0 -} - -.horizontalPaginationItemContainer__31873 { - align-items: center; - display: flex; - flex: initial; - overflow-x: auto; - overflow-y: hidden -} - -.verticalPaginationItemContainer__31873 { - overflow-y: hidden -} - -.arrow__31873 { - height: 26px; - width: 26px -} - -.arrow__31873,.arrowContainer__31873 { - box-sizing: border-box; - pointer-events: all -} - -.arrowContainer__31873 { - color: var(--white); - cursor: pointer; - font-size: 0; - height: 50px; - line-height: 0; - position: absolute; - top: 50%; - transform: translateY(-50%); - width: 50px -} - -.prevButtonContainer__31873 { - inset-inline-start: 4px -} - -.nextButtonContainer__31873 { - inset-inline-end: 4px -} - -@media (max-width: 600px) { - .prevButtonContainer__31873 { - inset-inline-start:5% - } - - .nextButtonContainer__31873 { - inset-inline-end: 5% - } -} - -.item_cf6769 { - border-radius: 3px; - box-sizing: border-box; - cursor: pointer; - font-size: 3em; - height: 100%; - overflow: hidden; - transform: translateZ(0); - transition: filter .2s ease; - width: 100% -} - -.currentItem_cf6769 { - cursor: default -} - -.itemImage_cf6769 { - height: 100%; - -o-object-fit: contain; - object-fit: contain; - width: 100% -} - -.currentImage_cf6769 { - cursor: zoom-in -} - -.itemImageWrapper_cf6769 { - height: 100%; - width: 100% -} - -.itemVideo_cf6769 { - cursor: pointer -} - -.paginationItem_cf6769 { - height: 73px; - margin-inline-end:20px;opacity: .6; - position: relative; - touch-action: manipulation; - width: 130px -} - -.paginationItem_cf6769:last-child { - margin-inline-end:0} - -.storePaginationImg_cf6769 { - border-radius: 3px; - height: 73px; - -o-object-fit: cover; - object-fit: cover; - width: 130px -} - -.selectedStorePaginationItem_cf6769 { - opacity: 1 -} - -.unselectedStorePaginationItem_cf6769:hover { - cursor: pointer; - opacity: .8; - transition: opacity .3s ease -} - -.overlappingBorder_cf6769 { - border: 3px solid var(--brand-500); - border-radius: 3px; - inset: 0; - position: absolute -} - -.paginationVideoOverlay_cf6769 { - align-items: center; - display: flex; - inset: 0; - justify-content: center; - position: absolute -} - -.paginationVideoPlayPill_cf6769 { - border-radius: 50%; - height: 40px; - padding: 8px -} - -.pagination_cf6769 { - box-sizing: border-box; - margin: 20px auto; - max-width: 920px; - padding: 0 20px -} - -.carouselButtonsContainer_cf6769 { - color: var(--primary-100); - position: relative -} - -.arrow_cf6769 { - background-color: var(--primary-800); - border-radius: 50%; - opacity: .4; - padding: 12px; - transition: opacity .2s ease-in-out -} - -.arrow_cf6769:hover,.arrowHovered_cf6769 { - opacity: .6 -} - -.scroller_cf6769 { - padding-bottom: 24px -} - -.video_cf6769 { - background: var(--black) -} - -.mediaPlayer_cf6769,.video_cf6769,.videoWrapper_cf6769 { - height: 100%!important; - width: 100%!important -} - -.item_cf6769 { - background-color: var(--black) -} - -.wrapper__21d85 { - align-content: stretch; - align-items: stretch; - bottom: 60px; - cursor: zoom-out; - display: flex; - top: 60px; - inset-inline: 110px; - justify-content: center -} - -.slide__21d85,.wrapper__21d85 { - position: absolute -} - -.slide__21d85 { - height: 100%; - inset-inline-start: 0; - -o-object-fit: scale-down; - object-fit: scale-down; - top: 0; - width: 100%; - z-index: 0 -} - -.nav__21d85 { - opacity: .6; - z-index: 1 -} - -.nav__21d85:hover { - opacity: 1 -} - -.nav__21d85:active { - margin-top: 1px -} - -.navPrev__21d85 { - margin-inline-start:-100px} - -.navNext__21d85 { - margin-inline-end:-100px} - -.root__00dfb { - position: relative -} - -.carouselContainer__00dfb { - overflow: hidden -} - -.carousel__00dfb { - height: 100%; - z-index: 0 -} - -.carouselItem__00dfb { - inset-inline: 0; - bottom: 0; - position: absolute; - top: 0 -} - -.pagination__00dfb { - height: 64px; - pointer-events: none -} - -.themedPagination__00dfb { -} - -.controls__00dfb { - align-items: center; - display: flex; - justify-content: space-between -} - -.arrowHitbox__00dfb { - pointer-events: all -} - -.arrowHitboxPadding__00dfb { - padding: 0 12px -} - -.arrow__00dfb { - color: #fff; - opacity: .5; - position: relative; - transition: opacity .2s ease-in-out -} - -.dots__00dfb { - display: flex; - flex-direction: row; - flex-grow: 0 -} - -.dot__00dfb+.dot__00dfb { - margin-inline-start:8px} - -.dot__00dfb { - background-color: var(--white); - border-radius: 4px; - cursor: pointer; - height: 8px; - padding: 0; - pointer-events: all; - transform: translateZ(0); - width: 8px -} - -.dotNormal__00dfb { - opacity: .6 -} - -.dotNormal__00dfb:hover { - opacity: .8 -} - -.dotSelected__00dfb { - opacity: 1 -} - -.theme-dark .themedPagination__00dfb .arrow__00dfb { - color: var(--white) -} - -.theme-dark .themedPagination__00dfb .dot__00dfb { - background-color: var(--white) -} - -.theme-light .themedPagination__00dfb .arrow__00dfb { - color: var(--primary-500) -} - -.theme-light .themedPagination__00dfb .dot__00dfb { - background-color: var(--primary-500) -} - -.smallCarousel__03498,.smallCarouselItem__03498 { - border-radius: 5px 5px 0 0; - overflow: hidden -} - -.smallCarousel__03498 { - background-color: var(--black) -} - -.smallCarouselItem__03498 { - height: 100% -} - -.smallCarouselImage__03498 { - cursor: zoom-in; - height: 100%; - -o-object-fit: contain; - object-fit: contain; - width: 100% -} - -.embedContainer__03498 { - background-color: var(--primary-800); - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.hidden__03498 { - display: none -} - -.spinner__03498 { - align-self: center; - margin-top: 64px -} - -.errorContainer__03498 { - align-items: center; - background-color: var(--background-base-lower); - border-radius: 8px; - display: flex; - flex-direction: column; - height: 100%; - justify-content: center -} - -.sizedToParent__03498 { - height: 100%!important; - width: 100%!important -} - -.sizedToParent__03498,.sizedToParent__03498>video { - border-radius: 0 -} - -.errorImage_d389c2 { - width: 33% -} - -.carousel_d389c2 { - background: transparent -} - -.sectionContainer_c6b177 { - display: flex; - flex-direction: column; - gap: 12px -} - -.contentContainer_c6b177 { - background-color: var(--background-base-lower); - border: 1px solid var(--border-subtle); - border-radius: 8px; - display: flex; - flex-direction: column; - gap: 16px; - padding: 16px -} - -.commandContainer_c6b177 { - align-items: baseline; - display: flex; - flex-direction: row; - gap: 16px -} - -.commandName_c6b177 { - background-color: var(--background-base-lowest); - border: 1px solid var(--border-subtle); - border-radius: 4px; - font-size: 14px; - font-weight: 400; - padding: 2px 4px -} - -.sectionContainer__8f46b { - display: flex; - flex-direction: column; - gap: 12px -} - -.contentContainer__8f46b { - background-color: var(--background-base-lower); - border: 1px solid var(--border-subtle); - border-radius: 8px; - padding: 16px -} - -.privacyPolicy__8f46b { - margin-top: 8px -} - -.intentsList__8f46b { - border-bottom: 1px solid var(--border-subtle); - display: flex; - flex-direction: column; - gap: 16px; - margin-bottom: 16px; - padding-bottom: 16px -} - -.intentContainer__8f46b { - color: var(--text-default); - display: flex; - gap: 16px -} - -.intentTextContainer__8f46b { - display: flex; - flex-direction: column; - gap: 4px -} - -.wrapper_bb856d { - align-items: flex-start -} - -.icons_bb856d,.wrapper_bb856d { - display: flex; - flex-direction: row -} - -.icons_bb856d { - align-items: center; - margin-inline-end:4px} - -.iconMask_bb856d { - margin-inline-end:-4px} - -.icon_bb856d { - border-radius: 50% -} - -.iconSmall_bb856d { - height: var(--custom-guild-count-small-icon-size); - width: var(--custom-guild-count-small-icon-size) -} - -.iconLarge_bb856d { - height: var(--custom-guild-count-large-icon-size); - width: var(--custom-guild-count-large-icon-size) -} - -.moreGuilds_bb856d { - background-color: var(--background-base-lowest); - box-sizing: border-box; - color: var(--text-default); - font-weight: var(--font-weight-medium); - padding: 0 4px; - text-align: center -} - -.moreGuildsSmall_bb856d { - border-radius: var(--custom-guild-count-small-icon-size); - font-size: 10px; - height: var(--custom-guild-count-small-icon-size); - line-height: var(--custom-guild-count-small-icon-size); - min-width: var(--custom-guild-count-small-icon-size) -} - -.moreGuildsLarge_bb856d { - border-radius: var(--custom-guild-count-large-icon-size); - font-size: 12px; - height: var(--custom-guild-count-large-icon-size); - line-height: var(--custom-guild-count-large-icon-size); - min-width: var(--custom-guild-count-large-icon-size) -} - -.defaultIcon_bb856d { - color: var(--interactive-text-default) -} - -.categories_ed45d7 { - display: flex; - flex-wrap: wrap; - gap: 8px -} - -.category_ed45d7 { - align-items: center; - border: 1px solid var(--border-strong); - border-radius: 4px; - display: flex; - justify-content: center; - padding: 6px 12px -} - -.category_ed45d7:hover { - background-color: var(--interactive-background-hover); - cursor: pointer; - transition: background-color .2s ease -} - -.container__8a003,.list__8a003 { - display: flex; - gap: 12px -} - -.list__8a003 { - flex-direction: column -} - -.listItem__8a003 { - align-items: center; - display: flex; - flex-direction: row -} - -.listItem__8a003.linkItem__8a003:hover { - cursor: pointer -} - -.listItem__8a003.linkItem__8a003:hover .listIcon__8a003,.listItem__8a003.linkItem__8a003:hover .listText__8a003 { - color: var(--interactive-text-active) -} - -.listText__8a003 { - flex: 1 -} - -.listIcon__8a003 { - color: var(--interactive-text-default) -} - -.listIcon__8a003,.listImage__8a003 { - margin-inline-end:8px} - -.listImage__8a003 { - height: 20px; - width: 20px -} - -@use postcss-pxtorem;.card_c59071 { - background-color: var(--background-mod-normal); - border-radius: 8px; - display: flex; - flex-direction: column; - min-height: 248px -} - -.card_c59071:hover { - cursor: pointer -} - -.cardHeader_c59071 { - display: block; - height: 108px; - margin-bottom: 33px; - overflow: visible; - position: relative -} - -.splashImage_c59071 { - border-start-end-radius: 8px; - border-start-start-radius: 8px; - height: 100%; - inset-inline-start: 0; - -o-object-fit: cover; - object-fit: cover; - position: absolute; - top: 0; - width: 100% -} - -.guildIcon_c59071 { - bottom: -32px; - inset-inline-start: 12px; - position: absolute -} - -.iconMask_c59071 { - background-color: var(--background-mod-normal); - padding: 4px -} - -.cardDetails_c59071 { - align-content: stretch; - display: flex; - flex: 1; - flex-direction: column; - overflow: hidden; - padding: 0 16px 16px -} - -.guildNameWrapper_c59071 { - align-items: center; - display: flex; - font-weight: var(--font-weight-semibold); - margin-bottom: 4px -} - -.guildBadge_c59071 { - flex: 0 0 16px; - height: 16px; - margin-inline:-2px 4px;width: 16px -} - -.guildName_c59071 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.guildDescription_c59071 { - flex: 1 1 auto; - margin-bottom: 16px -} - -.memberInfo_c59071 { - align-items: center; - display: flex; - flex: 0 -} - -.memberCountWrapper_c59071 { - align-items: center; - display: flex; - flex-direction: row -} - -.memberCountIcon_c59071 { - color: var(--text-default); - margin-inline-end:4px} - -.outerContainer__5a77b { - clip-path: inset(0 0 0 16px); - line-height: 16px; - margin-inline-start:-16px;row-gap: 4px -} - -.innerContainer__5a77b,.outerContainer__5a77b { - align-items: flex-start; - display: flex; - flex-direction: row; - flex-wrap: wrap -} - -.languageItem__5a77b { - padding-inline-start:4px} - -.languageItem__5a77b:before { - color: var(--text-muted); - content: "•"; - display: inline-block; - margin-inline-end:4px;text-align: center; - width: 1ch -} - -.sideContainer_de3a16 { - display: flex; - flex-direction: column; - gap: 32px -} - -.gridContainer_de3a16 { - display: grid; - gap: 24px; - grid-template-columns: repeat(auto-fit,minmax(180px,1fr)) -} - -.guildCountContainer_de3a16 { - display: flex; - gap: 4px -} - -.guildsIconContainer_de3a16 { - height: 18px; - justify-self: center -} - -.sectionContainer_de3a16 { - display: flex; - flex-direction: column; - gap: 32px -} - -.sectionContainer_de3a16.reducedGap_de3a16 { - gap: 24px -} - -.infoSection_de3a16 { - display: flex; - flex-direction: column; - gap: 12px -} - -.infoSection_de3a16:empty { - display: none -} - -.linkGrid_de3a16 { - display: grid; - gap: 8px; - grid-template-columns: 1fr 1fr -} - -.overviewContainer_c4b47c { - display: flex; - flex-direction: column; - gap: 8px -} - -.descriptionClamp_c4b47c { - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 12; - overflow: hidden -} - -.descriptionClampSafari_c4b47c { - max-height: 300px -} - -.detailedDescription_c4b47c { - color: var(--text-default)!important; - font-size: 16px; - line-height: 20px!important -} - -.detailedDescription_c4b47c>div { - margin-bottom: 24px -} - -.detailedDescription_c4b47c blockquote,.detailedDescription_c4b47c pre,.detailedDescription_c4b47c table { - margin: 24px 0!important -} - -.detailedDescription_c4b47c blockquote,.detailedDescription_c4b47c ol,.detailedDescription_c4b47c ul { - margin-inline-start:24px!important} - -.detailedDescription_c4b47c img { - max-width: 100% -} - -.showMoreButton_c4b47c { - cursor: pointer; - gap: 16px; - margin: 8px 0 -} - -.showMoreButton_c4b47c,.showMoreContent_c4b47c { - align-items: center; - display: flex; - flex-direction: row -} - -.showMoreContent_c4b47c { - gap: 8px -} - -.showMoreButtonInner_c4b47c { - align-items: center; - display: flex; - justify-content: center -} - -.showMoreButtonIcon_c4b47c { - color: var(--text-default); - height: 18px; - width: 18px -} - -.divider_c4b47c { - background: var(--border-strong,hsla(229,7%,45%,.22)); - flex: 1; - height: 1px -} - -.list__83c7c { - display: flex; - flex-direction: column; - gap: 12px -} - -.container__83c7c { - display: grid; - gap: 10px; - grid-template-columns: 1fr 1fr 1fr -} - -.header__83c7c { - text-transform: capitalize -} - -.permission__83c7c { - display: flex -} - -.check__83c7c { - color: var(--status-positive) -} - -.check__83c7c,.cross__83c7c { - flex-shrink: 0; - margin-inline-end:8px} - -.cross__83c7c { - color: var(--icon-feedback-critical) -} - -.sectionContainer_f8758b { - display: flex; - flex-direction: column; - gap: 12px -} - -.contentContainer_f8758b { - background-color: var(--background-base-lower); - border: 1px solid var(--border-subtle); - border-radius: 8px; - padding: 16px -} - -.aboutContainer__17a6d { - display: flex; - flex-direction: column; - gap: 32px -} - -.divider__17a6d { - border-top: 1px solid var(--border-strong); - margin: 16px 0 -} - -.informationContainer__17a6d { - display: none -} - -@container apps-root-container (max-width: 700px) { - .informationContainer__17a6d { - display: flex; - flex-direction: column; - gap: 12px - } -} - -.actionContainer_adfd31 { - flex-direction: row; - gap: 8px; - height: -moz-fit-content; - height: fit-content -} - -.iconButton_adfd31 { - box-sizing: border-box; - height: 38px; - width: 38px -} - -.iconButton_adfd31.small_adfd31 { - height: 32px; - width: 32px -} - -.actionContainer_adfd31.wide_adfd31,.innerIconButton_adfd31 { - display: flex -} - -.actionContainer_adfd31.small_adfd31,.actionContainer_adfd31.tiny_adfd31 { - display: none -} - -@container apps-root-container (max-width: 700px) { - .actionContainer_adfd31.wide_adfd31 { - display: none - } - - .actionContainer_adfd31.small_adfd31 { - display: flex - } - - .actionContainer_adfd31.tiny_adfd31 { - display: none - } -} - -@container apps-root-container (max-width: 455px) { - .actionContainer_adfd31.small_adfd31,.actionContainer_adfd31.wide_adfd31 { - display: none - } - - .actionContainer_adfd31.tiny_adfd31 { - display: flex - } -} - -.avatarContainer_efaad4 { - position: relative -} - -.avatar_efaad4 { - -webkit-user-drag: none; - background: var(--background-gradient-chat,var(--background-base-low)); - border: solid; - border-color: var(--background-gradient-chat,var(--background-base-low)); - border-radius: var(--radius-xl); - transform: translateY(-50%) -} - -.infoContainer_efaad4 { - display: flex; - flex-direction: row; - gap: 24px; - justify-content: space-between; - padding: 20px 32px 16px -} - -.metadataContainer_efaad4 { - display: flex; - flex-direction: column; - gap: 4px; - min-width: 0 -} - -.disclosuresContainer_efaad4 { - display: flex; - flex-direction: row; - gap: 8px -} - -.disclosuresText_efaad4 { - white-space: pre -} - -.sectionContainer__5a4b6 { - display: flex; - flex-direction: column; - gap: 16px -} - -.divider__5a4b6 { - border-top: 1px solid var(--border-strong); - margin: 16px 0 -} - -.sectionHeader__5a4b6 { - text-transform: capitalize -} - -.contentContainer__5a4b6 { - display: grid; - gap: 16px; - grid-template-columns: repeat(auto-fit,minmax(230px,1fr)) -} - -.appContainer__5a4b6 { - background-color: var(--background-base-lower); - border: 1px solid var(--border-subtle); - border-radius: 8px; - cursor: pointer; - display: flex; - flex: 1; - flex-direction: column; - gap: 16px; - min-width: 0; - padding: 16px -} - -.full-motion .appContainer__5a4b6 { - transition: box-shadow .2s ease-out,transform .2s ease-out,background .2s ease-out -} - -.appContainer__5a4b6:focus,.appContainer__5a4b6:hover { - background-color: var(--background-base-lowest) -} - -.full-motion .appContainer__5a4b6:focus,.full-motion .appContainer__5a4b6:hover { - box-shadow: var(--shadow-border),var(--shadow-high); - transform: translateY(-1px) -} - -.appHeader__5a4b6 { - align-items: center; - display: flex; - flex-direction: row; - gap: 16px -} - -.avatar__5a4b6 { - -webkit-user-drag: none; - border-radius: var(--radius-md) -} - -.titleContainer__5a4b6 { - display: flex; - flex-direction: column; - gap: 4px; - min-width: 0 -} - -.appName__5a4b6 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.appCategory__5a4b6 { - white-space: pre -} - -.appDescription__5a4b6 { - min-height: 0; - overflow: hidden; - text-overflow: ellipsis -} - -.wrapper_a4e118 { - align-items: center; - background-color: var(--background-base-lowest); - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - border-radius: var(--radius-sm); - box-sizing: border-box; - display: flex; - gap: 24px; - height: 200px; - overflow: hidden; - padding: 16px 24px; - position: relative; - width: 100% -} - -.wrapper_a4e118:hover .image_a4e118 { - background-image: var(--custom-background-animated,var(--custom-background-static)) -} - -.content_a4e118 { - align-items: center; - display: flex; - gap: 16px; - max-width: 304px; - min-width: 260px; - z-index: 1 -} - -@value iconSize: 64px;.icon_a4e118 { - border-radius: var(--radius-md); - flex-shrink: 0; - overflow: hidden -} - -.icon_a4e118,.icon_a4e118 img { - height: 64px; - width: 64px -} - -.image_a4e118 { - background-image: var(--custom-background-static); - border-radius: var(--radius-sm); - flex: 1; - height: 100%; - transition: background-image .2s ease; - z-index: 1 -} - -.background_a4e118,.image_a4e118 { - background-position: 50%; - background-repeat: no-repeat; - background-size: cover -} - -.background_a4e118 { - --custom-overlay-color: hsl(var(--primary-730-hsl)/0.8); - background-image: linear-gradient(var(--custom-overlay-color),var(--custom-overlay-color)),var(--custom-background-url); - filter: blur(4px); - height: 120%; - left: 50%; - position: absolute; - top: 50%; - transform: translate3d(-50%,-50%,0); - width: 120% -} - -.theme-light .background_a4e118 { - --custom-overlay-color: hsl(var(--white-500-hsl)/0.3) -} - -.wrapper__5c108 { - background-color: var(--card-background-default); - border: 1px solid var(--border-subtle); - border-radius: 8px; - overflow: hidden; - width: 100% -} - -.cardHeaderImg__5c108 { - align-items: center; - background-color: var(--brand-400); - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - display: flex; - height: 187px; - justify-content: center; - width: 100% -} - -.cardHeaderImg__5c108 img { - border-radius: 100%; - box-shadow: 0 0 4px 0 var(--brand-500); - overflow: hidden -} - -.details__5c108 { - align-items: flex-start; - align-self: stretch; - display: flex; - flex-direction: column; - gap: 10px; - padding: 16px -} - -.description__5c108 { - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - display: -webkit-box; - height: 36px; - overflow: hidden; - width: 100% -} - -.cardPrice__5c108 { - box-sizing: border-box -} - -.cardPrice__5c108,.footer__5c108 { - align-items: center; - display: flex; - justify-content: space-between; - padding: 16px -} - -.footer__5c108 { - align-self: stretch; - border-top: 1px solid var(--border-subtle) -} - -.buttonGroup__5c108 { - align-items: center; - display: flex; - gap: 8px -} - -.heading__66230 { - align-items: center; - display: flex; - gap: 10px -} - -.wrapper__29f4e { - padding: 16px 0 -} - -.productsContainer__29f4e,.wrapper__29f4e { - display: flex; - flex-direction: column; - gap: 40px -} - -@value gridSize: 287px;.productSection__29f4e { - display: grid; - gap: 16px; - grid-template-columns: 287px; - justify-items: center; - padding-top: 16px -} - -@media (min-width: 760px) { - .productSection__29f4e { - grid-template-columns:287px 287px - } -} - -@media (min-width: 1220px) { - .productSection__29f4e { - grid-template-columns:287px 287px 287px - } -} - -@media (min-width: 1600px) { - .productSection__29f4e { - grid-template-columns:287px 287px 287px 287px - } -} - -.subscriptionCardSubtext__29f4e { - align-items: center; - display: flex; - gap: 5px -} - -.storeContainer_c79ea3 { - display: flex; - flex-direction: column; - gap: 32px -} - -.header_c79ea3 { - color: var(--text-strong) -} - -.productSection_c79ea3 { - display: flex; - flex-direction: column; - gap: 16px -} - -.products_c79ea3 { - display: grid; - gap: 16px 24px; - grid-template-columns: repeat(auto-fill,minmax(230px,1fr)) -} - -.legalContainer_c79ea3 { - display: flex; - justify-content: center; - width: 100% -} - -.detailContainer__871ff { - min-width: 320px; - width: 100% -} - -.contentContainer__871ff { - display: flex; - flex-direction: row; - gap: 48px; - padding: 0 32px -} - -.contentTabsContainer__871ff { - display: flex; - flex: 1; - flex-direction: column; - gap: 16px; - min-width: 0; - padding: 0 0 32px -} - -.contentTabs__871ff { - flex-direction: row; - justify-content: space-between -} - -.centerContainer__871ff,.contentTabs__871ff { - align-items: center; - display: flex -} - -.centerContainer__871ff { - height: 100%; - justify-content: center; - min-height: 0; - width: 100% -} - -.error__871ff { - padding: 16px 32px -} - -.sidebar__871ff { - padding-bottom: 32px; - width: 240px -} - -@container apps-root-container (max-width: 700px) { - .sidebar__871ff { - display: none - } -} - -.sectionTitle_d2a5f7 { - padding-top: 16px; - padding-inline-start:32px;z-index: 1 -} - -.titleExtraPadding_d2a5f7 { - padding-top: 24px -} - -.content_d2a5f7 { - -moz-column-gap: 16px; - column-gap: 16px; - display: grid; - grid-auto-rows: 1fr; - grid-template-columns: repeat(1,minmax(200px,1fr)); - padding: 16px 32px; - row-gap: 16px -} - -@media (min-width: 800px) { - .content_d2a5f7 { - grid-template-columns:repeat(2,1fr) - } -} - -@media (min-width: 1200px) { - .content_d2a5f7 { - grid-template-columns:repeat(4,1fr) - } -} - -.errorContainer_d2a5f7 { - display: flex; - flex: 1 -} - -.error_d2a5f7 { - padding: 16px 32px -} - -.contentContainer__77062 { - display: flex; - flex-direction: column; - gap: 16px; - min-height: 100%; - width: 100% -} - -.filter__72086 { - cursor: pointer; - display: flex; - gap: 8px -} - -.filterBackground__72086 { - background-color: var(--background-mod-subtle); - border-radius: 4px; - padding: 4px 8px -} - -.filterBackground__72086:hover { - background-color: var(--interactive-background-hover) -} - -.filterBackground__72086:active { - background-color: var(--interactive-background-active) -} - -.menu__72086 { - max-height: 300px -} - -.categoryLabel__72086 { - display: flex; - gap: 16px; - justify-content: space-between -} - -.categories__97499 { - display: flex; - flex-direction: column; - gap: 2px -} - -.category__97499 { - align-items: center; - border-radius: 4px; - cursor: pointer; - display: flex; - justify-content: space-between; - padding: 8px -} - -.category__97499:hover { - background-color: var(--interactive-background-hover) -} - -.name__97499 { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.count__97499 { - margin-inline-start:8px} - -.selected__97499 { - background-color: var(--interactive-background-active) -} - -.container__97499 { - align-items: center; - display: flex; - gap: 8px -} - -.container__5682d { - align-items: center; - background: var(--background-gradient-low,var(--background-base-lower)); - border-radius: 8px; - display: flex; - flex-direction: column; - height: 400px; - justify-content: center; - padding: 16px -} - -.image__5682d { - background-repeat: no-repeat; - background-size: contain; - height: 145px; - margin-bottom: 16px; - width: 314px -} - -.header__5682d { - margin-bottom: 8px -} - -@value minCardWidth 254px;@value sidebarWidth 170px;.container__1eae0 { - display: flex; - flex: 1; - min-height: 0; - position: relative -} - -.innerContainer__1eae0 { - display: flex; - flex-direction: row -} - -.centeringBuffer__1eae0 { - width: 158px -} - -.contentContainer__1eae0 { - padding: 32px 32px 16px; - padding-inline-end:194px} - -.content__1eae0 { - -moz-column-gap: 16px; - column-gap: 16px; - display: grid; - grid-auto-rows: 1fr; - grid-template-columns: repeat(auto-fill,minmax(254px,1fr)); - row-gap: 16px -} - -.paginationInput__1eae0 { - margin: 0 -} - -.topFilterContainer__1eae0 { - display: none; - margin-bottom: 16px -} - -.sideFilterContainer__1eae0 { - height: 100%; - inset-inline-end: 0; - margin-top: 32px; - padding-inline-end:16px;position: absolute; - top: 0; - width: 170px; - z-index: 1 -} - -.sideFilterContent__1eae0 { - position: sticky; - top: 0 -} - -@media (max-width: 1336px) { - .centeringBuffer__1eae0 { - display:none - } - - .contentContainer__1eae0 { - padding-inline-end:32px;padding-top: 24px - } - - .topFilterContainer__1eae0 { - display: block - } - - .sideFilterContainer__1eae0 { - display: none - } -} - -.outerContainer_e1147e { - display: flex; - flex-direction: column; - height: 100% -} - -.loggedOutContainer_e1147e { - height: 100vh -} - -.innerContainer_e1147e { - container-name: apps-root-container; - container-type: inline-size; - display: flex; - flex: 1; - flex-direction: column; - min-height: 0; - position: relative -} - -.detailHeaderContainer_e1147e { - align-items: center; - display: grid; - flex: 1; - gap: 20px; - grid-template-columns: 1fr auto 1fr; - height: 100% -} - -.detailHeaderSection_e1147e { - align-items: center; - display: flex; - height: 100% -} - -.detailHeaderButtonsContainer_e1147e { - justify-content: flex-end; - transition: opacity .2s -} - -.detailHeaderButtonsContainer_e1147e.hidden_e1147e { - opacity: 0; - visibility: hidden -} - -.detailHeaderButtonsContainer_e1147e.hide_e1147e { - opacity: 0 -} - -.detailHeaderButtonsContainer_e1147e.visible_e1147e { - opacity: 1; - visibility: visible -} - -.detailHeader_e1147e,.searchHeader_e1147e { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.searchHeader_e1147e { - flex: 1; - min-width: 0 -} - -.nagbar_e1147e { - padding-inline:28px} - -.nagbarActionContainer_e1147e { - display: inline-block -} - -.logo_e1147e { - height: 22px; - margin-inline-end:8px;position: relative; - top: 6px; - width: 22px -} - -/*# sourceMappingURL=4851cc625502983d.css.map*/ diff --git a/discord-html-copy/css/587404af83693d7c.css b/discord-html-copy/css/587404af83693d7c.css deleted file mode 100644 index 0b6b151..0000000 --- a/discord-html-copy/css/587404af83693d7c.css +++ /dev/null @@ -1,1447 +0,0 @@ -.buttonContainer__7e329 { - align-items: center; - display: flex; - flex-direction: column; -} - -.keybind__7e329 { - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-sizing: border-box; - margin-top: var(--space-4); - padding: 2px var(--space-4); -} - -.button__7e329, .keybind__7e329 { - background-color: rgba(19, 19, 24, 0.8); -} - -.button__7e329 { - align-items: center; - border: 1px solid var(--border-subtle); - border-radius: 50%; - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - flex: 0 0 48px; - height: 48px; - justify-content: center; - width: 48px; -} - -.button__7e329:active, .button__7e329:hover { - background-color: rgba(28, 29, 35, 0.8); -} - -.button__7e329:active, .button__7e329:active .keybind__7e329, .button__7e329:hover, .button__7e329:hover .keybind__7e329 { - color: var(--interactive-text-hover); -} - -.button__7e329:active { - transform: translateY(1px); -} - -.overlayBackgroundNux__3db18 { - background: radial-gradient(50% 100% at 50% 100%, rgba(88, 101, 242, 0.4) 0px, rgba(88, 101, 242, 0) 100%); - height: 360px; -} - -.overlayBackgroundNux__3db18, .overlayInprocessBackgroundNux__3db18 { - bottom: 0px; - inset-inline-start: 50%; - padding: 0px 60px; - position: absolute; - transform: translateX(-384px); - width: 648px; -} - -.overlayInprocessBackgroundNux__3db18 { - background: radial-gradient(50% 80% at 50% 100%, rgba(88, 101, 242, 0.4) 0px, rgba(88, 101, 242, 0) 100%); - height: 660px; -} - -.mainContainer__3db18 { - gap: 40px; - height: 100%; - padding-top: 60px; - width: 100%; -} - -.mainContainer__3db18, .mainTitleContainer__3db18 { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; -} - -.mainTitleContainer__3db18 { - gap: 8px; - text-align: center; - width: 360px; -} - -.widgetCalloutContainer__3db18 { - flex-direction: row; - gap: 16px; -} - -.widgetCallout__3db18, .widgetCalloutContainer__3db18 { - align-items: center; - display: flex; - justify-content: center; -} - -.widgetCallout__3db18 { - flex-direction: column; - gap: 8px; - text-align: center; - width: 150px; -} - -.nuxImage__3db18 { - height: 188px; - width: 648px; -} - -.mainTitleBody__3db18 strong { - color: var(--white) !important; -} - -.mainTitle__3db18 { - width: 200px; -} - -.wrapper__58ece { - background-color: transparent; - border-color: transparent; - border-style: dashed; - box-sizing: border-box; - position: absolute; -} - -.wrapper__58ece.unlocked__58ece.lockExtras__58ece, .wrapper__58ece.unlocked__58ece.operation__58ece, .wrapper__58ece.unlocked__58ece:hover { - background-color: rgba(0, 0, 0, 0.16); - border-color: rgba(255, 255, 255, 0.16); -} - -.operation__58ece > * { - pointer-events: none; -} - -.handle__58ece { - position: absolute; - top: 0px; - inset-inline: 0px; - bottom: 0px; - box-sizing: border-box; - height: 100%; - width: 100%; - z-index: 1; -} - -.resizeNorth__58ece { - bottom: auto; - top: var(--custom-drag-resize-container-handle-offset); -} - -.resizeNorth__58ece, .resizeSouth__58ece { - height: var(--custom-drag-resize-container-handle-size); -} - -.resizeSouth__58ece { - bottom: var(--custom-drag-resize-container-handle-offset); - top: auto; -} - -.resizeNSCursor__58ece { - cursor: ns-resize; -} - -.resizeWest__58ece { - inset-inline: var(--custom-drag-resize-container-handle-offset) auto; -} - -.resizeEast__58ece, .resizeWest__58ece { - cursor: ew-resize; - width: var(--custom-drag-resize-container-handle-size); -} - -.resizeEast__58ece { - inset-inline: auto var(--custom-drag-resize-container-handle-offset); -} - -.resizeEWCursor__58ece { - cursor: ew-resize; -} - -.resizeNorthWest__58ece { - cursor: nwse-resize; - inset-inline: var(--custom-drag-resize-container-handle-offset) auto; -} - -.resizeNorthEast__58ece, .resizeNorthWest__58ece { - bottom: auto; - height: calc(var(--custom-drag-resize-container-handle-size)*2); - top: var(--custom-drag-resize-container-handle-offset); - width: calc(var(--custom-drag-resize-container-handle-size)*2); -} - -.resizeNorthEast__58ece { - cursor: nesw-resize; - inset-inline: auto var(--custom-drag-resize-container-handle-offset); -} - -.resizeSouthWest__58ece { - cursor: nesw-resize; - inset-inline: var(--custom-drag-resize-container-handle-offset) auto; -} - -.resizeSouthEast__58ece, .resizeSouthWest__58ece { - bottom: var(--custom-drag-resize-container-handle-offset); - height: calc(var(--custom-drag-resize-container-handle-size)*2); - top: auto; - width: calc(var(--custom-drag-resize-container-handle-size)*2); -} - -.resizeSouthEast__58ece { - inset-inline: auto var(--custom-drag-resize-container-handle-offset); -} - -.resizeNWSECursor__58ece, .resizeSouthEast__58ece { - cursor: nwse-resize; -} - -.resizeNESWCursor__58ece { - cursor: nesw-resize; -} - -.extras__58ece { - --custom-offset: 0; - box-sizing: content-box; - display: flex; - flex-direction: row; - inset-inline-start: 0px; - justify-content: flex-start; - max-width: 100%; - min-width: 100%; - padding-top: 10px; - position: absolute; - top: 100%; - transform-origin: 0px 100%; -} - -.extras__58ece, .wrapper__58ece.operation__58ece .extras__58ece, .wrapper__58ece.operation__58ece:hover .extras__58ece { - opacity: 0; - transform: translate3d(0,calc(var(--custom-offset)*-1),0) scale(.98); -} - -.extras__58ece { - transition: opacity 0.3s cubic-bezier(0.16, 1, 0.3, 1); -} - -.full-motion .extras__58ece { - --custom-offset: 6px; - transition: opacity 0.3s cubic-bezier(0.16, 1, 0.3, 1), transform 0.3s ease-out; -} - -.wrapper__58ece.unlocked__58ece.lockExtras__58ece .extras__58ece, .wrapper__58ece.unlocked__58ece:hover .extras__58ece { - opacity: 1; - transform: translateZ(0px) scale(1); -} - -.wrapper__58ece.forceShowExtras__58ece .extras__58ece, .wrapper__58ece.forceShowExtras__58ece:hover .extras__58ece { - opacity: 1; - transform: translate3d(0px, 8px, 0px) scale(1); -} - -.full-motion .wrapper__58ece.forceShowExtras__58ece .extras__58ece, .full-motion .wrapper__58ece.forceShowExtras__58ece:hover .extras__58ece { - transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out; -} - -.wrapper__58ece.locked__58ece.forceShowExtras__58ece { - background-color: rgba(0, 0, 0, 0.16); - border-color: rgba(255, 255, 255, 0.16); -} - -.extrasBottomOriented__58ece { - bottom: 100%; - padding-bottom: 10px; - padding-top: 0px; - top: auto; -} - -.extrasBottomOriented__58ece, .wrapper__58ece.operation__58ece .extrasBottomOriented__58ece, .wrapper__58ece.operation__58ece:hover .extrasBottomOriented__58ece { - transform: translate3d(0,var(--custom-offset),0) scale(.98); -} - -.extrasBottomOriented__58ece { - transform-origin: 0px 0px; -} - -.extrasRightOriented__58ece { - transform-origin: 100% 100%; -} - -.extrasBottomOriented__58ece.extrasRightOriented__58ece { - transform-origin: 100% 0px; -} - -.extrasRightOriented__58ece { - inset-inline-end: 0px; - inset-inline-start: unset; - justify-content: flex-end; -} - -.extrasContainer__19b74 { - display: flex; - flex-direction: row; - gap: var(--space-4); - min-width: 0px; - padding-inline: var(--space-8); } - -.fullFlex__19b74 { - flex: 1 1 0%; -} - -.debug__19b74::after { - content: ""; - position: absolute; - top: 0px; - inset-inline: 0px; - bottom: 0px; - pointer-events: none; - visibility: visible; -} - -.debugUnpinned__19b74::after { - border: 2px solid var(--primary-300); -} - -.debugPinned__19b74::after { - border: 2px solid var(--brand-600); -} - -.container__2af41 { - background-color: rgba(19, 19, 24, 0.95); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-border) var(--shadow-high); - box-sizing: border-box; - flex: 1 1 0%; - height: 100%; - max-width: 300px; - padding: var(--space-12); -} - -.activityCard__2af41 { - background-color: transparent; - flex: 1 1 0%; - padding: 0px !important; -} - -.contentInventoryContainer__2af41 { - border-top: 1px solid var(--background-mod-strong); - margin-top: var(--space-12); - padding-top: var(--space-12); -} - -.contentInventoryHeader__2af41 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-4); - padding-bottom: var(--space-8); -} - -.activityCard__2af41 + .row__2af41, .inviteFriendsButton__2af41 + .row__2af41 { - border-top: 1px solid var(--background-mod-strong); - margin-top: var(--space-12); - padding-top: var(--space-12); -} - -.title__2af41 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.avatar__2af41 { - flex-shrink: 0; -} - -.row__2af41 { - align-items: center; - display: flex; - justify-content: center; -} - -.row__2af41 + .row__2af41 { - margin-top: var(--space-12); -} - -.details__2af41 { - flex-grow: 1; - margin: 0 var(--space-12); - overflow: hidden; -} - -.usernameWrapper__2af41 { - align-items: center; - display: flex; -} - -.username__2af41 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.inviteButtons__2af41 { - display: flex; - flex-direction: row; - gap: var(--space-8); -} - -.inviteFriendsButton__2af41 { - margin-top: var(--space-12); - margin-inline-start: 68px; } - -.liveIndicator__2af41 { - margin-inline-start: var(--space-8); } - -.badgesContainer__2af41 { - flex-wrap: wrap; -} - -.clickZone__29c87, .clickZoneDebugContainer__29c87 { - height: 100%; - width: 100%; -} - -.clickZone__29c87 { - align-items: center; - background-color: rgba(255, 0, 0, 0.25); - cursor: pointer; - display: flex; - justify-content: center; - transition: background-color 0.2s ease-in-out; -} - -.clickBackground__29c87 { - background-color: hsl(var(--brand-500-hsl)/.9); -} - -.clickable__29c87 { - padding: 12px; -} - -.streamTile__8151b, .streamTileZoomWrapper__8151b, .tile__8151b { - aspect-ratio: 16 / 9; - border-radius: 8px; - flex: 1 1 0%; - height: auto; - overflow: hidden; - position: relative; - width: 100%; -} - -.streamTileWrapper__8151b { - background: transparent; -} - -.controls__8151b { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px; - height: 38px; - justify-content: space-between; - padding: 8px; - top: auto; -} - -.controlBackground__8151b, .controls__8151b { - bottom: 0px; - inset-inline: 0px; - position: absolute; -} - -.controlBackground__8151b { - backdrop-filter: blur(2px); - background: var(--background-scrim); - opacity: 1; - pointer-events: none; - top: 0px; - transition: opacity 0.2s ease-in-out; -} - -.controlBackground__8151b.controlsWithActiveStream__8151b { - opacity: 0; -} - -.controlUser__8151b { - align-items: center; - display: flex; - flex: 1 1 0%; - flex-direction: row; - min-width: 0px; -} - -.controlUserContainer__8151b { - align-items: center; - backdrop-filter: blur(4px); - background: var(--background-scrim); - border-radius: var(--radius-md); - display: flex; - flex-direction: row; - gap: 4px; - max-width: 90%; - padding: 6px 8px; -} - -.controlUserName__8151b, .guildTag__8151b { - overflow: hidden; - white-space: nowrap; -} - -.controlUserName__8151b { - max-width: 100%; - text-overflow: ellipsis; -} - -.controlAction__8151b { - backdrop-filter: blur(4px); - background: var(--background-scrim); - border-radius: var(--radius-round); - cursor: pointer; - height: 32px; - justify-content: center; - width: 32px; -} - -.controlAction__8151b, .controlActions__8151b { - align-items: center; - display: flex; -} - -.controlActions__8151b { - flex-direction: row; - flex-shrink: 0; - gap: 8px; - justify-content: flex-end; -} - -.absoluteFill__8151b, .streamPreview__8151b { - position: absolute; - top: 0px; - inset-inline: 0px; - bottom: 0px; -} - -.streamPreview__8151b { - align-items: center; - background: var(--background-scrim); - display: flex; - justify-content: center; - opacity: 0.65; - overflow: hidden; - transition: opacity 0.2s ease-in-out; -} - -.liveIndicator__8151b { - inset-inline-end: 12px; - position: absolute; - top: 12px; - z-index: 10; -} - -.watchActionContainer__8151b { - align-items: center; - position: absolute; - top: 0px; - inset-inline: 0px; - bottom: 0px; - display: flex; - justify-content: center; - z-index: 10; -} - -.watchButton__8151b { - align-items: center; - background: var(--background-mod-strong); - border-radius: var(--radius-lg); - cursor: pointer; - display: flex; - flex-direction: row; - gap: 4px; - justify-content: flex-end; - padding: 8px 12px; -} - -.gridContainer_ad58e7 { - align-items: flex-start; - display: flex; - flex: 1 1 0%; - gap: var(--space-8); - height: 100%; - justify-content: flex-start; - position: relative; - width: 100%; - z-index: 1; -} - -.gridItem_ad58e7 { - position: absolute; -} - -.horizontal_ad58e7 { - flex-direction: row; -} - -.vertical_ad58e7 { - flex-direction: column; -} - -.tileContainer_ad58e7 { - height: 100%; - position: relative; - width: 100%; -} - -.tile_ad58e7 { - aspect-ratio: 16 / 9; - border-radius: var(--radius-sm); - height: auto; - max-height: 100%; - overflow: hidden; - width: 100%; -} - -.goLiveGridContainer__466c6 { - height: 100%; - min-width: 128px; - position: relative; - width: 100%; -} - -.panel__2f37f { - gap: 16px; - padding: 8px; -} - -.panel__2f37f, .panelGroup__2f37f { - display: flex; - flex-direction: column; -} - -.panelGroup__2f37f { - border-radius: 8px; - gap: 4px; - padding: 12px; - width: 300px; -} - -.panelGroup__2f37f span { - transition: color 0.4s ease-in-out; -} - -.rightAligned__2f37f { - align-items: flex-end; -} - -.measurement__2f37f { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px; -} - -.measurementText__2f37f { - flex: 1 1 0%; - text-shadow: rgb(0, 0, 0) 0px 0px 2px; -} - -.measurementCheckbox__2f37f { - flex: 0 1 0%; -} - -.bottomPanelButton__2f37f { - padding-top: 12px; -} - -.secondaryInfoText__2f37f { - margin-inline-start: 8px; } - -.toggleIconContainer__46219 { - align-items: center; - cursor: pointer; - display: flex; - height: 16px; - justify-content: center; - width: 16px; -} - -.button__46219 { - position: relative; - border: none !important; -} - -.cutoutContainer__46219 { - align-items: center; - background: var(--background-mod-subtle); - border: 3px solid var(--black); - border-radius: var(--radius-round); - bottom: -2px; - cursor: pointer; - display: flex; - height: 16px; - inset-inline-end: -2px; - justify-content: center; - position: absolute; - width: 16px; -} - -.toggledCutoutContainer__46219 { - background: var(--white); -} - -.separatorDot__46219 { - background: var(--background-mod-strong); - border-radius: var(--radius-round); - height: 4px; - width: 4px; -} - -.guildIconContainer__46219 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-4); - justify-content: center; -} - -.guildName__46219 { - max-width: 180px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.guildIcon__46219 { - height: 16px; - width: 16px; -} - -.voiceChannelNameContainer__46219 { - gap: 4px; -} - -.channelIcon__46219, .voiceChannelNameContainer__46219 { - align-items: center; - display: flex; - flex-direction: row; - justify-content: center; -} - -.channelIcon__46219 { - height: 16px; - width: 16px; -} - -.channelName__46219 { - align-items: center; - display: flex; - flex-direction: row; - justify-content: center; - max-width: 180px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.panelContainer_a83780 { - align-items: center; - background: var(--black); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - display: flex; - flex-direction: column; - gap: 8px; - justify-content: center; - padding: 8px; -} - -.locked_a83780 { - display: none; - pointer-events: none; -} - -.buttonSection_a83780, .panelRow_a83780 { - align-items: center; - display: flex; - flex-direction: row; - gap: 12px; - justify-content: center; -} - -.buttonSection_a83780 { - background: var(--background-mod-subtle); - border: 1px solid var(--border-muted); - border-radius: 12px; - padding: 4px; -} - -.inCall_a83780 { - background: var(--background-feedback-critical); -} - -:root { - --custom-bg-surface-overlay: rgba(33,34,41,.8); -} - -.container__31961 { - align-items: stretch; - display: flex; - flex-direction: row; - height: calc(100% - 6px); - margin-inline-start: -12px; padding-bottom: 4px; - padding-inline-end: 0px; padding-top: 4px; - width: calc(100% + 10px); -} - -.topBar__31961 { - align-items: center; - background-color: rgba(19, 19, 24, 0.95); - border-bottom: 1px solid var(--background-mod-strong); - cursor: grab; - display: flex; - flex: 0 0 auto; - gap: var(--space-8); - height: 40px; - inset-inline: 0px; - justify-content: space-between; - padding: 4px 12px 8px; - position: absolute; - top: 0px; - z-index: 1; -} - -.topBar__31961.dragging__31961 { - cursor: grabbing; -} - -.headerLeft__31961 { - gap: var(--space-8); -} - -.headerLeft__31961, .headerTitleRow__31961 { - align-items: center; - display: flex; - flex: 1 1 auto; - min-width: 0px; -} - -.headerTitleRow__31961 { - gap: var(--space-4); -} - -.parentChannelLink__31961 { - color: var(--text-muted); - cursor: pointer; - flex: 0 1 auto; - min-width: 0px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.parentChannelSeparator__31961 { - color: var(--text-muted); - flex: 0 0 auto; -} - -.headerRight__31961 { - align-items: center; - display: inline-flex; - flex: 0 0 auto; - gap: var(--space-4); -} - -.title__31961 { - flex: 1 1 auto; - min-width: 0px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.currentlyPlaying__31961 { - margin-inline-start: 4px; } - -.titleIcon__31961 { - justify-content: center; -} - -.sidebar__31961, .titleIcon__31961 { - align-items: center; - display: flex; -} - -.sidebar__31961 { - background-color: transparent; - flex: 0 0 auto; - flex-direction: column; - gap: 4px; - overflow: visible; - padding: 0px; -} - -.sidebarSeparator__31961 { - border-right: none; - border-bottom: none; - border-left: none; - border-image: initial; - border-top: 1px solid var(--background-mod-strong); - margin: 2px auto; - margin-inline-start: 20px; opacity: 0.8; - width: 32px; -} - -.chatArea__31961 { - background-color: rgba(19, 19, 24, 0.95); - border: 1px solid var(--border-subtle); - border-radius: 12px; - box-shadow: var(--shadow-border) var(--shadow-high); - box-sizing: border-box; - display: flex; - flex: 1 1 auto; - flex-direction: column; - min-height: 0px; - min-width: 0px; - overflow: hidden; - position: relative; -} - -.navItem__4ccc5 { - cursor: pointer; - overflow: visible; - padding: 8px 12px; - padding-inline: 16px 12px; } - -.navItem__4ccc5, .navItemIconBadgeContainer__4ccc5 { - align-items: center; - display: flex; - height: 40px; - justify-content: center; - position: relative; - width: 40px; -} - -.animatedHalfPillContainer__4ccc5 { - height: 40px; - inset-inline-start: 2px; - position: absolute; - top: 8px; - width: 4px; -} - -.tivNavItemIconContainer__4ccc5 { - --custom-interactive-normal: var(--interactive-normal); - align-items: center; - background-color: var(--custom-bg-surface-overlay); - border-radius: 12px; - color: var(--custom-interactive-normal); - display: flex; - height: 40px; - justify-content: center; - transition: box-shadow 0.16s ease-out; - width: 40px; -} - -.tivNavItemIconContainerSelected__4ccc5 { - background-color: hsl(var(--brand-500-hsl)/.9); - color: var(--white-500); -} - -.tivChannelTypeIcon__4ccc5 { - color: currentcolor; -} - -.hidden__4ccc5 { - display: none; -} - -.content__2d9cc { - padding: 0px; -} - -.chat__2d9cc, .content__2d9cc { - display: flex; - flex: 1 1 auto; - flex-direction: column; - min-height: 0px; - min-width: 0px; -} - -.chat__2d9cc { - position: relative; -} - -:root { - --custom-bg-surface-overlay: rgba(33,34,41,.8); -} - -.container__5b1fc { - align-items: stretch; - display: flex; - flex-direction: row; - height: calc(100% - 6px); - margin-inline-start: -12px; padding-bottom: 4px; - padding-inline-end: 0px; padding-top: 4px; - width: calc(100% + 10px); -} - -.sidebar__5b1fc { - align-items: center; - background-color: transparent; - display: flex; - flex: 0 0 auto; - flex-direction: column; - gap: 4px; - overflow: visible; - padding: 0px; -} - -.sidebarSeparator__5b1fc { - border-right: none; - border-bottom: none; - border-left: none; - border-image: initial; - border-top: 1px solid var(--background-mod-strong); - margin: 2px auto; - margin-inline-start: 20px; opacity: 0.8; - width: 32px; -} - -.chatArea__5b1fc { - background-color: rgba(19, 19, 24, 0.95); - border: 1px solid var(--border-subtle); - border-radius: 12px; - box-shadow: var(--shadow-border) var(--shadow-high); - box-sizing: border-box; - display: flex; - flex: 1 1 auto; - flex-direction: column; - min-height: 0px; - min-width: 0px; - overflow: hidden; - position: relative; -} - -.videoList__95c18 { - display: flex; - flex: 1 1 0%; - flex-direction: row; - gap: 8px; - height: 100%; - justify-content: center; - width: 100%; -} - -.videoList__95c18.vertical__95c18 { - flex-direction: column; -} - -.tile__95c18 { - border-radius: var(--custom-base-tile-border-radius); - overflow: hidden; -} - -.hidden__95c18 { - display: none; -} - -.container_ce59d6 { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px; - padding-inline-end: 4px; width: 100%; -} - -.body_ce59d6 { - display: flex; - flex-direction: column; - gap: 4px; -} - -.avatarContainer_ce59d6 { - min-width: 24px; -} - -.avatarContainer_ce59d6, .avatarWrapper_ce59d6 { - align-items: center; - display: flex; - justify-content: center; -} - -.avatarWrapper_ce59d6 { - height: 50px; - position: relative; - width: 50px; -} - -.gameIcon_ce59d6, .gameIconMask_ce59d6 { - height: 16px; - width: 16px; -} - -.gameIcon_ce59d6 { - border-radius: 50%; -} - -.watchButtonContainer_ce59d6 { - align-items: center; - display: block; - flex-direction: row; - height: 100%; - justify-content: center; -} - -.buttonDivider_ce59d6 { - background-color: var(--background-mod-subtle); - height: 32px; - width: 1px; -} - -.watchButton_ce59d6 { - align-self: flex-start; - background: var(--control-connected-background-default); - border-radius: 8px; - display: block; - margin-inline-end: 4px; padding: 6px 12px; -} - -.watchButtonContents_ce59d6 { - align-items: center; - display: flex; - flex: 0 1 0%; - flex-direction: row; - gap: 4px; -} - -.bodyText_ce59d6 { - flex: 1 1 0%; - min-width: 0px; - overflow: hidden; - text-overflow: ellipsis; - vertical-align: middle; -} - -.bodyText_ce59d6 strong { - color: var(--interactive-text-active); - font-weight: 500; -} - -.singleLineBody_ce59d6 { - flex: 1 1 0%; - flex-direction: row; - height: 100%; - hyphens: auto; - max-width: 100%; - overflow: hidden; - overflow-wrap: break-word; - text-overflow: ellipsis; - vertical-align: middle; - white-space: normal; - word-break: break-word; -} - -.singleLineBody_ce59d6 strong { - color: var(--interactive-text-active); -} - -.watchIconButton_ce59d6 { - align-items: center; - border-radius: 50%; - cursor: pointer; - display: flex; - height: 44px; - justify-content: center; - width: 44px; -} - -.watchIconButton_ce59d6:hover { - background-color: var(--background-mod-subtle); -} - -.watchIconButton_ce59d6:active { - background-color: var(--background-mod-muted); -} - -.greenButton_ce59d6 { - background-color: var(--control-connected-background-default); -} - -.greenButton_ce59d6:hover { - background-color: var(--control-connected-background-hover); -} - -.greenButton_ce59d6:active { - background-color: var(--control-connected-background-active); -} - -.grayButton_ce59d6 { - background-color: var(--control-secondary-background-default); -} - -.grayButton_ce59d6:hover { - background-color: var(--control-secondary-background-hover); -} - -.grayButton_ce59d6:active { - background-color: var(--control-secondary-background-active); -} - -.nowPlayingNotification__81a05 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px; - max-width: 100%; - width: max-content; -} - -.nowPlayingNotificationAnimationWrapper__81a05 { - background-color: var(--background-base-lowest); - inset-inline: auto; - opacity: 0.9; - border-radius: 300px !important; - width: max-content !important; -} - -.nowPlayingNotificationContainer__81a05 { - padding: 8px 12px !important; - width: max-content !important; -} - -.nowPlayingNotificationWrapper__81a05 { - overflow: visible !important; - position: relative !important; - width: max-content !important; -} - -.nowPlayingNotificationClickZone__81a05 { - position: relative; -} - -.body__81a05 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px; - padding-top: 1px; - padding-inline-end: 2px !important; } - -.nowPlayingNotificationIcon__81a05 { - align-items: center; - display: flex; - justify-content: center; - min-width: 24px; -} - -.gameIcon__81a05 { - border-radius: 2px; - display: inline-block; - margin-top: -1px; -} - -.bodyText__81a05, .gameIcon__81a05 { - vertical-align: middle; -} - -.bodyText__81a05 { - flex: 1 1 0%; - min-width: 0px; - overflow: hidden; - text-overflow: ellipsis; -} - -.bodyText__81a05 strong { - color: var(--interactive-text-active); -} - -.textArea_ab8e5c { - background: var(--background-base-low); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 6px; -} - -.container_ab8e5c { - padding-top: 12px; - width: 100%; -} - -.container__32c39 { - border-top: 1px solid var(--background-mod-strong); - margin-top: var(--space-12); - padding-top: var(--space-12); - width: 100%; -} - -.containerFaint__32c39 { - border-top: 1px solid var(--background-mod-muted); -} - -.button__32c39 { - border-radius: var(--radius-sm); -} - -.keybindStreamContainer__32c39 { - align-items: center; - display: flex; - flex-wrap: wrap; - gap: var(--space-8); - justify-content: space-between; - vertical-align: sub; -} - -.keybindShortcutReminder__32c39 { - color: var(--text-muted); - font-size: 16px; - font-weight: var(--font-weight-medium); - min-width: 0px; - overflow-wrap: break-word; -} - -.notificationContainer__32c39 { - padding: var(--space-16); -} - -.title__32c39 { - color: var(--text-default); - font-size: 16px; - font-weight: var(--font-weight-semibold); -} - -.footerSection__32c39 { - width: 100%; -} - -.container_fb1c43 { - display: flex; - flex-direction: column; - gap: var(--space-8); - min-height: 100px; - min-width: 300px; - position: relative; -} - -.container_c39fe3 { - align-items: center; - display: flex; - justify-content: center; - position: absolute; - top: 0px; - inset-inline: 0px; - bottom: 0px; - z-index: 9999; -} - -.videoDev_eb35c5 { - background-color: var(--black); - position: absolute; - top: 0px; - inset-inline: 0px; - bottom: 0px; - height: 100%; - object-fit: cover; - width: 100%; - z-index: -1; -} - -.overlay_eb35c5 { - color: var(--white); - cursor: auto; - position: fixed; -} - -.overlay_eb35c5, .overlayBackground_eb35c5 { - top: 0px; - inset-inline: 0px; - bottom: 0px; - pointer-events: none; -} - -.overlayBackground_eb35c5 { - background-color: var(--background-scrim); - opacity: 0; - position: absolute; - transition: opacity 0.2s; -} - -.overlayActive_eb35c5 { - opacity: 1; - pointer-events: auto; -} - -.overlayLocked_eb35c5 { - z-index: 1; -} - -.topRightContainer_eb35c5 { - display: flex; - gap: 8px; - inset-inline-end: 24px; - justify-content: end; - pointer-events: auto; - position: absolute; - top: 24px; - z-index: 2; -} - -.invalidContainer_eb35c5 { - align-items: center; - background-color: hsl(var(--primary-500-hsl)/.8); - position: absolute; - top: 0px; - inset-inline: 0px; - bottom: 0px; - display: flex; - justify-content: center; -} - -.inactiveContainer_eb35c5 { - background-color: var(--primary-700); - border-radius: 5px; - font-size: 18px; - line-height: 30px; - padding: 20px 20px 25px; - text-align: center; - width: 300px; -} - -.layoutLocked_eb35c5 { - inset-inline-start: 0px; - position: absolute; - top: 0px; -} - -.layoutLocked_eb35c5, .layoutLocked_eb35c5 > *, .layoutUnlocked_eb35c5 { - pointer-events: none; -} - -.layoutUnlocked_eb35c5 { -} - -.layoutUnlocked_eb35c5 > * { - pointer-events: auto; -} - -.windowContainerDebug_eb35c5 { - border: 2px solid var(--brand-600); - position: absolute; - top: 0px; - inset-inline: 0px; - bottom: 0px; -} diff --git a/discord-html-copy/css/59c6d21704b78874.css b/discord-html-copy/css/59c6d21704b78874.css deleted file mode 100644 index 08cc281..0000000 --- a/discord-html-copy/css/59c6d21704b78874.css +++ /dev/null @@ -1,160 +0,0 @@ -.loadingWrapper__5a143 { - align-items: center; - border-radius: 2px; - display: flex; - flex-direction: row; - height: 16px; - justify-content: center; - margin: 2px 0; - padding: 8px 4px -} - -.list_c47777 { - max-height: 500px -} - -.actionContainer_bc4513 { - background-color: var(--background-mod-normal); - border-radius: 80px; - flex-direction: row; - padding-inline:6px 8px;padding-bottom: 4px; - padding-top: 4px -} - -.actionContainer_bc4513,.actionIconContainer_bc4513 { - align-items: center; - display: flex; - justify-content: center -} - -.actionIconContainer_bc4513 { - height: 16px; - margin-inline-end:4px;width: 16px -} - -.actionIcon_bc4513 { - color: var(--text-muted) -} - -.actionTextContainer_bc4513 { - flex: 1 -} - -.actionTextHeader_bc4513 { - word-wrap: normal; - text-transform: lowercase -} - -.actionTextHelper_bc4513 { - margin-inline-start:4px;text-transform: lowercase -} - -.emoji_ab6c65 { - -o-object-fit: contain; - object-fit: contain -} - -.pro__30cbe { - text-transform: uppercase -} - -.tip__30cbe { - line-height: 16px; - opacity: 1 -} - -.block__30cbe .pro__30cbe,.block__30cbe .tip__30cbe,.tip__30cbe { - font-size: 14px -} - -.inline__30cbe .pro__30cbe,.inline__30cbe .tip__30cbe { - display: inline; - font-size: 12px -} - -.inline__30cbe .pro__30cbe { - margin-inline-end:3px} - -.enable-forced-colors .tip__30cbe { - opacity: 1 -} - -.spacing_fd14e0 { - margin-bottom: 20px -} - -.spacingTop_fd14e0 { - margin-top: 20px -} - -.message_fd14e0 { - background-color: var(--background-base-low); - border-radius: 3px; - box-shadow: var(--legacy-elevation-border),var(--legacy-elevation-high); - overflow: hidden; - padding-bottom: 10px; - padding-top: 10px; - pointer-events: none; - position: relative -} - -.closeButton_fd14e0 { - justify-content: flex-end -} - -.wrapper_f563df { - display: grid; - gap: 4px; - grid-template-columns: repeat(4,1fr); - grid-template-rows: 1fr; - justify-items: center; - padding: 8px -} - -.button_f563df,.wrapper_f563df { - align-items: center -} - -.button_f563df { - background-color: var(--background-mod-subtle); - border-radius: 8px; - cursor: pointer; - display: flex; - flex: 0 0 auto; - height: 44px; - justify-content: center; - padding: 0; - width: 44px -} - -.button_f563df:hover { - background-color: var(--background-mod-strong) -} - -.button_f563df:active { - background-color: var(--background-mod-muted) -} - -.button_f563df:hover { - background-color: var(--background-mod-normal) -} - -.keyboard-mode .button_f563df.focused_f563df { - background-color: var(--background-mod-normal); - box-shadow: 0 0 0 2px var(--blue-345) -} - -.icon_f563df { - display: block; - height: 20px; - -o-object-fit: contain; - object-fit: contain; - width: 20px -} - -.flagIcon__45b6e { - height: 12px; - width: 16px -} - -/*# sourceMappingURL=59c6d21704b78874.css.map*/ diff --git a/discord-html-copy/css/6f7713d5b10d7cb3.css b/discord-html-copy/css/6f7713d5b10d7cb3.css deleted file mode 100644 index b1210fc..0000000 --- a/discord-html-copy/css/6f7713d5b10d7cb3.css +++ /dev/null @@ -1,13 +0,0 @@ -.highlight-mana-components [data-mana-component] { - box-shadow: 0 0 6px 2px var(--pink-51),0 0 8px 4px var(--opacity-blurple-80) -} - -.highlight-mana-components [data-mana-component=text-area],.highlight-mana-components [data-mana-component=text-input] { - box-shadow: 0 0 6px 2px var(--pink-51),0 0 8px 4px var(--opacity-blurple-80) -} - -.highlight-mana-buttons [data-mana-component=button] { - box-shadow: 0 0 6px 2px var(--pink-51),0 0 8px 4px var(--opacity-blurple-80) -} - -/*# sourceMappingURL=6f7713d5b10d7cb3.css.map*/ diff --git a/discord-html-copy/css/74c035a23e6d0bbb.css b/discord-html-copy/css/74c035a23e6d0bbb.css deleted file mode 100644 index 7ad38b3..0000000 --- a/discord-html-copy/css/74c035a23e6d0bbb.css +++ /dev/null @@ -1,1334 +0,0 @@ -.pro__30cbe { - text-transform: uppercase -} - -.tip__30cbe { - line-height: 16px; - opacity: 1 -} - -.block__30cbe .pro__30cbe,.block__30cbe .tip__30cbe,.tip__30cbe { - font-size: 14px -} - -.inline__30cbe .pro__30cbe,.inline__30cbe .tip__30cbe { - display: inline; - font-size: 12px -} - -.inline__30cbe .pro__30cbe { - margin-inline-end:3px} - -.enable-forced-colors .tip__30cbe { - opacity: 1 -} - -.container__1ce5d { - position: relative -} - -.circularImage__1ce5d { - border-radius: 50% -} - -.dots__1ce5d { - margin-top: .2px -} - -.popover_f84418 { - background: var(--background-surface-high); - border: 1px solid var(--border-muted); - border-radius: 8px; - box-shadow: var(--shadow-low)!important; - height: auto; - overflow: visible; - padding: 2px -} - -.popover_f84418:hover { - border: 1px solid var(--border-subtle); - box-shadow: var(--shadow-medium)!important -} - -.hoverBarButton_f84418 { - border-radius: 6px; - padding: 2px -} - -.hoverBarButton_f84418:hover { - background: var(--interactive-background-hover); - cursor: pointer -} - -.hoverBarButton_f84418:hover .icon_f84418 { - transform: scale(1.1) -} - -.hoverBarButton_f84418:active { - background: var(--interactive-background-active); - padding: 2px -} - -.hoverBarButton_f84418:active .icon_f84418 { - transform: scale(1) -} - -.icon_f84418 { - display: block; - height: 20px; - -o-object-fit: contain; - object-fit: contain; - width: 20px -} - -.full-motion .icon_f84418 { - transition: transform .4s cubic-bezier(.16,1,.3,1) -} - -.buttonContent_f84418 { - align-items: center; - display: flex; - justify-content: center -} - -.tooltip_f84418 { - max-width: 300px -} - -.newBadge_f84418 { - padding: 0 4px; - pointer-events: none; - position: absolute; - top: -12px -} - -.separator_f84418 { - background: var(--border-subtle); - border-radius: 100px; - height: 24px; - margin: 2px 4px; - width: 1px -} - -.messagesPopoutWrap_e8b59c { - background-color: var(--background-surface-high); - border-radius: 8px; - box-shadow: var(--shadow-border),var(--shadow-high); - box-sizing: border-box; - display: flex; - flex-direction: column; - max-height: 80vh!important; - min-height: 200px; - overflow: hidden; - position: relative; - width: 420px; - z-index: 0 -} - -.messagesPopoutWrap_e8b59c .mention { - pointer-events: none -} - -.header_e8b59c { - background-color: var(--background-surface-high); - border-bottom: 1px solid var(--border-subtle); - padding: 16px; - position: relative -} - -.titleContainer_e8b59c { - align-items: center; - display: flex; - gap: 8px -} - -.title_e8b59c { - color: var(--text-strong) -} - -.footer_e8b59c { - background-color: var(--background-mod-subtle); - padding: 16px; - text-align: center -} - -.loadingMore_e8b59c { - align-items: center; - display: flex; - justify-content: center; - margin-bottom: 8px -} - -.loadingMore_e8b59c .spinner_e8b59c { - display: inline-block; - margin: 0 -} - -.loadingMore_e8b59c { - height: 32px; - margin-top: 8px -} - -.hasMore_e8b59c { - align-items: center; - display: flex; - margin-top: 8px; - padding-block:0 16px;padding-inline:16px} - -.messagesPopout_e8b59c { - padding-block:8px 0;padding-inline:8px 0} - -.messageGroupWrapper_e8b59c { - background-color: var(--background-surface-higher); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - margin: 0 0 6px; - margin-bottom: var(--space-8); - margin-inline-end:var(--space-4);overflow: hidden; - position: relative; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.messagesPopout_e8b59c .messageGroupWrapper_e8b59c .messageGroupCozy_e8b59c { - margin-inline-start:-4px;padding-bottom: var(--space-16); - padding-top: var(--space-16) -} - -.scrollingFooterWrap_e8b59c { - position: relative -} - -.emptyPlaceholder_e8b59c { - align-items: center; - display: flex; - flex: 0 0 auto; - flex-direction: column; - margin-bottom: 32px; - margin-top: 8px -} - -.emptyPlaceholder_e8b59c.bottom_e8b59c { - align-items: center; - margin-bottom: 0 -} - -.emptyPlaceholder_e8b59c.bottom_e8b59c .image_e8b59c { - margin-top: 25px -} - -.emptyPlaceholder_e8b59c.loadingPlaceholder_e8b59c { - margin-top: 100px -} - -.image_e8b59c { - background-position: 50%; - background-repeat: no-repeat; - background-size: 94px var(--custom-messages-popout-messages-popout-footer-height); - height: var(--custom-messages-popout-messages-popout-footer-height); - width: var(--custom-messages-popout-messages-popout-footer-height) -} - -.body_e8b59c { - color: var(--text-default); - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px; - margin-top: 20px; - text-align: center; - white-space: pre -} - -.loadingPlaceholder_e8b59c { - margin-top: 240px -} - -.channelSeparator_e8b59c { - margin-bottom: 8px; - margin-top: 24px -} - -.channelName_e8b59c { - color: var(--text-strong); - cursor: pointer; - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 18px -} - -.channelName_e8b59c:hover { - text-decoration: underline -} - -.guildName_e8b59c { - color: var(--text-muted); - font-size: 12px; - font-weight: var(--font-weight-medium); - line-height: 16px; - margin-inline-start:4px} - -.channelSeparator_e8b59c:first-of-type { - margin-top: 13px -} - -.actionButtons_e8b59c { - display: none; - inset-inline-end: var(--space-8); - position: absolute; - top: var(--space-8) -} - -.buttonContainer_e8b59c { - background-color: var(--background-surface-high); - border-radius: var(--radius-sm) -} - -.messageGroupWrapper_e8b59c:focus-within .actionButtons_e8b59c,.messageGroupWrapper_e8b59c:hover .actionButtons_e8b59c { - display: flex; - gap: var(--space-8) -} - -.enable-forced-colors .messagesPopoutWrap_e8b59c { - border: 2px solid CanvasText -} - -.hoverRoll__0263c { - box-sizing: border-box; - contain: paint; - cursor: revert; - display: inline-block; - position: relative; - text-align: start; - vertical-align: top; - width: 100% -} - -.hoverRoll__0263c.forceHover__0263c:not(.disabled__0263c) .default__0263c,.hoverRoll__0263c:hover:not(.disabled__0263c) .default__0263c { - opacity: 0; - transform: translate3d(0,-107%,0); - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.hoverRoll__0263c.forceHover__0263c:not(.disabled__0263c) .hovered__0263c,.hoverRoll__0263c:hover:not(.disabled__0263c) .hovered__0263c { - opacity: 1; - transform: translateZ(0) -} - -.default__0263c,.hovered__0263c { - display: block; - overflow: hidden; - pointer-events: none; - text-overflow: ellipsis; - transform-style: preserve-3d; - white-space: nowrap; - width: 100% -} - -.full-motion .default__0263c,.full-motion .hovered__0263c { - transition: all .22s ease -} - -.hovered__0263c { - inset: 0; - opacity: 0; - position: absolute; - transform: translate3d(0,107%,0) -} - -.effectsWrapper__78936 { - height: 100%; - position: relative -} - -.effects__78936 { - inset: 0; - position: absolute -} - -.text_c8b06d { - color: var(--opacity-white-80); - font-size: 12px; - font-weight: var(--font-weight-medium); - line-height: 12px -} - -.textLeft_c8b06d,.textRight_c8b06d { -} - -.bar_c8b06d { - background-color: hsl(var(--primary-500-hsl)/.16); - border-radius: 2px; - height: 4px -} - -.barInMultiLine_c8b06d { - margin-bottom: 4px -} - -.barInner_c8b06d { - background-color: var(--white); - border-radius: 2px; - height: 100%; - min-width: 4px -} - -.themed_c8b06d .textLeft_c8b06d,.themed_c8b06d .textRight_c8b06d { - color: var(--text-default) -} - -.themed_c8b06d .barInner_c8b06d { - background-color: var(--text-default) -} - -.singleLineContainer_c8b06d { - align-items: center; - display: flex -} - -.barInSingleLine_c8b06d { - flex: 1; - margin-inline:4px} - -.textLeftInSingleLine_c8b06d { - min-width: 32px; - text-align: end -} - -.ellipsis__2ef49 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.headerContainer__2ef49 { - display: flex; - justify-content: space-between; - margin-bottom: 8px; - min-width: 0; - width: 100% -} - -.headerText__2ef49 { - font-weight: var(--font-weight-bold); - text-transform: uppercase -} - -.headerText__2ef49+div { - margin-inline-start:8px} - -.headerTextNormal__2ef49 { -} - -.headerTextEmptyBody__2ef49 { -} - -.body__2ef49 { - display: flex -} - -.bodyAlignCenter__2ef49,.bodyNormal__2ef49 { - align-items: center -} - -.openGameProfile__2ef49 { - cursor: pointer -} - -.openGameProfile__2ef49:hover { - text-decoration: underline -} - -.assets__2ef49 { - align-self: flex-start; - position: relative -} - -.assets__2ef49 .assetsHangStatus__2ef49 { - height: 40px; - width: 40px -} - -.assetsUserActivityHover__2ef49 { - height: 32px; - width: 32px -} - -.assetsLargeMaskStreamPreview__2ef49,.assetsLargeMaskVoiceChannel__2ef49 { - -webkit-mask: url(/assets/f86a4d3ee1fc9ef0.svg); - mask: url(/assets/f86a4d3ee1fc9ef0.svg); - -webkit-mask-size: 100% 100%; - mask-size: 100% 100% -} - -.assetsLargeMaskActivityFeed__2ef49 { - -webkit-mask: url(/assets/892bf2036797f905.svg); - mask: url(/assets/892bf2036797f905.svg) -} - -.assetsLargeImage__2ef49 { - -webkit-user-drag: none; - border-radius: 8px; - display: block; - -o-object-fit: cover; - object-fit: cover -} - -.assetsLargeImageSpotify__2ef49 { - border-radius: 0 -} - -.activityVoiceChannelUserHover__2ef49 .assetsLargeImageSpotify__2ef49 { - border-radius: 8px -} - -.assetsLargeImageStreamPreview__2ef49,.assetsLargeImageStreamPreviewXbox__2ef49 { - height: 60px; - width: 60px -} - -.assetsLargeImageStreamPreview__2ef49,.assetsLargeImageStreamPreviewXbox__2ef49 { -} - -.activityVoiceChannelUserHover__2ef49 .assetsLargeImageStreamPreview__2ef49 { - border-radius: 8px; - height: 32px; - width: 32px -} - -.assetsLargeImageActivityFeed__2ef49,.assetsLargeImageActivityFeedXbox__2ef49 { - height: 90px; - width: 90px -} - -.assetsLargeImageActivityFeed__2ef49,.assetsLargeImageActivityFeedXbox__2ef49 { -} - -.assetsLargeImageVoiceChannel__2ef49 { - height: 64px; - width: 64px -} - -.activityVoiceChannelUserHover__2ef49 .assetsLargeImageVoiceChannel__2ef49 { - border-radius: var(--radius-sm); - height: 32px; - width: 32px -} - -.assetsLargeImageVoiceChannelXbox__2ef49 { -} - -.assetsLargeImageActivityFeedTwitch__2ef49 { - border-radius: var(--radius-sm); - height: 260px; - -webkit-mask: linear-gradient(0deg,transparent 10%,#000 80%); - mask: linear-gradient(0deg,transparent 10%,#000 80%); - width: 100% -} - -.assetsSmallImage__2ef49 { - -webkit-user-drag: none; - border-radius: 50%; - bottom: -4px; - inset-inline-end: -4px; - position: absolute -} - -.assetsSmallImageStreamPreview__2ef49,.assetsSmallImageVoiceChannel__2ef49 { - height: 20px; - width: 20px -} - -.assetsSmallImageStreamPreview__2ef49,.assetsSmallImageVoiceChannel__2ef49 { -} - -.activityVoiceChannelUserHover__2ef49 .assetsSmallImageStreamPreview__2ef49,.activityVoiceChannelUserHover__2ef49 .assetsSmallImageVoiceChannel__2ef49 { - bottom: -2px; - height: 10px; - inset-inline-end: -2px; - width: 10px -} - -.assetsSmallImageActivityFeed__2ef49 { - height: 30px; - width: 30px; -} - -.assetsSmallImageActivityFeedWithoutLargeImage__2ef49,.assetsSmallImageStreamPreviewWithoutLargeImage__2ef49,.assetsSmallImageVoiceChannelWithoutLargeImage__2ef49 { - height: 40px; - width: 40px -} - -.activityVoiceChannelUserHover__2ef49 .assetsSmallImageStreamPreviewWithoutLargeImage__2ef49,.activityVoiceChannelUserHover__2ef49 .assetsSmallImageVoiceChannelWithoutLargeImage__2ef49 { - border-radius: var(--radius-sm); - height: 32px; - width: 32px -} - -.assetsSmallImageActivityFeedWithoutLargeImage__2ef49,.assetsSmallImageStreamPreviewWithoutLargeImage__2ef49,.assetsSmallImageVoiceChannelWithoutLargeImage__2ef49 { -} - -.gameIcon__2ef49 { - flex: 0 0 auto -} - -.activityVoiceChannelUserHover__2ef49 .gameIcon__2ef49 { - height: 32px; - width: 32px -} - -.screenshareIcon__2ef49 { - background-image: url(/assets/aa013155f3c2ecb0.svg); - height: 40px; - width: 40px -} - -.content__2ef49 { - overflow: hidden -} - -.contentImagesStreamPreview__2ef49,.contentImagesVoiceChannel__2ef49 { - margin-inline-start:10px} - -.contentImagesStreamPreview__2ef49,.contentImagesVoiceChannel__2ef49 { -} - -.contentImagesActivityFeed__2ef49 { - margin-inline-start:10px;} - -.contentNoImagesStreamPreview__2ef49,.contentNoImagesVoiceChannel__2ef49 { -} - -.contentNoImagesActivityFeed__2ef49 { - margin-bottom: -4px; -} - -.contentGameImageStreamPreview__2ef49,.contentGameImageVoiceChannel__2ef49 { - margin-bottom: -1px; - margin-inline-start:10px} - -.contentGameImageStreamPreview__2ef49,.contentGameImageVoiceChannel__2ef49 { -} - -.textRow__2ef49 { - display: block; - font-size: 14px; - line-height: 18px -} - -.clickableDetails__2ef49,.details__2ef49,.playTime__2ef49,.state__2ef49,.timestamp__2ef49 { -} - -.clickableDetails__2ef49 { - cursor: pointer -} - -.clickableDetails__2ef49:hover { - text-decoration: underline -} - -.nameNormal__2ef49 { -} - -.detailsWrap__2ef49 { -} - -.guildIcon__2ef49 { - border-radius: var(--radius-xs) -} - -.guildDetails__2ef49 { - align-items: center; - display: flex; - gap: 4px; - margin-top: 4px -} - -.voiceChannelInfo__2ef49 { - align-items: center; - display: flex; - gap: 2px -} - -.nameWrap__2ef49 { -} - -.activityActivityFeed__2ef49 .name__2ef49 { - display: none -} - -.activityActivityFeed__2ef49 .details__2ef49 { - font-weight: var(--font-weight-semibold) -} - -.timeBarActivityFeed__2ef49 { - margin-top: 10px -} - -.timeBarVoiceChannel__2ef49 { - margin-top: 4px -} - -.activityNameUserActivityHover__2ef49 { - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 18px -} - -.activityName__2ef49,.nameNormal__2ef49,.nameWrap__2ef49 { - color: var(--white) -} - -.bodyLink__2ef49 { -} - -.bodyLink__2ef49:hover { - text-decoration: underline -} - -.twitchImageContainer__2ef49 { - background: var(--primary-800); - border-radius: var(--radius-sm); - position: relative -} - -.twitchImageOverlay__2ef49 { - inset-inline: 0; - bottom: 0; - padding: 16px; - position: absolute -} - -.twitchBackgroundImage__2ef49 { - display: inline-block; - min-height: 260px -} - -.streamUsername__2ef49 { - color: var(--primary-200); - font-size: 16px; - font-weight: var(--font-weight-semibold); - line-height: 1.25 -} - -.streamName__2ef49 { - color: var(--primary-300); - font-size: 14px; - font-weight: var(--font-weight-medium); - margin-top: 8px -} - -.streamGame__2ef49 { - color: var(--primary-400); - font-size: 12px; - font-weight: var(--font-weight-semibold); - margin-top: 8px; - text-transform: uppercase -} - -.activityVoiceChannel__2ef49 .headerText__2ef49 { - color: var(--interactive-text-default) -} - -.activityVoiceChannel__2ef49 .activityName__2ef49,.activityVoiceChannel__2ef49 .name__2ef49,.activityVoiceChannel__2ef49 .nameNormal__2ef49,.activityVoiceChannel__2ef49 .nameWrap__2ef49 { - color: var(--text-strong) -} - -.activityVoiceChannel__2ef49 .content__2ef49,.activityVoiceChannel__2ef49 .details__2ef49,.activityVoiceChannel__2ef49 .playTime__2ef49,.activityVoiceChannel__2ef49 .state__2ef49,.activityVoiceChannel__2ef49 .timestamp__2ef49 { - color: var(--text-default) -} - -.activityVoiceChannelUserHover__2ef49 .activityName__2ef49,.activityVoiceChannelUserHover__2ef49 .name__2ef49,.activityVoiceChannelUserHover__2ef49 .nameNormal__2ef49,.activityVoiceChannelUserHover__2ef49 .nameWrap__2ef49 { - color: var(--text-strong) -} - -.activityVoiceChannelUserHover__2ef49 .content__2ef49,.activityVoiceChannelUserHover__2ef49 .details__2ef49,.activityVoiceChannelUserHover__2ef49 .playTime__2ef49,.activityVoiceChannelUserHover__2ef49 .state__2ef49,.activityVoiceChannelUserHover__2ef49 .timestamp__2ef49 { - color: var(--text-muted); - font-size: 12px -} - -.platformIcon__2ef49 { - display: none -} - -.icon__2ef49 { - color: var(--text-default); - display: block; - flex: 0 0 auto; - height: 16px; - margin-inline-end:4px;width: 16px -} - -.wrap__2ef49 { - flex-wrap: wrap -} - -.activityDetails__2ef49 { - display: flex; - flex-direction: row; - width: 100% -} - -.activityActivityFeed__2ef49 .content__2ef49,.activityActivityFeed__2ef49 .details__2ef49 { - color: var(--text-default) -} - -.activityStreamPreview__2ef49 .activityName__2ef49,.activityStreamPreview__2ef49 .content__2ef49,.activityStreamPreview__2ef49 .details__2ef49,.activityStreamPreview__2ef49 .name__2ef49,.activityStreamPreview__2ef49 .nameNormal__2ef49 { - color: var(--text-strong) -} - -.buttonsWrapper__65bb6 { - flex: 0 1 auto; - margin-top: 12px -} - -.buttonsWrapper__65bb6:empty { - margin: 0 -} - -.horizontal__65bb6>:not(:first-child) { - margin-inline-start:8px} - -.vertical__65bb6>:not(:first-child) { - margin-top: 8px -} - -.actionsStreamPreview__34f7d { - margin-top: 10px -} - -.actionsProfile__34f7d,.actionsProfileV2__34f7d { - flex: 1 1 auto; - margin-inline-start:20px;margin-top: 0 -} - -.actionsSimplifiedProfile__34f7d { - flex: 1 1 auto; - margin-inline-start:10px;margin-top: 0 -} - -.actionsActivityFeed__34f7d { - margin-top: 8px -} - -.errorImage__34f7d { - height: 135px -} - -.cannotLaunchImage__34f7d { -} - -.actionsWrapper__34f7d { - align-items: flex-end; - display: flex; - flex-direction: row; - justify-content: space-between; - width: 100% -} - -.actionsWrapper__34f7d .actionsProfile__34f7d,.actionsWrapper__34f7d .actionsProfileV2__34f7d,.actionsWrapper__34f7d .actionsSimplifiedProfile__34f7d { - margin-inline-start:0;margin-top: 12px -} - -.streamPreviewWrapper__0489e { - padding: var(--space-sm) 0; - width: 100% -} - -.streamPreview__0489e { - background-color: var(--background-surface-high); - border-radius: 5px; - box-shadow: var(--shadow-border),var(--shadow-high); - overflow: hidden; - width: 100% -} - -.streamBorder__0489e { - border-radius: var(--radius-sm) -} - -.streamHeader__0489e { - align-items: center; - display: flex; - gap: 6px; - margin-bottom: var(--space-xs); - padding: 10px 10px 0 -} - -.streamHeaderVoiceUserActivities__0489e { - display: flex; - justify-content: space-between; - margin-bottom: var(--space-xs) -} - -.streamHeaderIcon__0489e { - flex-shrink: 0; - height: 16px; - width: 16px -} - -.previewContainer__0489e { - background-color: var(--background-secondary-alt); - box-sizing: border-box; - height: 142px; - position: relative; - width: 100% -} - -.previewContainer__0489e:hover .previewHover__0489e { - opacity: 1 -} - -.previewContainerUserActivity__0489e { - border-radius: var(--radius-sm); - overflow: hidden -} - -.skipContainer__0489e .previewContainer__0489e { - box-shadow: var(--shadow-border),var(--shadow-high) -} - -.previewHover__0489e { - align-items: center; - opacity: 0; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - cursor: pointer; - display: flex; - font-weight: var(--font-weight-semibold); - justify-content: center; - line-height: 18px; - transition: opacity .2s ease-in-out -} - -.previewImage__0489e { - height: 100%; - width: 100% -} - -.body__0489e { - padding: 10px -} - -.bodyUserActivity__0489e { - padding-top: var(--space-xs) -} - -.activityActions__0489e { - margin-top: 0 -} - -.protip__0489e { - margin-top: 10px; - text-align: center -} - -.white__0489e { - color: var(--white) -} - -.theme-dark .previewHover__0489e { - background: var(--opacity-black-60) -} - -.theme-light .previewHover__0489e { - background: hsl(var(--primary-500-hsl)/.6) -} - -.body__6da2d { - padding: 10px -} - -.streamPreviewWrapper__6da2d { - opacity: 0; - padding-inline-start:16px;position: relative; - transform: translate3d(8px,0,0) scale(.98); - transform-origin: 50% 50% -} - -.full-motion .streamPreviewWrapper__6da2d { - transition: opacity .15s ease,transform 125ms ease-in -} - -.streamPreviewWrapper__6da2d.mounted__6da2d { - opacity: 1; - transform: translateZ(0) scale(1) -} - -.streamPreview__6da2d { - background-color: var(--background-surface-high); - border-radius: 5px; - box-shadow: var(--shadow-border),var(--shadow-high); - overflow: hidden; - width: 252px -} - -.previewContainer__6da2d { - background-color: var(--background-mod-normal); - box-sizing: border-box; - height: 142px; - position: relative; - width: 100% -} - -.previewContainer__6da2d:hover .previewHover__6da2d { - opacity: 1 -} - -.previewHover__6da2d { - align-items: center; - opacity: 0; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - cursor: pointer; - display: flex; - font-weight: var(--font-weight-semibold); - justify-content: center; - line-height: 18px; - transition: opacity .2s ease-in-out -} - -.previewImage__6da2d { - height: 100%; - width: 100% -} - -.applicationName__6da2d { - color: var(--text-default); - font-size: 14px; - inset-inline-start: 12px; - line-height: 18px; - position: absolute; - top: 8px -} - -.liveIcon__6da2d { - margin-inline-end:8px} - -.activityActions__6da2d { - margin-top: 0 -} - -.protip__6da2d { - margin-top: 10px; - text-align: center -} - -.white__6da2d { - color: var(--white) -} - -.theme-dark .applicationName__6da2d { - color: var(--primary-100) -} - -.theme-dark .previewHover__6da2d { - background: var(--opacity-black-60) -} - -.theme-light .previewHover__6da2d { - background: hsl(var(--primary-500-hsl)/.6) -} - -@keyframes outgoing-call-pulse__07f91 { - 0% { - opacity: .2 - } - - 50% { - opacity: .9 - } - - to { - opacity: .2 - } -} - -.list__07f91 { - display: flex; - justify-content: flex-start -} - -.listDefault__07f91 { - align-items: stretch; - flex-direction: column; - flex-wrap: nowrap -} - -.listCollapse__07f91 { - align-items: flex-start; - flex-direction: row; - flex-wrap: wrap -} - -.voiceUser__07f91 { - position: relative -} - -.clickable__07f91 { - cursor: pointer -} - -.content__07f91 { - align-items: center; - border-radius: var(--radius-sm); - display: flex; - gap: 8px; - justify-content: flex-start; - padding-block:4px;padding-inline:var(--space-xs) var(--space-xs)} - -.flipped__07f91 { - flex-direction: row-reverse -} - -.userLarge__07f91 { - height: 38px -} - -.userSmall__07f91 { - height: 32px -} - -.listCollapse__07f91 .content__07f91 { - margin-inline-start:8px;padding: 0 -} - -.listCollapse__07f91 .content__07f91.flipped__07f91 { - margin-inline:0 8px} - -.listDefault__07f91 .avatarContainer__07f91 { - margin-inline:8px} - -.overlap__07f91 { - margin-inline:-8px -4px;margin-bottom: -4px; - margin-top: -1px -} - -.overlap__07f91 .content__07f91 { - margin-inline-start:0} - -.overlap__07f91 .avatar__07f91 { - border: 4px solid var(--background-base-lower); - border-color: var(--background-base-lowest); - margin: 0 -} - -.avatar__07f91 { - background-position: 50% 50%; - background-repeat: no-repeat; - background-size: cover; - border-radius: 50%; - flex: 0 0 auto -} - -.avatarSmall__07f91 { - height: 24px; - width: 24px -} - -.avatarLarge__07f91 { - height: 30px; - width: 30px -} - -.username__07f91 { - flex: 1 1 auto; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.voiceUser__07f91:hover .selfHangStatus__07f91 { - background-color: transparent; - border-color: transparent -} - -div.clanTagBadgeContainer__07f91 { - margin-inline-start:0} - -.clanTagBadge__07f91 { - height: 16px; - width: 16px -} - -.usernameFont__07f91 { - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 18px -} - -.icons__07f91 { - flex: 0 0 auto; - margin-inline-end:0} - -.iconGroup__07f91,.icons__07f91 { - align-items: center; - display: flex; - gap: 4px -} - -.iconGroup__07f91 { - margin: 0 -} - -.iconGroup__07f91:empty { - display: none -} - -.flipped__07f91 .icons__07f91 { - margin-inline:8px 0} - -.icon__07f91 { - height: 16px; - width: 16px -} - -.hoverableIcon__07f91:hover { - color: var(--icon-subtle) -} - -.iconTooltipContainer__07f91 { - line-height: 0 -} - -.iconTooltip__07f91 { - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden -} - -.iconPriortySpeaker__07f91 { - border-radius: 8px; - height: 6px; - inset-inline-start: -16px; - position: absolute; - width: 6px -} - -.icon__07f91,.username__07f91 { - color: var(--channels-default) -} - -.iconServer__07f91,.strikethrough__07f91 { - color: var(--icon-feedback-critical) -} - -.listDefault__07f91 .clickable__07f91:hover .content__07f91 { - background: var(--interactive-background-hover) -} - -.listDefault__07f91 .clickable__07f91:active .content__07f91 { - background: var(--interactive-background-active) -} - -.listDefault__07f91 .clickable__07f91.selected__07f91 .content__07f91 { - background: var(--interactive-background-selected) -} - -.listDefault__07f91 .clickable__07f91:hover .username__07f91 { - color: var(--interactive-text-hover) -} - -.optionsButton__07f91 { - align-items: center; - border-radius: 4px; - display: flex; - flex-shrink: 0; - height: 24px; - inset-inline-end: 5px; - justify-content: center; - width: 24px -} - -.optionsActive__07f91,.optionsButton__07f91:hover { - background: var(--interactive-background-active) -} - -.voiceUser__07f91:not(:hover,:focus-within) .optionsButton__07f91:not(.optionsActive__07f91) { - width: 0 -} - -.voiceUser__07f91:not(:hover,:focus-within) .optionsButtonContainer__07f91:not(.optionsContainerActive__07f91) { - display: none -} - -span.clanTag__07f91 { - background: var(--background-mod-subtle); - flex: 0; - text-overflow: ellipsis; - white-space: nowrap -} - -span.clanTag__07f91.isOverlay__07f91 { - background: var(--background-base-lowest) -} - -.listDefault__07f91 .clickable__07f91:hover .clanTag__07f91 { - background: var(--interactive-background-hover) -} - -.theme-light .listDefault__07f91 .clickable__07f91:hover .clanTag__07f91 { - background: var(--background-mod-strong) -} - -.listDefault__07f91 .clickable__07f91.selected__07f91 .username__07f91,.usernameSpeaking__07f91 { - color: var(--interactive-text-active) -} - -.liveIcon__07f91 { - background-color: var(--background-feedback-notification); - color: var(--white) -} - -.iconPriortySpeakerSpeaking__07f91 { - background-color: var(--green-360) -} - -.iconPriortySpeaker__07f91 { - background-color: var(--text-muted) -} - -.disabled__07f91 { - opacity: .5 -} - -.disabled__07f91:hover { - opacity: 1 -} - -.ringing__07f91 { - animation: outgoing-call-pulse__07f91 1.25s ease-out infinite; - opacity: .3 -} - -.guestSuffix__07f91 { - color: var(--text-feedback-positive) -} - -.userAvatar__07f91 { - margin: 0 -} - -.gameIcon__07f91 { - border-radius: var(--radius-xs) -} - -.enable-forced-colors .clickable__07f91:hover .username__07f91,.enable-forced-colors .username__07f91 { - background-color: ButtonFace; - color: ButtonText; - forced-color-adjust: none; - text-decoration: underline -} - -.enable-forced-colors .icon__07f91 { - color: CanvasText -} - -/*# sourceMappingURL=74c035a23e6d0bbb.css.map*/ diff --git a/discord-html-copy/css/901301eeddfe8f4b.css b/discord-html-copy/css/901301eeddfe8f4b.css deleted file mode 100644 index 2bfc451..0000000 --- a/discord-html-copy/css/901301eeddfe8f4b.css +++ /dev/null @@ -1,14297 +0,0 @@ -.lineClamp__0b48b { - display: -webkit-box; - -webkit-box-orient: vertical; - overflow: hidden -} - -.ragingDemon_ac6454 { - color: var(--white); - display: none; - height: 100vh; - margin-top: -40px; - position: relative; - width: 100vw -} - -.ragingDemon_ac6454.visible_ac6454 { - display: block -} - -.symbol_ac6454 { - align-items: center; - display: flex; - inset: 0; - justify-content: center; - position: absolute -} - -.symbol_ac6454 img { - height: auto; - max-height: 60vh; - max-width: 80vw; - -o-object-fit: contain; - object-fit: contain; - opacity: 0; - position: relative; - width: 80% -} - -.full-motion .symbol_ac6454 img { - animation: symbolFadeIn_ac6454 3s ease-out forwards -} - -.reduce-motion .symbol_ac6454 img { - opacity: 1; - transition: opacity 1s ease-in -} - -.symbolBackground_ac6454 { - inset: 0; - opacity: 0; - position: absolute -} - -.symbolBackground_ac6454:after,.symbolBackground_ac6454:before { - background: linear-gradient(90deg,transparent 50%,#fff 0,#fff),linear-gradient(82deg,transparent 50%,#ddd 0,#ddd),linear-gradient(67deg,transparent 50%,#fff 0,#fff),linear-gradient(52deg,transparent 50%,#ddd 0,#ddd),linear-gradient(37deg,transparent 50%,#fff 0,#fff),linear-gradient(22deg,transparent 50%,#ddd 0,#ddd),linear-gradient(7deg,transparent 50%,#fff 0,#fff),linear-gradient(-8deg,transparent 50%,#ddd 0,#ddd),linear-gradient(-23deg,transparent 50%,#fff 0,#fff),linear-gradient(-38deg,transparent 50%,#ddd 0,#ddd),linear-gradient(-53deg,transparent 50%,#fff 0,#fff),linear-gradient(-68deg,transparent 50%,#ddd 0,#ddd),linear-gradient(-83deg,transparent 50%,#fff 0,#fff),linear-gradient(-90deg,transparent 50%,#ddd 0,#ddd); - background-position: 0 0; - background-size: 200% 100%; - content: ""; - height: 100%; - position: absolute; - width: 50% -} - -.symbolBackground_ac6454:before { - inset-inline-start: 50%; - transform: rotate(180deg) -} - -.full-motion .symbolBackground_ac6454 { - animation: symbolBgFadeInOut_ac6454 3s ease-in-out forwards -} - -.container_ac6454 { - position: absolute; - transform: translateZ(0) -} - -.explosion_ac6454 { - position: absolute; - transform: translateZ(0); - transform-origin: 50% -} - -.explosion_ac6454 img { - position: absolute; - transform-origin: 50% -} - -.full-motion .animate_ac6454 .circleInner_ac6454 { - animation: explCircleInner_ac6454 calc(var(--custom-raging-demon-duration)*2) var(--custom-raging-demon-duration) ease-out 1 forwards; - opacity: 0 -} - -.full-motion .animate_ac6454 .circleOuter_ac6454 { - animation: explCircleOuter_ac6454 calc(var(--custom-raging-demon-duration)*3) ease-out 1 forwards; - opacity: 0 -} - -.full-motion .animate_ac6454 .linesSecondary_ac6454 { - animation: explLinesSecondary_ac6454 calc(var(--custom-raging-demon-duration)*2.5) calc(var(--custom-raging-demon-duration)*1.5) ease-out 1 forwards; - opacity: 0 -} - -.full-motion .animate_ac6454 .linesMain_ac6454 { - animation: explLinesMain_ac6454 calc(var(--custom-raging-demon-duration)*2) var(--custom-raging-demon-duration) ease-out 1 forwards; - opacity: 0 -} - -.primaryExplosion_ac6454 { - height: 175px; - margin-inline-start:-77px;margin-top: -102px; - width: 178px -} - -.primaryExplosion_ac6454 .circleInner_ac6454 { - inset-inline-start: 43px; - top: 69px -} - -.primaryExplosion_ac6454 .circleOuter_ac6454 { - inset-inline-start: 30px; - top: 56px -} - -.primaryExplosion_ac6454 .linesSecondary_ac6454 { - inset-inline-start: 0; - top: 59px; - transform-origin: 77px 43px -} - -.primaryExplosion_ac6454 .linesMain_ac6454 { - inset-inline-start: 71px; - top: 0; - transform-origin: 3px 110px -} - -.secondaryExplosion_ac6454 { - height: 300px; - margin-inline-start:-88px;margin-top: -156px; - width: 170px -} - -.secondaryExplosion_ac6454 .circleInner_ac6454 { - inset-inline-start: 58px; - top: 130px; - transform-origin: 30.5px 27.5px -} - -.secondaryExplosion_ac6454 .circleOuter_ac6454 { - inset-inline-start: 46px; - top: 115px -} - -.secondaryExplosion_ac6454 .linesSecondary_ac6454 { - inset-inline-start: 19px; - top: 68px; - transform-origin: 70px 91px -} - -.secondaryExplosion_ac6454 .linesMain_ac6454 { - inset-inline-start: 5px; - top: 6px; - transform-origin: 87px 150px -} - -@keyframes symbolFadeIn_ac6454 { - 0% { - opacity: 0; - transform: scale(.8) - } - - 5% { - transform: scale(1.05) - } - - 15% { - opacity: 1; - transform: scale(1) - } - - to { - opacity: 1; - transform: scale(1) - } -} - -@keyframes symbolBgFadeInOut_ac6454 { - 0% { - opacity: 0 - } - - 5% { - opacity: .2 - } - - 15% { - opacity: .2 - } - - 60% { - opacity: 0 - } - - to { - opacity: 0 - } -} - -@keyframes explCircleInner_ac6454 { - 0% { - opacity: 0; - transform: scale(.2) translateZ(0) - } - - 8% { - opacity: 1 - } - - 70% { - opacity: 1 - } - - to { - opacity: 0; - transform: scale(.8) translateZ(0) - } -} - -@keyframes explCircleOuter_ac6454 { - 0% { - opacity: 0; - transform: scale(.2) translateZ(0) - } - - 8% { - opacity: 1 - } - - 70% { - opacity: 1 - } - - to { - opacity: 0; - transform: scale(.9) translateZ(0) - } -} - -@keyframes explLinesSecondary_ac6454 { - 0% { - opacity: 0; - transform: scale(0) translateZ(0) - } - - 20% { - opacity: 1 - } - - 50% { - opacity: 1 - } - - to { - opacity: 0; - transform: scale(1) translateZ(0) - } -} - -@keyframes explLinesMain_ac6454 { - 0% { - opacity: 0; - transform: scale(0) translateZ(0) - } - - 30% { - opacity: 1 - } - - 50% { - opacity: 1 - } - - to { - opacity: 0; - transform: scale(1) translateZ(0) - } -} - -.keyboardShortcutsModal_f061f6 { - background-color: var(--background-base-lower); - border-radius: 4px; - display: flex; - flex-direction: column; - max-height: 80vh; - max-width: 950px; - min-width: 800px; - overflow: hidden; - position: relative; - width: 80vw -} - -.noBackground_f061f6 { - background: none!important -} - -.noBackground_f061f6.noShadow_f061f6 { - box-shadow: none -} - -.backdrop_f061f6 { - background-color: rgba(0,0,0,.5); - pointer-events: none; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - opacity: 0; - transition: opacity 1s ease-in-out -} - -.backdrop_f061f6.show_f061f6 { - opacity: 1 -} - -.modalTitle_f061f6 { - align-items: center; - color: var(--text-strong); - display: flex; - flex: 0 0; - font-size: 26px; - line-height: 34px; - margin: 0 0 5px; - overflow: hidden; - padding: 20px 40px 0 -} - -.modalTitle_f061f6 .content_f061f6 { - margin-inline-end:20px} - -.modalSubtitle_f061f6 { - border-bottom: 1px solid var(--opacity-black-8); - color: var(--text-default); - flex: 0 0; - font-size: 16px; - line-height: 20px; - padding: 0 40px 20px -} - -.ddrArrows_f061f6 { - display: flex; - inset-inline-end: 40px; - overflow: hidden; - position: absolute; - top: 22px -} - -.ddrArrows_f061f6 .arrow_f061f6 { - float: inline-start; - height: 50px; - margin: 4px; - overflow: hidden; - text-indent: -9999em; - width: 50px -} - -.ddrArrows_f061f6 .arrow_f061f6.active_f061f6 { - animation: arrow_f061f6 .2s steps(2) 1; - background-position: 0 -100px -} - -.ddrArrows_f061f6 .arrow_f061f6.left_f061f6 { - background-image: url(/assets/574907598f87a615.svg) -} - -.ddrArrows_f061f6 .arrow_f061f6.down_f061f6 { - background-image: url(/assets/0a9fe5975bf11d56.svg) -} - -.ddrArrows_f061f6 .arrow_f061f6.up_f061f6 { - background-image: url(/assets/1930678cb18a10c6.svg) -} - -.ddrArrows_f061f6 .arrow_f061f6.right_f061f6 { - background-image: url(/assets/600de8edafaf94b2.svg) -} - -.keybindGroupDescription_f061f6,.keybindGroupDivider_f061f6 { - margin: 8px 0 -} - -.keyboardShortcutList_f061f6 .keybindGroup_f061f6 { - box-sizing: border-box; - -moz-column-break-inside: avoid; - background-color: var(--background-mod-muted); - border-radius: 8px; - break-inside: avoid; - display: flex; - flex-direction: column; - gap: 8px; - padding: 12px -} - -.keybindKey_f061f6 { - margin-bottom: 8px -} - -.keyboardShortcutSection_f061f6 { - border-bottom: 1px solid var(--border-subtle); - margin-inline-start:16px;padding: 24px 8px -} - -.keyboardShortcutSection_f061f6:last-child { - border-bottom: none -} - -.keyboardShortcutListGroup_f061f6 { - display: grid; - gap: 24px; - grid-template-columns: repeat(auto-fill,minmax(180px,max-content)); - margin-top: 8px; - padding: 8px 0 -} - -.firstGroup_f061f6 { - margin-top: 0; - padding-top: 0 -} - -@keyframes arrow_f061f6 { - 0% { - background-position: 0 -50px - } - - to { - background-position: 0 -150px - } -} - -.tutorial__73f2a { - position: absolute; - top: -80px; - inset-inline: -60px; - opacity: 0; - transition: opacity .2s ease-in-out -} - -.tutorial__73f2a.shown__73f2a { - opacity: 1 -} - -.arrowGroup__73f2a { - inset-inline-start: 0; - position: absolute; - top: 0; - transform-origin: 0 0 -} - -.arrowGroup__73f2a.right__73f2a { - inset-inline: auto 0; - transform: scaleX(-1) -} - -.arrowContainer__73f2a { - inset-inline-start: 0; - opacity: 1; - position: absolute; - top: 0; - transform-origin: 0 0 -} - -.full-motion .arrowContainer__73f2a { - transition: transform .2s ease-in-out,opacity .2s ease-in-out -} - -.horizontal__73f2a { - top: 118px; - transition: none -} - -.diag1__73f2a { - transform: translate3d(10px,24px,0) rotate(40deg) -} - -.diag2__73f2a { - transform: translate3d(85px,0,0) rotate(70deg) -} - -.full-motion .arrowIcon__73f2a { - animation: arrow-pulse__73f2a .82s ease-in-out infinite -} - -.tutorialMessages__73f2a { - color: var(--white); - font-size: 18px; - font-weight: var(--font-weight-medium); - height: 60px; - line-height: 24px; - overflow: hidden; - padding-top: 30px; - position: relative; - text-align: center; - top: 10px -} - -.message__73f2a { - position: absolute; - width: 100% -} - -.full-motion .message__73f2a { - transition: opacity .15s ease-in-out,transform .15s ease-in-out -} - -.searchMessage__73f2a { - opacity: 1; - transform: translateZ(0) -} - -.selectMessage__73f2a { - opacity: 0; - transform: translate3d(0,100%,0) -} - -.hasQuery__73f2a .diag1__73f2a,.hasQuery__73f2a .diag2__73f2a,.hasQuery__73f2a .horizontal__73f2a { - opacity: 0 -} - -.hasQuery__73f2a .diag1__73f2a { - transform: translate3d(0,12px,0) rotate(40deg) -} - -.hasQuery__73f2a .diag2__73f2a { - transform: translate3d(85px,-10px,0) rotate(70deg) -} - -.hasQuery__73f2a .searchMessage__73f2a { - opacity: 0; - transform: translate3d(0,-100%,0) -} - -.hasQuery__73f2a .selectMessage__73f2a { - opacity: 1; - transform: translateZ(0) -} - -@keyframes arrow-pulse__73f2a { - 0%,to { - transform: translateZ(0) - } - - 50% { - transform: translate3d(3px,0,0) - } -} - -.quickswitcher_ac6cb0 { - align-items: stretch; - border-radius: 8px; - color: var(--text-default); - display: flex; - flex-direction: column; - padding: 12px 20px 0; - position: relative -} - -.quickswitcher_ac6cb0,.quickswitcher_ac6cb0 * { - box-sizing: border-box -} - -.input_ac6cb0 { - background-color: var(--input-background-default); - border: 1px solid var(--input-border-default); - border-radius: var(--radius-sm); - color: var(--text-default); - display: block; - flex: 0 0 auto; - font-size: 22px; - height: 70px; - line-height: 70px; - padding: 0 12px; - position: relative; - z-index: 1 -} - -.input_ac6cb0::-moz-placeholder { - color: var(--input-placeholder-text-default) -} - -.input_ac6cb0::placeholder { - color: var(--input-placeholder-text-default) -} - -.autocompleteQuerySymbol_ac6cb0 { - background-color: var(--background-base-lowest); - border-radius: 4px; - font-family: var(--font-code); - padding: 2px -} - -.scroller_ac6cb0 { - height: 236px; - margin-inline-end:-17px;margin-top: 16px; - max-height: 236px; - z-index: 0 -} - -.scroller_ac6cb0::-webkit-scrollbar-track { - background-color: var(--background-mod-normal)!important -} - -.protip_ac6cb0 { - padding: 10px 0 0 -} - -.protip_ac6cb0.hasContent_ac6cb0 { - border-top: 1px solid var(--border-subtle) -} - -.resultsArea_ac6cb0 { - height: 262px -} - -.emptyState_ac6cb0 { - background: no-repeat 50% 20px; - font-weight: var(--font-weight-medium); - padding-bottom: 20px; - padding-top: 150px; - text-align: center -} - -.emptyStateNote_ac6cb0 { - color: var(--text-muted); - font-size: 16px; - line-height: 20px -} - -.emptyStateCTA_ac6cb0 { - font-size: 14px; - line-height: 16px; - opacity: 1 -} - -.emptyStateCTA_ac6cb0:hover { - opacity: 1 -} - -.miscContainer_ac6cb0 { - font-size: 14px; - font-weight: var(--font-weight-medium); - opacity: .6; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.enable-forced-colors .input_ac6cb0 { - border: 1px solid ButtonText -} - -.enable-forced-colors .input_ac6cb0:focus { - border-color: Highlight -} - -.enable-forced-colors .miscContainer_ac6cb0 { - opacity: 1 -} - -.images-light .emptyState_ac6cb0 { - background-image: url(/assets/4df87e6155cbedff.svg) -} - -.images-dark .emptyState_ac6cb0 { - background-image: url(/assets/05207174d5432737.svg) -} - -.listItem__650eb { - display: flex; - justify-content: center; - margin: 0; - position: relative; - width: var(--custom-guild-list-width) -} - -.unavailableBadge__650eb { - background-color: var(--white); - color: var(--red-400)!important -} - -.iconBadge__650eb { - background-color: var(--icon-overlay-dark) -} - -.iconBadge__650eb.isCurrentUserConnected__650eb { - background-color: var(--status-positive) -} - -.tutorialContainer__650eb { - position: relative -} - -.hideEmoji__650eb { - opacity: 0 -} - -.serverEmoji__650eb { - height: 16px; - width: 16px -} - -.navigationIcon__90d72 { - display: block; - height: 48px; - width: 48px -} - -.badgeIcon__90d72 { - height: 12px; - width: 12px -} - -.guildSeparator__90d72 { - background-color: var(--border-subtle); - border-radius: 1px; - height: 2px; - width: 32px -} - -.guildsError__90d72 { - align-items: center; - background-color: var(--background-base-low); - border-color: var(--border-feedback-critical); - border-radius: 50%; - border-style: solid; - border-width: 2px; - box-sizing: border-box; - color: var(--text-default); - display: flex; - font-size: 20px; - height: 48px; - justify-content: center; - padding: 0; - transition: background-color .15s ease-out; - width: 48px -} - -.errorInner__90d72 { - font-weight: var(--font-weight-bold) -} - -.guildsError__90d72:hover { - background-color: var(--background-feedback-critical); - border-color: var(--border-feedback-critical); - color: var(--white); - text-decoration: none -} - -.circleButtonBase__90d72 { - align-items: center; - box-sizing: border-box; - cursor: pointer; - display: flex; - height: 48px; - justify-content: center; - transition: color .15s ease-out,background-color .15s ease-out; - width: 48px -} - -.circleIconButton__90d72 { - background-color: var(--background-base-low); - color: var(--green-360) -} - -.circleIconButton__90d72.selected__90d72 { - background-color: var(--green-360); - color: var(--white) -} - -.nitroUpsell__90d72 { - background: linear-gradient(135deg,var(--premium-tier-2-purple) 0,var(--premium-tier-2-pink) 100%); - color: var(--white) -} - -.nitroUpsellIcon__90d72 { - stroke: #000 2px -} - -.circleIcon__90d72 { - height: 24px; - width: 24px -} - -.plus__90d72 { - font-size: 40px; - font-weight: var(--font-weight-normal); - line-height: 40px; - position: relative; - top: -1px -} - -.createJoinContainer__90d72 { - position: relative; - width: 50px -} - -.pill__90d72 { - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.listItem__90d72 { - display: flex; - justify-content: center; - margin: 0 0 8px; - position: relative; - width: var(--custom-guild-list-width) -} - -.listItemWrapper__90d72:active { - transform: translateY(1px) translateZ(0) -} - -.listItemTooltip__90d72 { - font-size: 16px; - font-weight: var(--font-weight-semibold); - line-height: 20px; - max-width: 196px; - word-wrap: break-word -} - -.placeholderMask__90d72 { - display: block -} - -.dragInner__90d72 { - height: 48px; - width: 48px -} - -.iconBadge__90d72 { - background-color: var(--background-mod-strong) -} - -.iconBadge__90d72.participating__90d72 { - background-color: var(--green-360) -} - -.unavailableBadge__90d72 { - background-color: var(--white); - color: var(--red-400)!important -} - -.dragInner__90d72 { - background-color: var(--background-base-low) -} - -.tutorialContainer__90d72 { - position: relative -} - -.upsellTooltip__90d72 { - display: flex -} - -.upsellIcon__90d72 { - flex: 0 0 auto; - margin-inline:-6px 8px} - -.upsellText__90d72 { - flex: 1 1 auto; - line-height: 22px -} - -.listItemTooltip__90d72 { - color: var(--text-default) -} - -.listItemTooltipContent__90d72 { - padding: 8px -} - -.row_b1f768 { - align-items: center; - display: flex -} - -.row_b1f768+.row_b1f768 { - margin-top: 8px -} - -.rowGuildName_b1f768 { - align-items: flex-start -} - -.rowIcon_b1f768 { - flex-shrink: 0; - margin-inline-end:8px} - -.rowIconV2_b1f768 { - margin-top: 2px -} - -.activityIcon_b1f768 { - color: var(--interactive-text-default); - height: var(--custom-guild-tooltip-icon-size); - width: var(--custom-guild-tooltip-icon-size) -} - -.stageListenerPill_b1f768 { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: 12px; - display: flex; - flex-direction: row; - height: 24px; - margin-inline-start:8px;padding: 0 8px -} - -.stageListenerCount_b1f768 { - margin-inline-start:4px} - -.guildNameText_b1f768 { - word-wrap: break-word; - color: var(--text-default); - min-width: 0 -} - -.viewAsRolesWarning_b1f768 { - margin: 8px 0 4px -} - -.guildNameTextLimitedSize_b1f768 { - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden -} - -.muteText_b1f768 { - font-weight: var(--font-weight-medium) -} - -.muteTextWithActivity_b1f768 { - margin-top: 8px -} - -.invitesDisabledTooltip_b1f768 { - margin-top: 2px -} - -.guildTooltipWrapper_b1f768 { - font-size: 16px; - font-weight: var(--font-weight-semibold); - line-height: 20px; - max-width: 196px; - word-wrap: break-word; - color: var(--text-default); - pointer-events: none -} - -.pill_c9fddf { - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.favoriteIcon_c9fddf { - display: block -} - -.ring_c9fddf { - align-items: center; - border-color: var(--status-positive-background); - border-radius: var(--radius-round); - border-style: solid; - box-sizing: border-box; - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.ringActive_c9fddf { - border-width: 2px -} - -.hoverCard_fdda30 { - cursor: pointer; - transition: box-shadow,transform .2s ease -} - -.hoverCard_fdda30:focus-within,.hoverCard_fdda30:hover { - box-shadow: var(--shadow-high); - transform: translateY(-4px) -} - -.notice__29487 { - align-items: center; - background: linear-gradient(90deg,var(--color-scoped-expressive-background-nitro-1-start,rgba(179,38,156,.7)) 0,var(--color-scoped-expressive-background-nitro-1-end,rgba(20,20,203,.7)) 100%),var(--background-base-lowest); - display: flex; - height: 54px; - position: relative; - z-index: 101 -} - -@supports not ((grid-template-columns: subgrid) and (white-space-collapse:collapse)) { - .notice__29487 { - border-start-start-radius:0 - } -} - -@supports (grid-template-columns: subgrid) and (white-space-collapse:collapse) { - .notice__29487 { - grid-area:notice - } -} - -.noticeContent__29487 { - align-items: center; - display: flex; - flex: 1; - gap: var(--space-16); - justify-content: center -} - -.noticeText__29487 strong { - font-weight: var(--font-weight-semibold) -} - -.closeButton__29487 { - margin-inline:auto var(--space-12)} - -.premiumIcon_b68a35 { - height: 24px; - margin-inline-end:8px;position: relative; - top: 6px; - width: 24px -} - -.platformIcon_b68a35 { - display: inline-block; - height: 28px; - margin-top: -4px; - margin-inline-end:10px;position: relative; - vertical-align: middle; - width: 28px -} - -.platformIcon_b68a35+.platformIcon_b68a35 { - margin-inline-start:-10px} - -.giftIcon_b68a35 { - margin-inline-end:4px;vertical-align: text-bottom -} - -.icon_b68a35 { - display: inline-block; - height: 20px; - margin-inline-start:10px;margin-top: -3px; - position: relative; - vertical-align: middle; - width: 20px -} - -.icon_b68a35+.icon_b68a35 { - margin-inline-start:6px} - -.iconWindows_b68a35 { - -webkit-mask-image: url(/assets/20dd0e244b9a7065.svg); - mask-image: url(/assets/20dd0e244b9a7065.svg) -} - -.iconApple_b68a35,.iconWindows_b68a35 { - background-color: currentColor -} - -.iconApple_b68a35 { - -webkit-mask-image: url(/assets/d11dc1928431aa27.svg); - mask-image: url(/assets/d11dc1928431aa27.svg) -} - -.iconAndroid_b68a35 { - background-color: currentColor; - -webkit-mask-image: url(/assets/f625814fc53c325e.svg); - mask-image: url(/assets/f625814fc53c325e.svg) -} - -.iconUSFlag_b68a35 { - background-image: url(/assets/423c2b95bd0fdafb.png); - background-position: top; - background-repeat: no-repeat; - background-size: 85%; - margin-inline-end:7px;margin-top: 0 -} - -.icon_b68a35+.btn_b68a35 { - margin-inline-start:20px} - -.textLink_b68a35 { - color: var(--white); - padding-inline-start:10px;text-decoration: underline; - -webkit-app-region: no-drag -} - -.textLinkSmall_b68a35 { - font-size: 12px -} - -.testModeSKUSelector_b68a35 { - height: 24px; - margin-inline-start:16px} - -.premiumLogo_b68a35 { - background-image: url(/assets/a3541a1173e706be.svg); - background-repeat: no-repeat; - background-size: 100%; - display: inline-block; - height: 13px; - margin-inline-end:20px;position: relative; - top: 2px; - width: 51px -} - -.premiumText_b68a35 { - font-weight: var(--font-weight-medium) -} - -.premiumAction_b68a35 { - margin-inline-start:20px} - -.ellipsis_b68a35 { - margin-inline-start:7px} - -.quarantineLearnMoreLink_b68a35 { - margin-inline-start:10px;text-decoration: underline -} - -.errorCodeNoticeText_b68a35 { - margin-inline-start:var(--space-8)} - -.errorCodeNoticeClickable_b68a35 { - cursor: pointer -} - -.text__7b750 { - display: inline-block -} - -.text__7b750,.text__7b750 a { - color: currentColor -} - -.text__7b750 a { - text-decoration: underline -} - -.premiumIcon__7b750 { - height: 24px; - margin-inline-end:8px;position: relative; - top: 6px; - width: 24px -} - -.notice__36c3e { - align-items: center; - display: flex; - flex-direction: row; - justify-content: center -} - -.guildIcon__36c3e { - margin-inline-end:8px} - -.guildName__36c3e { - color: inherit -} - -.guildName__36c3e:hover { - text-decoration: underline -} - -.actionButton__36c3e { - margin-inline-start:12px;top: 0 -} - -.actionButtonInner__36c3e { - align-items: center; - display: flex; - gap: 4px -} - -.notice__30f28 { - align-items: center; - background-color: var(--brand-600); - box-shadow: none; - color: var(--white); - display: flex; - height: 40px; - justify-content: center; - padding: 0 8px -} - -.notice__30f28.error__30f28 { - background-color: var(--background-feedback-critical) -} - -.button__30f28 { - align-items: center; - font-weight: var(--font-weight-semibold); - height: -moz-fit-content; - height: fit-content; - padding: 4px 8px -} - -.button__30f28:hover { - background-color: var(--brand-530) -} - -.error__30f28>.button__30f28:hover { - background-color: var(--red-430) -} - -.header__30f28 { - color: var(--white); - display: inline; - margin-inline-end:16px} - -.backButtonInner__84419 { - display: flex; - gap: 8px; - padding: 2px 0 -} - -.backButton__84419:hover { - background-color: var(--brand-530) -} - -.backNotice__84419 { - background-color: var(--brand-600); - border-radius: 0; - padding: 8px -} - -.closeButton__84419 { - height: 100% -} - -.notice_c5cd6a { - background-color: var(--brand-600); - color: var(--white); - display: flex; - height: 40px; - justify-content: center; - padding: 0 8px -} - -.button_c5cd6a,.notice_c5cd6a { - align-items: center -} - -.button_c5cd6a { - font-weight: var(--font-weight-semibold); - height: -moz-fit-content; - height: fit-content; - padding: 4px 8px -} - -.button_c5cd6a:hover { - background-color: var(--brand-530) -} - -.back_c5cd6a { - bottom: 0; - height: 24px; - inset-inline-start: 8px; - margin: auto; - position: absolute; - top: 0 -} - -.iconButton_c5cd6a { - align-items: center; - display: flex -} - -.arrow_c5cd6a { - margin-inline-end:8px} - -.header_c5cd6a { - color: var(--white); - display: inline; - margin-inline-end:16px} - -.container__477aa { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-12); - justify-content: center -} - -.buttonGroup__477aa { - margin: var(--space-4) 0; - width: unset -} - -.blocked__477aa { - align-items: center; - display: flex -} - -.blockedIcon__477aa { - margin-inline:var(--space-12) var(--space-4)} - -.blockedText__477aa { - font-size: 12px; - font-weight: var(--font-weight-normal) -} - -.noIcon__477aa { - margin-inline-start:var(--space-12)} - -.container_a54d1d { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - margin: 16px; - padding-bottom: 80px; - padding-top: 80px -} - -.image_a54d1d { - margin-bottom: 16px; - max-width: 400px; - -o-object-fit: contain; - object-fit: contain -} - -.title_a54d1d { - color: var(--text-strong); - font-family: var(--font-display); - font-size: 18px; - font-weight: var(--font-weight-extra-bold); - line-height: 24px; - margin-bottom: 8px; - text-transform: none -} - -.body_a54d1d { - color: var(--text-default); - font-family: var(--font-primary); - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px -} - -.container_db811b { - align-self: flex-start; - display: grid; - grid-template-columns: auto auto; - grid-template-rows: auto auto; - margin-inline-end:16px} - -.mask_db811b { - grid-column: 1/span 2; - grid-row: 1/span 2 -} - -.image_db811b { - height: 100%; - width: 100% -} - -.imageContainer_db811b { - height: 100%; - width: 100% -} - -.imageContainer_db811b,.maskBackground_db811b { - align-items: center; - display: flex; - justify-content: center -} - -.maskBackground_db811b { - border-radius: 50%; - grid-column: 2/2; - grid-row: 2/2; - height: 20px; - width: 20px -} - -.maskIcon_db811b { - height: 12px; - width: 12px -} - -.row__6fddf { - cursor: pointer; - display: flex; - flex-direction: row; - gap: 16px; - padding: var(--space-xs) var(--space-sm); - position: relative -} - -.row__6fddf:hover { - background-color: var(--background-mod-subtle) -} - -.rowContent__6fddf { - display: flex; - flex: 1; - flex-direction: row; - justify-content: space-between -} - -.body__6fddf { - flex: 1; - margin-top: 2px -} - -.messagePreviewContainer__6fddf { - background-color: var(--background-base-low); - border-radius: 8px; - display: flex; - margin-bottom: 2px; - margin-top: 4px; - padding: 8px; - pointer-events: none -} - -.messagePreviewText__6fddf { - flex: 1; - margin: auto; - padding-inline-start:8px;width: 0 -} - -.calloutContainer__6fddf { - background-color: var(--background-base-low); - border-radius: 8px; - margin-bottom: 2px; - margin-top: 4px; - padding: 8px 16px -} - -.unread__6fddf { - background-color: var(--interactive-text-active); - border-radius: 50%; - height: 8px; - inset-inline-start: -4px; - position: absolute; - top: 28px; - width: 8px -} - -.friendRequestContainer__6fddf { - display: flex; - margin-top: 4px -} - -.button__6fddf { - cursor: pointer; - padding: 7px 16px -} - -.acceptButton__6fddf,.ignoreButton__6fddf { -} - -.acceptButton__6fddf { - margin-inline-end:12px} - -.lifecycleContainer__6fddf { - align-items: center; - display: flex; - margin-bottom: 1px -} - -.lifecycleText__6fddf { - padding-inline-start:4px} - -.checkbox__6fddf { - height: 16px; - width: 16px -} - -.emoji__6fddf { - margin-inline-start:4px} - -.container__0f711 { - background-color: var(--background-surface-high) -} - -.spinner__0f711 { - margin-bottom: 32px; - margin-top: 32px -} - -.loadingPlaceholder__0f711 { - margin-top: 100px -} - -.gatedContent__7184c { - height: 100% -} - -.title__7184c { - max-width: 640px; - text-align: center -} - -.description__7184c { - max-width: 440px; - text-align: center -} - -.divider__7641b { - background-color: var(--border-subtle)!important; - height: 1px; - margin: 16px 16px 0 -} - -.private__7641b { - align-items: center; - display: flex; - padding: 16px 16px 8px; - text-transform: uppercase -} - -.instructions__7641b { - padding: 0 16px -} - -.container__7641b.emptyState__7641b { - box-sizing: border-box; - flex-direction: column; - justify-content: flex-start; - padding: 16px; - text-align: center -} - -.emptyStateHeader__7641b { - text-align: start; - text-transform: uppercase -} - -.emptyStateIcon__7641b { - background-color: var(--background-base-lowest); - border-radius: 100%; - color: var(--interactive-text-default); - height: 56px; - width: 56px -} - -.emptyStateIcon__7641b,.emptyStateIconContainer__7641b { - align-items: center; - display: flex; - justify-content: center -} - -.emptyStateIconContainer__7641b { - margin: 32px 0; - position: relative -} - -.emptyStateStars__7641b { - margin: 0 auto; - position: absolute; - top: -13px -} - -.emptyStateSubtext__7641b { - margin-top: 8px -} - -.container__7641b { - background-color: var(--background-base-lower); - border-inline-start:1px solid var(--border-subtle)} - -.refresh-fast-follow-distinct-borders .container__7641b { - border-inline-start-color: var(--app-frame-border) -} - -.jumpButton_ed0c8c { - align-items: center; - -webkit-backdrop-filter: blur(8px); - backdrop-filter: blur(8px); - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-xs); - box-shadow: var(--shadow-medium); - box-sizing: border-box; - color: var(--control-secondary-text-default); - cursor: pointer; - font-weight: var(--font-weight-semibold); - height: auto; - inset-inline-end: var(--space-8); - padding: var(--space-4) var(--space-8); - position: absolute; - text-align: center; - top: var(--space-8); - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - z-index: 1 -} - -.jumpButton_ed0c8c .text_ed0c8c { - color: var(--text-default) -} - -.jumpButton_ed0c8c:hover .text_ed0c8c { - color: var(--interactive-text-hover) -} - -.jumpButton_ed0c8c:active .text_ed0c8c { - color: var(--interactive-text-active) -} - -.jumpButton_ed0c8c:hover { - background-color: var(--background-surface-higher) -} - -.channelHeader__35a7e { - align-items: center; - background-color: var(--background-surface-high); - box-sizing: border-box; - display: flex; - height: 64px; - padding-bottom: 12px; - padding-top: 12px; - padding-inline-start:18px;position: sticky; - top: 0; - z-index: 10 -} - -.channelHeader__35a7e.showCollapseButton__35a7e { - padding-inline-start:0} - -.collapseButton__35a7e { - color: var(--interactive-text-default); - cursor: pointer; - height: 16px; - padding: 4px; - width: 16px -} - -.collapseButton__35a7e.collapsed__35a7e { - transform: rotate(-90deg) -} - -.dmIcon__35a7e { - border-radius: 12px; - height: 40px; - width: 40px -} - -.dmIcon__35a7e,.guildIcon__35a7e { - cursor: pointer; - flex-shrink: 0; - margin-inline-end:16px} - -.guildIcon__35a7e { - background-color: var(--background-mod-normal) -} - -.channelNameSection__35a7e { - display: flex; - flex-direction: column; - flex-grow: 1; - margin-inline-end:16px;overflow: hidden -} - -.badge__35a7e { - margin-inline-start:8px} - -.channelName__35a7e,.subtext__35a7e { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - word-break: break-all -} - -.channelName__35a7e,.guildName__35a7e { - cursor: pointer; - display: inline-block; - min-width: 0; - width: 100% -} - -.channelName__35a7e:hover .channelNameSpan__35a7e,.guildName__35a7e:hover .channelNameSpan__35a7e { - text-decoration: underline -} - -.channelName__35a7e { - align-items: center; - display: flex -} - -.forumIcon__35a7e { - margin-inline-end:4px} - -.channelNameHeader__35a7e { - height: 20px -} - -.subtext__35a7e,.subtextContainer__35a7e { - max-height: 16px; - min-width: 0; - width: 100% -} - -.container_d404a3 { - align-items: center; - display: flex; - flex: 1; - flex-direction: column; - justify-content: center; - min-height: 400px; - padding: 0 16px; - text-align: center -} - -.iconContainer_d404a3 { - margin-bottom: 16px; - position: relative -} - -.icon_d404a3 { - background-color: var(--background-base-low); - border-radius: 80px; - color: var(--text-muted); - padding: 22px -} - -.stars_d404a3 { - inset-inline-start: -10px; - position: absolute -} - -.header_d404a3 { - margin-bottom: 8px -} - -.protip_d404a3 { - text-transform: uppercase -} - -.recentMentionsPopout__95796 { - max-height: 70vh; - max-width: 600px; - min-width: 480px; - width: 35vw -} - -.widerInbox__95796 { - max-width: 750px; - width: 560px -} - -.scroller__95796 { - padding: 0 -} - -.container__95796 { - margin-bottom: 16px; - padding-inline-end:8px} - -.closeButton__95796 { - flex-shrink: 0; - margin-inline-start:12px} - -.messageContainer__95796 { - background-color: var(--background-surface-higher); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - margin-inline-start:16px;padding: 16px; - padding-inline-start:0;position: relative -} - -.jumpMessageButton__95796 { - inset-inline-end: 16px; - opacity: 0; - top: 19px -} - -.messageContainer__95796:focus-within .jumpMessageButton__95796,.messageContainer__95796:hover .jumpMessageButton__95796 { - opacity: 1 -} - -.message__95796 { - overflow: hidden; - padding-inline-end:0} - -.messageContainer__95796 .message__95796:first-of-type { - margin-top: 0 -} - -.enable-forced-colors .messageContainer__95796 { - border: 2px solid CanvasText -} - -.checkbox__95796 { - flex-grow: 0 -} - -.friendRequestsButton__523aa { - align-items: center; - background-color: var(--control-secondary-background-default); - border: 1px solid var(--control-secondary-border-default); - border-radius: 8px; - color: var(--control-secondary-text-default); - cursor: pointer; - display: flex; - gap: 8px; - height: 32px; - padding: 0 8px -} - -.friendRequestsButton__523aa:hover { - background-color: var(--control-secondary-background-hover); - color: var(--interactive-text-active) -} - -.obscuredIcon__43090 { - height: 32px; - width: 32px -} - -.author_d3eee8 { - flex-shrink: 0 -} - -.author_d3eee8 a { - color: var(--text-muted) -} - -.author_d3eee8.hasUnreads_d3eee8 a { - color: var(--text-strong) -} - -.container_faa96b { - align-items: start; - border: 1px solid transparent; - box-sizing: border-box; - cursor: pointer; - display: flex; - flex-direction: row; - overflow: hidden; - padding: var(--space-12) calc(var(--space-8) + 2px); - position: relative -} - -.full-motion .container_faa96b { - transition: box-shadow .2s ease-out,transform .2s ease-out,background .2s ease-out,border .2s ease-out -} - -.container_faa96b:hover { - background-color: var(--background-surface-high); - border-color: var(--border-strong); - box-shadow: var(--shadow-low); - transform: none -} - -.container_faa96b:hover .dots_faa96b { - border-color: var(--background-base-low) -} - -.container_faa96b:active { - background-color: var(--interactive-background-active); - box-shadow: var(--shadow-border),var(--shadow-high); - transform: none -} - -.container_faa96b:active .dots_faa96b { - border-color: var(--interactive-background-active) -} - -.container_faa96b.isOpen_faa96b { - background-color: var(--background-surface-higher); - border-color: var(--border-strong); - box-shadow: var(--shadow-low); - transform: none -} - -.container_faa96b.isOpen_faa96b .dots_faa96b { - border-color: var(--interactive-background-selected) -} - -.focusTarget_faa96b { - height: 0; - pointer-events: none; - width: 0 -} - -.left_faa96b { - flex: 1 1 auto; - min-width: 0 -} - -.body_faa96b { - display: flex; - flex: 1; - flex-direction: column -} - -.body_faa96b,.messageFocusBlock_faa96b { - min-width: 0 -} - -.message_faa96b { - align-items: center; - display: flex; - max-height: 18px; - overflow: hidden -} - -.messageContent_faa96b { - color: unset; - font-size: 14px!important; - font-weight: var(--font-weight-medium); - height: 18px; - line-height: 18px!important; - margin-inline-start:4px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - word-break: break-word -} - -.messageContent_faa96b .emoji { - height: 18px -} - -.messageContentTrailingIcon_faa96b { - flex: 0 0 auto; - margin-inline-start:4px} - -.messageContentLeadingIcon_faa96b { - flex: 0 0 auto; - margin-inline-end:4px} - -.bodyMedia_faa96b { - align-items: center; - border-radius: var(--radius-sm); - box-sizing: border-box; - flex-shrink: 0; - height: 100%; - margin-inline-start:12px;min-height: 72px; - overflow: hidden; - position: relative; - width: 72px -} - -.obscuredTag_faa96b { - color: var(--opacity-black-60); - inset-inline-start: 50%; - position: absolute; - top: 50%; - transform: translate(-50%,-50%) -} - -.thumbnailContainer_faa96b { - align-items: center; - display: flex; - justify-content: center; - --__post-thumbnail-size: 72px -} - -.thumbnailContainer_faa96b,.thumbnailOverride_faa96b { - height: var(--__post-thumbnail-size); - max-height: var(--__post-thumbnail-size); - max-width: var(--__post-thumbnail-size); - min-height: var(--__post-thumbnail-size); - min-width: var(--__post-thumbnail-size); - width: var(--__post-thumbnail-size) -} - -.thumbnailOverride_faa96b { - -o-object-fit: cover; - object-fit: cover -} - -.obscured_faa96b { - filter: blur(var(--custom-forum-post-obscured-blur-radius)); - pointer-events: none -} - -.obscuredThumbnailPlaceholder_faa96b { - background-color: var(--background-mod-strong) -} - -.body_faa96b { - padding-bottom: 8px -} - -.header_faa96b { - display: flex; - padding-bottom: 6px -} - -.footer_faa96b { - align-items: center; - display: flex; - gap: 8px; - height: 20px; - position: relative -} - -.bullet_faa96b { - color: var(--border-subtle) -} - -.headerText_faa96b { - align-items: center; - display: flex; - flex-grow: 1; - flex-wrap: wrap; - margin-inline-end:8px;max-height: 48px; - overflow: hidden -} - -.headerText_faa96b .emoji { - height: 20px; - width: 20px -} - -.postTitleText_faa96b { - line-height: 24px -} - -.newBadgeWrapper_faa96b { - display: inline; - padding-inline-start:8px} - -.newBadge_faa96b { - border-radius: var(--radius-md); - color: var(--brand-560); - display: inline; - padding: 2px 8px; - position: relative; - top: -2px -} - -.newBadge_faa96b.inTagsRow_faa96b { - height: 20px; - position: inherit -} - -.typing_faa96b { - flex-direction: row -} - -.dots_faa96b,.typing_faa96b { - align-items: center; - display: flex -} - -.dots_faa96b { - background-color: var(--background-base-lowest); - border: 3px solid var(--background-base-low); - border-radius: var(--radius-md); - height: 16px; - justify-content: center; - margin-inline-start:4px;width: 24px -} - -.typingUsers_faa96b { - position: static -} - -.messageCountBox_faa96b { - align-items: center; - display: flex; - height: 18px; - padding-top: 1px -} - -.messageCountIcon_faa96b { - align-items: center; - color: var(--text-default); - display: flex; - height: 24px; - margin-inline-end:4px;margin-top: 1px -} - -.hasRead_faa96b .messageCountIcon_faa96b { - color: var(--icon-muted) -} - -.hasRead_faa96b .messageCountText_faa96b { - color: var(--interactive-text-default) -} - -.messageCountText_faa96b { - color: var(--text-default); - font-size: 14px; - font-weight: var(--font-weight-medium) -} - -.newMessageCount_faa96b { - margin-inline-start:4px} - -.addReactButton_faa96b { - align-items: center; - color: var(--interactive-text-default); - display: flex; - font-size: 14px; - font-weight: var(--font-weight-medium); - gap: 8px; - height: unset; - line-height: 18px; - margin: 0; - visibility: visible; - width: unset -} - -.addReactButton_faa96b:hover { - color: var(--interactive-text-active) -} - -.reactions_faa96b { - margin: 0; - padding: 0 -} - -.updateReactionButton_faa96b { - margin-inline-end:4px;margin-bottom: 0 -} - -.updateReactionButton_faa96b>div { - padding-bottom: 1px; - padding-top: 1px -} - -.timestampTooltip_faa96b { - max-width: none; - white-space: nowrap -} - -.blockedMessage_faa96b { - font-style: italic -} - -.withNewBadgeOverflow_faa96b .headerText_faa96b { - align-items: flex-start; - display: flex; - flex-direction: row -} - -.withNewBadgeOverflow_faa96b .postTitleText_faa96b { - flex: 1 -} - -.withNewBadgeOverflow_faa96b .newBadge_faa96b { - height: 20px; - top: 2px -} - -.tags__08166 { - font-family: var(--font-primary); - font-weight: var(--font-weight-medium); - gap: 8px; - height: 24px; - margin-bottom: 8px -} - -.pinIcon__08166,.tags__08166 { - align-items: center; - display: inline-flex -} - -.pinIcon__08166 { - background-color: var(--brand-500); - border-radius: 9999px; - flex-shrink: 0; - height: 22px; - justify-content: center; - width: 22px -} - -.tagFiltered__08166 { - background-color: var(--background-base-lowest) -} - -.forumPost__7d15e { - background-color: var(--background-base-low); - border-radius: 8px; - margin: 0 0 16px -} - -.forumPost__7d15e:hover { - box-shadow: none; - transform: none -} - -.container__7d15e .forumPost__7d15e:last-of-type { - margin-bottom: 0 -} - -.messages__1ccd1 { - background-color: var(--background-surface-higher); - border: 1px solid var(--border-subtle); - border-radius: 8px; - padding: 16px; - padding-inline-start:0} - -.messageContainer__1ccd1 { - position: relative -} - -.jumpButton__1ccd1 { - opacity: 0 -} - -.jumpButton__1ccd1:focus-within,.jumpButton__1ccd1:hover { - opacity: 1 -} - -.messageContainer__1ccd1:focus-visible .jumpButton__1ccd1,.messageContainer__1ccd1:hover .jumpButton__1ccd1 { - opacity: 1 -} - -.message__1ccd1 { - overflow: hidden; - padding-inline-end:0} - -.message__1ccd1 .mention { - pointer-events: none -} - -.messages__1ccd1 .messageContainer__1ccd1:first-of-type .message__1ccd1 { - margin-top: 0 -} - -.divider__1ccd1 { - margin-bottom: 16px; - margin-top: 16px; - margin-inline-start:16px} - -.showAllButtonContainer__1ccd1 { - padding-block-end:var(--space-8);padding-block-start: var(--space-8); - padding-inline-start:var(--space-16)} - -.channel__427f0 { - padding-bottom: 16px -} - -.buttonGroup__427f0,.collapseButton__427f0,.markReadButton__427f0 { - flex-shrink: 0 -} - -.markReadButton__427f0 { - margin-inline-start:12px} - -.divider__27703 { - margin-bottom: 32px; - margin-top: 48px -} - -.dividerContent__27703 { - background: transparent -} - -.container__2692d { - background-color: var(--background-surface-high); - border-radius: 8px; - box-shadow: var(--shadow-border),var(--shadow-high); - box-sizing: border-box; - display: flex; - flex-direction: column; - overflow: hidden; - position: relative; - z-index: 0 -} - -.widerInbox__2692d { -} - -.spinner__2692d { - margin-bottom: 32px; - margin-top: 32px -} - -.tutorial__2692d { - background-color: var(--background-base-lowest); - border-radius: 8px; - display: flex; - margin: 16px 0; - padding: 16px -} - -.tutorialIcon__2692d { - align-items: center; - background-color: var(--background-base-low); - border-radius: 40px; - color: var(--interactive-text-default); - display: flex; - flex-shrink: 0; - height: 40px; - justify-content: center; - margin-inline-end:16px;width: 40px -} - -.tutorialButton__2692d { - margin-top: 8px -} - -.scroller__2692d { - padding: 0 16px -} - -.expandedMarkAllReadContainer__2692d { - background-color: var(--background-base-low); - padding: 16px -} - -.enable-forced-colors .container__2692d { - border: 2px solid CanvasText -} - -.header_ab6641 { - box-shadow: none; - box-sizing: border-box; - padding: var(--space-md) var(--space-md) 0 -} - -.headerTitle_ab6641 { - align-items: center; - display: flex -} - -.headerTabs_ab6641 { - margin: 0 -16px -} - -.tabBar_ab6641 { - margin-top: 16px -} - -.tab_ab6641 { - align-items: center; - display: flex; - justify-content: center; - margin-bottom: -2px; - padding: 0 16px 16px -} - -.inboxIcon_ab6641 { - color: var(--icon-strong); - height: 20px; - margin-inline-end:8px;width: 20px -} - -.inboxTitle_ab6641 { - flex: 1 -} - -.controls_ab6641 { - align-items: center; - display: flex; - gap: 8px; - justify-content: center; - margin-inline-start:auto} - -.iconBadge_ab6641 { - margin-inline-start:8px} - -.closeButton_f6bd5f { - flex-shrink: 0; - margin-inline-start:var(--space-12)} - -.messageContainer_f6bd5f { - background-color: var(--background-base-low); - border: 1px solid transparent; - border-radius: var(--radius-sm); - display: flex; - flex-direction: column; - justify-content: center; - margin: 0 var(--space-16) var(--space-8); - padding-block:var(--space-16) var(--space-16);padding-inline: 0 var(--space-16) -} - -.messageSpinner_f6bd5f { - flex: 1 0 0 -} - -.channelMessageAndButtons_f6bd5f { - align-items: center; - display: flex -} - -.messageContainer_f6bd5f.messageSendScheduled_f6bd5f { - border-color: var(--green-430) -} - -.messageContainer_f6bd5f.messageSendError_f6bd5f { - border-color: var(--border-feedback-critical) -} - -.messageState_f6bd5f { - color: var(--text-muted); - padding-block:0 var(--space-8);padding-inline: var(--space-16) 0 -} - -.message_f6bd5f { - flex: 1 0 0; - max-width: calc(100% - var(--space-80) - var(--space-24)); - overflow: hidden; - padding-inline-end:0} - -.messageContainer_f6bd5f .message_f6bd5f:first-of-type { - margin-top: 0 -} - -.enable-forced-colors .messageContainer_f6bd5f { - border: 2px solid CanvasText -} - -.checkbox_f6bd5f { - flex-grow: 0 -} - -.container_f6bd5f { - background-color: var(--background-surface-high); - border-radius: 0 0 var(--radius-sm) var(--radius-sm); - box-shadow: var(--shadow-border),var(--shadow-high); - box-sizing: border-box; - display: flex; - flex-direction: column; - overflow: hidden; - position: relative; - z-index: 0 -} - -.widerInbox_f6bd5f { -} - -.channelRow_f6bd5f { - border-bottom: 1px solid var(--border-subtle) -} - -.cannotSendMessage_f6bd5f { - color: var(--text-muted); - margin: var(--space-16) -} - -.sendMessageToCurrentChannel_f6bd5f { - display: flex; - margin: var(--space-16) -} - -.sendMessageToCurrentChannelInput_f6bd5f { - flex: 1 0 0 -} - -.loadingPlaceholder_f6bd5f { - margin-top: 100px -} - -.container_fc561d { - background-color: var(--background-surface-high); - border-radius: 8px; - box-shadow: var(--shadow-border),var(--shadow-high); - box-sizing: border-box; - display: flex; - flex-direction: column; - overflow: hidden; - position: relative; - z-index: 0 -} - -.widerInbox_fc561d { -} - -.enable-forced-colors .container_fc561d { - border: 2px solid CanvasText -} - -.pill__4f4b6 { - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.notificationsIcon__4f4b6 { - display: block -} - -.full-motion .badgeTransition__4f4b6 { - transition: all .2s cubic-bezier(0,0,.2,1.1) -} - -.grayBadge__4f4b6 { - background-color: var(--background-mod-strong)!important; - color: var(--white); - font-size: 10px -} - -.grayBadge__4f4b6.selected__4f4b6 { - background-color: var(--interactive-text-active)!important; - color: var(--background-base-lowest) -} - -.headerButton_c485a2 { - align-items: center; - border-radius: var(--radius-xs); - cursor: pointer; - display: flex; - height: 32px; - justify-content: center; - width: 32px -} - -.headerButton_c485a2.enabled_c485a2,.headerButton_c485a2:hover { - background-color: var(--control-secondary-background-hover); - color: var(--text-strong) -} - -.container_f37cb1 { - position: relative; - transition: color 1s linear; - --custom-guild-banner-height: 130px -} - -.clickable_f37cb1 { - cursor: pointer -} - -.animatedBannerHoverLayer_f37cb1 { - cursor: default; - inset-inline-start: 0; - position: absolute; - top: var(--custom-channel-header-height); - width: 100%; - z-index: 100 -} - -.animatedContainer_f37cb1 { - background-color: var(--interactive-background-active); - box-shadow: var(--shadow-low); - overflow: hidden; - width: 100% -} - -.animatedContainer_f37cb1,.bannerImage_f37cb1 { - height: var(--custom-guild-banner-height); - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.bannerImage_f37cb1,.bannerImg_f37cb1 { - width: 240px -} - -.bannerImg_f37cb1 { - -o-object-fit: cover; - object-fit: cover -} - -.bannerImage_f37cb1,.bannerImg_f37cb1 { - height: var(--custom-guild-banner-height) -} - -.bannerImage_f37cb1,.bannerImg_f37cb1 { - aspect-ratio: 21/9; - width: calc(var(--custom-guild-sidebar-width) - var(--custom-guild-list-width)) -} - -.bannerImgFullWidth_f37cb1 { - width: 100% -} - -.bannerImage_f37cb1:before { - background: linear-gradient(to bottom,var(--background-mod-strong),transparent 100%); - content: ""; - display: none; - height: 58px; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.header_f37cb1 { - border-bottom: 1px solid var(--border-subtle); - box-shadow: none; - box-sizing: border-box; - font-family: var(--font-display); - font-weight: var(--font-weight-medium); - height: var(--custom-channel-header-height); - padding: var(--space-xs); - position: relative; - transition: background-color .1s linear; - z-index: 1 -} - -.header_f37cb1,.headerContent_f37cb1 { - align-items: center; - display: flex -} - -.headerContent_f37cb1 { - color: var(--text-strong); - gap: var(--space-xxs); - justify-content: space-between; - width: 100% -} - -.refresh-fast-follow-avatars .header_f37cb1 { - padding-inline-start:min(var(--space-md),18px)} - -.primaryInfo_f37cb1 { - height: unset; - justify-content: space-between -} - -.headerButton_f37cb1 { - inset: 0; - pointer-events: none; - position: absolute -} - -.headerChildren_f37cb1 { - position: relative; - z-index: 4 -} - -.communityInfoContainer_f37cb1 { - align-items: flex-end; - display: flex; - overflow: hidden; - position: absolute -} - -.communityInfoContainer_f37cb1.hasSubheader_f37cb1 { - margin-bottom: -12px; - position: relative -} - -.communityInfoVisible_f37cb1 .communityInfoContainer_f37cb1 { - height: 20px; - margin-top: 4px -} - -.communityInfoVisible_f37cb1 .communityInfo_f37cb1 { - color: var(--white) -} - -.communityInfoPill_f37cb1 { - align-items: center; - background: var(--background-mod-subtle); - border-radius: 10px; - display: flex; - padding: 2px 6px -} - -.communityIcon_f37cb1,.communityInfoPill_f37cb1 { - margin-inline-end:4px} - -.bannerVisible_f37cb1 .headerContent_f37cb1 { - filter: drop-shadow(0 1px 3px var(--opacity-black-60)) -} - -.communityInfoVisible_f37cb1 .header_f37cb1,.communityInfoVisible_f37cb1 .header_f37cb1:hover,.hasBanner_f37cb1 .header_f37cb1,.hasBanner_f37cb1 .header_f37cb1:hover { - border-bottom: none; - box-shadow: none -} - -.bannerVisible_f37cb1 .dropdown_f37cb1 { - opacity: 1 -} - -.name_f37cb1 { - margin-inline-end:auto;z-index: 1 -} - -.favoritesIcon_f37cb1 { - margin-inline-end:8px;margin-bottom: 2px -} - -.guildDropdown_f37cb1 { - align-items: center; - border-radius: 8px; - cursor: pointer; - display: flex; - gap: 2px; - height: 32px; - min-width: 0; - padding-block:0;padding-inline:var(--space-xs)} - -.guildDropdown_f37cb1: hover { - background-color:var(--background-mod-subtle) -} - -.guildBadgeAndName_f37cb1 { - align-items: center; - display: flex; - flex: 1 1 auto; - gap: var(--space-xs); - min-width: 0 -} - -.inviteButton_f37cb1 { - align-items: center; - border-radius: var(--radius-sm); - cursor: pointer; - display: flex; - flex: 0 0 auto; - height: 32px; - justify-content: center; - width: 32px -} - -.inviteButton_f37cb1:hover { - background-color: var(--background-mod-subtle) -} - -.refresh-fast-follow-distinct-borders .header_f37cb1 { - border-bottom-color: var(--app-frame-border) -} - -.enable-forced-colors .container_f37cb1 { - border-bottom: 2px solid CanvasText -} - -.enable-forced-colors .clickable_f37cb1 .header_f37cb1 { - background-color: ButtonFace; - color: ButtonText; - forced-color-adjust: none -} - -.enable-forced-colors .clickable_f37cb1 .header_f37cb1 .headerContent_f37cb1,.enable-forced-colors .clickable_f37cb1 .header_f37cb1 .name_f37cb1 { - color: ButtonText -} - -.enable-forced-colors .selected_f37cb1 .header_f37cb1 { - background-color: HighlightText; - color: Highlight -} - -.enable-forced-colors .selected_f37cb1 .header_f37cb1 .headerContent_f37cb1,.enable-forced-colors .selected_f37cb1 .header_f37cb1 .name_f37cb1 { - color: Highlight -} - -.theme-dark .themedHeaderMobile_f37cb1 { - background: var(--background-gradient-high,var(--__header-bar-background)) -} - -.theme-light .themedHeaderMobile_f37cb1 { - background: var(--background-gradient-low,var(--__header-bar-background)) -} - -.bannerVisible_f37cb1 .headerContent_f37cb1,.bannerVisible_f37cb1 .name_f37cb1 { - color: var(--white) -} - -.bannerVisible_f37cb1 .headerEllipseForeground_f37cb1 { - background: var(--opacity-black-52); - border-radius: 50%; - filter: blur(10px); - height: calc(var(--custom-channel-header-height)*2); - position: absolute; - top: calc((var(--custom-channel-header-height) - var(--space-xs)/2)*-1); - inset-inline: calc((var(--custom-guild-sidebar-width) - var(--custom-guild-list-width))*-1/4); - mix-blend-mode: multiply -} - -.bannerVisible_f37cb1 .headerEllipseBackdrop_f37cb1 { - -webkit-backdrop-filter: blur(2px); - backdrop-filter: blur(2px); - height: var(--custom-channel-header-height); - position: absolute; - top: 0; - inset-inline: 1px -} - -.bannerVisible_f37cb1 .headerGlass_f37cb1 { - -webkit-backdrop-filter: blur(30px); - backdrop-filter: blur(30px); - border-bottom: 1px solid var(--border-subtle); - box-sizing: border-box; - height: calc(1px + var(--custom-channel-header-height)); - inset-inline-start: 1px; - opacity: 1; - position: absolute; - top: 0; - width: calc(100% - 1px) -} - -.bannerVisible_f37cb1:hover .headerGlass_f37cb1 { - opacity: 1!important; - transition: opacity .15s ease-in-out -} - -.bannerVisible_f37cb1 .header_f37cb1 { - border-bottom: none; - border-start-start-radius: 12px; - border-top: 1px solid var(--app-frame-border) -} - -.settingsButton_f37cb1:hover .settingsButtonIcon_f37cb1 { - color: var(--interactive-text-hover) -} - -.settingsButtonIcon_f37cb1 { - --chat-input-icon-size: 18px -} - -.skeleton__9897f { - padding-inline-end:8px} - -.skeletonItem__9897f { - align-items: flex-start; - display: flex; - gap: 12px; - padding: 4px -} - -.lineContainer__9897f { - display: flex; - flex-direction: column; - gap: 4px; - width: 100% -} - -.avatar__9897f { - background-color: var(--background-mod-muted); - border-radius: 50%; - flex-shrink: 0; - height: 32px; - width: 32px -} - -.wrapper__9897f { - position: relative; - z-index: 0 -} - -.loadingAnimation__9897f { - background: var(--background-mod-muted); - -webkit-mask: linear-gradient(#000 0 0); - mask: linear-gradient(#000 0 0) -} - -.loadingAnimation__9897f:before { - animation: shimmer__9897f 2s cubic-bezier(.4,0,.2,1) infinite; - background: linear-gradient(-65deg,transparent 45%,var(--background-mod-muted) 50%,transparent 55%) 100%/300% 100%; - content: ""; - inset: 0; - position: absolute; - z-index: -1 -} - -.noAnimation__9897f .loadingAnimation__9897f:before { - display: none -} - -.line__9897f { - background-color: var(--background-mod-muted); - border-radius: 8px; - height: 16px -} - -.lineShort__9897f { - width: 35% -} - -.lineMedium__9897f { - width: 60% -} - -.lineLong__9897f { - width: 95% -} - -@keyframes shimmer__9897f { - to { - background-position: 0 - } -} - -.channelName_cb862a { - color: var(--text-muted); - line-height: 20px; - max-width: -moz-min-content; - max-width: min-content -} - -.channelName_cb862a.unread_cb862a { - color: var(--text-strong) -} - -.inlineIcon_cb862a { - flex-shrink: 0 -} - -.clanTag_cb862a { - margin-inline-start:4px} - -.messageContainer_cb862a { - box-sizing: border-box; - display: flex; - flex-direction: row; - gap: 12px; - margin-bottom: 2px; - padding: 4px 8px -} - -.messageClickableContainer_cb862a { - border-radius: 8px; - cursor: pointer; - flex-direction: column; - position: relative -} - -.messageClickableContainer_cb862a:hover .channelName_cb862a,.messageClickableContainer_cb862a:hover .message_cb862a { - color: var(--text-strong) -} - -.messageClickableContainer_cb862a.actionMenuOpen_cb862a .timestamp_cb862a,.messageClickableContainer_cb862a:focus .timestamp_cb862a,.messageClickableContainer_cb862a:focus-within .timestamp_cb862a,.messageClickableContainer_cb862a:hover .timestamp_cb862a { - display: none -} - -.messageClickableContainer_cb862a.actionMenuOpen_cb862a .mentionBadge_cb862a,.messageClickableContainer_cb862a:focus .mentionBadge_cb862a,.messageClickableContainer_cb862a:focus-within .mentionBadge_cb862a,.messageClickableContainer_cb862a:hover .mentionBadge_cb862a { - display: none -} - -.messageClickableContainer_cb862a.actionMenuOpen_cb862a .actions_cb862a,.messageClickableContainer_cb862a:focus .actions_cb862a,.messageClickableContainer_cb862a:focus-within .actions_cb862a,.messageClickableContainer_cb862a:hover .actions_cb862a { - display: flex -} - -.channelName_cb862a.selected_cb862a,.message_cb862a.selected_cb862a { - color: var(--text-strong) -} - -.messageClickableContainer_cb862a.actionMenuOpen_cb862a,.messageClickableContainer_cb862a:focus-within,.messageClickableContainer_cb862a:hover { - background: var(--interactive-background-hover) -} - -.messageClickableContainer_cb862a.selected_cb862a { - background: var(--interactive-background-selected) -} - -.messageClickableContainer_cb862a.selected_cb862a .mentionBadge_cb862a,.messageClickableContainer_cb862a.selected_cb862a .timestamp_cb862a { - display: none -} - -.messageClickableContainer_cb862a.selected_cb862a .actions_cb862a { - display: flex -} - -.message_cb862a { - color: var(--text-muted); - display: inline; - font-size: 14px; - font-weight: 400; - line-height: 20px; - max-height: 20px; - padding-inline-end:2px;pointer-events: none; - text-overflow: ellipsis; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - white-space: nowrap; - width: -moz-min-content; - width: min-content -} - -.message_cb862a .emoji { - height: 18px; - width: 18px -} - -.message_cb862a .obscured { - background-color: var(--__spoiler-background-color--revealed); - margin-inline-end:4px} - -.message_cb862a em,.message_cb862a h1,.message_cb862a h2,.message_cb862a h3,.message_cb862a h4,.message_cb862a h5,.message_cb862a h6,.message_cb862a span,.message_cb862a strong { - background: revert; - color: revert; - display: inline; - font-style: normal; - font-weight: 400; - padding: 0; - text-overflow: ellipsis; - white-space: nowrap; - width: -moz-min-content; - width: min-content -} - -.message_cb862a.unread_cb862a { - color: var(--text-strong)!important -} - -.message_cb862a.descriptionMessage_cb862a { - font-style: italic -} - -.username_cb862a { - color: var(--text-strong); - display: inline; - font-weight: 500; - line-height: 1.375rem -} - -.username_cb862a,.usernameTagContainer_cb862a { - flex: 0 1 auto -} - -.username_cb862a,.usernameOuterContainer_cb862a,.usernameTagContainer_cb862a { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.usernameOuterContainer_cb862a { - display: flex; - flex: 1 1 auto; - flex-direction: row; - gap: 4px; - justify-content: flex-start -} - -.timestamp_cb862a { - align-items: baseline; - color: var(--text-muted); - flex-wrap: nowrap; - font-size: .75rem; - line-height: 1.5rem -} - -.unreadDot_cb862a { - background-color: var(--interactive-text-active); - border-radius: 0 var(--radius-xs) var(--radius-xs) 0; - height: 8px; - inset-inline-start: -8px; - position: absolute; - top: 20px; - width: 4px -} - -.actionIcon_cb862a { - margin-inline:1px;transition: all .1s -} - -.action_cb862a { - align-items: center; - display: flex; - height: 18px; - justify-content: center; - width: 18px -} - -.action_cb862a:hover .actionIcon_cb862a { - margin-inline:0} - -.actions_cb862a { - display: none; - width: -moz-fit-content; - width: fit-content -} - -.groupedMessageUsername_cb862a { - flex-grow: 0; - flex-shrink: 1; - font-weight: 500; - line-height: 1.375rem; - max-width: 40%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.groupedMessageContent_cb862a { - flex: 1 0 60%; - overflow: hidden -} - -.overflowText_cb862a { - color: var(--text-muted); - line-height: 20px; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.messagesPopoutWrap__0aee3 { - box-sizing: border-box; - display: flex; - flex-direction: column; - overflow: hidden; - position: relative; - z-index: 0 -} - -.messagesPopoutWrap__0aee3 .mention { - pointer-events: none -} - -.header__0aee3 { - border-bottom: 1px solid var(--border-subtle); - padding: 16px; - position: relative -} - -.titleContainer__0aee3 { - gap: 8px -} - -.loadMore__0aee3,.titleContainer__0aee3 { - align-items: center; - display: flex -} - -.loadMore__0aee3 { - height: 16px; - justify-content: center; - opacity: 0 -} - -.loadMore__0aee3.showLoadMore__0aee3 { - opacity: 1 -} - -.messagesPopout__0aee3 { - padding-block:0;padding-inline:8px 0} - -.scrollingFooterWrap__0aee3 { - position: relative -} - -.emptyPlaceholder__0aee3 { - align-items: center; - display: flex; - flex: 0 0 auto; - flex-direction: column; - margin-bottom: 32px; - margin-top: 8px -} - -.emptyPlaceholder__0aee3.bottom__0aee3 { - align-items: center; - margin-bottom: 0 -} - -.emptyPlaceholder__0aee3.bottom__0aee3 .image__0aee3 { - margin-top: 25px -} - -.emptyPlaceholder__0aee3.loadingPlaceholder__0aee3 { - margin-top: 100px -} - -.image__0aee3 { - background-position: 50%; - background-repeat: no-repeat; - background-size: 94px var(--custom-messages-popout-messages-popout-footer-height); - height: var(--custom-messages-popout-messages-popout-footer-height); - width: var(--custom-messages-popout-messages-popout-footer-height) -} - -.body__0aee3 { - color: var(--text-default); - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px; - margin-top: 20px; - text-align: center; - white-space: pre -} - -.loadingPlaceholder__0aee3 { - margin-top: 240px -} - -.enable-forced-colors .messagesPopoutWrap__0aee3 { - border: 2px solid CanvasText -} - -.messagesGroupHeaderWrap__0aee3 { - align-items: center; - cursor: pointer; - height: 24px; - padding: 8px 8px 0 -} - -.messagesGroupHeaderWrap__0aee3:hover .messagesGroupHeader__0aee3 { - color: var(--text-strong)!important -} - -.messagesGroupHeaderWrap__0aee3:nth-last-child(2) { - padding-bottom: 8px -} - -.messagesGroupHeader__0aee3 { - display: flex -} - -.chevron__0aee3 { - transition: transform .1s linear -} - -.collapsed__0aee3 .chevron__0aee3 { - --custom-icon-collapse-rotate: -90deg; - transform: rotate(var(--custom-icon-collapse-rotate)) -} - -.emptyStateContainer__0aee3 { - align-items: center; - display: flex; - height: 100%; - width: 100% -} - -.container_ea5470 { - align-items: stretch; - display: flex; - flex: 1 1 auto; - flex-direction: column; - justify-content: flex-start; - min-height: 0; - position: relative -} - -.panelSpacing_ea5470 { - padding-bottom: var(--custom-app-panels-height) -} - -.messageList_ea5470 { - border-radius: 0; - flex-grow: 1; - max-height: 100%; - width: 100% -} - -.caughtUpContainer_ea5470 { - background-color: var(--background-feedback-positive); - display: flex; - flex-direction: row; - gap: 4px; - justify-content: center; - min-height: -moz-fit-content; - min-height: fit-content; - overflow: hidden; - padding-bottom: 8px; - padding-top: 8px; - position: relative -} - -.caughtUpContainer_ea5470.hide_ea5470 { - display: none -} - -.caughtUpContent_ea5470 { - display: flex; - flex-direction: row; - gap: 8px; - justify-content: center; - max-height: 26px; - overflow: hidden; - padding: 4px 12px; - width: -moz-fit-content; - width: fit-content -} - -.full-motion .caughtUpContainer_ea5470 { - transition: opacity .3s ease -} - -.full-motion .caughtUpContainer_ea5470.hide_ea5470 { - display: flex; - opacity: 0; - padding-bottom: 0; - padding-top: 0; - transition: padding .3s ease,opacity 2s ease-in -} - -.full-motion .hide_ea5470 .caughtUpContent_ea5470 { - max-height: 0; - padding: 0; - transition: padding .3s ease,max-height .3s ease -} - -.caughtUpText_ea5470 { - flex: 1 0 auto; - text-align: center -} - -.full-motion .headerButton_ea5470 { - transition: all .3s ease -} - -.headerButton_ea5470 { - align-items: center; - border-radius: var(--radius-xs); - display: flex; - justify-content: center; - padding: 4px -} - -.headerButton_ea5470:hover { - background-color: var(--control-secondary-background-hover); - color: var(--text-strong) -} - -.emptyStateContainer_ea5470 { - align-items: center; - display: flex; - height: 100%; - width: 100% -} - -.filterHeaderWrap_ea5470 { - height: 24px; - padding: 8px 16px 6px -} - -.container_fc71d3 { - border: 1px solid var(--background-base-low); - box-shadow: var(--shadow-high); - container-type: inline-size; - display: flex; - flex: 0 0 auto; - flex-direction: column; - height: calc(100vh - var(--custom-app-top-bar-height) - var(--custom-app-panels-height) - 1rem); - max-height: 640px; - min-height: 0; - min-width: 320px; - width: calc(var(--custom-guild-sidebar-width) - var(--custom-guild-list-width) + 10px) -} - -.backgroundContainer_fc71d3,.container_fc71d3 { - border-radius: var(--radius-md); - overflow: hidden -} - -.backgroundContainer_fc71d3 { - border: 1px solid var(--app-frame-border); - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.background_fc71d3 { - background: var(--background-gradient-highest,var(--background-base-low)); - border-radius: var(--radius-md); - height: 100vh; - inset-inline-start: calc(var(--custom-guild-list-width)*-1); - position: absolute; - top: calc(var(--custom-app-top-bar-height)*-1); - width: 100vw -} - -.container__3b95d { - height: 24px; - overflow: hidden; - padding: 8px; - pointer-events: none; - position: absolute; - z-index: 2 -} - -.bar__3b95d { - align-items: center; - border-radius: 12px; - box-shadow: 0 2px 6px var(--opacity-black-24); - color: var(--text-strong); - cursor: pointer; - display: flex; - font-family: var(--font-display); - font-size: 12px; - font-weight: var(--font-weight-semibold); - height: 24px; - justify-content: center; - line-height: 16px; - pointer-events: auto; - position: relative; - text-transform: uppercase; - -webkit-app-region: no-drag; - transition-delay: 0s; - transition-property: visibility -} - -.bar__3b95d.hidden__3b95d { - pointer-events: none; - transition-delay: .2s; - visibility: hidden -} - -.active__3b95d:active { - top: 1px -} - -.unread__3b95d { - background-color: var(--background-surface-highest); - border: 1px solid var(--border-normal) -} - -.mention__3b95d { - background-color: var(--background-feedback-notification); - border: 1px solid var(--border-subtle); - box-shadow: var(--shadow-medium); - box-sizing: border-box; - color: var(--text-overlay-light) -} - -.mention__3b95d:active { - background-color: var(--red-430) -} - -.icon__3b95d,.text__3b95d { - display: block -} - -.icon__3b95d { - color: var(--icon-strong); - height: 12px; - margin-inline-start:2px;width: 12px -} - -.enable-forced-colors .bar__3b95d { - background-color: Highlight; - border: 1px solid HighlightText; - color: HighlightText; - forced-color-adjust: none; - outline: 2px solid Canvas -} - -.enable-forced-colors .mention__3b95d { - opacity: 1 -} - -.listItemWrapper__91816:active { - transform: translateY(1px) translateZ(0) -} - -.listItemTooltip__91816,.listItemTooltipContent__91816 { - font-size: 16px; - font-weight: var(--font-weight-semibold); - line-height: 20px; - max-width: 196px; - word-wrap: break-word -} - -.listItemTooltipContent__91816 { - color: var(--text-default) -} - -.enable-forced-colors .listItemWrapper__91816 { - background-color: ButtonFace; - border: 1px solid CanvasText; - border-radius: 4px -} - -.enable-forced-colors .listItemWrapper__91816:hover { - border-color: ButtonText -} - -.enable-forced-colors .listItemWrapper__91816.selected__91816 { - background-color: HighlightText; - border-color: Highlight; - outline: 2px solid Highlight -} - -.pill_ed9a5f { - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.circleIconButton__5bc7e { - align-items: center; - background-color: var(--background-mod-subtle); - box-sizing: border-box; - color: var(--text-default); - cursor: pointer; - display: flex; - height: var(--guildbar-avatar-size); - justify-content: center; - transition: color .15s ease-out,background-color .15s ease-out; - width: var(--guildbar-avatar-size) -} - -.circleIconButton__5bc7e.selected__5bc7e,.circleIconButton__5bc7e:hover:not(.disabled__5bc7e) { - background-color: var(--background-brand) -} - -.circleIconButton__5bc7e.selected__5bc7e { - color: var(--white) -} - -.circleIcon__5bc7e { - height: 20px; - width: 20px -} - -.pill__5bc7e { - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.custom-theme-background .circleIconButton__5bc7e { - background-color: var(--background-mod-strong) -} - -.enable-forced-colors .circleIconButton__5bc7e,.enable-forced-colors .circleIconButton__5bc7e.selected__5bc7e { - background-color: ButtonFace; - color: ButtonText -} - -.wrapper_d144f8 { - align-items: stretch; - bottom: -4px; - display: flex; - flex-direction: column; - position: absolute; - top: -16px; - inset-inline: 0; - pointer-events: none -} - -.target_d144f8 { - flex: 1 0 50% -} - -.target_d144f8.dragOver_d144f8:before { - background-color: var(--status-positive); - border-radius: 2px; - box-shadow: 0 0 3px var(--opacity-black-28); - content: ""; - height: 4px; - position: absolute; - top: 10px; - inset-inline: 4px; - z-index: 1 -} - -.centerTarget_d144f8 { - flex: 1 0 50% -} - -.centerTarget_d144f8.dragOver_d144f8:before { - background-color: var(--status-positive); - border-radius: 50%; - box-shadow: 0 0 3px var(--opacity-black-28); - content: ""; - height: 16px; - inset-inline-start: 4px; - margin: auto; - position: absolute; - top: 28px; - width: 16px; - z-index: 1 -} - -.autoPointerEvents_d144f8 { - pointer-events: auto -} - -.folderEndWrapper_d144f8 { - height: 0; - position: relative -} - -.folderEndWrapper_d144f8.wrapperOver_d144f8 { - height: 24px -} - -.folderTarget_d144f8 { - display: flex; - flex-direction: column; - height: 24px; - position: absolute; - top: -24px; - inset-inline: 0; - justify-content: space-between; - pointer-events: none -} - -.folderTarget_d144f8 .dragOver_d144f8:before { - bottom: 0; - top: unset -} - -.enable-forced-colors .centerTarget_d144f8.dragOver_d144f8:before,.enable-forced-colors .target_d144f8.dragOver_d144f8:before { - background-color: Highlight; - outline: 2px solid Canvas -} - -.circleIconButton_a2be55 { - align-items: center; - box-sizing: border-box; - cursor: pointer; - display: flex; - height: 40px; - justify-content: center; - transition: color .15s ease-out,background-color .15s ease-out; - width: 40px -} - -.geoRestrictedBadge_a2be55 { - background-color: var(--status-warning-background); - color: var(--status-warning-text) -} - -.progressContainer__81ae5 { - height: 20px; - position: relative; - width: 20px -} - -.downloadIcon__81ae5 { - color: var(--white); - height: 100%; - padding: 4px; - position: relative; - width: 100% -} - -.pill__1f388 { - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.downloadProgress__1f388 { - inset-inline-end: -2px; - position: relative; - top: -2px -} - -.homeIcon__1f388 { - display: block; - height: 20px; - width: 28px -} - -.tutorialContainer__1f388 { - position: relative -} - -.guildSeparator__252b6 { - background-color: var(--app-frame-border); - border-radius: 1px; - height: 1px; - width: 32px -} - -.fullWidth__252b6 { - width: 100% -} - -.guildsError_e8d03f { - align-items: center; - background-color: var(--background-base-low); - border: 2px solid var(--background-feedback-critical); - border-radius: var(--radius-md); - box-sizing: border-box; - color: var(--interactive-text-active); - display: flex; - height: var(--guildbar-avatar-size); - justify-content: center; - transition: background-color .15s ease-out; - width: var(--guildbar-avatar-size) -} - -.guildsError_e8d03f:hover { - background-color: var(--background-feedback-critical); - color: var(--text-feedback-critical) -} - -.folderGroup__48112 { - --custom-folder-padding: calc((var(--guildbar-folder-size) - var(--guildbar-avatar-size))/2); - --custom-folder-preview-padding: var(--space-4); - --custom-folder-preview-gap: 2px; - --custom-folder-preview-guild-size: calc((var(--guildbar-folder-size) - var(--custom-folder-preview-padding)*2 - var(--custom-folder-preview-gap))/2); - position: relative; - width: var(--custom-guild-list-width) -} - -.folderGroupBackground__48112 { - background-color: var(--background-mod-subtle); - border: 1px solid var(--border-muted); - border-radius: var(--radius-lg); - bottom: 0; - box-sizing: border-box; - inset-inline-start: calc(var(--custom-guild-list-padding) - (var(--guildbar-folder-size) - var(--guildbar-avatar-size))/2); - opacity: 0; - position: absolute; - top: 0; - transition: opacity var(--custom-folder-item-animation-duration) ease-out,background-color var(--custom-folder-item-animation-duration) ease-out; - width: var(--guildbar-folder-size) -} - -.isExpanded__48112 .folderGroupBackground__48112 { - --custom-folder-background: color-mix(in srgb,var(--custom-folder-color),transparent 85%); - background-color: var(--custom-folder-background,var(--background-mod-subtle)); - opacity: 1 -} - -.folderHeader__48112 { - box-sizing: border-box; - height: var(--guildbar-folder-size); - width: var(--guildbar-folder-size) -} - -.folderGroup__48112[data-drop-hovering=true].isExpanded__48112 .folderGroupBackground__48112 { - background-color: var(--opacity-green-28); - border-color: var(--status-positive) -} - -.folderGroup__48112[data-drop-hovering=true]:not(.isExpanded__48112) .folderHeader__48112 { - background-color: var(--opacity-green-28); - border-radius: 12px; - outline: 1px solid var(--status-positive) -} - -.folderButton__48112,.folderButtonInner__48112 { - box-sizing: border-box; - cursor: pointer; - height: var(--guildbar-folder-size); - width: var(--guildbar-folder-size) -} - -.folderButtonInner__48112 { - border-radius: var(--radius-md); - overflow: hidden; - position: relative -} - -.folderButtonContent__48112 { - position: relative; - transform: translate3d(0,calc(var(--guildbar-folder-size)*-1),0) -} - -.full-motion .folderButtonContent__48112 { - transition: transform var(--custom-folder-item-animation-duration) ease-out -} - -.isExpanded__48112 .folderButtonContent__48112 { - transform: translateZ(0) -} - -.folderDragPreview__48112 .folderIconWrapper__48112 { - display: none -} - -.folderIconWrapper__48112 { - box-sizing: border-box; - padding: var(--custom-folder-padding) -} - -.folderIcon__48112 { - align-items: center; - border-radius: var(--radius-md); - box-sizing: border-box; - color: var(--custom-folder-color,var(--background-brand)); - display: flex; - height: var(--guildbar-avatar-size); - justify-content: center; - transition: background-color var(--custom-folder-item-animation-duration) ease-out; - width: var(--guildbar-avatar-size) -} - -.folderIcon__48112:hover,.keyboard-mode .folderButton__48112:focus .folderIcon__48112 { - background-color: var(--background-mod-subtle) -} - -.folderPreviewWrapper__48112 { - --custom-preview-background: color-mix(in srgb,var(--custom-folder-color),transparent 60%); - background-color: var(--custom-preview-background,var(--background-mod-subtle)); - border-radius: var(--radius-lg); - box-sizing: border-box; - height: var(--guildbar-folder-size); - padding: var(--custom-folder-preview-padding); - width: var(--guildbar-folder-size) -} - -.custom-theme-background .folderPreviewWrapper__48112 { - background-color: var(--custom-preview-background,var(--background-mod-strong)) -} - -.folderPreview__48112 { - box-sizing: border-box; - display: flex; - flex-wrap: wrap; - gap: 2px; - height: calc(var(--guildbar-folder-size) - var(--custom-folder-preview-padding)*2); - width: calc(var(--guildbar-folder-size) - var(--custom-folder-preview-padding)*2) -} - -.folderPreviewGuildIcon__48112 { - box-sizing: border-box; - height: var(--custom-folder-preview-guild-size); - width: var(--custom-folder-preview-guild-size) -} - -.folderPreviewGuildIcon__48112:first-child { - border-radius: 13px 4px 4px -} - -.folderPreviewGuildIcon__48112:nth-child(2) { - border-radius: 4px 13px 4px 4px -} - -.folderPreviewGuildIcon__48112:nth-child(3) { - border-radius: 4px 4px 4px 13px -} - -.folderPreviewGuildIcon__48112:nth-child(4) { - border-radius: 4px 4px 13px -} - -.folderPreviewGuildIconError__48112 { - align-items: center; - background: var(--background-feedback-critical); - color: var(--text-feedback-critical); - display: flex; - justify-content: center; - padding: 3px -} - -.folderGuildsList__48112 { - overflow: hidden -} - -.folderGuildsList__48112>:first-child { - margin-top: calc(var(--space-xs) - var(--custom-folder-padding)) -} - -.folderHeaderSmall__48112 { - --custom-folder-preview-padding: 3px; - --custom-folder-preview-guild-size: calc((var(--guildbar-avatar-size) - var(--custom-folder-preview-padding)*2 - var(--custom-folder-preview-gap))/2); - margin: calc((var(--guildbar-avatar-size) - var(--guildbar-folder-size))/2) 0 -} - -.isExpanded__48112 .folderHeaderSmall__48112 { - margin: 0 -} - -.refresh-fast-follow-avatars .folderButton__48112 { - padding: var(--custom-folder-padding) -} - -.refresh-fast-follow-avatars .folderButtonInner__48112 { - height: var(--guildbar-avatar-size); - width: var(--guildbar-avatar-size) -} - -.refresh-fast-follow-avatars .folderButtonContent__48112 { - transform: translate3d(0,calc(var(--guildbar-avatar-size)*-1),0) -} - -.refresh-fast-follow-avatars .isExpanded__48112 .folderButtonContent__48112 { - transform: translateZ(0) -} - -.refresh-fast-follow-avatars .folderIconWrapper__48112 { - padding: 0 -} - -.refresh-fast-follow-avatars .folderButton__48112:focus .folderIcon__48112,.refresh-fast-follow-avatars .folderIcon__48112:hover { - background-color: var(--opacity-white-12) -} - -.refresh-fast-follow-avatars .folderPreviewWrapper__48112 { - border-radius: var(--radius-md); - height: var(--guildbar-avatar-size); - width: var(--guildbar-avatar-size) -} - -.refresh-fast-follow-avatars .folderPreview__48112 { - height: calc(var(--guildbar-avatar-size) - var(--custom-folder-preview-padding)*2); - width: calc(var(--guildbar-avatar-size) - var(--custom-folder-preview-padding)*2) -} - -.refresh-fast-follow-avatars .folderPreviewGuildIcon__48112:first-child { - border-radius: 10px 4px 4px -} - -.refresh-fast-follow-avatars .folderPreviewGuildIcon__48112:nth-child(2) { - border-radius: 4px 10px 4px 4px -} - -.refresh-fast-follow-avatars .folderPreviewGuildIcon__48112:nth-child(3) { - border-radius: 4px 4px 4px 10px -} - -.refresh-fast-follow-avatars .folderPreviewGuildIcon__48112:nth-child(4) { - border-radius: 4px 4px 10px -} - -.isHovering__48112 { - display: block -} - -.dragInner__87847 { - background-color: var(--background-mod-normal); - border: 1px dashed var(--border-subtle); - border-radius: 12px; - box-sizing: border-box; - height: var(--guildbar-avatar-size); - width: var(--guildbar-avatar-size) -} - -.placeholderMask__87847 { - align-items: center; - display: flex; - justify-content: center -} - -.isFolder__87847 { - height: var(--guildbar-folder-size); - width: var(--guildbar-folder-size) -} - -.refresh-fast-follow-avatars .isFolder__87847 { - height: var(--guildbar-avatar-size); - margin: calc((var(--guildbar-folder-size) - var(--guildbar-avatar-size))/2); - width: var(--guildbar-avatar-size) -} - -.blobContainer_e5445c:active { - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - position: relative; - transform: translateY(1px) translateZ(0) -} - -.pill_e5445c { - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.sorting_e5445c { - pointer-events: none -} - -@keyframes wobble_e5445c { - 0%,to { - transform: rotate(0) - } - - 25% { - transform: rotate(2deg) - } - - 75% { - transform: rotate(-2deg) - } -} - -.full-motion .wobble_e5445c { - animation-duration: .2s; - animation-iteration-count: infinite; - animation-name: wobble_e5445c; - transform-origin: 50% 30% -} - -.enable-forced-colors .blobContainer_e5445c { - background-color: ButtonFace; - border: 1px solid CanvasText; - border-radius: 4px; - margin-bottom: -2px -} - -.enable-forced-colors .blobContainer_e5445c:hover { - border-color: ButtonText -} - -.enable-forced-colors .blobContainer_e5445c.selected_e5445c { - background-color: HighlightText; - border-color: Highlight; - outline: 2px solid Highlight -} - -.pauseBackground_d70e0d { - align-self: center; - background-color: var(--background-mod-strong); - border-radius: 50%; - display: flex; - height: 16px; - justify-content: center; - justify-self: center; - overflow: hidden; - width: 16px -} - -.pause_d70e0d { - align-self: center; - color: var(--interactive-text-active) -} - -@value popoutBackgroundColor var(--background-surface-highest);@value popoutWidth 256px;.pendingFolderButtonIcon__93fc9 { - align-items: center; - background-color: var(--background-mod-subtle); - border-radius: var(--radius-md); - box-sizing: border-box; - color: var(--text-default); - display: flex; - height: var(--guildbar-avatar-size); - justify-content: center; - transition: background-color .2s ease-out; - width: var(--guildbar-avatar-size) -} - -.pendingFolderButtonIcon__93fc9:hover { - background-color: var(--background-mod-normal) -} - -.container__93fc9 { - display: flex; - flex-direction: column -} - -.popoutAnchor__93fc9 { - align-self: center; - width: 48px -} - -.popoutContainer__93fc9 { - background: var(--background-surface-highest); - border-radius: var(--radius-md); - box-shadow: var(--shadow-border),var(--shadow-high); - display: flex; - flex-direction: column; - position: relative; - width: 256px -} - -.popoutCaret__93fc9,.popoutCaretLeft__93fc9 { - border: 6px solid; - border-color: var(--background-surface-highest); - bottom: -6px; - height: 0; - inset-inline-start: 18px; - position: absolute; - transform: rotate(45deg); - width: 0; - z-index: 1 -} - -.popoutCaretLeft__93fc9 { - inset-inline-start: -6px; - top: 18px -} - -.popoutContent__93fc9 { - box-sizing: border-box; - padding: 16px; - width: 256px; - z-index: 2 -} - -.coachmarkTextContainer__93fc9 { - display: flex; - flex-direction: column; - gap: 8px; - margin-bottom: 16px -} - -.wrapper_ef3116 { - background-color: var(--background-base-lowest); - display: flex; - flex-direction: column; - flex-shrink: 0; - margin-bottom: calc(var(--custom-app-panels-height, 0) + var(--space-xs) - 16px); - -webkit-mask: linear-gradient(180deg,#000,transparent) bottom /100% 16px no-repeat,linear-gradient(#000,#000) top /100% calc(100% - 15px) no-repeat; - mask: linear-gradient(180deg,#000,transparent) bottom /100% 16px no-repeat,linear-gradient(#000,#000) top /100% calc(100% - 15px) no-repeat; - overflow: hidden; - position: relative; - width: var(--custom-guild-list-width) -} - -.wrapper_ef3116.hidden_ef3116 { - visibility: hidden; - width: 0 -} - -.refresh-fast-follow-guild-bg .wrapper_ef3116 { - background-color: var(--app-frame-background) -} - -@supports (grid-template-columns: subgrid) and (white-space-collapse:collapse) { - .wrapper_ef3116 { - grid-area:guildsList - } -} - -.platform-osx .wrapper_ef3116 { - margin-top: 0 -} - -.tree_ef3116 { - position: relative -} - -.itemsContainer_ef3116,.tree_ef3116 { - display: flex; - flex-direction: column; - height: 100% -} - -.itemsContainer_ef3116 { - justify-content: space-between -} - -.scroller_ef3116 { - flex: 0 1 auto; - padding: 12px 0 0; - padding-bottom: calc(var(--space-xs, 8px) + 16px); - padding-top: 0; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.scroller_ef3116>[hidden] { - margin-top: calc(var(--space-xs)*-1); - visibility: hidden -} - -.scroller_ef3116.scrolling_ef3116>* { - pointer-events: none -} - -.platform-osx .scroller_ef3116,.platform-win .scroller_ef3116 { - padding-top: 4px -} - -.platform-overlay .scroller_ef3116 { - padding-top: 12px -} - -.unreadMentionsIndicatorBottom_ef3116,.unreadMentionsIndicatorTop_ef3116 { - box-sizing: border-box; - height: auto; - inset-inline: 0; - overflow: hidden; - padding: 8px; - pointer-events: none; - position: absolute; - width: var(--custom-guild-list-width); - z-index: 10 -} - -.unreadMentionsIndicatorTop_ef3116 { - padding-top: 8px -} - -.unreadMentionsIndicatorBottom_ef3116 { - padding-bottom: calc(var(--space-xs, 8px) + 16px) -} - -.unreadMentionsBar_ef3116:before { - position: absolute; - top: 0; - inset-inline: -8px; - bottom: 0; - content: "" -} - -.unreadMentionsIndicatorTop_ef3116 { - top: 0 -} - -.unreadMentionsIndicatorTop_ef3116 .unreadMentionsBar_ef3116:before { - top: -8px -} - -.unreadMentionsIndicatorBottom_ef3116 { - bottom: 0 -} - -.unreadMentionsIndicatorBottom_ef3116 .unreadMentionsBar_ef3116:before { - bottom: -8px -} - -.unreadMentionsFixedFooter_ef3116 { - bottom: 0 -} - -.discoveryIcon_ef3116 { - color: var(--text-default) -} - -.platform-osx .unreadMentionsIndicatorTop_ef3116 .unreadMentionsBar_ef3116 { - -webkit-app-region: unset -} - -.custom-theme-background .itemsContainer_ef3116 { - background: var(--background-gradient-app-frame,var(--background-base-lowest)) -} - -.bottomRailNotifCenterButton_ef3116 { - padding-bottom: 12px; - padding-top: 8px -} - -.bottomRailNotifCenterButton_ef3116:before { - background: linear-gradient(to bottom,transparent,var(--background-base-lowest) 75%,var(--background-base-lowest) 100%); - content: ""; - height: 16px; - position: absolute; - translate: 0 -22px; - width: 100% -} - -.custom-theme-background .bottomRailNotifCenterButton_ef3116 { - padding-top: 12px -} - -.custom-theme-background .bottomRailNotifCenterButton_ef3116:before { - display: none -} - -.unreadMentionsFixedFooterBottomNotifCenterEntrypoint_ef3116 { - bottom: 76px -} - -.content__65844 { - align-items: center; - display: flex; - gap: var(--space-xs); - justify-content: center -} - -.clickable_c69b6d { - cursor: pointer -} - -.containerDefault_c69b6d,.containerDragAfter_c69b6d,.containerDragBefore_c69b6d,.containerUserOver_c69b6d { - position: relative; - transition: opacity .2s ease-in-out -} - -.containerDragAfter_c69b6d:after,.containerDragBefore_c69b6d:before { - background-color: var(--green-360); - border-radius: 2px; - box-shadow: 0 0 3px var(--opacity-black-40); - content: ""; - height: 4px; - inset-inline: 8px 0; - position: absolute; - z-index: 1 -} - -.containerDragBefore_c69b6d:before { - top: -2px -} - -.containerDragAfter_c69b6d:after { - bottom: -2px -} - -.containerUserOver_c69b6d:after { - background-color: hsl(var(--green-360-hsl)/.1); - border: 2px solid hsl(var(--green-360-hsl)/.5); - border-radius: 4px; - box-sizing: border-box; - content: ""; - position: absolute; - top: 0; - inset-inline: 8px 0; - bottom: 0; - z-index: 1 -} - -.iconBase_c69b6d { - cursor: pointer; - line-height: 0; - position: relative -} - -.iconItem_c69b6d { - display: none; - margin-inline-start:4px} - -.iconItem_c69b6d.alwaysShown_c69b6d { - display: block -} - -.containerDefault_c69b6d.selected_c69b6d .iconNoChannelInfo_c69b6d,.iconVisibility_c69b6d:focus .iconItem_c69b6d,.iconVisibility_c69b6d:focus-within .iconItem_c69b6d { - display: block -} - -.containerDefault_c69b6d.selected_c69b6d .iconWithChannelInfo_c69b6d { - display: none -} - -.iconItem_c69b6d:focus,.iconItem_c69b6d:focus-within { - display: block; - position: relative -} - -.disableClick_c69b6d { - pointer-events: none -} - -.disabled_c69b6d>div { - opacity: .3 -} - -.actionIcon_c69b6d { - display: block; - height: 16px; - width: 16px -} - -.actionIcon_c69b6d:active { - transform: translateY(.5px) -} - -.actionIcon_c69b6d,.actionIcon_c69b6d:hover { - color: var(--icon-muted) -} - -.channelInfo_c69b6d { - display: block; - margin-inline-start:12px} - -.iconVisibility_c69b6d:focus .channelInfo_c69b6d,.iconVisibility_c69b6d:focus-within .channelInfo_c69b6d { - display: none -} - -.actionIcon_c69b6d { - color: var(--interactive-text-default) -} - -.iconItem_c69b6d:hover .actionIcon_c69b6d { - color: var(--interactive-text-hover) -} - -.actionIcon_c69b6d:active { - color: var(--interactive-text-active) -} - -.iconLive_c69b6d { - color: var(--text-feedback-positive)!important -} - -.summary_c69b6d { - margin-inline-start:40px} - -.subtitleHasThreads_c69b6d { - color: var(--text-brand) -} - -@media (hover: hover) { - .iconVisibility_c69b6d:hover .iconItem_c69b6d { - display:block - } - - .iconVisibility_c69b6d:hover .channelInfo_c69b6d { - display: none - } -} - -.selectedChannel_c69b6d .actionIcon_c69b6d,.selectedChannel_c69b6d .actionIcon_c69b6d:hover { - color: var(--icon-strong) -} - -.voiceChannelHighlightContainer_c69b6d { - position: relative -} - -.voiceChannelHighlightBorder_c69b6d { - background-color: var(--icon-feedback-positive); - bottom: 4px; - inset-inline-start: 0; - position: absolute; - top: 2px; - width: 2px -} - -.voiceChannelHighlightGlow_c69b6d { - background: radial-gradient(ellipse 40px 50% at left center,rgba(0,158,66,.125) 0,rgba(0,158,66,0) 100%); - bottom: -10px; - inset-inline-start: 0; - position: absolute; - top: -12px; - width: 40px -} - -.enable-forced-colors .actionIcon_c69b6d { - color: ButtonText -} - -.enable-forced-colors .iconBase_c69b6d { - background-color: ButtonFace; - border: 1px solid ButtonFace; - border-radius: 4px; - color: ButtonText -} - -.enable-forced-colors .iconBase_c69b6d:hover { - border-color: ButtonText -} - -.enable-forced-colors .iconBase_c69b6d:hover .actionIcon_c69b6d { - color: ButtonText -} - -.enable-forced-colors .containerDragAfter_c69b6d:after,.enable-forced-colors .containerDragBefore_c69b6d:before,.enable-forced-colors .containerUserOver_c69b6d:after { - background-color: Highlight; - outline: 1px solid Canvas -} - -.background__6343b { - box-sizing: border-box; - -o-object-fit: contain; - object-fit: contain; - -o-object-position: center; - object-position: center; - width: 100% -} - -.gradientRoleColorsImage__6343b { - padding: 0 var(--space-16) -} - -.secondaryButton__652ee { - min-width: 0; - width: auto -} - -.buttonInner__652ee { - align-items: center; - display: flex; - justify-content: center -} - -.buttonWrapper__652ee { - flex-grow: 1 -} - -.noGrow__652ee { - flex: 0 0 auto -} - -.dot__652ee { - margin: 0 3px -} - -.boostIcon__652ee { - margin-top: 1px; - margin-inline-end:-3px;vertical-align: top -} - -.container__6f9f7 { - display: flex; - gap: var(--space-8) -} - -.tooltipContainer__6f9f7 { - display: flex; - flex-direction: column; - text-align: center -} - -.primaryButton__6f9f7 { - flex-grow: 1 -} - -.secondaryButton__6f9f7 { - min-width: 0; - width: auto -} - -.dot__6f9f7 { - margin: 0 6px; - margin-inline-end:4px} - -.boost__6f9f7 { - align-self: center; - justify-self: center -} - -.confettiCanvas__8fcbd { - height: 100%; - position: absolute; - width: 100% -} - -.close__8fcbd { - inset-inline-end: var(--space-16); - position: absolute; - top: var(--space-16) -} - -.modal__8fcbd { - margin: 0; - overflow: hidden; - width: 440px -} - -.modalContentContainer__8fcbd { - margin: 0; - overflow: hidden; - padding: 0!important -} - -.container__8fcbd,.contentContainer__8fcbd { - display: flex; - flex-direction: column -} - -.contentContainer__8fcbd { - align-items: center; - padding: var(--space-24) var(--space-48); - text-align: center -} - -.description__8fcbd { - margin-top: var(--space-8) -} - -.button__8fcbd { - margin-top: var(--space-16) -} - -.image__8fcbd { - height: 228px -} - -.imageContainer_a1bfef { - height: 100%; - overflow: hidden; - position: relative; - width: 100% -} - -.image_a1bfef { - border-radius: var(--radius-sm); - box-sizing: border-box; - height: 100%; - -o-object-fit: contain; - object-fit: contain; - width: 100% -} - -.gameServerHostingImage_a1bfef { - transform: scale(2) -} - -.levelImage_a1bfef { - -o-object-fit: cover; - object-fit: cover -} - -.badge_bb93f2 { - background-color: var(--brand-500) -} - -.container__877f0 { - position: relative -} - -.lottieContainer__877f0 { - border-radius: var(--radius-sm); - inset: 0; - bottom: 1px; - overflow: hidden; - position: absolute; - top: 1px -} - -.lottie__877f0 { - height: 34px -} - -.textImportant__877f0 { - color: var(--text-strong) -} - -.container__0d0f9 { - border-bottom: 1px solid var(--border-subtle); - cursor: pointer; - display: flex; - flex-direction: column; - margin-block:0;margin-inline:8px 0;padding: var(--space-xxs) var(--space-xxs) var(--space-sm); - position: relative -} - -.container__0d0f9:hover .progressContainer__0d0f9 { - transform: scale(1.02,1.1) -} - -.container__0d0f9:hover .progress__0d0f9 { - background: linear-gradient(270deg,rgba(255,115,250,.7),rgba(255,115,250,.25)) -} - -.container__0d0f9:hover .boostCountText__0d0f9 { - opacity: 1 -} - -.container__0d0f9:after { - display: none -} - -.containerWithMargin__0d0f9 { - margin-bottom: var(--custom-guild-boosting-sidebar-display-conditional-bottom-margin) -} - -.container__0d0f9:after { - border-bottom: 1px solid var(--border-subtle); - bottom: 0; - content: ""; - inset-inline: 8px 0; - position: absolute -} - -.contentContainer__0d0f9 { - position: relative -} - -.progressContainer__0d0f9 { - background: var(--background-gradient-highest,var(--background-base-low)); - border-radius: var(--radius-round); - display: flex; - height: 26px -} - -.textContainer__0d0f9 { - align-items: center; - display: flex; - flex: 1; - flex-direction: row; - inset: 0 var(--space-8); - justify-content: space-between; - position: absolute; - z-index: 1 -} - -.textContentContainer__0d0f9 { - align-items: center; - display: flex; - flex-direction: row; - gap: 2px -} - -.text__0d0f9 { - color: var(--text-default) -} - -.boostCountText__0d0f9 { - opacity: .7 -} - -.progress__0d0f9 { - background: linear-gradient(270deg,rgba(255,115,250,.4),rgba(255,115,250,.1)); - border-radius: var(--radius-round); - box-shadow: 0 1px 4px 0 rgba(0,0,0,.14),inset 0 0 4.6px 0 rgba(255,115,250,.2); - inset: 2px; - position: absolute -} - -.progressLow__0d0f9 { - border-end-end-radius: 0; - border-start-end-radius: 0 -} - -.enable-forced-colors .container__0d0f9 { - background-color: Canvas; - border: 1px solid ButtonText; - border-radius: 4px -} - -.full-motion .progressContainer__0d0f9 { - transition: transform .2s ease -} - -.full-motion .boostCountText__0d0f9 { - transition: color .2s ease -} - -.containerDefault__29444,.containerDragAfter__29444,.containerDragBefore__29444,.containerUserOver__29444 { - padding-top: var(--custom-category-channel-space-before-category); - position: relative -} - -.containerDefault__29444:active,.containerDragAfter__29444:active,.containerDragBefore__29444:active,.containerUserOver__29444:active { - transform: translateZ(0) -} - -.containerDragAfter__29444:before,.containerDragBefore__29444:before { - background-color: var(--green-360); - border-radius: 2px; - box-shadow: 0 0 3px var(--opacity-black-40); - content: ""; - height: 4px; - inset-inline: 8px 0; - position: absolute; - z-index: 1 -} - -.containerDragBefore__29444:before { - top: -2px -} - -.containerDragAfter__29444:before { - bottom: -2px -} - -.containerUserOver__29444:after { - background-color: hsl(var(--green-360-hsl)/.1); - border: 2px solid hsl(var(--green-360-hsl)/.5); - border-radius: 4px; - box-sizing: border-box; - content: ""; - position: absolute; - top: 0; - inset-inline: 8px 0; - bottom: 0; - z-index: 1 -} - -.disableClick__29444 { - pointer-events: none -} - -.addButton__29444 { - display: none; - height: 16px; - width: 16px -} - -.addButton__29444.forceVisible__29444,.iconVisibility__29444:hover .addButton__29444 { - display: block -} - -.addButton__29444:focus,.addButton__29444:focus-within { - display: block; - position: relative -} - -.addButtonIcon__29444:hover { - color: var(--interactive-text-hover) -} - -.addButtonIcon__29444:active { - color: var(--interactive-text-active); - transform: translateY(.5px) -} - -.wrapperCommon__29444 { - box-sizing: border-box; - color: var(--channels-default); - cursor: default; - height: 24px; - justify-content: space-between; - padding-inline:var(--space-md) calc(var(--space-md) - var(--space-xs));position: relative -} - -.wrapperCommon__29444,.wrapperCommon__29444 .mainContent__29444 { - align-items: center; - display: flex; - flex-direction: row -} - -.wrapperCommon__29444 .mainContent__29444 { - gap: 4px; - height: 100% -} - -.wrapperCommon__29444 .mainContent__29444:before { - display: none -} - -.wrapperCommon__29444 .mainContent__29444 .name__29444 { - flex: unset; - min-width: 0 -} - -.wrapperCommon__29444 .icon__29444 { - flex-shrink: 0; - inset-inline-start: unset; - position: relative; - top: unset -} - -.wrapper__29444,.wrapperStatic__29444 { -} - -.clickable__29444 { - cursor: pointer -} - -.children__29444 { - align-items: center; - display: flex; - flex: 0 0 auto; - justify-content: center -} - -.children__29444:empty { - display: none -} - -.mainContent__29444 { - flex: 1 1 auto; - overflow: hidden -} - -.mainContent__29444:before { - content: ""; - display: block; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.icon__29444 { - height: 12px; - inset-inline-start: 2px; - position: absolute; - top: 6px; - transition: transform .1s linear; - width: 12px -} - -.collapsed__29444 .icon__29444 { - --custom-icon-collapse-rotate: -90deg; - transform: rotate(var(--custom-icon-collapse-rotate)) -} - -.wrapper__29444.muted__29444,.wrapper__29444.muted__29444 .name__29444 { - color: var(--text-muted) -} - -.wrapper__29444.muted__29444 .icon__29444,.wrapper__29444.muted__29444 .name__29444 { - opacity: .5 -} - -.wrapper__29444.muted__29444:hover .icon__29444,.wrapper__29444.muted__29444:hover .name__29444,.wrapper__29444:hover .icon__29444,.wrapper__29444:hover .name__29444 { - color: var(--interactive-text-hover) -} - -.dismissWrapper__29444 { - z-index: 2 -} - -.dismissButton__29444 { - cursor: pointer; - opacity: .8; - z-index: 2 -} - -.dismissButton__29444:hover { - opacity: 1 -} - -.dismiss__29444 { - height: 16px; - width: 16px -} - -.voiceChannelsButton__29444 { - align-items: center; - border: 2px solid var(--border-subtle); - border-radius: 8px; - color: var(--interactive-text-active); - cursor: pointer; - display: flex; - flex-direction: row; - justify-content: center; - margin-top: 8px; - margin-inline:16px 8px;padding: 7px 8px -} - -.voiceChannelsButton__29444:hover { - background: var(--background-mod-normal) -} - -.voiceChannelsToggleIcon__29444 { - margin-inline-end:8px} - -.refreshVoiceChannelsButton__29444 { - margin-inline-start:var(--space-md);margin-bottom: var(--space-xxs); - margin-top: var(--space-xxs); - width: calc(100% - var(--space-md) - var(--space-8)) -} - -.refreshVoiceChannelsButtonInner__29444 { - align-items: center; - display: flex; - justify-content: center -} - -.enable-forced-colors .icon__29444,.enable-forced-colors .name__29444 { - color: ButtonText!important; - forced-color-adjust: none -} - -.enable-forced-colors .addButtonIcon__29444 { - background-color: ButtonFace; - border: 1px solid ButtonFace -} - -.enable-forced-colors .addButtonIcon__29444:hover { - border-color: ButtonText -} - -.enable-forced-colors .wrapper__29444 { - background-color: ButtonFace; - border-bottom: 1px solid ButtonFace -} - -.enable-forced-colors .wrapper__29444:hover { - border-color: ButtonText -} - -.enable-forced-colors .wrapper__29444.muted__29444 { - opacity: .4 -} - -.enable-forced-colors .wrapper__29444.muted__29444:focus,.enable-forced-colors .wrapper__29444.muted__29444:hover { - opacity: 1 -} - -.enable-forced-colors .containerDragAfter__29444:before,.enable-forced-colors .containerDragBefore__29444:before,.enable-forced-colors .containerUserOver__29444:after { - background-color: Highlight; - outline: 1px solid Canvas -} - -.scroller__629e4 { - margin-bottom: var(--space-4); - margin-inline-end:1px;padding-inline-end:8px;padding-bottom: 16px; - z-index: 1 -} - -.scroller__629e4::-webkit-scrollbar-track { - margin-bottom: 16px -} - -.unread__629e4 { - inset-inline: 0 -} - -.unreadBar__629e4:before { - content: ""; - position: absolute; - top: 0; - inset-inline: -8px; - bottom: 0 -} - -.unreadBottom__629e4 { - bottom: calc(var(--custom-app-panels-height, 0) + var(--space-8)) -} - -.unreadBottom__629e4 .unreadBar__629e4:before { - bottom: -8px -} - -.positionedContainer__629e4 { - position: relative -} - -.unreadTop__629e4 { - top: 0 -} - -.unreadTop__629e4 .unreadBar__629e4:before { - top: -8px -} - -.voiceUserSummary__629e4 { - align-items: center; - box-sizing: border-box; - display: flex; - height: 34px; - padding-inline-start:16px} - -.sectionDivider__629e4 { - background-color: var(--border-subtle); - height: 1px; - margin-top: calc(var(--space-sm) - 1px); - margin-inline-start:var(--space-md);width: calc(100% - var(--space-md) - var(--space-md) + 9px) -} - -.sectionDividerWithBottom__629e4 { - margin-bottom: 8px -} - -.refresh-fast-follow-distinct-borders .sectionDivider__629e4 { - background-color: var(--app-frame-border) -} - -.dynamicGraphicContainer__47887 { - border-radius: var(--radius-sm); - height: 100% -} - -.dynamicGraphicForegroundImage__47887 { - height: 100% -} - -.multiLineTitleHack__53030 { - white-space: pre-line -} - -.wrapper_fce7ca { - isolation: isolate -} - -.withGradient_fce7ca,.wrapper_fce7ca { - overflow: hidden; - position: relative -} - -.withGradient_fce7ca:after { - background-image: linear-gradient(90deg,#394aff,#e901d9); - border-radius: inherit; - bottom: 1px; - content: ""; - inset-inline: 0; - opacity: .16; - position: absolute; - top: 1px; - transition: opacity .125s; - z-index: 0 -} - -.withGradient_fce7ca:hover:after { - opacity: .2 -} - -.text_fce7ca { - color: var(--text-strong)!important -} - -.icon_fce7ca { - color: var(--icon-subtle)!important -} - -.shine_fce7ca { - background-image: linear-gradient(90deg,hsla(0,0%,100%,0) 25%,hsla(0,0%,100%,.7) 50%,hsla(0,0%,100%,0) 75%); - height: var(--custom-shine-dimensions); - inset-inline-end: calc(100% + var(--custom-shine-rotated-dimensions-delta)); - opacity: .2; - pointer-events: none; - position: absolute; - top: 50%; - transform-origin: 50%; - width: var(--custom-shine-dimensions); - z-index: 3 -} - -.progressBarContainer_baf530 { - cursor: pointer; - display: flex; - flex-direction: column; - gap: 4px; - margin-block:-8px 0;margin-inline:8px 0;padding: 8px -} - -.progressBarText_baf530 { - align-items: center; - display: flex; - justify-content: space-between -} - -.progressBar_baf530 { - height: 4px; - margin-top: 4px -} - -.rightContainer_baf530,.rightText_baf530 { - align-items: center; - display: flex -} - -.rightText_baf530 { - gap: 2px -} - -.arrow_baf530 { - color: var(--text-default); - flex-shrink: 0 -} - -.divider_baf530 { - background-color: var(--border-subtle); - height: 1px; - margin: 4px 0; - width: 100% -} - -.previewChannelRow_d59199.selected_d59199:focus-within .closeButton_d59199,.previewChannelRow_d59199.selected_d59199:hover .closeButton_d59199 { - display: block -} - -.previewChannelRow_d59199.selected_d59199:focus-within .money_d59199,.previewChannelRow_d59199.selected_d59199:focus-within .newBadge_d59199,.previewChannelRow_d59199.selected_d59199:hover .money_d59199,.previewChannelRow_d59199.selected_d59199:hover .newBadge_d59199 { - display: none -} - -.previewChannelRow_d59199.phantomPreview_d59199 { - margin-inline-start:7px;overflow: hidden -} - -.phantomPreview_d59199.selected_d59199 .shopIcon_d59199,.phantomPreview_d59199.selected_d59199:hover .shopIcon_d59199 { - color: var(--interactive-text-active) -} - -.theme-light .phantomPreview_d59199 .previewChannelRowContent_d59199 { - background: hsla(0,0%,96%,.4); - border: 1px solid var(--border-muted) -} - -.theme-light .phantomPreview_d59199 .previewChannelRowContent_d59199:hover,.theme-light .phantomPreview_d59199 .selected_d59199 .previewChannelRowContent_d59199 { - background: var(--white); - border: 1px solid var(--interactive-text-active) -} - -.theme-dark .phantomPreview_d59199 { - padding: 1px -} - -.theme-dark .phantomPreview_d59199 .previewChannelRowContent_d59199 { - background: var(--custom-guild-shop-channel-row-gradient); - position: relative -} - -.theme-dark .phantomPreview_d59199 .previewChannelRowContent_d59199:before { - background: var(--custom-guild-shop-channel-row-border-gradient); - border-radius: inherit; - content: ""; - position: absolute; - top: -1px; - inset-inline: -1px; - bottom: -1px; - z-index: -1 -} - -.theme-dark .phantomPreview_d59199 .previewChannelRowContent_d59199:hover { - background: var(--custom-guild-shop-channel-row-gradient-hover); - box-shadow: var(--custom-guild-shop-channel-row-glow) -} - -.gifSection_d59199 { - align-items: center; - display: flex; - gap: 6px; - margin-inline-start:auto} - -.money_d59199 { - display: none; - inset-inline-end: 4px; - max-height: 48px; - position: absolute -} - -.closeButton_d59199 { - align-items: center; - color: var(--interactive-text-default); - display: none; - margin-inline-start:auto} - -.phantomPreview_d59199 .money_d59199,.phantomPreview_d59199 .newBadge_d59199 { - display: block -} - -.newBadge_d59199 { - color: var(--brand-560); - display: none -} - -.enable-forced-colors .closeButton_d59199 { - background-color: ButtonFace; - color: ButtonText -} - -.wrapper__260e1 { - align-items: stretch; - background-color: var(--background-base-low); - border: 1px solid var(--border-normal); - border-radius: var(--radius-md); - box-sizing: border-box; - color: var(--text-subtle); - display: grid; - flex: 0 0 auto; - font-size: 12px; - font-weight: var(--font-weight-medium); - grid-template-columns: 1fr auto; - height: 20px; - justify-content: center; - line-height: 18px; - overflow: hidden -} - -.total__260e1,.users__260e1 { - display: block; - text-align: center -} - -.users__260e1 { - align-items: center; - background-color: var(--background-base-low); - box-sizing: border-box; - display: flex; - font-variant-numeric: tabular-nums; - padding: 0 var(--space-4); - width: 28px -} - -.users__260e1.video__260e1 { - width: 48px -} - -.users__260e1.video__260e1.extraLong__260e1 { - width: 56px -} - -.users__260e1.extraLong__260e1 { - width: auto -} - -.total__260e1 { - background-color: var(--background-mod-strong); - box-sizing: border-box; - font-variant-numeric: tabular-nums; - padding-block:0;padding-inline:2px 4px;position: relative; - width: 20px -} - -.total__260e1.extraLong__260e1 { - width: auto -} - -.total__260e1:after { - border-bottom: 0 solid transparent; - border-inline-end:6px solid transparent;border-inline-end-color:var(--background-mod-strong);border-top: 20px solid transparent; - content: ""; - height: 0; - inset-inline-start: -6px; - position: absolute; - top: 0; - width: 0 -} - -.videoIcon__260e1 { - height: 16px; - margin-inline-end:4px;width: 16px -} - -.popoutHeaderContainer__260e1 .users__260e1 { - background-color: var(--background-mod-strong) -} - -.iconBase__933a1 { - cursor: pointer; - line-height: 0; - position: relative -} - -.mentionsBadge__933a1 { - pointer-events: none -} - -.popover__1bad5 { - max-width: unset; - padding: 0; - text-align: unset; - width: 260px -} - -.inner__1bad5 { - align-items: start; - justify-content: space-between; - padding: var(--space-8); - position: relative -} - -.inner__1bad5,.list__1bad5 { - flex-direction: column; - gap: 2px; - width: 100% -} - -.inner__1bad5,.list__1bad5,.row__1bad5 { - box-sizing: border-box; - display: flex -} - -.row__1bad5 { - align-items: center; - border-radius: var(--space-xs); - flex-direction: row; - gap: var(--space-xs); - height: 40px; - padding: var(--space-xs) -} - -.row__1bad5.clickable__1bad5 { - cursor: pointer -} - -.row__1bad5.clickable__1bad5:hover { - background-color: var(--background-mod-subtle) -} - -.row__1bad5.clickable__1bad5:hover .userIconContainer__1bad5 { - color: var(--text-default) -} - -.row__1bad5 :disabled { - cursor: not-allowed -} - -.row__1bad5 .avatar__1bad5,.row__1bad5 .icon__1bad5 { - flex-shrink: 0 -} - -.row__1bad5 .leading__1bad5 { - align-items: center; - display: flex; - height: 24px; - justify-content: center; - width: 24px -} - -.row__1bad5 .name__1bad5 { - flex-grow: 1 -} - -.row__1bad5 .dots__1bad5 { - align-items: center; - border-radius: 2px; - display: flex; - flex-direction: row; - height: 16px; - justify-content: center; - margin: 2px 0 -} - -.row__1bad5 .dots__1bad5,.row__1bad5 .status__1bad5 { - padding: 0 var(--space-xxs) -} - -.row__1bad5 .dots__1bad5,.row__1bad5 .status__1bad5,.row__1bad5 .userIconContainer__1bad5 { - flex-shrink: 0 -} - -.row__1bad5 .userIconContainer__1bad5 { - align-items: center; - color: var(--icon-subtle); - display: flex; - height: 24px; - justify-content: center; - width: 24px -} - -.animation_f2170c { - overflow: hidden -} - -.clickable_f2170c:hover .icon_f2170c.userIcon_f2170c,.clickable_f2170c:hover .label_f2170c { - color: var(--interactive-text-hover) -} - -.clickable_f2170c:active .content_f2170c { - background: var(--interactive-background-active) -} - -.content_f2170c { - display: flex -} - -.label_f2170c { - flex-grow: 1 -} - -.icon_f2170c,.label_f2170c { - color: var(--channels-default) -} - -.close_f2170c,.icon_f2170c,.leading_f2170c { - flex-shrink: 0 -} - -.close_f2170c { - height: 16px; - width: 16px -} - -.close_f2170c:hover .icon_f2170c { - color: var(--interactive-text-hover) -} - -@property --custom-voice-invite-suggestions-timer-progress { - syntax: "<number>"; - inherits: false; - initial-value: 0 -} - -.leading_f2170c { - display: grid; - grid-template-columns: 1fr; - grid-template-rows: 1fr; - height: calc(var(--custom-voice-invite-suggestions-timer-size)*1px); - width: calc(var(--custom-voice-invite-suggestions-timer-size)*1px) -} - -.timer_f2170c,.userIcon_f2170c { - grid-column: 1; - grid-row: 1 -} - -.userIcon_f2170c { - align-self: center; - justify-self: center -} - -.timer_f2170c { - --custom-voice-invite-suggestions-timer-stroke-width: 2; - height: 100%; - transform: rotate(-90deg); - width: 100% -} - -.timer_f2170c .progress_f2170c,.timer_f2170c .ring_f2170c { - fill: transparent; - stroke-width: var(--custom-voice-invite-suggestions-timer-stroke-width); - r: calc(var(--custom-voice-invite-suggestions-timer-size)/2 - var(--custom-voice-invite-suggestions-timer-stroke-width)/2); - cx: calc(var(--custom-voice-invite-suggestions-timer-size)/2); - cy: calc(var(--custom-voice-invite-suggestions-timer-size)/2) -} - -.timer_f2170c .ring_f2170c { - stroke: var(--border-normal) -} - -.timer_f2170c .progress_f2170c { - stroke: var(--border-strong); - stroke-dasharray: calc(var(--custom-voice-invite-suggestions-timer-size)*pi); - stroke-dashoffset: calc(var(--custom-voice-invite-suggestions-timer-progress)*-1); - animation-duration: calc(var(--custom-voice-invite-suggestions-timer-duration)*1ms); - animation-fill-mode: forwards; - animation-name: custom-voice-invite-suggestions-timer_f2170c; - animation-timing-function: linear -} - -.timer_f2170c.paused_f2170c .progress_f2170c { - animation-duration: auto; - animation-fill-mode: none -} - -@keyframes custom-voice-invite-suggestions-timer_f2170c { - 0% { - --custom-voice-invite-suggestions-timer-progress: 0 - } - - to { - --custom-voice-invite-suggestions-timer-progress: calc(var(--custom-voice-invite-suggestions-timer-size)*pi) - } -} - -.container__394db { - align-items: center; - display: block; - flex: 1 1 auto; - overflow: hidden; - position: relative; - vertical-align: middle; - white-space: nowrap -} - -.container__394db .chipletContainer__394db { - display: inline-block; - overflow: hidden -} - -.container__394db .chipletParent__394db { - display: inline; - overflow: hidden; - padding-inline-start:4px;position: relative -} - -.container__394db .usernameContainer__394db { - display: inline-block; - flex: 1; - margin-top: 2px; - max-width: 100%; - overflow: hidden; - white-space: nowrap -} - -.container__394db.isOverlayContainer__394db { - align-content: center; - background: var(--background-base-low); - border-radius: 4px; - display: flex; - flex: none; - flex-direction: row; - height: 20px -} - -.container__394db.isOverlayContainer__394db .usernameContainer__394db { - margin-top: 0 -} - -.container__394db.isOverlayContainer__394db .chipletContainer__394db,.container__394db.isOverlayContainer__394db .chipletParent__394db { - align-content: center; - align-items: center; - display: flex -} - -.container__394db.isOverlayContainer__394db .chipletContainer__394db { - margin: 0; - padding: 1.5px -} - -.container__394db.isOverlayContainer__394db .chipletContainer__394db.noPadding__394db { - padding: 0 -} - -.isOverlayTag__394db { - margin: 0 -} - -.avatarWrapper_d8a370 { - align-items: center; - display: inline-flex; - flex: 0 0 auto; - justify-content: center; - position: relative -} - -.ring_d8a370 { - border-radius: 50%; - inset: -3px; - -webkit-mask: radial-gradient(farthest-side,transparent calc(100% - 2px),#000 calc(100% - 1px)); - mask: radial-gradient(farthest-side,transparent calc(100% - 2px),#000 calc(100% - 1px)); - pointer-events: none; - position: absolute; - transform: rotate(-90deg) -} - -.container__7116a { - border-radius: var(--radius-md); - flex-direction: column; - width: 264px -} - -.inputGroup__7116a { - padding: 8px 8px 0 -} - -.group__7116a { - flex-direction: column; - gap: 2px; - justify-content: stretch; - padding: 8px -} - -.scroller__7116a { - max-height: 400px -} - -.separator__7116a { - border-bottom-color: var(--border-subtle); - border-bottom: 1px solid var(--border-subtle); - box-sizing: border-box; - margin: 8px; - width: 100% -} - -.icon__7116a { - height: 20px; - width: 20px -} - -.removeStatus__7116a { - color: var(--text-feedback-critical) -} - -.input__7116a { - flex-grow: 0; - width: 100% -} - -.inputRow__7116a { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px; - --control-input-height-md: 40px -} - -.inputHint__7116a { - cursor: default; - margin-inline-start:8px;margin-top: 4px; - text-transform: none -} - -.subtitle__7116a { - align-items: center; - align-self: start; - display: flex; - gap: 4px; - padding: 6px 10px 8px -} - -.container__8c6be { - border-radius: 8px; - gap: 10px; - justify-content: unset; - padding: 8px 9px; - width: 100% -} - -.container__8c6be:hover { - background-color: var(--background-mod-subtle); - color: var(--text-strong) -} - -.container__8c6be:active { - background-color: var(--background-mod-normal) -} - -.itemText__8c6be { - flex-grow: 1; - overflow: hidden; - text-overflow: ellipsis; - text-transform: none; - white-space: nowrap -} - -.closeIcon__8c6be { - flex-shrink: 0; - height: 16px; - width: 16px -} - -.favoriteIcon__8c6be { - color: var(--icon-subtle) -} - -.favoriteIcon__8c6be:hover { - color: var(--icon-strong) -} - -.centerAlign__8c6be { - align-items: center; - display: flex -} - -.icon__912a0 { - border-radius: 0; - height: 20px; - width: 20px -} - -.separator__912a0 { - border-bottom-color: var(--border-subtle); - border-bottom: 1px solid var(--border-subtle); - box-sizing: border-box; - margin: 8px; - width: 100% -} - -.container__7ccfa { - align-items: center; - display: flex; - flex-direction: unset; - gap: 8px; - justify-content: space-between; - max-width: unset; - width: 100% -} - -.icon__7ccfa { - flex-shrink: 0; - height: 32px; - width: 32px -} - -.statusText__7ccfa { - flex-grow: 1; - overflow: hidden; - text-align: start; - text-overflow: ellipsis; - white-space: nowrap -} - -.container_a46475 { - align-items: center; - display: flex; - flex-direction: unset; - gap: 8px; - justify-content: space-between; - max-width: unset; - width: 355px -} - -.icon_a46475 { - height: 32px; - width: 32px -} - -.statusText_a46475 { - flex-grow: 1; - overflow: hidden; - text-align: start; - text-overflow: ellipsis; - white-space: nowrap -} - -.root__92958 { - background: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-md); - display: flex; - flex-direction: column; - min-width: 240px -} - -.activityList__92958 { - padding: 0 var(--space-16) -} - -.activityRow__92958 { - align-items: stretch; - border-bottom: 1px solid var(--border-subtle); - display: flex; - flex-direction: column; - gap: var(--space-8); - padding: var(--space-12) 0 -} - -.activityRowContent__92958 { - align-items: center; - display: flex; - gap: var(--space-12) -} - -.activityRow__92958:last-child { - border-bottom: none -} - -.disabledReason__92958 { - text-align: center -} - -.flexColumn__16b66,.flexRow__16b66 { - display: flex -} - -.flexColumn__16b66 { - flex-direction: column -} - -.headerContainer__16b66 { - display: flex; - justify-content: space-between; - margin-bottom: var(--space-xs); - min-width: 0; - width: 100% -} - -.assetsLargeImage__16b66 { - display: block; - height: 64px; - margin-inline-end:8px;-o-object-fit: cover; - object-fit: cover; - width: 64px -} - -.assetsLargeImageCompact__16b66 { - border-radius: var(--radius-sm); - height: 32px; - -o-object-fit: cover; - object-fit: cover; - width: 32px -} - -.applicationLargeImage__16b66 { - border-radius: var(--radius-sm) -} - -.applicationLargeImage__16b66,.spotifyLargeImage__16b66 { -} - -.headerTextSize__16b66 { - font-size: 14px; - line-height: 18px -} - -.bodyTextSize__16b66 { - font-size: 12px; - line-height: 16px -} - -.textRow__16b66 { - display: block -} - -.ellipsisRow__16b66 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.colorHeaderPrimary__16b66 { - color: var(--text-strong) -} - -.header__16b66 { - margin-bottom: 2px -} - -.colorHeaderSecondary__16b66 { - color: var(--text-default) -} - -.colorMuted__16b66 { - color: var(--text-muted) -} - -.link__16b66:hover { - text-decoration: underline -} - -.clickable__16b66 { - cursor: pointer -} - -.headerLink__16b66 { - color: var(--text-strong) -} - -.headerLink__16b66:hover { - text-decoration: underline -} - -.bodyLink__16b66 { - color: var(--text-strong) -} - -.bodyLink__16b66,.bodyLinkMuted__16b66 { -} - -.bodyLinkMuted__16b66 { - color: var(--text-muted) -} - -.usersSummary__16b66 { - margin-top: 4px -} - -.timeBar__16b66 { - margin-top: 8px -} - -.detailsAndAvatarsContainer__16b66 { - flex: 1; - min-width: 0; - padding-inline-start:var(--space-xs)} - -.avatar__16b66 { - border-radius: 50%; - height: 24px; - width: 24px -} - -.activity__7ba6e+.activity__7ba6e { - border-top: 1px solid var(--border-subtle) -} - -.partyMembers__7ba6e { - display: flex; - flex: 1; - justify-content: flex-end -} - -.partyAvatar__7ba6e { - cursor: default -} - -.morePartyMembers__7ba6e { - border-radius: 8px; - box-sizing: border-box; - font-size: 8px; - height: 16px; - line-height: 16px; - min-width: 16px; - padding: 0 6px; - text-align: center; - z-index: 2 -} - -.button__7ba6e { - border-color: var(--border-muted); - border-radius: 4px; - color: var(--interactive-text-active) -} - -.button__7ba6e:not([disabled]):hover { - border-color: var(--interactive-text-active) -} - -.channelActivityContainer__7ba6e { - padding-top: 12px -} - -.activityActionsContainer__7ba6e { - padding-bottom: 16px -} - -.container__218f7 { - background-color: var(--background-surface-high); - border-radius: 5px; - box-shadow: var(--shadow-border),var(--shadow-high); - margin-inline-start:16px;max-height: 90vh; - padding: 0 16px; - width: 280px -} - -.headerDivider__218f7 { - background-color: var(--border-subtle); - height: 1px; - width: 100% -} - -.voiceChannelGiftingBreadcrumb__218f7 { - margin-bottom: 16px -} - -.hangStatusActivity__218f7 { - padding: var(--space-sm) 0 -} - -.hangStatusContent__218f7 { - align-items: center; - display: flex; - gap: var(--space-xs); - justify-content: space-between -} - -.hangStatusIcon__218f7 { - flex-shrink: 0; - height: 32px; - width: 32px -} - -.hangStatusText__218f7 { - flex-grow: 1; - overflow: hidden; - text-align: start; - text-overflow: ellipsis; - white-space: nowrap -} - -.draggable__55bab { - height: 32px; - position: relative -} - -.draggable__55bab:active { - transform: translateZ(0) -} - -.moreContainer__55bab { - background-color: var(--background-base-lowest); - border: 4px solid var(--background-base-lower); - border-color: var(--background-base-lowest); - border-radius: 16px; - margin-inline-start:-8px;padding: 3px 8px; - z-index: 1 -} - -.audienceContainer__55bab { - align-items: center; - display: flex; - flex-direction: row; - height: 24px; - margin-top: 4px -} - -.audienceContainerCollapsed__55bab { - background-color: var(--background-base-lowest); - border: 4px solid var(--background-base-lower); - border-radius: 16px; - margin-top: 0; - margin-inline-start:-8px;padding-inline-end:8px;z-index: 1 -} - -.audienceContainerCollapsed__55bab .audienceIconContainer__55bab { - background: none; - margin: 4px 0 0 -} - -.audienceIconContainer__55bab { - background-color: var(--background-base-lowest); - border-radius: 50%; - margin-top: 4px; - margin-inline:6px} - -.audienceIcon__55bab { - color: var(--interactive-text-default); - height: 16px; - margin: 4px 6px; - width: 16px -} - -.userAvatar__55bab { - margin: 0 -} - -.list_c3cd7d { - padding-bottom: var(--space-xs); - padding-inline-start:calc(28px + var(--space-xs) + var(--space-xs) - var(--space-xs))} - -.list_c3cd7d.isThread_c3cd7d { - padding-bottom: 0 -} - -.list_c3cd7d.withGuildIcon_c3cd7d { - padding-bottom: 8px; - padding-inline-start:48px} - -.collapsed_c3cd7d { - padding-inline:48px 8px} - -.disableInteraction_c3cd7d { - pointer-events: none -} - -.collapsed_c3cd7d.list_c3cd7d { - padding-inline-start:64px} - -.container__5b40b { - position: relative -} - -.spine__5b40b { - color: var(--spine-default); - top: 8px -} - -.spine__5b40b,.spineBorder__5b40b { - inset-inline-start: 22px; - position: absolute -} - -.spineBorder__5b40b { - background: var(--spine-default); - border-radius: 2px; - bottom: 24px; - top: 10px; - width: 2px -} - -.spineWithGuildIcon__5b40b { - inset-inline-start: 32px -} - -.spineBorderWithGuildIcon__5b40b { - inset-inline-start: 32px -} - -.spine__5b40b,.spineBorder__5b40b { - inset-inline-start: var(--channels-spine-offset-left); - top: 0 -} - -.invertedSpine__5b40b { - top: var(--channels-spine-inverted-offset-top) -} - -.container__349bd { - align-items: flex-start; - background-color: var(--background-surface-high); - border-radius: 8px; - box-shadow: var(--elevation-high),var(--elevation-stroke); - display: flex; - padding: 16px -} - -.content__349bd { - margin-inline-start:24px;max-width: 320px -} - -.buttonContainer__349bd { - align-items: center; - display: flex; - margin-top: 16px -} - -.primaryButton__349bd { - margin-inline-end:24px} - -.image__349bd { - height: 64px; - width: 64px -} - -.premiumChannelIcon__15e7f { - display: block -} - -.icon20px__69362 { - border: 1px solid var(--border-subtle); - border-radius: 8px; - height: 20px; - width: 20px -} - -.icon20px__69362:not(:last-child) { - margin-inline-end:4px} - -.container__69362 { - align-items: center; - display: flex; - height: 16px; - justify-content: end; - overflow: visible -} - -.overflow__69362 { - background-color: var(--background-base-lowest); - border-radius: 4px; - height: 20px; - line-height: 20px; - padding: 0 4px; - text-align: center; - vertical-align: middle -} - -.modeMuted__69362 { - opacity: .3 -} - -.liveIndicator__46869 { - background-color: var(--green-360); - border-radius: 8px; - color: var(--white); - font-weight: var(--font-weight-semibold); - margin-inline-start:10px;padding-inline:4px;text-transform: uppercase -} - -.iconLive__46869 { - color: var(--green-360)!important -} - -.popout__76f04 { - background-color: var(--background-surface-high); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-medium); - box-sizing: border-box; - margin-inline-start:16px;max-width: 400px; - min-width: 272px; - padding: 0 12px -} - -.more__76f04,.row__76f04,.title__76f04 { - padding: 12px 4px -} - -.title__76f04 { - font-weight: var(--font-weight-semibold); - margin-top: 2px; - padding-bottom: 8px; - text-transform: none -} - -.row__76f04,.title__76f04 { - align-items: center; - display: flex -} - -.row__76f04 { - border-radius: 4px; - color: var(--text-default); - margin: 0 -4px; - padding: 8px -} - -.row__76f04:hover { - background-color: var(--background-mod-normal); - cursor: pointer -} - -.row__76f04:hover,.row__76f04:hover .timestamp__76f04 { - color: var(--text-strong) -} - -.avatar__76f04 { - flex-shrink: 0; - height: 16px; - margin-inline-end:8px;width: 16px -} - -.bullet__76f04 { - margin: 0 8px -} - -.timestamp__76f04 { - color: var(--text-muted); - flex-shrink: 0 -} - -.name__76f04,.timestamp__76f04 { - white-space: nowrap -} - -.name__76f04 { - overflow: hidden; - text-overflow: ellipsis -} - -.more__76f04 { - align-items: center; - border-top-color: var(--border-subtle); - border-top: 1px solid var(--border-subtle); - color: var(--text-link); - cursor: pointer; - display: flex; - font-weight: var(--font-weight-semibold); - margin-top: 8px; - padding-bottom: 10px; - padding-top: 8px -} - -.more__76f04:hover { - text-decoration: underline -} - -.custom-theme-background .popout__76f04 { - border: 1px solid var(--border-strong) -} - -.popoutHeader__628e6 { - display: flex; - flex: 1 -} - -.channelIcon__628e6 { - color: var(--text-muted); - display: block; - flex: 0 0 auto; - height: 20px; - margin-inline-end:4px;width: 20px -} - -.channelName__628e6 { - text-overflow: ellipsis -} - -.container__4e30a { - background-color: var(--background-surface-high); - border-radius: 5px; - box-shadow: var(--shadow-border),var(--shadow-high); - margin-inline-start:16px;max-height: 420px; - padding: 0 16px; - width: 280px -} - -.popoutHeaderContainer__4e30a { - display: flex; - padding: 12px 0 -} - -.headerDivider__4e30a { - background-color: var(--border-subtle); - height: 1px; - width: 100% -} - -.settingNudgeText__4e30a { - padding: 16px 0 -} - -.voiceChannelGiftingBreadcrumb__4e30a { - margin-bottom: 16px -} - -.container__7aaec { - background: none; - display: flex; - justify-content: center; - overflow: hidden; - pointer-events: none; - position: absolute; - width: 100%; - z-index: 2 -} - -.containerPadding__7aaec { - margin: 0 -8px -8px; - padding: 0 8px 8px; - pointer-events: auto -} - -.bottom__7aaec { - bottom: calc(var(--custom-app-panels-height, 0) + var(--space-xs)); - padding-top: 16px -} - -.top__7aaec { - padding-bottom: 36px; - top: var(--space-8) -} - -.bar__7aaec { - align-items: center; - background-color: var(--background-surface-highest); - border: 1px solid var(--border-normal); - border-radius: 20px; - box-shadow: var(--shadow-high); - cursor: pointer; - display: flex; - margin-bottom: var(--space-xs); - margin-top: 0; - padding: 4px 8px; - position: relative -} - -.bar__7aaec,.bar__7aaec:hover { - opacity: 1 -} - -.emptyBar__7aaec { - height: 16px; - width: 80px -} - -.mentionsBar__7aaec { - background-color: var(--background-feedback-notification) -} - -.unreadIcon__7aaec { - color: var(--interactive-text-default); - margin-inline-end:4px} - -.voiceChannelsIcon__7aaec { - color: var(--notice-text-positive); - margin-inline-end:4px} - -.barText__7aaec { - color: var(--text-strong); - text-transform: uppercase -} - -.voiceBar__7aaec { - background-color: var(--notice-background-positive) -} - -.voiceBar__7aaec,.voiceBar__7aaec .barText__7aaec { - color: var(--notice-text-positive) -} - -.voiceChannelsUsers__7aaec { - margin-inline-start:4px;margin-bottom: -2px; - margin-top: 2px -} - -.container_b1bfd4 { - align-items: center; - background: var(--background-base-lowest); - border-radius: 10px; - display: flex; - height: 20px; - justify-content: center; - padding: 0 8px -} - -.peopleIcon_b1bfd4 { - color: var(--text-default); - margin-inline-end:4px} - -.userCountText_b1bfd4 { - line-height: 18px -} - -.statusDiv__5cda9 { - align-items: center; - display: flex -} - -.statusDiv__5cda9.hoverable__5cda9:hover .pencilIcon__5cda9,.statusDiv__5cda9.hoverable__5cda9:hover .statusText__5cda9 { - color: var(--interactive-text-hover) -} - -.statusDiv__5cda9 .statusText__5cda9 { - font-size: 12px; - line-height: 16px -} - -.statusText__5cda9 { - overflow: hidden; - text-overflow: ellipsis -} - -.pencilIcon__5cda9,.statusText__5cda9 { - color: var(--channels-default) -} - -.pencilIcon__5cda9 { - margin-inline-start:4px} - -.educationFeatureArt__5cda9 { - align-self: center; - background-image: url(/assets/5bbf9ffe2fe5208f.svg); - height: 112px; - width: 206px -} - -.enable-forced-colors .statusDiv__5cda9 { - background-color: ButtonFace; - color: ButtonText; - forced-color-adjust: none; - text-decoration: underline -} - -.enable-forced-colors .statusDiv__5cda9 .pencilIcon__5cda9,.enable-forced-colors .statusDiv__5cda9 .statusText__5cda9 { - color: inherit -} - -.enable-forced-colors .statusDiv__5cda9:hover .pencilIcon__5cda9,.enable-forced-colors .statusDiv__5cda9:hover .statusText__5cda9 { - color: inherit -} - -@keyframes pulse__3b43f { - 0% { - opacity: .7 - } - - to { - opacity: 1 - } -} - -.container__3b43f { - animation: pulse__3b43f 5s ease-in-out infinite alternate; - height: 100%; - overflow: hidden; - padding: 16px 8px -} - -.bannerPadding__3b43f { - padding-top: 104px -} - -.category__3b43f { - background-color: var(--background-mod-strong); - border-radius: 4px; - height: 16px; - margin-bottom: 18px; - width: 77px -} - -.spacer__3b43f { - height: 1px; - margin: 16px 0; - width: 100px -} - -.channel__3b43f { - align-items: center; - display: flex; - justify-content: flex-start; - margin-bottom: 18px -} - -.channelIcon__3b43f { - border-radius: 16px; - margin: 0 8px; - width: 16px -} - -.channelIcon__3b43f,.channelName__3b43f { - background-color: var(--background-mod-strong); - height: 16px -} - -.channelName__3b43f { - border-radius: 8px -} - -.facepile_aaa08b { - display: flex -} - -.facepileItemContainer_aaa08b>foreignObject { - background-color: var(--background-surface-high); - padding: 2.5px -} - -.facepileItemContainer_aaa08b:not(:first-child) { - margin-inline-start:-4px} - -.facepileItemContainer_aaa08b:not(:last-child) { - margin-inline-end:-4px} - -.textItem_aaa08b { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.guildIconList_aaa08b { - flex-direction: column -} - -.guildIconItem_aaa08b,.guildIconList_aaa08b { - display: flex; - gap: 8px -} - -.guildIconItemText_aaa08b { - padding-top: 2px -} - -.chevronButton__5ae61 { - cursor: pointer; - padding: 0; - width: 100% -} - -.chevronButton__5ae61,.chevronButtonContent__5ae61 { - align-items: center; - display: flex; - justify-content: space-between -} - -.chevronButtonContent__5ae61 { - flex: 1; - height: 20px -} - -.customizeLink__5ae61 { - cursor: pointer -} - -.wrapper_fa8995 { - background-color: var(--modal-background); - border-radius: 8px; - box-shadow: var(--shadow-high); - box-sizing: border-box; - padding: 16px; - position: relative; - width: 240px -} - -.gradient_fa8995 { - border-radius: 8px; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.theme-dark .gradient_fa8995 { - background: radial-gradient(75% 75% at 0 0,rgba(62,22,137,.8) 0,transparent 100%),radial-gradient(70% 50% at 100% 0,rgba(160,86,242,.4) 0,transparent 100%) -} - -.theme-light .gradient_fa8995 { - background: radial-gradient(75% 75% at 0 0,rgba(159,86,242,.4) 0,transparent 100%),radial-gradient(70% 50% at 100% 0,rgba(222,194,252,.5) 0,transparent 100%) -} - -.content_fa8995 { - align-items: center; - display: flex; - flex-direction: column; - position: relative -} - -.closeButton_fa8995 { - inset-inline-end: 4px; - position: absolute; - top: 4px -} - -.controllerImage_fa8995 { - height: 78px; - margin-top: 8px; - width: 140px -} - -.nuxTitle_fa8995 { - margin-top: 12px; - text-align: center; - width: 180px -} - -.nuxContent_fa8995 { - margin-top: 4px; - text-align: center -} - -.controlTitle_fa8995 { - align-self: flex-start -} - -.toggleContainerWrapper_fa8995 { - background-color: var(--background-base-lower); - border-radius: 8px; - box-sizing: border-box; - margin-top: 12px; - padding: 12px; - width: 100% -} - -.toggleContainer_fa8995 { - align-items: center; - box-sizing: border-box; - color: var(--text-strong); - display: flex; - justify-content: space-between; - margin-bottom: 8px; - width: 100% -} - -.toggleContainerOnlineRow_fa8995 { - align-items: center; - display: flex; - gap: 4px -} - -.toggleContainerText_fa8995 { - display: flex; - flex-direction: column -} - -.goOnlineButton_fa8995 { - margin-top: 20px; - width: 100% -} - -.divider_fa8995 { - margin: 16px 0 -} - -.wrapper_a629d4 { - position: relative -} - -.icon_a629d4,.wrapper_a629d4 { - height: 32px; - width: 32px -} - -.icon_a629d4 { - border-radius: 4px; - color: var(--white); - display: block; - flex-shrink: 0; - font-weight: var(--font-weight-bold); - line-height: 34px; - text-align: center; - text-transform: uppercase -} - -.badge_a629d4 { - bottom: -2px; - color: var(--text-strong); - height: 14px; - inset-inline-end: -1px; - position: absolute; - width: 14px -} - -.text__87e56 { - color: var(--premium-nitro-pink-text) -} - -.subtext__339d0 { - color: var(--text-subtle); - line-height: 13px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.title_b6c092 { - color: var(--text-strong); - line-height: 18px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -button.button__67645,span.button__67645 { - align-items: center; - border-radius: var(--radius-sm); - color: var(--icon-subtle); - display: flex; - height: 32px; - justify-content: center; - line-height: 0; - margin: 0; - position: relative; - transition: background-color .2s ease; - width: 32px -} - -.high-contrast-mode button.button__67645,.high-contrast-mode span.button__67645 { - border: 1px solid transparent; - box-sizing: border-box -} - -button.button__67645.redGlow__67645,span.button__67645.redGlow__67645 { - background-color: var(--opacity-red-12) -} - -button.button__67645.redGlow__67645:hover,span.button__67645.redGlow__67645:hover { - background-color: var(--opacity-red-24) -} - -.high-contrast-mode button.button__67645.redGlow__67645,.high-contrast-mode span.button__67645.redGlow__67645 { - border: 1px solid var(--opacity-red-52); - box-sizing: border-box -} - -button.button__67645.enabled__67645:hover,span.button__67645.enabled__67645:hover { - background-color: var(--control-secondary-background-hover); - color: var(--interactive-text-hover) -} - -button.button__67645.enabled__67645:hover.redGlow__67645,span.button__67645.enabled__67645:hover.redGlow__67645 { - background-color: var(--opacity-red-24) -} - -button.button__67645.disabled__67645,span.button__67645.disabled__67645 { - opacity: .4 -} - -button.button__67645.enabled__67645.redGlow__67645:hover,span.button__67645.enabled__67645.redGlow__67645:hover { - background-color: var(--opacity-red-24) -} - -.enable-forced-colors button.button__67645,.enable-forced-colors span.button__67645 { - color: ButtonText -} - -.enable-forced-colors button.button__67645.enabled__67645:hover,.enable-forced-colors span.button__67645.enabled__67645:hover { - background-color: ButtonFace -} - -.plated__67645 { - background-color: unset -} - -.plated__67645:hover { - background-color: var(--custom-nameplate)!important -} - -.plateMuted__67645,.plated__67645:hover { - -webkit-backdrop-filter: blur(4px); - backdrop-filter: blur(4px) -} - -.plateMuted__67645 { - background-color: var(--custom-nameplate-neutral)!important -} - -.plateMuted__67645:hover { - background-color: var(--custom-nameplate-neutral-hovered)!important -} - -.greenTooltip__67645 { - max-width: 280px -} - -.greenTooltipContent__67645 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.keybindHintKeys__384ad { - display: inline-block; - translate: 0 calc(var(--space-4)*-1) -} - -.keybindShortcut__384ad { - height: 22px; - padding: 0 4px -} - -.actions__4cd01 { - align-items: center; - display: flex; - flex-grow: 0; - gap: var(--space-4); - margin-inline-start:4px} - -.panelButtonContainer__4cd01 { - text-align: center; - width: 32px -} - -.badgedGuildIcon__4cd01 { - position: relative -} - -.badge__4cd01 { - background-color: var(--brand-500); - border-radius: 50%; - height: 6px; - inset-inline-end: 0; - position: absolute; - top: 0; - width: 6px; - z-index: 1 -} - -.centeredTooltip__4cd01 { - text-align: center -} - -.container__2ee44 { - align-items: center; - cursor: pointer; - display: flex -} - -.container__2ee44:hover>.textContent__2ee44 { - text-decoration: underline -} - -.iconEye__2ee44 { - margin-inline-end:4px} - -.gameName__8c6c2 { - color: var(--text-default); - line-height: 16px -} - -.clickableGameName__8c6c2:hover .gameName__8c6c2 { - color: var(--interactive-text-active); - cursor: pointer; - text-decoration: underline -} - -.gameWrapper__8c6c2 { - align-items: center; - display: flex; - min-width: 0 -} - -.gameWrapper__8c6c2 .liveIndicator__8c6c2 { - padding: 0 4px -} - -.gameIconWrapper__8c6c2 { - height: 32px; - position: relative -} - -.gameIcon__8c6c2 { - border-radius: var(--radius-sm); - height: 32px; - width: 32px -} - -.info__8c6c2 { - display: flex; - flex-direction: column; - margin-inline-start:8px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.liveBadge__8c6c2 { - background-color: var(--background-mod-normal); - border-radius: 50%; - bottom: -4px; - color: var(--text-strong); - height: 14px; - inset-inline-end: -4px; - padding: 2px; - position: absolute; - width: 14px -} - -.panel__5dec7 { - background-color: var(--background-surface-high); - border-bottom: 1px solid var(--border-muted); - border-start-end-radius: var(--radius-sm); - border-start-start-radius: var(--radius-sm); - flex: 0 0 auto; - padding: var(--space-12) -} - -.body__5dec7 { - align-items: center; - display: flex; - justify-content: space-between -} - -.dropsBadgeWrapper__5dec7 { - display: inline-block; - padding-top: 4px -} - -.dropsBadge__5dec7 { - font-size: 10px; - font-weight: var(--font-weight-semibold); - line-height: 12px; - overflow: hidden; - padding-top: 2px; - text-align: center; - text-overflow: ellipsis; - text-transform: none; - white-space: nowrap -} - -.gameWrapper__5dec7 { - align-items: center; - display: flex; - min-width: 0 -} - -.gameWrapper__5dec7 .liveIndicator__5dec7 { - padding: 0 4px -} - -.clickableGameWrapper__5dec7 { - cursor: pointer; - flex-grow: 1; - padding: var(--space-4) -} - -.clickableGameWrapper__5dec7:hover { - background-color: var(--control-secondary-background-hover); - border-radius: var(--space-8) -} - -.gameIcon__5dec7 { - height: 32px; - width: 32px -} - -.info__5dec7 { - display: flex; - flex-direction: column; - margin-inline-start:8px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.sparkleContainer__5dec7 { - position: relative -} - -.sparkles__5dec7 { - height: 26px; - inset-inline-start: 5px; - position: absolute; - top: -12px; - width: 26px -} - -.infoWithDrops__5dec7 { - display: block -} - -.channel__5dec7:hover,.underlineOnHover__5dec7:hover { - text-decoration: underline -} - -.perksDemoContainer__5dec7 { - align-items: center; - display: flex; - flex-direction: row; - margin-top: 1px -} - -.perksDemoText__5dec7 { - margin-inline-start:2px} - -.frameApplicationNameTitleClickable__5dec7 { - cursor: pointer -} - -.container_e45859 { - border-radius: 8px; - box-sizing: border-box; - display: block; - padding: 1px 0; - transition: none -} - -.channel__972a0 { - margin-inline-start:8px;max-width: var(--custom-guild-sidebar-width); - position: relative -} - -.channel__972a0.fullWidth__972a0 { - max-width: 100% -} - -.unreadPill__972a0 { - background-color: var(--interactive-text-active); - border-radius: 0 var(--radius-xs) var(--radius-xs) 0; - height: 8px; - inset-inline-start: -8px; - margin-top: -4px; - position: absolute; - top: 50%; - width: 4px -} - -.unreadPill__972a0.muted__972a0 { - opacity: .3 -} - -.interactive__972a0 { - align-items: stretch; - box-sizing: border-box; - color: var(--text-muted); - display: flex; - position: relative; - width: 100%; - z-index: 1 -} - -.overflowTooltip__972a0 { - max-width: 350px; - width: auto -} - -.withDisplayNameStyles__972a0 { - margin: -2px -4px; - padding: 2px 4px; - text-overflow: unset -} - -.clanTag__972a0 { - margin-inline-start:4px} - -.clanTagMuted__972a0 { - opacity: .3 -} - -.interactiveSelected__972a0 { - color: var(--interactive-text-active) -} - -.avatarWithText__972a0 { - flex-grow: 1 -} - -.link__972a0 { - align-items: center; - border-radius: inherit; - color: inherit; - display: flex; - flex: 1 1 auto; - gap: 8px; - min-width: 0; - overflow: hidden; - padding-block:0;padding-inline:var(--space-xs) 0;text-overflow: ellipsis; - white-space: nowrap; - width: 100% -} - -.linkButton__972a0 { - padding-inline-end:0} - -.linkButtonIcon__972a0 { - color: var(--icon-muted); - height: 24px; - width: 24px -} - -.closeButton__972a0 { - align-items: center; - align-self: stretch; - display: none; - justify-content: center; - opacity: .7; - padding: 2px; - padding-inline-end:8px} - -.closeButton__972a0.reducedClickTarget__972a0 { - align-self: center; - margin-inline-end:8px;padding: 0 -} - -.closeButton__972a0.closeButtonForceShow__972a0 { - display: flex -} - -.closeButton__972a0.closeButtonPlated__972a0 { - opacity: unset -} - -.closeButton__972a0 .innerCloseButtonPlated__972a0 { - align-items: center; - -webkit-backdrop-filter: blur(4px); - backdrop-filter: blur(4px); - background-color: var(--custom-nameplate-neutral); - border-radius: 50%; - display: flex; - flex-grow: 0; - justify-content: center; - opacity: .9; - padding: 1px -} - -.closeButton__972a0.closeButtonForceShow__972a0,.closeButton__972a0:focus,.closeButton__972a0:hover { - opacity: 1 -} - -.closeButton__972a0.closeButtonForceShow__972a0 .innerCloseButtonPlated__972a0,.closeButton__972a0:focus .innerCloseButtonPlated__972a0,.closeButton__972a0:hover .innerCloseButtonPlated__972a0 { - -webkit-backdrop-filter: blur(10px); - backdrop-filter: blur(10px); - background-color: var(--custom-nameplate-neutral-hovered) -} - -.closeIconPlated__972a0 { - color: hsla(0,0%,100%,.9) -} - -.theme-light .closeIconPlated__972a0 { - color: rgba(0,0,0,.9) -} - -.iconsContainer__972a0 { - align-items: center; - box-sizing: border-box; - display: flex; - flex-direction: row; - flex-shrink: 0; - justify-content: flex-end; - padding-inline-start:var(--space-8);z-index: 1 -} - -.iconsContainer__972a0.nameplated__972a0 { - min-width: 32px -} - -.closeIcon__972a0,.favoriteIcon__972a0 { - display: block; - height: 16px; - width: 16px -} - -.closeButton__972a0,.favoriteIcon__972a0 { - margin: 0; - padding-inline-end:var(--space-xs)} - -.subtext__972a0 { - font-size: 12px; - font-weight: var(--font-weight-medium); - line-height: 16px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.mutedIcon__972a0 { - opacity: .3 -} - -.activityStatusText__972a0 { - color: inherit -} - -.decorator__972a0 { - margin-inline-start:4px} - -@media (hover: hover) { - .channel__972a0:focus-within .favoriteIcon__972a0,.channel__972a0:hover .favoriteIcon__972a0 { - display:none - } - - .channel__972a0:focus-within .closeButton__972a0,.channel__972a0:hover .closeButton__972a0 { - display: flex - } -} - -.interactive__972a0:hover { - background: var(--interactive-background-hover) -} - -.interactive__972a0:active,.interactiveSelected__972a0 { - background: var(--interactive-background-selected); - color: var(--text-default) -} - -.interactive__972a0:active .linkButtonIcon__972a0,.interactiveSelected__972a0 .linkButtonIcon__972a0 { - color: var(--icon-strong) -} - -.channel__972a0:not(.dm__972a0) { - height: auto -} - -.channel__972a0:not(.dm__972a0) .link__972a0 { - padding-block:var(--space-8) var(--space-8);padding-inline: var(--space-xs) var(--space-16) -} - -.channel__972a0:not(.dm__972a0) .avatarWithText__972a0 { - height: unset; - padding: 1px 0 -} - -.channel__972a0:not(.dm__972a0) svg { - height: 20px; - width: 20px -} - -.enable-forced-colors .unreadPill__972a0 { - background-color: Highlight; - height: 10px; - margin-top: -5px; - outline: 1px solid Canvas; - width: 6px -} - -.enable-forced-colors .closeButton__972a0 { - background-color: ButtonFace; - border: 1px solid ButtonFace; - color: ButtonText; - opacity: 1 -} - -.enable-forced-colors .closeButton__972a0:focus,.enable-forced-colors .closeButton__972a0:hover { - border-color: ButtonText -} - -.enable-forced-colors .interactive__972a0,.enable-forced-colors .link__972a0 { - color: ButtonText; - forced-color-adjust: none; - position: relative; - z-index: 1 -} - -.enable-forced-colors .interactive__972a0 .linkButtonIcon__972a0,.enable-forced-colors .link__972a0 .linkButtonIcon__972a0 { - color: ButtonText -} - -.enable-forced-colors .interactiveSelected__972a0,.enable-forced-colors .interactiveSelected__972a0 .link__972a0 { - background-color: Highlight; - color: HighlightText -} - -.enable-forced-colors .interactiveSelected__972a0 .link__972a0 .linkButtonIcon__972a0,.enable-forced-colors .interactiveSelected__972a0 .linkButtonIcon__972a0 { - color: HighlightText -} - -.empty__99e7c { - fill: var(--background-base-low); - padding: 16px -} - -.headerText__99e7c { - flex: 1; - margin-inline-end:var(--space-xs);overflow: hidden; - text-overflow: ellipsis -} - -.privateChannelsHeaderContainer__99e7c { - align-items: center; - display: flex; - height: var(--size-24); - justify-content: space-between; - padding-inline:var(--space-md) var(--space-xs);padding-bottom: var(--space-4); - padding-top: 0 -} - -.privateChannelsHeaderContainer__99e7c:hover { - color: var(--interactive-text-hover) -} - -.privateChannelRecipientsInviteButtonIcon__99e7c { - flex: unset; - height: 16px; - margin-inline:0;width: 16px -} - -.privateChannelRecipientsInviteButtonIcon__99e7c,.privateChannelRecipientsInviteButtonIconContainer__99e7c { - height: 16px; - width: 16px -} - -.scroller__99e7c { - background: var(--background-gradient-high,var(--background-base-lowest)); - margin-bottom: calc(var(--custom-app-panels-height, 0) + var(--space-xs) - 16px); - padding-bottom: 16px -} - -.scroller__99e7c::-webkit-scrollbar-track { - margin-bottom: 16px -} - -.enable-forced-colors .privateChannelsHeaderContainer__99e7c { - color: CanvasText; - forced-color-adjust: none -} - -.enable-forced-colors .privateChannelRecipientsInviteButtonIcon__99e7c { - border: 1px solid ButtonFace -} - -.enable-forced-colors .privateChannelRecipientsInviteButtonIcon__99e7c:hover { - border-color: ButtonText -} - -.base_e42a84 { - background-position: 0; - display: block; - position: absolute -} - -.sparkle_e42a84 { - animation: sparkle_e42a84 1.5s steps(17,start) infinite forwards; - background-position: -731px; - background-size: 774px 43px; - height: 43px; - width: 43px -} - -.pop_e42a84 { - animation: pop_e42a84 2.91666666667s steps(34,start) infinite forwards; - background-position: -816px; - background-size: 840px 24px; - height: 24px; - width: 24px -} - -.light_e42a84 { - animation: light_e42a84 2.58333333333s steps(30,start) infinite forwards; - background-size: 620px 20px; - height: 20px; - width: 20px -} - -.cross_e42a84,.light_e42a84 { - background-position: -600px -} - -.cross_e42a84 { - animation: cross_e42a84 2.083325s steps(23,start) infinite forwards; - background-size: 625px 25px; - height: 25px; - width: 25px -} - -.sparkleBlurple_e42a84,.sparkleGrey_e42a84,.sparkleWhite_e42a84 { -} - -.sparkleWhite_e42a84 { - background-image: url(/assets/fe36493b90ca212f.png) -} - -.sparkleGrey_e42a84 { - background-image: url(/assets/d4d24bae3c0559b3.png) -} - -.sparkleBlurple_e42a84 { - background-image: url(/assets/bf6de7bed70dd6d3.png) -} - -.popBlurple_e42a84,.popGrey_e42a84,.popWhite_e42a84 { -} - -.popWhite_e42a84 { - background-image: url(/assets/9e77accd9b306314.png) -} - -.popGrey_e42a84 { - background-image: url(/assets/ec347b8de4777f9a.png) -} - -.popBlurple_e42a84 { - background-image: url(/assets/889a2aa66cb786d7.png) -} - -.lightBlurple_e42a84,.lightGrey_e42a84,.lightWhite_e42a84 { -} - -.lightWhite_e42a84 { - background-image: url(/assets/d06f48f14653e6c8.png) -} - -.lightGrey_e42a84 { - background-image: url(/assets/b70ad231203bf921.png) -} - -.lightBlurple_e42a84 { - background-image: url(/assets/d2cc0071b8c9ceef.png) -} - -.crossBlurple_e42a84,.crossGrey_e42a84,.crossWhite_e42a84 { -} - -.crossWhite_e42a84 { - background-image: url(/assets/dd2ccc47416b2b19.png) -} - -.crossGrey_e42a84 { - background-image: url(/assets/d085fdd3215d0dc0.png) -} - -.crossBlurple_e42a84 { - background-image: url(/assets/18fc01aa48e3eedf.png) -} - -@keyframes sparkle_e42a84 { - 0% { - background-position: 0 - } - - 40% { - background-position: -731px - } - - to { - background-position: -731px - } -} - -@keyframes pop_e42a84 { - 0% { - background-position: 0 - } - - 40% { - background-position: -816px - } - - to { - background-position: -816px - } -} - -@keyframes light_e42a84 { - 0% { - background-position: 0 - } - - 40% { - background-position: -600px - } - - to { - background-position: -600px - } -} - -@keyframes cross_e42a84 { - 0% { - background-position: 0 - } - - 40% { - background-position: -600px - } - - to { - background-position: -600px - } -} - -.resizeHandle__4b144 { - bottom: -2px; - cursor: ns-resize; - height: 4px; - position: absolute; - width: 100% -} - -.unreadCount__23463 { - background: var(--interactive-text-default); - border-radius: 999px; - box-sizing: border-box; - color: var(--black); - flex-shrink: 0; - font-weight: var(--font-weight-semibold); - height: 16px; - margin-inline-start:-12px;min-width: 16px; - padding: 0 4px; - text-align: center; - width: auto -} - -.mention__23463 { - background: var(--background-feedback-notification); - color: var(--white) -} - -.circularButton__92e4b { - background: var(--background-gradient-lowest,var(--background-mod-normal)); - border-radius: 20px; - height: 40px; - width: 40px -} - -.wrapper__92e4b { - display: flex; - min-width: 32px; - position: relative; - width: auto -} - -.wrapper__92e4b svg { - flex-shrink: 0 -} - -.badge__92e4b { - flex-grow: 0; - flex-shrink: 1; - inset-inline-end: -4px; - position: absolute; - top: -4px -} - -.iframe__49094 { - height: 100%; - width: 100% -} - -.wrapper__49094 { - background-color: #000; - display: flex; - flex: 0 0 auto; - flex-direction: column; - position: relative; - z-index: 2 -} - -.wrapper__49094.resizable__49094 { - height: 65% -} - -.wrapper__49094.noChat__49094 { - height: 100%; - max-height: 100% -} - -.activityPanelContainer__49094 { - display: flex; - flex: 1; - flex-direction: column -} - -.header__49094 { - padding: 14px 8px -} - -.footer__49094,.header__49094 { - align-items: center; - display: flex; - flex-direction: row -} - -.footer__49094 { - justify-content: space-between; - padding: 12px 24px -} - -.minimizeContainer__49094 { - flex: 0 -} - -.circularButton__49094 { - background: var(--background-mod-normal); - border-radius: 20px; - height: 40px; - width: 40px -} - -.minimizeIcon__49094 { - height: 20px; - width: 20px -} - -.headerTitle__49094 { - flex: 1; - text-align: center -} - -.leaveButtonContainer__49094 { - margin: 0 16px -} - -.activityContainer__49094 { - align-items: center; - flex: 1; - flex-direction: row; - justify-content: center; - margin: 24px -} - -.activityContainerNoMargin__49094 { - margin: 0 -} - -.leaveActivityIcon__49094 { - height: 20px; - width: 20px -} - -.leaveActivityButton__49094 { - height: 40px; - width: 40px -} - -.footerButtons__49094 { - display: flex; - flex: 1; - flex-direction: row; - justify-content: center -} - -.flex__49094 { - flex: 1 -} - -.avatar__49094 { - border-radius: 50%; - height: 32px; - width: 32px -} - -.buttonSection__49094 { - align-items: center; - background: var(--plum-23); - border: 1px solid hsl(var(--plum-11-hsl)/.06); - border-radius: 12px; - display: flex; - gap: 8px; - justify-content: center; - padding: 4px -} - -.contextlessHeader__49094 { - align-items: center; - display: flex; - justify-content: space-between; - padding: var(--space-12) var(--space-16) -} - -.contextlessHeaderLeft__49094 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-8) -} - -.contextlessInviteButtonIcon__49094 { - height: 16px; - width: 16px -} - -.contextlessLeaveActivityButton__49094 { - border-radius: 8px; - padding: var(--space-4) -} - -.contextlessLeaveActivityButtonIcon__49094 { - padding: var(--space-4) var(--space-8) -} - -.fortniteUpsellModalButton__49094 { - background: var(--plum-23); - border: 1px solid hsl(var(--plum-11-hsl)/.06); - border-radius: 12px; - margin-inline-end:var(--space-24);padding: 4px -} - -.container__6b38f { - align-items: center; - background-color: var(--background-base-lower); - border-radius: 12px; - box-sizing: border-box; - cursor: pointer; - display: flex; - flex-direction: row; - padding: 16px; - width: 400px -} - -.container__6b38f:hover { - background-color: var(--background-base-low) -} - -.textContainer__6b38f { - margin-inline-start:16px} - -.caret__6b38f,.dismissButton__6b38f { - margin-inline-start:auto} - -.dismissButton__6b38f { - align-items: center; - display: flex; - justify-content: center -} - -.icon__6b38f { - border-radius: 50%; - display: flex; - padding: 6px -} - -.dot__6b38f { - margin: 0 8px -} - -.dot__6b38f,.inline__6b38f { - display: inline -} - -.pulse__6b38f { - animation: pulse-animation__6b38f 2s 1s infinite; - box-shadow: 0 0 0 0 hsl(var(--green-360-hsl)/.5) -} - -.container__6b38f:not(.pulse__6b38f) { - background-color: var(--opacity-black-52) -} - -.container__6b38f:not(.pulse__6b38f):hover { - background-color: var(--opacity-black-64) -} - -.container__6b38f:not(.pulse__6b38f):active { - background-color: var(--opacity-black-48) -} - -@keyframes pulse-animation__6b38f { - 70% { - box-shadow: 0 0 0 20px hsl(var(--green-360-hsl)/0) - } - - to { - box-shadow: 0 0 0 0 hsl(var(--green-360-hsl)/0) - } -} - -.enable-forced-colors .container__6b38f { - border: 1px solid ButtonText -} - -.enable-forced-colors .caret__6b38f { - color: ButtonText -} - -.eventPrompt_a5e25b { - margin-top: 8px -} - -.popoutContainer__02e2b { - align-items: center; - background: var(--background-surface-high); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-border),var(--shadow-high); - box-sizing: border-box; - display: flex; - filter: drop-shadow(0 0 12px #a944b0); - flex-direction: column; - opacity: 1; - overflow: hidden; - padding: 0; - text-align: center; - transform: translate3d(0,-24px,0); - width: 300px -} - -.full-motion .popoutContainer__02e2b { - transition: all .25s -} - -.image__02e2b { - background-color: #000 -} - -.image__02e2b,.infoContainerParent__02e2b { - width: 100% -} - -.infoContainer__02e2b { - margin: var(--space-12) -} - -.infoText__02e2b { - text-align: start -} - -.closeButton__02e2b { - inset-inline-end: var(--space-12); - position: absolute; - top: var(--space-12) -} - -.closeIcon__02e2b { - background-color: rgba(0,0,0,.44); - border-radius: var(--radius-round); - padding: var(--space-4) -} - -.hidden__02e2b { - opacity: 0; - pointer-events: none; - transform: translate3d(0,-14px,0) -} - -.poweredByNitroContainer__02e2b { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-4); - margin-top: var(--space-8) -} - -.activityItemButtonInnerClass__8a940 { - display: flex; - width: 100% -} - -.activityItem__8a940 { - border-radius: 12px; - cursor: pointer; - display: inline-block; - overflow: hidden; - position: relative; - transition: scale .2s; - width: 100% -} - -.activityItem_13_11__8a940 { - aspect-ratio: 13/11 -} - -.activityItem_16_9__8a940 { - aspect-ratio: 16/9 -} - -.activitySuggestionImage__8a940 { - height: 100%; - min-height: 100%; - min-width: 100%; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.iconOuterContainer__8a940 { - align-items: center; - display: flex; - inset: 0; - justify-content: center; - pointer-events: none; - position: absolute -} - -.iconInnerContainer__8a940 { - background-color: rgba(0,0,0,.8); - border-radius: 10px; - height: 20px; - pointer-events: none; - width: 20px -} - -.nitroIconContainer__8a940 { - bottom: 4px; - inset-inline-end: 4px; - position: absolute -} - -.nitroIcon__8a940 { - inset-inline-start: 1px; - position: absolute; - top: 2px -} - -.lockBackground__8a940 { - background-color: rgba(0,0,0,.2) -} - -.lockIconContainer__8a940 { - align-items: center; - border-radius: 50%; - display: flex; - justify-content: center; - position: absolute; - width: 20% -} - -.lock__8a940,.lockIconContainer__8a940 { - aspect-ratio: 1; - height: auto -} - -.lock__8a940 { - width: 75% -} - -.activityAction__8a940 { - align-items: center; - background-color: rgba(0,0,0,.45); - display: flex; - inset: 0; - justify-content: center; - pointer-events: none; - position: absolute -} - -.activityActionButton__8a940 { - background-color: var(--control-secondary-background-default); - border-radius: 12px; - padding: 4px 16px -} - -.activityActionButtonDanger__8a940 { - background-color: var(--control-critical-primary-background-default) -} - -.brokenImageIconWrapper__8a940 { - align-items: center; - background-color: #000; - display: flex; - justify-content: center -} - -.overlayBadge__8a940 { - inset-inline-start: 0; - padding: 4px; - position: absolute; - top: 0 -} - -.badgeContainer__8a940 { - align-items: flex-start; - display: flex; - flex-direction: column; - gap: 3px -} - -.container__9fa5a { - align-items: flex-start; - background-color: var(--background-surface-high); - border-radius: 16px; - box-shadow: var(--shadow-border),var(--shadow-high); - display: flex; - flex-direction: column; - min-width: 584px; - overflow: hidden; - padding: 12px -} - -.titleContainer__9fa5a { - justify-content: space-between; - width: 100% -} - -.titleContainer__9fa5a,.titleLeft__9fa5a { - align-items: center; - display: flex -} - -.titleLeftIcon__9fa5a { - margin-inline-end:4px} - -.titleRight__9fa5a { - align-items: center; - cursor: pointer; - display: flex -} - -.titleRightIcon__9fa5a { - margin-inline-start:4px} - -.activityContainer__9fa5a { - display: flex; - height: 88px; - margin-top: 16px; - position: relative; - width: 100% -} - -.activityContainer__9fa5a>:not(:first-child) { - margin-inline-start:16px} - -.activitySuggestion__9fa5a { - height: 88px -} - -.poster__9fa5a { - border-radius: 12px; - height: auto; - margin: 16px 0; - width: 584px -} - -.posterDivider__9fa5a { - background-color: var(--border-subtle); - height: 1px; - width: 100% -} - -.clickableBanner__9fa5a { - cursor: pointer; - transform: scale(1) -} - -.full-motion .clickableBanner__9fa5a { - transition: transform .25s -} - -.full-motion .clickableBanner__9fa5a:focus,.full-motion .clickableBanner__9fa5a:hover { - transform: scale(1.025) -} - -.dot__148e9 { - background-color: var(--interactive-text-active); - border-radius: 100%; - position: absolute; - transform: translateY(-5%) -} - -.maskPopout__148e9 { - border: 2px solid var(--background-surface-high) -} - -.maskPrimary__148e9 { - border: 2px solid var(--background-base-low) -} - -.maskSecondary__148e9 { - border: 2px solid var(--background-base-lower) -} - -.maskBlack__148e9 { - border: 2px solid var(--black) -} - -.alert__148e9 { - background-color: var(--background-feedback-notification) -} - -.blurpleTreatment__148e9 { - background-color: var(--blurple-50) -} - -.buttonColor__7b3e8 { - background-color: var(--primary-630); - color: #fff -} - -.buttonColor__7b3e8:hover { - background-color: var(--primary-700) -} - -@media (max-width: 1000px) { - .textButton__7b3e8 { - display:none - } -} - -@media (min-width: 1001px) { - .iconButton__7b3e8 { - display:none - } -} - -.container_faf161 { - align-items: flex-start; - background-color: var(--background-surface-high); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-border),var(--shadow-high); - display: flex; - flex-direction: column; - margin-top: 16px; - overflow: hidden; - padding: 16px -} - -.title_faf161 { - text-transform: capitalize -} - -.subtitle_faf161 { - margin-bottom: 16px -} - -.wishlistItemsContainer_faf161 { - display: flex; - flex-direction: row; - gap: 16px; - justify-content: center; - width: 100% -} - -.loading_faf161 { - height: 136px; - width: 716px -} - -.contextContainer_faf161 { - background-color: var(--background-surface-high) -} - -.container__1405b { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - position: relative -} - -.eventPromptsContainer__1405b { - bottom: 104px; - position: absolute -} - -@media (max-width: 456px) { - .controlButton__1405b { - margin:0 4px - } -} - -.wrapper__1405b { - align-items: center; - display: flex; - flex-direction: row; - gap: 12px -} - -.buttonContainer__1405b { - position: relative -} - -.buttonContainer__1405b,.buttonSection__1405b { - align-items: center; - display: flex; - justify-content: center -} - -.buttonSection__1405b { - background: var(--background-surface-high); - border: 1px solid var(--border-muted); - border-radius: 12px; - gap: 8px; - padding: 4px -} - -.root_c8dbe9 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.root_c8dbe9 .sprite_c8dbe9 { - animation: none; - position: relative -} - -.participant_c8dbe9 { - margin: 0 8px -} - -.transition_c8dbe9 { - position: relative -} - -.spriteWrapper_c8dbe9 { - inset: 0; - position: absolute -} - -.column__535e0 { - display: flex; - flex-direction: column -} - -.row__535e0 { - display: flex; - flex-direction: row -} - -.row__535e0.gap__535e0 { - gap: 8px -} - -.chatFlow__535e0 { - bottom: 0; - color: #fff; - display: flex; - flex-direction: column; - flex-shrink: 1; - gap: 8px; - inset-inline: 0; - margin: 0 auto; - max-height: 256px; - max-width: 980px; - min-width: 0; - opacity: 1; - overflow: hidden; - padding: 0 12px; - transition: all .3s ease; - width: 100% -} - -.chatFlow__535e0.hidden__535e0 { - opacity: 0; - pointer-events: none -} - -.chat__535e0 { - display: flex; - flex-direction: column; - max-height: 136px -} - -.chat__535e0,.history__535e0 { - height: 100%; - position: relative; - transition: all .3s ease-in-out -} - -.history__535e0 { - opacity: 1 -} - -.history__535e0.idle__535e0 { - opacity: 0 -} - -.message__535e0 { - align-items: flex-start; - background: transparent; - display: flex; - flex-direction: row; - font-size: 22px; - gap: 12px; - justify-content: flex-start; - overflow: visible; - padding: 6px 4px; - transition: all .3s ease-in-out -} - -.message__535e0:hover { - background: var(--message-background-hover) -} - -.message__535e0:not(.noAnimate__535e0) { - animation: messageIn__535e0 .3s ease-in-out forwards -} - -.message__535e0.out__535e0 { - animation: messageIn__535e0 .3s ease-in-out reverse forwards -} - -.author__535e0 { - flex-wrap: nowrap; - font-weight: bolder; - white-space: nowrap -} - -.content__535e0 { - color: #fff; - font-weight: 400 -} - -.attachment__535e0 { - border-radius: 12px -} - -.outerInput__535e0 { - -webkit-backdrop-filter: blur(10px); - backdrop-filter: blur(10px); - background: var(--border-faint,hsla(240,4%,61%,.04)); - border: 1px solid var(--border-subtle,hsla(240,4%,61%,.04)); - opacity: 1; - transition: all .2s ease!important -} - -.outerInput__535e0.idle__535e0 { - opacity: 0 -} - -.input__535e0 { - background: rgba(0,0,0,.24) -} - -@keyframes messageIn__535e0 { - 0% { - height: 0; - margin-top: -12px; - opacity: 0; - transform: translateY(12px) - } - - to { - height: 100%; - margin-top: 0; - opacity: 1; - transform: translate(0) - } -} - -.block__8dcfb { - background: rgba(36,36,41,.5); - border: 1px solid var(--border-faint,hsla(240,4%,61%,.04)); - border-radius: var(--radius-sm); - padding: var(--space-10) -} - -.clickable__8dcfb { - cursor: pointer -} - -.header__8dcfb { - margin-bottom: var(--space-12) -} - -.controlPopout__8dcfb { - -webkit-backdrop-filter: blur(3px); - backdrop-filter: blur(3px); - background: rgba(26,26,30,.9); - border: 1px solid var(--border-subtle,hsla(240,4%,61%,.12)); - border-radius: var(--radius-sm); - bottom: "auto"; - display: flex; - flex-direction: column; - gap: var(--space-12); - padding: var(--space-12); - position: absolute; - transform: translateY(calc(-100% - 8px)) scaleX(1); - transform-origin: bottom center; - width: 320px -} - -.full-motion .controlPopout__8dcfb { - transition: all .3s ease -} - -.controlPopout__8dcfb.hidden__8dcfb { - opacity: 0; - pointer-events: none; - transform: translateY(calc(-100% + 4px)) scale3d(.95,.95,.95) -} - -.row__8dcfb { - display: flex; - flex-direction: row -} - -.songBlock__8dcfb { - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - background: var(--color-background-mod-subtle,hsla(240,4%,61%,.08)); - background-clip: padding-box; - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - border: 2px solid var(--color-border-faint,hsla(240,4%,61%,0)); - border-radius: 16px; - box-sizing: border-box; - height: 90px; - margin-bottom: var(--space-4); - margin-top: var(--space-4); - overflow: hidden; - position: relative; - transform: scale(1) translateZ(1); - transform-style: preserve-3d; - width: 90px -} - -.full-motion .songBlock__8dcfb { - transition: all .3s ease -} - -.songBlock__8dcfb:hover { - border: 2px solid var(--color-border-faint,hsla(240,4%,61%,.15)) -} - -.full-motion .songBlock__8dcfb:hover { - transform: scale(1.025) -} - -.songBlock__8dcfb.selected__8dcfb { - border: 2px solid var(--color-border-faint,hsla(240,4%,61%,.4)) -} - -.songBlock__8dcfb.selected__8dcfb:hover { - border: 2px solid var(--color-border-faint,hsla(240,4%,61%,.45)) -} - -.visualizer__8dcfb { - align-items: center; - background: var(--control-overlay-secondary-background-active); - bottom: 0; - display: flex; - flex-direction: row; - gap: var(--space-4); - position: absolute; - top: 0; - inset-inline: 0; - justify-content: center; - opacity: 0 -} - -.full-motion .visualizer__8dcfb { - transition: all .3s ease -} - -.visualizer__8dcfb:hover { - background: var(--control-overlay-secondary-background-active) -} - -.visualizer__8dcfb.selected__8dcfb { - opacity: 1 -} - -.full-motion .visualizer__8dcfb.playing__8dcfb .dot__8dcfb { - animation: bounce__8dcfb .4s cubic-bezier(.175,.885,.32,1.275) infinite -} - -.full-motion .visualizer__8dcfb.playing__8dcfb .dot__8dcfb:first-child,.full-motion .visualizer__8dcfb.playing__8dcfb .dot__8dcfb:nth-child(3) { - animation-delay: .15s; - animation-duration: .6s -} - -.full-motion .visualizer__8dcfb.playing__8dcfb .dot__8dcfb:nth-child(2) { - animation-duration: .6s -} - -.dot__8dcfb,.visualizer__8dcfb.paused__8dcfb .dot__8dcfb { - transform: scaleY(.45) -} - -.dot__8dcfb { - background: var(--white); - border-radius: var(--space-8); - height: var(--space-24); - width: var(--space-8) -} - -.full-motion .dot__8dcfb { - transition: all .5s ease -} - -.muted__8dcfb * { - color: var(--icon-feedback-critical); - stroke: var(--icon-feedback-critical); - fill: var(--icon-feedback-critical) -} - -@keyframes bounce__8dcfb { - 0% { - transform: scaleY(.45) - } - - 50% { - transform: scale(1) - } - - to { - transform: scaleY(.45) - } -} - -.container__01290 { - -webkit-backdrop-filter: blur(4px); - backdrop-filter: blur(4px); - border-radius: var(--radius-sm); - display: flex; - flex-direction: column; - gap: var(--space-8); - inset-inline-start: 32px; - padding: var(--space-8); - position: absolute; - top: 64px -} - -.participantItem__01290 { - align-items: center; - cursor: pointer; - display: flex; - flex-direction: row; - opacity: .4; - position: relative; - transition: opacity .1s ease-in-out -} - -.speaking__01290 { - opacity: .9 -} - -.speaking__01290 .avatar__01290 { - outline: 1px solid var(--green-300); - outline-offset: 2px -} - -.avatar__01290 { - flex-shrink: 0 -} - -.username__01290 { - align-items: center; - box-sizing: border-box; - display: flex; - flex-shrink: 1; - gap: 8px; - height: 24px; - justify-content: center; - margin-inline-start:var(--space-4);max-width: 100%; - opacity: .8; - overflow: hidden; - padding: var(--space-4) var(--space-8); - text-overflow: ellipsis; - white-space: nowrap -} - -.gradientBackground__11664 { - background-color: var(--bg-animated-gradient-background-not-black); - contain: layout style size; - filter: blur(100px); - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.sphere__11664 { - inset-inline-start: 50%; - position: absolute; - transform: translate3d(-50%,0,0) -} - -.backgroundDark__11664 { - background: var(--bg-animated-gradient-background-indigo-1); - border-radius: 50%; - bottom: -920px; - filter: blur(200px); - height: 1570px; - opacity: .75; - width: 1260px -} - -.backgroundLight__11664 { - background: var(--bg-animated-gradient-background-pink-1); - border-radius: 50%; - bottom: -600px; - height: 1050px; - opacity: .66; - transform: translate3d(-50%,0,0) scaleX(.5) scaleY(.65); - width: 1120px -} - -.foregroundRing__11664 { - border: 15px solid var(--blurple-49); - border-radius: 50%; - bottom: -675px; - height: 1333px; - opacity: .75; - transform: translate3d(-50%,0,0) scale(1); - width: 1333px -} - -.foregroundBase__11664 { - background: var(--blurple-49); - border-radius: 50%; - bottom: -637.5px; - height: 1125px; - opacity: .75; - transform: translate3d(-50%,0,0) scaleX(.64) scaleY(.56); - width: 1125px -} - -.full-motion .backgroundDark__11664 { - animation: pulseBackgroundDark__11664 6s ease-in-out infinite; - animation-play-state: paused -} - -.full-motion .backgroundLight__11664 { - animation: pulseBackgroundLight__11664 6s ease-in-out infinite; - animation-play-state: paused -} - -.full-motion .foregroundRing__11664 { - animation: pulseForegroundRing__11664 4s ease-in-out infinite; - animation-play-state: paused -} - -.full-motion .foregroundBase__11664 { - animation: pulseForeground__11664 4s ease-in-out infinite; - animation-play-state: paused -} - -.full-motion.app-focused:not(.hardware-acceleration-disabled) .backgroundDark__11664 { - animation-play-state: running -} - -.full-motion.app-focused:not(.hardware-acceleration-disabled) .backgroundLight__11664 { - animation-play-state: running -} - -.full-motion.app-focused:not(.hardware-acceleration-disabled) .foregroundRing__11664 { - animation-play-state: running -} - -.full-motion.app-focused:not(.hardware-acceleration-disabled) .foregroundBase__11664 { - animation-play-state: running -} - -@keyframes pulseBackgroundDark__11664 { - 0%,to { - opacity: .75 - } - - 50% { - opacity: .5 - } -} - -@keyframes pulseBackgroundLight__11664 { - 0%,to { - transform: translate3d(-50%,0,0) scaleX(.65) scaleY(.75) - } - - 50% { - transform: translate3d(-50%,0,0) scaleX(.5) scaleY(.65) - } -} - -@keyframes pulseForegroundRing__11664 { - 0%,to { - border-width: 15px; - transform: translate3d(-50%,0,0) scale(1) - } - - 50% { - border-width: 25px; - transform: translate3d(-50%,0,0) scale(.834) - } -} - -@keyframes pulseForeground__11664 { - 0%,to { - transform: translate3d(-50%,0,0) scaleX(.64) scaleY(.56) - } - - 50% { - transform: translate3d(-50%,0,0) scaleX(.81) scaleY(.81) - } -} - -.pulseGradient__11664 { - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.container__5aa3a { - border-radius: 0; - flex-direction: column; - height: 100%; - margin: 0; - overflow: hidden; - width: 100% -} - -.container__5aa3a,.tiles__5aa3a { - align-items: center; - display: flex; - justify-content: center; - position: relative -} - -.tiles__5aa3a { - flex-wrap: wrap; - margin-bottom: 20px; - z-index: 1 -} - -.tile__5aa3a { - height: 90px; - margin: 4px; - width: 160px -} - -.participantsRow__5aa3a { - align-items: center; - display: flex; - flex-direction: row; - position: relative -} - -.channelName__5aa3a { - margin: 8px 0; - max-width: 75%; - overflow: hidden; - position: relative; - text-overflow: ellipsis; - white-space: nowrap -} - -.joinButton__5aa3a { - margin-top: 24px -} - -.buttonContainer__5aa3a { - align-items: flex-end; - display: flex; - flex-direction: row; - gap: var(--space-8); - z-index: 1 -} - -.content__5aa3a { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-24); - height: 100%; - justify-content: center; - position: relative; - width: 100% -} - -.header__5aa3a,.subtext__5aa3a { - text-align: center -} - -.root__6dcca { - align-items: center; - display: flex; - flex-direction: column; - height: calc(100% - 112px); - justify-content: center; - text-align: center; - width: 100% -} - -.art__6dcca { - height: 50%; - margin-bottom: 8px; - max-height: 222px; - max-width: 362px; - width: 50% -} - -.hidden__6dcca { - display: none -} - -.header__6dcca { - margin-bottom: 8px -} - -.row_d6271c { - justify-content: center -} - -.row_d6271c,.tile_d6271c { - display: flex -} - -.tile_d6271c { - align-items: center; - margin-inline-end:8px;margin-bottom: 8px -} - -.tile_d6271c.noVerticalMargin_d6271c { - margin-bottom: 0 -} - -.tile_d6271c.noHorizontalMargin_d6271c { - margin-inline-end:0} - -.tile_d6271c.padColumn_d6271c { - padding: 0 2px -} - -.tileSizer_d6271c { - aspect-ratio: 16/9; - width: 100% -} - -.root__4ad81 { - align-items: center; - background-color: var(--background-base-lower); - color: var(--white); - display: flex; - flex-direction: column; - height: 100%; - justify-content: center; - padding: 16px -} - -.art__4ad81 { - flex: 1; - margin-bottom: var(--space-24); - max-height: 210px; - min-height: 0 -} - -.heading__4ad81 { - flex: none; - margin-top: 16px; - padding: 0 16px; - text-align: center; - white-space: pre-line -} - -.checkboxContainer__4ad81 { - display: grid; - flex: none; - grid-template-columns: auto auto; - grid-gap: 8px; - align-items: center; - cursor: pointer; - margin-top: 16px -} - -.closeButton__4ad81 { - color: var(--interactive-text-default) -} - -.closeButtonContainer__4ad81 { - cursor: pointer; - inset-inline-end: 8px; - position: absolute; - top: 8px -} - -.closeButtonContainer__4ad81 :focus>.closeButton__4ad81,.closeButtonContainer__4ad81 :hover { - color: var(--interactive-text-hover) -} - -.activitiesContainer__4ad81 { - display: grid; - flex: none; - grid-template-columns: 1fr 1fr 1fr; - margin-top: 16px; - grid-gap: 16px -} - -.activitiesContainerSmol__4ad81 { - grid-gap: 8px -} - -.activitySuggestion__4ad81 { - max-width: 250px -} - -.clickableTile__4ad81 { - cursor: pointer; - display: flex; - flex: 1 -} - -.iconContainer__4ad81 { - align-items: center; - display: flex; - justify-content: center; - position: relative -} - -.iconPlusSign__4ad81 { - bottom: 4px -} - -.iconPlusSign__4ad81,.shelfButtonCloseButton__4ad81 { - inset-inline-end: 4px; - position: absolute -} - -.shelfButtonCloseButton__4ad81 { - cursor: pointer; - top: 4px -} - -.button__4ad81 { - border-radius: 100px -} - -.closeButtonIcon__4ad81 { - color: var(--background-base-lower) -} - -.closeButtonIcon__4ad81:hover { - color: var(--icon-muted) -} - -.singleUserRoot__4ad81 { - align-items: center; - background: radial-gradient(110.79% 100% at 50% 100%,var(--background-base-lower) 60%,var(--transparent) 100%),linear-gradient(270deg,var(--background-tile-gradient-pink-end) 0,var(--background-tile-gradient-pink-start) 80%),linear-gradient(0deg,var(--background-base-lower),var(--background-base-lower)),var(--opacity-black-20); - display: flex; - gap: 16px; - justify-content: center -} - -.root_f555ee { - align-items: center; - color: var(--white); - display: flex; - flex-direction: column; - height: 100%; - justify-content: center; - padding: 16px -} - -.text_f555ee { - font-weight: var(--font-weight-semibold); - margin-bottom: 16px; - text-align: center -} - -.buttonText_f555ee { - padding: 0 8px -} - -.art_f555ee { - margin-bottom: 16px -} - -.tile_eaee1d { - height: 100% -} - -.container__2aff1 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - pointer-events: none -} - -.tileContainer__2aff1 { - display: flex -} - -.tile__2aff1 { - aspect-ratio: 16/9; - border-radius: 8px; - margin-inline-end:8px;width: 100% -} - -.tile__2aff1:last-child { - margin-inline-end:0} - -.tile_ba65b0 { - height: 100%; - width: 100% -} - -.tileSizer_ba65b0 { - height: 112px; - width: 195px -} - -.tileSizer_ba65b0:not(:first-child) { - margin-inline-start:8px} - -.root_ba65b0 { - box-sizing: border-box; - display: flex; - height: 112px; - justify-content: center; - width: 100% -} - -.voiceCallWrapper_a21736 { - padding-bottom: 24px -} - -.videoGridWrapper_a21736 { - align-self: stretch -} - -.root_a21736 { - height: 100%; - position: relative; - width: 100% -} - -.flexCenter_a21736 { - align-items: center; - display: flex; - justify-content: center -} - -.videoFrame_a21736 { - flex: 1; - position: relative -} - -.videoWrapper_a21736 { - flex: 1 1 100%; - margin: 0 auto; - position: relative -} - -.videoWrapperAnimated_a21736 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.videoGrid_a21736 { - height: 100%; - min-width: 100px -} - -.hiddenParticipants_a21736 p { - margin: 0 -} - -.videoSizer_a21736 { - position: relative; - width: 100% -} - -.focusedVideo_a21736 { - height: 100% -} - -.participantsButton_a21736 { - inset-inline-end: 8px; - position: absolute; - top: 50%; - transform: translateY(-50%); - transition: opacity .2s ease-in-out; - z-index: 1 -} - -.participantsButton_a21736.idle_a21736 { - opacity: 0 -} - -.root__6981d { - height: 100% -} - -.root__6981d,.tileWrapper__6981d { - position: relative; - width: 100% -} - -.videoFrame__6981d { - display: flex; - flex: 1; - justify-content: center; - position: relative -} - -.videoWrapper__6981d { - z-index: 1 -} - -.actionRow__6981d { - bottom: -8px; - display: flex; - inset-inline-start: 50%; - position: absolute; - transform: translateX(-50%) translateY(100%); - transition: opacity .2s ease-in-out; - z-index: 10 -} - -.actionRow__6981d.idle__6981d { - opacity: 0 -} - -.viewAllButton__6981d { - background: var(--primary-630); - border-radius: 48px; - box-shadow: var(--shadow-border),var(--shadow-high); - margin-inline-start:8px;padding: 4px 8px -} - -.viewAllButton__6981d:hover { - background: var(--primary-700) -} - -.viewAllButtonInner__6981d { - align-items: center; - display: flex; - white-space: nowrap -} - -.buttonIcon__6981d { - color: var(--white); - margin-inline-end:4px} - -.participantsWrapperAnimated__6981d { - align-items: center; - display: flex; - flex-shrink: 0; - inset-inline: 8px; - bottom: 0; - overflow: hidden; - position: absolute -} - -.gradientBackground__41626 { - background-color: var(--bg-animated-gradient-background-not-black); - filter: blur(200px); - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.gradientBackground__41626>div { - border-radius: 50%; - position: absolute; - transform-origin: center center -} - -.leftDarkEllipse__41626 { - background: var(--bg-animated-gradient-background-indigo-2); - height: 88.5%; - inset-inline-start: -49.5%; - opacity: 1; - top: -44.2%; - transform: scaleX(1) scaleY(1.25); - width: 99% -} - -@keyframes leftDarkEllipse__41626 { - 0%,to { - opacity: 1; - transform: scaleX(1) scaleY(1.25) - } - - 55% { - opacity: .75; - transform: scale(.82) - } -} - -.leftLightEllipse__41626 { - background: var(--blurple-49); - height: 23.5%; - inset-inline-start: -22.5%; - opacity: 1; - top: -11.8%; - transform: scaleX(1) scaleY(1.25); - width: 45% -} - -@keyframes leftLightEllipse__41626 { - 0%,to { - opacity: .6; - transform: scaleX(1) scaleY(1.25) - } - - 55% { - opacity: .45; - transform: scale(.82) - } -} - -.rightDarkEllipse__41626 { - background: var(--bg-animated-gradient-background-indigo-2); - height: 88.5%; - inset-inline-end: -50%; - opacity: .75; - top: -44.2%; - transform: scale(.82); - width: 99% -} - -@keyframes rightDarkEllipse__41626 { - 0%,to { - opacity: .75; - transform: scale(.82) - } - - 55% { - opacity: .7; - transform: scaleX(1) scaleY(1.25) - } -} - -.rightLightEllipse__41626 { - background: var(--blurple-49); - height: 23.5%; - inset-inline-end: -22.5%; - opacity: .45; - top: -11.7%; - transform: scale(.82); - width: 45% -} - -@keyframes rightLightEllipse__41626 { - 0%,to { - opacity: .45; - transform: scale(.82) - } - - 55% { - opacity: .7; - transform: scaleX(1) scaleY(1.25) - } -} - -.centerEllipse__41626 { - background: var(--blurple-49); - height: 100%; - opacity: .45; - top: -50%; - transform: translate(50%) scale(.36); - width: 100% -} - -@keyframes centerEllipse__41626 { - 0%,to { - opacity: .45; - transform: scale(.36) - } - - 55% { - opacity: .6; - transform: scaleX(.7) scaleY(.3) - } -} - -.full-motion .leftDarkEllipse__41626 { - animation: leftDarkEllipse__41626 4.5s ease-in-out infinite -} - -.full-motion .leftLightEllipse__41626 { - animation: leftLightEllipse__41626 4.5s ease-in-out infinite -} - -.full-motion .rightDarkEllipse__41626 { - animation: rightDarkEllipse__41626 4.5s ease-in-out infinite -} - -.full-motion .rightLightEllipse__41626 { - animation: rightLightEllipse__41626 4.5s ease-in-out infinite -} - -.full-motion .centerEllipse__41626 { - animation: centerEllipse__41626 4.5s ease-in-out infinite -} - -.toastWrapper_d3c698 { - display: flex; - inset-inline-end: 0; - justify-content: flex-end; - pointer-events: all; - position: absolute; - width: 100% -} - -.toast_d3c698 { - cursor: pointer; - display: flex; - flex-direction: row; - overflow: hidden -} - -.header_d3c698 { - align-items: center; - display: flex -} - -.avatar_d3c698 { - border-radius: 100%; - height: 32px; - margin-inline-start:8px;width: 32px -} - -.avatar_d3c698,.messageContentWrapper_d3c698 { - box-shadow: var(--legacy-elevation-high) -} - -.messageContentWrapper_d3c698 { - align-items: center; - background: var(--background-base-lowest); - border-radius: 16px; - box-sizing: border-box; - color: var(--text-default); - display: flex; - min-height: 32px; - overflow: hidden; - padding: 4px 12px; - position: relative -} - -.toast_d3c698:hover .messageContentWrapper_d3c698 { - background: var(--background-base-lower) -} - -.messageContentWrapper_d3c698.mentioned_d3c698:before { - background: hsl(var(--yellow-300-hsl)/.15); - border-radius: 16px; - box-shadow: inset 0 0 0 2px hsl(var(--yellow-300-hsl)/.3); - content: ""; - height: 100%; - inset-inline-start: 0; - position: absolute; - width: 100% -} - -.messageContentWrapper_d3c698 .sticker_d3c698,.messageContentWrapper_d3c698 .emoji.jumboable { - padding: 4px 0 -} - -.messageContent_d3c698 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.messageContentTrailingIcon_d3c698 { - flex-shrink: 0; - margin-inline-start:4px} - -.messageContentLeadingIcon_d3c698 { - flex-shrink: 0; - margin-inline-end:4px} - -.root_bfe55a { - align-items: center; - background: var(--black); - color: var(--white); - display: flex; - height: 100%; - justify-content: center; - position: relative; - width: 100% -} - -html.keyboard-mode .root_bfe55a.idle_bfe55a:not(:focus-within),html:not(.keyboard-mode) .root_bfe55a.idle_bfe55a { - cursor: none -} - -html.keyboard-mode .root_bfe55a.idle_bfe55a:not(:focus-within) .gradientContainer_bfe55a,html:not(.keyboard-mode) .root_bfe55a.idle_bfe55a .gradientContainer_bfe55a { - opacity: 0 -} - -html.keyboard-mode .root_bfe55a.idle_bfe55a:not(:focus-within) .topControls_bfe55a,html:not(.keyboard-mode) .root_bfe55a.idle_bfe55a .topControls_bfe55a { - opacity: 0; - transform: translate3d(0,-8px,0) -} - -html.keyboard-mode .root_bfe55a.idle_bfe55a:not(:focus-within) .bottomControls_bfe55a,html:not(.keyboard-mode) .root_bfe55a.idle_bfe55a .bottomControls_bfe55a { - opacity: 0; - transform: translate3d(0,8px,0) -} - -.videoControls_bfe55a { - pointer-events: none; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - display: flex; - flex-direction: column; - justify-content: space-between; - padding: 0 var(--space-md) var(--space-md); - z-index: 1001 -} - -.gradientContainer_bfe55a { - background-image: linear-gradient(#000,rgba(0,0,0,.738) 19%,rgba(0,0,0,.541) 34%,rgba(0,0,0,.382) 47%,rgba(0,0,0,.278) 56.5%,rgba(0,0,0,.194) 65%,rgba(0,0,0,.126) 73%,rgba(0,0,0,.075) 80.2%,rgba(0,0,0,.042) 86.1%,rgba(0,0,0,.021) 91%,rgba(0,0,0,.008) 95.2%,rgba(0,0,0,.002) 98.2%,transparent); - height: 160px; - pointer-events: none; - transition: opacity .2s ease-in-out -} - -.gradientTop_bfe55a { - top: 0 -} - -.gradientBottom_bfe55a,.gradientTop_bfe55a { - inset-inline: 0; - position: absolute -} - -.gradientBottom_bfe55a { - bottom: 0; - transform: scaleY(-1) -} - -.controlSection_bfe55a { - pointer-events: all -} - -.full-motion .controlSection_bfe55a { - transition: transform .2s ease-in-out,opacity .2s ease-in-out -} - -.bottomControls_bfe55a,.topControls_bfe55a { - flex: 0 1 auto; - transform: translateZ(0); - width: 100% -} - -.bottomControls_bfe55a { - align-items: center; - display: flex; - justify-content: space-between; - line-height: 0 -} - -.bottomControls_bfe55a .edgeControls_bfe55a:first-child:not(:empty) { - margin-inline-end:var(--space-lg)} - -.bottomControls_bfe55a .edgeControls_bfe55a: last-child:not(:empty) { - margin-inline-start:var(--space-lg) -} - -.edgeControls_bfe55a { - gap: var(--space-lg); - width: 100% -} - -.screenMessage_bfe55a { - background-color: var(--opacity-black-60); - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.title_bfe55a { - font-size: 24px -} - -.supportingText_bfe55a,.title_bfe55a { - font-weight: var(--font-weight-medium); - line-height: 1.25; - text-align: center -} - -.supportingText_bfe55a { - font-size: 16px -} - -.title_bfe55a { - color: var(--white) -} - -.supportingText_bfe55a { - color: var(--primary-300) -} - -.full-motion .arrow__6c50b { - transition: transform .2s ease-in-out -} - -.up__6c50b { - transform: rotate(180deg) -} - -.container_f0df50 { - inset-inline-start: 48px; - position: absolute -} - -.emoji_f0df50 { - opacity: 0; - position: absolute -} - -.full-motion .emoji1_f0df50 { - animation: floating-animation-1_f0df50 1.4s cubic-bezier(.25,.25,.75,.75) -} - -.full-motion .emoji2_f0df50 { - animation: floating-animation-2_f0df50 1.4s cubic-bezier(.25,.25,.75,.75) -} - -.full-motion .emoji3_f0df50 { - animation: floating-animation-3_f0df50 1.4s cubic-bezier(.25,.25,.75,.75) -} - -.full-motion .emoji4_f0df50 { - animation: floating-animation-4_f0df50 1.4s cubic-bezier(.25,.25,.75,.75) -} - -@keyframes floating-animation-1_f0df50 { - 0% { - bottom: 0; - inset-inline-start: 0; - opacity: .6; - transform: scale(1) - } - - 33% { - bottom: 100px; - inset-inline-start: -10px; - opacity: .8; - transform: scale(1.75) - } - - 66% { - bottom: 200px; - inset-inline-start: -20px; - opacity: .8; - transform: scale(1.75) - } - - to { - bottom: 300px; - inset-inline-start: -20px; - opacity: 0; - transform: scale(1.25) - } -} - -@keyframes floating-animation-2_f0df50 { - 0% { - bottom: 25px; - inset-inline-start: 0; - opacity: 0; - transform: scale(1.4) - } - - 25% { - bottom: 100px; - inset-inline-start: 15px; - opacity: .6; - transform: scale(2) - } - - 50% { - bottom: 175px; - inset-inline-start: 10px; - opacity: .8; - transform: scale(1.75) - } - - 75% { - bottom: 250px; - inset-inline-start: 15px; - opacity: .8; - transform: scale(1.5) - } - - to { - bottom: 325px; - inset-inline-start: 20px; - opacity: 0; - transform: scale(1) - } -} - -@keyframes floating-animation-3_f0df50 { - 0% { - bottom: -20px; - inset-inline-start: 0; - opacity: 0; - transform: scale(1) - } - - 33% { - bottom: 80px; - inset-inline-start: 5px; - opacity: .8; - transform: scale(1.6) - } - - 66% { - bottom: 190px; - inset-inline-start: 10px; - opacity: .8; - transform: scale(1.3) - } - - to { - bottom: 290px; - inset-inline-start: 15px; - opacity: 0; - transform: scale(1) - } -} - -@keyframes floating-animation-4_f0df50 { - 0% { - bottom: 60px; - inset-inline-start: 0; - opacity: .4; - transform: scale(1) - } - - 33% { - bottom: 160px; - inset-inline-start: -5px; - opacity: .8; - transform: scale(1.75) - } - - 66% { - bottom: 260px; - inset-inline-start: -10px; - opacity: .8; - transform: scale(1.25) - } - - to { - bottom: 360px; - inset-inline-start: -15px; - opacity: 0; - transform: scale(1) - } -} - -.chat_f75fb0 { - background: var(--background-gradient-chat,var(--background-base-lowest)); - display: flex; - flex-direction: column; - min-height: 0; - min-width: 0; - overflow: hidden; - position: relative -} - -.chat_f75fb0[data-has-border=true] { - border-top: 1px solid var(--app-frame-border) -} - -[data-collapsed=true]>.chat_f75fb0 { - border-inline-start:1px solid var(--border-subtle);border-start-start-radius: var(--radius-md) -} - -.refresh-fast-follow-distinct-borders .chat_f75fb0.threadSidebarOpen_f75fb0 { - border-inline-end-color:var(--app-frame-border)} - -.chat_f75fb0 { - flex: 1 1 auto -} - -.chat_f75fb0 .uploadArea_f75fb0 { - position: fixed -} - -.chat_f75fb0.threadSidebarOpen_f75fb0 { - border-inline-end:1px solid var(--border-subtle);border-radius: 0 8px 8px 0 -} - -.chat_f75fb0.threadSidebarOpen_f75fb0 .uploadArea_f75fb0 { - border-radius: 0 8px 8px 0; - margin-inline-end:8px;width: auto -} - -.chat_f75fb0.threadSidebarOpen_f75fb0.threadSidebarFloating_f75fb0 .uploadArea_f75fb0 { - border-radius: 0; - margin-inline-end:0} - -.typing_f75fb0 { - display: flex -} - -.form_f75fb0 { - flex-shrink: 0; - margin-top: -16px; - position: relative -} - -.form_f75fb0,.formWithLoadedChatInput_f75fb0 { - padding-inline:var(--space-xs)} - -.formWithLoadedChatInput_f75fb0: before { - content:""; - height: .5rem; - position: absolute -} - -.formWithLoadedChatInput_f75fb0 :before { - top: 0; - inset-inline: 0 -} - -.formWithLoadedChatInput_f75fb0:before { - background: linear-gradient(to bottom,transparent,var(--background-base-low) 100%) -} - -.custom-theme-background .formWithLoadedChatInput_f75fb0:before { - content: unset -} - -.chatContent_f75fb0 { - background: var(--background-gradient-chat,var(--background-base-lower)); - display: flex; - flex: 1 1 auto; - flex-direction: column; - min-height: 0; - min-width: 0; - position: relative -} - -.cursorPointer_f75fb0 { - cursor: pointer -} - -.content_f75fb0 { - align-items: stretch; - display: flex; - flex: 1 1 auto; - flex-direction: row; - justify-content: stretch; - min-height: 0; - min-width: 0; - position: relative -} - -.content_f75fb0.noChat_f75fb0 { - overflow: hidden -} - -.content_f75fb0:before { - box-shadow: var(--shadow-ledge); - content: ""; - display: none; - height: 1px; - position: absolute; - top: -1px; - inset-inline: 0; - pointer-events: none; - z-index: 1 -} - -.channelBottomBarArea_f75fb0 { - display: flex; - flex-direction: row -} - -.channelTextArea_f75fb0 { - background: var(--background-gradient-highest,var(--chat-background-default)); - margin-bottom: var(--custom-chat-input-margin-bottom) -} - -.refresh-fast-follow-avatars .form_f75fb0 { - padding-inline:min(var(--space-xs),var(--space-8))} - -.titleWrapper_f75fb0 { - align-items: center; - display: flex; - flex: 1 1 auto; - overflow: hidden -} - -.editPartyIcon_f75fb0 { - cursor: pointer; - margin-inline-end:4px;opacity: .6; - transition: opacity .2s ease -} - -.editPartyIcon_f75fb0:hover { - opacity: 1 -} - -.channelName_f75fb0 { - margin: 0 8px -} - -.avatar_f75fb0 { - margin-inline-end:var(--space-8)} - -.parentChannelName_f75fb0 { - color: var(--text-default) -} - -.parentChannelName_f75fb0:hover { - color: var(--text-strong) -} - -.title_f75fb0 { - flex: 0 0 auto; - padding-inline-end:var(--space-xs);padding-top: var(--space-8); - position: relative; - z-index: 100 -} - -.gdm_f75fb0 { - padding-inline-start:calc(var(--custom-message-margin-horizontal)/2)} - -.followButton_f75fb0 { - margin-inline-end: 8px; - padding: 4px 8px -} - -.status_f75fb0 { - align-items: center; - display: flex; - flex: 0 0 auto; - margin-inline-end:8px} - -.stop-animations .title_f75fb0 { - -webkit-app-region: no-drag -} - -.theme-light.custom-client-theme .title_f75fb0 { - --background-gradient-lowest: var(--background-gradient-chat) -} - -.guildBreadcrumbContainer_f75fb0 { - align-items: center; - cursor: pointer; - display: flex; - flex-shrink: 0 -} - -.guildSidebar_f75fb0 { - flex-shrink: 0 -} - -.guildBreadcrumbIcon_f75fb0 { - margin-inline-end:var(--space-4)} - -.loader_f75fb0 { - align-items: center; - display: flex; - justify-content: center; - width: 100% -} - -.enable-forced-colors .cursorPointer_f75fb0 { - background-color: ButtonFace; - color: ButtonText; - forced-color-adjust: none; - text-decoration: underline -} - -.forumPostTitle_f75fb0 { - margin-inline-start:8px} - -.forumPostSidebarTitle_f75fb0 { - margin-inline-start:0} - -.subtitleContainer_f75fb0 { - display: flex; - flex-direction: column; - position: relative -} - -.secureFramesIcon_f75fb0 { - margin-inline-start:4px} - -.shaker_f75fb0 { - flex: 1; - width: 100% -} - -.linkedLobbyTooltip_f75fb0 { - margin-inline-end:8px} - -.linkedLobby_f75fb0,.linkedLobbyTooltip_f75fb0 { - align-items: center; - display: flex -} - -.linkedLobbyApplicationIcon_f75fb0 { - border-radius: var(--radius-sm); - height: 14px; - margin-inline:8px 4px;width: 14px -} - -.linkedLobbyEducationTooltipWrapper_f75fb0 { - max-width: 288px; - pointer-events: all -} - -.linkedLobbyEducationTooltip_f75fb0 { - align-items: center; - display: flex; - flex-direction: column; - gap: 4px; - padding: 8px 4px; - text-align: center -} - -.linkedLobbyEducationTooltip_f75fb0 p { - margin: 0 -} - -.linkedLobbyEducationTooltipCloseClickContainer_f75fb0 { - cursor: pointer; - inset-inline-end: 8px; - position: absolute; - top: 8px -} - -.linkedLobbyEducationTooltipCloseClickContainer_f75fb0:hover .linkedLobbyEducationTooltipCloseIcon_f75fb0 { - color: var(--interactive-text-active) -} - -.linkedLobbyEducationTooltipCloseIcon_f75fb0 { - color: var(--icon-muted); - height: 24px; - width: 24px -} - -.container__01ae2 { - background: var(--background-gradient-app-frame,var(--background-base-lowest)); - border-inline-end:1px solid var(--app-frame-border);border-inline-start: 1px solid var(--app-frame-border); - border-radius: 8px 0 0 8px; - border-top: 1px solid var(--app-frame-border); - box-sizing: border-box; - display: flex; - flex: 0 0 auto; - flex-direction: column; - overflow: hidden; - position: relative; - width: 400px -} - -.floating__01ae2 { - border-inline-end:1px solid var(--background-base-lower);border-inline-start: 1px solid var(--background-base-lower); - border-top: 1px solid var(--background-base-lower); - bottom: 0; - filter: drop-shadow(0 8px 40px var(--opacity-black-32)); - inset-inline-end: 0; - position: absolute; - top: 0; - width: 400px; - z-index: 100 -} - -.notFloating__01ae2 { - display: none -} - -.resizeHandle__01ae2 { - background: var(--background-gradient-app-frame,var(--background-base-lowest)); - cursor: ew-resize; - flex-shrink: 0; - height: 100%; - width: var(--chat-resize-handle-width) -} - -.chatTarget__01ae2 { - background-color: var(--background-base-lowest) -} - -.chatLayerWrapper__01ae2 { - display: flex; - height: 100%; - inset-inline-end: 0; - justify-content: flex-end; - pointer-events: auto; - position: absolute; - top: 0 -} - -.hidden__01ae2 { - display: none -} - -.enable-forced-colors .resizeHandle__01ae2 { - background-color: ButtonText; - width: 4px -} - -.chat_ee72fa { - align-items: stretch; - display: flex; - flex: 1 1 auto; - flex-direction: column; - justify-content: stretch; - min-height: 0; - min-width: 0; - position: relative -} - -.chat_ee72fa:before { - box-shadow: var(--elevation-low); - content: ""; - display: block; - height: 1px; - position: absolute; - top: -1px; - inset-inline: 0; - pointer-events: none; - z-index: 1 -} - -.divider_cfc051 { - margin-inline-end:16px} - -.eventSchedule_cfc051 { - flex: 1 -} - -.eventName_cfc051 { - margin: auto 8px; - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.akaBadge__488b1 { - background-color: var(--background-base-lower); - margin-inline-start:8px} - -.akaBadge__488b1,.nicknames__488b1 { - color: var(--text-default) -} - -.nicknames__488b1 { - flex: 1 1 auto; - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 18px; - margin-block:0;margin-inline:8px 0;min-width: 0; - width: 100% -} - -.nicknames__488b1 span { - cursor: pointer -} - -.avatars__488b1 { - margin-inline-start:8px} - -.avatar__488b1 { - border-radius: 50%; - cursor: pointer; - display: block; - height: 16px; - width: 16px -} - -.tooltip__488b1 { - display: inline -} - -.spacer__488b1 { - flex: 1 1 auto -} - -.divider__488b1 { - margin-inline-start:8px} - -.moreAvatars__488b1 { - box-sizing: border-box; - color: var(--interactive-text-default); - cursor: pointer; - height: 16px; - overflow: hidden; - width: 16px -} - -.moreAvatars__488b1:hover { - color: var(--interactive-text-hover) -} - -.plusIcon__488b1 { - display: block; - height: 18px; - margin-top: -1px; - margin-inline-start:-1px;width: 18px -} - -.hoverableContainer__754bd { - align-items: center; - align-self: stretch; - border-radius: 8px; - cursor: pointer; - display: flex; - flex-direction: row; - gap: calc(var(--custom-message-margin-horizontal)/2); - height: 32px; - padding-inline:calc(var(--custom-message-margin-horizontal)/2);white-space: nowrap -} - -.hoverableContainer__754bd .editIcon__754bd { - display: none -} - -.hoverableContainer__754bd .gdmIcon__754bd { - margin-inline-end:4px} - -.hoverableContainer__754bd:hover { - background-color: var(--background-mod-subtle) -} - -.hoverableContainer__754bd:hover .editIcon__754bd { - display: block -} - -.heading-sm\/normal__6ec1a { - font-family: var(--font-display); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.heading-sm\/normal__6ec1a.fontScaling__6ec1a { - font-size: .875rem -} - -.heading-sm\/medium__6ec1a { - font-family: var(--font-display); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.heading-sm\/medium__6ec1a.fontScaling__6ec1a { - font-size: .875rem -} - -.heading-sm\/semibold__6ec1a { - font-family: var(--font-display); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.heading-sm\/semibold__6ec1a.fontScaling__6ec1a { - font-size: .875rem -} - -.heading-sm\/bold__6ec1a { - font-family: var(--font-display); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.heading-sm\/bold__6ec1a.fontScaling__6ec1a { - font-size: .875rem -} - -.heading-sm\/extrabold__6ec1a { - font-family: var(--font-display); - font-size: 14px; - font-weight: 800; - line-height: 1.2857142857142858 -} - -.heading-sm\/extrabold__6ec1a.fontScaling__6ec1a { - font-size: .875rem -} - -.heading-md\/normal__6ec1a { - font-family: var(--font-display); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.heading-md\/normal__6ec1a.fontScaling__6ec1a { - font-size: 1rem -} - -.heading-md\/medium__6ec1a { - font-family: var(--font-display); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.heading-md\/medium__6ec1a.fontScaling__6ec1a { - font-size: 1rem -} - -.heading-md\/semibold__6ec1a { - font-family: var(--font-display); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.heading-md\/semibold__6ec1a.fontScaling__6ec1a { - font-size: 1rem -} - -.heading-md\/bold__6ec1a { - font-family: var(--font-display); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.heading-md\/bold__6ec1a.fontScaling__6ec1a { - font-size: 1rem -} - -.heading-md\/extrabold__6ec1a { - font-family: var(--font-display); - font-size: 16px; - font-weight: 800; - line-height: 1.25 -} - -.heading-md\/extrabold__6ec1a.fontScaling__6ec1a { - font-size: 1rem -} - -.heading-lg\/normal__6ec1a { - font-family: var(--font-display); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.heading-lg\/normal__6ec1a.fontScaling__6ec1a { - font-size: 1.25rem -} - -.heading-lg\/medium__6ec1a { - font-family: var(--font-display); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.heading-lg\/medium__6ec1a.fontScaling__6ec1a { - font-size: 1.25rem -} - -.heading-lg\/semibold__6ec1a { - font-family: var(--font-display); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.heading-lg\/semibold__6ec1a.fontScaling__6ec1a { - font-size: 1.25rem -} - -.heading-lg\/bold__6ec1a { - font-family: var(--font-display); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.heading-lg\/bold__6ec1a.fontScaling__6ec1a { - font-size: 1.25rem -} - -.heading-lg\/extrabold__6ec1a { - font-family: var(--font-display); - font-size: 20px; - font-weight: 800; - line-height: 1.2 -} - -.heading-lg\/extrabold__6ec1a.fontScaling__6ec1a { - font-size: 1.25rem -} - -.heading-xl\/normal__6ec1a { - font-family: var(--font-display); - font-size: 24px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xl\/normal__6ec1a.fontScaling__6ec1a { - font-size: 1.5rem -} - -.heading-xl\/medium__6ec1a { - font-family: var(--font-display); - font-size: 24px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xl\/medium__6ec1a.fontScaling__6ec1a { - font-size: 1.5rem -} - -.heading-xl\/semibold__6ec1a { - font-family: var(--font-display); - font-size: 24px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xl\/semibold__6ec1a.fontScaling__6ec1a { - font-size: 1.5rem -} - -.heading-xl\/bold__6ec1a { - font-family: var(--font-display); - font-size: 24px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xl\/bold__6ec1a.fontScaling__6ec1a { - font-size: 1.5rem -} - -.heading-xl\/extrabold__6ec1a { - font-family: var(--font-display); - font-size: 24px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xl\/extrabold__6ec1a.fontScaling__6ec1a { - font-size: 1.5rem -} - -.heading-xxl\/normal__6ec1a { - font-family: var(--font-display); - font-size: 32px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xxl\/normal__6ec1a.fontScaling__6ec1a { - font-size: 2rem -} - -.heading-xxl\/medium__6ec1a { - font-family: var(--font-display); - font-size: 32px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xxl\/medium__6ec1a.fontScaling__6ec1a { - font-size: 2rem -} - -.heading-xxl\/semibold__6ec1a { - font-family: var(--font-display); - font-size: 32px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xxl\/semibold__6ec1a.fontScaling__6ec1a { - font-size: 2rem -} - -.heading-xxl\/bold__6ec1a { - font-family: var(--font-display); - font-size: 32px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xxl\/bold__6ec1a.fontScaling__6ec1a { - font-size: 2rem -} - -.heading-xxl\/extrabold__6ec1a { - font-family: var(--font-display); - font-size: 32px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xxl\/extrabold__6ec1a.fontScaling__6ec1a { - font-size: 2rem -} - -.eyebrow__6ec1a { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - letter-spacing: .02em; - line-height: 1.3333333333333333; - text-transform: uppercase -} - -.eyebrow__6ec1a.fontScaling__6ec1a { - font-size: .75rem -} - -.heading-deprecated-12\/normal__6ec1a { - font-family: var(--font-display); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/normal__6ec1a.fontScaling__6ec1a { - font-size: .75rem -} - -.heading-deprecated-12\/medium__6ec1a { - font-family: var(--font-display); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/medium__6ec1a.fontScaling__6ec1a { - font-size: .75rem -} - -.heading-deprecated-12\/semibold__6ec1a { - font-family: var(--font-display); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/semibold__6ec1a.fontScaling__6ec1a { - font-size: .75rem -} - -.heading-deprecated-12\/bold__6ec1a { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/bold__6ec1a.fontScaling__6ec1a { - font-size: .75rem -} - -.heading-deprecated-12\/extrabold__6ec1a { - font-family: var(--font-display); - font-size: 12px; - font-weight: 800; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/extrabold__6ec1a.fontScaling__6ec1a { - font-size: .75rem -} - -.redesign\/heading-18\/bold__6ec1a { - font-family: var(--font-display); - font-size: 18px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/heading-18\/bold__6ec1a.fontScaling__6ec1a { - font-size: 1.125rem -} - -.text-xxs\/normal__6ec1a { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 400; - line-height: 1.2 -} - -.text-xxs\/normal__6ec1a.fontScaling__6ec1a { - font-size: .625rem -} - -.text-xxs\/medium__6ec1a { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 500; - line-height: 1.2 -} - -.text-xxs\/medium__6ec1a.fontScaling__6ec1a { - font-size: .625rem -} - -.text-xxs\/semibold__6ec1a { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 600; - line-height: 1.2 -} - -.text-xxs\/semibold__6ec1a.fontScaling__6ec1a { - font-size: .625rem -} - -.text-xxs\/bold__6ec1a { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 700; - line-height: 1.2 -} - -.text-xxs\/bold__6ec1a.fontScaling__6ec1a { - font-size: .625rem -} - -.text-xs\/normal__6ec1a { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.text-xs\/normal__6ec1a.fontScaling__6ec1a { - font-size: .75rem -} - -.text-xs\/medium__6ec1a { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.text-xs\/medium__6ec1a.fontScaling__6ec1a { - font-size: .75rem -} - -.text-xs\/semibold__6ec1a { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.text-xs\/semibold__6ec1a.fontScaling__6ec1a { - font-size: .75rem -} - -.text-xs\/bold__6ec1a { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.text-xs\/bold__6ec1a.fontScaling__6ec1a { - font-size: .75rem -} - -.text-sm\/normal__6ec1a { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.text-sm\/normal__6ec1a.fontScaling__6ec1a { - font-size: .875rem -} - -.text-sm\/medium__6ec1a { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.text-sm\/medium__6ec1a.fontScaling__6ec1a { - font-size: .875rem -} - -.text-sm\/semibold__6ec1a { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.text-sm\/semibold__6ec1a.fontScaling__6ec1a { - font-size: .875rem -} - -.text-sm\/bold__6ec1a { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.text-sm\/bold__6ec1a.fontScaling__6ec1a { - font-size: .875rem -} - -.text-md\/normal__6ec1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.text-md\/normal__6ec1a.fontScaling__6ec1a { - font-size: 1rem -} - -.text-md\/medium__6ec1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.text-md\/medium__6ec1a.fontScaling__6ec1a { - font-size: 1rem -} - -.text-md\/semibold__6ec1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.text-md\/semibold__6ec1a.fontScaling__6ec1a { - font-size: 1rem -} - -.text-md\/bold__6ec1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.text-md\/bold__6ec1a.fontScaling__6ec1a { - font-size: 1rem -} - -.text-lg\/normal__6ec1a { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.text-lg\/normal__6ec1a.fontScaling__6ec1a { - font-size: 1.25rem -} - -.text-lg\/medium__6ec1a { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.text-lg\/medium__6ec1a.fontScaling__6ec1a { - font-size: 1.25rem -} - -.text-lg\/semibold__6ec1a { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.text-lg\/semibold__6ec1a.fontScaling__6ec1a { - font-size: 1.25rem -} - -.text-lg\/bold__6ec1a { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.text-lg\/bold__6ec1a.fontScaling__6ec1a { - font-size: 1.25rem -} - -.redesign\/message-preview\/normal__6ec1a { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/normal__6ec1a.fontScaling__6ec1a { - font-size: .9375rem -} - -.redesign\/message-preview\/medium__6ec1a { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/medium__6ec1a.fontScaling__6ec1a { - font-size: .9375rem -} - -.redesign\/message-preview\/semibold__6ec1a { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/semibold__6ec1a.fontScaling__6ec1a { - font-size: .9375rem -} - -.redesign\/message-preview\/bold__6ec1a { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/bold__6ec1a.fontScaling__6ec1a { - font-size: .9375rem -} - -.redesign\/channel-title\/normal__6ec1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.375 -} - -.redesign\/channel-title\/normal__6ec1a.fontScaling__6ec1a { - font-size: 1rem -} - -.redesign\/channel-title\/medium__6ec1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.375 -} - -.redesign\/channel-title\/medium__6ec1a.fontScaling__6ec1a { - font-size: 1rem -} - -.redesign\/channel-title\/semibold__6ec1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.375 -} - -.redesign\/channel-title\/semibold__6ec1a.fontScaling__6ec1a { - font-size: 1rem -} - -.redesign\/channel-title\/bold__6ec1a { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.375 -} - -.redesign\/channel-title\/bold__6ec1a.fontScaling__6ec1a { - font-size: 1rem -} - -.display-sm__6ec1a { - font-family: var(--font-headline); - font-size: 20px; - font-weight: 800; - line-height: 1 -} - -.display-sm__6ec1a.fontScaling__6ec1a { - font-size: 1.25rem -} - -.display-md__6ec1a { - font-family: var(--font-headline); - font-size: 34px; - font-weight: 800; - line-height: 1.0588235294117647 -} - -.display-md__6ec1a.fontScaling__6ec1a { - font-size: 2.125rem -} - -.display-lg__6ec1a { - font-family: var(--font-headline); - font-size: 44px; - font-weight: 800; - line-height: .9545454545454546 -} - -.display-lg__6ec1a.fontScaling__6ec1a { - font-size: 2.75rem -} - -.code__6ec1a { - font-family: var(--font-code); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.code__6ec1a.fontScaling__6ec1a { - font-size: .75rem -} - -.topic__6ec1a { - flex: 1 1 auto; - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 18px; - margin-block:0;margin-inline:8px 0;min-width: 0; - overflow: hidden; - overflow-wrap: normal; - position: relative; - text-overflow: ellipsis; - white-space: nowrap; - color: var(--text-muted) -} - -.topic__6ec1a a { - color: var(--text-link) -} - -.topic__6ec1a a:hover { - text-decoration: underline -} - -.topicClickTarget__6ec1a { - bottom: 0; - pointer-events: none; - position: absolute; - top: 0; - inset-inline: 0 -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .topic__6ec1a { - font-weight:var(--font-weight-normal) - } -} - -.topic__6ec1a.expandable__6ec1a { - cursor: pointer -} - -.desktopTooltip__7b565 { - display: flex; - padding: 4px 0; - width: 100% -} - -.infoIcon__7b565 { - color: var(--white); - margin-inline-end:4px} - -.wrapper_ef0e9b { - width: 100% -} - -.percentCompleteWrapper_ef0e9b { - display: flex -} - -.percentCompleteLabel_ef0e9b { - flex: 1 1 auto; - text-align: end -} - -.progressBar_ef0e9b { - height: 4px; - position: relative -} - -.progressBar_ef0e9b:after { - content: ""; - opacity: .2; - width: 100% -} - -.progressBar_ef0e9b:after,.progressBarFill_ef0e9b { - background-color: currentColor; - border-radius: 4px; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.progressBarFill_ef0e9b:after { - box-shadow: 0 0 8px currentColor; - content: ""; - height: 100%; - inset-inline-start: 0; - opacity: .7; - position: absolute; - top: 0; - width: 100% -} - -.percentCompleteLabelOffset_ef0e9b,.progressBarFill_ef0e9b { - transition: width .3s ease-in-out -} - -.wrapper_f7a803 { - align-items: center; - background-color: var(--background-base-lower); - border-radius: 8px; - color: var(--text-default); - display: flex; - gap: 8px; - margin-bottom: 8px; - padding: 8px; - position: relative -} - -.copy_f7a803 { - margin-bottom: 0 -} - -.rewardTile_f7a803 { - border-radius: 4px; - flex: 0 0 auto; - height: 58px; - width: 58px -} - -.wrapper__19b5e { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: 8px; - box-shadow: var(--shadow-border),var(--shadow-high); - box-sizing: border-box; - display: flex; - gap: 8px; - inset-inline-start: 0; - margin-inline-start:16px;padding: 8px; - position: absolute; - top: 100%; - width: 385px -} - -.wrapperAccepted__19b5e { - width: 305px -} - -.rewardTileWrapper__19b5e { - flex: 0 0 auto; - height: 76px; - position: relative; - width: 76px -} - -.rewardTile__19b5e { - border-radius: 4px; - display: block -} - -.rewardTile__19b5e.rewardTile__19b5e { - height: 100%; - width: 100% -} - -.heading__19b5e .rewardTile__19b5e { - flex: 0 0 auto; - height: 38px; - width: 38px -} - -.promotedTag__19b5e { - box-sizing: border-box; - inset-inline-start: 4px; - position: absolute; - text-align: center; - width: calc(100% - 8px); - word-break: break-word; - z-index: 1 -} - -.content__19b5e { - flex: 1 1 auto -} - -.heading__19b5e { - align-items: center; - display: flex; - gap: 8px; - margin-bottom: 6px -} - -.headingWithSubmenu__19b5e { - display: flex; - gap: 4px; - margin-bottom: 2px -} - -.questTitle__19b5e { - flex: 1 1 auto; - margin-inline-end:24px} - -.gameTile__19b5e { - border-radius: 4px; - flex: 0 0 auto; - height: 38px; - width: 38px -} - -.ctas__19b5e { - display: flex; - gap: 8px -} - -.submenuWrapper__19b5e { - cursor: pointer; - inset-inline-end: 8px; - position: absolute; - top: 8px -} - -.submenuIcon__19b5e { - color: var(--interactive-text-default); - height: 24px; - width: 24px -} - -.submenuIcon__19b5e:focus,.submenuIcon__19b5e:hover { - color: var(--interactive-text-hover) -} - -.submenuIcon__19b5e:active { - color: var(--interactive-text-active) -} - -.clipsEnabledIndicator__8c88a { - display: flex; - margin: 0 8px -} - -.clipBadgeText__8c88a { -} - -.clipBadgeIcon__8c88a { - padding-inline-start:4px;width: var(--custom-clips-enabled-indicator-badge-icon-dimension-override) -} - -@media (max-width: 920px) { - .clipBadgeText__8c88a { - display:none - } - - .clipBadgeIcon__8c88a { - border-radius: var(--radius-sm); - height: var(--custom-clips-enabled-indicator-badge-icon-dimension-override); - padding: 2px - } -} - -.chatIcon__233f8 { - align-items: center; - display: flex; - justify-content: center; - position: relative -} - -.chatIcon__233f8 svg { - flex-shrink: 0 -} - -.chatIcon__233f8:hover .unreadCount__233f8 { - background: var(--white) -} - -.chatIcon__233f8:hover .unreadCount__233f8.mention__233f8 { - background: var(--red-360) -} - -.upArrow__233f8 { - transform: rotate(180deg) -} - -.downArrow__233f8 { - transform: rotate(0deg) -} - -.badge__233f8 { - margin-top: 14px -} - -.root_d529e9 { - background-color: var(--background-surface-high); - border-radius: 4px; - display: flex; - flex-direction: column; - max-height: 260px; - width: 240px -} - -.root_d529e9 .voiceUsers_d529e9 { - padding: 0 -} - -.header_d529e9 { - color: var(--interactive-text-default); - padding: var(--custom-channel-call-participants-popout-padding-value); - padding-bottom: 9px; - text-transform: uppercase -} - -.scroller_d529e9 { - padding: var(--custom-channel-call-participants-popout-padding-value); - padding-inline-start:8px;padding-top: 0 -} - -.button__9db96 { - margin-inline-start:0} - -.divider__9db96 { - background: var(--border-strong); - margin-inline-end:16px} - -.speakers__9db96 { - display: flex; - flex-direction: row -} - -.lastButton__9db96 { - margin-inline-end:8px} - -.avatar__49508 { - flex-shrink: 0 -} - -.divider__49508 { - background: var(--primary-700) -} - -.channelStatusClickable__49508 { - align-items: center; - color: var(--channel-icon); - display: flex; - margin-inline-start:28px;max-width: 50%; - width: -moz-fit-content; - width: fit-content -} - -.channelStatusClickable__49508 .channelStatus__49508 { - font-size: 12px; - line-height: 16px -} - -.hoverable__49508:hover { - color: var(--interactive-text-hover); - cursor: pointer -} - -.channelStatus__49508 { - color: var(--channel-icon); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.subtitleContainer__49508 { - display: flex; - flex-direction: column; - position: relative -} - -.pencilIcon__49508,.secureFramesIcon__49508 { - margin-inline-start:4px} - -.pencilIcon__49508 { - min-width: 16px -} - -.playingText__49508 { - align-items: center; - color: var(--primary-300); - display: flex; - text-overflow: ellipsis -} - -.eventStatusText__49508,.playingText__49508 { - overflow: hidden; - white-space: nowrap -} - -.eventStatusText__49508 { - font-weight: var(--font-weight-semibold); - margin-inline-end:16px} - -.container__49508 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-xs); - justify-content: center -} - -.activityIcon__49508 { - border-radius: var(--radius-xs); - height: 24px; - margin-inline-end:10px;width: 24px -} - -.headerBar__49508 { - justify-content: flex-start; - padding-inline-end:var(--space-md)} - -.headerBarChildren__49508 { - gap: var(--space-xs) -} - -.title__49508 { - margin: 0; - padding: 0 -} - -.toolbar__49508 { - gap: var(--space-md); - padding-inline-start:0} - -.iconWrapper__49508 { - height: 28px; - width: 28px -} - -.actionBarButton__18c2e { - border-radius: 8px; - height: 24px; - margin-inline-end:20px;padding: 0; - width: 24px -} - -.voiceEffectsActionBar__68198 { - display: flex -} - -.divider__68198 { - background-color: var(--primary-700); - border-radius: 0; - height: 24px; - margin-inline-end:20px;width: 1px -} - -.wrapper_cb9592 { - background-color: var(--background-base-lowest); - display: flex; - flex: 0 0 auto; - position: relative; - z-index: 2 -} - -.wrapper_cb9592.animated_cb9592 { - transition: height .35s ease -} - -.wrapper_cb9592.normal_cb9592 { - height: 50% -} - -.wrapper_cb9592.haven_cb9592 { - height: 100% -} - -.wrapper_cb9592.minimum_cb9592 { - background-color: var(--background-surface-high); - height: 240px -} - -.wrapper_cb9592.minimum_cb9592.video_cb9592 { - height: 284px -} - -.wrapper_cb9592.fullScreen_cb9592,.wrapper_cb9592.noChat_cb9592,.wrapper_cb9592.sidebarOpen_cb9592 { - height: 100% -} - -.wrapper_cb9592.fullScreen_cb9592 { - width: 100% -} - -.wrapper_cb9592.popout_cb9592 { - height: 100%; - width: 100% -} - -.wrapper_cb9592.poppedOut_cb9592 { - height: 154px -} - -.callContainer_cb9592 { - background: var(--black); - flex: 1 1 auto; - overflow: hidden -} - -.high-contrast-mode:not([data-popout-root]) .callContainer_cb9592,.theme-midnight:not([data-popout-root]) .callContainer_cb9592 { - border-top: 1px solid var(--app-frame-border) -} - -.high-contrast-mode:not([data-popout-root]) .wrapper_cb9592.sidebarOpen_cb9592 .callContainer_cb9592,.theme-midnight:not([data-popout-root]) .wrapper_cb9592.sidebarOpen_cb9592 .callContainer_cb9592 { - border-inline-end:1px solid var(--app-frame-border)} - -.centerControls_cb9592 { - display: flex; - flex: 1 1 auto; - justify-content: center; - pointer-events: all -} - -.leftTrayIcon_cb9592 { - margin-inline-end:16px} - -@media (max-width: 456px) { - .leftTrayIcon_cb9592 { - margin-inline-end:8px - } - - .rightTrayIcon_cb9592 { - margin-inline-start:8px} - - .settingsButton_cb9592,.viewersButton_cb9592 { - display: none - } -} - -.iconWrapper_cb9592 { - margin-inline-end:0;position: relative -} - -.volumeSlider_cb9592 { - margin-top: -16px -} - -.badge_cb9592 { - inset-inline-end: 0; - pointer-events: none; - position: absolute; - top: -8px -} - -.headerWrapper_cb9592 { - margin: 0 -16px -} - -.wrapper_cb9592.sidebarOpen_cb9592 .callContainer_cb9592,.wrapper_cb9592.sidebarOpen_cb9592 .callContent_cb9592 { - border-radius: 0 8px 8px 0 -} - -.wrapper_cb9592.sidebarOpen_cb9592 .callContent_cb9592 { - border-radius: 0 7px 7px 0 -} - -.wrapper_cb9592.minimum_cb9592 .callContainer_cb9592 { - background: none -} - -.voiceChannelEffectsLayerContainer_cb9592 { - overflow-x: clip; - position: relative -} - -.channelChatWrapper_cb9592 { - background-color: var(--background-base-lowest); - display: flex -} - -.chatToasts_cb9592 { - height: 50%; - inset-inline-end: 12px; - overflow: hidden; - position: absolute; - top: 48px; - width: 304px -} - -.enable-forced-colors .resizeHandle_cb9592 { - background-color: ButtonText -} - -.channelChatWrapper_cb9592,.wrapper_cb9592 { - background: var(--background-gradient-app-frame,var(--background-base-lowest)) -} - -.emptyWrapper_f12222 { - align-items: center; - display: flex; - flex-direction: column; - margin: auto; - max-width: 340px -} - -.emptyImage_f12222 { - filter: saturate(var(--saturation-factor,1)); - width: 200px -} - -.emptyTitle_f12222 { - margin: 16px 0 8px; - text-align: center -} - -.emptySubtitle_f12222 { - margin-bottom: 16px; - text-align: center -} - -.emptyCTA_f12222 { - padding: 12px 16px -} - -.card__0c0bf { - border: 1px solid var(--border-subtle); - border-radius: 8px; - display: flex; - flex: 1; - flex-direction: column; - height: var(--custom-guild-directory-entry-card-card-height); - overflow: hidden; - position: relative -} - -.addEntryCard__0c0bf,.card__0c0bf { - background-color: var(--background-base-low) -} - -.addEntryCard__0c0bf { - align-items: center; - border: 2px dashed var(--text-muted); - border-radius: 4px; - box-sizing: border-box; - cursor: pointer; - justify-content: center -} - -.addEntryCard__0c0bf:hover { - background-color: var(--interactive-background-hover) -} - -.addEntryCard__0c0bf:active { - background-color: var(--interactive-background-active) -} - -.addServerText__0c0bf { - margin-top: 8px -} - -.cardHeader__0c0bf { - display: block; - height: 40px; - margin-bottom: 36px; - overflow: visible; - position: relative -} - -.splash__0c0bf { - background-color: var(--background-mod-normal); - display: block; - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.splash__0c0bf,.splashImage__0c0bf { - height: 100%; - width: 100% -} - -.splashImage__0c0bf { - -o-object-fit: cover; - object-fit: cover -} - -.guildIcon__0c0bf { - inset-inline-start: 12px; - position: absolute; - top: 20px -} - -.icon__0c0bf { - background-color: var(--background-base-low) -} - -.actionButtonsContainer__0c0bf { - align-items: center; - display: none; - inset-inline-end: 16px; - justify-content: center; - position: absolute; - top: 24px -} - -.forceButtonsShow__0c0bf { - display: flex -} - -.card__0c0bf:focus-within .actionButtonsContainer__0c0bf,.card__0c0bf:hover .actionButtonsContainer__0c0bf { - display: flex -} - -.overflowIcon__0c0bf { - display: block; - height: 20px; - -o-object-fit: contain; - object-fit: contain; - width: 20px -} - -.iconMask__0c0bf { - background-color: var(--background-base-lower); - padding: 4px -} - -.avatar__0c0bf { - height: 100%; - width: 100% -} - -.guildInfo__0c0bf { - align-content: stretch; - display: flex; - flex: 1 1 auto; - flex-direction: column; - overflow: hidden; - padding: 0 16px 16px; - position: relative -} - -.title__0c0bf { - align-items: center; - display: flex; - width: 100% -} - -.guildBadge__0c0bf { - flex: 0 0 16px; - height: 16px; - margin-inline:-2px 4px;width: 16px -} - -.guildName__0c0bf { - white-space: nowrap -} - -.description__0c0bf,.guildName__0c0bf { - overflow: hidden; - text-overflow: ellipsis -} - -.description__0c0bf { - display: -webkit-box; - flex: 1 1 auto; - margin: 4px 0 16px; - -webkit-line-clamp: 4; - -webkit-box-orient: vertical -} - -.memberInfo__0c0bf { - flex: 0 0 auto -} - -.memberCount__0c0bf,.memberInfo__0c0bf { - align-items: center; - display: flex -} - -.memberCount__0c0bf { - font-size: 12px; - line-height: 16px; - margin-inline-end:16px} - -.memberCount__0c0bf:last-child { - margin-inline-end:0} - -.statusDot__0c0bf { - border-radius: 50%; - flex-shrink: 0; - height: 8px; - margin-inline-end:4px;width: 8px -} - -.dotOnline__0c0bf { - background-color: var(--green-360) -} - -.dotOffline__0c0bf { - background-color: var(--primary-300) -} - -.joinButton__0c0bf { - margin-top: 16px -} - -.pageContainer__09fde { - background: var(--background-gradient-chat,var(--background-base-lower)); - display: flex; - flex: 1 1 auto; - flex-direction: column; - margin: 0 auto; - max-width: var(--custom-guild-directory-max-page-width); - min-width: minPageWidth; - overflow: hidden; - width: 100% -} - -.scroller__09fde { - padding: 32px -} - -.cardsContainer__09fde { - display: grid; - min-width: var(--custom-guild-directory-min-content-width); - padding: 24px 0; - grid-gap: var(--custom-guild-directory-gutter-size); - grid-template-columns: repeat(auto-fill,minmax(var(--custom-guild-directory-min-card-width),1fr)) -} - -.spinner__09fde { - margin-top: 56px -} - -.header_a7218d { - overflow: hidden; - position: relative; - text-align: center -} - -.header_a7218d,.headerImage_a7218d { - border-radius: 8px; - min-height: var(--custom-guild-directory-landing-min-header-height); - min-width: var(--custom-guild-directory-min-content-width) -} - -.headerImage_a7218d { - display: inherit; - filter: saturate(var(--saturation-factor,1)); - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.headerImageSimple_a7218d { - margin-bottom: 24px; - max-height: var(--custom-guild-directory-landing-min-header-height) -} - -.headerImageBG_a7218d,.headerImageSimple_a7218d { - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.headerImageBG_a7218d { - background: linear-gradient(180deg,hsl(var(--primary-800-hsl)/.2) 0,hsl(var(--primary-800-hsl)/1) 100%); - height: 100%; - width: 100% -} - -.searchSubtitle_a7218d,.searchTitle_a7218d { - color: var(--white) -} - -.searchSubtitle_a7218d { - margin: 8px 0 -} - -.headerContentWrapper_a7218d { - align-items: center; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - display: flex; - justify-content: center -} - -.headerContent_a7218d { - margin: 0 60px; - max-width: 700px; - width: 100% -} - -.headerContentSmall_a7218d { - margin: 0 36px -} - -.tabBar_a7218d { - border-bottom: 2px solid var(--border-subtle); - margin-bottom: -2px; - margin-top: 20px -} - -.tabBarItem_a7218d { - margin-inline-end:32px;margin-bottom: -2px; - padding-bottom: 16px -} - -.sectionHeader_a7218d { - margin-top: 20px -} - -.searchContainer_a7218d { - background-color: var(--background-base-low); - border-radius: var(--radius-sm) -} - -.searchHeader__83bd4 { - align-items: flex-start; - display: flex; - flex-direction: column; - gap: var(--space-8) -} - -.searchHeader__83bd4 .searchPageBox__83bd4 { - border-radius: 8px; - box-sizing: border-box; - margin-top: 16px; - width: 100% -} - -.searchPageBox__83bd4 .searchPageInput__83bd4 { - padding: 8px -} - -.headerTitleWrapper__83bd4 { - align-items: center; - display: flex; - flex-direction: row -} - -.arrow__83bd4 { - color: var(--interactive-text-default); - cursor: pointer -} - -.arrow__83bd4:hover { - color: var(--interactive-text-hover) -} - -.arrow__83bd4:focus { - color: var(--interactive-text-active) -} - -.searchPageTitle__83bd4 { - color: var(--text-muted); - margin-inline-start:12px} - -.searchPageTitle__83bd4 strong { - color: var(--text-strong) -} - -.emptySearchWrapper__83bd4 { - align-items: center; - background-color: var(--background-base-lower); - border-radius: 8px; - display: flex; - flex: 1; - flex-direction: column; - justify-content: center; - margin: 24px 0; - padding: 92px 0 -} - -.emptySearchImage__83bd4 { - width: 120px -} - -.emptySearchTitle__83bd4 { - margin-top: 28px -} - -.emptySearchSubtitle__83bd4 { - margin-top: 8px -} - -.chat_f02135 { - align-items: stretch; - display: flex; - flex: 1 1 auto; - flex-direction: column; - justify-content: stretch; - min-height: 0; - min-width: 0; - position: relative -} - -.chat_f02135:before { - box-shadow: var(--elevation-low); - content: ""; - display: none; - height: 1px; - position: absolute; - top: -1px; - inset-inline: 0; - pointer-events: none; - z-index: 1 -} - -.upsellPage_d204dc { - align-items: center; - display: flex; - flex: 1; - justify-content: center -} - -.upsellContainer_d204dc { - background-color: var(--background-base-lower); - display: flex; - flex-direction: row; - gap: 24px; - padding: 24px -} - -.upsellImage_d204dc { - height: 163px; - width: 201px -} - -.upsellContent_d204dc { - display: flex; - flex-direction: column; - width: 357px -} - -.bodyText_d204dc { - margin-bottom: auto; - margin-top: 8px -} - -@keyframes horizontalBounce__3e795 { - 0% { - transform: translateZ(0) - } - - 50% { - transform: translate3d(-8px,0,0) - } - - to { - transform: translateZ(0) - } -} - -.container__3e795 { - display: flex; - flex-direction: row; - inset-inline-end: 0; - margin-block:0 24px;margin-inline:auto 0;pointer-events: none; - position: absolute; - top: 40px; - z-index: 1 -} - -.full-motion .container__3e795 { - animation: horizontalBounce__3e795 3s ease-in-out .5s 4 -} - -.containerHide__3e795 { - opacity: 0; - transition: opacity .8s ease-out -} - -.notice__3e795 { - border-radius: 4px; - box-shadow: var(--elevation-medium); - padding: 8px 16px; - position: relative -} - -.notice__3e795,.noticeArrow__3e795 { - background-color: var(--orange-360) -} - -.noticeArrow__3e795 { - align-self: center; - border-radius: 1px; - height: 8px; - transform: translateX(-4px) rotate(45deg); - transform-origin: center; - width: 8px; - z-index: 1 -} - -.carouselMaxWidth_b8429c { - align-self: stretch; - position: relative -} - -.scroller_b8429c { - inset-inline: 0; - position: absolute -} - -.tierPreviewsContainer_b8429c { - display: flex; - flex-direction: row; - min-width: 0 -} - -.tierPreviews_b8429c { - align-items: flex-start; - display: flex; - flex-grow: 1; - gap: 16px; - justify-content: center; - margin-bottom: 16px -} - -@media (max-width: 485px) { - .tierPreviews_b8429c { - align-items:stretch; - flex-direction: column - } - - .tierPreviewsContainer_b8429c { - padding-inline:16px} -} - -.pageContainer__8893c { - align-items: center; - display: flex; - flex: 1 1 auto; - flex-direction: column; - padding-top: 32px; - position: relative; - width: 100%; - z-index: 1 -} - -.placeholdersContainer__8893c { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - opacity: .3; - overflow: hidden; - z-index: 0 -} - -.guildIconContainer__8893c { - margin-bottom: 16px; - margin-top: 40px -} - -.joinCtaTitle__8893c { - color: var(--text-default); - font-family: var(--font-display); - max-width: 640px; - text-align: center -} - -.joinCtaTitle__8893c strong { - color: var(--text-strong) -} - -.joinCtaSubtitle__8893c { - margin-bottom: 32px; - margin-top: 8px; - max-width: 640px -} - -.tierPreviewsContainer__8893c { - overflow-x: scroll; - width: 100% -} - -.tierPreviews__8893c { - display: flex; - flex-direction: row; - gap: 16px -} - -.searchHeader_ae7890 { - align-items: center; - background: none; - border-bottom: 1px solid var(--border-subtle); - box-sizing: border-box; - display: flex; - flex-grow: 0; - flex-shrink: 0; - height: auto; - padding: var(--space-8) var(--space-16); - position: relative; - z-index: 2 -} - -.searchHeaderWithSubtitle_ae7890 { - min-height: 58px -} - -.searchModeAndFiltersContainer_ae7890 { - align-items: center; - display: flex; - gap: 8px -} - -.searchModeSelect_ae7890 { - background-color: var(--background-mod-muted); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-low); - min-height: 28px; - padding: 5px 10px -} - -.searchHeaderTabList_ae7890 { - align-items: center; - display: grid; - gap: 8px; - grid-template-columns: repeat(3,min-content) -} - -.searchHeaderTabList_ae7890 .searchHeaderTab_ae7890 { - margin-bottom: 0 -} - -.helpdeskLink_ae7890 { - color: var(--interactive-text-default) -} - -.helpdeskLink_ae7890:hover { - color: var(--interactive-text-hover); - text-decoration: underline -} - -.totalResultsWrapper_ae7890 { - display: flex -} - -.totalResults_ae7890 { - display: flex; - flex-grow: 1; - font-weight: var(--font-weight-semibold); - padding-inline-end:1px} - -.totalResultsWithSubtitle_ae7890 { - display: flex; - flex-direction: column; - padding: 2px 0 -} - -.spinnerWrapper_ae7890 { - align-items: center; - display: flex; - flex: 0; - height: 16px; - justify-content: center; - margin-inline-start:8px;stroke: var(--text-default); - width: 16px -} - -.spinner_ae7890 { - height: 100%; - width: 100% -} - -.spinnerPath_ae7890 { - stroke: var(--text-default) -} - -.container_bd5bb4 { - align-items: center; - background-color: var(--background-base-lowest); - border-top: 1px solid var(--border-subtle); - display: flex; - gap: 8px; - padding: 12px 16px -} - -.icons_bd5bb4 { - display: flex; - flex: 1; - gap: 16px; - justify-content: flex-end -} - -.iconContainer_bd5bb4 { - cursor: pointer -} - -.icon_bd5bb4 { - color: var(--interactive-text-default) -} - -.icon_bd5bb4:hover { - color: var(--interactive-text-hover) -} - -.searchResultsWrap_a98f3b { - background: var(--background-gradient-lowest,var(--background-base-lowest)); - border-inline-start:1px solid var(--app-frame-border);display: flex; - flex: 0 0 auto; - flex-direction: column; - overflow: hidden; - position: relative; - width: 418px -} - -.scroller_a98f3b { - padding: 16px 16px 0 -} - -.emptyResultsWrap_a98f3b { - align-items: center; - box-sizing: border-box; - color: var(--text-default); - display: flex; - flex: 1; - font-size: 16px; - font-weight: var(--font-weight-medium); - height: 100%; - justify-content: center; - line-height: 24px; - padding: 20px; - text-align: center -} - -.emptyResultsContent_a98f3b { - margin: auto; - padding-bottom: 40px; - position: relative -} - -.emptyResultsText_a98f3b { - font-size: 16px; - font-weight: var(--font-weight-medium); - margin: 40px auto 0 -} - -.stillIndexing_a98f3b { - width: 300px -} - -.noResultsImage_a98f3b { - filter: saturate(var(--saturation-factor,1)) -} - -.errorImage_a98f3b,.noResultsImage_a98f3b { - height: 160px; - margin: 0 auto; - width: 160px -} - -.errorMessage_a98f3b { - width: 300px -} - -.noResults_a98f3b { - width: 280px -} - -.noResults_a98f3b.alt_a98f3b { - width: 200px -} - -.helpMessageContainer_a98f3b { - margin-bottom: 16px -} - -.theme-dark .errorImage_a98f3b { - background-image: url(/assets/c451b132a4f69f5b.svg) -} - -.theme-light .errorImage_a98f3b { - background: url(/assets/5153f8efac26047f.svg) -} - -.images-light .noResultsImage_a98f3b { - background-image: url(/assets/1fa1021e7db304e1.svg) -} - -.images-light .noResultsImage_a98f3b.alt_a98f3b { - background-image: url(/assets/e83b7cd64938e9cb.svg) -} - -.images-dark .noResultsImage_a98f3b { - background-image: url(/assets/0dfcd735f1a5349c.svg) -} - -.images-dark .noResultsImage_a98f3b.alt_a98f3b { - background-image: url(/assets/140c2a4c9ce5623d.svg) -} - -.paginationLimitHeader_a98f3b { - margin-bottom: 16px -} - -.container__05cdc { - display: flex -} - -.sparkleIcon__05cdc { - height: 12px; - width: 12px -} - -.sparkleTop__05cdc { - align-self: flex-start; - margin-inline-end:auto} - -.sparkleBottom__05cdc { - align-self: flex-end; - margin-inline-start:auto} - -.background__506d9 { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: 50%; - display: flex; - justify-content: center; - margin-inline:4px;padding: 12px -} - -.foreground__506d9 { - color: var(--interactive-text-default) -} - -.container__664ff { - background-color: var(--background-base-low); - border-end-start-radius: 8px; - border-start-start-radius: 8px; - display: flex; - flex-direction: column; - flex-shrink: 0; - width: 320px -} - -.container__664ff.chatOpen__664ff { - border-end-end-radius: 8px; - border-start-end-radius: 8px -} - -.headerContainer__664ff { - border-start-start-radius: 8px; - box-shadow: var(--elevation-low) -} - -.headerContainer__664ff.chatOpen__664ff { - border-start-end-radius: 8px -} - -.headerTitle__664ff { - margin-inline-start:12px} - -.headerClose__664ff { - color: var(--interactive-text-default); - cursor: pointer; - margin-inline-start:auto} - -.headerClose__664ff:hover { - color: var(--interactive-text-hover) -} - -.headerClose__664ff:active { - color: var(--interactive-text-active) -} - -.contentContainer__664ff { - padding-inline:16px} - -.selfSpeakerContainer__664ff { - align-items: center; - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - flex-direction: row; - padding-top: 16px -} - -.selfSpeakerContainer__664ff:hover { - color: var(--interactive-text-hover) -} - -.selfSpeakerContainer__664ff:active { - color: var(--interactive-text-active) -} - -.buttonContainer__664ff { - display: flex; - gap: 8px -} - -.selfSpeakerButton__664ff { - background-color: var(--background-base-lowest) -} - -.selfSpeakerText__664ff { - margin-inline-start:12px} - -.listTitle__664ff { - padding-top: 24px; - text-transform: uppercase -} - -.participantRowContainer__664ff { - align-items: center; - display: flex; - flex-direction: row; - height: 40px; - padding-top: 16px -} - -.participantMemberContainer__664ff { - align-items: center; - cursor: pointer; - display: flex; - flex-direction: row; - flex-shrink: 1; - margin-inline-end:auto;overflow: hidden -} - -.participantTextContainer__664ff { - display: flex; - flex-direction: column; - flex-shrink: 1; - margin-inline:16px 8px;min-width: 1px -} - -.participantAvatar__664ff { - flex-shrink: 0 -} - -.participantName__664ff { - color: var(--text-strong); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.emptyStateContainer__664ff { - align-items: center; - display: flex; - flex-direction: column; - padding-bottom: 24px; - padding-top: 32px; - text-align: center -} - -.emptyStateTitle__664ff { - margin-top: 16px -} - -.emptyStateBody__664ff { - margin-top: 8px -} - -.toggle__664ff { - margin-bottom: 0; - padding-top: 20px -} - -.enable-forced-colors .selfSpeakerContainer__664ff { - background-color: ButtonFace; - color: ButtonText; - text-decoration: underline -} - -.enable-forced-colors .selfSpeakerContainer__664ff:hover { - color: ButtonText -} - -.enable-forced-colors .selfSpeakerContainer__664ff:active { - background-color: HighlightText; - color: Highlight -} - -.enable-forced-colors .buttonContainer__664ff { - background-color: transparent -} - -.enable-forced-colors .participantName__664ff { - background-color: ButtonFace; - color: ButtonText; - text-decoration: underline -} - -.raisedHandIcon__15cd2 { - color: var(--interactive-text-default); - margin-inline-start:-4px;min-width: 32px; - position: relative; - width: auto -} - -.raisedHandIcon__15cd2 svg { - flex-shrink: 0 -} - -.raisedHandCount__15cd2,.raisedHandIcon__15cd2:hover { - color: var(--white) -} - -.raisedHandCount__15cd2 { - background: var(--background-feedback-notification); - border-radius: 999px; - bottom: -3px; - box-sizing: border-box; - flex-shrink: 0; - height: 16px; - inset-inline-start: 50%; - min-width: 16px; - padding: 0 4px; - position: absolute; - width: auto -} - -.participants__4783a { - color: var(--interactive-text-default); - padding-inline:0} - -.icon__4783a { - color: var(--text-muted); - margin-inline-start:0} - -.buttonIcon__4783a { - cursor: pointer -} - -.channelName__4783a { - color: var(--text-strong) -} - -.channelNameWrapper__4783a { - margin-inline-end:0} - -.divider__4783a { - background: var(--primary-560); - margin-inline:16px} - -.speakerCount__4783a { - margin-inline:4px 8px} - -.button__4783a { - margin-inline:12px 4px} - -.sidebarOpen__4783a { - margin-inline-end:-4px} - -.boostUpsell__4783a { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: 5px; - display: flex; - height: 52px; - padding: 8px 16px -} - -.boostUpsell__4783a .text__4783a { - display: flex; - flex-direction: column; - flex-grow: 0; - margin-inline-start:10px} - -.boostUpsell__4783a .buttons__4783a { - align-items: center; - display: flex; - flex-shrink: 0; - gap: 16px; - margin-inline-start:auto} - -.tileBaseContainer__71eb2 { - border-radius: 8px; - display: inline-block; - height: 98px; - margin: auto; - width: 102px -} - -.tileContainer__71eb2 { - background-color: var(--black); - cursor: pointer; - text-align: center -} - -.tileContainer__71eb2:hover { - background-color: var(--background-base-lowest) -} - -.textContainer__71eb2 { - align-items: center; - display: flex; - justify-content: center; - padding: 0 4px -} - -.avatarContainer__71eb2 { - height: 56px; - margin: 8px auto; - position: relative; - width: 56px -} - -.avatar__71eb2 { - background-color: var(--black); - border-radius: 28px; - height: 56px; - width: 56px -} - -.avatar__71eb2.faded__71eb2 { - opacity: .5 -} - -.text__71eb2 { - display: inline-block; - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.singleIcon__71eb2 .text__71eb2 { - max-width: calc(100% - 16px) -} - -.doubleIcon__71eb2 .text__71eb2 { - max-width: calc(100% - 32px) -} - -.boost__71eb2 { - display: inline-block; - height: 16px; - margin-inline-start:4px;vertical-align: text-top; - width: 10px -} - -.blockedIcon__71eb2 { - margin-inline-end:2px} - -.blockedIcon__71eb2,.icon__71eb2 { - height: 12px; - width: 12px -} - -.icon__71eb2 { - background-clip: padding-box; - background-color: var(--text-strong); - border: 3px solid var(--black); - border-radius: 50%; - color: var(--black); - inset-inline-end: -3px; - padding: 2px; - position: absolute; - top: -3px -} - -.icon__71eb2.invited__71eb2 { - background-color: var(--green-360); - color: var(--text-strong) -} - -.enable-forced-colors .text__71eb2 { - background-color: ButtonFace; - color: ButtonText; - forced-color-adjust: none; - text-decoration: underline -} - -.participants__9c309 { - background-color: var(--black); - display: flex; - height: 98px -} - -.scrollerBase_c8b5c7 { - box-sizing: border-box; - flex: 1 1 auto; - min-height: 0; - position: relative -} - -.auto_c8b5c7 { -} - -.no-webkit-scrollbar .auto_c8b5c7 { - scrollbar-color: var(--black) var(--border-subtle); - scrollbar-width: auto -} - -.no-webkit-scrollbar .auto_c8b5c7.fade_c8b5c7:hover { - scrollbar-color: var(--black) var(--border-subtle) -} - -.auto_c8b5c7::-webkit-scrollbar { - height: 16px; - width: 16px -} - -.auto_c8b5c7::-webkit-scrollbar-track { - background-color: var(--black) -} - -.auto_c8b5c7::-webkit-scrollbar-thumb,.auto_c8b5c7::-webkit-scrollbar-track { - background-clip: padding-box; - border: 4px solid var(--black); - border-radius: 8px -} - -.auto_c8b5c7::-webkit-scrollbar-thumb { - background-color: var(--background-mod-subtle); - min-height: 40px -} - -.auto_c8b5c7::-webkit-scrollbar-corner { - background-color: var(--black) -} - -.no-webkit-scrollbar .fade_c8b5c7 { - scrollbar-color: var(--black) var(--border-subtle) -} - -.fade_c8b5c7::-webkit-scrollbar-thumb,.fade_c8b5c7::-webkit-scrollbar-track { - visibility: hidden -} - -.fade_c8b5c7:hover::-webkit-scrollbar-thumb,.fade_c8b5c7:hover::-webkit-scrollbar-track { - visibility: visible -} - -.spaceBetweenTiles_fa73d7 { - box-sizing: border-box; - padding-inline:4px} - -.tileSizer_fa73d7 { - border-radius: 8px; - overflow: hidden; - position: relative; - width: 100% -} - -.container_fdb9cf { - inset-inline-end: 8px; - position: absolute; - top: 8px -} - -.tile_fdc206 { - cursor: pointer; - height: 100% -} - -.rowContainer__8a920 { - align-items: center; - background-color: var(--black); - display: flex; - flex-direction: row; - justify-content: center; - margin-bottom: 8px; - width: 100% -} - -.summary__3af26 { - margin-inline-end:8px} - -.speakers__3af26 { - display: flex; - flex: 1; - justify-content: flex-end -} - -.avatar__3af26 { - cursor: pointer -} - -.container__9aed4 { - align-items: center; - border-bottom: 1px solid var(--border-subtle); - cursor: pointer; - display: flex; - flex-direction: row; - height: 48px -} - -.micIcon__9aed4 { - margin-inline-end:16px} - -.downIcon__9aed4,.micIcon__9aed4 { - color: var(--text-strong) -} - -.downIcon__9aed4 { - margin-inline-start:auto} - -.upIcon__9aed4 { - transform: rotate(180deg) -} - -.text__9aed4 { - margin-inline-end:12px} - -.scroller__56ccd { - background-color: var(--black); - padding-inline:16px} - -.grid__56ccd,.scroller__56ccd { - height: 100%; - width: 100% -} - -.grid__56ccd { - display: flex; - flex-direction: column; - margin-top: 88px -} - -.spacer__56ccd { - padding-bottom: 88px -} - -.divider__56ccd { - border-bottom: 1px solid var(--background-base-low); - margin-top: 23px; - width: 100% -} - -.focusedRow__56ccd { - display: flex; - justify-content: center; - margin-top: 48px; - width: 100% -} - -.focusedRow__56ccd,.header__56ccd { - margin-bottom: 12px -} - -.containerColumn_fcb27c { - flex-direction: column -} - -.container_fcb27c,.containerColumn_fcb27c { - align-items: center; - display: flex -} - -.container_fcb27c { - flex-direction: row; - gap: var(--space-sm) -} - -.requestToSpeakIcon_fcb27c { - grid-area: icon; - margin: auto -} - -.slash_fcb27c { - color: var(--icon-feedback-critical) -} - -.primaryButtonColor_fcb27c { - background: var(--primary-630); - color: #fff -} - -.primaryButtonColor_fcb27c.active_fcb27c,.primaryButtonColor_fcb27c:hover { - background: var(--primary-700) -} - -.raisedHandButtonIcon_fcb27c { - color: #fff -} - -.raisedHandButtonIconActive_fcb27c { - color: var(--primary-600) -} - -.speakerRaiseHandButtonContainer_fcb27c { - position: relative -} - -.speakerRaiseHandButton_fcb27c { - position: static -} - -.speakerRaiseHandBadge_fcb27c { - background-color: var(--background-feedback-notification); - border-radius: 8px; - box-sizing: border-box; - font-weight: var(--font-weight-semibold); - inset-inline-end: 0; - min-width: 16px; - padding: 0 4px; - position: absolute; - text-align: center; - top: 0 -} - -.eventPrompts_fcb27c { - bottom: 40px; - display: flex; - flex-direction: column; - margin-bottom: 40px; - position: absolute -} - -.separator_fcb27c { - height: 8px -} - -.avControls_fcb27c { - gap: 8px -} - -.avControls_fcb27c,.wrapper_fcb27c { - display: flex; - flex-direction: row -} - -.wrapper_fcb27c { - align-items: center; - gap: 12px -} - -.buttonSection_fcb27c { - align-items: center; - background: var(--plum-23); - border: 1px solid hsl(var(--plum-11-hsl)/.06); - border-radius: 12px; - display: flex; - gap: 8px; - justify-content: center; - padding: 4px -} - -.enable-forced-colors .speakerRaiseHandBadge_fcb27c { - background-color: HighlightText; - color: Highlight -} - -.iconBackground__0b34e { - align-items: center; - background-color: var(--background-base-low); - border-radius: 50%; - display: flex; - height: 64px; - justify-content: center; - width: 64px -} - -.icon__0b34e { - color: var(--interactive-text-active) -} - -.headerContainer__0b34e { - margin-bottom: 24px; - margin-top: 24px; - text-align: center -} - -.eventPrompt__0b34e,.subtitle__0b34e { - margin-top: 8px -} - -.separator__0b34e { - background-color: var(--background-base-low); - border: none; - height: 1px; - margin-bottom: 24px; - margin-top: 24px -} - -.continueIcon__0b34e { - color: var(--interactive-text-active) -} - -.continueIconContainer__0b34e { - background-color: inherit -} - -.audienceSummary__0b34e { - display: flex; - flex-direction: row; - justify-content: center; - margin-top: 24px; - max-width: 400px -} - -.summaryItem__0b34e { - margin-inline-end:8px} - -.container__0b34e { - align-items: center; - border-radius: 0; - display: flex; - flex-direction: column; - height: 100%; - justify-content: center; - margin: 0; - overflow: hidden; - width: 100% -} - -.container__0b34e,.content__0b34e { - position: relative -} - -.container__722ff { - background-color: var(--background-base-lowest); - display: flex; - flex: 0 0 auto; - flex-direction: row; - height: 100%; - width: 100% -} - -.callContainer__722ff { - background-color: var(--black); - flex: 1 1 auto; - overflow: hidden -} - -.sidebarVisible__722ff { - margin-inline-end:8px;overflow: hidden -} - -.sidebarOrChatVisible__722ff { - border-end-end-radius: 8px; - border-start-end-radius: 8px -} - -.volumeSlider__722ff { - margin-top: -16px -} - -.chatToasts__722ff { - height: 50%; - inset-inline-end: 16px; - overflow: hidden; - position: absolute; - top: 48px; - width: 304px -} - -.chatToasts__722ff.rtsSidebarOpen__722ff { - inset-inline-end: 336px -} - -.channelChatWrapper__722ff { - display: flex -} - -.notConnectedView__722ff { - align-items: center; - display: flex; - flex-direction: column; - width: 100% -} - -.notConnectedChannelName__722ff { - font-weight: var(--font-weight-bold); - margin: 8px 0; - max-width: 75%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.notConnectedJoinButton__722ff { - border-radius: 999px; - margin-top: 24px -} - -.customInviteButtonColors__722ff { - background-color: var(--primary-630); - color: #fff -} - -.customInviteButtonColors__722ff:hover { - background-color: var(--primary-700) -} - -.container_fb64c9 { - background: var(--background-gradient-lower,var(--background-base-lower)) -} - -.chat_fb64c9,.container_fb64c9 { - display: flex; - flex: 1 1 auto; - flex-direction: column; - min-height: 0 -} - -.chat_fb64c9 { - align-items: stretch; - justify-content: stretch; - min-width: 0; - position: relative -} - -.chat_fb64c9:before { - box-shadow: var(--elevation-low); - content: ""; - display: block; - height: 1px; - position: absolute; - top: -1px; - inset-inline: 0; - pointer-events: none; - z-index: 1 -} - -.messagesWrapper_fb64c9 { - display: flex; - flex: 1 1 auto; - min-height: 0; - min-width: 0; - position: relative; - z-index: 0 -} - -.scroller_fb64c9 { - align-items: stretch; - display: flex; - flex-direction: column; - justify-content: flex-end -} - -.scrollerInner_fb64c9 { - min-height: 0 -} - -.formSection_fb64c9 { - margin-top: 16px -} - -.checkbox_fb64c9 { - color: var(--interactive-text-default) -} - -.checkbox_fb64c9:hover { - color: var(--interactive-text-hover) -} - -.checkbox_fb64c9:active { - color: var(--interactive-text-active) -} - -.privateThreadDescription_fb64c9 { - margin-top: 8px -} - -.chat_fb64c9 .select_fb64c9 { - background-color: var(--background-base-lowest); - border: none; - display: inline-grid; - padding-block:0;padding-inline:4px 2px} - -.chat_fb64c9 .select_fb64c9>svg { - background-color: var(--background-base-low); - border-radius: 4px; - height: 18px; - margin-inline-start:8px;width: 18px -} - -.form_fb64c9 { - display: flex; - flex-direction: column; - min-height: 0; - position: relative; - width: 100% -} - -.channelTextArea_fb64c9 { - margin: 0 var(--space-8) var(--space-8); - width: calc(100% - 32px) -} - -.channelTextAreaInner_fb64c9>div>div { - min-height: auto!important -} - -.channelTextAreaWithTypingIndicator_fb64c9 { - margin-block:0 var(--space-xs);margin-inline: var(--space-8) 0 -} - -.typingIndicator_fb64c9 { - inset-inline-end: var(--space-24) -} - -.channelTextAreaInnerFocused_fb64c9 { - border-color: var(--text-link) -} - -.channelTextAreaInnerError_fb64c9 { - border: 1px solid var(--red-400) -} - -.starterMessageError_fb64c9 { - margin-inline-start:16px} - -.privateThreadFormTitle_fb64c9 { - display: flex -} - -.privateThreadFormTitleText_fb64c9 { - margin-inline-end:8px} - -.privateThreadFeaturePill_fb64c9 { - background-color: var(--background-mod-normal); - border-radius: 16px; - padding: 1px 2px -} - -.submitContainer_fb64c9 { - flex-shrink: 0 -} - -.messagePreview_fb64c9 { - padding-bottom: 24px!important -} - -.threadNameInputWithAI_fb64c9 { - padding-inline-end:48px} - -.iconWrapper_fb64c9 { - margin-bottom: var(--space-16) -} - -.container__1b24f { - align-items: center; - box-sizing: border-box; - display: flex; - flex: 1; - flex-direction: column; - justify-content: center; - min-height: 400px; - padding: 32px; - text-align: center -} - -.container__1b24f.compact__1b24f { - max-height: 140px; - min-height: 140px; - padding: 0 -} - -.iconContainer__1b24f { - margin-bottom: 16px; - position: relative -} - -.icon__1b24f { - background-color: var(--background-base-low); - border-radius: 80px; - color: var(--interactive-text-default); - display: inline-block; - padding: 22px -} - -.compact__1b24f .icon__1b24f { - padding: 10px -} - -.stars__1b24f { - inset-inline-start: -10px; - position: absolute -} - -.header__1b24f { - margin-bottom: 8px -} - -.cta__1b24f { - margin-top: 24px -} - -.container__6764b { - background-color: var(--background-surface-higher); - border: 1px solid var(--background-base-low); - border-color: var(--border-subtle); - border-radius: 8px; - box-sizing: border-box; - cursor: pointer; - display: flex; - height: 72px; - margin-bottom: 8px; - padding: 15px; - position: relative -} - -.container__6764b:hover { - background-color: var(--background-surface-highest); - border-color: var(--border-normal) -} - -.left__6764b { - display: flex; - flex-direction: column; - flex-grow: 1; - min-width: 0 -} - -.authorName__6764b,.messageContent__6764b,.parentName__6764b,.startedByName__6764b,.subtext__6764b,.threadName__6764b { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.threadNameLine__6764b { - align-items: center; - display: flex -} - -.parentName__6764b { - color: var(--text-muted); - flex-shrink: 0; - font-size: 14px; - font-weight: var(--font-weight-medium); - margin-inline-start:8px} - -.threadName__6764b { - color: var(--text-default) -} - -.facepile__6764b { - flex-shrink: 0; - margin-inline-start:16px} - -.subtext__6764b { - align-items: center; - display: flex -} - -.avatar__6764b,.noAvatarIcon__6764b { - flex-shrink: 0; - margin-inline-end:8px} - -.noAvatarIcon__6764b { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: 16px; - display: flex; - height: 16px; - justify-content: center; - width: 16px -} - -.authorName__6764b,.startedByName__6764b { - flex-shrink: 0; - font-weight: var(--font-weight-semibold); - max-width: 150px -} - -.startedByName__6764b { - margin-inline-start:4px} - -.messageContent__6764b { - align-items: center; - color: var(--text-default); - display: flex; - flex-shrink: 1; - white-space: pre -} - -.messageContent__6764b .inline { - white-space: pre!important -} - -.messageContent__6764b .mention { - pointer-events: none -} - -.messageContentTrailingIcon__6764b { - flex: 0 0 auto; - margin-inline-start:4px} - -.messageContentLeadingIcon__6764b { - flex: 0 0 auto; - margin-inline-end:4px} - -.bullet__6764b { - color: var(--background-mod-muted); - padding: 0 8px -} - -.list_c441f0 { - padding: 0 16px -} - -.activeThreadsList_c441f0 { - padding-bottom: 8px; - padding-top: 16px -} - -.sectionHeader_c441f0 { - align-items: center; - box-sizing: border-box; - display: flex; - height: 24px; - margin: 8px 0; - text-transform: uppercase -} - -.controls_c441f0 { - display: flex; - height: 64px; - justify-content: space-between -} - -.tabBar_c441f0 { - height: 64px -} - -.tabBar_c441f0,.tabBar_c441f0 .tab_c441f0 { - align-items: center; - box-sizing: border-box -} - -.tabBar_c441f0 .tab_c441f0 { - display: flex; - height: 24px; - justify-content: center; - margin-inline-start:0;margin-top: 0; - text-align: center -} - -.theme-dark .list_c441f0 .tabBar_c441f0 .tab_c441f0.active_c441f0 { - background-color: var(--interactive-background-selected) -} - -.moderatorView_c441f0 { - align-items: center; - display: flex -} - -.checkbox_c441f0 { - margin-inline-end:4px} - -.infoIcon_c441f0 { - margin-inline-start:4px;position: relative; - top: 2px -} - -.spinner_c441f0 { - margin: 64px 0 -} - -.container_d9c882 { - background-color: var(--background-base-lower); - border-radius: 8px; - box-sizing: border-box; - cursor: default; - display: flex; - flex-direction: column -} - -.header_d9c882 { - align-items: center; - background-color: var(--background-base-lowest); - border-bottom: 1px solid var(--border-subtle); - display: flex; - height: 48px; - min-height: 48px; - padding: 0 16px -} - -.closeIcon_d9c882,.divider_d9c882,.tabBar_d9c882,.threadIcon_d9c882,.title_d9c882 { - flex-shrink: 0 -} - -.threadIcon_d9c882 { - margin-inline-end:8px} - -.divider_d9c882,.tabBar_d9c882 .tab_d9c882,.title_d9c882 { - margin-inline-end:16px} - -.closeIcon_d9c882,.threadIcon_d9c882 { - color: var(--interactive-text-default) -} - -.closeIcon_d9c882 { - margin-inline-start:var(--space-8)} - -.divider_d9c882 { - background-color: var(--border-subtle); - height: 24px; - width: 1px -} - -.container_d9c882,.header_d9c882 { - background-color: var(--background-surface-high) -} - -.tabBar_d9c882 .tab_d9c882 { - align-items: center; - box-sizing: border-box; - display: flex; - height: 24px; - justify-content: center; - margin-inline-start:0;text-align: center -} - -.spacer_d9c882 { - flex-grow: 1 -} - -.createButton_d9c882 { - margin-inline-start:var(--space-8)} - -.theme-dark .header_d9c882 .tabBar_d9c882 .tab_d9c882.active_d9c882 { - background-color: var(--interactive-background-selected) -} - -.searchIcon_d9c882 { - color: var(--text-default); - cursor: pointer; - margin-inline-end:16px;margin-top: 2px -} - -.searchIcon_d9c882:hover { - color: var(--interactive-text-hover) -} - -.searchBox_d9c882 { - background-color: var(--background-base-lower); - margin-inline-end:16px} - -.icon_d98031 { - align-items: center; - display: flex -} - -.count_d98031 { - margin-inline-start:4px} - -.icon_d98031:hover .count_d98031 { - color: var(--interactive-text-hover) -} - -.browser_d98031 { - box-shadow: var(--shadow-border),var(--shadow-high); - max-height: 80vh; - max-width: 600px; - min-height: 400px; - min-width: 480px; - overflow: hidden; - width: 35vw -} - -.enable-forced-colors .browser_d98031 { - border: 2px solid CanvasText -} - -.memberSinceWrapper_c4eb81 { - align-items: center; - display: flex; - gap: 8px -} - -.memberSince_c4eb81 { - align-items: center; - display: flex; - gap: 4px -} - -.guildIcon_c4eb81 { - border-radius: var(--radius-xs) -} - -.discordIcon_c4eb81 { - color: var(--interactive-text-default); - height: 16px; - width: 16px -} - -.divider_c4eb81 { - background-color: var(--interactive-text-default); - border-radius: 50%; - height: 4px; - width: 4px -} - -.header__7f9c0 { - margin-bottom: 8px; - min-height: calc(var(--custom-user-profile-banner-height) + 39px); - position: relative -} - -.headerTag__7f9c0 { - border-radius: var(--radius-xs); - bottom: 8px; - inset-inline-end: 16px; - position: absolute -} - -.footer__7f9c0 { - border-top: 1px solid var(--user-profile-border) -} - -.footerButton__7f9c0 { - align-items: center; - cursor: pointer; - display: flex; - height: 44px; - justify-content: center; - width: 100% -} - -.banner__7f9c0 { - border-radius: 0 -} - -.backdrop__7f9c0 { - background: var(--background-scrim); - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - z-index: var(--custom-user-profile-hoist-z-index) -} - -.toast__7f9c0 { - top: calc((var(--custom-user-profile-banner-height) - 56px)/2); - z-index: var(--custom-user-profile-toast-z-index) -} - -.widgetPreviews__7f9c0,.wishlistBreadcrumb__7f9c0 { - margin: 12px 16px 0 -} - -.body_b32ca4 { - margin: 0 16px -} - -.body_b32ca4,.overlay_b32ca4 { - display: flex; - flex-direction: column; - gap: 12px -} - -.overlay_b32ca4 { - padding: 12px -} - -.note_b32ca4 { - margin: -4px -} - -.note_b32ca4 textarea { - border-radius: 3px; - font-size: 14px; - line-height: 18px; - padding: 4px -} - -.note_b32ca4 textarea:focus { - background: var(--user-profile-note-background-focus) -} - -.note_b32ca4 textarea:focus::-moz-placeholder { - color: transparent -} - -.note_b32ca4 textarea:focus::placeholder { - color: transparent -} - -.container_a419e0 { - width: 340px -} - -.headerContainer_a419e0 { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-8); - justify-content: center -} - -.preview_a419e0 { - filter: blur(5px); - -o-object-fit: contain; - object-fit: contain; - -o-object-position: top; - object-position: top; - opacity: .3; - width: 100% -} - -.container_a419e0,.preview_a419e0 { - height: 100%; - position: relative -} - -.body_a419e0 { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-16); - inset-inline-start: 50%; - justify-content: center; - position: absolute; - text-align: center; - top: 50%; - transform: translate(-50%,-50%); - width: 80% -} - -.row__89036 { - align-items: center; - border-radius: 4px; - color: var(--text-default); - cursor: pointer; - display: flex; - font-weight: var(--font-weight-medium); - line-height: 30px; - margin-block:1px;margin-inline:12px 0;padding: 4px -} - -.row__89036:hover { - background-color: var(--user-profile-background-hover) -} - -.avatar__89036 { - flex-shrink: 0; - margin-inline-end:10px} - -.details__89036 { - display: flex; - flex-direction: column -} - -.details__89036,.tag__89036 { - overflow: hidden -} - -.tag__89036 { - font-size: 16px; - line-height: 20px; - text-overflow: ellipsis; - white-space: nowrap -} - -.discriminator__89036 { - font-size: 12px; - opacity: .6 -} - -.row__0a95c { - align-items: center; - border-radius: 4px; - color: var(--text-default); - cursor: pointer; - display: flex; - font-weight: var(--font-weight-medium); - line-height: 30px; - margin-block:1px;margin-inline:12px 0;padding: 4px -} - -.row__0a95c:hover { - background-color: var(--user-profile-background-hover) -} - -.icon__0a95c,.noIcon__0a95c { - flex-shrink: 0; - margin-inline-end:10px} - -.noIcon__0a95c { - background-color: var(--brand-500); - color: var(--white) -} - -.details__0a95c { - flex: 1 1 auto -} - -.details__0a95c,.name__0a95c { - overflow: hidden -} - -.name__0a95c { - font-size: 16px; - line-height: 20px; - text-overflow: ellipsis; - white-space: nowrap -} - -.avatar__0a95c { - flex: 0 0 auto; - margin-inline-end:4px} - -.nick__0a95c { - align-items: center; - display: flex; - justify-content: flex-start; - overflow: hidden -} - -.section__1f6ca { - display: flex; - flex-direction: column; - gap: 12px; - padding: 12px -} - -.header__1f6ca { - align-items: center; - display: flex; - flex-direction: row; - justify-content: space-between -} - -.header__1f6ca.clickable__1f6ca { - cursor: pointer -} - -.list__1f6ca { - display: flex; - flex-direction: column; - gap: 8px -} - -.list__1f6ca[hidden] { - display: none -} - -.overlay__3b260 { - margin: 12px 16px -} - -.divider__3b260 { - margin: 0 12px -} - -.list__3b260 { - gap: 0; - margin-inline-start:-8px} - -.loadingMutualFriend__3b260 { - align-items: center; - display: flex; - gap: 10px; - margin-block:1px;margin-inline:8px 0;padding: 4px -} - -.loadingMutualFriend__3b260>:first-child { - border-radius: var(--radius-round); - height: 40px; - margin: 0 -} - -.card__6d190 { - background: var(--scoped-control-background-secondary-default,hsla(240,4%,61%,.08)); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-shadow: 0 0 8px 0 rgba(0,0,0,.04); - box-sizing: border-box; - cursor: pointer; - flex-shrink: 0; - height: 87px; - overflow: hidden; - position: relative; - width: 65px -} - -.card__6d190:focus-visible,.card__6d190:hover { - background: var(--scoped-control-background-secondary-hover,hsla(240,4%,61%,.16)) -} - -.cardSmall__6d190 { - height: 73px; - width: 55px -} - -.cardSingle__6d190 { - height: 80px; - width: 60px -} - -.cardPreview__6d190,.cardPreviewNoScale__6d190 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.cardPreview__6d190>div { - height: 136px; - transform: scale(.613); - width: 106px -} - -.cardPreview__6d190>.bundlePreview__6d190 { - margin-inline-start:-19px;transform: scale(.42) -} - -.cardPreviewNoScale__6d190>div { - height: 100%; - width: 100% -} - -.moreOverlay__6d190 { - align-items: center; - background: rgba(0,0,0,.5); - display: flex; - inset: 0; - justify-content: center; - position: absolute; - z-index: 1 -} - -.card__6d190:focus-visible .moreOverlay__6d190,.moreOverlay__6d190 .card__6d190:hover { - background: rgba(0,0,0,.6) -} - -.card__46c9e { - border-radius: none; - display: flex; - height: 100%; - width: 100% -} - -.cardBackgroundImage__46c9e { - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - overflow: hidden; - top: 0; - transform: scale(105%) -} - -.cardBackgroundImage__46c9e,.cardImage__46c9e { - inset-inline: 0; - bottom: 0; - height: 100%; - position: absolute; - width: 100% -} - -.cardImage__46c9e { - -o-object-fit: cover; - object-fit: cover; - -o-object-position: center; - object-position: center; - top: 4px -} - -.singleItemContainer__4af00 { - align-items: center; - display: flex; - gap: 8px -} - -.singleItemInfo__4af00 { - display: flex; - flex: 1; - flex-direction: column; - min-width: 0 -} - -.singleItemName__4af00 { - cursor: pointer; - width: -moz-fit-content; - width: fit-content -} - -.singleItemName__4af00:focus-visible,.singleItemName__4af00:hover { - text-decoration: underline; - text-decoration-color: var(--text-default) -} - -.overlay__18000 { - align-items: center; - display: flex; - height: 100%; - inset-inline-start: 0; - justify-content: center; - position: absolute; - top: 0; - width: 100%; - z-index: 2 -} - -.full-motion .checkmark__18000 { - transition: opacity .3s ease -} - -.checkmarkHover__18000 { - opacity: 0 -} - -.cardStateIcon__18000 { - color: var(--status-positive); - filter: drop-shadow(0 0 4px rgba(0,0,0,.3)); - height: 38px; - transition: opacity .3s ease; - width: 38px -} - -.seeMoreOverlay__18000 { - background: rgba(0,0,0,.5); - pointer-events: none -} - -.singleItemInfo__08054 { - display: flex; - flex: 1; - flex-direction: column; - min-width: 0 -} - -.singleItemName__08054 { - cursor: pointer; - width: -moz-fit-content; - width: fit-content -} - -.singleItemName__08054:focus-visible,.singleItemName__08054:hover { - text-decoration: underline; - text-decoration-color: var(--text-default) -} - -.container__73abe { - align-items: center; - color: var(--white); - flex-direction: row; - gap: 8px; - justify-content: center -} - -.cardContainer__73abe,.container__73abe { - box-sizing: border-box; - display: flex; - position: relative -} - -.cardContainer__73abe { - border-radius: var(--radius-md); - box-shadow: 0 0 8px 0 rgba(0,0,0,.04); - flex-shrink: 0; - height: 87px; - width: 65px -} - -.card__73abe.wishlistCard__73abe { - align-items: center; - background-color: var(--scoped-control-background-secondary-default,hsla(240,4%,61%,.08)); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-md); - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.custom-user-profile-theme .card__73abe.wishlistCard__73abe { - background-color: var(--scoped-control-background-secondary-default,hsla(240,4%,61%,.08)) -} - -.card__73abe.wishlistCard__73abe:focus-visible,.card__73abe.wishlistCard__73abe:hover { - background-color: var(--scoped-control-background-secondary-hover,hsla(0,4%,61%,.16)) -} - -.cardSingle__73abe { - height: 80px; - width: 60px -} - -.cardPreview__73abe { - align-items: center; - display: flex; - justify-content: center -} - -.scaledSkuPreview__73abe { - transform: scale(.613); - transform-origin: center -} - -.scaledSkuPreview__73abe.avatarDecorationPreview__73abe { - height: auto; - width: auto -} - -.collectiblesSkuPreview__73abe>div { - height: 136px; - width: 106px -} - -.container__06b03 { - -webkit-backdrop-filter: blur(16px); - backdrop-filter: blur(16px); - display: flex; - flex-direction: column; - gap: 8px; - padding: 12px -} - -.header__06b03 { - align-items: center; - display: flex; - justify-content: space-between -} - -.cardsContainer__06b03 { - display: flex; - gap: 8px -} - -.scroller_f35c3c { - background-color: var(--background-base-lower); - flex: 1 1 auto; - height: auto; - min-height: 1px; - min-width: 1px -} - -.wrapper_f35c3c { - flex: 1 0 auto; - height: 100%; - margin: auto; - width: 100% -} - -.container_a154bf { - flex-direction: column -} - -.container_a154bf,.content_a154bf { - display: flex; - width: 100% -} - -.content_a154bf { - align-items: center; - background-color: var(--background-base-low); - flex: 1 0 auto; - flex-direction: column; - justify-content: center -} - -.splashImage_a154bf { - margin-bottom: 24px; - width: 400px -} - -.splashHeader_a154bf { - color: var(--text-strong); - margin-bottom: 8px -} - -.splashHeader_a154bf,.splashText_a154bf { - text-align: center; - width: 580px -} - -.noChannel__01d5c { - flex: 1 1 auto; - position: relative -} - -.container_e131a9 { - background: unset; - border-bottom-color: var(--border-subtle); - border-bottom: 1px solid var(--border-muted); - border-radius: var(--radius-sm) var(--radius-sm) 0 0; - color: var(--text-strong); - display: flex; - flex-direction: column; - flex-shrink: 0; - font-size: 14px; - font-weight: var(--font-weight-medium); - gap: 12px; - margin: 0; - padding: calc(var(--custom-guild-list-padding) - var(--custom-panels-spacing) + 4px) -} - -.inner_e131a9 { - flex: 1; - height: 32px; - min-width: 0 -} - -.channel_e131a9:hover { - text-decoration: underline -} - -.customStatusContainer_e131a9 { - position: relative -} - -.noiseCancellationPopout_e131a9 { - background: var(--background-surface-high); - border-radius: var(--radius-xs); - box-shadow: var(--shadow-border),var(--shadow-high); - display: flex; - flex-direction: column; - gap: var(--space-8); - padding: var(--space-16); - width: 244px -} - -.krispLogo_e131a9 { - background-position: 50%; - background-repeat: no-repeat; - background-size: contain; - height: 20px; - width: 44px -} - -.krispLink_e131a9 { - font-size: 14px -} - -.connection_e131a9 { - height: 32px; - padding-bottom: 0 -} - -.voiceUsers_e131a9 { - padding-bottom: 4px; - padding-top: 2px -} - -.actionButtons_e131a9 { - display: grid; - grid-template-columns: repeat(auto-fit,minmax(0,1fr)); - justify-content: space-between; - padding-top: 0; - grid-gap: var(--space-xs) -} - -.button_e131a9 { - border-radius: var(--radius-sm); - height: var(--space-32); - min-height: var(--space-32); - min-width: auto; - transition: background-color .1s ease-in-out,color .1s ease-in-out; - width: 100% -} - -.button_e131a9 .buttonColor_e131a9,.button_e131a9.buttonColor_e131a9 { - background-color: var(--background-base-low); - color: var(--interactive-text-active) -} - -.button_e131a9 .buttonColor_e131a9:hover,.button_e131a9.buttonColor_e131a9:hover { - background-color: var(--background-base-lower) -} - -.button_e131a9 .buttonColor_e131a9.greyButtonActive_e131a9,.button_e131a9.buttonColor_e131a9.greyButtonActive_e131a9 { - background-color: var(--background-mod-strong); - color: var(--interactive-text-active) -} - -.button_e131a9 .buttonColor_e131a9.fauxDisabled_e131a9,.button_e131a9 .buttonColor_e131a9:disabled,.button_e131a9.buttonColor_e131a9.fauxDisabled_e131a9,.button_e131a9.buttonColor_e131a9:disabled { - background-color: var(--background-base-low); - color: var(--interactive-text-active); - opacity: .3 -} - -.button_e131a9 .buttonColor_e131a9,.button_e131a9.buttonColor_e131a9 { - background-color: var(--control-secondary-background-default); - border-color: var(--border-muted); - color: var(--control-secondary-text-default) -} - -.button_e131a9 .buttonColor_e131a9:hover,.button_e131a9.buttonColor_e131a9:hover { - background-color: var(--control-secondary-background-hover) -} - -.button_e131a9 .buttonColor_e131a9.fauxDisabled_e131a9,.button_e131a9 .buttonColor_e131a9:disabled,.button_e131a9.buttonColor_e131a9.fauxDisabled_e131a9,.button_e131a9.buttonColor_e131a9:disabled { - background-color: var(--control-secondary-background-default) -} - -.button_e131a9.buttonActive_e131a9 { - background-color: var(--green-360); - color: var(--white) -} - -.button_e131a9.buttonActive_e131a9:hover { - background-color: var(--green-330) -} - -.density-compact .button_e131a9 { - height: 28px; - min-height: 28px -} - -.button_e131a9 .buttonColor_e131a9.buttonActive_e131a9,.button_e131a9.buttonColor_e131a9.buttonActive_e131a9 { - background-color: var(--opacity-green-12); - border: 1px solid var(--opacity-green-12); - color: var(--green-new-30) -} - -.button_e131a9 .buttonColor_e131a9.buttonActive_e131a9:hover,.button_e131a9.buttonColor_e131a9.buttonActive_e131a9:hover { - background-color: var(--opacity-green-24) -} - -.button_e131a9 .buttonColor_e131a9.buttonActive_e131a9:active,.button_e131a9.buttonColor_e131a9.buttonActive_e131a9:active { - background-color: var(--opacity-green-36) -} - -.high-contrast-mode .button_e131a9 .buttonColor_e131a9.buttonActive_e131a9,.high-contrast-mode .button_e131a9.buttonColor_e131a9.buttonActive_e131a9 { - background-color: var(--background-feedback-positive); - border-color: var(--text-feedback-positive); - color: var(--text-feedback-positive) -} - -.custom-theme-background .container_e131a9 { - background: unset -} - -.custom-theme-background .button_e131a9 .buttonColor_e131a9,.custom-theme-background .button_e131a9.buttonColor_e131a9 { - background: var(--background-gradient-low,var(--background-mod-subtle)) -} - -.custom-theme-background .button_e131a9 .buttonColor_e131a9:hover,.custom-theme-background .button_e131a9.buttonColor_e131a9:hover { - background: var(--background-gradient-high,var(--background-mod-strong)) -} - -.buttonContents_e131a9 { - align-items: center; - display: flex -} - -.buttonIcon_e131a9 { - height: 20px!important; - width: 20px!important -} - -.buttonIcon_e131a9.withText_e131a9 { - flex-shrink: 0; - margin-inline-end:8px} - -.voicePanelIntroductionHeader_e131a9,.voicePanelIntroductionText_e131a9 { - text-align: center -} - -.voicePanelIntroductionHeader_e131a9 { - color: var(--text-strong) -} - -.voicePanelIntroductionText_e131a9 { - color: var(--text-default); - margin-bottom: 16px; - margin-top: 4px -} - -.voicePanelIntroductionWrapper_e131a9 { - background-color: var(--white); - border-radius: 4px; - box-shadow: var(--shadow-border),var(--shadow-high); - max-width: 280px; - padding: 16px -} - -.voicePanelIntroductionWrapper_e131a9:after { - border-block-color:var(--white) transparent;border-inline-color: transparent; - border-style: solid; - border-width: 8px; - content: " "; - inset-inline-start: 50%; - margin-inline-start:-8px;position: absolute; - top: 100% -} - -.wrapper_e131a9 { - margin-inline-start:-1px;overflow: visible; - position: relative -} - -.viewAsRolesWarning_e131a9 { - align-items: center; - background-color: var(--background-surface-high); - border-radius: 4px; - bottom: 96px; - display: flex; - flex-direction: column; - inset-inline-start: 6px; - justify-content: center; - max-width: 196px; - padding: 16px; - position: absolute; - text-align: center; - z-index: 1000 -} - -.viewAsRolesWarning_e131a9 .viewAsRolesWarningText_e131a9 { - margin-bottom: 16px -} - -.enable-forced-colors .button_e131a9.disabled_e131a9,.enable-forced-colors .button_e131a9.fauxDisabled_e131a9,.enable-forced-colors .button_e131a9:disabled { - opacity: 1 -} - -.images-light .krispLogo_e131a9 { - background-image: url(/assets/415036ce2536a149.svg) -} - -.images-dark .krispLogo_e131a9 { - background-image: url(/assets/b46488e4f6a1d7ef.svg) -} - -.voiceButtonsContainer_e131a9 { - gap: var(--space-4) -} - -/*# sourceMappingURL=901301eeddfe8f4b.css.map*/ diff --git a/discord-html-copy/css/9296efc0128dcaf8.css b/discord-html-copy/css/9296efc0128dcaf8.css deleted file mode 100644 index cd77215..0000000 --- a/discord-html-copy/css/9296efc0128dcaf8.css +++ /dev/null @@ -1,212 +0,0 @@ -.DraftEditor-editorContainer,.DraftEditor-root,.public-DraftEditor-content { - height: inherit; - text-align: initial -} - -.public-DraftEditor-content[contenteditable=true] { - -webkit-user-modify: read-write-plaintext-only -} - -.DraftEditor-root { - position: relative -} - -.DraftEditor-editorContainer { - background-color: hsla(0,0%,100%,0); - border-left: .1px solid transparent; - position: relative; - z-index: 1 -} - -.public-DraftEditor-block { - position: relative -} - -.DraftEditor-alignLeft .public-DraftStyleDefault-block { - text-align: left -} - -.DraftEditor-alignLeft .public-DraftEditorPlaceholder-root { - left: 0; - text-align: left -} - -.DraftEditor-alignCenter .public-DraftStyleDefault-block { - text-align: center -} - -.DraftEditor-alignCenter .public-DraftEditorPlaceholder-root { - margin: 0 auto; - text-align: center; - width: 100% -} - -.DraftEditor-alignRight .public-DraftStyleDefault-block { - text-align: right -} - -.DraftEditor-alignRight .public-DraftEditorPlaceholder-root { - right: 0; - text-align: right -} - -.public-DraftEditorPlaceholder-root { - color: #9197a3; - position: absolute; - z-index: 1 -} - -.public-DraftEditorPlaceholder-hasFocus { - color: #bdc1c9 -} - -.DraftEditorPlaceholder-hidden { - display: none -} - -.public-DraftStyleDefault-block { - position: relative; - white-space: pre-wrap -} - -.public-DraftStyleDefault-ltr { - direction: ltr; - text-align: left -} - -.public-DraftStyleDefault-rtl { - direction: rtl; - text-align: right -} - -.public-DraftStyleDefault-listLTR { - direction: ltr -} - -.public-DraftStyleDefault-listRTL { - direction: rtl -} - -.public-DraftStyleDefault-ol,.public-DraftStyleDefault-ul { - margin: 16px 0; - padding: 0 -} - -.public-DraftStyleDefault-depth0.public-DraftStyleDefault-listLTR { - margin-left: 1.5em -} - -.public-DraftStyleDefault-depth0.public-DraftStyleDefault-listRTL { - margin-right: 1.5em -} - -.public-DraftStyleDefault-depth1.public-DraftStyleDefault-listLTR { - margin-left: 3em -} - -.public-DraftStyleDefault-depth1.public-DraftStyleDefault-listRTL { - margin-right: 3em -} - -.public-DraftStyleDefault-depth2.public-DraftStyleDefault-listLTR { - margin-left: 4.5em -} - -.public-DraftStyleDefault-depth2.public-DraftStyleDefault-listRTL { - margin-right: 4.5em -} - -.public-DraftStyleDefault-depth3.public-DraftStyleDefault-listLTR { - margin-left: 6em -} - -.public-DraftStyleDefault-depth3.public-DraftStyleDefault-listRTL { - margin-right: 6em -} - -.public-DraftStyleDefault-depth4.public-DraftStyleDefault-listLTR { - margin-left: 7.5em -} - -.public-DraftStyleDefault-depth4.public-DraftStyleDefault-listRTL { - margin-right: 7.5em -} - -.public-DraftStyleDefault-unorderedListItem { - list-style-type: square; - position: relative -} - -.public-DraftStyleDefault-unorderedListItem.public-DraftStyleDefault-depth0 { - list-style-type: disc -} - -.public-DraftStyleDefault-unorderedListItem.public-DraftStyleDefault-depth1 { - list-style-type: circle -} - -.public-DraftStyleDefault-orderedListItem { - list-style-type: none; - position: relative -} - -.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-listLTR:before { - left: -36px; - position: absolute; - text-align: right; - width: 30px -} - -.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-listRTL:before { - position: absolute; - right: -36px; - text-align: left; - width: 30px -} - -.public-DraftStyleDefault-orderedListItem:before { - content: counter(ol0) ". "; - counter-increment: ol0 -} - -.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-depth1:before { - content: counter(ol1,lower-alpha) ". "; - counter-increment: ol1 -} - -.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-depth2:before { - content: counter(ol2,lower-roman) ". "; - counter-increment: ol2 -} - -.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-depth3:before { - content: counter(ol3) ". "; - counter-increment: ol3 -} - -.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-depth4:before { - content: counter(ol4,lower-alpha) ". "; - counter-increment: ol4 -} - -.public-DraftStyleDefault-depth0.public-DraftStyleDefault-reset { - counter-reset: ol0 -} - -.public-DraftStyleDefault-depth1.public-DraftStyleDefault-reset { - counter-reset: ol1 -} - -.public-DraftStyleDefault-depth2.public-DraftStyleDefault-reset { - counter-reset: ol2 -} - -.public-DraftStyleDefault-depth3.public-DraftStyleDefault-reset { - counter-reset: ol3 -} - -.public-DraftStyleDefault-depth4.public-DraftStyleDefault-reset { - counter-reset: ol4 -} - -/*# sourceMappingURL=9296efc0128dcaf8.css.map*/ diff --git a/discord-html-copy/css/9b2747232e9f34a1.css b/discord-html-copy/css/9b2747232e9f34a1.css deleted file mode 100644 index 0a901ed..0000000 --- a/discord-html-copy/css/9b2747232e9f34a1.css +++ /dev/null @@ -1,1174 +0,0 @@ -.premiumLabel_e681d1 { - align-items: center; - display: flex; - justify-content: space-between; - pointer-events: none; - position: relative; - z-index: 3 -} - -.selected_e681d1 { - color: var(--white) -} - -.background_e681d1 { - border-radius: var(--radius-xs); - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 1 -} - -.selectedBackground_e681d1 { - background-color: var(--brand-500) -} - -.enable-forced-colors .selected_e681d1 { - color: Highlight -} - -.enable-forced-colors .selectedIcon_e681d1 { - fill: Highlight -} - -.enable-forced-colors .selectedBackground_e681d1 { - background-color: HighlightText -} - -.breadcrumbsNav__6989f { - max-width: 100%; - min-width: 0; - overflow: hidden -} - -.clickable__6989f { - align-items: center; - cursor: pointer; - display: inline-flex; - min-width: 0 -} - -.clickable__6989f .breadcrumbText__6989f { - color: var(--interactive-text-default) -} - -.clickable__6989f:hover .breadcrumbText__6989f { - color: var(--interactive-text-hover) -} - -.breadcrumbs__6989f { - flex-wrap: nowrap; - list-style: none -} - -.breadcrumb__6989f { - display: flex; - flex-shrink: 1; - min-width: 0 -} - -.breadcrumb__6989f:last-child { - flex-shrink: 0 -} - -.breadcrumbContent__6989f { - max-width: 100%; - min-width: 0 -} - -.breadcrumbText__6989f { - display: block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.content_e9e3ed { - background: var(--background-base-low); - display: flex; - flex: 1 1 auto; - flex-direction: column; - overflow: hidden; - position: relative -} - -.content_e9e3ed.mobile_e9e3ed.mobileNavigationOpen_e9e3ed { - display: none -} - -.contentHeader_e9e3ed { - align-items: center; - background: var(--background-base-low); - border-bottom: 1px solid var(--border-muted); - display: flex; - height: 48px; - justify-content: space-between; - padding-block:0;padding-inline:var(--space-16) var(--space-8)} - -.contentHeaderLeft_e9e3ed { - flex: 1 1 auto; - min-width: 0; - overflow: hidden -} - -.contentBody_e9e3ed { - flex: 1; - overflow: hidden -} - -.subnav_e4d939 { - display: grid; - gap: var(--space-md); - grid-auto-flow: row; - grid-template-columns: var(--custom-nav-width) 1fr; - margin-inline-start:var(--space-md);padding: var(--space-xs) 0 -} - -.subnav_e4d939 .item_e4d939 { - color: var(--text-muted); - cursor: pointer; - font-size: 16px; - grid-column: 2; - line-height: 20px -} - -.subnav_e4d939 .item_e4d939:focus-visible,.subnav_e4d939 .item_e4d939:hover { - color: var(--interactive-text-hover) -} - -.subnav_e4d939 .item_e4d939.active_e4d939 { - color: var(--interactive-text-active) -} - -.track_e4d939 { - background: var(--background-mod-subtle); - border-radius: 1px; - grid-column: 1; - grid-row: 1/span var(--custom-nav-count); - width: var(--custom-nav-width) -} - -.thumb_e4d939 { - background-color: var(--interactive-text-active); - border-radius: 100vw; - width: 100% -} - -.thumbAnchor_e4d939 { - align-self: center; - grid-column: 1; - grid-row: calc(var(--custom-nav-index) + 1); - height: 100%; - transform-origin: center; - width: 100% -} - -.item_caf372 { - align-items: center; - border-radius: var(--radius-sm); - color: var(--text-muted); - cursor: pointer; - display: flex; - gap: var(--space-sm); - justify-content: space-between; - padding: var(--space-xs) -} - -.item_caf372.active_caf372,.item_caf372:hover { - background-color: var(--background-mod-muted); - color: var(--text-strong) -} - -.item_caf372:hover:not(.active_caf372) { - transition: none -} - -.item_caf372.active_caf372 { - background-color: var(--background-mod-normal); - transition: background-color .3s ease -} - -.icon_caf372 { - flex-shrink: 0 -} - -.itemContent_caf372 { - align-items: center; - display: flex; - gap: var(--space-xs) -} - -.itemContainer_caf372 { - scroll-padding-block: var(--space-xs) -} - -.sidebar__409aa { - border-inline-end:1px solid var(--border-muted);display: flex; - flex: 0 0 240px; - flex-direction: column; - padding-block:var(--space-16) 0;padding-inline: var(--space-12) 0 -} - -.sidebar__409aa.mobile__409aa { - flex: 1 1 auto -} - -.sidebar__409aa.mobile__409aa:not(.mobileNavigationOpen__409aa) { - display: none -} - -.mobileCloseButton__409aa { - display: flex; - justify-content: flex-end -} - -.navScroller__409aa { - gap: var(--space-24); - margin-inline-end:var(--space-4);padding-bottom: var(--space-16); - padding-top: var(--space-12); - padding-inline:var(--space-4) var(--space-12)} - -.nav__409aa,.navScroller__409aa { - display: flex; - flex-direction: column -} - -.nav__409aa { - gap: var(--space-xs); - inset: auto 0 -} - -.section__409aa { - border-bottom: 1px solid var(--border-subtle); - display: flex; - flex-direction: column; - gap: var(--space-4); - padding-bottom: var(--space-xs) -} - -.section__409aa:last-of-type { - border-bottom: none -} - -.sectionLabel__409aa { - align-items: center; - display: flex; - flex-direction: row; - justify-content: space-between -} - -.label__409aa { - margin: var(--space-8) var(--space-8) var(--space-4) -} - -.fixedContent__409aa { - padding-inline:var(--space-4) var(--space-16);position: relative -} - -.fixedContent__409aa:after { - background: linear-gradient(to bottom,var(--background-base-lower),transparent); - content: ""; - height: var(--space-20); - position: absolute; - top: 100%; - inset-inline: 0 var(--space-16); - z-index: 1 -} - -.searchBarContainer__409aa { - display: flex; - position: relative -} - -.popoverAnchor__409aa { - bottom: 0; - inset-inline-end: -4px; - position: absolute; - top: 0 -} - -.container__8a969 { - background-color: var(--background-mod-normal); - border-radius: var(--radius-sm); - flex-direction: row; - justify-content: flex-start; - padding: 12px -} - -.container__8a969,.iconContainer__8a969 { - align-items: center; - display: flex; - position: relative -} - -.iconContainer__8a969 { - flex-grow: 0; - flex-shrink: 0; - height: 40px; - justify-content: center; - margin-inline-end:12px;width: 40px -} - -.actionable__8a969 { - cursor: pointer -} - -.name__8a969 { - color: var(--text-strong); - font-size: 16px; - font-weight: var(--font-weight-bold); - line-height: 20px -} - -.description__8a969 { - color: var(--text-default); - font-size: 12px; - font-weight: var(--font-weight-medium); - line-height: 16px -} - -.flair__8a969 { - bottom: -16px; - inset-inline-start: calc(50% - 24px); - position: absolute -} - -.hoverCard_fdda30 { - cursor: pointer; - transition: box-shadow,transform .2s ease -} - -.hoverCard_fdda30:focus-within,.hoverCard_fdda30:hover { - box-shadow: var(--shadow-high); - transform: translateY(-4px) -} - -.wrapper_fc8177 { - box-sizing: border-box; - position: relative; - word-wrap: break-word; - background: var(--background-gradient-chat,var(--background-base-lower)); - contain: paint layout; - flex: 0 0 auto; - overflow: hidden; - padding-inline-end:1rem;-webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.compact_fc8177.wrapper_fc8177 { - padding-bottom: .125rem; - padding-top: .125rem -} - -.cozy_fc8177.wrapper_fc8177 { - padding-inline-start:4.5rem} - -.a11y-font-scaled-up .cozy_fc8177.wrapper_fc8177 { - padding-inline-start:72px} - -.compact_fc8177 .contents_fc8177 { - display: flex; - flex-wrap: wrap; - height: 1.375rem; - overflow: hidden; - padding-inline-start:5rem} - -.compact_fc8177 .contents_fc8177 .content_fc8177 { - display: contents -} - -.cozy_fc8177 .content_fc8177 { - align-items: center; - display: flex; - flex-wrap: wrap; - height: 1.375rem; - overflow: hidden; - text-indent: 0 -} - -.compact_fc8177 .content_fc8177 { - display: inline; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.blob_fc8177 { - flex: 0 0 auto; - height: 1rem; - line-height: 1.375rem; - margin-top: .1875rem; - vertical-align: middle -} - -.cozy_fc8177 .blob_fc8177 { - display: block -} - -.hidden_fc8177 { - visibility: hidden -} - -.compact_fc8177 .blob_fc8177 { - display: inline-block -} - -.cozy_fc8177 .header_fc8177 { - align-items: center; - display: flex; - flex-wrap: wrap; - height: 1.375rem; - line-height: 1.375rem; - overflow: hidden; - position: relative -} - -.compact_fc8177 .header_fc8177 { - margin-inline-end:.5rem} - -.attachmentContainer_fc8177 { - padding-bottom: .125rem; - padding-top: .125rem -} - -.compact_fc8177 .attachmentContainer_fc8177 { - margin-top: .25rem -} - -.attachment_fc8177 { - height: 120px; - margin-top: .125rem; - width: 240px -} - -.compact_fc8177 .attachment_fc8177 { - margin-inline-start:5rem} - -.blob_fc8177+.blob_fc8177 { - margin-inline-start:.25rem} - -.header_fc8177 .blob_fc8177+.blob_fc8177 { - margin-inline-start:.5rem} - -.blob_fc8177 { - border-radius: .5rem -} - -.attachment_fc8177 { - border-radius: 6px -} - -.attachment_fc8177,.avatar_fc8177,.blob_fc8177 { - background-color: var(--text-default) -} - -.avatar_fc8177 { - border-radius: 50%; - flex: 0 0 auto; - height: 2.5rem; - inset-inline-start: 1rem; - max-height: 40px; - max-width: 40px; - position: absolute; - top: .25rem; - width: 2.5rem -} - -.a11y-font-scaled-up .avatar_fc8177 { - inset-inline-start: 16px; - top: 4px -} - -.compactTimestamp_fc8177 { - margin-inline:-4rem .25rem} - -.lineClamp__0b48b { - display: -webkit-box; - -webkit-box-orient: vertical; - overflow: hidden -} - -.divider__1fcac { - border-bottom: 1px solid var(--background-mod-strong) -} - -.spacingLarge__1fcac { - margin: var(--space-8) 0 -} - -.root_da9de7.root_da9de7.sizeReduced_da9de7 { - font-size: .875rem; - line-height: calc(var(--chat-markup-line-height)*.875) -} - -.root_da9de7.root_da9de7.sizeReduced_da9de7 small { - font-size: .75rem; - line-height: calc(var(--chat-markup-line-height)*.75) -} - -.root_da9de7.root_da9de7.colorMuted_da9de7 { - color: var(--text-subtle) -} - -.root_da9de7.root_da9de7.colorMuted_da9de7 small { - color: var(--text-muted) -} - -.root_da9de7.root_da9de7.weightReduced_da9de7 { - font-weight: var(--font-weight-normal) -} - -.root_da9de7.root_da9de7.weightReduced_da9de7 h1,.root_da9de7.root_da9de7.weightReduced_da9de7 h2,.root_da9de7.root_da9de7.weightReduced_da9de7 h3,.root_da9de7.root_da9de7.weightReduced_da9de7 h4,.root_da9de7.root_da9de7.weightReduced_da9de7 h5,.root_da9de7.root_da9de7.weightReduced_da9de7 h6,.root_da9de7.root_da9de7.weightReduced_da9de7 strong { - font-weight: var(--font-weight-medium) -} - -.markdownContainer__48344 { - overflow-wrap: anywhere -} - -.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344>p:first-child { - margin-top: 0 -} - -.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344>p:last-child { - margin-bottom: 0 -} - -.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 h1:first-child,.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 h2:first-child,.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 h3:first-child { - margin-top: 0 -} - -.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 h1:not(:has(+*)),.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 h2:not(:has(+*)),.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 h3:not(:has(+*)) { - margin-bottom: 0 -} - -.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 blockquote,.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344.markdownContainer__48344 pre { - max-width: unset -} - -.notice__29487 { - align-items: center; - background: linear-gradient(90deg,var(--color-scoped-expressive-background-nitro-1-start,rgba(179,38,156,.7)) 0,var(--color-scoped-expressive-background-nitro-1-end,rgba(20,20,203,.7)) 100%),var(--background-base-lowest); - display: flex; - height: 54px; - position: relative; - z-index: 101 -} - -@supports not ((grid-template-columns: subgrid) and (white-space-collapse:collapse)) { - .notice__29487 { - border-start-start-radius:0 - } -} - -@supports (grid-template-columns: subgrid) and (white-space-collapse:collapse) { - .notice__29487 { - grid-area:notice - } -} - -.noticeContent__29487 { - align-items: center; - display: flex; - flex: 1; - gap: var(--space-16); - justify-content: center -} - -.noticeText__29487 strong { - font-weight: var(--font-weight-semibold) -} - -.closeButton__29487 { - margin-inline:auto var(--space-12)} - -.premiumIcon_b68a35 { - height: 24px; - margin-inline-end:8px;position: relative; - top: 6px; - width: 24px -} - -.platformIcon_b68a35 { - display: inline-block; - height: 28px; - margin-top: -4px; - margin-inline-end:10px;position: relative; - vertical-align: middle; - width: 28px -} - -.platformIcon_b68a35+.platformIcon_b68a35 { - margin-inline-start:-10px} - -.giftIcon_b68a35 { - margin-inline-end:4px;vertical-align: text-bottom -} - -.icon_b68a35 { - display: inline-block; - height: 20px; - margin-inline-start:10px;margin-top: -3px; - position: relative; - vertical-align: middle; - width: 20px -} - -.icon_b68a35+.icon_b68a35 { - margin-inline-start:6px} - -.iconWindows_b68a35 { - -webkit-mask-image: url(/assets/20dd0e244b9a7065.svg); - mask-image: url(/assets/20dd0e244b9a7065.svg) -} - -.iconApple_b68a35,.iconWindows_b68a35 { - background-color: currentColor -} - -.iconApple_b68a35 { - -webkit-mask-image: url(/assets/d11dc1928431aa27.svg); - mask-image: url(/assets/d11dc1928431aa27.svg) -} - -.iconAndroid_b68a35 { - background-color: currentColor; - -webkit-mask-image: url(/assets/f625814fc53c325e.svg); - mask-image: url(/assets/f625814fc53c325e.svg) -} - -.iconUSFlag_b68a35 { - background-image: url(/assets/423c2b95bd0fdafb.png); - background-position: top; - background-repeat: no-repeat; - background-size: 85%; - margin-inline-end:7px;margin-top: 0 -} - -.icon_b68a35+.btn_b68a35 { - margin-inline-start:20px} - -.textLink_b68a35 { - color: var(--white); - padding-inline-start:10px;text-decoration: underline; - -webkit-app-region: no-drag -} - -.textLinkSmall_b68a35 { - font-size: 12px -} - -.testModeSKUSelector_b68a35 { - height: 24px; - margin-inline-start:16px} - -.premiumLogo_b68a35 { - background-image: url(/assets/a3541a1173e706be.svg); - background-repeat: no-repeat; - background-size: 100%; - display: inline-block; - height: 13px; - margin-inline-end:20px;position: relative; - top: 2px; - width: 51px -} - -.premiumText_b68a35 { - font-weight: var(--font-weight-medium) -} - -.premiumAction_b68a35 { - margin-inline-start:20px} - -.ellipsis_b68a35 { - margin-inline-start:7px} - -.quarantineLearnMoreLink_b68a35 { - margin-inline-start:10px;text-decoration: underline -} - -.errorCodeNoticeText_b68a35 { - margin-inline-start:var(--space-8)} - -.errorCodeNoticeClickable_b68a35 { - cursor: pointer -} - -.text__7b750 { - display: inline-block -} - -.text__7b750,.text__7b750 a { - color: currentColor -} - -.text__7b750 a { - text-decoration: underline -} - -.premiumIcon__7b750 { - height: 24px; - margin-inline-end:8px;position: relative; - top: 6px; - width: 24px -} - -.notice__36c3e { - align-items: center; - display: flex; - flex-direction: row; - justify-content: center -} - -.guildIcon__36c3e { - margin-inline-end:8px} - -.guildName__36c3e { - color: inherit -} - -.guildName__36c3e:hover { - text-decoration: underline -} - -.actionButton__36c3e { - margin-inline-start:12px;top: 0 -} - -.actionButtonInner__36c3e { - align-items: center; - display: flex; - gap: 4px -} - -.notice__30f28 { - align-items: center; - background-color: var(--brand-600); - box-shadow: none; - color: var(--white); - display: flex; - height: 40px; - justify-content: center; - padding: 0 8px -} - -.notice__30f28.error__30f28 { - background-color: var(--background-feedback-critical) -} - -.button__30f28 { - align-items: center; - font-weight: var(--font-weight-semibold); - height: -moz-fit-content; - height: fit-content; - padding: 4px 8px -} - -.button__30f28:hover { - background-color: var(--brand-530) -} - -.error__30f28>.button__30f28:hover { - background-color: var(--red-430) -} - -.header__30f28 { - color: var(--white); - display: inline; - margin-inline-end:16px} - -.backButtonInner__84419 { - display: flex; - gap: 8px; - padding: 2px 0 -} - -.backButton__84419:hover { - background-color: var(--brand-530) -} - -.backNotice__84419 { - background-color: var(--brand-600); - border-radius: 0; - padding: 8px -} - -.closeButton__84419 { - height: 100% -} - -.notice_c5cd6a { - background-color: var(--brand-600); - color: var(--white); - display: flex; - height: 40px; - justify-content: center; - padding: 0 8px -} - -.button_c5cd6a,.notice_c5cd6a { - align-items: center -} - -.button_c5cd6a { - font-weight: var(--font-weight-semibold); - height: -moz-fit-content; - height: fit-content; - padding: 4px 8px -} - -.button_c5cd6a:hover { - background-color: var(--brand-530) -} - -.back_c5cd6a { - bottom: 0; - height: 24px; - inset-inline-start: 8px; - margin: auto; - position: absolute; - top: 0 -} - -.iconButton_c5cd6a { - align-items: center; - display: flex -} - -.arrow_c5cd6a { - margin-inline-end:8px} - -.header_c5cd6a { - color: var(--white); - display: inline; - margin-inline-end:16px} - -.container__477aa { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-12); - justify-content: center -} - -.buttonGroup__477aa { - margin: var(--space-4) 0; - width: unset -} - -.blocked__477aa { - align-items: center; - display: flex -} - -.blockedIcon__477aa { - margin-inline:var(--space-12) var(--space-4)} - -.blockedText__477aa { - font-size: 12px; - font-weight: var(--font-weight-normal) -} - -.noIcon__477aa { - margin-inline-start:var(--space-12)} - -.safetySettingsNotice__9536c { - align-items: center; - background: var(--background-base-lowest); - border-radius: var(--radius-xs); - display: flex; - gap: var(--space-8); - padding: var(--space-16) -} - -.safetySettingsNotice__9536c .closeButton__9536c { - cursor: pointer; - margin-inline-start:auto} - -.container__394db { - align-items: center; - display: block; - flex: 1 1 auto; - overflow: hidden; - position: relative; - vertical-align: middle; - white-space: nowrap -} - -.container__394db .chipletContainer__394db { - display: inline-block; - overflow: hidden -} - -.container__394db .chipletParent__394db { - display: inline; - overflow: hidden; - padding-inline-start:4px;position: relative -} - -.container__394db .usernameContainer__394db { - display: inline-block; - flex: 1; - margin-top: 2px; - max-width: 100%; - overflow: hidden; - white-space: nowrap -} - -.container__394db.isOverlayContainer__394db { - align-content: center; - background: var(--background-base-low); - border-radius: 4px; - display: flex; - flex: none; - flex-direction: row; - height: 20px -} - -.container__394db.isOverlayContainer__394db .usernameContainer__394db { - margin-top: 0 -} - -.container__394db.isOverlayContainer__394db .chipletContainer__394db,.container__394db.isOverlayContainer__394db .chipletParent__394db { - align-content: center; - align-items: center; - display: flex -} - -.container__394db.isOverlayContainer__394db .chipletContainer__394db { - margin: 0; - padding: 1.5px -} - -.container__394db.isOverlayContainer__394db .chipletContainer__394db.noPadding__394db { - padding: 0 -} - -.isOverlayTag__394db { - margin: 0 -} - -.avatarWrapper_d8a370 { - align-items: center; - display: inline-flex; - flex: 0 0 auto; - justify-content: center; - position: relative -} - -.ring_d8a370 { - border-radius: 50%; - inset: -3px; - -webkit-mask: radial-gradient(farthest-side,transparent calc(100% - 2px),#000 calc(100% - 1px)); - mask: radial-gradient(farthest-side,transparent calc(100% - 2px),#000 calc(100% - 1px)); - pointer-events: none; - position: absolute; - transform: rotate(-90deg) -} - -.keybindFlexboxLayout_cbf20c { - align-items: baseline; - display: flex; - flex: 0 0 auto; - flex-direction: row; - flex-wrap: wrap; - gap: var(--space-4); - justify-content: flex-start; - max-width: 100%; - overflow: visible; - white-space: normal; - width: -moz-fit-content; - width: fit-content -} - -.keyCombo_cbf20c { - vertical-align: middle -} - -.keyCombo_cbf20c,.keyComboKey_cbf20c { - display: inline-block -} - -.container_abd9a8 { - color: var(--text-default); - contain: layout; - container-type: inline-size; - display: flex; - flex-direction: row; - height: 100% -} - -.links__7aac8 { - display: inline; - overflow-wrap: break-word -} - -.bullet__7aac8 { - font-size: 10px; - margin: 0 var(--space-4) -} - -.bullet__7aac8,.moreButton__7aac8 { - color: var(--text-muted) -} - -.moreButton__7aac8.active__7aac8,.moreButton__7aac8:hover { - color: var(--text-default) -} - -.singleSelectOption__12eef { - -moz-column-gap: 8px; - column-gap: 8px; - display: flex -} - -.deviceContainer__12eef { - align-items: center; - display: grid; - gap: 4px; - grid-template-areas: "label subLabel certifiedPill"; - grid-template-columns: auto 1fr min-content; - width: 100% -} - -.deviceContainer__12eef.withIcon__12eef { - grid-template-areas: "icon label subLabel certifiedPill"; - grid-template-columns: min-content auto 1fr min-content -} - -.deviceContainer__12eef.multiLine__12eef { - grid-template-areas: "label certifiedPill" "subLabel certifiedPill"; - grid-template-columns: 1fr min-content -} - -.deviceContainer__12eef.multiLine__12eef.withIcon__12eef { - grid-template-areas: "icon label certifiedPill" "icon subLabel certifiedPill"; - grid-template-columns: min-content 1fr min-content -} - -.deviceContainer__12eef.multiLine__12eef .deviceSubLabel__12eef:after,.deviceContainer__12eef.multiLine__12eef .deviceSubLabel__12eef:before { - content: "" -} - -.deviceContainer__12eef .deviceIcon__12eef { - align-items: center; - display: flex; - grid-area: icon -} - -.deviceContainer__12eef .deviceLabel__12eef { - grid-area: label -} - -.deviceContainer__12eef .deviceSubLabel__12eef { - grid-area: subLabel; - text-overflow: ellipsis -} - -.deviceContainer__12eef .deviceSubLabel__12eef:before { - content: "(" -} - -.deviceContainer__12eef .deviceSubLabel__12eef:after { - content: ")" -} - -.deviceContainer__12eef .deviceCertifiedPill__12eef { - align-items: center; - display: flex; - grid-area: certifiedPill -} - -.wrapper__06283 { - height: calc(var(--custom-gradient-progress-notch-height) + var(--custom-gradient-progress-notch-margin)*2); - min-width: 0; - position: relative; - width: 100% -} - -.container__06283 { - display: flex; - height: 100%; - overflow: hidden; - position: relative -} - -.progress__06283 { - background-color: var(--gradient-progress-pill-background) -} - -.notches__06283,.progress__06283 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.notches__06283 { - width: 100% -} - -.notches__06283.gray__06283 { - color: var(--background-base-low) -} - -.notches__06283.black__06283 { - color: var(--background-surface-high) -} - -.container__011b7 { - display: flex; - flex-direction: column -} - -.micTest__011b7 { - align-items: center; - display: flex; - margin-top: 4px; - position: relative -} - -.title__011b7 { - margin-top: 8px -} - -.description__011b7,.title__011b7 { - margin-bottom: 4px -} - -.micTestCaption__011b7 { - inset-inline-start: 0; - position: absolute; - top: calc(100% + 4px) -} - -.buttonSizer__011b7 { - align-items: center; - display: flex; - margin-top: 4px; - position: absolute; - visibility: hidden; - width: 100% -} - -.buttonSizerSpacer__011b7 { - min-width: 0; - width: 100% -} - -.buttonWrapper__011b7 { - margin-inline-end:var(--space-8)} - -.emptySearchResultsContainer_cf016e { - align-items: center; - display: flex; - flex-direction: column; - gap: 4px; - justify-content: center; - padding: 24px 0 -} - -/*# sourceMappingURL=9b2747232e9f34a1.css.map*/ diff --git a/discord-html-copy/css/a06f142ee55db4f5.css b/discord-html-copy/css/a06f142ee55db4f5.css deleted file mode 100644 index 093d1f3..0000000 --- a/discord-html-copy/css/a06f142ee55db4f5.css +++ /dev/null @@ -1,65 +0,0 @@ -.activeWrapper__452c3,.wrapper__452c3 { - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 1002 -} - -.videoWrapper__452c3,.wrapper__452c3 { - pointer-events: none -} - -.videoWrapper__452c3 { - height: 100%; - inset-inline-start: 50%; - position: absolute; - top: 50%; - transform: translate(-50%,-50%); - width: auto; - z-index: 200 -} - -@media (min-aspect-ratio: 45/32) { - .videoWrapper__452c3 { - height:auto; - width: 100% - } -} - -.videoWrapperForHelper__452c3 { - inset-inline-start: 0; - top: 0; - z-index: 200 -} - -.gadientHighlight__452c3,.videoWrapperForHelper__452c3 { - height: 100%; - pointer-events: none; - position: absolute; - width: 100% -} - -.gadientHighlight__452c3 { - background-image: linear-gradient(90deg,var(--premium-tier-2-purple-for-gradients) 0,var(--premium-tier-2-purple-for-gradients-2) 50%,var(--premium-tier-2-pink-for-gradients) 100%) -} - -.swipeWrapper__452c3 { - pointer-events: none; - width: 100% -} - -.swipe__452c3,.swipeWrapper__452c3 { - height: 100%; - position: absolute -} - -.swipe__452c3 { - inset-inline-end: 0; - opacity: .1; - top: 0; - width: auto -} - -/*# sourceMappingURL=a06f142ee55db4f5.css.map*/ diff --git a/discord-html-copy/css/f37149369742ded7.css b/discord-html-copy/css/f37149369742ded7.css deleted file mode 100644 index ed2df35..0000000 --- a/discord-html-copy/css/f37149369742ded7.css +++ /dev/null @@ -1,6122 +0,0 @@ -.container_d79086 { - box-sizing: border-box; - contain: paint; - cursor: revert; - display: inline-block; - position: relative; - text-align: start; - vertical-align: top; - width: 100% -} - -.animatedText_d79086 { - display: block; - overflow: hidden; - pointer-events: none; - text-overflow: ellipsis; - transform-style: preserve-3d; - white-space: nowrap; - width: 100% -} - -.emoji_ce5b39 { - flex-shrink: 0 -} - -.panel_fe7ab2 { - flex: 1; - min-height: 0 -} - -.header_fe7ab2 { - margin: 16px 0 8px -} - -.headerBar_fe7ab2 { - box-shadow: var(--elevation-low) -} - -.headerTitle_fe7ab2 { - flex: 1 -} - -.dispatcherHeader_fe7ab2 { - max-width: calc(100% - 90px) -} - -.headerTitleText_fe7ab2 { - display: block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.inspectorWrapper_fe7ab2 { - font-family: var(--font-code); - font-size: 14px -} - -.tabBarContainer_fe7ab2 { - align-items: center; - display: flex; - width: 100% -} - -.tabBar_fe7ab2 { - background: var(--background-base-lower); - border-bottom: 1px solid var(--background-base-lowest); - flex-shrink: 0; - height: 36px; - overflow: hidden; - white-space: nowrap; - width: 95% -} - -.tabContent_fe7ab2 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px -} - -.tabMeasurer_fe7ab2 { - height: 0; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - visibility: hidden; - width: 0 -} - -.tabItem_fe7ab2 { - border-bottom: 2px solid transparent; - color: var(--interactive-text-default); - cursor: pointer; - display: inline-block; - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 22px; - overflow: hidden; - padding: 6px 12px; - text-overflow: ellipsis; - white-space: nowrap -} - -.tabItem_fe7ab2:hover { - border-bottom-color: var(--brand-500) -} - -.tabItem_fe7ab2.selected_fe7ab2,.tabItem_fe7ab2:active { - border-bottom-color: var(--control-brand-foreground) -} - -.menu_fe7ab2 { - display: flex; - height: 100%; - justify-content: flex-end; - width: 5% -} - -.overflowChevron_fe7ab2 { - height: 100%; - position: relative -} - -.table_fe7ab2,.tableContainer_fe7ab2 { - display: flex; - flex: 1; - flex-direction: column; - min-height: 0 -} - -.tableHeader_fe7ab2 { - background: var(--background-base-lower); - height: 24px; - text-transform: uppercase -} - -.tableHeader_fe7ab2,.tableRow_fe7ab2 { - align-items: center; - border-bottom: 1px solid hsla(0,0%,100%,.06); - display: flex; - flex-direction: row; - padding: 8px -} - -.tableRow_fe7ab2 { - color: var(--interactive-text-default); - cursor: pointer; - font-size: 16px; - line-height: 22px; - min-height: 24px -} - -.tableRow_fe7ab2>div { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.tableRow_fe7ab2:hover { - background: var(--interactive-background-hover) -} - -.tableRow_fe7ab2:last-child { - border-bottom: none -} - -.selectedTableRow_fe7ab2 { - background: var(--interactive-background-selected) -} - -.properties_fe7ab2 { - align-items: baseline; - background: var(--background-base-lower); - box-shadow: var(--elevation-low); - display: grid; - gap: 16px 8px; - grid-template-columns: max-content auto; - padding: 16px -} - -.propertyName_fe7ab2 { - color: var(--text-default); - flex: 0 0 auto; - font-family: var(--font-display); - font-size: 14px; - font-weight: var(--font-weight-semibold); - text-align: end; - text-transform: uppercase -} - -.propertyValue_fe7ab2 { - align-items: center; - display: flex -} - -.propertyValue_fe7ab2:hover>.copyPropertyButton_fe7ab2 { - opacity: 1 -} - -.propertyValue_fe7ab2 .copyPropertyButton_fe7ab2 { - color: var(--interactive-text-default); - height: 18px; - margin-inline-start:4px;opacity: 0; - transition: opacity .1s ease-in-out -} - -.propertyValue_fe7ab2 .copyPropertyButton_fe7ab2:hover { - color: var(--interactive-text-hover); - cursor: pointer -} - -.toolbar_fe7ab2 { - align-items: center; - border-bottom: 1px solid var(--background-base-lowest); - display: flex; - flex-direction: row; - height: 34px -} - -.toolbarGroup_fe7ab2 { - align-items: center; - display: inline-flex; - margin: 0 8px -} - -.toolbarGroupLabel_fe7ab2 { - font-size: 14px; - margin-inline-end:8px} - -.toolbarButton_fe7ab2 { - flex: 0 0 auto -} - -.toolbarDivider_fe7ab2 { - border-inline-start:1px solid var(--text-muted);height: 18px; - margin: 8px 0 -} - -.premiumStreakOverride_fe7ab2 { - margin-top: 12px -} - -.container_db0ccb { - background: var(--background-base-low); - color: var(--text-default); - position: relative -} - -.resizeHandle_db0ccb { - cursor: ew-resize; - height: 100%; - inset-inline-start: 0; - position: absolute; - width: 8px; - z-index: 1 -} - -.sidebarContent_db0ccb { - border-inline-start:1px solid var(--background-base-lowest);display: flex; - flex-direction: column; - height: 100% -} - -.mobileHeaderCollapsed_db0ccb { - cursor: pointer -} - -.mobileContainerExpanded_db0ccb { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - z-index: 9999999999999 -} - -.layerContainer_db0ccb { - inset-inline-end: 0 -} - -.popoutContainer_db0ccb { - background: var(--background-base-low); - color: var(--text-default); - display: flex; - flex-direction: column; - height: 100%; - position: relative; - width: 100% -} - -.background_df5e2e { - background-repeat: repeat; - bottom: -100vh; - position: absolute; - top: -100vh; - inset-inline: -100vw; - pointer-events: none -} - -.wrapper_a230e0 { - bottom: 120px; - box-shadow: var(--elevation-high),0 0 7px 2px var(--opacity-white-80) inset; - display: flex; - flex-direction: column; - inset-inline-end: 40px; - max-width: 376px; - pointer-events: all; - z-index: 1000 -} - -.backgroundWrapper_a230e0,.wrapper_a230e0 { - border-radius: var(--radius-sm); - position: absolute -} - -.backgroundWrapper_a230e0 { - top: 0; - inset-inline: 0; - bottom: 0; - overflow: hidden -} - -.backgroundImage_a230e0 { - background-image: url(https://cdn.discordapp.com/assets/content/03fd0662b5ff101e4fc8c43309a51174a8d6de4275ebc364897623093487fa45.png); - background-size: 1120px 630px -} - -.backgroundOverlay_a230e0 { - background: linear-gradient(to bottom,hsl(var(--black-hsl)/80%),hsl(var(--black-hsl)/40%)); - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.heroImage_a230e0 { - background-image: url(https://cdn.discordapp.com/assets/content/df9ae78646e66db6aa0c2286477f8e97de435683b446dc437c2276e461568783.png); - background-repeat: no-repeat; - background-size: contain; - bottom: 0; - height: 104px; - inset-inline-start: -16px; - min-width: 94px; - position: absolute -} - -.content_a230e0 { - display: flex; - padding: 12px; - padding-inline-start:80px;position: relative -} - -.link_a230e0 { - cursor: pointer; - font-weight: 600; - text-decoration: underline -} - -.closeClickable_a230e0 { - color: var(--white); - cursor: pointer; - inset-inline-end: 8px; - opacity: .8; - position: absolute; - top: 8px; - transition: opacity .2s ease -} - -.closeClickable_a230e0:hover { - opacity: 1 -} - -.wrapper__946ec { - background-color: #000; - border-radius: 8px; - inset: 0; - overflow: hidden; - position: absolute; - z-index: var(--custom-user-profile-toast-z-index) -} - -.theme-light .wrapper__946ec { - background-color: #eeeff1 -} - -.closeButton__946ec.closeButton__946ec { - inset-inline-end: 10px; - position: absolute; - top: 10px; - z-index: 1 -} - -.copyButton__252af { - border-radius: var(--radius-round); - color: var(--interactive-text-default); - cursor: pointer; - height: 16px; - opacity: 0; - width: 16px -} - -:not(.keyboard-mode).full-motion .copyButton__252af { - transition: opacity var(--custom-button-transition-duration) ease -} - -.copyButton__252af.visible__252af,.copyButton__252af:focus-visible { - opacity: 1 -} - -.copyButton__252af:hover { - color: var(--interactive-text-hover) -} - -.copyButton__252af:active { - color: var(--interactive-text-active) -} - -.userMenuItem_d40c56 { - align-items: center; - display: flex; - gap: var(--space-8) -} - -.userMenuUsername_d40c56 { - flex-grow: 1; - max-width: 132px -} - -.userMenuText_d40c56 { - color: var(--text-strong); - display: block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.userMenuDiscriminator_d40c56 { - color: var(--text-default) -} - -.focused_d40c56 .userMenuUsername_d40c56 .userMenuText_d40c56 { - color: var(--text-strong) -} - -.focused_d40c56 .userMenuUsername_d40c56 .userMenuDiscriminator_d40c56 { - color: var(--text-default) -} - -.activeIcon_d40c56 { - flex-shrink: 0 -} - -.popoutContainer_ce8328 { - align-items: flex-end; - display: flex; - flex-direction: row-reverse -} - -.body_ce8328 { - padding: 4px 16px 16px -} - -.body_ce8328,.menus_ce8328 { - display: flex; - flex-direction: column; - gap: 12px -} - -.menuOverlay_ce8328 { - padding: 8px -} - -.menuItem_ce8328 { - display: flex; - flex-direction: column -} - -.menuItem_ce8328:after { - background-color: var(--user-profile-border); - content: ""; - display: block; - height: 1px; - margin: 8px 4px -} - -.menuItem_ce8328:last-child:after { - display: none -} - -.menuItemLabelText_ce8328 { - line-height: 16px -} - -.menuItemInner_ce8328 { - align-items: center; - border-radius: 8px; - box-sizing: border-box; - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - gap: 8px; - justify-content: space-between; - padding: 8px; - width: 100% -} - -.menuItemInner_ce8328:hover { - background-color: var(--user-profile-overlay-background-hover); - color: var(--interactive-text-hover) -} - -.menuItemInner_ce8328:active { - color: var(--interactive-text-active) -} - -.menuItemContent_ce8328 { - flex-grow: 1; - gap: 8px -} - -.menuItemContent_ce8328,.menuItemLabel_ce8328 { - align-items: center; - display: flex; - justify-content: space-between -} - -.menuItemLabel_ce8328 { - flex: 1; - gap: 4px; - text-align: start -} - -.menuItemIcon_ce8328 { - align-items: center; - display: flex; - height: 16px; - justify-content: center; - width: 16px -} - -.menuItemLabelHeader_ce8328 { - align-items: center; - display: flex; - gap: 8px -} - -.submenuIconWrapper_ce8328 { - align-items: center; - align-self: stretch; - display: flex -} - -.submenuPaddingContainer_ce8328 { - padding: 0 12px -} - -.statusPickerModal_ce8328 { - background: unset; - border: none; - box-shadow: none; - width: 100% -} - -.statusPickerModalMenu_ce8328 { - max-height: calc(100vh - 72px); - overflow: hidden; - width: 300px -} - -.mainStatusIcon_ce8328 { - height: 12px; - margin-top: 3.5px; - margin-inline-start:3px;width: 12px -} - -.statusItem_ce8328 { - align-items: center; - box-sizing: border-box; - display: grid; - grid-template-areas: "icon status" ". description"; - grid-template-columns: 20px 1fr; - grid-template-rows: 20px 1fr; - min-height: 20px; - padding: 6px 8px -} - -.expiringStatusMenuItem_ce8328 { - padding: 0 -} - -.expiringStatusIcon_ce8328 { - margin-inline-end:-6px} - -.durationButtons_ce8328 { - display: flex; - grid-column-start: 2; - justify-content: space-between; - margin-bottom: 2px; - margin-top: 6px -} - -.durationButton_ce8328 { - height: 28px -} - -.status_ce8328 { - align-items: center; - display: flex; - grid-area: status span 2 -} - -.icon_ce8328 { - grid-area: icon -} - -.description_ce8328 { - color: currentColor; - font-size: 12px; - grid-area: description; - line-height: 16px; - margin-bottom: 2px; - white-space: normal -} - -.themeContainer_ce8328>div { - max-height: calc(100vh - 124px) -} - -.username_ce8328 { - margin-bottom: -4px -} - -.focusModeItem_ce8328 { - padding-inline-start:2px} - -.focusModeIcon_ce8328 { - height: 12px; - margin-top: 3px; - margin-inline-start:8px;width: 12px -} - -.focusModeTitle_ce8328 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px -} - -.container_caf802 { - align-items: center; - display: flex; - gap: 3px -} - -.container_c3474d { - background: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-md); - box-shadow: var(--shadow-high); - box-sizing: border-box; - margin-inline-start:8px;width: 400px -} - -.container_c3474d,.content_c3474d { - display: flex; - flex-direction: column -} - -.content_c3474d { - overflow: hidden; - padding: 24px 24px 0 -} - -.headerRow_c3474d { - align-items: flex-start; - display: flex; - flex-direction: row-reverse; - justify-content: space-between; - margin-bottom: 8px -} - -.closeButton_c3474d { - align-items: center; - color: var(--icon-subtle); - cursor: pointer; - height: 24px; - justify-content: center; - margin-bottom: 0; - margin-top: -4px; - margin-inline-end:-4px;margin-inline-start:8px;transition: color 50ms ease-in; - width: 24px -} - -.closeButton_c3474d:hover { - color: var(--icon-strong); - transition: color .15s ease-out -} - -.scroller_c3474d { - margin-bottom: -4px; - margin-top: 20px; - margin-inline-end:-12px;margin-inline-start:-4px;padding-bottom: 24px -} - -.scrollerContent_c3474d { - padding: 4px -} - -.grid__78100 { - display: grid; - gap: var(--space-8); - grid-auto-rows: minmax(122px,1fr); - grid-template-columns: repeat(auto-fill,95px); - justify-content: start; - width: 100% -} - -.loadingContainer__78100 { - align-items: center; - display: flex; - height: 382px; - justify-content: center; - width: 100% -} - -.tooltipContent__78100 { - text-align: center -} - -.card__78100 { - border-radius: var(--radius-sm); - cursor: pointer; - display: flex; - height: 100%; - position: relative; - width: 100% -} - -.highlightOverlay__78100 { - background-color: var(--black-500); - border-radius: var(--radius-sm); - opacity: .5 -} - -.highlightOverlay__78100,.plusIconContainer__78100 { - align-items: center; - height: 100%; - inset-inline-start: 0; - justify-content: center; - pointer-events: none; - position: absolute; - top: 0; - width: 100%; - z-index: 2 -} - -.plusIconContainer__78100 { - display: flex -} - -.cardBackgroundImage__78100 { - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - overflow: hidden -} - -.cardBackgroundImage__78100,.cardImage__78100 { - height: 100%; - inset-inline: 0; - position: absolute; - top: 0; - width: 100% -} - -.cardImage__78100 { - bottom: 0; - -o-object-fit: cover; - object-fit: cover; - -o-object-position: center; - object-position: center -} - -.upsellContainer_e75cd3 { - width: 344px -} - -.contentContainer_e75cd3 { - padding: 0 -} - -.buttonContainer_e75cd3 { - margin-top: 16px -} - -.container__7a78a { - box-sizing: border-box; - width: 100%; - --custom-game-cover-width: 87px; - --custom-game-cover-height: 116px; - --custom-game-cover-grid-row-size: 4; - --custom-game-cover-gap: 16px; - --custom-placeholder-text-size: 20px; - --custom-placeholder-text-gap: 9px -} - -.container__7a78a.sizeMedium__7a78a { - --custom-game-cover-width: 72px; - --custom-game-cover-height: 96px; - --custom-game-cover-grid-row-size: 4; - --custom-game-cover-gap: 10px; - --custom-placeholder-text-size: 20px; - --custom-placeholder-text-gap: 9px -} - -.container__7a78a.sizeSmall__7a78a { - --custom-game-cover-width: 56px; - --custom-game-cover-height: 75px; - --custom-game-cover-grid-row-size: 3; - --custom-game-cover-gap: 9px; - --custom-placeholder-text-size: 12px; - --custom-placeholder-text-gap: 8px -} - -.placeholderDetailsCard__7a78a { - align-items: center; - display: flex; - gap: var(--custom-game-cover-gap); - pointer-events: none -} - -.placeholderText__7a78a { - display: flex; - flex: 1; - flex-direction: column; - gap: var(--custom-placeholder-text-gap); - justify-content: center; - width: 100% -} - -.placeholderBar__7a78a { - background-color: var(--background-mod-subtle); - border-radius: 8px; - height: var(--custom-placeholder-text-size) -} - -.placeholderBar__7a78a:first-child { - width: 60% -} - -.placeholderBar__7a78a:last-child { - width: 95% -} - -.placeholderCoverGrid__7a78a { - display: grid; - grid-template-columns: repeat(var(--custom-game-cover-grid-row-size),var(--custom-game-cover-width)); - justify-content: space-between; - pointer-events: none -} - -.placeholderCover__7a78a { - opacity: .2 -} - -.container__3f982 { - display: grid; - gap: 16px; - grid-template-columns: 3fr 2fr; - height: 165px; - width: 100% -} - -.container__3f982.sizeSmall__3f982 { - gap: 4px; - height: 99px -} - -.content__3f982 { - display: flex; - flex-direction: column; - gap: 16px; - padding: var(--custom-option-add-button-padding) -} - -.sizeSmall__3f982 .content__3f982 { - gap: 8px -} - -.header__3f982 { - background: var(--background-mod-subtle); - border-radius: var(--radius-sm); - height: 20px; - width: 50% -} - -.sizeSmall__3f982 .header__3f982 { - height: 12px -} - -.divider__3f982 { - border: 1px solid var(--background-mod-subtle) -} - -.stats__3f982 { - display: grid; - flex: 1; - gap: 8px 18px; - grid-template-columns: 1fr 1fr 1fr -} - -.sizeSmall__3f982 .stats__3f982 { - gap: 8px -} - -.stat__3f982 { - background: var(--background-mod-subtle); - border-radius: var(--radius-md) -} - -.imageContainer__3f982 { - overflow: hidden; - position: relative -} - -.previewImage__3f982 { - inset-inline-end: 0; - -webkit-mask: linear-gradient(270deg,#000 75%,transparent); - mask: linear-gradient(270deg,#000 75%,transparent); - opacity: .2; - position: absolute; - top: 0; - width: 100% -} - -.footer__3f982 { - align-items: center; - background: var(--background-mod-subtle); - display: flex; - gap: 4px; - justify-content: center; - padding: 12px -} - -.footerPlaceholder__3f982 { - height: 1lh; - width: 25ch -} - -.footerPlaceholder__3f982,.previewImagePlaceholder__3f982 { - background: var(--background-mod-subtle); - border-radius: var(--radius-sm) -} - -.previewImagePlaceholder__3f982 { - inset: var(--custom-option-add-button-padding); - position: absolute -} - -.addButtonContainer__4a011 { - background: var(--background-mod-subtle); - border-radius: 8px; - overflow: hidden; - transition: background-color .15s ease -} - -.custom-user-profile-theme.theme-light .addButtonContainer__4a011 { - background: var(--opacity-white-24) -} - -.addButtonContainer__4a011:has(.addButtonContent__4a011:hover) { - background: var(--background-mod-normal) -} - -.custom-user-profile-theme.theme-light .addButtonContainer__4a011:has(.addButtonContent__4a011:hover) { - background: var(--opacity-white-40) -} - -.addButtonContainer__4a011:has(.addButtonContent__4a011:active) { - background: var(--background-mod-strong) -} - -.custom-user-profile-theme.theme-light .addButtonContainer__4a011:has(.addButtonContent__4a011:active) { - background: var(--opacity-white-56) -} - -.addButtonContent__4a011 { - --custom-option-add-button-padding: 16px; - align-items: center; - cursor: pointer; - display: flex; - flex-direction: column; - justify-content: center; - position: relative; - text-align: center -} - -.addButtonContent__4a011.sizeSmall__4a011 { - --custom-option-add-button-padding: 12px -} - -.placeholderPadding__4a011 { - padding: var(--custom-option-add-button-padding) -} - -.overlay__4a011 { - flex-direction: column; - gap: 8px; - inset: 0; - position: absolute -} - -.addButton__4a011,.overlay__4a011 { - align-items: center; - display: flex; - justify-content: center -} - -.addButton__4a011 { - background: var(--custom-background-primary); - color: var(--text-default) -} - -.loading__4a011 { - pointer-events: none -} - -.title__4a011 { - align-items: center; - display: flex; - gap: 4px -} - -.icon__4a011 { - border-radius: 2px -} - -.options__60a25 { - display: flex; - flex-direction: column; - gap: 12px; - --custom-game-cover-width: 72px; - --custom-game-cover-height: 96px -} - -.container__37e49 { - align-items: center; - background: unset; - border-radius: 7px; - box-sizing: border-box; - container-type: inline-size; - display: flex; - flex-shrink: 0; - font-size: 14px; - font-weight: var(--font-weight-medium); - gap: var(--space-xs); - height: 56px; - justify-content: space-between; - margin-inline-start:-1px;min-height: var(--custom-rtc-account-height); - padding: calc(var(--custom-guild-list-padding) - var(--custom-panels-spacing) + 4px); - position: relative; - z-index: 1 -} - -.container__37e49 .avatar__37e49 { - cursor: pointer; - flex-shrink: 0 -} - -.container__37e49 .avatar__37e49:hover { - opacity: .8 -} - -.container__37e49 .redIcon__37e49 { - color: var(--icon-feedback-critical) -} - -.accountPopoutButtonWrapper__37e49 { - align-items: center; - border-radius: var(--radius-xs); - cursor: pointer; - display: flex; - flex: 1; - gap: var(--space-8); - margin: 0; - margin-inline-start:calc(var(--custom-panels-spacing) - var(--custom-guild-list-padding) + 4px);min-width: 112px; - padding: 5px calc(var(--custom-guild-list-padding) - var(--custom-panels-spacing) - 4px); - position: relative; - transition: background-color .2s ease -} - -.accountPopoutButtonWrapper__37e49.experiment__37e49 { - margin-inline-end:4px} - -.accountPopoutButtonWrapper__37e49:hover { - background-color: var(--background-mod-normal); - color: var(--interactive-text-hover) -} - -.accountPopoutButtonWrapper__37e49 .nameTag__37e49 { - margin: 0; - padding: 0 -} - -.accountPopoutButtonWrapper__37e49 .avatar__37e49:hover { - opacity: 1 -} - -.accountPopoutButton__37e49 { - height: 0; - inset: 0; - pointer-events: none; - position: absolute; - width: 0 -} - -.accountPopout__37e49 { - margin-inline-start:-8px} - -.nameTag__37e49 { - flex-grow: 1; - margin-inline-end:4px;min-width: 0; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.nameTag__37e49.canCopy__37e49 { - cursor: pointer -} - -.hasBuildOverride__37e49 { - border-radius: 50%; - bottom: 0; - inset-inline-end: 0; - position: absolute -} - -.buildOverrideButton__37e49 { - position: relative -} - -.usernameContainer__37e49 .panelTitleContainer__37e49 .panelSubtextContainer__37e49 { - align-items: center; - display: flex; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.panelTitleContainer__37e49 .withDisplayNameStyles__37e49 { - overflow: visible -} - -.copySuccess__37e49 { - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.copySuccess__37e49.godlike__37e49 { - color: var(--text-feedback-critical) -} - -.statusTooltip__37e49 { - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden -} - -.activityStatusText__37e49 { - font-weight: inherit; - line-height: inherit -} - -.customStatus__37e49 { - overflow: hidden; - text-overflow: ellipsis -} - -.strikethrough__37e49 { - color: var(--icon-feedback-critical) -} - -.emoji__37e49 { - height: 14px; - margin-inline-end:4px;width: 14px -} - -.micButtonParent__37e49 { - display: flex; - flex: direct; - margin-inline-end:0} - -.micButtonParent__37e49 .buttonChevron__37e49 { - align-items: center; - border-end-start-radius: 0; - border-start-start-radius: 0; - display: flex; - height: 32px; - justify-content: center; - line-height: 0; - margin-inline-start:1px;position: relative; - width: 16px -} - -.micButtonParent__37e49 .micButtonWithMenu__37e49 { - border-end-end-radius: 0; - border-start-end-radius: 0 -} - -.micButtonParent__37e49.popoutOpen__37e49 .buttonChevron__37e49,.micButtonParent__37e49.popoutOpen__37e49 .micButtonWithMenu__37e49,.micButtonParent__37e49:not(.hasColorGlow__37e49):hover .buttonChevron__37e49,.micButtonParent__37e49:not(.hasColorGlow__37e49):hover .micButtonWithMenu__37e49 { - background-color: var(--background-mod-muted) -} - -.micButtonParent__37e49.popoutOpen__37e49 .buttonChevron__37e49.popoutOpen__37e49,.micButtonParent__37e49.popoutOpen__37e49 .buttonChevron__37e49:hover,.micButtonParent__37e49.popoutOpen__37e49 .micButtonWithMenu__37e49.popoutOpen__37e49,.micButtonParent__37e49.popoutOpen__37e49 .micButtonWithMenu__37e49:hover,.micButtonParent__37e49:not(.hasColorGlow__37e49):hover .buttonChevron__37e49.popoutOpen__37e49,.micButtonParent__37e49:not(.hasColorGlow__37e49):hover .buttonChevron__37e49:hover,.micButtonParent__37e49:not(.hasColorGlow__37e49):hover .micButtonWithMenu__37e49.popoutOpen__37e49,.micButtonParent__37e49:not(.hasColorGlow__37e49):hover .micButtonWithMenu__37e49:hover { - background-color: var(--interactive-background-selected); - color: var(--interactive-text-hover) -} - -.micButtonParent__37e49:hover>button:hover { - background-color: var(--control-secondary-background-hover) -} - -.micButtonParent__37e49 .buttonChevronIcon__37e49 { - height: 16px; - width: 16px -} - -.buttons__37e49 { - display: flex; - gap: var(--space-8) -} - -.containerListenAlongVisible__37e49,.containerQuestBarVisible__37e49,.containerRtcOpened__37e49 { - border-start-end-radius: 0; - border-start-start-radius: 0 -} - -.panelTitleContainer__37e49 { - margin-bottom: 2px; - margin-top: -1px -} - -.refresh-fast-follow-avatars .container__37e49 { - padding: min(var(--space-xs),var(--space-8)) -} - -.theme-light .iconForeground__37e49 { - color: rgba(0,0,0,.9) -} - -.theme-dark .iconForeground__37e49 { - color: hsla(0,0%,100%,.9) -} - -.plated__37e49 { - background-color: unset!important -} - -.plated__37e49:hover { - background-color: var(--custom-nameplate)!important -} - -@container (max-width: 260px) { - .accountPopoutButtonWrapper__37e49 { - min-width: 0 - } -} - -@container (max-width: 100px) { - .nameTag__37e49 { - display: none - } -} - -.pttIndicator__37e49 { - border: 2px solid; - border-color: var(--status-danger); - border-radius: 50%; - bottom: 7px; - box-sizing: border-box; - height: 8px; - position: absolute; - right: 7px; - width: 8px -} - -.pttIndicator__37e49.speaking__37e49 { - border-color: var(--status-positive) -} - -.pttIndicator__37e49.latched__37e49 { - border-color: var(--status-warning) -} - -.enable-forced-colors.theme-dark .iconForeground__37e49,.enable-forced-colors.theme-light .iconForeground__37e49 { - color: currentColor -} - -.picker__7f2be { - display: flex; - flex-direction: column; - gap: 8px -} - -.themes__7f2be { - display: flex; - flex-wrap: wrap; - gap: 16px -} - -.inputs_c202a4 { - background: var(--background-base-lowest); - border-radius: 4px; - border-style: none; - color: var(--text-default); - width: 100px -} - -.hexInput_c202a4 { - align-items: center; - display: flex; - gap: 8px -} - -.previewsContainer_c202a4 { - flex: 0 0 auto; - padding-bottom: var(--space-16) -} - -.preview_c202a4 { - background: var(--background-base-lowest); - padding: 8px -} - -.preview_c202a4,.simArea_c202a4 { - border-radius: 8px; - width: 100% -} - -.simArea_c202a4 { - background: var(--background-base-lower); - display: flex; - flex-direction: column; - gap: 8px; - padding-bottom: 10px; - padding-inline-end:8px;padding-top: 10px; - position: relative -} - -.themePicker_c202a4 { - margin-top: 8px -} - -.controls_c202a4 { - flex-direction: column; - margin-bottom: 32px; - margin-top: 8px -} - -.controls_c202a4,.toggles_c202a4 { - display: flex; - gap: 8px -} - -.dmlist_c202a4 { - box-sizing: border-box; - display: flex; - flex: 1; - flex-direction: column; - overflow: hidden; - position: relative; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.dark_c202a4 { - background: #2b2d31 -} - -.light_c202a4 { - background: #f2f3f5 -} - -.overflowTooltip_c202a4 { - margin-inline-start:8px;max-width: 224px; - position: relative -} - -.overflowTooltip_c202a4.fullWidth_c202a4 { - max-width: 100% -} - -.clanTag_c202a4 { - margin-inline-start:4px} - -.main_c202a4 { - display: flex; - flex-wrap: wrap; - gap: var(--space-16) -} - -.flex_c202a4 { - align-items: center; - width: -moz-fit-content; - width: fit-content -} - -.buttons_c202a4,.flex_c202a4 { - display: flex; - gap: 8px -} - -.buttons_c202a4 { - flex: 0 0 250px; - flex-direction: column -} - -.uploaded_c202a4 { - align-items: center; - background: var(--background-base-lowest); - border-radius: 8px; - display: flex; - flex-direction: column; - gap: 8px; - height: -moz-fit-content; - height: fit-content; - padding: 8px; - position: relative; - width: 250px -} - -.uploadedImg_c202a4 { - max-height: 50px; - max-width: 80% -} - -.uploadedArea_c202a4 { - background: var(--background-base-lower); - border-radius: 8px; - flex-direction: column; - height: auto; - width: -moz-fit-content; - width: fit-content -} - -.uploadedArea_c202a4,.uploadedControls_c202a4 { - display: flex; - gap: 8px -} - -.xicon_c202a4 { - cursor: pointer; - inset-inline-end: 8px; - position: absolute; - top: 8px -} - -.previews_c202a4 { - display: flex; - flex-direction: column; - gap: 8px; - max-width: 432px; - min-width: 264px; - overflow-x: hidden; - overflow-y: auto; - position: relative; - resize: none -} - -.label_c202a4 { - margin-inline-start:8px} - -.rtcSim_c202a4 { - border-radius: 8px -} - -.rtcSim_c202a4>div>div { - padding-inline:10px!important} - -.theme-dark .rtcSim_c202a4 { - background: var(--background-gradient-lowest,var(--background-mod-normal)) -} - -.theme-light .rtcSim_c202a4 { - background: var(--background-gradient-low,var(--background-mod-normal)) -} - -.resizeHandle_c202a4 { - align-items: center; - background: var(--background-base-lowest); - border-radius: 4px; - bottom: 0; - cursor: ew-resize; - display: flex; - inset-inline-end: -2px; - justify-content: center; - position: absolute; - top: 0; - transition: background-color .2s ease; - width: 8px; - z-index: 10 -} - -.resizeHandle_c202a4:hover { - background: var(--background-base-lower) -} - -.resizeHandle_c202a4:after,.resizeHandle_c202a4:before { - background: var(--text-muted); - border-radius: 1px; - content: ""; - height: 20px; - width: 2px -} - -.resizeHandle_c202a4:after { - inset-inline-start: 50%; - margin-inline-start:2px;position: absolute; - top: 50%; - transform: translate(-50%,-50%) -} - -.row_a35735 { - align-items: center; - flex-direction: row; - flex-wrap: wrap; - gap: 8px; - margin-bottom: 16px -} - -.col_a35735,.row_a35735 { - display: flex; - justify-content: flex-start -} - -.col_a35735 { - flex-direction: column; - max-width: 100%; - min-width: 0 -} - -.grid_a35735 { - display: grid; - gap: 12px; - grid-template-columns: repeat(auto-fill,450px); - max-width: 100%; - overflow: hidden; - width: 100% -} - -.customEffectsGrid_a35735 { - display: grid; - gap: 12px; - grid-template-columns: repeat(auto-fill,400px) -} - -.end_a35735 { - align-items: flex-end; - justify-content: flex-end -} - -.section_a35735 { - margin: 12px 0 -} - -.root_a35735 { - margin-top: var(--space-24) -} - -.previewCard_a35735 { - background: var(--background-surface-high); - border-radius: 4px; - cursor: pointer; - height: 250px; - transition: box-shadow .2s ease-in-out -} - -.previewCard_a35735:hover { - box-shadow: var(--shadow-high) -} - -.previewCardImage_a35735 { - height: 200px -} - -.previewCardFooter_a35735 { - align-items: center; - background: var(--background-surface-high); - border-radius: 4px; - display: flex; - flex-direction: row; - height: 50px; - justify-content: space-between; - padding: 12px 8px -} - -.preview_a35735 { - position: relative -} - -.profileEffectPreviewContent_a35735 { - height: 100%; - inset: 0; - pointer-events: none; - position: absolute; - width: 100%; - z-index: 1 -} - -.userProfilePreview_a35735 { - margin-top: 20px; - width: 308px -} - -.dangerControls_a35735 { - display: flex; - justify-content: center; - margin: 12px 0 -} - -.options_a35735 { - display: flex; - flex-direction: row; - margin-top: 20px -} - -.checkBox_a35735 { - height: 20px; - width: 16px -} - -.uploadButton_a35735 { - background-color: green; - border-radius: 4px; - flex: 1px; - height: 38px; - padding-top: 8px; - position: relative; - text-align: center; - width: 200px -} - -.input_a35735 { - box-sizing: border-box; - margin-inline-start:0;max-width: 100%; - width: 100% -} - -.bottomControls_a35735 { - display: flex; - flex-direction: column; - gap: 8px; - margin: 12px 0 -} - -.shareSection_a35735 { - border: 1px solid var(--control-primary-border-default); - border-radius: 4px; - padding: 4px 12px -} - -.randomizedRules_a35735 { - background: var(--background-base-lower); - border-radius: 4px; - margin-top: 24px; - padding: 4px -} - -.randomizedRules_a35735 ol { - list-style: decimal; - margin-top: 8px; - padding-inline-start:24px} - -.randomizedRules_a35735 ol li { - color: var(--text-strong); - margin-top: 4px -} - -.warningText_a35735 { - color: var(--text-feedback-critical); - font-weight: 800 -} - -.layers_a35735 { - grid-template-columns: repeat(auto-fill,minmax(300px,1fr)); - margin-top: 8px -} - -.layerForm_a35735 { - background: var(--background-base-lower); - border-radius: 8px; - padding: 8px -} - -.layerPreviewContainer_a35735 { - align-items: center; - display: flex; - flex-direction: column; - gap: 8px -} - -.layerPreview_a35735 { - border: 1px solid var(--border-strong); - border-radius: 8px; - height: 250px -} - -.stillFramesContainer_a35735 { - display: flex; - gap: 12px; - margin-top: 12px -} - -.stillFramePreviewContainer_a35735 { - align-items: center; - display: flex; - flex-direction: column; - gap: 4px -} - -.stillFramePreviewWrapper_a35735 { - aspect-ratio: 450/880; - border: 1px solid var(--border-strong); - border-radius: 4px; - height: 250px; - margin-bottom: 4px; - overflow: hidden; - position: relative -} - -.stillFramePreviewClickable_a35735 { - cursor: pointer -} - -.full-motion .stillFramePreviewClickable_a35735 { - transition: transform .2s ease-in-out -} - -.full-motion .stillFramePreviewClickable_a35735:hover { - transform: scale(1.02) -} - -.stillFramePreviewClickable_a35735:focus { - outline: 2px solid var(--border-focus); - outline-offset: 2px -} - -.stillFramePlaceholder_a35735 { - z-index: 0 -} - -.stillFramePlaceholder_a35735,.stillFramePreview_a35735 { - height: 100%; - inset: 0; - -o-object-fit: cover; - object-fit: cover; - -o-object-position: top; - object-position: top; - position: absolute; - width: 100% -} - -.stillFramePreview_a35735 { - z-index: 1 -} - -.reducedMotionModalContent_a35735 { - align-items: flex-start; - display: flex; - justify-content: center; - min-height: 100%; - padding: 64px 24px 48px -} - -.reducedMotionModalCloseButton_a35735 { - inset-inline-end: 16px; - position: fixed; - top: 16px; - z-index: 10 -} - -.reducedMotionModalWrapper_a35735 { - aspect-ratio: 450/880; - border: 1px solid var(--border-strong); - border-radius: 8px; - max-width: 450px; - overflow: hidden; - position: relative; - width: 100% -} - -.reducedMotionModalPlaceholder_a35735 { - z-index: 0 -} - -.reducedMotionModalFrame_a35735,.reducedMotionModalPlaceholder_a35735 { - height: 100%; - inset: 0; - -o-object-fit: cover; - object-fit: cover; - -o-object-position: top; - object-position: top; - position: absolute; - width: 100% -} - -.reducedMotionModalFrame_a35735 { - z-index: 1 -} - -.pfxGrid_a35735 { - display: flex; - flex-wrap: wrap; - gap: 12px; - margin: 12px 0 -} - -.pfxListItem_a35735 { - aspect-ratio: 450/880; - background-position: 50%; - background-size: cover; - cursor: pointer; - height: auto; - justify-content: flex-end; - text-align: center; - width: 180px -} - -.pfxListItem_a35735,.pfxListItemFooter_a35735 { - display: flex; - flex-direction: column -} - -.pfxListItemFooter_a35735 { - background: rgba(0,0,0,.6); - height: 32px; - justify-content: center -} - -.container__85f5c { - gap: 32px; - padding: 24px -} - -.container__85f5c,.section__85f5c { - display: flex; - flex-direction: column -} - -.section__85f5c { - gap: var(--space-16) -} - -.section__85f5c:not(:last-child) { - border-bottom: 1px solid var(--border-muted) -} - -.inputSection__85f5c { - margin-bottom: 24px -} - -.inputLabel__85f5c { - margin-bottom: 8px -} - -.previewContainer__85f5c { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-16) -} - -.previewProductCardContainer__85f5c { - height: 246px -} - -.placeholder__85f5c { - border: 2px dashed var(--border-muted); - border-radius: 8px; - padding: 48px; - text-align: center -} - -.errorText__85f5c { - color: var(--text-feedback-critical) -} - -.errorText__85f5c,.successText__85f5c { - font-weight: 600; - margin-top: 4px -} - -.successText__85f5c { - color: var(--green-300) -} - -.mutedText__85f5c { - color: var(--text-subtle); - margin-top: 4px -} - -.loadingText__85f5c { - color: var(--text-strong); - margin-top: 4px -} - -.wrapper__7a068 { - flex: 1; - padding: var(--space-16) -} - -.wrapper__7a068 * { - box-sizing: border-box -} - -.notice__7a068 { - margin-top: var(--space-16) -} - -.noticeBody__7a068 { - gap: var(--space-8) -} - -.content__7a068,.noticeBody__7a068 { - display: flex; - flex-direction: column -} - -.content__7a068 { - padding-top: var(--space-16) -} - -.nav__7a068 { - flex-direction: row; - gap: var(--space-12) -} - -.banner__7a068,.nav__7a068 { - align-items: center; - display: flex -} - -.banner__7a068 { - background-position: 100%; - background-size: cover; - cursor: pointer; - height: 64px; - margin-top: 12px; - padding: 12px; - width: 100% -} - -.pfxBanner__7a068 { - background-image: url(https://cdn.discordapp.com/assets/content/100926ee18a28a73bf09b6dfeb3406b2c669808ff8cd820e4af6e93c6a5b184a.png) -} - -.nameplateBanner__7a068 { - background-image: url(https://cdn.discordapp.com/assets/content/f03942e6b99f99660e054f531489b8bf1ccc50d28242e893a8569929eee26fa7.png) -} - -.bundlesBanner__7a068 { - background-image: url(https://cdn.discordapp.com/assets/content/dec663d6f1a34d2c384a7c02d97a2ad2526a0cca19cb894ff9681ecf82b9c7b5.png) -} - -.locatorEntry__19739 { - background: var(--background-base-lower); - border-radius: 8px; - display: flex; - flex-direction: column; - padding: 8px -} - -.entryAuthor__19739 { - cursor: pointer; - padding: 4px 0 -} - -.content_a45742 { - gap: 32px; - height: 100%; - padding: 16px 8px; - width: 100% -} - -.content_a45742,.refreshContainer_a45742 { - align-items: stretch; - display: flex; - flex-direction: column -} - -.refreshContainer_a45742 { - padding: 8px -} - -.cell_a45742 { - padding: 0 8px -} - -.cellType_a45742 { - width: 40% -} - -.cellCount_a45742 { - width: 20% -} - -.content_ad8b04 { - align-items: stretch; - display: flex; - flex-direction: column; - gap: 32px; - height: 100%; - padding: 16px 8px; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text; - width: 100% -} - -.cell_ad8b04 { - padding: 0 8px -} - -.channelName_ad8b04 { - width: 40% -} - -.loadState_ad8b04 { - width: 20% -} - -.channelId_ad8b04 { - flex: 1 -} - -.container_d4630c { - display: flex; - flex-direction: column; - gap: var(--space-12); - padding: var(--space-24) -} - -.container_a51e6d { - background: var(--background-base-low); - border-top: 1px solid var(--background-base-lowest); - color: var(--text-default); - position: relative -} - -.resizeHandle_a51e6d { - cursor: ns-resize; - height: 8px; - inset-inline-start: 0; - position: absolute; - width: 100%; - z-index: 1 -} - -.subPanelContent_a51e6d { - height: 100%; - min-height: 0; - overflow: hidden -} - -[data-accessibility-violation] { - filter: url(#violation-overlay) -} - -.panel__583eb { - display: flex; - flex-direction: column -} - -.tableContainer__583eb { - flex: 1; - min-height: 0 -} - -.subPanel__583eb { - display: flex; - flex-direction: column -} - -.subPanelHeaderBar__583eb { - flex: 0 0 auto -} - -.inspectorContainer__583eb { - background: var(--background-base-lower); - flex: 1; - padding: 8px -} - -.traceContainer__583eb { - flex: 1; - padding: 8px -} - -.traceItem__583eb { - cursor: pointer; - display: block; - padding: 4px; - -webkit-font-smoothing: antialiased -} - -.traceItem__583eb:hover { - background: var(--interactive-background-hover) -} - -.accessibilityOverlays__583eb,.violationList__583eb { - display: flex; - flex-direction: column; - gap: 8px -} - -.accessibilityOverlays__583eb { - padding: 8px -} - -.row_e4169f { - display: flex -} - -.userCell_e4169f { - flex: 2 -} - -.affinityCell_e4169f { - flex: 1; - text-align: end -} - -.contentContainer__8820a { - gap: 8px -} - -.container__8820a,.contentContainer__8820a { - display: flex; - flex-direction: column -} - -.container__8820a { - gap: 32px; - margin: 16px 8px -} - -.panel_fb48ea { - display: flex; - flex-direction: column -} - -.tableContainer_fb48ea { - flex: 1; - min-height: 0; - overflow: auto -} - -.subPanel_fb48ea { - display: flex; - flex-direction: column; - overflow: scroll -} - -.toolbar_fb48ea { - align-items: center; - border-bottom: 1px solid var(--border-subtle); - display: flex; - flex-direction: row; - gap: var(--space-12); - padding: var(--space-8) -} - -.toolbarButton_fb48ea { - flex: 0 0 auto; - padding: 3px 4px 1px -} - -.toolbarButton_fb48ea:hover { - background: var(--background-base-lower) -} - -.toolbarDivider_fb48ea { - border-inline-start:1px solid var(--text-muted);height: 18px; - margin: 8px 0 -} - -.filters_fb48ea { - padding: 0 4px -} - -.filter_fb48ea { - align-items: center; - background-color: var(--background-mod-normal); - border-radius: 4px; - color: var(--interactive-text-active); - cursor: pointer; - display: inline-flex; - font-size: 12px; - line-height: 16px; - margin: 0 4px; - padding: 2px 4px; - width: auto -} - -.activeFilter_fb48ea { - background-color: var(--brand-500); - color: var(--white) -} - -.eventColumn_fb48ea { - flex: 2 -} - -.locationColumn_fb48ea { - flex: 1 -} - -.commonProperties_fb48ea,.subPanelHeaderBar_fb48ea { - flex: 0 0 auto -} - -.customPropertiesContainer_fb48ea { - flex: 1; - min-height: 0; - overflow: auto; - padding: 16px -} - -.customProperty_fb48ea { - align-items: center; - display: flex; - margin-bottom: 8px -} - -.customProperty_fb48ea:hover>.copyPropertyButton_fb48ea { - opacity: 1 -} - -.customPropertiesName_fb48ea { - color: var(--text-brand); - float: inline-start; - font-weight: var(--font-weight-medium); - padding-inline-end:4px} - -.copyPropertyButton_fb48ea { - color: var(--interactive-text-default); - height: 18px; - margin-inline-start:4px;opacity: 0; - transition: opacity .1s ease-in-out -} - -.copyPropertyButton_fb48ea:hover { - color: var(--interactive-text-hover); - cursor: pointer -} - -.emptyProperty_fb48ea { - color: var(--text-muted) -} - -.triggersEnable_fb48ea { - margin: 0 8px -} - -.headerTitle_fb48ea { - flex: 1 -} - -.headerTitle_fb48ea:hover .copyEventButton_fb48ea { - opacity: 1 -} - -.headerTitle_fb48ea .copyEventButton_fb48ea { - color: var(--interactive-text-default); - margin-inline-start:4px;margin-bottom: -4px; - opacity: 0; - transition: opacity .1s ease-in-out -} - -.headerTitle_fb48ea .copyEventButton_fb48ea:hover { - color: var(--interactive-text-hover); - cursor: pointer -} - -.panelContainer_fe44d5 { - display: flex; - flex: 1; - flex-direction: column; - min-height: 0; - padding-top: 12px -} - -.candidatesTable_fe44d5,.candidatesTableContainer_fe44d5 { - max-height: 200px -} - -.info_fe44d5 { - margin-inline-start:12px} - -.panelContainer__829c8 { - box-sizing: border-box; - display: flex; - flex-direction: column; - gap: var(--space-24); - padding: var(--space-24) -} - -.divider__829c8 { - margin: var(--space-24) 0 -} - -.searchBarContainer__829c8 { - margin-bottom: var(--space-24) -} - -.button_afb575 { - margin-bottom: 14px; - margin-top: 10px -} - -.container_afb575 { - overflow-y: scroll; - padding: 10px -} - -.rowsContainer_afb575 { - flex-basis: 100%; - flex-grow: 1; - flex-shrink: 0 -} - -.panel_a81334 { - display: flex; - flex-direction: column -} - -.toolbar_a81334 { - align-items: center; - border-bottom: 1px solid var(--border-subtle); - display: flex; - flex-direction: row; - gap: var(--space-8); - padding: var(--space-8) -} - -.searchBar_a81334 { - flex: 1 -} - -.pausedEvents_a81334 { - margin: 0 8px -} - -.tableContainer_a81334 { - flex: 1; - min-height: 0 -} - -.actionColumn_a81334 { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.totalTimeColumn_a81334 { - flex: 0 0 100px -} - -.storeNameColumn_a81334 { - flex: 1 -} - -.subPanel_a81334 { - display: flex; - flex-direction: column -} - -.subPanelHeaderBar_a81334 { - flex: 0 0 auto -} - -.inspectorContainer_a81334 { - background: var(--background-base-lower); - flex: 1; - padding: 8px -} - -.actionProperties_a81334 { - background: var(--background-base-low); - border-bottom: 1px solid var(--background-base-lowest); - box-shadow: var(--shadow-border),var(--shadow-high) -} - -.errorIcon_a81334 { - color: var(--icon-feedback-critical); - height: 22px; - margin-inline-end:8px;vertical-align: bottom; - width: 22px -} - -.errorToolbar_a81334 { - border-top: 1px solid var(--background-base-lowest); - flex: 0 0 auto -} - -.panel__578a4 { - display: flex; - flex-direction: column; - gap: 12px; - padding: 12px -} - -.container_bb6304 { - margin: var(--space-12) -} - -.formElement_bb6304 { - display: flex; - flex-direction: column; - margin-bottom: var(--space-32) -} - -.formRow_bb6304 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-8) -} - -.formDividerTitle_bb6304,.formRow_bb6304 { - margin: var(--space-12) 0 -} - -.container__23012 { - padding: 8px -} - -.header__23012 { - align-items: center; - display: flex; - justify-content: space-between -} - -.deleteEntitlementButton__23012 { - inset-inline-end: 4px; - margin: 8px; - position: absolute; - top: 4px -} - -.card__23012 { - align-items: flex-start; - background-color: var(--background-base-lower); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-medium); - display: flex; - flex-direction: column; - margin: var(--space-12) 0; - padding: var(--space-12); - position: relative -} - -.clickableGroup__23012 { - align-items: center; - border-radius: 3px; - display: flex; - height: var(--custom-button-button-md-height); - padding: 2px 16px -} - -.clickableGroup__23012:hover { - background-color: var(--background-base-lower); - cursor: pointer -} - -.clickableGroupContainer__23012 { - align-items: flex-end; - display: flex -} - -.section__23012 { - margin-bottom: var(--space-8) -} - -.buttonGroup__23012 { - display: flex; - flex-direction: row -} - -.buttonGroup__23012>button { - margin-inline-start:8px} - -.panelInner__9b718 { - padding: var(--space-8) -} - -.headerWrapper__9b718 { - align-items: center; - display: flex; - flex-direction: row; - justify-content: space-between -} - -.section__9b718 { - margin-bottom: var(--space-8) -} - -.buttons__9b718 { - align-items: center; - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: flex-start -} - -.buttons__9b718>* { - margin-inline-end:var(--space-8);margin-bottom: var(--space-8) -} - -.inputRow__9b718 { - align-items: center; - display: flex; - flex-direction: row; - justify-content: space-between; - width: 100% -} - -.input__9b718 { - margin-inline-end:var(--space-8);width: 100% -} - -.card__9b718 { - align-items: flex-start; - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-medium); - margin: var(--space-4) 0; - padding: var(--space-12); - position: relative -} - -.badgeContainer__9b718,.card__9b718 { - display: flex; - flex-direction: column -} - -.badgeContainer__9b718 { - align-items: flex-end; - inset-inline-end: var(--space-12); - position: absolute; - top: var(--space-12) -} - -.badge__9b718 { - background-color: var(--status-positive-background); - border-radius: var(--radius-round); - margin-bottom: 2px; - padding: 2px var(--space-8) -} - -.clickable__9b718 { - cursor: pointer -} - -.acked__9b718 { - background-color: var(--background-mod-muted) -} - -.expired__9b718 { - background-color: var(--background-feedback-critical) -} - -.redeemed__9b718 { - background-color: var(--control-primary-background-default) -} - -.row__9b718 { - align-items: center; - display: flex; - flex-direction: row; - gap: 10px; - margin: 2px 0 -} - -.nameRow__9b718 { - cursor: unset -} - -.nameRow__9b718:hover .trashIcon__9b718 { - opacity: 1 -} - -.idRow__9b718 { - cursor: pointer -} - -.idRow__9b718:hover { - text-decoration: underline; - text-decoration-color: var(--white) -} - -.icon__9b718 { - color: var(--white); - height: 12px; - margin-inline-start:-10px} - -.discount__9b718 .icon__9b718 { - color: var(--text-default) -} - -.noMargin__9b718 { - margin: 0 -} - -.trashIcon__9b718 { - bottom: 15px; - cursor: pointer; - height: 16px; - inset-inline-end: 15px; - position: absolute -} - -.deleteRow__9b718 { - display: flex; - justify-content: flex-end; - margin-top: var(--space-8); - width: 100% -} - -.deleteIcon__9b718 { - cursor: pointer; - height: 16px; - transition: color .2s ease -} - -.deleteIcon__9b718,.deleteIcon__9b718:hover { - color: var(--status-danger) -} - -.gradientWrapperTier0__9b718 { - background-image: linear-gradient(90deg,var(--premium-tier-0-purple-for-gradients) 0,var(--premium-tier-0-blue-for-gradients-2) 50%,var(--premium-tier-0-blue-for-gradients) 100%) -} - -.gradientWrapperTier2__9b718 { - border: 1px solid var(--premium-tier-2-pink-for-gradients); - outline: 1px solid var(--premium-tier-2-purple-for-gradients) -} - -.loadingContainer__9b718 { - align-items: center; - background-color: var(--opacity-black-68); - border-radius: var(--radius-lg); - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - display: flex; - justify-content: center; - opacity: 0; - pointer-events: none; - transition: opacity .2s ease-in-out -} - -.isLoading__9b718 { - opacity: 1; - pointer-events: all -} - -.container_bbaf4d { - padding: var(--space-24) -} - -.section_bbaf4d { - display: flex; - flex-direction: column; - gap: var(--space-16); - padding-bottom: var(--space-24) -} - -.buttonContainer_bbaf4d { - display: flex; - flex-direction: row; - flex-wrap: wrap; - gap: var(--space-8) -} - -.panel_c8030e { - display: flex; - flex-direction: column; - gap: 8px; - padding: 8px -} - -.switch_c8030e { - margin-bottom: 0 -} - -.accordionContainer__7e354 { - display: flex; - flex-direction: column; - text-align: start -} - -.accordionContainer__7e354.opened__7e354 .header__7e354 { - border-end-end-radius: 0; - border-end-start-radius: 0 -} - -.content__7e354 { - background-color: var(--background-mod-muted); - border-end-end-radius: var(--radius-sm); - border-end-start-radius: var(--radius-sm); - border-width: 0; - display: flex; - flex-direction: column; - gap: var(--space-12); - margin: 0; - max-height: 0; - overflow: hidden; - padding-top: 0; - visibility: hidden -} - -.full-motion .content__7e354 { - transition: max-height .5s cubic-bezier(.16,1,.3,1),padding-top .5s cubic-bezier(.16,1,.3,1),margin-top .5s cubic-bezier(.16,1,.3,1),visibility 0s linear .1s -} - -.content__7e354.opened__7e354 { - padding: var(--space-12); - visibility: visible -} - -.header__7e354 { - border-radius: var(--radius-sm); - cursor: pointer; - gap: var(--space-12); - padding: var(--space-12) -} - -.header__7e354,.headerIconWrapper__7e354 { - align-items: center; - background-color: var(--background-mod-subtle); - display: flex -} - -.headerIconWrapper__7e354 { - border-radius: var(--radius-xs); - height: 36px; - justify-content: center; - width: 36px -} - -.title__7e354 { - display: flex; - flex-direction: column; - gap: 2px -} - -.caret__7e354 { - color: var(--interactive-text-default); - margin-inline-start:auto} - -.full-motion .caret__7e354 { - transition: transform .5s cubic-bezier(.16,1,.3,1) -} - -.full-motion .caret__7e354.opened__7e354 { - transform: rotate(90deg) -} - -.container_dcb0ac { - height: 100%; - overflow: hidden -} - -.usersCount_dcb0ac { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: var(--radius-round); - display: flex; - height: var(--space-20); - justify-content: center; - padding: var(--space-4); - width: var(--space-20) -} - -.emailText_dcb0ac { - max-width: 225px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.iconText_dcb0ac { - align-items: center; - display: flex; - justify-content: center -} - -.clickable_dcb0ac { - cursor: pointer -} - -.clickableDisabled_dcb0ac { - cursor: not-allowed; - pointer-events: not-allowed -} - -.userInfoDisabled_dcb0ac { - opacity: .5 -} - -.poolsScroller_dcb0ac { - display: flex; - flex: 1; - flex-direction: column; - gap: var(--space-16); - min-height: 0 -} - -.accordion_dcb0ac { - width: 100% -} - -.buttonContainer_dcb0ac { - margin-inline-start:auto;margin-top: var(--space-16) -} - -.panelHeader__6edf0 { - margin-bottom: 16px -} - -.panelInner__6edf0 { - padding: var(--space-8) -} - -.panelRow__6edf0 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-12); - justify-content: flex-start; - margin-bottom: var(--space-16) -} - -.container__9b0bb { - padding: var(--space-24) -} - -.section__9b0bb { - padding-bottom: var(--space-24) -} - -.formSwitch__9b0bb,.header__9b0bb { - margin-bottom: var(--space-8) -} - -.hotspotsSection__16e9e { - padding: var(--space-24) -} - -.statusIcon__16e9e { - height: 16px; - width: 16px -} - -.checkmark__16e9e { - color: var(--status-positive) -} - -.checkmark__16e9e,.xmark__16e9e { -} - -.xmark__16e9e { - color: var(--icon-feedback-critical) -} - -.helpText__16e9e { - margin-bottom: 32px -} - -.hotspotSwitch__16e9e { - align-items: center; - display: flex; - margin: 8px 0 -} - -.overrideSelect__16e9e { - flex: 0 0 30% -} - -.krispTesterRow_fc5fb8 { - margin-bottom: var(--space-8) -} - -.innerPanel_fc5fb8 { - display: flex; - flex-direction: column; - gap: var(--space-8); - padding: var(--space-16) -} - -.dataAssurance__2a048 { - background-color: var(--background-feedback-info); - border: 1px solid var(--border-muted); - border-radius: var(--radius-md); - color: var(--text-feedback-info) -} - -.footer_bb3ce8 { - align-items: center; - margin-top: auto -} - -.buttonGroup_bb3ce8,.footer_bb3ce8 { - display: flex; - gap: var(--space-8) -} - -.buttonGroup_bb3ce8 { - margin-inline-start:auto;max-width: 100% -} - -.container__5667e { - box-sizing: border-box; - height: 100%; - width: 100% -} - -.heading__5667e { - margin-bottom: var(--space-24) -} - -.footerSpacer__5667e { - margin-top: var(--space-16) -} - -.container__44153 { - background-color: var(--background-surface-higher); - border-radius: 8px; - padding: var(--space-16) -} - -.title__44153 { - margin-bottom: var(--space-8) -} - -.description__44153 { - margin-bottom: var(--space-16) -} - -.container_de2e28 { - background-color: var(--background-surface-highest); - box-sizing: border-box; - height: 100%; - max-width: 240px; - width: 100% -} - -.title_de2e28 { - text-transform: capitalize -} - -.safetyFlow__300a2 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - position: relative -} - -.container__300a2 { - aspect-ratio: 750/520; - background-color: var(--background-surface-higher); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-border),var(--shadow-high); - max-height: 520px; - max-width: 750px; - overflow: hidden -} - -.body__300a2,.header__300a2 { - padding: var(--space-16) -} - -.error__300a2 { - margin-top: var(--space-8) -} - -.background__300a2 { - align-items: center; - display: flex; - justify-content: center; - min-height: 100vh; - overflow: auto; - position: relative; - width: 100vw -} - -.artwork__300a2 { - inset-inline-start: 0; - position: fixed; - top: 0 -} - -.artwork__300a2,.body__300a2 { - height: 100%; - width: 100% -} - -.interimBody__300a2 { - min-width: 320px -} - -.verification_dede4b { - background-color: var(--background-base-low); - height: 100%; - width: 100% -} - -.verification_dede4b .title_dede4b { - color: var(--text-default) -} - -.verification_dede4b .body_dede4b,.verification_dede4b .footer_dede4b { - color: var(--text-muted) -} - -.textContainer_dede4b { - text-align: center -} - -.container_dede4b { - width: 500px -} - -.container_dede4b.isMobile_dede4b { - width: 100% -} - -.footer_dede4b { - text-align: center -} - -.footerBullet_dede4b { - opacity: .3 -} - -.logoutButton_dede4b { - position: relative -} - -.verificationBlock_dede4b { - border-radius: 3px; - cursor: pointer; - padding-inline:20px} - -.verificationBlock_dede4b .image_dede4b { - background-position: 50%; - background-repeat: no-repeat; - background-size: 80%; - height: 160px; - width: 180px -} - -.verificationBlock_dede4b .body_dede4b { - color: var(--text-muted); - text-transform: uppercase -} - -.verificationBlock_dede4b:hover { - background-color: var(--background-base-lower) -} - -.theme-light .verificationBlock_dede4b { - border: 1px solid var(--primary-300) -} - -.theme-dark .verificationBlock_dede4b { - border: 1px solid var(--primary-700) -} - -.switch__9d40d { - margin-bottom: 0 -} - -.panel__9d40d { - display: flex; - flex-direction: column; - gap: 8px; - padding: 8px -} - -.divider__9d40d { - background-color: var(--border-subtle); - height: 1px; - margin: var(--space-8) 0 -} - -.inGameNuxContainer__9d40d { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px -} - -.search__9d40d { - flex: 1 -} - -.panel_b6e84c { - display: flex; - flex: 1; - flex-direction: column; - gap: 12px; - overflow: hidden; - padding: 12px -} - -.panel_b6e84c .panelContainer_b6e84c { - flex: 1; - max-height: 100% -} - -.panel_b6e84c .panelContainer_b6e84c .tableContainer_b6e84c { - flex: 1; - max-height: 70%; - overflow: hidden -} - -.subPanel_b6e84c { - display: flex; - flex-direction: column; - gap: 12px; - overflow: hidden; - padding: 0!important -} - -.toolbar_b6e84c { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px -} - -.toolbar_b6e84c.filtersToolbar_b6e84c { - gap: 12px -} - -.toolbar_b6e84c .paneOption_b6e84c { - border-bottom: 2px solid var(--control-secondary-background-default); - color: var(--text-subtle); - cursor: pointer; - padding: 8px 12px -} - -.toolbar_b6e84c .paneOption_b6e84c.activePaneOption_b6e84c { - border-bottom: 2px solid var(--control-primary-background-default); - color: var(--text-strong) -} - -.toolbar_b6e84c .copyAll_b6e84c { - align-items: center; - background: var(--background-mod-strong); - display: flex; - flex-direction: row; - gap: 6px -} - -.toolbar_b6e84c .copyAll_b6e84c,.toolbar_b6e84c .filter_b6e84c { - border-radius: 4px; - color: var(--text-subtle); - cursor: pointer; - padding: 4px 8px -} - -.toolbar_b6e84c .filter_b6e84c { - background: var(--control-secondary-background-default) -} - -.toolbar_b6e84c .filter_b6e84c.activeFilter_b6e84c { - background: var(--control-primary-background-default); - color: var(--text-strong) -} - -.toolbar_b6e84c .pollBreadcrumbs_b6e84c { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px -} - -.scroller_b6e84c { - max-height: 300px -} - -.panelGroup_b6e84c { - background: var(--background-base-lower); - border-radius: 8px; - display: flex; - flex-direction: column; - gap: 8px; - padding: 12px -} - -.panelGroup_b6e84c .panelHeader_b6e84c { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px -} - -.panelGroup_b6e84c .panelButton_b6e84c { - cursor: pointer -} - -.panelGroup_b6e84c span { - transition: color .4s ease-in-out -} - -.panelGroup_b6e84c .panelGroup_b6e84c { - background: var(--background-mod-normal); - gap: 4px; - padding: 8px -} - -.panelGroup_b6e84c .subPanel_b6e84c { - overflow: hidden -} - -.panelGroup_b6e84c .subPanel_b6e84c>div:first-child { - display: flex; - flex-direction: column; - overflow: scroll; - padding: 0 -} - -.stackTraceFunction_b6e84c { - color: var(--text-strong) -} - -.stackTraceLocation_b6e84c { - color: var(--text-muted) -} - -.panelContainer_b6e84c { - display: flex; - flex-direction: column; - gap: 12px -} - -.copyId_b6e84c { - padding: 0 -} - -.subPanelHeaderBar_b6e84c { - flex: 0 0 auto -} - -.subPanelScroller_b6e84c { - height: 100% -} - -.commonProperties_b6e84c { - flex: 0 0 auto -} - -.rightColumn_b6e84c { - text-align: end -} - -.nameColumn_b6e84c { - align-items: baseline; - display: flex; - flex: 1; - flex-direction: row; - gap: 8px; - max-width: 80%; - overflow: hidden; - padding-inline-end:8px;text-overflow: ellipsis; - white-space: nowrap -} - -.tableBarColumn_b6e84c { - margin-inline-end:4px} - -.headerIcon_b6e84c,.tableBarColumn_b6e84c { - height: calc(100% - 4px); - padding-top: 4px; - width: 20px -} - -.headerIcon_b6e84c { - margin-inline-end:2px} - -.tableBar_b6e84c { - height: 100%; - width: 2px -} - -.headerTitle_b6e84c { - flex: 1; - max-width: 80%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.headerTitle_b6e84c:hover .copyEventButton_b6e84c { - opacity: 1 -} - -.headerTitle_b6e84c .copyEventButton_b6e84c { - color: var(--interactive-text-default); - margin-inline-start:4px;margin-bottom: -4px; - opacity: 0; - transition: opacity .1s ease-in-out -} - -.headerTitle_b6e84c .copyEventButton_b6e84c:hover { - color: var(--interactive-text-hover); - cursor: pointer -} - -.scroller__44cf4 { - padding: var(--space-16) -} - -.formDivider__44cf4 { - margin: 10px 0 -} - -.loader__44cf4 { - align-items: center; - display: flex; - justify-content: center; - width: 100% -} - -.balanceWidgetPillContainer__44cf4 { - margin-top: 4px -} - -.countryOption_b9da6a:has(.header_b9da6a) { - background-color: var(--background-base-lowest); - cursor: default -} - -.countryContainer_b9da6a { - align-items: center; - display: flex -} - -.countryFlagEmoji_b9da6a { - height: 25px; - margin-inline-end:5px} - -.panel_ef5082 { - gap: 16px; - padding: 8px -} - -.panel_ef5082,.panelGroup_ef5082 { - display: flex; - flex-direction: column -} - -.panelGroup_ef5082 { - background: var(--background-base-lower); - border-radius: 8px; - gap: 4px; - padding: 12px -} - -.panelGroup_ef5082 span { - transition: color .4s ease-in-out -} - -.bottomPanelButton_ef5082 { - padding-top: 12px -} - -.topPanelToggle_ef5082 { - padding-bottom: 12px -} - -.secondaryInfoText_ef5082 { - margin-inline-start:8px} - -.container_f1a68f { - padding: 8px -} - -.header_f1a68f,.subheader_f1a68f { - width: -moz-fit-content; - width: fit-content -} - -.header_f1a68f { - margin: 16px 0 -} - -.subheader_f1a68f { - margin: 12px 0 8px -} - -.hasTooltip_f1a68f { - border-bottom: 2px dotted var(--text-muted) -} - -.code_f1a68f { - background-color: var(--background-base-lowest); - color: var(--text-subtle); - font-family: monospace; - font-size: 11px; - line-height: 14px; - overflow-x: auto; - padding: 8px; - scrollbar-color: var(--background-base-low) var(--background-base-lowest); - -webkit-user-select: text; - -moz-user-select: text; - user-select: text; - white-space: pre -} - -.slider_f1a68f { - width: 100% -} - -.colorInputContainer_f1a68f { - align-items: center; - display: flex; - gap: 4px; - margin: 8px 0 -} - -.removeButton_f1a68f { - color: var(--text-feedback-critical) -} - -.slider_f1a68f { - margin: -8px 0 -} - -.resetButton_f1a68f { - margin-top: 16px -} - -.explanation_f1a68f { - color: var(--text-muted) -} - -.tenureBadgeControls_f1a68f { - margin: 8px 0 16px -} - -.icon__2a871 { - color: var(--white); - height: 12px; - width: 12px -} - -.iconOuter__2a871 { - border-radius: 50%; - box-sizing: border-box; - height: 16px; - margin-top: 2px; - margin-inline-end:10px;padding: 1.5px; - width: 16px -} - -.iconCheck__2a871 { - background-color: var(--green-360) -} - -.iconCross__2a871 { - background-color: var(--primary-400) -} - -.scope__2a871 { - display: flex; - margin-top: 12px -} - -.scopeInner__2a871 { - box-sizing: border-box; - display: flex; - flex: 1; - flex-direction: column; - justify-content: center -} - -.panel__2a871 { - overflow: scroll -} - -.panelInner__2a871 { - padding: 8px -} - -.section__2a871 { - margin-bottom: 16px -} - -.buttonsContainer__5989f { - display: flex; - flex-direction: row; - flex-wrap: wrap; - gap: 8px; - margin-inline-start:12px;margin-top: 12px -} - -.instructionsList__5d321 { - margin-inline-start:16px} - -.instructionsList__5d321 li { - color: var(--text-subtle); - list-style-position: inside; - list-style-type: disc; - margin-bottom: 8px -} - -.modalContent__5d321 { - display: flex; - flex-direction: column; - gap: 8px -} - -.wrapper_dae93f { - display: flex; - flex-direction: column; - height: 100%; - position: relative -} - -.uploadModal_dae93f { - height: 190px; - inset: 50%; - pointer-events: none; - position: absolute; - transform: translate(-50%,-50%); - width: 310px; - z-index: 1000 -} - -.uploadModal_dae93f .inner_dae93f { - align-items: center; - background: var(--brand-500); - border: 2px dashed hsl(var(--white-hsl)/.4); - border-radius: 6px; - display: flex; - flex-direction: column; - width: 100% -} - -.uploadModal_dae93f .inner_dae93f .instructions_dae93f { - align-items: center; - display: flex; - flex-direction: column; - gap: 8px; - padding: 16px; - text-align: center -} - -.uploadModal_dae93f .inner_dae93f .questionIcon_dae93f { - margin: 0 4px; - vertical-align: top -} - -@keyframes uploadModalShake_dae93f { - 10%,90% { - transform: translate3d(-1px,0,0) - } - - 20%,80% { - transform: translate3d(2px,0,0) - } - - 30%,50%,70% { - transform: translate3d(-4px,0,0) - } - - 40%,60% { - transform: translate3d(4px,0,0) - } -} - -.errorModal_dae93f { - background-color: var(--red-430); - border-radius: 4px; - box-sizing: border-box; - opacity: 1; - padding: 10px; - transform: translateZ(0) -} - -.full-motion .errorModal_dae93f { - animation: uploadModalShake_dae93f .82s cubic-bezier(.36,.07,.19,.97) both -} - -.errorModal_dae93f .instructions_dae93f { - margin-bottom: 15px -} - -.container__77347 { - display: flex; - flex-direction: column; - height: 100% -} - -.headingContainer__77347 { - gap: 12px -} - -.headingContainer__77347,.previewToggleContainer__77347 { - align-items: center; - display: flex; - flex-direction: row -} - -.previewToggleContainer__77347 { - gap: 8px; - margin: 8px 0 -} - -.uploadInstructionsContainer__77347 { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px -} - -.uploadedFileListItem__77347 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px -} - -.ignoredFileListItem__77347 { - list-style-type: disc; - margin-inline-start:24px;padding: 4px 0 -} - -.removeFileButtonInnerContents__77347 { - height: 24px -} - -.previewSelector__77347 { - display: flex; - flex-direction: column; - gap: 8px -} - -.panelModeControl__77347 { - margin-bottom: 8px -} - -.panelModeControlOption__77347 { - padding: 4px 0 -} - -.validationSummary__77347 { - align-items: flex-start; - display: flex; - flex-direction: column; - gap: 8px -} - -.validationIssuesList__77347 { - list-style-type: disc; - margin-inline-start:24px} - -.validationIssuesList__77347 li { - margin: 8px 0 -} - -.container_ac8a82 { - align-items: center; - display: flex; - gap: 12px -} - -.container__0ccef { - align-items: center; - box-sizing: border-box; - display: flex; - gap: 8px; - width: 100% -} - -.selector__0ccef { - width: 250px -} - -.previewPanel__6dc2e { - display: flex; - flex-direction: column; - gap: 12px; - height: 100%; - overflow-y: auto; - padding: 12px; - position: relative -} - -.previewSection__6dc2e { - border: 1px solid var(--border-subtle); - border-radius: 8px; - padding: 12px -} - -.divider__6dc2e { - margin: 12px 0 -} - -.panel__777bf { - gap: var(--space-32); - overflow-y: auto; - padding: 12px -} - -.container__777bf,.panel__777bf { - display: flex; - flex-direction: column -} - -.container__777bf { - gap: var(--space-8) -} - -.panel__0939d { - display: flex; - flex-direction: column -} - -.toolbar__0939d { - align-items: center; - border-bottom: 1px solid var(--border-subtle); - display: flex; - flex-direction: row; - padding: var(--space-8) -} - -.tableContainer__0939d { - flex: 1; - min-height: 0 -} - -.storeNameColumn__0939d { - flex: 1 -} - -.subPanel__0939d { - display: flex; - flex-direction: column -} - -.subPanelHeaderBar__0939d { - flex: 0 0 auto -} - -.inspectorContainer__0939d { - background: var(--background-base-lower); - flex: 1; - padding: 8px -} - -.container__44c48 { - padding: var(--space-24) -} - -.panelHeader__86951 { - margin-bottom: 16px -} - -.panelInner__86951 { - padding: var(--space-16) -} - -.panelRow__86951 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-12); - justify-content: flex-start; - margin-bottom: var(--space-16) -} - -.panel__1d8b2 { - display: flex; - flex-direction: column; - gap: var(--space-16); - padding: var(--space-16) -} - -.sectionTitle__1d8b2 { - margin: 0 -} - -.headerSection__1d8b2 { - display: flex; - flex-direction: row; - gap: var(--space-8) -} - -.resultsSection__1d8b2 { - display: flex; - flex-direction: column; - gap: var(--space-12) -} - -.resultsContainer__1d8b2,.resultsSection__1d8b2 { - flex: 1; - min-height: 0 -} - -.resultsTable__1d8b2 { - display: flex; - flex-direction: column -} - -.tableHeader__1d8b2 { - font-size: 12px; - font-weight: var(--font-weight-semibold) -} - -.tableHeader__1d8b2,.tableRow__1d8b2 { - align-items: center; - display: flex; - padding: var(--space-8) var(--space-12) -} - -.tableRow__1d8b2 { - min-height: 36px -} - -.indexColumn__1d8b2 { - flex: 1 -} - -.indexColumn__1d8b2,.wordColumn__1d8b2 { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.wordColumn__1d8b2 { - flex: 2 -} - -.valueColumn__1d8b2 { - flex-grow: 1; - flex-shrink: 0; - font-family: var(--font-code); - text-align: end -} - -.emptyState__1d8b2 { - align-items: center; - display: flex; - flex: 1; - justify-content: center; - min-height: 200px; - text-align: center -} - -.container__5ce93 { - background: var(--background-base-low); - border: 1px solid var(--border-muted); - border-radius: var(--radius-md); - box-sizing: border-box; - margin-top: var(--space-16); - position: relative -} - -.gradientBackground__5ce93 { - border-radius: var(--radius-md); - overflow: hidden -} - -.contentContainer__5ce93 { - align-items: center; - display: flex; - gap: var(--space-12); - justify-content: space-between; - padding: var(--space-12) -} - -.graphicContainer__5ce93 { - pointer-events: none; - width: 54px -} - -.details__5ce93 { - display: flex; - flex: 1; - flex-direction: column; - gap: var(--space-4) -} - -.learnMore__5ce93 { - text-wrap: nowrap; - white-space: nowrap -} - -.betaBadge__5ce93 { - inset-inline-end: var(--space-12); - position: absolute; - top: -8px; - z-index: 10 -} - -.imageWrapper__5ce93 { - align-self: flex-start; - background: var(--background-base-lowest); - border-radius: var(--radius-md); - display: flex; - height: 64px; - overflow: hidden; - width: 64px -} - -.imageContainer__5ce93 { - align-items: center; - display: flex; - flex: 1; - justify-content: center -} - -.ctaContainer__5ce93 { - padding-inline-start:var(--space-26)} - -.title_ad9c52 { - font-weight: var(--font-weight-bold); - margin-bottom: 8px; - margin-top: 24px; - text-align: center -} - -.warningText_ad9c52 { - text-align: center -} - -.warningText_ad9c52,.warningTextMana_ad9c52 { - margin-bottom: 16px -} - -.linkCalloutContainer_ad9c52 { - align-items: center; - border: 1px solid var(--border-subtle); - border-radius: 8px; - justify-content: space-between; - margin-bottom: 12px; - max-height: 144px; - max-width: 498px; - min-width: min(408px,calc(100vw - 50px)); - overflow-wrap: anywhere; - padding: 16px -} - -.checkbox_ad9c52 { - margin-bottom: 24px -} - -.checkbox_ad9c52,.checkboxMana_ad9c52 { - overflow-wrap: anywhere -} - -.container__5d1be { - display: flex; - flex: 1; - flex-direction: column; - overflow: hidden; - padding: var(--space-12) 0 -} - -.content__5d1be,.tabBar__5d1be { - padding: 0 var(--space-12) -} - -.collapsablePane__7ed4d { - margin-bottom: var(--space-16); - width: 100% -} - -.fieldset__7ed4d { - width: 100% -} - -.collapsablePaneHeader__7ed4d { - align-items: center; - display: flex; - justify-content: space-between; - margin-bottom: var(--space-8) -} - -.collapsiblePaneList__7ed4d { - margin-bottom: 15px -} - -.collapsiblePaneList__7ed4d>li { - margin: 8px 0 -} - -.formSection__7ed4d { - border-bottom: 1px solid var(--primary-200); - padding-bottom: var(--space-16) -} - -.error__7ed4d,.formSection__7ed4d { - margin-top: var(--space-8) -} - -.subscriptionTextContainer__7ed4d { - margin-bottom: 15px -} - -.badge__7ed4d { - background-color: var(--control-primary-background-default); - border-radius: var(--radius-round); - margin-bottom: var(--space-4); - padding: 2px var(--space-8) -} - -.timeInput__7ed4d { - border: 1px solid var(--input-border-default); - border-radius: var(--radius-xs); - box-sizing: border-box; - max-width: 100%; - padding: var(--space-8); - width: 100% -} - -.periodText__7ed4d { - word-break: break-word -} - -.rewardTileContainer_d8917e { - position: relative; - transform-origin: left top -} - -.rewardTileSpacer_d8917e { - width: 56px -} - -.rewardTile_d8917e { - margin-inline-end:12px} - -.rewardHighlightLogoCTA_d8917e { - align-items: flex-start; - display: flex; - flex-direction: column; - justify-content: space-between; - z-index: 2 -} - -.rewardHighlightLogoCTALabel_d8917e { - align-items: center; - flex: 1; - flex-direction: row; - gap: 8px; - min-width: 0; - width: 100% -} - -.partnerBranding_d8917e { - margin-bottom: 4px -} - -.rewardHighlightCTA_d8917e { - pointer-events: none -} - -.rewardHighlightCTALabel_d8917e { - background-color: rgba(0,0,0,.329); - border-radius: var(--radius-xl); - max-width: 100%; - min-width: 0; - padding: 7px 12px; - pointer-events: none -} - -.isExpanded_d8917e { - flex-direction: row; - height: 24px -} - -.progressCircle__304c7 { - height: 12px; - inset-inline-start: 4px; - position: absolute; - top: 4px; - width: 12px -} - -.progress__304c7 { - transform: rotate(-90deg); - transform-origin: 50% 50% -} - -.wrapper__8c034 { - align-items: center; - border-radius: inherit; - display: flex; - position: absolute; - top: 0; - inset-inline: 0; - justify-content: space-between; - padding: 4px 8px; - pointer-events: none; - z-index: 4 -} - -.opaqueExpandedBackground__8c034 { - -webkit-backdrop-filter: blur(10px) brightness(.75); - backdrop-filter: blur(10px) brightness(.75); - border-radius: 8px; - height: 32px; - position: absolute; - top: 8px; - inset-inline: 8px -} - -.high-contrast-mode .opaqueExpandedBackground__8c034 { - -webkit-backdrop-filter: blur(10px) brightness(.25); - backdrop-filter: blur(10px) brightness(.25) -} - -.rewardHighlightWrapper__8c034 { - justify-content: flex-start -} - -.interactable__8c034 { - pointer-events: all -} - -.wreathIcon__8c034 { - height: 24px; - margin-inline:-4px 4px;width: 24px -} - -.rightContent__8c034 { - flex: 0 0 auto; - height: 20px; - margin-inline-start:12px;position: relative; - width: 20px -} - -.rightContent__8c034:before { - background-color: var(--opacity-white-40); - content: ""; - height: 12px; - inset-inline-start: 0; - position: absolute; - top: 50%; - transform: translate(-6px,-50%); - width: 1px -} - -.submenuWrapper__8c034 { - cursor: pointer; - transition: opacity .125s -} - -.submenuWrapper__8c034:focus,.submenuWrapper__8c034:hover { - opacity: .8 -} - -.submenuWrapper__8c034:active { - opacity: .7 -} - -.submenuIcon__8c034 { - height: 16px; - inset-inline-start: 2px; - position: absolute; - top: 2px; - width: 16px -} - -.white__8c034 { - color: var(--white) -} - -.promotedBadgeWrapper__8c034 { - display: flex; - flex: 0 0 auto; - inset-inline-end: 16px; - position: absolute; - top: 14px -} - -.logo__8c034 { - transition: opacity .125s -} - -.logo__8c034.clickable__8c034 { - cursor: pointer -} - -.logo__8c034.clickable__8c034:hover { - opacity: .8 -} - -.promotedBadge__8c034 { - cursor: pointer; - display: flex; - flex: 1 0 auto; - gap: 3px; - place-items: center; - transition: opacity .125s -} - -.promotedBadge__8c034:hover { - opacity: .8 -} - -.promotedBadgeIcon__8c034 { - flex: 0 0 auto; - height: 12px; - width: 12px -} - -.background__8c034 { - background-color: transparent -} - -.backgroundAnimation__8c034 { - opacity: .5 -} - -.backgroundWrapper__8c034 { - pointer-events: none -} - -.rewardHighlightLogotypeHeightBoost__8c034 { - height: 32px; - max-width: 130px -} - -.rewardHighlightLogotype__8c034 { - height: 24px -} - -.partnerBranding__8c034 { - transform-origin: left -} - -.ctaButtons_cc03e1 { - align-items: end; - display: flex; - flex-direction: row; - gap: 8px -} - -.cta_cc03e1 { - flex: 1; - margin-top: 12px -} - -.copyIcon_cc03e1 { - margin-inline-start:8px} - -.iconButtonLarge_cc03e1 { - align-items: center; - background-color: var(--interactive-background-hover); - display: flex; - flex: 1 0 0; - height: 32px; - justify-content: center; - padding: 8px -} - -.iconButtonLarge_cc03e1:hover { - background-color: var(--background-mod-subtle) -} - -.iconButton_cc03e1 { - background-color: var(--interactive-background-hover); - padding: 8px -} - -.iconButton_cc03e1:hover { - background-color: var(--background-mod-subtle) -} - -.iconButtonInner_cc03e1 { - align-items: center; - color: var(--interactive-text-default); - display: flex -} - -.wrapper_c4293b { - overflow: hidden; - pointer-events: none; - z-index: 4 -} - -.background_c4293b,.wrapper_c4293b { - border-radius: 8px 8px 0 0; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.background_c4293b { - background-image: linear-gradient(180deg,rgba(45,199,113,.05),rgba(45,199,113,.1)) -} - -.borders_c4293b { - border: 0 solid var(--text-feedback-positive); - border-radius: 8px 8px 0 0; - box-sizing: border-box; - content: ""; - height: 100%; - inset-inline-start: 0; - -webkit-mask-image: linear-gradient(115deg,rgba(0,0,0,.25),transparent 70%); - mask-image: linear-gradient(115deg,rgba(0,0,0,.25),transparent 70%); - pointer-events: none; - position: absolute; - top: 0; - width: 100%; - z-index: 1 -} - -.bordersTopLeft_c4293b { - border-left-width: 2px; - border-top-width: 2px -} - -.bordersBottom_c4293b { - border-bottom-width: 2px; - inset-inline-start: 2px -} - -.confetti_c4293b,.confettiWrapper_c4293b { - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 1 -} - -.confetti_c4293b { - border-radius: 8px 8px 0 0 -} - -.outer__146e2 { - align-items: center; - display: flex; - justify-content: center; - position: relative; - z-index: 2 -} - -.progressBar__146e2 { - overflow: visible; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - shape-rendering: geometricPrecision -} - -.progress__146e2 { - transform: rotate(-90deg); - transform-origin: 50% 50%; - transition: stroke-dashoffset .35s -} - -.inner__146e2 { - border-radius: 50%; - margin: 5px; - overflow: visible; - position: relative -} - -.coverContent__146e2 { - border-radius: 50%; - inset: 0; - position: absolute; - z-index: 999 -} - -.progressTextWrapper__146e2 { - align-items: center; - display: flex; - justify-content: center -} - -.progressTextOverlay__146e2 { - background-color: var(--background-base-lowest); - opacity: .75; - z-index: 1 -} - -.progressText__146e2 { - z-index: 2 -} - -.questProgressWrapper_ae7810 { - align-items: center; - display: flex; - gap: 8px -} - -.questProgressRewardTile_ae7810 { - box-shadow: var(--shadow-high); - display: block; - flex: 1 -} - -.questProgressRewardTile_ae7810.questProgressRewardTile_ae7810 { - border-radius: 50%; - height: 32px; - width: 32px -} - -.questProgressCopy_ae7810,.questProgressHint_ae7810 { - flex: 1 1 auto -} - -.questAcceptedHeader__1ba69 { - display: flex; - flex-direction: row -} - -.flex__1ba69 { - flex: 1 -} - -.headerText__1ba69 { - opacity: .7 -} - -.submenuWrapper__1ba69 { - cursor: pointer; - flex: 0 0 auto; - height: 20px; - opacity: .9; - position: relative; - transition: opacity .125s; - width: 20px -} - -.submenuWrapper__1ba69:focus,.submenuWrapper__1ba69:hover { - opacity: 1 -} - -.submenuWrapper__1ba69:active { - opacity: .7 -} - -.questAcceptedHeader__1ba69 .submenuWrapper__1ba69 { - height: 24px; - width: 24px -} - -.submenuIcon__1ba69 { - height: 16px; - inset-inline-start: 2px; - position: absolute; - top: 2px; - width: 16px -} - -.questAcceptedHeader__1ba69 .submenuIcon__1ba69 { - inset-inline-start: 0; - top: 0 -} - -.interactiveNormal__1ba69 { - color: var(--interactive-text-default) -} - -.divider__1ba69 { - background-color: var(--interactive-background-hover); - height: 1px; - margin-bottom: 12px; - width: 100% -} - -.contentCollapsed__7004f { - box-sizing: border-box; - display: flex; - height: 100%; - padding: 0; - width: 100%; - z-index: 2 -} - -.contentCollapsedWrapper__7004f { - flex: 1 1 auto; - position: relative; - z-index: 2 -} - -.contentCollapsedAccepted__7004f { - padding: 14px 12px -} - -.contentCollapsedExpanded__7004f { - pointer-events: none -} - -.divider__7004f { - background-color: var(--interactive-background-hover); - height: 1px; - margin-bottom: 12px; - width: 100% -} - -.connectConsoleButton_b69c3a { - fill: var(--interactive-text-default) -} - -.theme-light .playstationButtonConnected_b69c3a { - fill: var(--playstation) -} - -.theme-light .xboxButtonConnected_b69c3a { - fill: var(--xbox) -} - -.theme-dark .playstationButtonConnected_b69c3a { - fill: var(--interactive-text-active); - filter: drop-shadow(0 0 1px var(--opacity-white-80)) -} - -.theme-dark .xboxButtonConnected_b69c3a { - fill: var(--interactive-text-active); - filter: drop-shadow(0 0 1px var(--opacity-white-80)) -} - -.connectConsoleButtonWrapper_b69c3a:hover { - cursor: pointer -} - -.connectConsoleButtonWrapper_b69c3a:hover .connectConsoleButtonUnconnected_b69c3a { - fill: var(--interactive-text-hover) -} - -.connectConsoleButtonWrapper_b69c3a:active .connectConsoleButtonUnconnected_b69c3a { - fill: var(--interactive-text-active) -} - -.inlineConsoleConnectionDetails_b69c3a { - background-color: var(--interactive-background-hover); - border: 1px solid var(--interactive-background-hover); - border-radius: 8px; - display: flex; - flex-direction: column; - margin-top: 8px -} - -.inlineConsoleConnectionDetailsIcons_b69c3a { - align-items: center; - display: flex; - flex-direction: row -} - -.consoleIconDivider_b69c3a { - background-color: var(--border-subtle); - height: 12px; - margin: 0 8px; - width: 1px -} - -.inlineConsoleConnectionDetailsUpper_b69c3a { - align-items: center; - display: flex; - flex-direction: row; - justify-content: space-between; - padding: 8px 12px -} - -.inlineConsoleConnectionDetailsLower_b69c3a { - border-top: 1px solid var(--border-subtle); - display: flex; - flex-direction: column; - padding: 8px 12px -} - -.inlineConsoleConnectionDetailsLowerItem_b69c3a { - display: flex; - flex-direction: row; - justify-content: space-between -} - -.marginTop4px_b69c3a { - margin-top: 4px -} - -.showConnectionsButton_b69c3a { - color: var(--text-link); - margin-top: 4px -} - -.showConnectionsButton_b69c3a:hover { - cursor: pointer; - text-decoration: underline -} - -.wrapper_a99139 { - background-color: var(--message-background-hover); - border: 1px solid var(--interactive-background-hover); - border-radius: 8px; - margin-top: 12px; - padding: 12px -} - -.headingWrapper_a99139,.stepsWrapper_a99139:not(:last-child) { - margin-bottom: 12px -} - -.heading_a99139 { - flex: 1 1 auto -} - -.stepWrapper_a99139 { - display: flex; - gap: 10px -} - -.stepContent_a99139 { - padding-top: 3px -} - -.stepWrapperWithNextStep_a99139 .stepContent_a99139 { - margin-bottom: 12px -} - -.stepIndicator_a99139 { - align-items: center; - align-self: stretch; - display: flex; - flex-direction: column -} - -.stepIconWrapper_a99139 { - align-items: center; - border: 2px solid var(--interactive-background-selected); - border-radius: 20px; - display: flex; - height: 20px; - justify-content: center; - width: 20px -} - -.theme-light .stepIconWrapper_a99139 { - border: 2px solid var(--opacity-black-8) -} - -.stepWrapperComplete_a99139 .stepIconWrapper_a99139 { - background-color: var(--brand-500) -} - -.stepConnector_a99139 { - background-color: var(--interactive-background-selected); - flex: 1 1 auto; - position: relative; - width: 2px -} - -.stepWrapperComplete_a99139 .stepConnector_a99139:before { - background-color: var(--white); - bottom: 0; - box-shadow: 0 0 6px var(--opacity-white-80); - content: ""; - inset-inline: 0; - position: absolute; - top: 0 -} - -.theme-light .stepWrapperComplete_a99139 .stepConnector_a99139:before { - background-color: hsl(var(--brand-500-hsl)/.6); - box-shadow: 0 0 6px hsl(var(--brand-500-hsl)/.4) -} - -.stepIcon_a99139 { - display: block; - height: 14px; - width: 14px -} - -.opacity_50_a99139 { - opacity: .5 -} - -.rewardsListWrapper_a99139 { - display: flex; - flex-direction: column; - gap: 2px; - margin-top: 12px -} - -.rewardsListBody_a99139 { - list-style-type: none -} - -.rewardsListPremiumDurationExtension_a99139 { - -webkit-background-clip: text; - background-clip: text; - background-image: linear-gradient(90deg,var(--premium-tier-2-purple) 0,var(--premium-tier-2-pink) 100%); - color: transparent -} - -.microphoneUnit__7f3d5 { - border-top: 1px solid var(--border-subtle); - display: flex; - flex-direction: column; - margin-top: 16px -} - -.microphoneUnitHeader__7f3d5 { - align-items: center; - display: flex; - flex-direction: row; - height: 16px; - justify-content: flex-start; - margin-top: 16px -} - -.warningCircle__7f3d5 { - color: var(--text-feedback-warning); - margin-inline-end:4px} - -.errorCircle__7f3d5 { - color: var(--text-feedback-critical); - margin-inline-end:4px} - -.microphoneUnitRefreshIconWrapper__7f3d5 { - margin-inline-start:auto} - -:not(.disabled__7f3d5).microphoneUnitRefreshIconWrapper__7f3d5:hover { - color: var(--interactive-text-hover); - cursor: pointer -} - -:not(.disabled__7f3d5).microphoneUnitRefreshIconWrapper__7f3d5:active { - color: var(--interactive-text-active) -} - -.theme-light .microphoneUnitRefreshIconWrapper__7f3d5 { - color: var(--opacity-black-48) -} - -.theme-dark .microphoneUnitRefreshIconWrapper__7f3d5 { - color: var(--interactive-text-default) -} - -.microphoneUnitBodyText__7f3d5 { - margin-top: 8px -} - -.opacity_50__7f3d5 { - opacity: .5 -} - -.card__3eaf0 { - background-color: var(--message-background-hover); - border: 1px solid var(--interactive-background-hover); - border-radius: 8px; - margin-top: 12px; - padding: 12px -} - -.header__3eaf0 { - align-items: center; - display: flex; - margin-bottom: 6px -} - -.icon__3eaf0 { - color: var(--text-feedback-warning); - display: inline-block; - margin-inline-end:6px} - -.warningBody__3eaf0 { - text-wrap: pretty -} - -.buttonGroup_a3ac69 { - margin-top: 12px -} - -.contentExpanded__24e2c { - box-sizing: border-box; - height: auto; - inset-inline-start: 0; - padding: 8px; - position: absolute; - top: 0; - width: 100%; - z-index: 1 -} - -.contentExpandedAccepted__24e2c { - padding: 12px -} - -.questPromoContent__24e2c { - align-items: stretch; - display: flex; - flex-direction: column; - height: 100%; - position: relative; - z-index: 3 -} - -.divider__24e2c { - background-color: var(--interactive-background-hover); - height: 1px; - margin-bottom: 12px; - width: 100% -} - -.details__24e2c { - display: flex; - flex: 1 1 auto; - flex-direction: column; - justify-content: flex-end; - margin-bottom: 2px -} - -.detailsMargin__24e2c { - margin-top: 74px -} - -.detailsMarginVideoQuest__24e2c { - margin-top: 45px -} - -.white__24e2c { - color: var(--white) -} - -.videoQuestPreviewCont__24e2c { - aspect-ratio: 16/9; - border: 1px solid var(--themes-dark-background-background-modifier-accent,rgba(78,80,88,.48)); - border-radius: 8px; - box-sizing: border-box; - margin-top: 12px; - overflow: hidden; - position: relative; - width: 100% -} - -.assetBodyVideoPreviewMedia__24e2c { - cursor: pointer; - height: 100%; - width: 100% -} - -.assetBodyVideoPreviewVideo__24e2c { - height: 102%; - inset-inline-start: -1px; - -o-object-fit: cover; - object-fit: cover; - opacity: 0; - position: absolute; - top: -1px; - transition: opacity .3s; - width: 102% -} - -.assetBodyVideoPreviewVisible__24e2c { - opacity: 1 -} - -.heroAssetWrapper__24e2c { - border-radius: var(--radius-sm) var(--radius-sm) 0 0; - height: 100%; - inset-inline: 0; - overflow: hidden; - position: absolute; - top: 0; - width: 100%; - z-index: 1 -} - -.high-contrast-mode .heroAssetWrapper__24e2c { - border: 1px solid var(--border-subtle) -} - -.legibilityGradient__24e2c { - background-image: linear-gradient(180deg,transparent,#fff); - bottom: 0; - height: 100%; - inset-inline-start: 0; - position: absolute; - width: 100%; - z-index: 1 -} - -.legibilityGradientDark__24e2c { - background-image: linear-gradient(180deg,transparent,#000) -} - -.heroAsset__24e2c { - height: 100%; - -o-object-fit: cover; - object-fit: cover; - position: absolute; - width: 100%; - z-index: 0 -} - -.cta__24e2c { - flex: 0 0 auto; - margin-top: 8px; - pointer-events: none -} - -.contentInteractable__24e2c .cta__24e2c { - pointer-events: all -} - -.iconButton__24e2c { - background-color: var(--interactive-background-hover); - padding: 8px -} - -.iconButton__24e2c:hover { - background-color: var(--background-mod-subtle) -} - -.ctaClaimReward__24e2c { - margin-bottom: 4px -} - -.title__24e2c { - margin-bottom: 2px -} - -.rewardTile__24e2c { - margin-bottom: 8px; - margin-top: 74px -} - -.hiddenRewardTile__24e2c { - pointer-events: none; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - visibility: hidden; - z-index: -1 -} - -.questAcceptedContentHeading__24e2c { - align-items: center; - display: flex -} - -.questProgressRewardTile__24e2c { - border-radius: 4px; - box-shadow: var(--shadow-high); - display: block; - flex: 0 0 auto; - height: 28px; - margin-inline-end:8px;width: 28px -} - -.questAcceptedContentHeading__24e2c .questProgressRewardTile__24e2c { - height: 32px; - width: 32px -} - -.questAcceptedContentCopySubheading__24e2c { - opacity: .7 -} - -.questAcceptedContent__24e2c .description__24e2c { - margin-bottom: 2px; - text-shadow: none -} - -.description__24e2c a:hover { - opacity: .8; - text-decoration: none -} - -.promotedBadgeWrapper__24e2c { - flex: 0 0 auto; - margin-inline-end:4px;padding-inline-end:8px;position: relative -} - -.promotedBadge__24e2c { - cursor: pointer; - display: flex; - flex: 1 0 auto; - gap: 3px; - opacity: .8; - place-items: center; - transition: opacity .125s -} - -.promotedBadge__24e2c:hover { - opacity: 1 -} - -.promotedBadgeWrapper__24e2c:after { - background-color: var(--opacity-white-40); - content: ""; - height: 12px; - inset-inline-end: 0; - position: absolute; - top: 50%; - transform: translateY(-50%); - width: 1px -} - -.promotedBadgeIcon__24e2c { - flex: 0 0 auto; - height: 12px; - width: 12px -} - -.postEnrollmentBackground__24e2c { - background-color: var(--home-background); - border-radius: 8px 8px 0 0; - border-top: 2px solid var(--background-mod-subtle); - height: 100%; - inset-inline-start: 0; - overflow: hidden; - position: absolute; - top: 0; - width: 100% -} - -.postEnrollmentBackgroundCollapsed__24e2c { - pointer-events: none; - z-index: 3 -} - -.postEnrollmentBackgroundExpanded__24e2c { - z-index: -1 -} - -.previewPlayButtonCont__24e2c { - align-items: center; - -webkit-backdrop-filter: blur(5px); - backdrop-filter: blur(5px); - background: hsla(0,0%,100%,.08); - border-radius: 275px; - cursor: pointer; - display: flex; - height: 44px; - inset-inline-start: 50%; - justify-content: center; - position: absolute; - top: 50%; - transform: translate(-50%,-50%); - width: 44px -} - -.previewPlayButton__24e2c { - cursor: pointer; - height: 22px; - width: 22px -} - -.contentCollapsedBackgroundWrapper__24e2c { - border-radius: var(--radius-sm) var(--radius-sm) 0 0; - height: 50%; - inset-inline-start: 0; - overflow: hidden; - pointer-events: none; - position: absolute; - top: 0; - transform: translateZ(0); - width: 100%; - z-index: 2 -} - -.contentCollapsedBackgroundWrapper__24e2c:after { - background-color: var(--background-base-low); - bottom: 0; - content: ""; - display: block; - inset-inline: 0; - opacity: 0; - position: absolute; - top: 0 -} - -.contentCollapsedBackground__24e2c { - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - bottom: -20px; - filter: brightness(.6) saturate(110%); - inset-inline: -20px; - position: absolute; - top: -20px -} - -.blur__24e2c { - bottom: -50px; - filter: blur(30px) brightness(.6) saturate(110%); - inset-inline: -50px; - top: -50px -} - -.wrapper__0d616 { - background: var(--background-base-lower); - border-start-end-radius: var(--radius-sm); - border-start-start-radius: var(--radius-sm); - isolation: isolate; - pointer-events: all; - position: relative; - z-index: 0 -} - -.theme-dark .wrapper__0d616 { - background: var(--background-gradient-low,var(--background-base-lower)) -} - -.theme-light .wrapper__0d616 { - background: var(--background-gradient-lower,var(--background-base-lower)) -} - -.wrapperInvisible__0d616 { - opacity: 0 -} - -.wrapperVisible__0d616 { - z-index: 1 -} - -.contentWrapper__0d616 { - background-color: transparent; - z-index: 2 -} - -.contentWrapper__0d616,.contentWrapper__0d616:after { - border-radius: 8px 8px 0 0; - height: 100%; - width: 100% -} - -.contentWrapper__0d616:after { - content: ""; - inset-inline-start: 0; - opacity: .5; - pointer-events: none; - position: absolute; - top: 0; - transition: box-shadow .125s -} - -.wrapperVisible__0d616 .contentWrapperExpanded__0d616:after { - box-shadow: 0 -4px 10px rgba(0,0,0,.2),0 -2px 4px rgba(0,0,0,.3) -} - -.contentWrapperAccepted__0d616 { - border-radius: 8px 8px 0 0 -} - -.mask__0d616 { - margin-inline:-20px;margin-top: -100vh; - padding-inline:20px;padding-top: 100vh; - pointer-events: none -} - -.content__0d616,.mask__0d616 { - overflow: hidden -} - -.overlay__0d616 { - bottom: 0; - inset-inline: 0; - position: absolute; - top: 0 -} - -.contentHeader__5b400 { - justify-content: space-between; - margin-bottom: 8px -} - -.contentHeader__5b400,.refreshWrapper__5b400 { - align-items: center; - display: flex -} - -.refreshWrapper__5b400 { - color: var(--text-muted); - cursor: pointer; - gap: 4px -} - -.refreshWrapper__5b400:hover { - color: var(--interactive-text-hover) -} - -.disabled__5b400 { - color: var(--text-muted); - pointer-events: none -} - -.colorTransition__5b400 { - transition: color .15s ease-in-out -} - -.accountsWrapper__5b400 { - display: flex; - flex-direction: column; - gap: 8px -} - -.connectionRow__5b400 { - border: 1px solid var(--border-subtle); - border-radius: 8px; - overflow: hidden -} - -.connectionRowHeader__5b400 { - align-items: center; - background-color: var(--background-base-low); - display: flex; - justify-content: space-between; - padding: 10px 12px -} - -.connectionRowHeaderError__5b400 { - background-color: hsl(var(--background-base-low-hsl)/.7) -} - -.connectionRowHeaderContent__5b400 { - align-items: center; - display: flex; - gap: 8px -} - -.success__5b400 { - color: var(--text-feedback-positive) -} - -.error__5b400 { - color: var(--text-feedback-critical) -} - -.gameTile__5b400 { - height: 16px; - width: 16px -} - -.errorRow__5b400 { - align-items: center; - border-top: 1px solid var(--border-subtle); - display: flex; - gap: 8px; - padding: 10px 12px -} - -.errorsContainer__5b400 { - background: linear-gradient(to bottom,hsl(var(--red-430-hsl)/.15),hsl(var(--red-430-hsl)/.02)) -} - -.tooltip__5b400 { - align-items: center; - display: flex; - flex-direction: column; - gap: 2px -} - -.placeholderElement__6e847 { - background-color: var(--background-base-low); - border-radius: 8px -} - -.container_b5b7aa { - background-color: var(--background-mod-normal); - border-radius: 8px 8px 0 0; - box-sizing: border-box; - height: 150px; - position: relative; - width: 100% -} - -.positionContentOverBackground_b5b7aa { - inset-inline-start: 0; - position: absolute; - top: 0; - z-index: 2 -} - -.contents_b5b7aa,.positionContentOverBackground_b5b7aa { - display: flex; - flex-direction: column; - height: 100%; - width: 100% -} - -.contents_b5b7aa { - box-sizing: border-box; - padding: 12px -} - -.heroAssetWrapper_b5b7aa { - align-items: center; - border-radius: 8px 8px 0 0; - display: flex; - height: 100%; - inset-inline-start: 0; - justify-content: center; - overflow: hidden; - position: absolute; - top: 0; - width: 100%; - z-index: 1 -} - -.heroAssetCont_b5b7aa,.overlay_b5b7aa { - aspect-ratio: 1320/370 -} - -.overlay_b5b7aa { - border-radius: 8px 8px 0 0; - inset-inline-start: 0; - max-height: 150px; - position: absolute; - top: 0; - width: 100%; - z-index: 1 -} - -.darkThemeGradient_b5b7aa { - background: linear-gradient(to top,var(--neutral-64) 0,hsl(var(--neutral-64-hsl)/.95) 20.31%,hsl(var(--neutral-64-hsl)/0) 100%) -} - -.darkerThemeGradient_b5b7aa { - background: linear-gradient(to top,var(--primary-660) 0,hsl(var(--primary-660-hsl)/.95) 20.31%,hsl(var(--primary-660-hsl)/0) 100%) -} - -.midnightThemeGradient_b5b7aa { - background: linear-gradient(to top,var(--neutral-91) 0,hsl(var(--neutral-91-hsl)/.95) 20.31%,hsl(var(--neutral-91-hsl)/0) 100%) -} - -.lightThemeGradient_b5b7aa { - background: linear-gradient(to top,var(--black) 0,hsl(var(--black-hsl)/.5) 20.31%,hsl(var(--black-hsl)/0) 100%) -} - -.topRow_b5b7aa { - display: flex; - flex-direction: row; - gap: 16px; - justify-content: flex-end -} - -.pills_b5b7aa { - margin-inline-end:auto} - -.pill_b5b7aa,.pills_b5b7aa { - align-items: center; - display: flex -} - -.pill_b5b7aa { - background-color: var(--white); - border-radius: var(--radius-round); - box-shadow: var(--shadow-low); - padding: 2px 8px -} - -.pillBrand_b5b7aa { - background: var(--brand-500) -} - -.eyebrowText_b5b7aa { - font-size: 12px; - font-weight: 700 -} - -.utilButtonWrapper_b5b7aa { - align-items: center; - -webkit-backdrop-filter: blur(8px); - backdrop-filter: blur(8px); - background-color: hsl(var(--primary-700-hsl)/.48); - border-radius: 50%; - box-shadow: var(--shadow-medium); - cursor: pointer; - display: flex; - height: 24px; - justify-content: center; - justify-self: flex-end; - transition: background-color .15s ease-in-out; - width: 24px -} - -.utilButtonWrapper_b5b7aa:active,.utilButtonWrapper_b5b7aa:focus,.utilButtonWrapper_b5b7aa:hover { - background-color: hsl(var(--primary-700-hsl)/.7) -} - -.theme-light .container_b5b7aa { - background-color: var(--black) -} - -.theme-light .utilButtonWrapper_b5b7aa { - background-color: hsl(var(--primary-100-hsl)/.66) -} - -.theme-light .utilButtonWrapper_b5b7aa:active,.theme-light .utilButtonWrapper_b5b7aa:focus,.theme-light .utilButtonWrapper_b5b7aa:hover { - background-color: hsl(var(--primary-100-hsl)/1) -} - -.theme-dark .container_b5b7aa { - background-color: var(--neutral-64) -} - -.theme-darker .container_b5b7aa { - background-color: var(--primary-660) -} - -.theme-midnight .container_b5b7aa { - background-color: var(--neutral-91) -} - -.reduce-motion .utilButtonWrapper_b5b7aa { - transition: none -} - -.utilButtonIcon_b5b7aa { - color: var(--interactive-text-active); - height: 16px; - width: 16px -} - -.partnerBranding_b5b7aa { - justify-self: flex-end; - margin-bottom: 4px; - margin-top: auto -} - -.partnerLogotypes_b5b7aa { - align-items: flex-end; - display: flex; - height: auto; - justify-content: flex-start; - max-height: 34px; - max-width: 121px; - width: -moz-min-content; - width: min-content -} - -.bottomRow_b5b7aa,.hints_b5b7aa { - align-items: center; - display: flex; - flex-direction: row; - justify-content: space-between -} - -.hints_b5b7aa { - border-top: 1px solid var(--border-muted); - padding: 12px -} - -.hintsContainer_b5b7aa { - gap: 8px -} - -.hintsContainer_b5b7aa,.promotedByRow_b5b7aa { - align-items: center; - display: flex; - flex-direction: row -} - -.verifiedIcon_b5b7aa { - margin: 0 3px -} - -.container__960ef { - border-top: 1px solid var(--border-subtle); - box-sizing: border-box; - flex-direction: row; - gap: 8px; - justify-content: center; - min-height: 62px; - padding: 12px; - width: 100% -} - -.container__960ef,.ctaInner__960ef { - align-items: center; - display: flex -} - -.ctaInner__960ef { - gap: 6px -} - -.ctaItem__960ef { - display: flex; - flex: 1 1 0; - flex-direction: row; - gap: 8px; - min-width: 0 -} - -.button__960ef { - flex: 1 1 0; - min-width: 0 -} - -.platformSelectorPrimaryLabel__960ef { - align-items: center; - display: flex; - gap: 8px -} - -.platformSelectorPrimaryLabelIcon__960ef { - height: 16px; - width: 16px -} - -.platformSelectorPrimary__960ef { - flex: 1 1 auto; - width: 100% -} - -.platformSelectorSecondary__960ef { - flex: 1 1 0; - height: 40px; - min-height: auto; - min-width: 0 -} - -.videoQuestPlayIcon__960ef { - height: 16px; - width: 16px -} - -.container__956c6 { - align-items: flex-start; - background-color: var(--background-surface-high); - display: flex; - flex-direction: column; - justify-content: flex-start; - transition: max-height .2s ease-out,height .2s ease-in; - width: 100%; - z-index: 2 -} - -.reduce-motion .container__956c6 { - transition: none -} - -.height__956c6 { - height: 166px; - max-height: 166px -} - -.hoveredHeight__956c6 { - height: 180px; - max-height: 180px -} - -.rewardDescriptionContainer__956c6 { - box-sizing: border-box; - display: flex; - flex-direction: row; - justify-content: flex-start; - overflow: hidden; - position: relative; - width: 100% -} - -@keyframes pulse__956c6 { - 0% { - opacity: 0 - } - - 10% { - opacity: 0 - } - - 30% { - opacity: 1 - } - - 80% { - opacity: 1 - } - - to { - opacity: 0 - } -} - -.confetti__956c6 { - position: absolute; - transform: scale(1.4); - z-index: 1 -} - -.completionAnimation__956c6 { - animation: pulse__956c6 2s ease-in-out both; - background-image: linear-gradient(180deg,rgba(45,199,113,.05),rgba(45,199,113,.1)); - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.progressWrapper__956c6 { - flex-direction: row; - height: 80px; - min-width: 80px; - width: 80px -} - -.circularRewardTileWrapper__956c6,.progressWrapper__956c6 { - align-items: center; - box-sizing: border-box; - display: flex; - justify-content: center -} - -.circularRewardTileWrapper__956c6 { - height: 66px; - width: 66px -} - -.circularQuestRewardTileAsset__956c6 { - align-items: center; - border-radius: 50%; - display: flex; - height: 64px; - justify-content: center; - min-width: 64px; - width: 64px -} - -.questRewardTileAsset__956c6 { - border-radius: 9.412px; - height: 80px; - min-width: 80px; - width: 80px -} - -.assetWrapper__956c6 { - justify-content: center; - padding-block:12px;padding-inline:12px 0;z-index: 999 -} - -.assetWrapper__956c6,.textContainer__956c6 { - display: flex; - flex-direction: column -} - -.textContainer__956c6 { - overflow: hidden; - padding: 12px -} - -.justifyCenter__956c6 { - justify-content: center -} - -.questName__956c6 { - margin-bottom: 4px -} - -.header__956c6 { - display: inline; - margin-bottom: 2px -} - -.orbsBalanceIcon__956c6 { - height: 14px; - margin-inline-end:3px;position: relative; - top: 1px -} - -.description__956c6 { - color: var(--text-default); - font-size: 12px; - line-height: 16px -} - -.textOverflowBlur__956c6 { - background: linear-gradient(transparent 50%,var(--background-base-lower) 100%); - bottom: 0; - inset-inline: 0; - pointer-events: none; - position: absolute; - top: 0 -} - -.container_cec934 { - align-items: center; - background-color: var(--background-surface-high); - border: 1px solid var(--border-muted); - border-radius: 10px; - box-shadow: var(--shadow-low); - box-sizing: border-box; - display: flex; - flex-direction: column; - height: auto; - max-height: 316px; - min-width: 336px; - overflow: hidden; - width: 100% -} - -.container_f50ddc { - padding: var(--space-16) -} - -.heading_f50ddc { - margin-bottom: 16px -} - -.subheading_f50ddc { - margin-bottom: 8px -} - -.componentPreviews_f50ddc { - display: grid; - grid-template-columns: 1fr; - grid-gap: 16px -} - -.componentPreviewWrapper_f50ddc { - border-width: 0 -} - -.componentPreviewWrapperBordered_f50ddc { - border-width: 1px; - padding: 24px -} - -.componentPreview_f50ddc { - isolation: isolate; - position: relative -} - -.questBarPreviewWrapper_f50ddc { - display: flex; - flex-direction: column; - height: 180px; - place-content: flex-end -} - -.questBarPreview_f50ddc,.questBarPreviewWrapper_f50ddc { - position: relative; - width: 362px -} - -.questChannelCallHeaderPreview_f50ddc>div { - margin: 0; - position: absolute; - top: 0 -} - -.toggleSwitch_f50ddc { - align-items: center; - padding: 0 16px -} - -.fields_f50ddc { - display: grid; - grid-template-columns: 1fr 1fr; - grid-gap: 16px; - margin-bottom: 24px -} - -.fullWidthField_f50ddc { - grid-column: 1/-1 -} - -.fieldsVertical_f50ddc { - display: grid; - grid-gap: 16px; - grid-template-columns: 100% -} - -.swatchContainer_f50ddc { - border-radius: 4px; - cursor: pointer; - display: flex; - height: 48px; - place-content: center; - place-items: center; - width: 48px -} - -.swatchIcon_f50ddc { - color: var(--white); - opacity: .5; - transition: opacity .125s -} - -.swatchContainer_f50ddc:hover .swatchIcon_f50ddc { - opacity: .75 -} - -.errorBoundary_f50ddc { - display: flex; - flex-direction: column; - gap: 12px; - padding: var(--space-16); - place-content: center; - place-items: center -} - -.errorBoundaryIcon_f50ddc { - color: var(--text-strong); - height: 24px; - width: 24px -} - -.headingWithTooltip_f50ddc { - display: flex; - gap: 4px; - place-items: center -} - -.errorMessageWrapper_f50ddc { - background-color: var(--background-base-lower)!important; - margin-top: 12px; - padding: 12px -} - -.errorMessage_f50ddc { - color: var(--text-default); - text-align: start; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.questTile_f50ddc { - max-width: 450px -} - -.wideField_f50ddc { - grid-column: span 2 -} - -.fileUpload__8b203 { - box-sizing: border-box; - display: grid; - gap: var(--space-8); - grid-template-columns: auto 1fr auto; - min-height: var(--control-input-height-md); - padding-block:0;padding-inline:var(--space-8) var(--space-4)} - -.fileUploadInput__8b203 { - cursor: default -} - -.taskPresetSelect_a8dbb2 { - padding-bottom: 10px; - padding-top: 10px -} - -.detailsList__85e60 { - margin-top: 16px -} - -.detailsList__85e60,.nestedDetailsList__85e60 { - display: flex; - flex-direction: column; - gap: 8px -} - -.nestedDetailsList__85e60 { - margin-inline-start:32px} - -.detailsRow__85e60 { - align-items: center; - display: flex; - gap: 8px -} - -.panel__85e60 { - display: flex; - flex-direction: column; - gap: var(--space-24); - padding: var(--space-16) var(--space-24) -} - -.subtitle__85e60 { - margin-bottom: 16px -} - -.statusText__85e60 { - margin-top: 8px -} - -.inputWithButtonRow__85e60 { - align-items: center; - display: flex; - gap: 8px -} - -.container_aa69cc { - display: flex; - flex: 1; - flex-direction: column; - overflow: hidden; - padding: var(--space-12) 0 -} - -.content_aa69cc,.tabBar_aa69cc { - padding: 0 var(--space-12) -} - -.root_fb0b3f { - display: flex; - flex-direction: column; - gap: var(--space-16); - padding: var(--space-24) -} - -.settingNode_fb0b3f,.tree_fb0b3f { - display: flex; - flex-direction: column; - gap: var(--space-4) -} - -.settingNode_fb0b3f { - margin-inline-start:var(--space-16)} - -.settingNodeChildren_fb0b3f { - border-inline-start: 2px solid var(--primary-500); - display: flex; - flex-direction: column; - gap: var(--space-4) -} - -.headerBar_fb0b3f { - align-items: center; - cursor: pointer; - display: flex -} - -.headerCaret_fb0b3f { - color: var(--interactive-text-default); - margin-inline-start:var(--space-4)} - -.holidayText_fb0b3f { - display: inline -} - -.holidayLetter_fb0b3f { - color: var(--white) -} - -.holidayLetter_fb0b3f:nth-child(2n) { - color: var(--status-positive) -} - -.holidayLetter_fb0b3f:nth-child(8n+1) { - animation: lightsA_fb0b3f 2.1s ease-in-out infinite backwards; - animation-delay: calc(var(--custom-letter-index)*.02s) -} - -.holidayLetter_fb0b3f:nth-child(8n+3) { - animation: lightsB_fb0b3f 1.8s ease-in-out infinite backwards; - animation-delay: calc(var(--custom-letter-index)*.03s) -} - -.holidayLetter_fb0b3f:nth-child(8n+5) { - animation: lightsC_fb0b3f 2.4s ease-in-out infinite backwards; - animation-delay: calc(var(--custom-letter-index)*.01s) -} - -.holidayLetter_fb0b3f:nth-child(8n+7) { - animation: lightsD_fb0b3f 1.9s ease-in-out infinite backwards; - animation-delay: calc(var(--custom-letter-index)*.04s) -} - -@keyframes lightsA_fb0b3f { - 0%,to { - color: var(--red-400) - } - - 25% { - color: var(--yellow-300) - } - - 50% { - color: var(--blue-400) - } - - 75% { - color: var(--white) - } -} - -@keyframes lightsB_fb0b3f { - 0%,to { - color: var(--yellow-300) - } - - 20% { - color: var(--brand-500) - } - - 45% { - color: var(--red-400) - } - - 70% { - color: var(--teal-400) - } -} - -@keyframes lightsC_fb0b3f { - 0%,to { - color: var(--blue-400) - } - - 30% { - color: var(--white) - } - - 55% { - color: var(--orange-400) - } - - 80% { - color: var(--brand-500) - } -} - -@keyframes lightsD_fb0b3f { - 0%,to { - color: var(--brand-500) - } - - 15% { - color: var(--red-400) - } - - 40% { - color: var(--yellow-300) - } - - 65% { - color: var(--teal-400) - } - - 85% { - color: var(--blue-400) - } -} - -.rootMigratedLetter_fb0b3f { - animation: rootMigratedA_fb0b3f 1.5s ease-in-out infinite backwards; - animation-delay: calc(var(--custom-letter-index)*.08s) -} - -.rootMigratedLetter_fb0b3f:nth-child(3n+2) { - animation: rootMigratedB_fb0b3f 1.8s ease-in-out infinite backwards; - animation-delay: calc(var(--custom-letter-index)*.06s) -} - -.rootMigratedLetter_fb0b3f:nth-child(3n) { - animation: rootMigratedC_fb0b3f 1.3s ease-in-out infinite backwards; - animation-delay: calc(var(--custom-letter-index)*.1s) -} - -@keyframes rootMigratedA_fb0b3f { - 0%,to { - color: var(--yellow-300) - } - - 50% { - color: var(--white) - } -} - -@keyframes rootMigratedB_fb0b3f { - 0%,to { - color: var(--white) - } - - 50% { - color: var(--yellow-300) - } -} - -@keyframes rootMigratedC_fb0b3f { - 0%,to { - color: var(--yellow-300) - } - - 30% { - color: var(--white) - } - - 70% { - color: var(--yellow-300) - } -} - -.panel_cd7bbe { - display: flex; - flex-direction: column; - gap: var(--space-24); - padding: var(--space-16) var(--space-24) -} - -/*# sourceMappingURL=f37149369742ded7.css.map*/ diff --git a/discord-html-copy/css/web.cf1abc4e5994931f.css b/discord-html-copy/css/web.cf1abc4e5994931f.css deleted file mode 100644 index 583c879..0000000 --- a/discord-html-copy/css/web.cf1abc4e5994931f.css +++ /dev/null @@ -1,70519 +0,0 @@ -.defaultColor__4bd52 { - color: var(--text-default) -} - -.lineClamp1__4bd52 { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.lineClamp2Plus__4bd52 { - display: -webkit-box; - -webkit-box-orient: vertical; - overflow: hidden -} - -.selectable__4bd52 { - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.tabularNumbers__4bd52 { - font-variant-numeric: tabular-nums -} - -.heading-sm\/normal_cf4812 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.heading-sm\/normal_cf4812.fontScaling_cf4812 { - font-size: .875rem -} - -.heading-sm\/medium_cf4812 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.heading-sm\/medium_cf4812.fontScaling_cf4812 { - font-size: .875rem -} - -.heading-sm\/semibold_cf4812 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.heading-sm\/semibold_cf4812.fontScaling_cf4812 { - font-size: .875rem -} - -.heading-sm\/bold_cf4812 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.heading-sm\/bold_cf4812.fontScaling_cf4812 { - font-size: .875rem -} - -.heading-sm\/extrabold_cf4812 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 800; - line-height: 1.2857142857142858 -} - -.heading-sm\/extrabold_cf4812.fontScaling_cf4812 { - font-size: .875rem -} - -.heading-md\/normal_cf4812 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.heading-md\/normal_cf4812.fontScaling_cf4812 { - font-size: 1rem -} - -.heading-md\/medium_cf4812 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.heading-md\/medium_cf4812.fontScaling_cf4812 { - font-size: 1rem -} - -.heading-md\/semibold_cf4812 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.heading-md\/semibold_cf4812.fontScaling_cf4812 { - font-size: 1rem -} - -.heading-md\/bold_cf4812 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.heading-md\/bold_cf4812.fontScaling_cf4812 { - font-size: 1rem -} - -.heading-md\/extrabold_cf4812 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 800; - line-height: 1.25 -} - -.heading-md\/extrabold_cf4812.fontScaling_cf4812 { - font-size: 1rem -} - -.heading-lg\/normal_cf4812 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.heading-lg\/normal_cf4812.fontScaling_cf4812 { - font-size: 1.25rem -} - -.heading-lg\/medium_cf4812 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.heading-lg\/medium_cf4812.fontScaling_cf4812 { - font-size: 1.25rem -} - -.heading-lg\/semibold_cf4812 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.heading-lg\/semibold_cf4812.fontScaling_cf4812 { - font-size: 1.25rem -} - -.heading-lg\/bold_cf4812 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.heading-lg\/bold_cf4812.fontScaling_cf4812 { - font-size: 1.25rem -} - -.heading-lg\/extrabold_cf4812 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 800; - line-height: 1.2 -} - -.heading-lg\/extrabold_cf4812.fontScaling_cf4812 { - font-size: 1.25rem -} - -.heading-xl\/normal_cf4812 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xl\/normal_cf4812.fontScaling_cf4812 { - font-size: 1.5rem -} - -.heading-xl\/medium_cf4812 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xl\/medium_cf4812.fontScaling_cf4812 { - font-size: 1.5rem -} - -.heading-xl\/semibold_cf4812 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xl\/semibold_cf4812.fontScaling_cf4812 { - font-size: 1.5rem -} - -.heading-xl\/bold_cf4812 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xl\/bold_cf4812.fontScaling_cf4812 { - font-size: 1.5rem -} - -.heading-xl\/extrabold_cf4812 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xl\/extrabold_cf4812.fontScaling_cf4812 { - font-size: 1.5rem -} - -.heading-xxl\/normal_cf4812 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xxl\/normal_cf4812.fontScaling_cf4812 { - font-size: 2rem -} - -.heading-xxl\/medium_cf4812 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xxl\/medium_cf4812.fontScaling_cf4812 { - font-size: 2rem -} - -.heading-xxl\/semibold_cf4812 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xxl\/semibold_cf4812.fontScaling_cf4812 { - font-size: 2rem -} - -.heading-xxl\/bold_cf4812 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xxl\/bold_cf4812.fontScaling_cf4812 { - font-size: 2rem -} - -.heading-xxl\/extrabold_cf4812 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xxl\/extrabold_cf4812.fontScaling_cf4812 { - font-size: 2rem -} - -.eyebrow_cf4812 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - letter-spacing: .02em; - line-height: 1.3333333333333333; - text-transform: uppercase -} - -.eyebrow_cf4812.fontScaling_cf4812 { - font-size: .75rem -} - -.heading-deprecated-12\/normal_cf4812 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/normal_cf4812.fontScaling_cf4812 { - font-size: .75rem -} - -.heading-deprecated-12\/medium_cf4812 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/medium_cf4812.fontScaling_cf4812 { - font-size: .75rem -} - -.heading-deprecated-12\/semibold_cf4812 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/semibold_cf4812.fontScaling_cf4812 { - font-size: .75rem -} - -.heading-deprecated-12\/bold_cf4812 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/bold_cf4812.fontScaling_cf4812 { - font-size: .75rem -} - -.heading-deprecated-12\/extrabold_cf4812 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 800; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/extrabold_cf4812.fontScaling_cf4812 { - font-size: .75rem -} - -.redesign\/heading-18\/bold_cf4812 { - font-family: var(--font-display); - font-size: 18px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/heading-18\/bold_cf4812.fontScaling_cf4812 { - font-size: 1.125rem -} - -.text-xxs\/normal_cf4812 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 400; - line-height: 1.2 -} - -.text-xxs\/normal_cf4812.fontScaling_cf4812 { - font-size: .625rem -} - -.text-xxs\/medium_cf4812 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 500; - line-height: 1.2 -} - -.text-xxs\/medium_cf4812.fontScaling_cf4812 { - font-size: .625rem -} - -.text-xxs\/semibold_cf4812 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 600; - line-height: 1.2 -} - -.text-xxs\/semibold_cf4812.fontScaling_cf4812 { - font-size: .625rem -} - -.text-xxs\/bold_cf4812 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 700; - line-height: 1.2 -} - -.text-xxs\/bold_cf4812.fontScaling_cf4812 { - font-size: .625rem -} - -.text-xs\/normal_cf4812 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.text-xs\/normal_cf4812.fontScaling_cf4812 { - font-size: .75rem -} - -.text-xs\/medium_cf4812 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.text-xs\/medium_cf4812.fontScaling_cf4812 { - font-size: .75rem -} - -.text-xs\/semibold_cf4812 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.text-xs\/semibold_cf4812.fontScaling_cf4812 { - font-size: .75rem -} - -.text-xs\/bold_cf4812 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.text-xs\/bold_cf4812.fontScaling_cf4812 { - font-size: .75rem -} - -.text-sm\/normal_cf4812 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.text-sm\/normal_cf4812.fontScaling_cf4812 { - font-size: .875rem -} - -.text-sm\/medium_cf4812 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.text-sm\/medium_cf4812.fontScaling_cf4812 { - font-size: .875rem -} - -.text-sm\/semibold_cf4812 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.text-sm\/semibold_cf4812.fontScaling_cf4812 { - font-size: .875rem -} - -.text-sm\/bold_cf4812 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.text-sm\/bold_cf4812.fontScaling_cf4812 { - font-size: .875rem -} - -.text-md\/normal_cf4812 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.text-md\/normal_cf4812.fontScaling_cf4812 { - font-size: 1rem -} - -.text-md\/medium_cf4812 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.text-md\/medium_cf4812.fontScaling_cf4812 { - font-size: 1rem -} - -.text-md\/semibold_cf4812 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.text-md\/semibold_cf4812.fontScaling_cf4812 { - font-size: 1rem -} - -.text-md\/bold_cf4812 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.text-md\/bold_cf4812.fontScaling_cf4812 { - font-size: 1rem -} - -.text-lg\/normal_cf4812 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.text-lg\/normal_cf4812.fontScaling_cf4812 { - font-size: 1.25rem -} - -.text-lg\/medium_cf4812 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.text-lg\/medium_cf4812.fontScaling_cf4812 { - font-size: 1.25rem -} - -.text-lg\/semibold_cf4812 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.text-lg\/semibold_cf4812.fontScaling_cf4812 { - font-size: 1.25rem -} - -.text-lg\/bold_cf4812 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.text-lg\/bold_cf4812.fontScaling_cf4812 { - font-size: 1.25rem -} - -.redesign\/message-preview\/normal_cf4812 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/normal_cf4812.fontScaling_cf4812 { - font-size: .9375rem -} - -.redesign\/message-preview\/medium_cf4812 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/medium_cf4812.fontScaling_cf4812 { - font-size: .9375rem -} - -.redesign\/message-preview\/semibold_cf4812 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/semibold_cf4812.fontScaling_cf4812 { - font-size: .9375rem -} - -.redesign\/message-preview\/bold_cf4812 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/bold_cf4812.fontScaling_cf4812 { - font-size: .9375rem -} - -.redesign\/channel-title\/normal_cf4812 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.375 -} - -.redesign\/channel-title\/normal_cf4812.fontScaling_cf4812 { - font-size: 1rem -} - -.redesign\/channel-title\/medium_cf4812 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.375 -} - -.redesign\/channel-title\/medium_cf4812.fontScaling_cf4812 { - font-size: 1rem -} - -.redesign\/channel-title\/semibold_cf4812 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.375 -} - -.redesign\/channel-title\/semibold_cf4812.fontScaling_cf4812 { - font-size: 1rem -} - -.redesign\/channel-title\/bold_cf4812 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.375 -} - -.redesign\/channel-title\/bold_cf4812.fontScaling_cf4812 { - font-size: 1rem -} - -.display-sm_cf4812 { - font-family: var(--font-headline); - font-size: 20px; - font-weight: 800; - line-height: 1 -} - -.display-sm_cf4812.fontScaling_cf4812 { - font-size: 1.25rem -} - -.display-md_cf4812 { - font-family: var(--font-headline); - font-size: 34px; - font-weight: 800; - line-height: 1.0588235294117647 -} - -.display-md_cf4812.fontScaling_cf4812 { - font-size: 2.125rem -} - -.display-lg_cf4812 { - font-family: var(--font-headline); - font-size: 44px; - font-weight: 800; - line-height: .9545454545454546 -} - -.display-lg_cf4812.fontScaling_cf4812 { - font-size: 2.75rem -} - -.code_cf4812 { - font-family: var(--font-code); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.code_cf4812.fontScaling_cf4812 { - font-size: .75rem -} - -.defaultColor__5345c { - color: var(--text-strong) -} - -:root { - --expand-structural-duration: 100ms; - --expand-fade-duration: 200ms; - --expand-easing-function: ease-out; - --collapse-structural-duration: 150ms; - --collapse-fade-duration: 150ms; - --collapse-easing-function: ease-in -} - -.container__35859 { - border-radius: var(--radius-sm); - box-sizing: border-box; - display: grid; - font-weight: 500; - grid-template-rows: 1fr; - padding: 8px; - transition-duration: var(--expand-structural-duration); - transition-property: grid-template-rows,padding-bottom,padding-top,border-width; - transition-timing-function: var(--expand-easing-function); - width: 100% -} - -.container__35859.hidden__35859 { - border-width: 0; - grid-template-rows: 0fr; - padding-bottom: 0; - padding-top: 0; - transition-delay: var(--collapse-fade-duration); - transition-duration: var(--collapse-structural-duration); - transition-property: grid-template-rows,padding-bottom,padding-top,border-width; - transition-timing-function: var(--collapse-easing-function) -} - -.innerContainer__35859 { - display: flex; - min-height: 0; - opacity: 1; - overflow: hidden; - transition-delay: var(--expand-structural-duration); - transition-duration: var(--expand-fade-duration); - transition-property: opacity; - transition-timing-function: var(--expand-easing-function) -} - -.container__35859.hidden__35859 .innerContainer__35859 { - opacity: 0; - transition-delay: 0s; - transition-duration: var(--collapse-fade-duration); - transition-property: opacity; - transition-timing-function: var(--collapse-easing-function) -} - -.icon__35859 { - flex-shrink: 0; - height: 20px; - width: 20px -} - -.iconDiv__35859 { - display: flex -} - -.text__35859 { - align-self: center; - flex: 1; - margin-left: var(--space-8) -} - -.text__35859 p { - margin: 0 -} - -.text__35859 p~p { - margin-top: 8px -} - -.positive__35859 { - background: var(--background-feedback-positive); - border: 1px solid var(--icon-feedback-positive); - color: var(--text-feedback-positive) -} - -.positive__35859 .icon__35859 { - color: var(--icon-feedback-positive) -} - -.warning__35859 { - background: var(--background-feedback-warning); - border: 1px solid var(--icon-feedback-warning); - color: var(--text-feedback-warning) -} - -.warning__35859 .icon__35859 { - color: var(--icon-feedback-warning) -} - -.info__35859 { - background: var(--background-feedback-info); - border: 1px solid var(--icon-feedback-info); - color: var(--text-feedback-info) -} - -.info__35859 .icon__35859 { - color: var(--icon-feedback-info) -} - -.error__35859 { - background: var(--background-feedback-critical); - border: 1px solid var(--icon-feedback-critical); - color: var(--text-feedback-critical) -} - -.error__35859 .icon__35859 { - color: var(--icon-feedback-critical) -} - -.actionContainer__35859 { - margin-bottom: auto; - margin-left: 16px; - margin-top: auto -} - -.hiddenVisually_b18fe2,.showOnFocus_b18fe2:not(:focus-within) { - clip: rect(0 0 0 0); - clip-path: inset(50%); - height: 1px; - overflow: hidden; - position: absolute; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - white-space: nowrap; - width: 1px -} - -.badge_c2b88c { - align-items: center; - align-self: center; - border-radius: var(--radius-round); - box-sizing: border-box; - display: inline-flex; - gap: var(--space-4); - padding: 0 var(--space-8); - text-align: center; - text-transform: uppercase -} - -.default_c2b88c { - background-color: var(--badge-background-default); - color: var(--badge-text-default) -} - -.brand_c2b88c { - background-color: var(--badge-background-brand); - color: var(--badge-text-brand) -} - -.expressive_c2b88c { - background-color: var(--badge-expressive-background-default); - color: var(--badge-expressive-text-default) -} - -.chip__3854f { - align-self: center; - border-radius: var(--radius-round); - box-sizing: border-box; - display: inline-block; - padding: 0 var(--space-8); - text-align: center; - text-transform: uppercase -} - -.blurple-light__3854f { - background-color: var(--chip-blurple-light-background); - color: var(--chip-blurple-light-text) -} - -.blurple-medium__3854f { - background-color: var(--chip-blurple-medium-background); - color: var(--chip-blurple-medium-text) -} - -.blurple-dark__3854f { - background-color: var(--chip-blurple-dark-background); - color: var(--chip-blurple-dark-text) -} - -.purple-light__3854f { - background-color: var(--chip-purple-light-background); - color: var(--chip-purple-light-text) -} - -.purple-medium__3854f { - background-color: var(--chip-purple-medium-background); - color: var(--chip-purple-medium-text) -} - -.purple-dark__3854f { - background-color: var(--chip-purple-dark-background); - color: var(--chip-purple-dark-text) -} - -.green-light__3854f { - background-color: var(--chip-green-light-background); - color: var(--chip-green-light-text) -} - -.green-medium__3854f { - background-color: var(--chip-green-medium-background); - color: var(--chip-green-medium-text) -} - -.green-dark__3854f { - background-color: var(--chip-green-dark-background); - color: var(--chip-green-dark-text) -} - -.orange-light__3854f { - background-color: var(--chip-orange-light-background); - color: var(--chip-orange-light-text) -} - -.orange-medium__3854f { - background-color: var(--chip-orange-medium-background); - color: var(--chip-orange-medium-text) -} - -.orange-dark__3854f { - background-color: var(--chip-orange-dark-background); - color: var(--chip-orange-dark-text) -} - -.yellow-light__3854f { - background-color: var(--chip-yellow-light-background); - color: var(--chip-yellow-light-text) -} - -.yellow-medium__3854f { - background-color: var(--chip-yellow-medium-background); - color: var(--chip-yellow-medium-text) -} - -.yellow-dark__3854f { - background-color: var(--chip-yellow-dark-background); - color: var(--chip-yellow-dark-text) -} - -.pink-light__3854f { - background-color: var(--chip-pink-light-background); - color: var(--chip-pink-light-text) -} - -.pink-medium__3854f { - background-color: var(--chip-pink-medium-background); - color: var(--chip-pink-medium-text) -} - -.pink-dark__3854f { - background-color: var(--chip-pink-dark-background); - color: var(--chip-pink-dark-text) -} - -.red-light__3854f { - background-color: var(--chip-red-light-background); - color: var(--chip-red-light-text) -} - -.red-medium__3854f { - background-color: var(--chip-red-medium-background); - color: var(--chip-red-medium-text) -} - -.red-dark__3854f { - background-color: var(--chip-red-dark-background); - color: var(--chip-red-dark-text) -} - -.gray-light__3854f { - background-color: var(--chip-gray-light-background); - color: var(--chip-gray-light-text) -} - -.gray-medium__3854f { - background-color: var(--chip-gray-medium-background); - color: var(--chip-gray-medium-text) -} - -.gray-dark__3854f { - background-color: var(--chip-gray-dark-background); - color: var(--chip-gray-dark-text) -} - -.progress__45530 { - overflow: hidden; - position: relative; - transform: translateZ(0) -} - -.progress__45530,.progressBar__45530 { - width: 100% -} - -.animating__45530 { - transition: transform .2s ease-out -} - -.xxsmall__45530 { - border-radius: 2px; - height: 2px -} - -.xsmall__45530 { - height: 4px -} - -.small__45530,.xsmall__45530 { - border-radius: 3px -} - -.small__45530 { - height: 6px -} - -.large__45530 { - border-radius: 4px; - height: 8px -} - -.indeterminate__45530 { - position: absolute; - top: 0; - width: auto -} - -.indeterminateBar1__45530 { - animation: indeterminate1__45530 2.1s cubic-bezier(.65,.815,.735,.395) infinite; - animation-play-state: paused -} - -.indeterminateBar1__45530.animating__45530 { - animation-play-state: running -} - -.indeterminateBar2__45530 { - animation: indeterminate2__45530 2.1s cubic-bezier(.165,.84,.44,1) infinite; - animation-delay: 1.15s; - animation-play-state: paused -} - -.indeterminateBar2__45530.animating__45530 { - animation-play-state: running -} - -@keyframes indeterminate1__45530 { - 0% { - left: -35%; - right: 100% - } - - 60% { - left: 100%; - right: -90% - } - - to { - left: 100%; - right: -90% - } -} - -@keyframes indeterminate2__45530 { - 0% { - left: -200%; - right: 100% - } - - 60% { - left: 107%; - right: -8% - } - - to { - left: 107%; - right: -8% - } -} - -.progressCircle__28edc { - align-items: center; - color: var(--blue-345); - display: flex; - height: 100%; - justify-content: center; - position: relative; - width: 100% -} - -.circle__28edc { - bottom: 0; - left: 0; - position: absolute; - right: 0; - top: 0 -} - -.circleBackgroundAlt__28edc { - fill: var(--opacity-black-20) -} - -.circleBackground__28edc { - fill: var(--background-mod-muted) -} - -.circleProgress__28edc { - fill: none -} - -.circleOverlay__28edc { - align-items: center; - display: flex; - justify-content: center; - position: relative -} - -.progress__67225 { - overflow: hidden; - position: relative; - transform: translateZ(0); - width: 100% -} - -.progressBar__67225 { - position: absolute; - width: 100% -} - -.full-motion .animating__67225 { - transition: transform .2s ease-out -} - -.xsmall__67225 { - height: 4px -} - -.small__67225,.xsmall__67225 { - border-radius: 3px -} - -.small__67225 { - height: 6px -} - -.large__67225 { - border-radius: 4px; - height: 8px -} - -.stack_dbd263 { - display: flex -} - -.stack_dbd263[data-full-width=true] { - width: 100% -} - -.stack_dbd263[data-direction=vertical] { - flex-direction: column -} - -.stack_dbd263[data-direction=vertical-reverse] { - flex-direction: column-reverse -} - -.stack_dbd263[data-direction=horizontal] { - flex-direction: row -} - -.stack_dbd263[data-direction=horizontal-reverse] { - flex-direction: row-reverse -} - -.stack_dbd263[data-align=start] { - align-items: flex-start -} - -.stack_dbd263[data-align=end] { - align-items: flex-end -} - -.stack_dbd263[data-align=center] { - align-items: center -} - -.stack_dbd263[data-align=stretch] { - align-items: stretch -} - -.stack_dbd263[data-align=baseline] { - align-items: baseline -} - -.stack_dbd263[data-wrap=true] { - flex-wrap: wrap -} - -.stack_dbd263[data-justify=start] { - justify-content: flex-start -} - -.stack_dbd263[data-justify=end] { - justify-content: flex-end -} - -.stack_dbd263[data-justify=center] { - justify-content: center -} - -.stack_dbd263[data-justify=space-around] { - justify-content: space-around -} - -.stack_dbd263[data-justify=space-between] { - justify-content: space-between -} - -.scrollerBase_d125d2 { - box-sizing: border-box; - flex: 1 1 auto; - min-height: 0; - position: relative -} - -.auto_d125d2,.none_d125d2,.thin_d125d2 { -} - -.thin_d125d2::-webkit-scrollbar { - height: 8px; - width: 8px -} - -.thin_d125d2::-webkit-scrollbar-track { - background-color: var(--scrollbar-thin-track); - border: 2px solid var(--scrollbar-thin-track); - border-color: var(--scrollbar-thin-track) -} - -.thin_d125d2::-webkit-scrollbar-thumb { - background-clip: padding-box; - background-color: var(--scrollbar-thin-thumb); - border: 2px solid transparent; - border-radius: 4px; - min-height: 40px -} - -.thin_d125d2::-webkit-scrollbar-corner { - background-color: transparent -} - -.auto_d125d2::-webkit-scrollbar { - height: 16px; - width: 16px -} - -.auto_d125d2::-webkit-scrollbar-track { - background-color: var(--scrollbar-auto-track) -} - -.auto_d125d2::-webkit-scrollbar-thumb,.auto_d125d2::-webkit-scrollbar-track { - background-clip: padding-box; - border: 4px solid transparent; - border-radius: 8px -} - -.auto_d125d2::-webkit-scrollbar-thumb { - background-color: var(--scrollbar-auto-thumb); - min-height: 40px -} - -.auto_d125d2::-webkit-scrollbar-corner { - background-color: transparent -} - -.custom-theme-background .customTheme_d125d2.auto_d125d2::-webkit-scrollbar-track { - background-color: var(--background-mod-muted) -} - -.none_d125d2::-webkit-scrollbar { - height: 0; - width: 0 -} - -.fade_d125d2::-webkit-scrollbar-thumb,.fade_d125d2::-webkit-scrollbar-track { - visibility: hidden -} - -.fade_d125d2:hover::-webkit-scrollbar-thumb,.fade_d125d2:hover::-webkit-scrollbar-track { - visibility: visible -} - -.scrolling_d125d2.fade_d125d2:focus-within::-webkit-scrollbar-thumb,.scrolling_d125d2.fade_d125d2:focus-within::-webkit-scrollbar-track { - visibility: visible -} - -.no-webkit-scrollbar .thin_d125d2 { - scrollbar-color: var(--scrollbar-thin-thumb) var(--scrollbar-thin-track); - scrollbar-width: thin -} - -.no-webkit-scrollbar .thin_d125d2.fade_d125d2.scrolling_d125d2,.no-webkit-scrollbar .thin_d125d2.fade_d125d2:hover { - scrollbar-color: var(--scrollbar-thin-thumb) var(--scrollbar-thin-track) -} - -.no-webkit-scrollbar .auto_d125d2 { - scrollbar-color: var(--scrollbar-auto-scrollbar-color-thumb) var(--scrollbar-auto-scrollbar-color-track); - scrollbar-width: auto -} - -.no-webkit-scrollbar .auto_d125d2.fade_d125d2.scrolling_d125d2,.no-webkit-scrollbar .auto_d125d2.fade_d125d2:hover { - scrollbar-color: var(--scrollbar-auto-scrollbar-color-thumb) var(--scrollbar-auto-scrollbar-color-track) -} - -.no-webkit-scrollbar .none_d125d2 { - scrollbar-width: none -} - -.no-webkit-scrollbar .fade_d125d2 { - scrollbar-color: transparent transparent -} - -.content_d125d2 { - position: relative -} - -.disableScrollAnchor_d125d2 { - overflow-anchor: none -} - -.managedReactiveScroller_d125d2 { - overflow-x: hidden; - overflow-y: scroll -} - -.pointerCover_d125d2 { - inset: 0; - position: absolute; - z-index: 9999 -} - -.enable-forced-colors ::-webkit-scrollbar-track { - border-radius: 0; - border-width: 1px -} - -.enable-forced-colors ::-webkit-scrollbar-thumb { - background-color: CanvasText; - border-width: 1px -} - -.enable-forced-colors ::-webkit-scrollbar-thumb:horizontal:active,.enable-forced-colors ::-webkit-scrollbar-thumb:horizontal:hover,.enable-forced-colors ::-webkit-scrollbar-thumb:vertical:active,.enable-forced-colors ::-webkit-scrollbar-thumb:vertical:hover { - background-color: Highlight -} - -.enable-forced-colors .auto_d125d2::-webkit-scrollbar { - height: 8px; - width: 8px -} - -.enable-forced-colors .auto_d125d2::-webkit-scrollbar-track { - border-radius: 0; - border-width: 1px -} - -@keyframes spinner-wandering-cubes__46696 { - 25% { - transform: translateX(22px) rotate(-90deg) scale(.5) - } - - 50% { - transform: translateX(22px) translateY(22px) rotate(-180deg) - } - - 75% { - transform: translateX(0) translateY(22px) rotate(-270deg) scale(.5) - } - - to { - transform: rotate(-1turn) - } -} - -@keyframes spinner-chasing-dots-rotate__46696 { - to { - transform: rotate(1turn) - } -} - -@keyframes spinner-chasing-dots-bounce__46696 { - 0%,to { - transform: scale(0) - } - - 50% { - transform: scale(1) - } -} - -@keyframes spinner-pulsing-ellipsis__46696 { - 0% { - opacity: 1; - transform: scale(1) - } - - 50% { - opacity: .3; - transform: scale(.8) - } - - to { - opacity: 1; - transform: scale(1) - } -} - -@keyframes spinner-low-motion__46696 { - 0% { - opacity: 1 - } - - 50% { - opacity: .6 - } - - to { - opacity: 1 - } -} - -@keyframes spinner-spinning-circle-rotate__46696 { - to { - transform: rotate(1turn) - } -} - -@keyframes spinner-spinning-circle-dash__46696 { - 0% { - stroke-dasharray: 1,200; - stroke-dashoffset: 0 - } - - 50% { - stroke-dasharray: 130,200 - } - - to { - stroke-dasharray: 130,200; - stroke-dashoffset: -124 - } -} - -.spinner__46696 { - display: flex -} - -.inner__46696,.spinner__46696 { - align-items: center; - justify-content: center -} - -.inner__46696 { - contain: paint; - display: inline-flex; - height: 32px; - position: relative; - width: 32px -} - -.wanderingCubes__46696 .item__46696 { - animation: spinner-wandering-cubes__46696 1.8s ease-in-out infinite; - background-color: var(--brand-400); - height: 10px; - left: 0; - position: absolute; - top: 0; - width: 10px -} - -.wanderingCubes__46696 .item__46696:last-child { - animation-delay: -.9s -} - -.chasingDots__46696 { - animation: spinner-chasing-dots-rotate__46696 2s linear infinite -} - -.chasingDots__46696 .item__46696 { - animation: spinner-chasing-dots-bounce__46696 2s ease-in-out infinite; - background-color: var(--brand-500); - border-radius: 100%; - display: inline-block; - height: 60%; - position: absolute; - top: 0; - width: 60% -} - -.chasingDots__46696 .item__46696:last-child { - animation-delay: -1s; - bottom: 0; - top: auto -} - -.pulsingEllipsis__46696 { - display: flex; - height: auto; - justify-content: center; - position: relative; - width: 28px -} - -.pulsingEllipsis__46696 .item__46696 { - animation: spinner-pulsing-ellipsis__46696 1.4s ease-in-out infinite; - background-color: var(--primary-100); - border-radius: 3px; - display: inline-block; - height: 6px; - margin-right: 2px; - opacity: .3; - width: 6px -} - -.pulsingEllipsis__46696 .item__46696:nth-of-type(2) { - animation-delay: .2s -} - -.pulsingEllipsis__46696 .item__46696:nth-of-type(3) { - animation-delay: .4s -} - -.lowMotion__46696 .item__46696 { - animation: spinner-low-motion__46696 1.4s ease-in-out infinite; - background-color: var(--interactive-text-default); - border-radius: 3px; - display: inline-block; - height: 6px; - margin-right: 2px; - opacity: .3; - width: 6px -} - -.lowMotion__46696 .item__46696:nth-of-type(2) { - animation-delay: .2s -} - -.lowMotion__46696 .item__46696:nth-of-type(3) { - animation-delay: .4s -} - -.stop-animation .pulsingEllipsis__46696 .item__46696 { - animation: none -} - -.stopAnimation__46696 .chasingDots__46696,.stopAnimation__46696 .circular__46696,.stopAnimation__46696 .item__46696,.stopAnimation__46696 .path__46696 { - animation: none -} - -.spinningCircle__46696 { - width: 100% -} - -.spinningCircleInner__46696 { - transform: rotate(280deg) -} - -.circular__46696 { - animation: spinner-spinning-circle-rotate__46696 2s linear infinite; - height: 100%; - width: 100% -} - -.path__46696 { - animation: spinner-spinning-circle-dash__46696 2s ease-in-out infinite; - stroke-dasharray: 1,200; - stroke-dashoffset: 0; - fill: none; - stroke-width: 6; - stroke-miterlimit: 10; - stroke-linecap: round; - stroke: var(--brand-500) -} - -.path2__46696 { - animation-delay: .15s; - opacity: .6 -} - -.path2__46696,.path3__46696 { - stroke: var(--text-brand) -} - -.path3__46696 { - animation-delay: .23s -} - -.theme-light .path3__46696 { - opacity: .3 -} - -.combo__61c93 { - box-sizing: border-box; - display: flex; - font-size: 12px; - font-weight: var(--font-weight-semibold); - height: 23px; - line-height: 12px; - position: relative; - text-transform: uppercase -} - -.systemFont__61c93 { - font-family: "system-ui",sans-serif -} - -.key__61c93 { - background-color: var(--background-mod-muted); - border: 1px solid var(--border-strong); - border-radius: 4px; - box-sizing: border-box; - cursor: default; - display: block; - height: 23px; - min-height: 14px; - min-width: 14px; - padding: 3px 6px 4px; - position: relative; - text-align: center -} - -.key__61c93:not(:last-child) { - margin-inline-end:3px} - -.key__61c93 .bindArrow__61c93 { - height: 10px; - width: 10px -} - -.key__61c93 .bindArrow__61c93.up__61c93 { - transform: rotate(180deg) -} - -.key__61c93 .bindArrow__61c93.left__61c93 { - transform: rotate(90deg) -} - -.key__61c93 .bindArrow__61c93.right__61c93 { - transform: rotate(-90deg) -} - -.key__61c93:active { - height: 21px; - padding-bottom: 2px; - transform: translateY(2px) -} - -.combo__61c93 { - color: var(--text-strong) -} - -.key__61c93 { - border: 1px solid var(--border-subtle); - box-shadow: inset 0 -4px 0 var(--background-mod-muted); - color: var(--interactive-text-active) -} - -.key__61c93 g { - fill: var(--interactive-text-active) -} - -.key__61c93:active { - border: 1px solid var(--border-strong); - box-shadow: inset 0 -2px 0 var(--background-mod-subtle); - color: var(--interactive-text-default) -} - -.key__61c93:active g { - fill: var(--interactive-text-default) -} - -.dim__61c93 span { - background-color: var(--background-mod-subtle); - color: var(--text-subtle) -} - -.dim__61c93 span .bindArrow__61c93 g { - fill: var(--text-subtle) -} - -.dim__61c93 span:active { - color: var(--text-subtle) -} - -.dim__61c93 span:active .bindArrow__61c93 g { - fill: var(--text-subtle) -} - -.keybindShortcutSearchPopout__61c93 { - flex-grow: 0; - flex-shrink: 0; - margin: 0 -} - -.keyboardShortcuts__61c93 { - margin: 0 -} - -.button_a22cb0 { - align-items: center; - background: initial; - border: 1px solid transparent; - border-radius: var(--radius-sm); - box-sizing: border-box; - color: inherit; - cursor: pointer; - display: flex; - flex-grow: 0; - flex-shrink: 0; - font-size: medium; - font-weight: 400; - justify-content: center; - margin: 0; - max-height: -moz-min-content; - max-height: min-content; - max-width: 100%; - padding: 0; - position: relative; - text-align: start; - transition: background-color 50ms ease-in,color 50ms ease-in,border-color 50ms ease-in,opacity 50ms ease-in; - width: -moz-min-content; - width: min-content -} - -.button_a22cb0:hover { - transition: background-color .15s ease-out,color .15s ease-out,border-color .15s ease-out,opacity .15s ease-out -} - -.button_a22cb0:disabled { - opacity: .5; - pointer-events: none -} - -.highlight-mana-buttons .button_a22cb0 { - box-shadow: 0 0 4px 4px var(--opacity-white-60) -} - -.highlight-mana-buttons [data-button-hoisted-classname-wrapper] .button_a22cb0 { - box-shadow: 0 0 4px 4px var(--opacity-green-60) -} - -[data-button-hoisted-classname-wrapper] { - display: flex; - width: auto -} - -.buttonChildrenWrapper_a22cb0 { - border-radius: var(--radius-sm); - box-sizing: border-box; - justify-content: center; - position: relative; - width: 100% -} - -.buttonChildren_a22cb0,.buttonChildrenWrapper_a22cb0 { - align-items: center; - display: flex; - overflow: hidden -} - -.buttonChildren_a22cb0 { - gap: var(--space-4); - text-overflow: ellipsis; - transition: opacity .2s ease-out,transform .2s ease-out; - white-space: nowrap -} - -.icon_a22cb0 { - flex-shrink: 0 -} - -.buttonChildren_a22cb0 { - opacity: 1 -} - -.buttonChildren_a22cb0.loading_a22cb0 { - opacity: 0 -} - -.spinnerWrapper_a22cb0 { - animation-duration: .2s; - animation-fill-mode: forwards; - animation-timing-function: ease; - height: 100%; - position: absolute; - top: 0; - width: 100% -} - -.spinnerWrapper_a22cb0.fadeIn_a22cb0 { - animation-name: spinner-opacity-in_a22cb0 -} - -.spinnerWrapper_a22cb0.fadeOut_a22cb0 { - animation-name: spinner-opacity-out_a22cb0 -} - -.full-motion .spinnerWrapper_a22cb0.fadeIn_a22cb0 { - animation-name: spinner-transform-in_a22cb0,spinner-opacity-in_a22cb0 -} - -.full-motion .spinnerWrapper_a22cb0.fadeOut_a22cb0 { - animation-name: spinner-transform-out_a22cb0,spinner-opacity-out_a22cb0 -} - -.full-motion .buttonChildren_a22cb0 { - transform: translateY(0) -} - -.full-motion .buttonChildren_a22cb0.loading_a22cb0 { - transform: translateY(-100%) -} - -@keyframes spinner-opacity-in_a22cb0 { - 0% { - opacity: 0 - } - - to { - opacity: 1 - } -} - -@keyframes spinner-opacity-out_a22cb0 { - 0% { - opacity: 1 - } - - to { - opacity: 0 - } -} - -@keyframes spinner-transform-in_a22cb0 { - 0% { - transform: translateY(100%) - } - - to { - transform: translateY(0) - } -} - -@keyframes spinner-transform-out_a22cb0 { - 0% { - transform: translateY(0) - } - - to { - transform: translateY(100%) - } -} - -.xs_a22cb0 { - border-radius: var(--radius-xs) -} - -.xs_a22cb0 .buttonChildrenWrapper_a22cb0 { - min-height: 22px; - min-width: 22px -} - -.xs_a22cb0.hasText_a22cb0 { - min-width: var(--__button-min-width,60px) -} - -.xs_a22cb0.hasText_a22cb0 .buttonChildrenWrapper_a22cb0 { - padding: calc(var(--space-4) - 1px) calc(var(--space-8) - 1px) -} - -.sm_a22cb0 .buttonChildrenWrapper_a22cb0 { - min-height: 30px; - min-width: 30px -} - -.sm_a22cb0.hasText_a22cb0 { - min-width: var(--__button-min-width,60px) -} - -.sm_a22cb0.hasText_a22cb0 .buttonChildrenWrapper_a22cb0 { - padding: calc(var(--space-4) - 1px) calc(var(--space-12) - 1px) -} - -.md_a22cb0 .buttonChildrenWrapper_a22cb0 { - min-height: 38px; - min-width: 38px -} - -.md_a22cb0.hasText_a22cb0 { - min-width: var(--__button-min-width,100px) -} - -.md_a22cb0.hasText_a22cb0 .buttonChildrenWrapper_a22cb0 { - padding: calc(var(--space-8) - 1px) calc(var(--space-16) - 1px) -} - -.spinnerItem_a22cb0 { - background-color: currentColor!important -} - -.spinner_a22cb0 { - height: 100% -} - -.spinner-sm_a22cb0,.spinner-xs_a22cb0 { - min-height: 16px; - min-width: 16px; - transform: scale(.75) -} - -.spinner-md_a22cb0 { - transform: scale(.9) -} - -.spinner-lg_a22cb0,.spinner-md_a22cb0 { - min-height: 20px; - min-width: 20px -} - -.primary_a22cb0 { - background-color: var(--control-primary-background-default); - border-color: var(--control-primary-border-default); - color: var(--control-primary-text-default) -} - -.primary_a22cb0:hover { - background-color: var(--control-primary-background-hover); - border-color: var(--control-primary-border-hover); - color: var(--control-primary-text-hover) -} - -.primary_a22cb0:active { - background-color: var(--control-primary-background-active); - border-color: var(--control-primary-border-active); - color: var(--control-primary-text-active) -} - -.secondary_a22cb0 { - background-color: var(--control-secondary-background-default); - border-color: var(--control-secondary-border-default); - color: var(--control-secondary-text-default) -} - -.secondary_a22cb0:hover { - background-color: var(--control-secondary-background-hover); - border-color: var(--control-secondary-border-hover); - color: var(--control-secondary-text-hover) -} - -.secondary_a22cb0:active { - background-color: var(--control-secondary-background-active); - border-color: var(--control-secondary-border-active); - color: var(--control-secondary-text-active) -} - -.icon-only_a22cb0 { - background-color: transparent; - border-color: transparent; - color: var(--control-icon-only-icon-default) -} - -.icon-only_a22cb0:hover { - background-color: var(--control-icon-only-background-hover); - border-color: var(--control-icon-only-border-hover); - color: var(--control-icon-only-icon-hover) -} - -.icon-only_a22cb0:active { - background-color: var(--control-icon-only-background-active); - border-color: var(--control-icon-only-border-active); - color: var(--control-icon-only-icon-active) -} - -.color-mix_a22cb0 { - background-color: transparent; - border: transparent; - color: var(--icon-strong) -} - -.color-mix_a22cb0:hover { - color: var(--control-icon-only-icon-hover) -} - -.color-mix_a22cb0:active { - color: var(--control-icon-only-icon-active) -} - -.color-mix_a22cb0:before { - border: 1px solid transparent; - border-radius: var(--radius-sm); - box-sizing: border-box; - content: ""; - height: 100%; - left: 0; - mix-blend-mode: plus-lighter; - position: absolute; - top: 0; - transition: background-color 50ms ease-in,border-color 50ms ease-in,opacity 50ms ease-in; - width: 100% -} - -.color-mix_a22cb0:hover:before { - background-color: var(--control-icon-only-background-hover); - border-color: var(--border-subtle); - transition: background-color .15s ease-out,border-color .15s ease-out,opacity .15s ease-out -} - -.color-mix_a22cb0:active:before { - background-color: var(--control-icon-only-background-active); - border-color: var(--border-subtle) -} - -.input-accessory_a22cb0 { - border-radius: var(--radius-xs); - color: var(--icon-strong) -} - -.theme-light .color-mix_a22cb0:before { - mix-blend-mode: difference -} - -.critical-primary_a22cb0 { - background-color: var(--control-critical-primary-background-default); - border-color: var(--control-critical-primary-border-default); - color: var(--control-critical-primary-text-default) -} - -.critical-primary_a22cb0:hover { - background-color: var(--control-critical-primary-background-hover); - border-color: var(--control-critical-primary-border-hover); - color: var(--control-critical-primary-text-hover) -} - -.critical-primary_a22cb0:active { - background-color: var(--control-critical-primary-background-active); - border-color: var(--control-critical-primary-border-active); - color: var(--control-critical-primary-text-active) -} - -.critical-secondary_a22cb0 { - background-color: var(--control-critical-secondary-background-default); - border-color: var(--control-critical-secondary-border-default); - color: var(--control-critical-secondary-text-default) -} - -.critical-secondary_a22cb0:hover { - background-color: var(--control-critical-secondary-background-hover); - border-color: var(--control-critical-secondary-border-hover); - color: var(--control-critical-secondary-text-hover) -} - -.critical-secondary_a22cb0:active { - background-color: var(--control-critical-secondary-background-active); - border-color: var(--control-critical-secondary-border-active); - color: var(--control-critical-secondary-text-active) -} - -.active_a22cb0 { - background-color: var(--control-connected-background-default); - border-color: var(--control-connected-border-default); - color: var(--control-connected-text-default) -} - -.active_a22cb0:hover { - background-color: var(--control-connected-background-hover); - border-color: var(--control-connected-border-hover); - color: var(--control-connected-text-hover) -} - -.active_a22cb0:active { - background-color: var(--control-connected-background-active); - border-color: var(--control-connected-border-active); - color: var(--control-connected-text-active) -} - -.overlay-primary_a22cb0 { - background-color: var(--control-overlay-primary-background-default); - border-color: var(--control-overlay-primary-border-default); - color: var(--control-overlay-primary-text-default) -} - -.overlay-primary_a22cb0:hover { - background-color: var(--control-overlay-primary-background-hover); - border-color: var(--control-overlay-primary-border-hover); - color: var(--control-overlay-primary-text-hover) -} - -.overlay-primary_a22cb0:active { - background-color: var(--control-overlay-primary-background-active); - border-color: var(--control-overlay-primary-border-active); - color: var(--control-overlay-primary-text-active) -} - -.overlay-secondary_a22cb0 { - background-color: var(--control-overlay-secondary-background-default); - border-color: var(--control-overlay-secondary-border-default); - color: var(--control-overlay-secondary-text-default) -} - -.overlay-secondary_a22cb0:hover { - background-color: var(--control-overlay-secondary-background-hover); - border-color: var(--control-overlay-secondary-border-hover); - color: var(--control-overlay-secondary-text-hover) -} - -.overlay-secondary_a22cb0:active { - background-color: var(--control-overlay-secondary-background-active); - border-color: var(--control-overlay-secondary-border-active); - color: var(--control-overlay-secondary-text-active) -} - -.expressive_a22cb0>* { - pointer-events: none; - z-index: 1 -} - -.expressiveRive_a22cb0 { - height: calc(100% + var(--__glow-amount)*2 + 2px); - left: calc(var(--__glow-amount)*-1 - 1px); - position: absolute; - top: calc(var(--__glow-amount)*-1 - 1px); - width: calc(100% + var(--__glow-amount)*2 + 2px) -} - -.expressive_a22cb0 .expressiveBackground_a22cb0 { - filter: blur(10px) saturate(var(--saturation-factor,1)) -} - -.reduce-motion .expressive_a22cb0 .expressiveBackground_a22cb0 { - filter: blur(7px) saturate(var(--saturation-factor,1)) -} - -.expressive_a22cb0 { - color: var(--control-expressive-text-default) -} - -.expressive_a22cb0 .expressiveFill_a22cb0 { - --__glow-amount: 0px; - background-color: var(--control-expressive-background-default); - border-radius: 8px; - transition: background-color .15s ease,width .15s ease,height .15s ease,top .15s ease,left .15s ease -} - -.expressive_a22cb0:hover { - color: var(--control-expressive-text-hover) -} - -.expressive_a22cb0:hover .expressiveFill_a22cb0 { - background-color: var(--control-expressive-background-hover) -} - -.expressive_a22cb0:active { - color: var(--control-expressive-text-active) -} - -.expressive_a22cb0:active .expressiveFill_a22cb0 { - --__glow-amount: -1px; - background-color: var(--control-expressive-background-active) -} - -.expressive_a22cb0 .expressiveHoverContainer_a22cb0 { - filter: blur(8px); - mix-blend-mode: plus-lighter; - pointer-events: all -} - -.expressiveWrapper_a22cb0 { - --__glow-amount: 8px; - display: flex; - max-width: 100%; - position: relative; - width: -moz-min-content; - width: min-content -} - -.fullWidth_a22cb0.hasText_a22cb0 { - flex: 1; - width: 100% -} - -.rounded_a22cb0 { - border-radius: var(--radius-round) -} - -.enable-forced-colors .button_a22cb0 { - background-color: ButtonFace; - border-color: ButtonText; - color: ButtonText; - forced-color-adjust: none -} - -.enable-forced-colors .button_a22cb0:disabled { - background-color: Canvas; - border-color: GrayText; - color: GrayText; - opacity: 1 -} - -.enable-forced-colors .button_a22cb0 .expressiveFill_a22cb0 { - display: none -} - -.textButton__7a01b { - background: initial; - box-sizing: border-box; - color: inherit; - display: inline-flex; - flex-grow: 0; - flex-shrink: 0; - font-size: medium; - font-weight: 400; - gap: var(--space-4); - margin: 0; - max-width: 100%; - padding: 0; - text-align: start; - text-decoration: none; - width: -moz-min-content; - width: min-content -} - -.textButton__7a01b:disabled { - opacity: .5 -} - -.textButton__7a01b:hover .text__7a01b,:where(.decorate-links) .text__7a01b { - text-decoration: underline -} - -.primary__7a01b { - color: var(--text-brand) -} - -.secondary__7a01b { - color: var(--text-strong) -} - -.always-white__7a01b { - color: var(--white) -} - -.critical__7a01b { - color: var(--text-feedback-critical) -} - -.highlight-mana-buttons .button__7a01b { - box-shadow: 0 0 2px 2px var(--opacity-white-60) -} - -.button__039e0 { - align-items: center; - background-color: transparent; - border-radius: var(--radius-sm); - cursor: pointer; - display: flex; - justify-content: center; - transition: background-color .3s ease,color .3s ease -} - -.secondary__039e0 { - background-color: var(--control-secondary-background-default); - border: 1px solid var(--control-secondary-border-default); - color: var(--icon-subtle) -} - -.secondary__039e0.pressed__039e0 { - background-color: var(--control-secondary-background-active); - border-color: var(--control-secondary-border-active); - color: var(--icon-strong) -} - -.secondary__039e0:hover { - background-color: var(--control-secondary-background-hover); - border-color: var(--control-secondary-border-hover) -} - -.secondary__039e0:active { - background-color: var(--control-secondary-background-active); - border-color: var(--control-secondary-border-active) -} - -.tertiary__039e0 { - background-color: transparent; - border: 1px solid transparent; - color: var(--icon-subtle) -} - -.tertiary__039e0.pressed__039e0 { - background-color: var(--control-icon-only-background-active); - border-color: var(--control-icon-only-border-active); - color: var(--icon-strong) -} - -.tertiary__039e0:hover { - background-color: var(--control-icon-only-background-hover); - border-color: var(--control-icon-only-border-hover) -} - -.tertiary__039e0:active { - background-color: var(--control-icon-only-background-active); - border-color: var(--control-icon-only-border-active) -} - -.overlay-secondary__039e0 { - background-color: var(--control-secondary-background-default); - border: 1px solid var(--control-secondary-border-default); - color: var(--icon-strong) -} - -.overlay-secondary__039e0.pressed__039e0 { - background-color: var(--control-secondary-background-active); - border-color: var(--control-secondary-border-active) -} - -.overlay-secondary__039e0:hover { - background-color: var(--control-secondary-background-hover); - border-color: var(--control-secondary-border-hover) -} - -.overlay-secondary__039e0:active { - background-color: var(--control-secondary-background-active); - border-color: var(--control-secondary-border-active) -} - -.overlay-tertiary__039e0 { - background-color: transparent; - border: 1px solid transparent; - color: var(--icon-strong) -} - -.overlay-tertiary__039e0.pressed__039e0 { - background-color: var(--control-icon-only-background-active); - border-color: var(--control-icon-only-border-active) -} - -.overlay-tertiary__039e0:hover { - background-color: var(--control-icon-only-background-hover); - border-color: var(--control-icon-only-border-hover) -} - -.overlay-tertiary__039e0:active { - background-color: var(--control-icon-only-background-active); - border-color: var(--control-icon-only-border-active) -} - -.outerContainer__8a031 { - align-items: center; - container: modal-container/inline-size; - height: 100vh; - justify-content: center; - padding: calc(var(--custom-app-top-bar-height) + var(--space-24)) var(--space-24); - pointer-events: none; - position: relative; - width: calc(100vw - var(--devtools-sidebar-width, 0px)) -} - -.container__8a031,.outerContainer__8a031 { - box-sizing: border-box; - display: flex -} - -.container__8a031 { - --custom-border-radius: var(--radius-md); - background: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--custom-border-radius); - box-shadow: var(--shadow-high); - color: var(--text-default); - flex-direction: column; - max-height: 100%; - pointer-events: auto; - width: 100% -} - -.padding-size-sm__8a031 { - --custom-modal-padding: var(--space-24); - --custom-modal-padding-sm: var(--space-8); - --custom-modal-padding-md: var(--space-16); - --custom-modal-padding-top: var(--custom-modal-padding-sm); - --custom-modal-padding-bottom: var(--custom-modal-padding-md) -} - -.padding-size-lg__8a031,.padding-size-sm__8a031 { - padding-bottom: var(--custom-modal-padding-bottom); - padding-top: var(--custom-modal-padding-top) -} - -.padding-size-lg__8a031 { - --custom-modal-padding: var(--space-32); - --custom-modal-padding-sm: var(--space-16); - --custom-modal-padding-md: var(--space-16); - --custom-modal-padding-top: calc(var(--space-24) - var(--custom-modal-padding-md)); - --custom-modal-padding-bottom: var(--custom-modal-padding-md) -} - -.size-sm__8a031 { - max-height: min(720px,100%); - max-width: 400px -} - -.size-md__8a031 { - max-height: min(800px,100%); - max-width: 480px -} - -.size-lg__8a031 { - max-width: 680px -} - -.size-xl__8a031 { - max-width: 960px -} - -.size-xxl__8a031 { - max-width: 1280px -} - -@container (min-width: 1080px) { - .size-xxl__8a031 { - width: calc(80vw - var(--devtools-sidebar-width, 0)) - } -} - -@media (max-height: 550px),(max-width:485px) { - .fullScreenOnMobile__8a031 { - padding:0 - } - - .fullScreenOnMobile__8a031 .container__8a031 { - --custom-border-radius: 0; - --custom-modal-padding-top: calc(var(--custom-app-top-bar-height) + var(--custom-modal-padding-sm)); - border: none; - height: 100%; - overflow-y: auto - } -} - -.section__8a031 { - box-sizing: border-box; - flex-grow: 0; - flex-shrink: 0; - padding: var(--custom-modal-padding-md) var(--custom-modal-padding) var(--custom-modal-padding-sm); - transition-duration: var(--expand-structural-duration); - transition-property: padding; - transition-timing-function: var(--expand-easing-function) -} - -.sectionHidden__8a031 { - overflow: hidden; - padding: 0 var(--custom-modal-padding) 0; - transition-delay: var(--collapse-fade-duration); - transition-duration: var(--collapse-structural-duration); - transition-timing-function: var(--collapse-easing-function) -} - -.header__8a031 { - position: relative -} - -.headerCentered__8a031 { - text-align: center -} - -.headerLayout__8a031 { - align-items: stretch; - display: flex -} - -.headerStepIndicator__8a031 { - margin-bottom: var(--space-24); - width: 120px -} - -.headerGradient__8a031:before { - border-radius: var(--custom-border-radius) var(--custom-border-radius) 0 0; - left: -1.5px; - right: -1.5px; - top: calc((var(--custom-modal-padding-top) + 1.5px)*-1) -} - -.headerGraphic__8a031 { - align-items: center; - box-sizing: border-box; - display: flex; - justify-content: center; - padding-bottom: calc(var(--space-24) - var(--space-8)); - padding-top: calc(var(--space-24) - var(--space-4)); - width: 100% -} - -.headerGraphicContainer__8a031 { - box-sizing: border-box; - max-width: 288px; - width: 100% -} - -.headerGraphicAnimated__8a031 { - margin: 0 calc(var(--space-12)*-1); - padding-bottom: calc(var(--space-12) - var(--space-8)); - padding-top: 0; - width: calc(100% + var(--space-24)) -} - -.headerGraphicAnimated__8a031 .headerGraphicContainer__8a031 { - max-width: 352px -} - -@media (max-height: 550px) { - .headerGraphicContainer__8a031>* { - max-height:15vh - } -} - -.headerLeading__8a031,.headerLeadingSpacer__8a031 { - margin-left: calc((var(--custom-modal-padding) - var(--space-8))*-1); - margin-top: calc(var(--custom-modal-padding-md)*-1); - padding-right: var(--space-12) -} - -.headerTrailing__8a031,.headerTrailingSpacer__8a031 { - margin-right: calc((var(--custom-modal-padding) - var(--space-8))*-1); - margin-top: calc(var(--custom-modal-padding-md)*-1); - padding-left: var(--space-12) -} - -.headerLeading__8a031,.headerLeadingSpacer__8a031,.headerTrailing__8a031,.headerTrailingSpacer__8a031 { - align-items: flex-start; - box-sizing: border-box; - display: flex; - flex-grow: 0; - flex-shrink: 0; - gap: var(--space-8); - min-width: var(--space-4) -} - -.headerLeadingAbsolute__8a031 { - left: var(--custom-modal-padding) -} - -.headerLeadingAbsolute__8a031,.headerTrailingAbsolute__8a031 { - position: absolute; - top: var(--custom-modal-padding-md) -} - -.headerTrailingAbsolute__8a031 { - right: var(--custom-modal-padding) -} - -.headerMain__8a031 { - box-sizing: border-box; - flex-grow: 1; - flex-shrink: 1; - min-width: 0 -} - -.headerSubtitleWrapper__8a031 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-4) -} - -.headerSubtitleIcon__8a031 { - flex-grow: 0; - flex-shrink: 0 -} - -.headerCentered__8a031 .headerSubtitleWrapper__8a031 { - justify-content: center -} - -.headerSubtitle__8a031,.headerTitle__8a031 { - overflow-wrap: break-word; - word-break: break-word -} - -.headerBadge__8a031[data-position=top] { - padding-bottom: calc(var(--space-16) - var(--space-8)) -} - -.body__8a031 { - box-sizing: border-box; - min-height: 1px; - padding-left: 0; - padding-right: 0 -} - -.body__8a031,.bodyList__8a031 { - flex-grow: 0; - flex-shrink: 1 -} - -.bodyList__8a031 { - --custom-list-scrollbar-top-padding: 12px; - height: 100vh; - padding-left: var(--custom-modal-padding); - padding-right: var(--custom-modal-padding); - padding-top: var(--custom-list-scrollbar-top-padding) -} - -.bodyList__8a031::-webkit-scrollbar-thumb,.bodyList__8a031::-webkit-scrollbar-track { - margin-top: var(--custom-list-scrollbar-top-padding) -} - -.bodySpacerTop__8a031 { - flex-grow: 0; - padding-top: var(--custom-modal-padding-md) -} - -.bodySpacerTopBorder__8a031 { - border-bottom: 1px solid var(--border-subtle) -} - -.bodySpacerBottom__8a031 { - flex-grow: 0; - padding-bottom: var(--custom-modal-padding-sm) -} - -.bodySpacerBottomBorder__8a031 { - border-top: 1px solid var(--border-subtle) -} - -.bodyControls__8a031 { - padding: 0 var(--custom-modal-padding); - position: relative -} - -.bodyControls__8a031:after { - background: linear-gradient(to bottom,var(--background-surface-high) 0,transparent 100%); - content: ""; - display: block; - height: 40px; - left: 0; - opacity: 0; - pointer-events: none; - position: absolute; - right: var(--space-16); - top: 100%; - transition: opacity .2s ease-out 0s,visibility 0s ease-out .2s; - visibility: hidden; - z-index: 1 -} - -.bodyControlsWithFade__8a031:after { - opacity: 1; - transition: opacity .2s ease-out 0s,visibility 0s ease-out 0s; - visibility: visible -} - -.bodyInner__8a031 { - box-sizing: border-box; - overflow-wrap: break-word; - overflow-y: hidden; - padding-left: var(--custom-modal-padding); - padding-right: var(--custom-modal-padding); - word-break: break-word -} - -.has-webkit-scrollbar .bodyInner__8a031 { - padding-right: calc(var(--custom-modal-padding) - var(--space-16)) -} - -.bodyControls__8a031+.body__8a031 .bodyInner__8a031,.bodyInnerShouldScroll__8a031 { - padding-top: var(--space-12) -} - -.bodyInnerShouldScroll__8a031 { - padding-bottom: var(--space-12) -} - -.footer__8a031 { - overflow-wrap: break-word; - padding-bottom: 0; - word-break: break-word -} - -.actionBar__8a031 { - align-items: center; - display: flex; - flex-wrap: wrap; - gap: var(--custom-modal-padding) var(--space-8) -} - -.actionBarLeading__8a031 { - flex-grow: 1; - flex-shrink: 1; - overflow-wrap: break-word; - word-break: break-word -} - -.actionBarTrailing__8a031 { - display: flex; - flex-grow: 0; - flex-shrink: 0; - flex-wrap: wrap; - gap: var(--space-8); - margin-left: auto; - max-width: 100% -} - -.actionButtonWrapper__8a031 { - display: flex -} - -.actionBarTrailingFullWidth__8a031 { - width: 100% -} - -.actionBarTrailingFullWidth__8a031 .actionButtonWrapper__8a031 { - flex: 1 -} - -.actionBarCheckbox__8a031 { - padding-inline-end:var(--space-8)} - -.actionBarLayoutChatInput__8a031 { - align-items: flex-start -} - -.actionBarLayoutChatInput__8a031 .actionBarLeading__8a031 { - flex-basis: 60% -} - -@keyframes dotGrow__714a9 { - 0% { - opacity: 1; - transform: scale(1) - } - - 25% { - opacity: 1; - transform: scale(3) - } - - 26% { - opacity: 0 - } - - to { - opacity: 0 - } -} - -@keyframes dotShrink__714a9 { - 0% { - opacity: 0; - transform: scale(2) - } - - 25% { - opacity: 0; - transform: scale(2) - } - - 26% { - opacity: 1; - transform: scale(2) - } - - to { - opacity: 1; - transform: scale(1) - } -} - -@keyframes checkDraw__714a9 { - 0% { - opacity: 0; - transform: scale(.7) - } - - 25% { - opacity: 0; - transform: scale(.7) - } - - 26% { - opacity: 1; - transform: scale(.7) - } - - 75% { - transform: scale(1.1) - } - - to { - opacity: 1; - transform: scale(1) - } -} - -@keyframes checkUndraw__714a9 { - 0% { - opacity: 1; - transform: scale(1) - } - - 25% { - opacity: 1; - transform: scale(.7) - } - - 26% { - opacity: 0; - transform: scale(.7) - } - - to { - opacity: 0 - } -} - -.checkboxOption__714a9 { - align-items: start; - border-radius: var(--radius-sm); - display: flex; - gap: var(--space-12); - max-width: 100% -} - -.spacing__714a9 { - flex: 1 1 auto -} - -.checkboxIndicator__714a9 { - align-items: center; - background-color: var(--checkbox-background-default); - border: 1px solid; - border-color: var(--checkbox-border-default); - border-radius: var(--radius-xs); - box-sizing: border-box; - display: flex; - flex: 0 0 auto; - height: 20px; - justify-content: center; - position: relative; - width: 20px -} - -.checkStroke__714a9 { - opacity: 0 -} - -.checkStroke__714a9,.checkmark__714a9 { - color: var(--checkbox-icon-active); - display: block; - height: 100%; - inset: 0; - position: absolute; - transform-box: fill-box; - transform-origin: 50% 50%; - width: 100% -} - -.dot__714a9 { - opacity: 0; - transform-origin: 10px 10px -} - -.checkboxOption__714a9[data-selected] .checkboxIndicator__714a9 { - background-color: var(--checkbox-background-selected-default); - border-color: var(--checkbox-border-selected-default) -} - -.checkboxOption__714a9[data-selected] .checkStroke__714a9,.checkboxOption__714a9[data-selected] .checkmark__714a9 { - color: var(--checkbox-icon-active); - opacity: 1 -} - -.animateIn__714a9 .dot__714a9 { - animation: dotGrow__714a9 .3s cubic-bezier(.65,0,.83,.83) forwards -} - -.animateIn__714a9 .checkStroke__714a9 { - animation: checkDraw__714a9 .3s cubic-bezier(.65,0,.83,.83) both -} - -.animateOut__714a9 .dot__714a9 { - animation: dotShrink__714a9 .12s cubic-bezier(.65,0,.83,.84) backwards -} - -.animateOut__714a9 .checkStroke__714a9 { - animation: checkUndraw__714a9 .12s cubic-bezier(.65,0,.83,.84) backwards -} - -.checkboxOption__714a9:not([data-selected]):hover:not([data-disabled]) { - cursor: pointer -} - -.checkboxOption__714a9:not([data-selected]):hover:not([data-disabled]) .checkboxIndicator__714a9 { - background-color: var(--checkbox-background-hover); - border-color: var(--checkbox-border-hover) -} - -.checkboxOption__714a9[data-selected]:hover:not([data-disabled]) { - cursor: pointer -} - -.checkboxOption__714a9[data-selected]:hover:not([data-disabled]) .checkboxIndicator__714a9 { - background-color: var(--checkbox-background-selected-hover); - border-color: var(--checkbox-border-selected-hover) -} - -.checkboxOption__714a9[data-disabled] { - cursor: not-allowed; - opacity: .5 -} - -.leadingIcon__714a9 { - color: var(--icon-strong); - flex-shrink: 0; - height: 20px; - width: 20px -} - -.label__714a9 { - align-items: flex-start; - display: flex; - gap: var(--space-4); - width: 100% -} - -.enable-forced-colors .checkboxIndicator__714a9 { - background-color: ButtonFace!important; - border-color: ButtonText!important -} - -.enable-forced-colors .checkboxOption__714a9[data-selected] .checkboxIndicator__714a9 { - background-color: Highlight!important; - border-color: HighlightText!important -} - -.enable-forced-colors .checkboxOption__714a9[data-selected] .checkboxIndicator__714a9 svg * { - fill: HighlightText -} - -.enable-forced-colors .checkboxOption__714a9[data-disabled] { - opacity: 1 -} - -.enable-forced-colors .checkboxOption__714a9[data-disabled] .checkboxIndicator__714a9 { - background-color: transparent!important; - border-color: GrayText!important -} - -.enable-forced-colors .checkboxOption__714a9[data-disabled] .checkboxIndicator__714a9 svg * { - fill: GrayText -} - -.enable-forced-colors .checkboxOption__714a9[data-disabled][data-selected] .checkboxIndicator__714a9 svg * { - fill: GrayText -} - -.enable-forced-colors .checkboxOption__714a9[data-disabled] .label__714a9,.enable-forced-colors .checkboxOption__714a9[data-disabled] .label__714a9>div { - color: GrayText -} - -.checkboxWrapper__09aca { - display: flex; - flex: 1 1 auto; - font-size: 16px; - max-width: 100%; - position: relative; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.checkboxWrapper__09aca.row__09aca { - background-color: var(--background-base-lower); - border-radius: 3px; - padding: 10px -} - -.checkboxWrapper__09aca.row__09aca.checked__09aca { - background-color: var(--background-base-lowest); - color: var(--interactive-text-active) -} - -.checkboxWrapper__09aca.row__09aca:hover:not(.checked__09aca) { - background-color: var(--interactive-background-hover); - color: var(--interactive-text-hover) -} - -.checkboxWrapper__09aca.row__09aca:active:not(.checked__09aca) { - background-color: var(--interactive-background-active); - color: var(--interactive-text-active) -} - -.checkboxWrapper__09aca.row__09aca .inputDefault__09aca { - left: 10px; - top: 10px -} - -.checkboxWrapperDisabled__09aca { - opacity: .6 -} - -.checkboxWrapperDisabled__09aca.row__09aca { - opacity: .3 -} - -.checkboxWrapperDisabled__09aca .checkbox__09aca { - opacity: 1 -} - -.alignTop__09aca { - align-items: top -} - -.alignCenter__09aca { - align-items: center -} - -.input__09aca { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - left: 0; - margin: 0; - opacity: 0; - padding: 0; - position: absolute; - top: 0 -} - -.inputDefault__09aca,.inputDisabled__09aca { - height: 100%; - width: 100%; - z-index: 1 -} - -.inputDefault__09aca,.inputDisabled__09aca { -} - -.inputDefault__09aca { - cursor: pointer -} - -.inputDisabled__09aca { - cursor: not-allowed -} - -.inputReadonly__09aca { - cursor: default; - height: 0; - width: 0; - z-index: -1 -} - -.box__09aca { - border-radius: 6px -} - -.smallBox__09aca { - border-radius: 4px -} - -.round__09aca { - border-radius: 50% -} - -.checkbox__09aca { - align-items: center; - background-color: var(--checkbox-background-default); - border-color: var(--checkbox-border-default); - border-radius: 6px; - border-style: solid; - border-width: 1px; - box-sizing: border-box; - display: flex; - flex: 0 0 auto; - justify-content: center -} - -.checkbox__09aca.checkboxDisabled__09aca { - opacity: .6 -} - -.checkbox__09aca.checked__09aca { - background-color: var(--checkbox-background-selected-default); - border-color: var(--checkbox-border-default) -} - -.label__09aca { - color: var(--text-default); - flex: 1 1 auto; - min-width: 0 -} - -.labelClickable__09aca { - cursor: pointer -} - -.labelDisabled__09aca { - cursor: not-allowed -} - -.labelForward__09aca { - padding-left: 8px -} - -.labelReversed__09aca { - padding-right: 8px -} - -.enable-forced-colors .checkbox__09aca { - background-color: ButtonFace!important; - border-color: ButtonText!important -} - -.enable-forced-colors .checkbox__09aca.checked__09aca { - background-color: Highlight!important; - border-color: HighlightText!important -} - -.enable-forced-colors .checkbox__09aca.checked__09aca svg * { - fill: HighlightText -} - -.enable-forced-colors .checkboxWrapperDisabled__09aca { - opacity: 1 -} - -.enable-forced-colors .checkboxWrapperDisabled__09aca.row__09aca { - opacity: 1 -} - -.enable-forced-colors .checkboxWrapperDisabled__09aca .checkbox__09aca { - background-color: transparent!important; - border-color: GrayText!important -} - -.enable-forced-colors .checkboxWrapperDisabled__09aca .checkbox__09aca.checked__09aca svg * { - fill: GrayText -} - -.enable-forced-colors .labelDisabled__09aca,.enable-forced-colors .labelDisabled__09aca>div { - color: GrayText -} - -.highlight-void-toggleables [data-toggleable-component=checkbox] { - box-shadow: 0 0 6px 2px var(--green-360),0 0 8px 4px var(--opacity-green-60) -} - -:where(.container_a62383) { - --custom-gradient-color-start: var(--blurple-50); - --custom-gradient-color-end: var(--blurple-50); - --custom-gradient-offset-bottom: 0%; - isolation: isolate; - position: relative -} - -:where(.container_a62383):before { - background: linear-gradient(270deg in oklab,var(--custom-gradient-color-end) 0,var(--custom-gradient-color-start) 100%); - border-radius: inherit; - bottom: var(--custom-gradient-offset-bottom); - content: ""; - display: block; - left: 0; - -webkit-mask-image: radial-gradient(100% 100% at 50% 100%,var(--transparent) 60%,#000 100%); - mask-image: radial-gradient(100% 100% at 50% 100%,var(--transparent) 60%,#000 100%); - position: absolute; - right: 0; - top: 0; - z-index: -1 -} - -.purple_a62383 { - --custom-gradient-color-start: var(--expressive-gradient-purple-start); - --custom-gradient-color-end: var(--expressive-gradient-purple-end) -} - -.blue_a62383 { - --custom-gradient-color-start: var(--expressive-gradient-blue-start); - --custom-gradient-color-end: var(--expressive-gradient-blue-end) -} - -.green_a62383 { - --custom-gradient-color-start: var(--expressive-gradient-green-start); - --custom-gradient-color-end: var(--expressive-gradient-green-end) -} - -.pink_a62383 { - --custom-gradient-color-start: var(--expressive-gradient-pink-start); - --custom-gradient-color-end: var(--expressive-gradient-pink-end) -} - -.nitro-pink_a62383 { - --custom-gradient-color-start: var(--expressive-gradient-nitro-pink-start); - --custom-gradient-color-end: var(--expressive-gradient-nitro-pink-end) -} - -.nitro-green_a62383 { - --custom-gradient-color-start: var(--expressive-gradient-nitro-green-start); - --custom-gradient-color-end: var(--expressive-gradient-nitro-green-end) -} - -.container__8ef77 { - align-items: center; - box-sizing: border-box; - display: flex; - justify-content: center; - overflow: hidden; - pointer-events: none; - position: relative; - width: 100% -} - -.aspect-ratio-21\/9__8ef77 { - aspect-ratio: 21/9 -} - -.aspect-ratio-16\/9__8ef77 { - aspect-ratio: 16/9 -} - -.aspect-ratio-6\/4__8ef77 { - aspect-ratio: 6/4 -} - -.aspect-ratio-2\/1__8ef77 { - aspect-ratio: 2/1 -} - -.aspect-ratio-1\/1__8ef77 { - aspect-ratio: 1/1 -} - -.image__8ef77,.video__8ef77 { - max-height: 100%; - max-width: 100% -} - -.lottie__8ef77,.rive__8ef77 { - height: 100%; - width: 100% -} - -.scrim__40128 { - background-color: var(--background-scrim); - bottom: 0; - left: 0; - pointer-events: auto; - position: fixed; - right: var(--devtools-sidebar-width,0); - top: 0; - transform: translateZ(0) -} - -.lightbox__40128 { - background-color: var(--background-scrim-lightbox) -} - -.pointerEventsNone__40128 { - pointer-events: none -} - -.layer__529b0 { - position: fixed; - z-index: 9999 -} - -.layerContainer__59d0d { - background: none!important; - right: var(--devtools-sidebar-width,0); - z-index: 1002 -} - -.clickTrapContainer__59d0d,.layerContainer__59d0d { - bottom: 0; - left: 0; - pointer-events: none; - position: absolute; - top: 0 -} - -.clickTrapContainer__59d0d { - overflow: hidden; - right: 0 -} - -.clickTrapContainer__59d0d.trapClicks__59d0d,.layer__59d0d { - pointer-events: auto -} - -.layer__59d0d { - position: absolute -} - -.emptyError__59d0d:empty:before { - background-color: red; - border: 10px dashed var(--green-230); - color: var(--white); - content: "RENDERING NULL FOR A POPOUT/MODAL/LAYER WILL BREAK THE APP"; - display: block; - font-size: 32px; - font-weight: var(--font-weight-bold); - max-width: 500px; - padding: 8px; - word-break: break-word -} - -.layerHidden__59d0d { - visibility: hidden -} - -.disabledPointerEvents__59d0d { - pointer-events: none -} - -.focusTarget__54e4b { - height: 0; - pointer-events: none; - width: 0 -} - -.container__5a838 { - --custom-description-max-width: 70ch; - flex-grow: 1; - interpolate-size: allow-keywords; - display: grid; - grid-template-areas: "labels" "control" "helper-text" -} - -.container__5a838 .labelContainer__5a838 { - display: flex; - flex-direction: column; - gap: var(--space-4); - grid-area: labels; - min-width: 0 -} - -.container__5a838[data-layout=vertical] .labelContainer__5a838 { - margin-bottom: var(--space-8) -} - -.container__5a838[data-layout=horizontal-responsive],.container__5a838[data-layout=horizontal] { - -moz-column-gap: var(--space-24); - column-gap: var(--space-24); - display: grid; - grid-template-areas: "labels control" "empty helper-text"; - grid-template-columns: auto var(--custom-field-horizontal-control-width,auto); - grid-template-rows: auto auto -} - -.container__5a838[data-layout=horizontal-responsive] .control__5a838,.container__5a838[data-layout=horizontal] .control__5a838 { - align-items: end; - align-self: flex-start -} - -.container__5a838[data-layout=horizontal-responsive] .helperTextContainer__5a838,.container__5a838[data-layout=horizontal] .helperTextContainer__5a838 { - align-self: flex-end; - contain: inline-size; - gap: 0; - padding-top: var(--space-4); - width: 100% -} - -.container__5a838[data-layout=horizontal-responsive] .description__5a838,.container__5a838[data-layout=horizontal] .description__5a838 { - max-width: var(--custom-description-max-width) -} - -.container__5a838[data-disabled=true] { - opacity: .5 -} - -.control__5a838 { - display: flex; - flex-direction: column; - gap: var(--space-4); - grid-area: control; - min-width: 0 -} - -.icon__5a838 { - margin-bottom: -1px; - padding-right: var(--space-4) -} - -.description__5a838 { - grid-area: description -} - -.label__5a838[data-interactive=true] { - cursor: pointer -} - -.container__5a838[data-disabled=true] .label__5a838[data-interactive=true] { - cursor: not-allowed -} - -.required__5a838 { - display: inline-block; - margin-left: var(--space-4) -} - -@keyframes slideDown__5a838 { - 0% { - height: 0; - opacity: 0; - transform: translateY(-4px) - } - - to { - height: auto; - opacity: 1; - transform: translateY(0) - } -} - -.helperTextContainer__5a838 { - align-items: start; - display: grid; - gap: var(--space-12); - grid-area: helper-text; - grid-template-columns: 1fr auto; - min-width: 0 -} - -.statusMessageContainer__5a838 { - align-items: start; - display: grid; - gap: var(--space-4); - grid-template-columns: auto 1fr -} - -.full-motion .statusMessageContainer__5a838 { - animation: slideDown__5a838 .3s ease-in-out -} - -.badgeContainer__5a838 { - margin-left: var(--space-4) -} - -@container (max-width: 480px) { - .container__5a838[data-layout=horizontal-responsive] { - grid-template-areas: "labels" "control" "helper-text"; - grid-template-columns: 1fr; - grid-template-rows: auto - } - - .container__5a838[data-layout=horizontal-responsive] .labelContainer__5a838 { - margin-bottom: var(--space-8) - } - - .container__5a838[data-layout=horizontal-responsive] .control__5a838 { - align-items: unset - } - - .container__5a838[data-layout=horizontal-responsive] .helperTextContainer__5a838 { - align-self: unset; - contain: unset; - gap: var(--space-12); - padding-top: 0; - width: unset - } - - .container__5a838[data-layout=horizontal-responsive] .description__5a838 { - max-width: none - } -} - -.group__0a16d { - display: flex; - flex-direction: column; - gap: var(--space-16); - padding: var(--space-8) 0 var(--space-4) -} - -.backwardsCompatibleCheckbox__0a16d { - align-items: start; - display: flex; - padding: var(--space-8) -} - -@keyframes dotIn__64e61 { - 0% { - transform: scale(0) - } - - 40% { - transform: scale(1.2) - } - - 75% { - transform: scale(1) - } - - to { - transform: scale(1) - } -} - -@keyframes fillIn__64e61 { - 0% { - opacity: 0; - transform: scale(0) - } - - 1% { - opacity: 1 - } - - 35% { - transform: scale(.33) - } - - 40% { - transform: scale(1.15) - } - - to { - opacity: 1; - transform: scale(1) - } -} - -@keyframes dotOut__64e61 { - 0% { - opacity: 1; - transform: scale(1) - } - - 40% { - transform: scale(1) - } - - 99% { - opacity: 1 - } - - to { - opacity: 0; - transform: scale(0) - } -} - -@keyframes fillOut__64e61 { - 0% { - opacity: 1; - transform: scale(1) - } - - 99% { - opacity: 1 - } - - to { - opacity: 0; - transform: scale(0) - } -} - -.radioIndicator__64e61 { - display: "block"; - image-rendering: crisp-edges; - overflow: "visible" -} - -.innerDotRadio__64e61,.outerRadioBase__64e61,.outerRadioFill__64e61,.radioIndicator__64e61 { - transform-box: fill-box; - transform-origin: center; - will-change: transform,opacity; - fill: none; - overflow: visible -} - -.radioGroupOption__64e61 { - align-items: top; - border-radius: var(--radius-sm); - box-sizing: border-box; - display: flex; - gap: var(--space-12) -} - -.radioIndicator__64e61,.standaloneRadioIndicator__64e61 { - background: transparent; - border-radius: var(--radius-lg); - box-sizing: border-box; - display: block -} - -.outerRadioBase__64e61 { - fill: var(--radio-background-default); - stroke: var(--radio-border-default); - stroke-width: 2 -} - -.outerRadioFill__64e61 { - fill: none -} - -.radioGroupOption__64e61[data-selected] .outerRadioBase__64e61,.standaloneRadioIndicator__64e61[data-selected=true] .outerRadioBase__64e61 { - fill: var(--radio-background-selected-default); - stroke: var(--radio-border-selected-default) -} - -.radioGroupOption__64e61[data-selected] .outerRadioFill__64e61,.standaloneRadioIndicator__64e61[data-selected=true] .outerRadioFill__64e61 { - fill: var(--radio-background-selected-default); - stroke: var(--radio-border-selected-default); - stroke-width: 2 -} - -.radioGroupOption__64e61[data-selected] .innerDotRadio__64e61,.standaloneRadioIndicator__64e61[data-selected=true] .innerDotRadio__64e61 { - fill: var(--radio-thumb-background-active); - opacity: 1 -} - -.animateIn__64e61 .outerRadioFill__64e61 { - animation: fillIn__64e61 225ms cubic-bezier(.33,0,.67,1) forwards -} - -.animateIn__64e61 .innerDotRadio__64e61 { - animation: dotIn__64e61 333ms cubic-bezier(.26,.21,.67,1) forwards -} - -.animateOut__64e61 .outerRadioFill__64e61 { - animation: fillOut__64e61 .16s cubic-bezier(.33,0,.67,1) both -} - -.animateOut__64e61 .innerDotRadio__64e61 { - fill: var(--radio-thumb-background-active); - animation: dotOut__64e61 .25s cubic-bezier(.33,0,.25,1) both -} - -.radioGroupOption__64e61:not([data-selected]):not([data-disabled]):hover { - cursor: pointer -} - -.radioGroupOption__64e61:not([data-selected]):not([data-disabled]):hover .outerRadioBase__64e61 { - fill: var(--radio-background-hover); - stroke: var(--radio-border-hover) -} - -.radioGroupOption__64e61[data-selected]:not([data-disabled]):hover { - cursor: pointer -} - -.radioGroupOption__64e61[data-selected]:not([data-disabled]):hover .outerRadioBase__64e61,.radioGroupOption__64e61[data-selected]:not([data-disabled]):hover .outerRadioFill__64e61 { - fill: var(--radio-background-selected-hover); - stroke: var(--radio-border-selected-hover) -} - -.radioGroupOption__64e61[data-disabled],.standaloneRadioIndicator__64e61[data-disabled=true] { - cursor: not-allowed; - opacity: .5 -} - -.group__64e61 { - box-sizing: border-box; - display: flex; - flex-direction: column; - gap: var(--space-16); - padding: var(--space-8) 0 var(--space-4) -} - -.radioItemIcon__64e61 { - color: var(--icon-strong); - flex-shrink: 0; - height: 20px; - width: 20px -} - -.label__64e61 { - align-items: flex-start; - display: flex; - gap: var(--space-4); - width: 100% -} - -.enable-forced-colors .outerRadioBorderStroke__64e61 { - stroke: ButtonText; - opacity: 1 -} - -.enable-forced-colors .outerRadioBase__64e61 { - fill: Canvas -} - -.enable-forced-colors .innerDotRadio__64e61 { - fill: HighlightText -} - -.enable-forced-colors .radioGroupOption__64e61[data-disabled] { - opacity: 1 -} - -.enable-forced-colors .radioGroupOption__64e61[data-disabled],.enable-forced-colors .radioGroupOption__64e61[data-disabled]:hover { - color: GrayText -} - -.enable-forced-colors .radioGroupOption__64e61[data-selected] .outerRadioBase__64e61 { - fill: Highlight -} - -.enable-forced-colors .radioGroupOption__64e61[data-selected] .outerRadioBorderStroke__64e61 { - stroke: HighlightText; - opacity: 1 -} - -.enable-forced-colors .radioGroupOption__64e61[data-disabled] .outerRadioBase__64e61 { - fill: Canvas -} - -.enable-forced-colors .radioGroupOption__64e61[data-disabled] .outerRadioBorderStroke__64e61 { - stroke: GrayText; - opacity: 1 -} - -.enable-forced-colors .radioGroupOption__64e61[data-disabled] .innerDotRadio__64e61,.enable-forced-colors .standaloneRadioIndicator__64e61[data-disabled=true] .innerDotRadio__64e61 { - fill: GrayText -} - -.enable-forced-colors .standaloneRadioIndicator__64e61[data-disabled=true] { - opacity: 1 -} - -.enable-forced-colors .standaloneRadioIndicator__64e61[data-disabled=true] .outerRadioBase__64e61 { - fill: Canvas -} - -.enable-forced-colors .standaloneRadioIndicator__64e61[data-disabled=true] .outerRadioBorderStroke__64e61 { - stroke: GrayText; - opacity: 1 -} - -.radioBar__88a69 { - border-inline-start:3px solid var(--radio-bar-accent-color);border-radius: 4px; - display: grid; - grid-gap: 8px; - align-items: center; - padding: var(--space-12) var(--space-16)!important -} - -.radioBar__88a69.radioPositionLeft__88a69 { - grid-template-columns: auto 1fr -} - -.radioBar__88a69.radioPositionRight__88a69 { - grid-template-columns: 1fr auto -} - -.item__88a69[aria-checked=true] { - color: var(--interactive-text-active) -} - -.itemFilled__88a69[aria-checked=true] { - background-color: var(--background-mod-subtle) -} - -.radioIconForeground__88a69 { - color: var(--interactive-text-active) -} - -.item__88a69 { - border-radius: 3px; - color: var(--text-strong); - cursor: pointer; - display: block; - flex-direction: row; - margin-bottom: var(--space-4) -} - -.item__88a69:hover:not([aria-checked=true]):not(.disabled__88a69) { - color: var(--interactive-text-hover) -} - -.item__88a69:active:not([aria-checked=true]):not(.disabled__88a69) { - color: var(--interactive-text-active) -} - -.collapsibleItem__88a69 { - border-radius: 3px; - color: var(--interactive-text-default); - cursor: pointer; - display: block; - flex: 1; - flex-direction: row -} - -.collapsibleItem__88a69:hover:not([aria-checked=true]):not(.disabled__88a69) { - color: var(--interactive-text-hover) -} - -.collapsibleItem__88a69:active:not([aria-checked=true]):not(.disabled__88a69) { - color: var(--interactive-text-active) -} - -.itemFilled__88a69 { - background: none -} - -.itemFilled__88a69:hover:not([aria-checked=true]):not(.disabled__88a69) { - background-color: var(--background-mod-subtle); - color: var(--text-strong) -} - -.itemFilled__88a69:active:not([aria-checked=true]):not(.disabled__88a69) { - background-color: var(--background-mod-normal); - color: var(--text-strong) -} - -.tooltipWrapper__88a69 { - display: flex; - flex-direction: column; - margin-bottom: 8px -} - -.tooltipWrapper__88a69:last-child { - margin: 0 -} - -.high-contrast-mode .container__88a69 { - border: 1px solid var(--border-normal); - border-radius: var(--radius-sm) -} - -.disabled__88a69 { - cursor: not-allowed; - opacity: .3 -} - -.info__88a69 { - display: grid; - margin-inline-end:8px;width: 100%; - grid-gap: 4px; - align-items: center -} - -.icon__88a69 { - margin-inline-start:8px} - -.item__88a69,.itemFilled__88a69[aria-checked=true],.item__88a69[aria-checked=true],.radioBar__88a69 { - border-radius: var(--radius-sm) -} - -.radioIndicator__88a69 { - background: transparent; - border-radius: 50%; - box-sizing: border-box; - height: 24px; - width: 24px -} - -.radioIndicatorGroup__88a69 { - display: flex; - flex-direction: row; - gap: var(--space-8) -} - -.refreshIcon__88a69 { - fill: #fff -} - -.refreshIconStroke__88a69 { - stroke: var(--checkbox-border-default) -} - -.refreshIconFill__88a69 { - fill: transparent -} - -.radioIndicatorChecked__88a69 .refreshIconStroke__88a69 { - stroke: var(--checkbox-border-default) -} - -.radioIndicatorChecked__88a69 .refreshIconFill__88a69 { - fill: var(--background-brand) -} - -.full-motion .item__88a69 { - transition: background-color .1s ease,color .1s ease -} - -.high-contrast-mode .item__88a69 { - border-bottom: 1px solid var(--border-subtle); - border-radius: 0; - box-sizing: border-box; - margin-bottom: 0 -} - -.high-contrast-mode .item__88a69:last-child { - border-bottom: none -} - -.high-contrast-mode .item__88a69:last-child .radioBar__88a69 { - border-end-start-radius: var(--radius-sm) -} - -.high-contrast-mode .item__88a69:first-child .radioBar__88a69 { - border-start-start-radius: var(--radius-xs) -} - -.high-contrast-mode .radioBar__88a69 { - border-radius: 0 -} - -.enable-forced-colors .radioBar__88a69 { - background-color: ButtonFace; - border: 1px solid ButtonText; - color: ButtonText; - forced-color-adjust: none -} - -.enable-forced-colors .radioIconForeground__88a69 { - color: HighlightText -} - -.enable-forced-colors .refreshIcon__88a69 { - fill: HighlightText -} - -.enable-forced-colors .refreshIconStroke__88a69 { - stroke: ButtonText -} - -.enable-forced-colors .refreshIconFill__88a69 { - fill: transparent -} - -.enable-forced-colors .item__88a69[aria-checked=true] .radioBar__88a69 { - background-color: Highlight; - border-color: Highlight; - color: HighlightText -} - -.enable-forced-colors .radioIndicatorChecked__88a69 .refreshIconStroke__88a69 { - stroke: HighlightText -} - -.enable-forced-colors .radioIndicatorChecked__88a69 .refreshIconFill__88a69 { - fill: Highlight -} - -.enable-forced-colors .item__88a69.disabled__88a69 { - opacity: 1 -} - -.enable-forced-colors .item__88a69.disabled__88a69,.enable-forced-colors .item__88a69.disabled__88a69:hover { - color: GrayText -} - -.enable-forced-colors .item__88a69.disabled__88a69 .radioBar__88a69 { - background-color: Canvas; - border-color: GrayText; - color: GrayText -} - -.enable-forced-colors .radioIndicatorDisabled__88a69 .radioIconForeground__88a69 { - color: GrayText -} - -.enable-forced-colors .radioIndicatorDisabled__88a69 .refreshIcon__88a69 { - fill: GrayText -} - -.enable-forced-colors .radioIndicatorDisabled__88a69 .refreshIconStroke__88a69 { - stroke: GrayText -} - -.enable-forced-colors .radioIndicatorDisabled__88a69 .refreshIconFill__88a69 { - fill: Canvas -} - -.highlight-void-toggleables [data-toggleable-component=radiogroup] { - box-shadow: 0 0 6px 2px var(--yellow-360),0 0 8px 4px var(--opacity-yellow-60) -} - -.avatar_f0960a,.image_f0960a { - height: 16px; - -o-object-fit: contain; - object-fit: contain; - width: 16px -} - -.avatar_f0960a { - background-color: #000; - -o-object-fit: cover; - object-fit: cover; - overflow: hidden -} - -.avatar_f0960a,.roleDot_f0960a { - border-radius: var(--radius-round) -} - -.roleDot_f0960a { - border: 1px solid var(--text-strong); - box-sizing: border-box; - display: block; - height: 16px; - width: 16px -} - -.tagGroup_cfee8f { - display: flex; - flex-wrap: wrap; - gap: var(--space-8) -} - -.tagList_cfee8f { - display: contents -} - -.tag_cfee8f { - align-items: center; - background-color: var(--background-mod-subtle); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-sizing: border-box; - cursor: default; - display: inline-flex; - gap: var(--space-8); - min-height: 40px; - outline: none; - padding: var(--space-4) var(--space-12); - transition: background-color 50ms ease-in,color 50ms ease-in,border-color 50ms ease-in,opacity 50ms ease-in; - white-space: nowrap -} - -.tag_cfee8f:has([slot=remove]) { - padding-right: var(--space-8) -} - -.tag_cfee8f[data-selection-mode=multiple],.tag_cfee8f[data-selection-mode=single] { - cursor: pointer -} - -.tag_cfee8f[data-selection-mode=multiple][data-hovered],.tag_cfee8f[data-selection-mode=single][data-hovered] { - background: var(--background-mod-normal); - border-color: var(--border-normal) -} - -.tag_cfee8f[data-selection-mode=multiple][data-hovered][data-selected],.tag_cfee8f[data-selection-mode=single][data-hovered][data-selected] { - background: var(--opacity-blurple-16); - border-color: var(--input-border-active) -} - -.tag_cfee8f[data-focus-visible] { - outline: 1px solid var(--border-focus); - outline-offset: 1px -} - -.tag_cfee8f[data-selected] { - background: var(--opacity-blurple-12); - border-color: var(--input-border-active) -} - -.tag_cfee8f[data-disabled] { - cursor: not-allowed; - opacity: .5 -} - -.tag_cfee8f [slot=remove] { - align-items: center; - aspect-ratio: 1/1; - background: none; - border: 1px solid transparent; - border-radius: var(--radius-xs); - display: flex; - height: calc(100% - var(--space-8)); - justify-content: center; - outline: none; - padding: 0; - transition: background-color 50ms ease-in,color 50ms ease-in,border-color 50ms ease-in,opacity 50ms ease-in -} - -.tag_cfee8f [slot=remove][data-hovered] { - background-color: var(--control-icon-only-background-hover); - border-color: var(--control-icon-only-border-hover); - transition: background-color .15s ease-out,color .15s ease-out,border-color .15s ease-out,opacity .15s ease-out -} - -.tag_cfee8f [slot=remove][data-focus-visible] { - outline: 2px solid var(--border-focus); - outline-offset: -1px -} - -.tagGroup_cfee8f[data-layout=inline] { - display: contents -} - -.tagGroup_cfee8f[data-layout=inline] .tag_cfee8f { - border-radius: var(--radius-xs); - gap: var(--space-4); - min-height: 30px; - padding: var(--space-4) var(--space-8) -} - -.tagGroup_cfee8f[data-layout=inline] .tag_cfee8f:has([slot=remove]) { - padding-right: var(--space-4) -} - -.tagGroup_cfee8f[data-layout=inline] [slot=remove] { - height: 100% -} - -.container_a28278 { - cursor: pointer -} - -.container_a28278[data-disabled] { - cursor: not-allowed -} - -.switchIndicator_a28278 { - border: 1px solid transparent; - border-radius: var(--radius-lg); - box-sizing: border-box; - height: 24px; - position: relative; - width: 48px -} - -.switchIndicator_a28278 .thumb_a28278 { - align-items: center; - display: flex; - height: 24px; - justify-content: center; - margin-left: -1px; - margin-top: -1px; - position: absolute; - width: 24px -} - -.container__3f21e { - border: 1px solid transparent; - border-radius: 16px; - box-sizing: border-box; - cursor: pointer; - height: 28px; - position: relative; - width: 44px -} - -.high-contrast-mode .container__3f21e { - border-color: var(--border-strong) -} - -.container__3f21e.disabled__3f21e { - cursor: not-allowed -} - -.container__3f21e.checked__3f21e { - border-color: var(--control-primary-border-default) -} - -.input__3f21e { - border-radius: 14px; - cursor: pointer; - height: 100%; - left: 0; - margin: 0; - opacity: 0; - position: absolute; - top: 0; - width: 100% -} - -.input__3f21e[disabled] { - pointer-events: none -} - -.slider__3f21e { - display: block; - height: 20px; - left: 0; - margin: 3px; - position: absolute; - width: 28px -} - -.enable-forced-colors .container__3f21e { - background-color: ButtonFace!important; - border: 2px solid ButtonText; - height: 28px; - width: 44px -} - -.enable-forced-colors .container__3f21e.checked__3f21e { - background-color: Highlight!important; - border-color: Highlight -} - -.enable-forced-colors .container__3f21e.disabled__3f21e { - background-color: Canvas!important; - border-color: GrayText; - opacity: 1!important -} - -.enable-forced-colors .container__3f21e.disabled__3f21e.checked__3f21e { - background-color: GrayText!important; - border-color: GrayText -} - -.highlight-void-toggleables [data-toggleable-component=switch] { - box-shadow: 0 0 6px 2px var(--blue-360),0 0 8px 4px var(--opacity-blue-60) -} - -.fieldset__7fb92 { - display: flex; - flex-direction: column; - gap: var(--space-16) -} - -.description__7fb92,.legend__7fb92 { - margin-bottom: var(--space-12) -} - -.legend__7fb92+.description__7fb92 { - margin-bottom: 0; - margin-top: -8px -} - -.container__72c38 { - display: flex; - flex-direction: column; - flex-grow: 1 -} - -.container__72c38[data-full-width=true] { - flex: 1 1 auto; - width: 100% -} - -.container__72c38[data-full-width=true] .input__72c38,.container__72c38[data-full-width=true] .wrapper__72c38 { - width: 100% -} - -.wrapper__72c38 { - position: relative; - --custom-input-background-color: var(--input-background-default); - --custom-input-border-color: var(--input-border-default); - --custom-input-text-color: var(--input-text-default); - --custom-input-hover-border-color: var(--input-border-hover); - --custom-input-focus-border-color: var(--input-border-active); - align-items: center; - background-color: var(--custom-input-background-color); - border: 1px solid var(--custom-input-border-color); - border-radius: var(--radius-sm); - box-sizing: border-box; - color: var(--custom-input-text-color); - display: flex; - flex-wrap: wrap; - gap: var(--space-4); - transition: border-color .1s ease -} - -.wrapper__72c38[data-read-only=true] { - --custom-input-border-color: var(--input-border-readonly) -} - -.wrapper__72c38:not([data-read-only=true]):not([data-disabled=true]):not(:focus-within):hover { - --custom-input-border-color: var(--custom-input-hover-border-color) -} - -.wrapper__72c38:not([data-read-only=true]):has(input:focus,textarea:focus) { - --custom-input-border-color: var(--custom-input-focus-border-color); - outline: 2px solid var(--custom-input-focus-border-color); - outline-offset: -2px -} - -.wrapper__72c38[data-error=true] { - --custom-input-border-color: var(--input-border-error-default); - --custom-input-text-color: var(--input-text-error-default); - --custom-input-background-color: var(--input-background-error-default); - --custom-input-hover-border-color: var(--input-border-error-default); - --custom-input-focus-border-color: var(--input-border-error-default) -} - -.wrapper__72c38[data-error=true]:not([data-read-only=true],[data-disabled=true],:focus-within):hover { - outline: 2px solid var(--input-border-error-default); - outline-offset: -2px -} - -.wrapper__72c38[data-disabled=true] { - cursor: not-allowed; - opacity: .5; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.enable-forced-colors .wrapper__72c38 { - border: 1px solid ButtonText -} - -.enable-forced-colors .wrapper__72c38:not([data-read-only=true]):focus-within { - border-color: Highlight -} - -.enable-forced-colors .wrapper__72c38[data-disabled=true] { - background-color: Canvas; - border-color: GrayText; - opacity: 1 -} - -.wrapper_a35ace svg { - display: block -} - -.divider__1de9c { - border-top: thin solid var(--border-subtle); - height: 1px; - width: 100% -} - -.header_aa8da2,.item_aa8da2 { - flex-shrink: 0; - overflow: hidden -} - -.header_aa8da2,.headerText_aa8da2,.item_aa8da2 { - text-overflow: ellipsis; - white-space: nowrap -} - -.headerText_aa8da2 { - overflow-x: hidden -} - -.header_aa8da2 { - color: var(--channels-default) -} - -.item_aa8da2 { - cursor: pointer; - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px; - position: relative -} - -.brand_aa8da2.item_aa8da2,.themed_aa8da2.item_aa8da2 { - color: var(--text-subtle) -} - -.brand_aa8da2.item_aa8da2:active,.brand_aa8da2.item_aa8da2:hover,.themed_aa8da2.item_aa8da2:active,.themed_aa8da2.item_aa8da2:hover { - color: var(--text-strong) -} - -.separator_aa8da2 { - background-color: var(--border-subtle) -} - -.selected_aa8da2.item_aa8da2,.selected_aa8da2.item_aa8da2:hover { - color: var(--text-strong); - cursor: default -} - -.selected_aa8da2 { - cursor: default -} - -.disabled_aa8da2.item_aa8da2,.disabled_aa8da2.item_aa8da2:hover { - color: var(--text-muted); - cursor: default -} - -.density-compact .side_aa8da2 .item_aa8da2 { - line-height: 16px -} - -.density-cozy .side_aa8da2 .item_aa8da2 { - line-height: 24px -} - -.side_aa8da2 { - display: flex; - flex: 1; - flex-direction: column -} - -.side_aa8da2 .header_aa8da2,.side_aa8da2 .item_aa8da2 { - padding: 6px 10px -} - -.side_aa8da2 .header_aa8da2:first-child { - padding-top: 0 -} - -.side_aa8da2 .item_aa8da2 { - border-radius: 4px; - margin-bottom: 2px; - padding-bottom: 6px; - padding-top: 6px -} - -.side_aa8da2 .separator_aa8da2 { - height: 1px; - margin: 8px 10px -} - -.top_aa8da2 { - border-bottom-color: var(--border-subtle); - border-bottom: 1px solid var(--border-subtle); - display: flex; - flex-direction: row -} - -.top_aa8da2 .item_aa8da2 { - --selected-tab-item-color: var(--interactive-text-active); - border-bottom: none!important; - font-size: 14px; - font-weight: 600; - line-height: 14px; - margin-bottom: -1px; - padding-bottom: 16px; - position: relative; - --tab-item-color: var(--text-strong) -} - -.top_aa8da2 .item_aa8da2.selected_aa8da2:after,.top_aa8da2 .item_aa8da2:hover:after { - background: var(--selected-tab-item-color); - border-top-left-radius: 2px; - border-top-right-radius: 2px; - content: ""; - height: 2px; - left: 0; - margin-top: -2px; - position: absolute; - top: 100%; - width: 100%; - z-index: 1 -} - -.top_aa8da2 .item_aa8da2.selected_aa8da2 { - color: var(--selected-tab-item-color) -} - -.top_aa8da2 .item_aa8da2.brand_aa8da2 { - --selected-tab-item-color: var(--text-brand) -} - -.top_aa8da2 .item_aa8da2.brand_aa8da2:hover { - color: var(--text-strong) -} - -.top_aa8da2 .item_aa8da2+.item_aa8da2 { - margin-left: var(--space-xl) -} - -.top_aa8da2 .disabled_aa8da2.item_aa8da2 { - cursor: not-allowed -} - -.top_aa8da2 .themed_aa8da2.item_aa8da2:hover { - border-bottom-color: var(--interactive-text-hover) -} - -.top_aa8da2 .selected_aa8da2.themed_aa8da2.item_aa8da2,.top_aa8da2 .themed_aa8da2.item_aa8da2:active { - border-bottom-color: var(--interactive-text-active) -} - -.top_aa8da2 .brand_aa8da2.item_aa8da2:hover { - border-bottom-color: var(--brand-500) -} - -.top_aa8da2 .brand_aa8da2.item_aa8da2:active,.top_aa8da2 .brand_aa8da2.selected_aa8da2.item_aa8da2 { - border-bottom-color: var(--control-brand-foreground) -} - -.top_aa8da2 .disabled_aa8da2.brand_aa8da2.item_aa8da2:hover,.top_aa8da2 .disabled_aa8da2.themed_aa8da2.item_aa8da2:hover { - border-bottom-color: transparent -} - -.topPill_aa8da2 { - display: flex; - flex-direction: row -} - -.topPill_aa8da2 .item_aa8da2 { - border-radius: var(--radius-sm); - cursor: pointer; - margin: 0 8px; - min-height: 32px; - padding: 4px 12px -} - -.topPill_aa8da2 .separator_aa8da2 { - width: 1px -} - -.topPill_aa8da2 .item_aa8da2.disabled_aa8da2 { - cursor: not-allowed -} - -.side_aa8da2 .themed_aa8da2.item_aa8da2:hover:not(.disabled_aa8da2),.topPill_aa8da2 .themed_aa8da2.item_aa8da2:hover:not(.disabled_aa8da2) { - background-color: var(--background-mod-subtle) -} - -.side_aa8da2 .themed_aa8da2.item_aa8da2:active:not(.disabled_aa8da2),.side_aa8da2 .themed_aa8da2.selected_aa8da2.item_aa8da2,.side_aa8da2 .themed_aa8da2.selected_aa8da2:hover:not(.disabled_aa8da2),.topPill_aa8da2 .themed_aa8da2.item_aa8da2:active:not(.disabled_aa8da2),.topPill_aa8da2 .themed_aa8da2.selected_aa8da2.item_aa8da2,.topPill_aa8da2 .themed_aa8da2.selected_aa8da2:hover:not(.disabled_aa8da2) { - background-color: var(--background-mod-strong) -} - -.side_aa8da2 .destructive_aa8da2.item_aa8da2:not(.disabled_aa8da2),.topPill_aa8da2 .destructive_aa8da2.item_aa8da2:not(.disabled_aa8da2) { - color: var(--text-feedback-critical) -} - -.side_aa8da2 .destructive_aa8da2.item_aa8da2:hover:not(.disabled_aa8da2),.topPill_aa8da2 .destructive_aa8da2.item_aa8da2:hover:not(.disabled_aa8da2) { - background-color: var(--background-feedback-critical); - color: var(--text-feedback-critical) -} - -.side_aa8da2 .destructive_aa8da2.item_aa8da2:active:not(.disabled_aa8da2),.side_aa8da2 .destructive_aa8da2.selected_aa8da2.item_aa8da2:not(.disabled_aa8da2),.topPill_aa8da2 .destructive_aa8da2.item_aa8da2:active:not(.disabled_aa8da2),.topPill_aa8da2 .destructive_aa8da2.selected_aa8da2.item_aa8da2:not(.disabled_aa8da2) { - background-color: var(--background-feedback-critical); - color: var(--white) -} - -.full-motion .item_aa8da2 { - transition: background-color .3s ease -} - -.enable-forced-colors .item_aa8da2 { - background-color: ButtonFace!important; - border: 1px solid ButtonFace; - color: ButtonText!important; - forced-color-adjust: none -} - -.enable-forced-colors .item_aa8da2:hover { - background-color: ButtonFace!important; - border-color: ButtonText; - color: ButtonText!important -} - -.enable-forced-colors .item_aa8da2[aria-selected=true],.enable-forced-colors .item_aa8da2[aria-selected=true]:hover,.enable-forced-colors .selected_aa8da2.item_aa8da2,.enable-forced-colors .selected_aa8da2.item_aa8da2:hover { - background-color: HighlightText!important; - border-color: Highlight; - color: Highlight!important -} - -.enable-forced-colors .disabled_aa8da2.item_aa8da2,.enable-forced-colors .disabled_aa8da2.item_aa8da2:hover { - background-color: Canvas!important; - border-color: Canvas!important; - color: GrayText!important -} - -.enable-forced-colors .top_aa8da2 .item_aa8da2 { - border-radius: 4px 4px 0 0; - padding-left: 4px; - padding-right: 4px -} - -.refresh-fast-follow-distinct-borders .top_aa8da2 { - border-color: var(--app-frame-border) -} - -.container__2fba1 { - background-color: var(--background-mod-muted); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - width: 100% -} - -.footer__2fba1,.toolbar__2fba1 { - align-items: center; - display: flex; - justify-content: space-between; - padding: 16px -} - -.table__2fba1 { - border-collapse: collapse; - color: var(--text-default); - overflow: scroll; - table-layout: fixed; - width: 100% -} - -.header__2fba1 { - border-bottom: 1px solid var(--border-subtle) -} - -.body__2fba1 { - padding: 8px -} - -.row__2fba1 { - border-bottom: 1px solid var(--border-subtle); - outline: none; - transition: background-color .1s ease; - white-space: nowrap -} - -.row__2fba1[data-focus-visible] { - box-shadow: 0 0 0 4px var(--blue-345) -} - -.row__2fba1[data-pressed] { - background-color: var(--interactive-background-active) -} - -.row__2fba1[data-hovered] { - background-color: var(--interactive-background-hover) -} - -.row__2fba1[aria-selected=true] { - background-color: var(--interactive-background-selected) -} - -.row__2fba1:last-child { - border-bottom: none -} - -.column__2fba1 { - outline: none; - padding: 16px -} - -.column__2fba1[data-focus-visible] { - box-shadow: 0 0 0 4px var(--blue-345) -} - -.columnText__2fba1 { - text-align: start -} - -.cell__2fba1 { - outline: none; - padding: 16px -} - -.cell__2fba1[data-focus-visible] { - box-shadow: 0 0 0 4px var(--blue-345) -} - -.emptyStateText_deaec9 { - color: var(--text-muted); - font-style: italic -} - -.emptyState_deaec9 { - align-items: center; - display: flex; - justify-content: center; - min-height: 40px -} - -.listBox__2e223 { - box-sizing: border-box; - padding: var(--space-4) -} - -.listBox__2e223:not(.scrollable__2e223) ::-webkit-scrollbar { - height: 0; - width: 0 -} - -.listBoxInner__2e223,.listBoxItem__2e223 { - box-sizing: border-box -} - -.listBoxItem__2e223 { - align-items: stretch; - background-color: transparent; - border-radius: var(--radius-xs); - color: var(--text-strong); - cursor: pointer; - display: flex; - justify-content: flex-start; - transition: background-color 50ms ease-in -} - -.listBoxItem__2e223:hover { - transition: background-color .15s ease-out -} - -.listBoxItem__2e223:focus:not([aria-disabled]),.mouse-mode .listBoxItem__2e223:hover:not([aria-disabled]) { - background-color: var(--background-mod-normal) -} - -.listBoxItem__2e223[aria-disabled=true] { - background-color: transparent; - opacity: .5 -} - -.listBox__2e223:not(:focus-within) .listBoxItem__2e223[data-focus-visible=true],.keyboard-mode .listBoxItem__2e223:focus { - background-color: var(--background-mod-normal); - outline: 2px solid var(--border-focus); - outline-offset: -2px -} - -.listBoxItemContent__2e223 { - align-items: center; - box-sizing: border-box; - color: var(--text-strong); - display: flex; - flex-grow: 1; - justify-content: flex-start; - min-height: var(--select-option-height); - padding: calc(var(--space-xxs) - 1px) var(--space-4) calc(var(--space-xxs) - 1px) var(--space-8) -} - -.listBoxItemContent__2e223.inInput__2e223 { - min-height: calc(var(--select-option-height) - 2px) -} - -.listBoxItemLabel__2e223 { - flex-grow: 1; - flex-shrink: 1 -} - -.selectedIcon__2e223 { - padding: var(--space-xxs) var(--space-4) var(--space-xxs) 0 -} - -.loadingSpinnerWrapper__2e223,.selectedIcon__2e223 { - align-items: center; - display: flex; - justify-content: center -} - -.loadingSpinnerWrapper__2e223 { - height: var(--select-option-height) -} - -.loadingSpinner__2e223 { - width: 32px -} - -.loadingSpinnerInner__2e223 { - border-radius: 50%; - height: 8px; - width: 8px -} - -.option__56a50 { - align-items: center; - cursor: pointer; - display: grid; - flex-shrink: 1; - gap: var(--space-4); - grid-template-columns: auto 1fr auto; - grid-template-rows: 1fr -} - -.leading__56a50 { - box-sizing: border-box; - height: 32px; - margin-inline-start:calc(var(--space-8)*-1);padding: var(--space-4); - width: 32px -} - -.leading__56a50,.trailing__56a50 { - align-items: center; - display: flex; - justify-content: center -} - -.avatarAccessory__56a50,.imageAccessory__56a50 { - height: 24px; - -o-object-fit: contain; - object-fit: contain; - width: 24px -} - -.avatarAccessory__56a50 { - background-color: #000; - border-radius: var(--radius-round); - -o-object-fit: cover; - object-fit: cover; - overflow: hidden -} - -.iconAccessory__56a50 { - align-items: center; - box-sizing: border-box; - color: var(--icon-subtle); - display: flex; - height: 32px; - justify-content: center; - padding: var(--space-4); - width: 32px -} - -.selectFieldContainer__0edde { - align-self: flex-start; - max-width: 100% -} - -.selectFieldContainer__0edde.fullWidth__0edde { - align-self: unset -} - -.selectField__0edde { - align-items: stretch; - display: grid; - gap: 0; - grid-template-columns: 1fr auto auto; - grid-template-rows: 1fr; - width: 100% -} - -.selectField__0edde:not(.isInert__0edde).isFocused__0edde,.selectField__0edde:not(.isInert__0edde):focus-within { - outline: 2px solid var(--custom-input-focus-border-color); - outline-offset: -2px -} - -.selectField__0edde[data-read-only=true] { - cursor: default!important -} - -.selectField__0edde[data-disabled=true] { - cursor: not-allowed!important -} - -.selectButton__0edde,.selectFieldContent__0edde { - overflow: hidden -} - -.selectButton__0edde { - align-items: center; - background-color: transparent; - box-sizing: border-box; - display: flex; - gap: 4px; - max-width: 100%; - min-height: calc(var(--select-option-height) - 2px); - padding-inline-start:var(--space-4);text-align: start; - width: 100% -} - -.selectButton__0edde.wrapTags__0edde { - flex-wrap: wrap -} - -.selectButton__0edde.multiSelect__0edde { - padding: 4px -} - -.multipleValues__0edde,.placeholder__0edde { - align-items: center; - box-sizing: border-box; - display: inline-flex; - justify-content: flex-start; - max-width: 100%; - padding-block:var(--space-xxs) var(--space-xxs);padding-inline: var(--space-8) var(--space-4) -} - -.placeholder__0edde { - color: var(--input-placeholder-text-default) -} - -.multipleValues__0edde { - color: var(--text-strong) -} - -.singleSelectOption__0edde { - flex-grow: 1; - padding-inline-start:var(--space-4)} - -.comboBoxInputScroller__0edde: not(.wrapTags__0edde) { - overflow:auto hidden; - scrollbar-width: none -} - -.comboBoxInputScroller__0edde:not(.wrapTags__0edde)::-webkit-scrollbar { - display: none -} - -.comboBoxInputContainer__0edde { - align-items: center; - display: flex; - gap: var(--space-4); - justify-content: flex-start -} - -.hasTags__0edde .comboBoxInputContainer__0edde { - padding: var(--space-4) -} - -.wrapTags__0edde .comboBoxInputContainer__0edde { - flex-wrap: wrap -} - -.comboBoxInput__0edde { - max-height: 1lh; - min-height: calc(var(--select-option-height) - 2px); - min-width: min(12ch,80%); - padding-inline:var(--space-12) var(--space-8)} - -.comboBoxInputScroller__0edde.hasTags__0edde .comboBoxInput__0edde { - margin: calc(var(--space-4)*-1) 0; - padding-inline-start:var(--space-8)} - -.chevronButton__0edde { - background-color: transparent; - line-height: 0 -} - -.chevronButton__0edde,.loadingSpinner__0edde { - align-items: center; - color: var(--icon-strong); - display: flex; - height: 100%; - justify-content: center; - padding-block:0;padding-inline:0 var(--space-4);width: 36px -} - -.isInert__0edde .chevronButton__0edde,.isInert__0edde .loadingSpinner__0edde { - cursor: not-allowed -} - -.chevronIcon__0edde { - display: block; - height: 20px; - transform: rotateX(0deg); - width: 20px -} - -.chevronIcon__0edde.isOpen__0edde { - transform: rotateX(180deg) -} - -.full-motion .chevronIcon__0edde { - transition: transform .2s ease-out -} - -.clearButton__0edde { - background-color: transparent; - color: var(--icon-subtle); - line-height: 0; - padding: 0 var(--space-4); - transition: color .2s ease-out -} - -.clearButton__0edde:focus,.clearButton__0edde:hover { - color: var(--icon-strong) -} - -.isInert__0edde .clearButton__0edde { - cursor: not-allowed -} - -.clearButton__0edde:last-child { - padding-block:0;padding-inline:0 var(--space-4);width: 36px -} - -.hiddenVisually__0edde { - clip: rect(0 0 0 0); - clip-path: inset(50%); - height: 1px; - overflow: hidden; - position: absolute; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - white-space: nowrap; - width: 1px -} - -.selectDropdown__0edde { - background-color: var(--background-surface-higher); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-shadow: 0 2px 8px 0 var(--opacity-black-16); - box-sizing: border-box; - z-index: 10 -} - -.anchor_edefb8 { - color: var(--text-link); - -webkit-text-decoration: var(--link-decoration); - text-decoration: var(--link-decoration) -} - -.anchorUnderlineOnHover_edefb8:hover { - text-decoration: underline -} - -.enable-forced-colors .anchor_edefb8,.enable-forced-colors .anchorUnderlineOnHover_edefb8 { - background-color: Canvas; - color: LinkText!important; - text-decoration: underline -} - -.enable-forced-colors .anchor_edefb8 svg,.enable-forced-colors .anchorUnderlineOnHover_edefb8 svg { - color: currentColor -} - -.enable-forced-colors .anchor_edefb8:not([href]),.enable-forced-colors .anchorUnderlineOnHover_edefb8:not([href]) { - background-color: ButtonFace; - color: ButtonText!important -} - -html.decorate-links .lowSaturationUnderline__41f68 { - text-decoration: underline!important -} - -[data-accessibility=desaturate] { - filter: saturate(var(--saturation-factor,1)) -} - -.desaturate__41f68 { - filter: saturate(var(--saturation-factor,1)) -} - -.theme-dark { - --brightness: calc(1.5 - var(--saturation-factor, 1)*0.5); - --contrast: var(--saturation-factor,1) -} - -.theme-light { - --brightness: calc(0.5 + var(--saturation-factor, 1)*0.5); - --contrast: var(--saturation-factor,1) -} - -.desaturate-user-colors .desaturateUserColors__41f68 { - filter: saturate(var(--saturation-factor,1)) contrast(var(--contrast,1)) brightness(var(--brightness,1)) -} - -.button__201d5 { - align-items: center; - background: none; - border: 1px solid var(--opacity-white-8); - border-radius: 8px; - box-sizing: border-box; - display: flex; - font-size: 14px; - font-weight: var(--font-weight-medium); - justify-content: center; - line-height: 16px; - padding: 2px 16px; - position: relative; - transition-duration: .2s; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.button__201d5:disabled,.button__201d5[aria-disabled=true] { - cursor: not-allowed; - opacity: .5 -} - -.button__201d5 .contents__201d5 { - --button--underline-color: transparent; - background-image: linear-gradient(to top,transparent,transparent var(--custom-button-link-underline-offset),var(--button--underline-color) var(--custom-button-link-underline-offset),var(--button--underline-color) var(--custom-button-link-underline-stop),transparent var(--custom-button-link-underline-stop)) -} - -.highlight-void-buttons .button__201d5 { - box-shadow: 0 0 4px 4px var(--yellow-new-30) -} - -.lookFilled__201d5.colorBrand__201d5 { - background-color: var(--control-primary-background-default); - border: 1px solid var(--control-primary-border-default); - color: var(--control-primary-text-default) -} - -.lookFilled__201d5.colorBrand__201d5:hover { - background-color: var(--control-primary-background-hover) -} - -.lookFilled__201d5.colorBrand__201d5:active { - background-color: var(--control-primary-background-active) -} - -.lookFilled__201d5.colorBrand__201d5 .spinnerItem__201d5 { - background-color: var(--control-primary-text-default) -} - -.lookFilled__201d5.colorBrand__201d5:disabled,.lookFilled__201d5.colorBrand__201d5[aria-disabled=true] { - background-color: var(--control-primary-background-default) -} - -.lookFilled__201d5.colorBrandInverted__201d5 { - background-color: var(--control-overlay-primary-background-default); - color: var(--control-primary-text-default) -} - -.lookFilled__201d5.colorBrandInverted__201d5:hover { - background-color: var(--control-overlay-primary-background-hover) -} - -.lookFilled__201d5.colorBrandInverted__201d5:active { - background-color: var(--control-overlay-primary-background-active) -} - -.lookFilled__201d5.colorBrandInverted__201d5 .spinnerItem__201d5 { - background-color: var(--control-primary-text-default) -} - -.lookFilled__201d5.colorBrandInverted__201d5:disabled,.lookFilled__201d5.colorBrandInverted__201d5[aria-disabled=true] { - background-color: var(--control-overlay-primary-background-default) -} - -.lookOutlined__201d5.colorBrand__201d5 { - border-color: var(--control-secondary-border-default); - color: var(--control-secondary-text-default) -} - -.lookOutlined__201d5.colorBrand__201d5:hover { - background-color: var(--button-outline-brand-background-hover); - border-color: var(--control-secondary-border-hover); - color: var(--control-secondary-text-hover) -} - -.lookOutlined__201d5.colorBrand__201d5:active { - background-color: var(--control-secondary-background-active); - border-color: var(--button-outline-brand-border-active); - color: var(--control-secondary-text-active) -} - -.lookOutlined__201d5.colorBrand__201d5:disabled,.lookOutlined__201d5.colorBrand__201d5[aria-disabled=true] { - background-color: transparent -} - -.lookOutlined__201d5.colorBrand__201d5 .spinnerItem__201d5 { - background-color: var(--brand-500) -} - -.lookLink__201d5.colorBrand__201d5 { - color: var(--brand-500) -} - -.lookLink__201d5.colorBrand__201d5:hover .contents__201d5 { - --button--underline-color: var(--brand-500) -} - -.lookLink__201d5.colorBrand__201d5:disabled .contents__201d5,.lookLink__201d5.colorBrand__201d5[aria-disabled=true] .contents__201d5 { - background-image: none -} - -.lookLink__201d5.colorBrand__201d5 .spinnerItem__201d5 { - background-color: var(--brand-500) -} - -.lookOutlined__201d5.colorPrimary__201d5 { - border-color: var(--control-secondary-border-default); - color: var(--button-outline-primary-text) -} - -.lookOutlined__201d5.colorPrimary__201d5:hover { - background-color: var(--control-secondary-background-hover); - border-color: var(--control-secondary-border-hover); - color: var(--control-secondary-text-hover) -} - -.lookOutlined__201d5.colorPrimary__201d5:active { - background-color: var(--control-secondary-background-active); - border-color: var(--control-secondary-border-active); - color: var(--control-secondary-text-active) -} - -.lookOutlined__201d5.colorPrimary__201d5:disabled,.lookOutlined__201d5.colorPrimary__201d5[aria-disabled=true] { - background-color: transparent; - color: var(--button-outline-primary-text) -} - -.lookOutlined__201d5.colorPrimary__201d5 .spinnerItem__201d5 { - background-color: var(--white) -} - -.lookFilled__201d5.colorLink__201d5 { - background-color: var(--text-link); - color: var(--white) -} - -.lookFilled__201d5.colorLink__201d5:hover { - background-color: var(--blue-500) -} - -.lookFilled__201d5.colorLink__201d5:active { - background-color: var(--blue-530) -} - -.lookFilled__201d5.colorLink__201d5 .spinnerItem__201d5 { - background-color: var(--white) -} - -.lookFilled__201d5.colorLink__201d5:disabled,.lookFilled__201d5.colorLink__201d5[aria-disabled=true] { - background-color: var(--text-link) -} - -.lookOutlined__201d5.colorLink__201d5 { - border-color: var(--text-link); - color: var(--text-link) -} - -.lookOutlined__201d5.colorLink__201d5:active { - background-color: hsl(var(--text-link-hsl)/.1) -} - -.lookOutlined__201d5.colorLink__201d5:disabled,.lookOutlined__201d5.colorLink__201d5[aria-disabled=true] { - background-color: transparent -} - -.lookOutlined__201d5.colorLink__201d5 .spinnerItem__201d5 { - background-color: var(--text-link) -} - -.lookLink__201d5.colorLink__201d5 { - color: var(--text-link) -} - -.lookLink__201d5.colorLink__201d5:hover .contents__201d5 { - --button--underline-color: var(--text-link) -} - -.lookLink__201d5.colorLink__201d5:disabled .contents__201d5,.lookLink__201d5.colorLink__201d5[aria-disabled=true] .contents__201d5 { - background-image: none -} - -.lookLink__201d5.colorLink__201d5 .spinnerItem__201d5 { - background-color: var(--text-link) -} - -.lookFilled__201d5.colorWhite__201d5 { - background-color: var(--control-overlay-primary-background-default); - border-color: var(--opacity-8); - color: var(--control-primary-text-default) -} - -.lookFilled__201d5.colorWhite__201d5:hover { - background-color: var(--control-overlay-primary-background-hover) -} - -.lookFilled__201d5.colorWhite__201d5:active { - background-color: var(--control-overlay-primary-background-active) -} - -.lookFilled__201d5.colorWhite__201d5 .spinnerItem__201d5 { - background-color: var(--control-primary-text-default) -} - -.lookFilled__201d5.colorWhite__201d5:disabled,.lookFilled__201d5.colorWhite__201d5[aria-disabled=true] { - background-color: var(--control-overlay-primary-background-default) -} - -.lookOutlined__201d5.colorWhite__201d5 { - border-color: var(--white); - color: var(--white) -} - -.lookOutlined__201d5.colorWhite__201d5:active { - background-color: hsl(var(--white-hsl)/.1) -} - -.lookOutlined__201d5.colorWhite__201d5:disabled,.lookOutlined__201d5.colorWhite__201d5[aria-disabled=true] { - background-color: transparent -} - -.lookOutlined__201d5.colorWhite__201d5 .spinnerItem__201d5 { - background-color: var(--white) -} - -.lookLink__201d5.colorWhite__201d5 { - color: var(--white) -} - -.lookLink__201d5.colorWhite__201d5:hover .contents__201d5 { - --button--underline-color: var(--white) -} - -.lookLink__201d5.colorWhite__201d5:disabled .contents__201d5,.lookLink__201d5.colorWhite__201d5[aria-disabled=true] .contents__201d5 { - background-image: none -} - -.lookLink__201d5.colorWhite__201d5 .spinnerItem__201d5 { - background-color: var(--white) -} - -.lookFilled__201d5.colorRed__201d5 { - background-color: var(--control-critical-primary-background-default); - border: 1px solid var(--control-critical-primary-border-default); - color: var(--white) -} - -.lookFilled__201d5.colorRed__201d5:hover { - background-color: var(--control-critical-primary-background-hover) -} - -.lookFilled__201d5.colorRed__201d5:active { - background-color: var(--control-critical-primary-background-active) -} - -.lookFilled__201d5.colorRed__201d5 .spinnerItem__201d5 { - background-color: var(--white) -} - -.lookFilled__201d5.colorRed__201d5:disabled,.lookFilled__201d5.colorRed__201d5[aria-disabled=true] { - background-color: var(--control-critical-primary-background-default) -} - -.lookOutlined__201d5.colorRed__201d5 { - background-color: var(--control-critical-secondary-background-default); - border-color: var(--control-critical-secondary-border-default); - color: var(--control-critical-primary-text-default) -} - -.lookOutlined__201d5.colorRed__201d5:hover { - background-color: var(--control-critical-secondary-background-hover); - border-color: var(--control-critical-secondary-border-hover); - color: var(--control-critical-primary-text-hover) -} - -.lookOutlined__201d5.colorRed__201d5:active { - background-color: var(--control-critical-secondary-background-active); - border-color: var(--control-critical-secondary-border-active); - color: var(--control-critical-primary-text-active) -} - -.lookOutlined__201d5.colorRed__201d5:disabled,.lookOutlined__201d5.colorRed__201d5[aria-disabled=true] { - background-color: transparent -} - -.lookOutlined__201d5.colorRed__201d5 .spinnerItem__201d5 { - background-color: var(--red-400) -} - -.lookLink__201d5.colorRed__201d5 { - color: var(--text-feedback-critical) -} - -.lookLink__201d5.colorRed__201d5:hover .contents__201d5 { - --button--underline-color: var(--text-feedback-critical) -} - -.lookLink__201d5.colorRed__201d5:disabled .contents__201d5,.lookLink__201d5.colorRed__201d5[aria-disabled=true] .contents__201d5 { - background-image: none -} - -.lookLink__201d5.colorRed__201d5 .spinnerItem__201d5 { - background-color: var(--text-feedback-critical) -} - -.lookFilled__201d5.colorGreen__201d5 { - background-color: var(--control-connected-background-default); - border: 1px solid var(--control-connected-border-default); - color: var(--white) -} - -.lookFilled__201d5.colorGreen__201d5:hover { - background-color: var(--control-connected-background-hover) -} - -.lookFilled__201d5.colorGreen__201d5:active { - background-color: var(--control-connected-background-active) -} - -.lookFilled__201d5.colorGreen__201d5 .spinnerItem__201d5 { - background-color: var(--white) -} - -.lookFilled__201d5.colorGreen__201d5:disabled,.lookFilled__201d5.colorGreen__201d5[aria-disabled=true] { - background-color: var(--control-connected-background-default) -} - -.lookOutlined__201d5.colorGreen__201d5 { - border-color: var(--control-connected-border-default); - color: var(--control-primary-text-default) -} - -.lookOutlined__201d5.colorGreen__201d5:hover { - background-color: var(--control-connected-background-hover); - border-color: var(--control-connected-border-hover); - color: var(--control-primary-text-hover) -} - -.lookOutlined__201d5.colorGreen__201d5:active { - background-color: var(--control-connected-background-active); - border-color: var(--control-connected-border-active); - color: var(--control-primary-text-active) -} - -.lookOutlined__201d5.colorGreen__201d5:disabled,.lookOutlined__201d5.colorGreen__201d5[aria-disabled=true] { - background-color: transparent -} - -.lookOutlined__201d5.colorGreen__201d5 .spinnerItem__201d5 { - background-color: var(--green-230) -} - -.lookLink__201d5.colorGreen__201d5 { - color: var(--green-360) -} - -.lookLink__201d5.colorGreen__201d5:hover .contents__201d5 { - --button--underline-color: var(--green-360) -} - -.lookLink__201d5.colorGreen__201d5:disabled .contents__201d5,.lookLink__201d5.colorGreen__201d5[aria-disabled=true] .contents__201d5 { - background-image: none -} - -.lookLink__201d5.colorGreen__201d5 .spinnerItem__201d5 { - background-color: var(--green-360) -} - -.lookFilled__201d5.colorPrimary__201d5 { - background-color: var(--control-secondary-background-default); - border-color: var(--border-muted); - color: var(--control-secondary-text-default) -} - -.lookFilled__201d5.colorPrimary__201d5:hover { - background-color: var(--control-secondary-background-hover) -} - -.lookFilled__201d5.colorPrimary__201d5:active { - background-color: var(--control-secondary-background-active) -} - -.lookFilled__201d5.colorPrimary__201d5 .spinnerItem__201d5 { - background-color: var(--control-secondary-text-default) -} - -.lookFilled__201d5.colorPrimary__201d5:disabled,.lookFilled__201d5.colorPrimary__201d5[aria-disabled=true],.lookFilled__201d5.colorTransparent__201d5 { - background-color: var(--control-secondary-background-default) -} - -.lookFilled__201d5.colorTransparent__201d5 { - border-color: var(--border-muted); - color: var(--control-secondary-text-default) -} - -.lookFilled__201d5.colorTransparent__201d5:hover { - background-color: var(--control-secondary-background-hover) -} - -.lookFilled__201d5.colorTransparent__201d5:active { - background-color: var(--control-secondary-background-active) -} - -.lookFilled__201d5.colorTransparent__201d5 .spinnerItem__201d5 { - background-color: var(--control-secondary-text-default) -} - -.lookFilled__201d5.colorTransparent__201d5:disabled,.lookFilled__201d5.colorTransparent__201d5[aria-disabled=true] { - background-color: var(--control-secondary-background-default) -} - -.theme-dark .lookLink__201d5.colorPrimary__201d5:hover .contents__201d5 { - --button--underline-color: var(--white) -} - -.theme-dark .lookLink__201d5.colorPrimary__201d5 .spinnerItem__201d5 { - background-color: var(--white) -} - -.theme-dark .lookOutlined__201d5.colorTransparent__201d5 { - border-color: var(--primary-200) -} - -.theme-dark .lookOutlined__201d5.colorTransparent__201d5:active { - background-color: hsl(var(--primary-200-hsl)/.1) -} - -.theme-dark .lookOutlined__201d5.colorTransparent__201d5 .spinnerItem__201d5 { - background-color: var(--primary-200) -} - -.theme-dark .lookLink__201d5.colorTransparent__201d5:hover .contents__201d5 { - --button--underline-color: var(--primary-200) -} - -.theme-dark .lookLink__201d5.colorTransparent__201d5 .spinnerItem__201d5 { - background-color: var(--primary-200) -} - -.theme-light .lookLink__201d5.colorPrimary__201d5:hover .contents__201d5 { - --button--underline-color: var(--primary-400) -} - -.theme-light .lookLink__201d5.colorPrimary__201d5 .spinnerItem__201d5 { - background-color: var(--primary-400) -} - -.theme-light .lookOutlined__201d5.colorTransparent__201d5 { - border-color: var(--primary-400) -} - -.theme-light .lookOutlined__201d5.colorTransparent__201d5:active { - background-color: hsl(var(--primary-400-hsl)/.1) -} - -.theme-light .lookOutlined__201d5.colorTransparent__201d5 .spinnerItem__201d5 { - background-color: var(--primary-400) -} - -.theme-light .lookLink__201d5.colorTransparent__201d5:hover .contents__201d5 { - --button--underline-color: var(--primary-400) -} - -.theme-light .lookLink__201d5.colorTransparent__201d5 .spinnerItem__201d5 { - background-color: var(--primary-400) -} - -.lookFilled__201d5 { - transition: background-color var(--custom-button-transition-duration) ease,color var(--custom-button-transition-duration) ease -} - -.lookOutlined__201d5 { - border-style: solid; - border-width: 1px; - transition: color var(--custom-button-transition-duration) ease,background-color var(--custom-button-transition-duration) ease,border-color var(--custom-button-transition-duration) ease -} - -.lookBlank__201d5 { - background: transparent; - border: 0; - color: currentColor; - margin: 0; - padding: 0 -} - -.sizeTiny__201d5 { - height: var(--custom-button-button-tn-height); - min-height: var(--custom-button-button-tn-height); - min-width: var(--custom-button-button-tn-width); - width: var(--custom-button-button-tn-width) -} - -.sizeSmall__201d5 { - height: 32px; - line-height: 18px; - min-height: 32px; - min-width: 60px; - padding: calc(var(--space-4) - 1px) calc(var(--space-12) - 1px); - width: 60px -} - -.sizeMedium__201d5 { - font-size: 16px; - height: 40px; - line-height: 18px; - min-height: 40px; - min-width: 100px; - padding: calc(var(--space-8) - 1px) calc(var(--space-16) - 1px); - width: 100px -} - -.sizeLarge__201d5 { - height: var(--custom-button-button-lg-height); - min-height: var(--custom-button-button-lg-height); - min-width: var(--custom-button-button-lg-width); - width: var(--custom-button-button-lg-width) -} - -.sizeMin__201d5 { - display: inline; - height: auto; - padding: 0 4px; - width: auto -} - -.sizeMax__201d5 { - font-size: 16px; - height: 100%; - min-height: 100%; - min-width: 100%; - width: 100% -} - -.sizeIcon__201d5 { - height: auto; - padding: 4px -} - -.grow__201d5,.sizeIcon__201d5 { - width: auto -} - -.fullWidth__201d5 { - width: 100% -} - -.submitting__201d5 { - pointer-events: none -} - -.lookFilled__201d5 .contents__201d5,.lookLink__201d5 .contents__201d5,.lookOutlined__201d5 .contents__201d5 { - margin: 0 auto; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.lookLink__201d5 { -} - -@media (-ms-high-contrast:active),(-ms-high-contrast:none) { - .lookFilled__201d5 .contents__201d5,.lookLink__201d5 .contents__201d5,.lookOutlined__201d5 .contents__201d5 { - margin: 0 - } -} - -.submitting__201d5 .contents__201d5 { - visibility: hidden -} - -.spinner__201d5 { - align-items: center; - display: flex; - height: 100%; - inset-inline-start: 0; - justify-content: center; - margin: 0; - position: absolute; - top: 0; - width: 100% -} - -.disabledButtonWrapper__201d5 { - display: inline-block; - padding: 0; - position: relative -} - -.disabledButtonWrapper__201d5 .button__201d5.grow__201d5 { - width: 100% -} - -.disabledButtonOverlay__201d5 { - cursor: not-allowed; - inset: 0; - position: absolute; - z-index: 9 -} - -.enable-forced-colors.theme-dark .button__201d5,.enable-forced-colors.theme-light .button__201d5 { - background-color: ButtonFace; - transition: none -} - -.enable-forced-colors.theme-dark .button__201d5 .contents__201d5,.enable-forced-colors.theme-light .button__201d5 .contents__201d5 { - color: ButtonText; - forced-color-adjust: none -} - -.enable-forced-colors.theme-dark .button__201d5 .contents__201d5 svg,.enable-forced-colors.theme-light .button__201d5 .contents__201d5 svg { - color: currentColor -} - -.enable-forced-colors.theme-dark .button__201d5:hover,.enable-forced-colors.theme-light .button__201d5:hover { - background-color: ButtonFace -} - -.enable-forced-colors.theme-dark .button__201d5:hover .contents__201d5,.enable-forced-colors.theme-light .button__201d5:hover .contents__201d5 { - color: ButtonText; - text-decoration: underline -} - -.enable-forced-colors.theme-dark .button__201d5:disabled,.enable-forced-colors.theme-dark .button__201d5[aria-disabled=true],.enable-forced-colors.theme-light .button__201d5:disabled,.enable-forced-colors.theme-light .button__201d5[aria-disabled=true] { - background-color: Canvas; - border-color: GrayText!important; - opacity: 1 -} - -.enable-forced-colors.theme-dark .button__201d5:disabled .contents__201d5,.enable-forced-colors.theme-dark .button__201d5[aria-disabled=true] .contents__201d5,.enable-forced-colors.theme-light .button__201d5:disabled .contents__201d5,.enable-forced-colors.theme-light .button__201d5[aria-disabled=true] .contents__201d5 { - color: GrayText -} - -.enable-forced-colors.theme-dark .button__201d5.lookLink__201d5 .contents__201d5,.enable-forced-colors.theme-light .button__201d5.lookLink__201d5 .contents__201d5 { - text-decoration: underline -} - -.enable-forced-colors.theme-dark .button__201d5.lookLink__201d5:not(:disabled),.enable-forced-colors.theme-dark .button__201d5.lookLink__201d5:not([aria-disabled=true]),.enable-forced-colors.theme-light .button__201d5.lookLink__201d5:not(:disabled),.enable-forced-colors.theme-light .button__201d5.lookLink__201d5:not([aria-disabled=true]) { - background-color: Canvas -} - -.enable-forced-colors.theme-dark .button__201d5.lookLink__201d5:not(:disabled) .contents__201d5,.enable-forced-colors.theme-dark .button__201d5.lookLink__201d5:not([aria-disabled=true]) .contents__201d5,.enable-forced-colors.theme-light .button__201d5.lookLink__201d5:not(:disabled) .contents__201d5,.enable-forced-colors.theme-light .button__201d5.lookLink__201d5:not([aria-disabled=true]) .contents__201d5 { - color: LinkText -} - -.enable-forced-colors.theme-dark .button__201d5.lookLink__201d5:not(:disabled):hover,.enable-forced-colors.theme-dark .button__201d5.lookLink__201d5:not([aria-disabled=true]):hover,.enable-forced-colors.theme-light .button__201d5.lookLink__201d5:not(:disabled):hover,.enable-forced-colors.theme-light .button__201d5.lookLink__201d5:not([aria-disabled=true]):hover { - background-color: Canvas -} - -.enable-forced-colors.theme-dark .button__201d5.lookLink__201d5:not(:disabled):hover .contents__201d5,.enable-forced-colors.theme-dark .button__201d5.lookLink__201d5:not([aria-disabled=true]):hover .contents__201d5,.enable-forced-colors.theme-light .button__201d5.lookLink__201d5:not(:disabled):hover .contents__201d5,.enable-forced-colors.theme-light .button__201d5.lookLink__201d5:not([aria-disabled=true]):hover .contents__201d5 { - color: LinkText -} - -.enable-forced-colors.theme-dark .button__201d5.lookFilled__201d5,.enable-forced-colors.theme-dark .button__201d5.lookOutlined__201d5,.enable-forced-colors.theme-light .button__201d5.lookFilled__201d5,.enable-forced-colors.theme-light .button__201d5.lookOutlined__201d5 { - border-style: solid; - border-width: 1px -} - -.enable-forced-colors.theme-dark .button__201d5.lookFilled__201d5:not(:disabled),.enable-forced-colors.theme-dark .button__201d5.lookFilled__201d5:not([aria-disabled=true]),.enable-forced-colors.theme-dark .button__201d5.lookOutlined__201d5:not(:disabled),.enable-forced-colors.theme-dark .button__201d5.lookOutlined__201d5:not([aria-disabled=true]),.enable-forced-colors.theme-light .button__201d5.lookFilled__201d5:not(:disabled),.enable-forced-colors.theme-light .button__201d5.lookFilled__201d5:not([aria-disabled=true]),.enable-forced-colors.theme-light .button__201d5.lookOutlined__201d5:not(:disabled),.enable-forced-colors.theme-light .button__201d5.lookOutlined__201d5:not([aria-disabled=true]) { - border-color: CanvasText!important -} - -.enable-forced-colors.theme-dark .button__201d5.lookFilled__201d5:not(:disabled):hover,.enable-forced-colors.theme-dark .button__201d5.lookFilled__201d5:not([aria-disabled=true]):hover,.enable-forced-colors.theme-dark .button__201d5.lookOutlined__201d5:not(:disabled):hover,.enable-forced-colors.theme-dark .button__201d5.lookOutlined__201d5:not([aria-disabled=true]):hover,.enable-forced-colors.theme-light .button__201d5.lookFilled__201d5:not(:disabled):hover,.enable-forced-colors.theme-light .button__201d5.lookFilled__201d5:not([aria-disabled=true]):hover,.enable-forced-colors.theme-light .button__201d5.lookOutlined__201d5:not(:disabled):hover,.enable-forced-colors.theme-light .button__201d5.lookOutlined__201d5:not([aria-disabled=true]):hover { - border-color: ButtonText!important -} - -.enable-forced-colors.theme-dark .button__201d5:active,.enable-forced-colors.theme-dark .button__201d5[aria-expanded=true],.enable-forced-colors.theme-dark .button__201d5[aria-selected=true],.enable-forced-colors.theme-light .button__201d5:active,.enable-forced-colors.theme-light .button__201d5[aria-expanded=true],.enable-forced-colors.theme-light .button__201d5[aria-selected=true] { - background-color: HighlightText -} - -.enable-forced-colors.theme-dark .button__201d5:active .contents__201d5,.enable-forced-colors.theme-dark .button__201d5[aria-expanded=true] .contents__201d5,.enable-forced-colors.theme-dark .button__201d5[aria-selected=true] .contents__201d5,.enable-forced-colors.theme-light .button__201d5:active .contents__201d5,.enable-forced-colors.theme-light .button__201d5[aria-expanded=true] .contents__201d5,.enable-forced-colors.theme-light .button__201d5[aria-selected=true] .contents__201d5 { - color: Highlight; - text-decoration: underline -} - -.enable-forced-colors.theme-dark .button__201d5:active:hover,.enable-forced-colors.theme-dark .button__201d5[aria-expanded=true]:hover,.enable-forced-colors.theme-dark .button__201d5[aria-selected=true]:hover,.enable-forced-colors.theme-light .button__201d5:active:hover,.enable-forced-colors.theme-light .button__201d5[aria-expanded=true]:hover,.enable-forced-colors.theme-light .button__201d5[aria-selected=true]:hover { - background-color: HighlightText -} - -.enable-forced-colors.theme-dark .button__201d5:active:hover .contents__201d5,.enable-forced-colors.theme-dark .button__201d5[aria-expanded=true]:hover .contents__201d5,.enable-forced-colors.theme-dark .button__201d5[aria-selected=true]:hover .contents__201d5,.enable-forced-colors.theme-light .button__201d5:active:hover .contents__201d5,.enable-forced-colors.theme-light .button__201d5[aria-expanded=true]:hover .contents__201d5,.enable-forced-colors.theme-light .button__201d5[aria-selected=true]:hover .contents__201d5 { - color: Highlight -} - -.enable-forced-colors.theme-dark .button__201d5.lookFilled__201d5:active,.enable-forced-colors.theme-dark .button__201d5.lookFilled__201d5[aria-expanded=true],.enable-forced-colors.theme-dark .button__201d5.lookFilled__201d5[aria-selected=true],.enable-forced-colors.theme-dark .button__201d5.lookOutlined__201d5:active,.enable-forced-colors.theme-dark .button__201d5.lookOutlined__201d5[aria-expanded=true],.enable-forced-colors.theme-dark .button__201d5.lookOutlined__201d5[aria-selected=true],.enable-forced-colors.theme-light .button__201d5.lookFilled__201d5:active,.enable-forced-colors.theme-light .button__201d5.lookFilled__201d5[aria-expanded=true],.enable-forced-colors.theme-light .button__201d5.lookFilled__201d5[aria-selected=true],.enable-forced-colors.theme-light .button__201d5.lookOutlined__201d5:active,.enable-forced-colors.theme-light .button__201d5.lookOutlined__201d5[aria-expanded=true],.enable-forced-colors.theme-light .button__201d5.lookOutlined__201d5[aria-selected=true] { - border-color: Highlight!important -} - -.lookLink__201d5.colorPrimary__201d5 { - color: var(--text-default) -} - -.images-light .lookLink__201d5.colorPrimary__201d5:disabled .contents__201d5,.images-light .lookLink__201d5.colorPrimary__201d5[aria-disabled=true] .contents__201d5 { - background-image: none -} - -.images-dark .lookLink__201d5.colorPrimary__201d5:disabled .contents__201d5,.images-dark .lookLink__201d5.colorPrimary__201d5[aria-disabled=true] .contents__201d5 { - background-image: none -} - -.lookOutlined__201d5.colorTransparent__201d5 { - color: var(--text-default) -} - -.lookOutlined__201d5.colorTransparent__201d5:disabled,.lookOutlined__201d5.colorTransparent__201d5[aria-disabled=true] { - background-color: transparent -} - -.lookLink__201d5.colorTransparent__201d5 { - color: var(--text-default) -} - -.lookLink__201d5.sizeMin__201d5 .contents__201d5 { - display: inline -} - -&.theme-dark .lookLink__201d5.colorLink__201d5,&.theme-dark .lookLink__201d5.colorPrimary__201d5 { - color: var(--brand-360) -} - -&.theme-dark .lookLink__201d5.colorLink__201d5:hover .contents__201d5,&.theme-dark .lookLink__201d5.colorPrimary__201d5:hover .contents__201d5 { - --button--underline-color: var(--brand-360) -} - -.lookBlank__201d5,.lookLink__201d5 { - border: none -} - -.inputWrapper__0ed4f { - display: flex; - flex-direction: column -} - -.input__0ed4f { - background-color: var(--input-background-default); - border: 1px solid var(--input-border-default); - border-radius: 8px; - box-sizing: border-box; - color: var(--text-default); - font-size: 16px; - height: 44px; - padding: 12px 10px; - transition: border-color .2s ease-in-out; - width: 100% -} - -.input__0ed4f::-moz-placeholder { - color: var(--input-placeholder-text-default); - -moz-user-select: none; - user-select: none -} - -.input__0ed4f::placeholder { - color: var(--input-placeholder-text-default); - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.input__0ed4f:hover { - border-color: var(--input-border-hover) -} - -.input__0ed4f.focused__0ed4f,.input__0ed4f:focus { - border-color: var(--text-link) -} - -.input__0ed4f.error__0ed4f { - border-color: var(--text-feedback-critical); - border-width: 2px; - color: var(--text-feedback-critical) -} - -.input__0ed4f.success__0ed4f { - border-color: var(--green-360) -} - -.input__0ed4f.disabled__0ed4f { - border-color: var(--input-border-default); - cursor: not-allowed; - opacity: 1 -} - -.input__0ed4f.editable__0ed4f { - background-color: transparent; - border-color: transparent -} - -.input__0ed4f:hover,.input__0ed4f[readonly] { - border: 1px solid var(--input-border-default) -} - -.input__0ed4f:not([readOnly]).focused__0ed4f,.input__0ed4f:not([readOnly]).focused__0ed4f:hover,.input__0ed4f:not([readOnly]):focus,.input__0ed4f:not([readOnly]):focus:hover { - border-color: var(--text-link) -} - -.disabled__0ed4f { - cursor: not-allowed; - opacity: .5; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.focused__0ed4f { - border-color: var(--brand-500); - opacity: 1 -} - -.inputPrefix__0ed4f { - color: var(--text-muted); - font-size: 16px; - height: 40px; - line-height: normal; - padding-top: 10px; - padding-inline-start:20px;position: absolute; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.theme-dark .inputPrefix__0ed4f { - opacity: .5 -} - -.enable-forced-colors .input__0ed4f { - border: 1px solid ButtonText -} - -.enable-forced-colors .input__0ed4f:focus { - border-color: Highlight -} - -.enable-forced-colors .input__0ed4f.disabled__0ed4f { - background-color: Canvas; - border-color: GrayText; - opacity: 1 -} - -.inputError__0ed4f { - align-items: center; - display: flex; - gap: 8px -} - -.heading-sm\/normal__5a092 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.heading-sm\/normal__5a092.fontScaling__5a092 { - font-size: .875rem -} - -.heading-sm\/medium__5a092 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.heading-sm\/medium__5a092.fontScaling__5a092 { - font-size: .875rem -} - -.heading-sm\/semibold__5a092 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.heading-sm\/semibold__5a092.fontScaling__5a092 { - font-size: .875rem -} - -.heading-sm\/bold__5a092 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.heading-sm\/bold__5a092.fontScaling__5a092 { - font-size: .875rem -} - -.heading-sm\/extrabold__5a092 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 800; - line-height: 1.2857142857142858 -} - -.heading-sm\/extrabold__5a092.fontScaling__5a092 { - font-size: .875rem -} - -.heading-md\/normal__5a092 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.heading-md\/normal__5a092.fontScaling__5a092 { - font-size: 1rem -} - -.heading-md\/medium__5a092 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.heading-md\/medium__5a092.fontScaling__5a092 { - font-size: 1rem -} - -.heading-md\/semibold__5a092 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.heading-md\/semibold__5a092.fontScaling__5a092 { - font-size: 1rem -} - -.heading-md\/bold__5a092 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.heading-md\/bold__5a092.fontScaling__5a092 { - font-size: 1rem -} - -.heading-md\/extrabold__5a092 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 800; - line-height: 1.25 -} - -.heading-md\/extrabold__5a092.fontScaling__5a092 { - font-size: 1rem -} - -.heading-lg\/normal__5a092 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.heading-lg\/normal__5a092.fontScaling__5a092 { - font-size: 1.25rem -} - -.heading-lg\/medium__5a092 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.heading-lg\/medium__5a092.fontScaling__5a092 { - font-size: 1.25rem -} - -.heading-lg\/semibold__5a092 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.heading-lg\/semibold__5a092.fontScaling__5a092 { - font-size: 1.25rem -} - -.heading-lg\/bold__5a092 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.heading-lg\/bold__5a092.fontScaling__5a092 { - font-size: 1.25rem -} - -.heading-lg\/extrabold__5a092 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 800; - line-height: 1.2 -} - -.heading-lg\/extrabold__5a092.fontScaling__5a092 { - font-size: 1.25rem -} - -.heading-xl\/normal__5a092 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xl\/normal__5a092.fontScaling__5a092 { - font-size: 1.5rem -} - -.heading-xl\/medium__5a092 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xl\/medium__5a092.fontScaling__5a092 { - font-size: 1.5rem -} - -.heading-xl\/semibold__5a092 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xl\/semibold__5a092.fontScaling__5a092 { - font-size: 1.5rem -} - -.heading-xl\/bold__5a092 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xl\/bold__5a092.fontScaling__5a092 { - font-size: 1.5rem -} - -.heading-xl\/extrabold__5a092 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xl\/extrabold__5a092.fontScaling__5a092 { - font-size: 1.5rem -} - -.heading-xxl\/normal__5a092 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xxl\/normal__5a092.fontScaling__5a092 { - font-size: 2rem -} - -.heading-xxl\/medium__5a092 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xxl\/medium__5a092.fontScaling__5a092 { - font-size: 2rem -} - -.heading-xxl\/semibold__5a092 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xxl\/semibold__5a092.fontScaling__5a092 { - font-size: 2rem -} - -.heading-xxl\/bold__5a092 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xxl\/bold__5a092.fontScaling__5a092 { - font-size: 2rem -} - -.heading-xxl\/extrabold__5a092 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xxl\/extrabold__5a092.fontScaling__5a092 { - font-size: 2rem -} - -.eyebrow__5a092 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - letter-spacing: .02em; - line-height: 1.3333333333333333; - text-transform: uppercase -} - -.eyebrow__5a092.fontScaling__5a092 { - font-size: .75rem -} - -.heading-deprecated-12\/normal__5a092 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/normal__5a092.fontScaling__5a092 { - font-size: .75rem -} - -.heading-deprecated-12\/medium__5a092 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/medium__5a092.fontScaling__5a092 { - font-size: .75rem -} - -.heading-deprecated-12\/semibold__5a092 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/semibold__5a092.fontScaling__5a092 { - font-size: .75rem -} - -.heading-deprecated-12\/bold__5a092 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/bold__5a092.fontScaling__5a092 { - font-size: .75rem -} - -.heading-deprecated-12\/extrabold__5a092 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 800; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/extrabold__5a092.fontScaling__5a092 { - font-size: .75rem -} - -.redesign\/heading-18\/bold__5a092 { - font-family: var(--font-display); - font-size: 18px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/heading-18\/bold__5a092.fontScaling__5a092 { - font-size: 1.125rem -} - -.text-xxs\/normal__5a092 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 400; - line-height: 1.2 -} - -.text-xxs\/normal__5a092.fontScaling__5a092 { - font-size: .625rem -} - -.text-xxs\/medium__5a092 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 500; - line-height: 1.2 -} - -.text-xxs\/medium__5a092.fontScaling__5a092 { - font-size: .625rem -} - -.text-xxs\/semibold__5a092 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 600; - line-height: 1.2 -} - -.text-xxs\/semibold__5a092.fontScaling__5a092 { - font-size: .625rem -} - -.text-xxs\/bold__5a092 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 700; - line-height: 1.2 -} - -.text-xxs\/bold__5a092.fontScaling__5a092 { - font-size: .625rem -} - -.text-xs\/normal__5a092 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.text-xs\/normal__5a092.fontScaling__5a092 { - font-size: .75rem -} - -.text-xs\/medium__5a092 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.text-xs\/medium__5a092.fontScaling__5a092 { - font-size: .75rem -} - -.text-xs\/semibold__5a092 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.text-xs\/semibold__5a092.fontScaling__5a092 { - font-size: .75rem -} - -.text-xs\/bold__5a092 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.text-xs\/bold__5a092.fontScaling__5a092 { - font-size: .75rem -} - -.text-sm\/normal__5a092 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.text-sm\/normal__5a092.fontScaling__5a092 { - font-size: .875rem -} - -.text-sm\/medium__5a092 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.text-sm\/medium__5a092.fontScaling__5a092 { - font-size: .875rem -} - -.text-sm\/semibold__5a092 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.text-sm\/semibold__5a092.fontScaling__5a092 { - font-size: .875rem -} - -.text-sm\/bold__5a092 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.text-sm\/bold__5a092.fontScaling__5a092 { - font-size: .875rem -} - -.text-md\/normal__5a092 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.text-md\/normal__5a092.fontScaling__5a092 { - font-size: 1rem -} - -.text-md\/medium__5a092 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.text-md\/medium__5a092.fontScaling__5a092 { - font-size: 1rem -} - -.text-md\/semibold__5a092 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.text-md\/semibold__5a092.fontScaling__5a092 { - font-size: 1rem -} - -.text-md\/bold__5a092 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.text-md\/bold__5a092.fontScaling__5a092 { - font-size: 1rem -} - -.text-lg\/normal__5a092 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.text-lg\/normal__5a092.fontScaling__5a092 { - font-size: 1.25rem -} - -.text-lg\/medium__5a092 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.text-lg\/medium__5a092.fontScaling__5a092 { - font-size: 1.25rem -} - -.text-lg\/semibold__5a092 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.text-lg\/semibold__5a092.fontScaling__5a092 { - font-size: 1.25rem -} - -.text-lg\/bold__5a092 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.text-lg\/bold__5a092.fontScaling__5a092 { - font-size: 1.25rem -} - -.redesign\/message-preview\/normal__5a092 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/normal__5a092.fontScaling__5a092 { - font-size: .9375rem -} - -.redesign\/message-preview\/medium__5a092 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/medium__5a092.fontScaling__5a092 { - font-size: .9375rem -} - -.redesign\/message-preview\/semibold__5a092 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/semibold__5a092.fontScaling__5a092 { - font-size: .9375rem -} - -.redesign\/message-preview\/bold__5a092 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/bold__5a092.fontScaling__5a092 { - font-size: .9375rem -} - -.redesign\/channel-title\/normal__5a092 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.375 -} - -.redesign\/channel-title\/normal__5a092.fontScaling__5a092 { - font-size: 1rem -} - -.redesign\/channel-title\/medium__5a092 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.375 -} - -.redesign\/channel-title\/medium__5a092.fontScaling__5a092 { - font-size: 1rem -} - -.redesign\/channel-title\/semibold__5a092 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.375 -} - -.redesign\/channel-title\/semibold__5a092.fontScaling__5a092 { - font-size: 1rem -} - -.redesign\/channel-title\/bold__5a092 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.375 -} - -.redesign\/channel-title\/bold__5a092.fontScaling__5a092 { - font-size: 1rem -} - -.display-sm__5a092 { - font-family: var(--font-headline); - font-size: 20px; - font-weight: 800; - line-height: 1 -} - -.display-sm__5a092.fontScaling__5a092 { - font-size: 1.25rem -} - -.display-md__5a092 { - font-family: var(--font-headline); - font-size: 34px; - font-weight: 800; - line-height: 1.0588235294117647 -} - -.display-md__5a092.fontScaling__5a092 { - font-size: 2.125rem -} - -.display-lg__5a092 { - font-family: var(--font-headline); - font-size: 44px; - font-weight: 800; - line-height: .9545454545454546 -} - -.display-lg__5a092.fontScaling__5a092 { - font-size: 2.75rem -} - -.code__5a092 { - font-family: var(--font-code); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.code__5a092.fontScaling__5a092 { - font-size: .75rem -} - -.wrapper__5a092 { - display: flex; - flex-direction: column; -} - -.textArea__5a092 { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: transparent; - border: none; - color: inherit; - font-size: inherit; - height: auto; - padding: var(--space-12) 10px; - resize: none; - width: 100% -} - -.textArea__5a092:disabled { - cursor: not-allowed; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.textArea__5a092::-moz-placeholder { - color: var(--input-placeholder-text-default); - -moz-user-select: none; - user-select: none -} - -.textArea__5a092::placeholder { - color: var(--input-placeholder-text-default); - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.resizeable__5a092 { - resize: auto -} - -.inputMaxLength__5a092 { - position: relative -} - -.flex__5a092 { - display: flex; - flex: 1 1 auto -} - -.characterCount__5a092,.maxLength__5a092 { - bottom: 12px; - color: var(--text-muted); - font-family: var(--font-code); - font-size: 12px; - inset-inline-end: 14px; - pointer-events: none; - position: absolute -} - -.errorOverflow__5a092 { - color: var(--text-feedback-critical) -} - -.errorMessage__5a092 { - margin-top: 1px -} - -.scrollbar__506b3::-webkit-scrollbar-corner { - background: none; - border: none -} - -.scrollbarDefault__506b3 { -} - -.scrollbarDefault__506b3::-webkit-scrollbar { - height: var(--custom-scrollbar-scrollbar-width); - width: var(--custom-scrollbar-scrollbar-width) -} - -.scrollbarDefault__506b3::-webkit-scrollbar-thumb,.scrollbarDefault__506b3::-webkit-scrollbar-track { - background-clip: padding-box; - border-color: transparent; - border-radius: var(--custom-scrollbar-border-radius); - border-style: solid; - border-width: var(--custom-scrollbar-scrollbar-margin) -} - -.scrollbarDefault__506b3::-webkit-scrollbar-thumb { - background-color: var(--scrollbar-auto-thumb) -} - -.scrollbarDefault__506b3::-webkit-scrollbar-track { - background-color: var(--scrollbar-auto-track); - border-width: initial -} - -.scrollbarGhost__506b3 { -} - -.scrollbarGhost__506b3::-webkit-scrollbar { - height: var(--custom-scrollbar-scrollbar-width); - width: var(--custom-scrollbar-scrollbar-width) -} - -.scrollbarGhost__506b3::-webkit-scrollbar-thumb,.scrollbarGhost__506b3::-webkit-scrollbar-track { - background-clip: padding-box; - border-radius: var(--custom-scrollbar-border-radius); - border-style: solid; - border-width: var(--custom-scrollbar-scrollbar-margin) -} - -.scrollbarGhost__506b3::-webkit-scrollbar-thumb { - background-color: var(--opacity-black-40); - border-color: transparent -} - -.scrollbarGhost__506b3::-webkit-scrollbar-track { - background-color: var(--opacity-black-8); - border-color: transparent; - border-width: initial -} - -.scrollbarGhostHairline__506b3 { -} - -.scrollbarGhostHairline__506b3::-webkit-scrollbar { - height: 4px; - width: 4px -} - -.scrollbarGhostHairline__506b3::-webkit-scrollbar-thumb { - background-color: hsl(var(--primary-800-hsl)/.6); - border-radius: 2px; - cursor: move -} - -.scrollbarGhostHairline__506b3::-webkit-scrollbar-track { - background-color: transparent; - border: none -} - -.enable-forced-colors .scrollbar__506b3::-webkit-scrollbar-track { - background-color: Canvas; - border-radius: 0; - border-width: 1px -} - -.enable-forced-colors .scrollbar__506b3::-webkit-scrollbar-thumb { - background-color: CanvasText; - border-width: 1px -} - -@keyframes spin3d { - 0% { - transform: rotateY(0deg) rotateX(0deg) - } - - 25% { - transform: rotateY(90deg) rotateX(15deg) - } - - 50% { - transform: rotateY(180deg) rotateX(0deg) - } - - 75% { - transform: rotateY(270deg) rotateX(-15deg) - } - - to { - transform: rotateY(1turn) rotateX(0deg) - } -} - -@keyframes rainbowSlide { - 0% { - background-position: 0 50% - } - - to { - background-position: 200% 50% - } -} - -.dynamicGraphicDemoContainer { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - perspective: 1000px; - width: 100% -} - -.dynamicGraphicDemoText { - background: linear-gradient(90deg,red,#ff7f00,#ff0,#0f0,#00f,indigo,#9400d3,red); - -webkit-background-clip: text; - background-size: 200% 100%; - font-size: 32px; - font-weight: 700; - text-shadow: 2px 2px 4px rgba(0,0,0,.3),4px 4px 8px rgba(0,0,0,.2); - -webkit-text-fill-color: transparent; - animation: spin3d 5s linear infinite,rainbowSlide 1.5s linear infinite; - background-clip: text -} - -.group__3d4a0,.item__3d4a0 { - box-sizing: border-box; - display: flex -} - -.item__3d4a0 { - align-items: center; - background: transparent; - border-style: solid; - border-width: 1px 1px 1px 0; - cursor: pointer; - justify-content: center; - overflow: hidden; - padding: 0; - text-overflow: ellipsis; - white-space: nowrap -} - -.item__3d4a0:first-child { - border-left-width: 1px; - border-radius: 3px 0 0 3px -} - -.item__3d4a0:last-child { - border-radius: 0 3px 3px 0 -} - -.item__3d4a0:disabled { - cursor: not-allowed; - opacity: .3 -} - -.theme-light .item__3d4a0 { - border-color: var(--primary-200) -} - -.theme-dark .item__3d4a0 { - border-color: var(--primary-800) -} - -.textArea__89a34 { - height: auto; - resize: none -} - -.resizeable__89a34 { - resize: auto -} - -.inputMaxLength__89a34 { - position: relative -} - -.flex__89a34 { - display: flex; - flex: 1 1 auto -} - -.characterCount__89a34,.maxLength__89a34 { - bottom: 12px; - color: var(--text-muted); - font-family: var(--font-code); - font-size: 12px; - inset-inline-end: 14px; - pointer-events: none; - position: absolute -} - -.errorOverflow__89a34 { - color: var(--text-feedback-critical) -} - -.errorMessage__89a34 { - margin-top: 1px -} - -.allChips_ff7bda { - gap: var(--space-32); - padding: var(--space-24) -} - -.allChips_ff7bda,.section_ff7bda { - display: flex; - flex-direction: column -} - -.section_ff7bda { - gap: var(--space-16) -} - -.sectionTitle_ff7bda { - color: var(--text-strong); - font-weight: var(--font-weight-semibold); - margin: 0 -} - -.chipGrid_ff7bda { - align-items: center; - display: flex; - flex-wrap: wrap; - gap: var(--space-12) -} - -.card__73069 { - border-style: solid; - border-width: 1px; - position: relative; - --__card-accent-color: transparent; - border-color: var(--__card-accent-color); - border-radius: var(--radius-sm) -} - -.card__73069:not(.outline__73069) { - background-color: var(--__card-accent-color) -} - -.card__73069 a:hover { - text-decoration: underline -} - -.cardBrand__73069 a,.cardDanger__73069 a,.cardSuccess__73069 a,.cardWarning__73069 a { - font-weight: var(--font-weight-bold) -} - -.cardDanger__73069 { - --__card-accent-color: var(--background-feedback-critical); - color: var(--text-feedback-critical) -} - -.cardWarning__73069 { - --__card-accent-color: var(--background-feedback-warning); - color: var(--text-feedback-warning) -} - -.cardSuccess__73069 { - --__card-accent-color: var(--background-feedback-positive); - color: var(--text-feedback-positive) -} - -.cardBrand__73069 { - --__card-accent-color: var(--brand-500) -} - -.cardBrand__73069,.cardPrimary__73069,.cardPrimaryEditable__73069,.cardPrimaryOutline__73069,.cardPrimaryOutlineEditable__73069 { -} - -.card__73069 a { - color: var(--text-link) -} - -.cardBrand__73069 a,.cardDanger__73069 a,.cardSuccess__73069 a,.cardWarning__73069 a { - color: var(--white) -} - -.cardPrimary__73069,.cardPrimary__73069.editable__73069 { - --__card-accent-color: var(--card-background-default); - background-color: var(--card-background-default); - border: 1px solid var(--border-subtle) -} - -.flex__7c0ba,.horizontal__7c0ba { - display: flex -} - -.horizontal__7c0ba { - flex-direction: row -} - -.horizontalReverse__7c0ba { - display: flex; - flex-direction: row-reverse -} - -.horizontal__7c0ba>.flex__7c0ba,.horizontal__7c0ba>.flexChild__7c0ba { - margin-inline:10px} - -.horizontal__7c0ba>.flex__7c0ba:first-child,.horizontal__7c0ba>.flexChild__7c0ba:first-child { - margin-inline-start:0} - -.horizontal__7c0ba>.flex__7c0ba:last-child,.horizontal__7c0ba>.flexChild__7c0ba:last-child { - margin-inline-end:0} - -.horizontalReverse__7c0ba>.flex__7c0ba,.horizontalReverse__7c0ba>.flexChild__7c0ba { - margin-inline:10px} - -.horizontalReverse__7c0ba>.flex__7c0ba:first-child,.horizontalReverse__7c0ba>.flexChild__7c0ba:first-child { - margin-inline-end:0} - -.horizontalReverse__7c0ba>.flex__7c0ba:last-child,.horizontalReverse__7c0ba>.flexChild__7c0ba:last-child { - margin-inline-start:0} - -.horizontal__7c0ba>.flexMarginReset__7c0ba { - margin: 0 -} - -.flex_abf706 { - display: flex -} - -.alignStart_abf706 { - align-items: flex-start -} - -.alignEnd_abf706 { - align-items: flex-end -} - -.alignCenter_abf706 { - align-items: center -} - -.alignStretch_abf706 { - align-items: stretch -} - -.alignBaseline_abf706 { - align-items: baseline -} - -.justifyStart_abf706 { - justify-content: flex-start -} - -.justifyEnd_abf706 { - justify-content: flex-end -} - -.justifyCenter_abf706 { - justify-content: center -} - -.justifyAround_abf706 { - justify-content: space-around -} - -.justifyBetween_abf706 { - justify-content: space-between -} - -.noWrap_abf706 { - flex-wrap: nowrap -} - -.wrap_abf706 { - flex-wrap: wrap -} - -.wrapReverse_abf706 { - flex-wrap: wrap-reverse -} - -.directionRow_abf706 { - flex-direction: row -} - -.directionRowReverse_abf706 { - flex-direction: row-reverse -} - -.directionColumn_abf706 { - flex-direction: column -} - -.spacer_abf706 { - flex: 1; - overflow: hidden -} - -.vertical_abf706 { - display: flex; - flex-direction: column -} - -.horizontal_abf706 { - display: flex; - flex-direction: row -} - -.horizontalReverse_abf706 { - display: flex; - flex-direction: row-reverse -} - -.horizontal_abf706>.spacer_abf706,.horizontalReverse_abf706>.spacer_abf706,.vertical_abf706>.spacer_abf706 { - min-height: 1px -} - -.flexCenter_abf706 { - align-items: center; - display: flex; - justify-content: center -} - -.formText_ddd181 a:hover { - text-decoration: underline -} - -.formText_ddd181 strong { - font-weight: var(--font-weight-semibold) -} - -.default_ddd181,.labelDescriptor_ddd181,.labelSelected_ddd181,.placeholder_ddd181 { -} - -.labelSelected_ddd181 { - font-weight: var(--font-weight-normal) -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .labelSelected_ddd181 { - font-weight:var(--font-weight-medium) - } -} - -.labelBold_ddd181 { - font-weight: var(--font-weight-semibold) -} - -.description_ddd181 { - font-size: 14px; - font-weight: var(--font-weight-normal); - line-height: 20px -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .description_ddd181 { - font-weight:var(--font-weight-medium) - } -} - -.modeDefault_ddd181 { - cursor: default -} - -.modeSelectable_ddd181 { - cursor: text; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.modeDisabled_ddd181 { - cursor: not-allowed; - opacity: .3 -} - -.description_ddd181,.labelDescriptor_ddd181 { - color: var(--text-default) -} - -.placeholder_ddd181 { - color: var(--text-muted) -} - -.error_ddd181 { - color: var(--text-feedback-critical) -} - -.error_ddd181,.success_ddd181 { -} - -.success_ddd181 { - color: var(--text-feedback-positive) -} - -.enable-forced-colors .modeDisabled_ddd181 { - color: GrayText; - opacity: 1 -} - -.heading-sm\/normal_b717a1 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.heading-sm\/normal_b717a1.fontScaling_b717a1 { - font-size: .875rem -} - -.heading-sm\/medium_b717a1 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.heading-sm\/medium_b717a1.fontScaling_b717a1 { - font-size: .875rem -} - -.heading-sm\/semibold_b717a1 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.heading-sm\/semibold_b717a1.fontScaling_b717a1 { - font-size: .875rem -} - -.heading-sm\/bold_b717a1 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.heading-sm\/bold_b717a1.fontScaling_b717a1 { - font-size: .875rem -} - -.heading-sm\/extrabold_b717a1 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 800; - line-height: 1.2857142857142858 -} - -.heading-sm\/extrabold_b717a1.fontScaling_b717a1 { - font-size: .875rem -} - -.heading-md\/normal_b717a1 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.heading-md\/normal_b717a1.fontScaling_b717a1 { - font-size: 1rem -} - -.heading-md\/medium_b717a1 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.heading-md\/medium_b717a1.fontScaling_b717a1 { - font-size: 1rem -} - -.heading-md\/semibold_b717a1 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.heading-md\/semibold_b717a1.fontScaling_b717a1 { - font-size: 1rem -} - -.heading-md\/bold_b717a1 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.heading-md\/bold_b717a1.fontScaling_b717a1 { - font-size: 1rem -} - -.heading-md\/extrabold_b717a1 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 800; - line-height: 1.25 -} - -.heading-md\/extrabold_b717a1.fontScaling_b717a1 { - font-size: 1rem -} - -.heading-lg\/normal_b717a1 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.heading-lg\/normal_b717a1.fontScaling_b717a1 { - font-size: 1.25rem -} - -.heading-lg\/medium_b717a1 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.heading-lg\/medium_b717a1.fontScaling_b717a1 { - font-size: 1.25rem -} - -.heading-lg\/semibold_b717a1 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.heading-lg\/semibold_b717a1.fontScaling_b717a1 { - font-size: 1.25rem -} - -.heading-lg\/bold_b717a1 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.heading-lg\/bold_b717a1.fontScaling_b717a1 { - font-size: 1.25rem -} - -.heading-lg\/extrabold_b717a1 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 800; - line-height: 1.2 -} - -.heading-lg\/extrabold_b717a1.fontScaling_b717a1 { - font-size: 1.25rem -} - -.heading-xl\/normal_b717a1 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xl\/normal_b717a1.fontScaling_b717a1 { - font-size: 1.5rem -} - -.heading-xl\/medium_b717a1 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xl\/medium_b717a1.fontScaling_b717a1 { - font-size: 1.5rem -} - -.heading-xl\/semibold_b717a1 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xl\/semibold_b717a1.fontScaling_b717a1 { - font-size: 1.5rem -} - -.heading-xl\/bold_b717a1 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xl\/bold_b717a1.fontScaling_b717a1 { - font-size: 1.5rem -} - -.heading-xl\/extrabold_b717a1 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xl\/extrabold_b717a1.fontScaling_b717a1 { - font-size: 1.5rem -} - -.heading-xxl\/normal_b717a1 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xxl\/normal_b717a1.fontScaling_b717a1 { - font-size: 2rem -} - -.heading-xxl\/medium_b717a1 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xxl\/medium_b717a1.fontScaling_b717a1 { - font-size: 2rem -} - -.heading-xxl\/semibold_b717a1 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xxl\/semibold_b717a1.fontScaling_b717a1 { - font-size: 2rem -} - -.heading-xxl\/bold_b717a1 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xxl\/bold_b717a1.fontScaling_b717a1 { - font-size: 2rem -} - -.heading-xxl\/extrabold_b717a1 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xxl\/extrabold_b717a1.fontScaling_b717a1 { - font-size: 2rem -} - -.eyebrow_b717a1 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - letter-spacing: .02em; - line-height: 1.3333333333333333; - text-transform: uppercase -} - -.eyebrow_b717a1.fontScaling_b717a1 { - font-size: .75rem -} - -.heading-deprecated-12\/normal_b717a1 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/normal_b717a1.fontScaling_b717a1 { - font-size: .75rem -} - -.heading-deprecated-12\/medium_b717a1 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/medium_b717a1.fontScaling_b717a1 { - font-size: .75rem -} - -.heading-deprecated-12\/semibold_b717a1 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/semibold_b717a1.fontScaling_b717a1 { - font-size: .75rem -} - -.heading-deprecated-12\/bold_b717a1 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/bold_b717a1.fontScaling_b717a1 { - font-size: .75rem -} - -.heading-deprecated-12\/extrabold_b717a1 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 800; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/extrabold_b717a1.fontScaling_b717a1 { - font-size: .75rem -} - -.redesign\/heading-18\/bold_b717a1 { - font-family: var(--font-display); - font-size: 18px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/heading-18\/bold_b717a1.fontScaling_b717a1 { - font-size: 1.125rem -} - -.text-xxs\/normal_b717a1 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 400; - line-height: 1.2 -} - -.text-xxs\/normal_b717a1.fontScaling_b717a1 { - font-size: .625rem -} - -.text-xxs\/medium_b717a1 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 500; - line-height: 1.2 -} - -.text-xxs\/medium_b717a1.fontScaling_b717a1 { - font-size: .625rem -} - -.text-xxs\/semibold_b717a1 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 600; - line-height: 1.2 -} - -.text-xxs\/semibold_b717a1.fontScaling_b717a1 { - font-size: .625rem -} - -.text-xxs\/bold_b717a1 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 700; - line-height: 1.2 -} - -.text-xxs\/bold_b717a1.fontScaling_b717a1 { - font-size: .625rem -} - -.text-xs\/normal_b717a1 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.text-xs\/normal_b717a1.fontScaling_b717a1 { - font-size: .75rem -} - -.text-xs\/medium_b717a1 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.text-xs\/medium_b717a1.fontScaling_b717a1 { - font-size: .75rem -} - -.text-xs\/semibold_b717a1 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.text-xs\/semibold_b717a1.fontScaling_b717a1 { - font-size: .75rem -} - -.text-xs\/bold_b717a1 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.text-xs\/bold_b717a1.fontScaling_b717a1 { - font-size: .75rem -} - -.text-sm\/normal_b717a1 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.text-sm\/normal_b717a1.fontScaling_b717a1 { - font-size: .875rem -} - -.text-sm\/medium_b717a1 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.text-sm\/medium_b717a1.fontScaling_b717a1 { - font-size: .875rem -} - -.text-sm\/semibold_b717a1 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.text-sm\/semibold_b717a1.fontScaling_b717a1 { - font-size: .875rem -} - -.text-sm\/bold_b717a1 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.text-sm\/bold_b717a1.fontScaling_b717a1 { - font-size: .875rem -} - -.text-md\/normal_b717a1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.text-md\/normal_b717a1.fontScaling_b717a1 { - font-size: 1rem -} - -.text-md\/medium_b717a1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.text-md\/medium_b717a1.fontScaling_b717a1 { - font-size: 1rem -} - -.text-md\/semibold_b717a1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.text-md\/semibold_b717a1.fontScaling_b717a1 { - font-size: 1rem -} - -.text-md\/bold_b717a1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.text-md\/bold_b717a1.fontScaling_b717a1 { - font-size: 1rem -} - -.text-lg\/normal_b717a1 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.text-lg\/normal_b717a1.fontScaling_b717a1 { - font-size: 1.25rem -} - -.text-lg\/medium_b717a1 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.text-lg\/medium_b717a1.fontScaling_b717a1 { - font-size: 1.25rem -} - -.text-lg\/semibold_b717a1 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.text-lg\/semibold_b717a1.fontScaling_b717a1 { - font-size: 1.25rem -} - -.text-lg\/bold_b717a1 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.text-lg\/bold_b717a1.fontScaling_b717a1 { - font-size: 1.25rem -} - -.redesign\/message-preview\/normal_b717a1 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/normal_b717a1.fontScaling_b717a1 { - font-size: .9375rem -} - -.redesign\/message-preview\/medium_b717a1 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/medium_b717a1.fontScaling_b717a1 { - font-size: .9375rem -} - -.redesign\/message-preview\/semibold_b717a1 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/semibold_b717a1.fontScaling_b717a1 { - font-size: .9375rem -} - -.redesign\/message-preview\/bold_b717a1 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/bold_b717a1.fontScaling_b717a1 { - font-size: .9375rem -} - -.redesign\/channel-title\/normal_b717a1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.375 -} - -.redesign\/channel-title\/normal_b717a1.fontScaling_b717a1 { - font-size: 1rem -} - -.redesign\/channel-title\/medium_b717a1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.375 -} - -.redesign\/channel-title\/medium_b717a1.fontScaling_b717a1 { - font-size: 1rem -} - -.redesign\/channel-title\/semibold_b717a1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.375 -} - -.redesign\/channel-title\/semibold_b717a1.fontScaling_b717a1 { - font-size: 1rem -} - -.redesign\/channel-title\/bold_b717a1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.375 -} - -.redesign\/channel-title\/bold_b717a1.fontScaling_b717a1 { - font-size: 1rem -} - -.display-sm_b717a1 { - font-family: var(--font-headline); - font-size: 20px; - font-weight: 800; - line-height: 1 -} - -.display-sm_b717a1.fontScaling_b717a1 { - font-size: 1.25rem -} - -.display-md_b717a1 { - font-family: var(--font-headline); - font-size: 34px; - font-weight: 800; - line-height: 1.0588235294117647 -} - -.display-md_b717a1.fontScaling_b717a1 { - font-size: 2.125rem -} - -.display-lg_b717a1 { - font-family: var(--font-headline); - font-size: 44px; - font-weight: 800; - line-height: .9545454545454546 -} - -.display-lg_b717a1.fontScaling_b717a1 { - font-size: 2.75rem -} - -.code_b717a1 { - font-family: var(--font-code); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.code_b717a1.fontScaling_b717a1 { - font-size: .75rem -} - -.title_b717a1 { - cursor: default; - flex: 1; - font-family: var(--font-display) -} - -.h1_b717a1 { - font-size: 20px; - line-height: 24px -} - -.h1_b717a1,.h2_b717a1 { - font-weight: var(--font-weight-semibold) -} - -.h2_b717a1 { - font-size: 16px; - line-height: 20px -} - -.h3_b717a1 { - font-weight: var(--font-weight-medium); - line-height: 24px -} - -.h3_b717a1,.h4_b717a1 { - font-size: 16px -} - -.h4_b717a1 { - font-weight: var(--font-weight-semibold); - letter-spacing: .3px -} - -.h4_b717a1,.h5_b717a1,.legend_b717a1 { - line-height: 20px -} - -.h5_b717a1,.legend_b717a1 { - color: var(--text-strong); - font-size: 16px; - font-weight: 500; - margin-bottom: 8px; - text-transform: unset -} - -.defaultMarginh4_b717a1 { - margin-bottom: 0; - margin-top: 0 -} - -.defaultMarginh1_b717a1,.defaultMarginh2_b717a1 { - margin-bottom: 20px -} - -.defaultMarginh3_b717a1,.defaultMarginh5_b717a1,.defaultMarginlabel_b717a1,.defaultMarginlegend_b717a1 { - margin-bottom: 8px -} - -.disabled_b717a1 { - cursor: not-allowed; - opacity: .5 -} - -.required_b717a1 { - color: var(--text-feedback-critical); - padding-inline-start:4px} - -.errorMessage_b717a1 { - font-size: 12px; - font-style: italic; - font-weight: var(--font-weight-medium) -} - -.errorSeparator_b717a1 { - padding-inline:4px} - -.defaultColor_b717a1 { - color: var(--text-strong) -} - -.label_b717a1,.legend_b717a1 { - color: var(--text-default) -} - -.label_b717a1 { - display: block -} - -.faded_b717a1 { - opacity: .8 -} - -.error_b717a1 { - color: var(--text-feedback-critical) -} - -.enable-forced-colors .disabled_b717a1 { - color: GrayText; - opacity: 1 -} - -.formNotice_f43ba5 { - cursor: default; - padding: 20px -} - -.formNoticeTitle_f43ba5 { - margin-bottom: 4px -} - -.formNoticeBody_f43ba5 p { - letter-spacing: -.39px; - margin: 0 0 12px -} - -.formNoticeBody_f43ba5 p:last-child { - margin-bottom: 0 -} - -.formNoticeBody_f43ba5 u { - font-style: italic; - text-decoration: none -} - -.icon_f43ba5 { - -webkit-user-drag: none -} - -.whiteText_f43ba5,.whiteText_f43ba5 a { - color: unset -} - -.layerRoot__7b917 { - min-height: 600px; - position: relative; - width: 100% -} - -.container__7b917 { - align-items: flex-start; - display: flex; - justify-content: center; - min-height: 400px; - padding: 32px -} - -.notice__6e2b9 { - background: var(--custom-notice-background); - border-inline-start:1px solid var(--app-frame-border);border-radius: 8px 0 0; - border-top: 1px solid var(--app-frame-border); - box-shadow: none; - color: var(--custom-notice-text); - flex-grow: 0; - flex-shrink: 0; - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 36px; - padding-inline:4px 28px;position: relative; - text-align: center; - z-index: 101 -} - -.notice__6e2b9 strong { - font-weight: var(--font-weight-semibold) -} - -.notice__6e2b9.isMobile__6e2b9 { - line-height: 30px; - padding: 10px 20px -} - -.notice__6e2b9 .button__6e2b9 { - border-color: var(--custom-notice-text)!important; - color: var(--custom-notice-text)!important -} - -.notice__6e2b9 .button__6e2b9:hover { - background-color: var(--background-mod-subtle); - color: var(--custom-notice-button-hover) -} - -.notice__6e2b9 .closeIcon__6e2b9 path { - fill: var(--custom-notice-text) -} - -@supports not ((grid-template-columns: subgrid) and (white-space-collapse:collapse)) { - .notice__6e2b9 { - border-start-start-radius:0 - } -} - -@supports (grid-template-columns: subgrid) and (white-space-collapse:collapse) { - .notice__6e2b9 { - grid-area:notice - } -} - -.colorDefault__6e2b9 { - --custom-notice-background: var(--status-positive-background); - --custom-notice-text: var(--status-positive-text); - --custom-notice-button-hover: var(--status-positive-background) -} - -.colorNeutral__6e2b9 { - --custom-notice-background: var(--background-surface-high); - --custom-notice-text: var(--text-strong); - --custom-notice-button-hover: var(--control-secondary-background-hover) -} - -.colorPremium__6e2b9 { - --custom-notice-text: var(--white) -} - -.colorPremiumTier0__6e2b9 { - --custom-notice-background: linear-gradient(to left,var(--premium-tier-0-purple),var(--premium-tier-0-blue)); - --custom-notice-button-hover: var(--brand-500) -} - -.colorPremiumTier1__6e2b9 { - --custom-notice-background: linear-gradient(270deg,var(--premium-tier-1-blue-for-gradients) 0%,var(--premium-tier-1-dark-blue-for-gradients) 100%); - --custom-notice-button-hover: var(--brand-500) -} - -.colorPremiumTier2__6e2b9 { - --custom-notice-background: linear-gradient(90deg,var(--premium-tier-2-purple-for-gradients) 0%,var(--premium-tier-2-purple-for-gradients-2) 50.24%,var(--premium-tier-2-pink-for-gradients) 100%); - --custom-notice-button-hover: var(--premium-tier-2-pink) -} - -.colorInfo__6e2b9 { - --custom-notice-background: var(--notice-background-info); - --custom-notice-text: var(--notice-text-info); - --custom-notice-button-hover: var(--notice-text-info) -} - -.colorSuccess__6e2b9 { - --custom-notice-background: var(--status-positive-background); - --custom-notice-text: var(--status-positive-text); - --custom-notice-button-hover: var(--text-feedback-positive) -} - -.colorWarning__6e2b9 { - --custom-notice-background: var(--notice-background-warning); - --custom-notice-text: var(--notice-text-warning); - --custom-notice-button-hover: var(--notice-text-warning) -} - -.colorWarning__6e2b9 .button__6e2b9 { - border-color: var(--status-warning-text)!important; - color: var(--status-warning-text) -} - -.colorDanger__6e2b9 { - --custom-notice-background: var(--notice-background-critical); - --custom-notice-text: var(--notice-text-critical); - --custom-notice-button-hover: var(--notice-text-critical) -} - -.colorDefault__6e2b9,.colorSuccess__6e2b9 { - --custom-notice-background: var(--notice-background-positive); - --custom-notice-text: var(--notice-text-positive); - --custom-notice-button-hover: var(--notice-text-positive) -} - -.colorStreamerMode__6e2b9 { - --custom-notice-background: var(--twitch); - --custom-notice-text: var(--white); - --custom-notice-button-hover: var(--twitch) -} - -.colorSpotify__6e2b9 { - --custom-notice-background: var(--spotify); - --custom-notice-text: var(--white); - --custom-notice-button-hover: var(--spotify); - align-items: center; - display: flex; - justify-content: center -} - -.colorSpotify__6e2b9 .platformIcon__6e2b9 { - height: 24px; - margin-top: 0; - width: 24px -} - -.colorSpotify__6e2b9 .button__6e2b9 { - margin-inline-start:20px;top: 0 -} - -.colorPlayStation__6e2b9 { - --custom-notice-background: var(--playstation); - --custom-notice-text: var(--white); - --custom-notice-button-hover: var(--playstation); - align-items: center; - display: flex; - justify-content: center -} - -.colorPlayStation__6e2b9 .platformIcon__6e2b9 { - height: 24px; - margin-top: 0; - width: 24px -} - -.colorPlayStation__6e2b9 .button__6e2b9 { - margin-inline-start:20px;top: 0 -} - -.colorBrand__6e2b9 { - --custom-notice-background: var(--brand-500); - --custom-notice-text: var(--white); - --custom-notice-button-hover: var(--brand-500) -} - -.colorCustom__6e2b9 { - --custom-notice-button-hover: #222 -} - -.closeButton__6e2b9 { - align-items: center; - cursor: pointer; - display: flex; - height: 36px; - inset-inline-end: 0; - position: absolute; - top: 0; - width: 36px; - -webkit-app-region: no-drag -} - -.closeIcon__6e2b9 path { - fill: var(--white) -} - -.button__6e2b9 { - border: 1px solid; - border-radius: 3px; - box-sizing: border-box; - color: var(--white); - display: inline-block; - font-size: 14px; - font-weight: var(--font-weight-medium); - height: 24px; - line-height: 22px; - margin-inline-start:10px;padding: 0 10px; - position: relative; - top: 6px; - transition: background-color .2s ease,color .2s ease,border-color .2s ease; - vertical-align: top; - -webkit-app-region: no-drag; - background-color: transparent; - border-color: var(--white) -} - -.button__6e2b9.buttonMinor__6e2b9 { - border: none; - text-decoration: underline -} - -.button__6e2b9:hover { - background-color: var(--white); - border-color: var(--white) -} - -.enable-forced-colors .notice__6e2b9 { - border-bottom: 2px solid CanvasText -} - -.enable-forced-colors .closeButton__6e2b9 { - background-color: ButtonFace; - color: ButtonText; - opacity: 1 -} - -.enable-forced-colors .closeButton__6e2b9:focus,.enable-forced-colors .closeButton__6e2b9:hover { - outline: 1px solid ButtonText -} - -.tooltip__4e35b { - align-items: center; - background-color: var(--background-surface-high); - border-radius: var(--radius-sm); - box-shadow: inset 0 0 0 1px var(--border-subtle),var(--shadow-high); - box-sizing: border-box; - color: var(--text-default); - display: flex; - gap: var(--space-8); - max-width: 200px; - padding: var(--space-8) var(--space-12); - position: relative; - width: auto; - will-change: opacity,transform -} - -.tooltipLayer__4e35b { - pointer-events: none -} - -.richTooltip__4e35b { - max-width: none; - padding: 0; - width: auto -} - -.tooltipContent__4e35b { - overflow: hidden -} - -.tooltipContentAllowOverflow__4e35b { - overflow: visible -} - -.tooltip__4e35b[data-position=top] { - transform-origin: 50% 100% -} - -.tooltip__4e35b[data-position=bottom] { - transform-origin: 50% 0 -} - -.tooltip__4e35b[data-position=left] { - transform-origin: 100% 50% -} - -.tooltip__4e35b[data-position=right] { - transform-origin: 0 50% -} - -.caretIcon__4e35b .caretFill__4e35b { - fill: var(--background-surface-high) -} - -.caretIcon__4e35b .caretStroke__4e35b { - stroke: var(--border-subtle); - stroke-opacity: 1 -} - -.tooltipWithShortcut__4e35b { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-4); - justify-content: center; - text-align: center -} - -.caret__5bc6a { - --custom-caret-half-width: 8px; - --custom-caret-half-height: 7px; - --custom-caret-edge-offset-horizontal: 16px; - --custom-caret-edge-offset-vertical: 10px; - --custom-caret-horizontal-distance: 11px; - --custom-caret-border-overlap: 5px; - pointer-events: none; - position: absolute; - transform-origin: center -} - -.caret--bottom__5bc6a { - top: calc(100% - var(--custom-caret-border-overlap)); - transform: rotate(0deg) -} - -.caret--top__5bc6a { - bottom: calc(100% - var(--custom-caret-border-overlap)); - transform: rotate(180deg) -} - -.caret--left__5bc6a { - inset-inline-start: calc(var(--custom-caret-horizontal-distance)*-1); - transform: rotate(90deg) -} - -.caret--right__5bc6a { - inset-inline-end: calc(var(--custom-caret-horizontal-distance)*-1); - transform: rotate(-90deg) -} - -:is(.caret--top__5bc6a,.caret--bottom__5bc6a).caret--center__5bc6a { - inset-inline-start: 50%; - margin-inline-start:calc(var(--custom-caret-half-width)*-1)} - -: is(.caret--top__5bc6a,.caret--bottom__5bc6a).caret--start__5bc6a { - inset-inline-start:var(--custom-caret-edge-offset-horizontal); - margin-inline-start:calc(var(--custom-caret-half-width)*-1)} - -: is(.caret--top__5bc6a,.caret--bottom__5bc6a).caret--end__5bc6a { - inset-inline-end:var(--custom-caret-edge-offset-horizontal); - margin-inline-end:calc(var(--custom-caret-half-width)*-1)} - -: is(.caret--left__5bc6a,.caret--right__5bc6a).caret--center__5bc6a { - margin-top:calc(var(--custom-caret-half-height)*-1); - top: 50% -} - -:is(.caret--left__5bc6a,.caret--right__5bc6a).caret--start__5bc6a { - margin-top: calc(var(--custom-caret-half-height)*-1); - top: var(--custom-caret-edge-offset-vertical) -} - -:is(.caret--left__5bc6a,.caret--right__5bc6a).caret--end__5bc6a { - bottom: var(--custom-caret-edge-offset-vertical); - margin-bottom: calc(var(--custom-caret-half-height)*-1) -} - -.caret--custom__5bc6a { - --custom-caret-offset-x: 0; - --custom-caret-offset-y: 0 -} - -.caret--bottom__5bc6a.caret--custom__5bc6a,.caret--top__5bc6a.caret--custom__5bc6a { - inset-inline-start: 50%; - margin-inline-start:calc(var(--custom-caret-offset-x) - var(--custom-caret-half-width))} - -.caret--left__5bc6a.caret--custom__5bc6a,.caret--right__5bc6a.caret--custom__5bc6a { - margin-top: calc(var(--custom-caret-offset-y) - var(--custom-caret-half-height)); - top: 50% -} - -.slider_a562c8 { - height: 40px; - position: relative; - width: 100% -} - -.mini_a562c8 { - height: 20px -} - -.mini_a562c8 .bar_a562c8 { - height: 6px; - top: 17px -} - -.mini_a562c8 .grabber_a562c8 { - border-radius: 50%; - height: 12px; - margin-inline-start:-7px;margin-top: 3px; - width: 12px -} - -.disabled_a562c8 { - opacity: .6 -} - -.disabled_a562c8 .grabber_a562c8 { - cursor: not-allowed -} - -.bar_a562c8 { - background-color: var(--slider-track-background); - border-radius: 4px; - display: block; - height: 8px; - overflow: hidden; - position: relative; - top: 16px -} - -.barFill_a562c8 { - background: var(--control-brand-foreground-new); - height: 100% -} - -.track_a562c8 { - position: absolute; - top: 0; - inset-inline: 5px; - bottom: 0 -} - -.grabber_a562c8 { - background-color: var(--white); - border: 1px solid var(--border-strong); - border-radius: 3px; - box-shadow: var(--shadow-border),var(--shadow-ledge),var(--shadow-low); - cursor: ew-resize; - height: 24px; - margin-inline-start:-5px;margin-top: -13px; - top: 50%; - width: 10px -} - -.grabber_a562c8,.mark_a562c8 { - inset-inline-start: 0; - position: absolute -} - -.mark_a562c8 { - align-items: center; - display: flex; - flex-direction: column; - margin-inline-start:-12px;-webkit-user-select: none; - -moz-user-select: none; - user-select: none; - width: 24px -} - -.markAbove_a562c8 { - top: -6px -} - -.markBelow_a562c8 { - bottom: -12px -} - -.markValue_a562c8 { - font-size: 10px; - font-weight: var(--font-weight-bold); - margin-bottom: 4px; - min-height: 10px; - padding-inline-start:1px} - -.markDash_a562c8 { - background-color: var(--slider-track-background); - height: 24px; - width: 2px -} - -.markDashSimple_a562c8 { - margin-top: 10px -} - -.markValue_a562c8 { - color: var(--text-muted) -} - -.defaultValue_a562c8 .markValue_a562c8 { - color: var(--text-feedback-positive) -} - -.mini_a562c8,.slider_a562c8 { - height: calc(24px + var(--bar-offset)); - --grabber-size: 16px; - --bar-size: 4px; - --bar-offset: 0px -} - -.mini_a562c8.hasMarks_a562c8,.slider_a562c8.hasMarks_a562c8 { - --bar-offset: 24px -} - -.mini_a562c8 .mark_a562c8,.slider_a562c8 .mark_a562c8 { - display: flex; - flex-direction: column; - height: 48px; - justify-content: space-between; - top: 0 -} - -.mini_a562c8 .markValue_a562c8,.slider_a562c8 .markValue_a562c8 { - color: var(--text-subtle); - font-size: 12px; - font-weight: 500; - line-height: 16px -} - -.mini_a562c8 .defaultValue_a562c8 .markValue_a562c8,.slider_a562c8 .defaultValue_a562c8 .markValue_a562c8 { - color: var(--text-feedback-positive) -} - -.mini_a562c8 .markDash_a562c8,.slider_a562c8 .markDash_a562c8 { - border-radius: 12px -} - -.mini_a562c8 .grabber_a562c8,.slider_a562c8 .grabber_a562c8 { - border: 1px solid var(--border-normal); - border-radius: 50%; - box-shadow: var(--shadow-low); - box-sizing: border-box; - height: var(--grabber-size); - margin-inline-start:calc(var(--grabber-size)/-2);margin-top: calc(var(--grabber-size)/-2 + var(--bar-offset)/2); - width: var(--grabber-size) -} - -.high-contrast-mode .mini_a562c8 .grabber_a562c8,.high-contrast-mode .slider_a562c8 .grabber_a562c8 { - border-color: var(--border-strong) -} - -.mini_a562c8 .hasMarks_a562c8 .grabber_a562c8,.slider_a562c8 .hasMarks_a562c8 .grabber_a562c8 { - margin-top: calc(var(--grabber-size)/-2 + 12px) -} - -.mini_a562c8 .bar_a562c8,.slider_a562c8 .bar_a562c8 { - background-color: var(--slider-track-background); - height: var(--bar-size); - top: calc((24px - var(--bar-size))/2 + var(--bar-offset)) -} - -.mini_a562c8 .hasMarks_a562c8 .bar_a562c8,.slider_a562c8 .hasMarks_a562c8 .bar_a562c8 { - top: 34px -} - -.mini_a562c8 .barFill_a562c8,.slider_a562c8 .barFill_a562c8 { - background-color: var(--background-brand); - border: 1px solid hsla(0,0%,100%,.1) -} - -.enable-forced-colors .barFill_a562c8 { - background: Highlight -} - -.enable-forced-colors .grabber_a562c8 { - background-color: Highlight; - border-color: Highlight -} - -.enable-forced-colors .bar_a562c8 { - background: Canvas; - border: 1px solid ButtonText -} - -.enable-forced-colors .markDash_a562c8 { - background: CanvasText -} - -.enable-forced-colors .disabled_a562c8 { - opacity: 1 -} - -.enable-forced-colors .disabled_a562c8 .barFill_a562c8 { - background: GrayText -} - -.enable-forced-colors .disabled_a562c8 .grabber_a562c8 { - background-color: GrayText; - border-color: GrayText -} - -.enable-forced-colors .disabled_a562c8 .bar_a562c8 { - background: Canvas; - border-color: GrayText -} - -.loadingPopout__58f1c { - background-color: var(--background-base-lower); - display: flex; - justify-content: center; - padding: 8px -} - -.full-motion .translate_faf9c0.animatorTop_faf9c0 { - transform: translate3d(0,-10px,0) -} - -.full-motion .translate_faf9c0.animatorBottom_faf9c0 { - transform: translate3d(0,10px,0) -} - -.full-motion .translate_faf9c0.animatorLeft_faf9c0 { - transform: translate3d(-10px,0,0) -} - -.full-motion .translate_faf9c0.animatorRight_faf9c0 { - transform: translate3d(10px,0,0) -} - -.full-motion .translate_faf9c0.animatorCenter_faf9c0 { - transform: translate3d(0,-10px,0) -} - -.full-motion .translate_faf9c0.didRender_faf9c0 { - transform: translateZ(0); - transition: transform .2s ease-out -} - -.full-motion .translate_faf9c0[data-popout-animating=false] { - position: relative; - transform: none; - transition: none; - z-index: 0 -} - -.full-motion .scale_faf9c0 { - opacity: 0; - transform: scale(.01) -} - -.full-motion .scale_faf9c0.animatorTop_faf9c0 { - transform-origin: bottom center -} - -.full-motion .scale_faf9c0.animatorBottom_faf9c0 { - transform-origin: top center -} - -.full-motion .scale_faf9c0.animatorLeft_faf9c0 { - transform-origin: top right -} - -.full-motion .scale_faf9c0.animatorRight_faf9c0 { - transform-origin: top left -} - -.full-motion .scale_faf9c0.animatorRight_faf9c0 { - transform-origin: top center -} - -.full-motion .scale_faf9c0.didRender_faf9c0 { - opacity: 1; - transform: scale(1); - transition: transform .12s ease-out,opacity .12s ease-out -} - -.full-motion .scale_faf9c0[data-popout-animating=false] { - position: relative; - transform: none; - transition: none; - z-index: 0 -} - -.fade_faf9c0 { - opacity: 0 -} - -.fade_faf9c0.didRender_faf9c0 { - opacity: 1; - position: relative; - transition: opacity .08s ease-out; - z-index: 0 -} - -.loader__82471 { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: 8px; - display: flex; - height: 80px; - justify-content: center -} - -.select_a16aea { - align-items: center; - box-sizing: border-box; - cursor: pointer; - display: grid; - gap: 8px; - grid-template-columns: 1fr auto; - padding-block:8px;padding-inline:12px 8px} - -.select_a16aea,.wrapper_a16aea { - color: var(--text-default); - font-weight: var(--font-weight-medium) -} - -.searchable_a16aea { - padding-bottom: 8px; - padding-top: 8px -} - -.searchInput_a16aea { - background: inherit; - border: none; - color: inherit; - cursor: pointer; - font-size: 16px; - padding: 0; - width: 100% -} - -.searchInput_a16aea::-moz-placeholder { - color: var(--input-placeholder-text-default) -} - -.searchInput_a16aea::placeholder { - color: var(--input-placeholder-text-default) -} - -.searchInput_a16aea.multi_a16aea { - height: 32px; - margin-inline-start:4px;width: auto -} - -.editing_a16aea { - font-weight: var(--font-weight-normal) -} - -.label_a16aea { - bottom: 0; - height: 16px; - inset-inline-start: 12px; - margin: auto; - position: absolute; - top: 0 -} - -.optionDisabled_a16aea { - opacity: .5 -} - -.searchInput_a16aea.multi_a16aea.hidden_a16aea { - opacity: 0; - width: 0 -} - -.iconsCenter_a16aea { - align-items: center; - display: flex; - height: 100% -} - -.placeholder_a16aea,.value_a16aea { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.placeholder_a16aea { - color: var(--text-subtle) -} - -.measurement_a16aea { - border: 1px solid transparent; - pointer-events: none; - position: absolute; - visibility: hidden -} - -.measurement_a16aea,.popout_a16aea { - box-sizing: border-box -} - -.popout_a16aea { - background: var(--background-base-lower); - background-color: var(--background-surface-higher); - border: 1px solid var(--background-base-lowest); - border-color: var(--border-subtle); - border-radius: 0 0 4px 4px; - margin: 0 -} - -.popout_a16aea:not(.popoutPositionTop_a16aea) { - margin-top: 8px -} - -.popout_a16aea.popoutPositionTop_a16aea { - border-radius: 4px 4px 0 0; - margin-bottom: 8px -} - -.lookFilled_a16aea.popout_a16aea { - border-radius: 0 0 3px 3px -} - -.lookFilled_a16aea.popout_a16aea.popoutPositionTop_a16aea { - border-radius: 3px 3px 0 0 -} - -.lookFilled_a16aea.option_a16aea,.lookFilled_a16aea.select_a16aea:hover.option_a16aea { - border-color: var(--brand-500); - border-bottom-color: var(--background-base-lowest) -} - -.option_a16aea { - align-items: center; - box-sizing: border-box; - color: var(--text-subtle); - cursor: pointer; - display: grid; - font-size: 16px; - font-weight: 400; - grid-template-columns: 1fr auto; - line-height: 20px; - padding: 12px -} - -.option_a16aea[aria-selected=true] { - color: var(--text-default); - font-weight: var(--font-weight-medium) -} - -.option_a16aea:hover,.option_a16aea[aria-selected=true]:hover { - background-color: var(--background-mod-subtle) -} - -.option_a16aea:hover { - color: var(--text-default) -} - -.option_a16aea.focused_a16aea,.option_a16aea:focus-visible,.option_a16aea:hover { - background-color: var(--interactive-background-hover); - color: var(--interactive-text-hover) -} - -.option_a16aea[aria-selected=true]:not(.option_a16aea.multi_a16aea) { - background-color: var(--interactive-background-selected); - color: var(--interactive-text-active) -} - -.selectedIcon_a16aea { - color: var(--brand-500) -} - -.icons_a16aea { - gap: 4px -} - -.icons_a16aea,.value_a16aea { - align-items: center; - display: flex -} - -.value_a16aea { - gap: 8px -} - -.clear_a16aea { - display: block -} - -.clear_a16aea,.dropdownIcon_a16aea { - cursor: pointer; - pointer-events: all -} - -.noResults_a16aea { - align-items: center; - background: var(--background-base-lower); - padding: 12px; - width: 100% -} - -.loading_a16aea,.noResults_a16aea { - display: flex; - justify-content: center -} - -.loading_a16aea { - height: 40px -} - -.loadingSpinner_a16aea { - background-color: var(--interactive-text-active) -} - -.wrapper_a16aea { - display: grid; - grid-template-columns: 1fr auto -} - -.wrapper_a16aea .select_a16aea { - grid-column: 1/span 2; - grid-row: 1; - padding-inline-end:calc(24px + var(--icons-width))} - -.wrapper_a16aea .select_a16aea.multi_a16aea { - padding-bottom: 6px; - padding-top: 0; - padding-inline-start:6px} - -.wrapper_a16aea .icons_a16aea { - grid-column: 2; - grid-row: 1; - margin-inline-end:8px;pointer-events: none -} - -.wrapper_a16aea .value_a16aea { - display: flex -} - -.wrapper_a16aea .value_a16aea.multi_a16aea { - display: block; - white-space: normal -} - -.wrapper_a16aea .searchInput_a16aea { - flex: 1 0 0 -} - -.searchableSelect_a16aea .option_a16aea { - align-content: center; - display: grid; - grid-template-areas: "prefix content suffix selectedIcon"; - grid-template-columns: auto 1fr auto auto -} - -.searchableSelect_a16aea .prefix_a16aea { - grid-area: prefix; - margin-inline-end:8px} - -.searchableSelect_a16aea .suffix_a16aea { - grid-area: suffix; - margin-inline-end:8px;margin-inline-start:8px} - -.searchableSelect_a16aea .content_a16aea { - display: flex; - grid-area: content; - min-width: 0 -} - -.searchableSelect_a16aea .selectedIcon_a16aea { - grid-area: selectedIcon -} - -.optionPillWrapper_a16aea { - min-width: auto; - padding: 0; - width: auto -} - -.optionPillContainer_a16aea { - display: inline -} - -.optionPillItem_a16aea { - align-items: center; - display: inline-flex; - margin-inline-end:6px} - -.optionPillItem_a16aea button { - background-color: var(--background-surface-higher) -} - -.optionPillItem_a16aea button .optionPill_a16aea { - border: 1px solid var(--border-muted); - border-radius: var(--radius-xs) -} - -.optionPill_a16aea { - align-items: center; - background: var(--background-base-low); - border-radius: 2px; - color: var(--interactive-text-active); - display: inline-flex; - font-size: 14px; - line-height: 20px; - padding: var(--space-4) var(--space-8) -} - -.optionPill_a16aea .deleteOptionIcon_a16aea { - height: 16px; - width: 16px -} - -.optionPillBtn_a16aea { - height: auto; - min-height: auto; - min-width: 0; - padding: 0 -} - -.deleteOptionIcon_a16aea { - color: var(--interactive-text-default); - margin-inline-start:8px} - -.optionPillItem_a16aea,.searchInput_a16aea.multi_a16aea { - margin-top: var(--space-4) -} - -.no-webkit-scrollbar .noScrollbar_a16aea { - scrollbar-width: none -} - -.noScrollbar_a16aea::-webkit-scrollbar { - height: 0; - width: 0 -} - -.multiSelectCheckbox_a16aea { - align-items: center; - border-radius: 6px; - box-sizing: border-box; - display: flex; - grid-area: selectedIcon; - height: 20px; - width: 20px -} - -.multiSelectCheckbox_a16aea.unchecked_a16aea { - border: 1px solid var(--border-strong) -} - -.multiSelectCheckbox_a16aea.checked_a16aea { - background: var(--brand-500); - border: 1px solid var(--brand-500) -} - -.multiSelectCheck_a16aea { - color: var(--white) -} - -.optionPill_a16aea { - align-content: center; - display: grid; - grid-template-areas: "prefix content suffix clearIcon"; - grid-template-columns: auto 1fr auto auto -} - -.optionPill_a16aea .prefix_a16aea { - grid-area: prefix; - margin-inline-end:8px} - -.optionPill_a16aea .suffix_a16aea { - grid-area: suffix; - margin-inline-end:8px;margin-inline-start:8px} - -.optionPill_a16aea .content_a16aea { - grid-area: content -} - -.optionPill_a16aea .deleteOptionIcon_a16aea { - grid-area: clearIcon -} - -.enable-forced-colors .select_a16aea { - background-color: Canvas; - border-color: ButtonText -} - -.enable-forced-colors .disabled_a16aea,.enable-forced-colors .disabled_a16aea * { - border-color: GrayText; - color: GrayText; - opacity: 1 -} - -.enable-forced-colors .placeholder_a16aea { - color: GrayText -} - -.enable-forced-colors .option_a16aea { - background-color: Canvas; - border: 1px solid Canvas; - color: CanvasText; - forced-color-adjust: none -} - -.enable-forced-colors .option_a16aea.focused_a16aea,.enable-forced-colors .option_a16aea:focus,.enable-forced-colors .option_a16aea:hover { - background-color: ButtonFace; - border-color: ButtonText; - color: ButtonText -} - -.enable-forced-colors .option_a16aea[aria-selected=true] { - background-color: HighlightText!important; - color: Highlight!important -} - -.enable-forced-colors .option_a16aea[aria-selected=true].focused_a16aea,.enable-forced-colors .option_a16aea[aria-selected=true]:focus,.enable-forced-colors .option_a16aea[aria-selected=true]:hover { - border-color: Highlight!important -} - -.enable-forced-colors .selectedIcon_a16aea { - color: Highlight -} - -.select_a16aea,.wrapper_a16aea { - min-height: var(--control-input-height-md) -} - -.iconsContainer_a16aea { - height: calc(var(--control-input-height-sm) - 2px) -} - -.container_a16aea[data-size=sm] .select_a16aea,.container_a16aea[data-size=sm] .wrapper_a16aea { - min-height: var(--control-input-height-sm) -} - -.container_a16aea[data-size=sm] .iconsContainer_a16aea { - height: calc(var(--control-input-height-sm) - 2px) -} - -.container_a16aea[data-variant=text-only] .select_a16aea { - background: none; - border: none -} - -.searchable_a16aea { - min-height: auto -} - -.lookFilled_a16aea.popout_a16aea,.lookFilled_a16aea.popout_a16aea.popoutPositionTop_a16aea,.popout_a16aea,.popout_a16aea.popoutPositionTop_a16aea { - background-color: var(--background-surface-higher); - border: 1px solid var(--border-subtle); - border-radius: 8px -} - -.newOptionLabel_a16aea { - align-items: center; - display: grid; - gap: 4px; - grid-template-columns: auto 1fr auto -} - -.newOptionLeading_a16aea { - height: 24px; - -o-object-fit: contain; - object-fit: contain; - width: 24px -} - -.react-datepicker__month-read-view--down-arrow,.react-datepicker__month-year-read-view--down-arrow,.react-datepicker__navigation-icon:before,.react-datepicker__year-read-view--down-arrow { - border-color: #ccc; - border-style: solid; - border-width: 3px 3px 0 0; - content: ""; - display: block; - height: 9px; - position: absolute; - top: 6px; - width: 9px -} - -.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle { - margin-left: -4px; - position: absolute; - width: 0 -} - -.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before { - border: 8px solid transparent; - box-sizing: content-box; - content: ""; - height: 0; - left: -8px; - position: absolute; - width: 1px; - z-index: -1 -} - -.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before { - border-bottom-color: #aeaeae -} - -.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle { - margin-top: -8px; - top: 0 -} - -.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before { - border-bottom-color: #f0f0f0; - border-top: none -} - -.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:after { - top: 0 -} - -.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before { - border-bottom-color: #aeaeae; - top: -1px -} - -.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle { - bottom: 0; - margin-bottom: -8px -} - -.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before { - border-bottom: none; - border-top-color: #fff -} - -.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:after { - bottom: 0 -} - -.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before { - border-top-color: #aeaeae; - bottom: -1px -} - -.react-datepicker-wrapper { - border: 0; - display: inline-block; - padding: 0 -} - -.react-datepicker { - background-color: #fff; - border: 1px solid #aeaeae; - border-radius: .3rem; - color: #000; - display: inline-block; - font-family: Helvetica Neue,helvetica,arial,sans-serif; - font-size: .8rem; - position: relative -} - -.react-datepicker--time-only .react-datepicker__triangle { - left: 35px -} - -.react-datepicker--time-only .react-datepicker__time-container { - border-left: 0 -} - -.react-datepicker--time-only .react-datepicker__time,.react-datepicker--time-only .react-datepicker__time-box { - border-bottom-left-radius: .3rem; - border-bottom-right-radius: .3rem -} - -.react-datepicker__triangle { - left: 50px; - position: absolute -} - -.react-datepicker-popper { - z-index: 1 -} - -.react-datepicker-popper[data-placement^=bottom] { - padding-top: 10px -} - -.react-datepicker-popper[data-placement=bottom-end] .react-datepicker__triangle,.react-datepicker-popper[data-placement=top-end] .react-datepicker__triangle { - left: auto; - right: 50px -} - -.react-datepicker-popper[data-placement^=top] { - padding-bottom: 10px -} - -.react-datepicker-popper[data-placement^=right] { - padding-left: 8px -} - -.react-datepicker-popper[data-placement^=right] .react-datepicker__triangle { - left: auto; - right: 42px -} - -.react-datepicker-popper[data-placement^=left] { - padding-right: 8px -} - -.react-datepicker-popper[data-placement^=left] .react-datepicker__triangle { - left: 42px; - right: auto -} - -.react-datepicker__header { - background-color: #f0f0f0; - border-bottom: 1px solid #aeaeae; - border-top-left-radius: .3rem; - padding: 8px 0; - position: relative; - text-align: center -} - -.react-datepicker__header--time { - padding-bottom: 8px; - padding-left: 5px; - padding-right: 5px -} - -.react-datepicker__header--time:not(.react-datepicker__header--time--only) { - border-top-left-radius: 0 -} - -.react-datepicker__header:not(.react-datepicker__header--has-time-select) { - border-top-right-radius: .3rem -} - -.react-datepicker__month-dropdown-container--scroll,.react-datepicker__month-dropdown-container--select,.react-datepicker__month-year-dropdown-container--scroll,.react-datepicker__month-year-dropdown-container--select,.react-datepicker__year-dropdown-container--scroll,.react-datepicker__year-dropdown-container--select { - display: inline-block; - margin: 0 2px -} - -.react-datepicker-time__header,.react-datepicker-year-header,.react-datepicker__current-month { - color: #000; - font-size: .944rem; - font-weight: 700; - margin-top: 0 -} - -.react-datepicker-time__header { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.react-datepicker__navigation { - align-items: center; - background: none; - border: none; - cursor: pointer; - display: flex; - height: 32px; - justify-content: center; - overflow: hidden; - padding: 0; - position: absolute; - text-align: center; - text-indent: -999em; - top: 2px; - width: 32px; - z-index: 1 -} - -.react-datepicker__navigation--previous { - left: 2px -} - -.react-datepicker__navigation--next { - right: 2px -} - -.react-datepicker__navigation--next--with-time:not(.react-datepicker__navigation--next--with-today-button) { - right: 85px -} - -.react-datepicker__navigation--years { - display: block; - margin-left: auto; - margin-right: auto; - position: relative; - top: 0 -} - -.react-datepicker__navigation--years-previous { - top: 4px -} - -.react-datepicker__navigation--years-upcoming { - top: -4px -} - -.react-datepicker__navigation:hover :before { - border-color: #a6a6a6 -} - -.react-datepicker__navigation-icon { - font-size: 20px; - position: relative; - top: -1px -} - -.react-datepicker__navigation-icon--next { - left: -2px -} - -.react-datepicker__navigation-icon--next:before { - left: -7px; - transform: rotate(45deg) -} - -.react-datepicker__navigation-icon--previous { - right: -2px -} - -.react-datepicker__navigation-icon--previous:before { - right: -7px; - transform: rotate(225deg) -} - -.react-datepicker__month-container { - float: left -} - -.react-datepicker__year { - margin: .4rem; - text-align: center -} - -.react-datepicker__year-wrapper { - display: flex; - flex-wrap: wrap; - max-width: 180px -} - -.react-datepicker__year .react-datepicker__year-text { - display: inline-block; - margin: 2px; - width: 4rem -} - -.react-datepicker__month { - margin: .4rem; - text-align: center -} - -.react-datepicker__month .react-datepicker__month-text,.react-datepicker__month .react-datepicker__quarter-text { - display: inline-block; - margin: 2px; - width: 4rem -} - -.react-datepicker__input-time-container { - clear: both; - float: left; - margin: 5px 0 10px 15px; - text-align: left; - width: 100% -} - -.react-datepicker__input-time-container .react-datepicker-time__caption,.react-datepicker__input-time-container .react-datepicker-time__input-container { - display: inline-block -} - -.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input { - display: inline-block; - margin-left: 10px -} - -.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input { - width: auto -} - -.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time]::-webkit-inner-spin-button,.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time]::-webkit-outer-spin-button { - -webkit-appearance: none; - margin: 0 -} - -.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time] { - -moz-appearance: textfield -} - -.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__delimiter { - display: inline-block; - margin-left: 5px -} - -.react-datepicker__time-container { - border-left: 1px solid #aeaeae; - float: right; - width: 85px -} - -.react-datepicker__time-container--with-today-button { - border: 1px solid #aeaeae; - border-radius: .3rem; - display: inline; - position: absolute; - right: -72px; - top: 0 -} - -.react-datepicker__time-container .react-datepicker__time { - background: #fff; - border-bottom-right-radius: .3rem; - position: relative -} - -.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box { - border-bottom-right-radius: .3rem; - margin: 0 auto; - overflow-x: hidden; - text-align: center; - width: 85px -} - -.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list { - box-sizing: content-box; - height: calc(195px + .85rem); - list-style: none; - margin: 0; - overflow-y: scroll; - padding-left: 0; - padding-right: 0; - width: 100% -} - -.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item { - height: 30px; - padding: 5px 10px; - white-space: nowrap -} - -.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item:hover { - background-color: #f0f0f0; - cursor: pointer -} - -.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected { - background-color: #216ba5; - color: #fff; - font-weight: 700 -} - -.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected:hover { - background-color: #216ba5 -} - -.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled { - color: #ccc -} - -.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled:hover { - background-color: transparent; - cursor: default -} - -.react-datepicker__week-number { - color: #ccc; - display: inline-block; - line-height: 1.7rem; - margin: .166rem; - text-align: center; - width: 1.7rem -} - -.react-datepicker__week-number.react-datepicker__week-number--clickable { - cursor: pointer -} - -.react-datepicker__week-number.react-datepicker__week-number--clickable:hover { - background-color: #f0f0f0; - border-radius: .3rem -} - -.react-datepicker__day-names,.react-datepicker__week { - white-space: nowrap -} - -.react-datepicker__day-names { - margin-bottom: -8px -} - -.react-datepicker__day,.react-datepicker__day-name,.react-datepicker__time-name { - color: #000; - display: inline-block; - line-height: 1.7rem; - margin: .166rem; - text-align: center; - width: 1.7rem -} - -.react-datepicker__month--in-range,.react-datepicker__month--in-selecting-range,.react-datepicker__month--selected,.react-datepicker__quarter--in-range,.react-datepicker__quarter--in-selecting-range,.react-datepicker__quarter--selected { - background-color: #216ba5; - border-radius: .3rem; - color: #fff -} - -.react-datepicker__month--in-range:hover,.react-datepicker__month--in-selecting-range:hover,.react-datepicker__month--selected:hover,.react-datepicker__quarter--in-range:hover,.react-datepicker__quarter--in-selecting-range:hover,.react-datepicker__quarter--selected:hover { - background-color: #1d5d90 -} - -.react-datepicker__month--disabled,.react-datepicker__quarter--disabled { - color: #ccc; - pointer-events: none -} - -.react-datepicker__month--disabled:hover,.react-datepicker__quarter--disabled:hover { - background-color: transparent; - cursor: default -} - -.react-datepicker__day,.react-datepicker__month-text,.react-datepicker__quarter-text,.react-datepicker__year-text { - cursor: pointer -} - -.react-datepicker__day:hover,.react-datepicker__month-text:hover,.react-datepicker__quarter-text:hover,.react-datepicker__year-text:hover { - background-color: #f0f0f0; - border-radius: .3rem -} - -.react-datepicker__day--today,.react-datepicker__month-text--today,.react-datepicker__quarter-text--today,.react-datepicker__year-text--today { - font-weight: 700 -} - -.react-datepicker__day--highlighted,.react-datepicker__month-text--highlighted,.react-datepicker__quarter-text--highlighted,.react-datepicker__year-text--highlighted { - background-color: #3dcc4a; - border-radius: .3rem; - color: #fff -} - -.react-datepicker__day--highlighted:hover,.react-datepicker__month-text--highlighted:hover,.react-datepicker__quarter-text--highlighted:hover,.react-datepicker__year-text--highlighted:hover { - background-color: #32be3f -} - -.react-datepicker__day--highlighted-custom-1,.react-datepicker__month-text--highlighted-custom-1,.react-datepicker__quarter-text--highlighted-custom-1,.react-datepicker__year-text--highlighted-custom-1 { - color: #f0f -} - -.react-datepicker__day--highlighted-custom-2,.react-datepicker__month-text--highlighted-custom-2,.react-datepicker__quarter-text--highlighted-custom-2,.react-datepicker__year-text--highlighted-custom-2 { - color: green -} - -.react-datepicker__day--in-range,.react-datepicker__day--in-selecting-range,.react-datepicker__day--selected,.react-datepicker__month-text--in-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__month-text--selected,.react-datepicker__quarter-text--in-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__quarter-text--selected,.react-datepicker__year-text--in-range,.react-datepicker__year-text--in-selecting-range,.react-datepicker__year-text--selected { - background-color: #216ba5; - border-radius: .3rem; - color: #fff -} - -.react-datepicker__day--in-range:hover,.react-datepicker__day--in-selecting-range:hover,.react-datepicker__day--selected:hover,.react-datepicker__month-text--in-range:hover,.react-datepicker__month-text--in-selecting-range:hover,.react-datepicker__month-text--selected:hover,.react-datepicker__quarter-text--in-range:hover,.react-datepicker__quarter-text--in-selecting-range:hover,.react-datepicker__quarter-text--selected:hover,.react-datepicker__year-text--in-range:hover,.react-datepicker__year-text--in-selecting-range:hover,.react-datepicker__year-text--selected:hover { - background-color: #1d5d90 -} - -.react-datepicker__day--keyboard-selected,.react-datepicker__month-text--keyboard-selected,.react-datepicker__quarter-text--keyboard-selected,.react-datepicker__year-text--keyboard-selected { - background-color: #2a87d0; - border-radius: .3rem; - color: #fff -} - -.react-datepicker__day--keyboard-selected:hover,.react-datepicker__month-text--keyboard-selected:hover,.react-datepicker__quarter-text--keyboard-selected:hover,.react-datepicker__year-text--keyboard-selected:hover { - background-color: #1d5d90 -} - -.react-datepicker__day--in-selecting-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__year-text--in-selecting-range { - background-color: rgba(33,107,165,.5) -} - -.react-datepicker__month--selecting-range .react-datepicker__day--in-range,.react-datepicker__month--selecting-range .react-datepicker__month-text--in-range,.react-datepicker__month--selecting-range .react-datepicker__quarter-text--in-range,.react-datepicker__month--selecting-range .react-datepicker__year-text--in-range { - background-color: #f0f0f0; - color: #000 -} - -.react-datepicker__day--disabled,.react-datepicker__month-text--disabled,.react-datepicker__quarter-text--disabled,.react-datepicker__year-text--disabled { - color: #ccc; - cursor: default -} - -.react-datepicker__day--disabled:hover,.react-datepicker__month-text--disabled:hover,.react-datepicker__quarter-text--disabled:hover,.react-datepicker__year-text--disabled:hover { - background-color: transparent -} - -.react-datepicker__month-text.react-datepicker__month--in-range:hover,.react-datepicker__month-text.react-datepicker__month--selected:hover,.react-datepicker__month-text.react-datepicker__quarter--in-range:hover,.react-datepicker__month-text.react-datepicker__quarter--selected:hover,.react-datepicker__quarter-text.react-datepicker__month--in-range:hover,.react-datepicker__quarter-text.react-datepicker__month--selected:hover,.react-datepicker__quarter-text.react-datepicker__quarter--in-range:hover,.react-datepicker__quarter-text.react-datepicker__quarter--selected:hover { - background-color: #216ba5 -} - -.react-datepicker__month-text:hover,.react-datepicker__quarter-text:hover { - background-color: #f0f0f0 -} - -.react-datepicker__input-container { - display: inline-block; - position: relative; - width: 100% -} - -.react-datepicker__month-read-view,.react-datepicker__month-year-read-view,.react-datepicker__year-read-view { - border: 1px solid transparent; - border-radius: .3rem; - position: relative -} - -.react-datepicker__month-read-view:hover,.react-datepicker__month-year-read-view:hover,.react-datepicker__year-read-view:hover { - cursor: pointer -} - -.react-datepicker__month-read-view:hover .react-datepicker__month-read-view--down-arrow,.react-datepicker__month-read-view:hover .react-datepicker__year-read-view--down-arrow,.react-datepicker__month-year-read-view:hover .react-datepicker__month-read-view--down-arrow,.react-datepicker__month-year-read-view:hover .react-datepicker__year-read-view--down-arrow,.react-datepicker__year-read-view:hover .react-datepicker__month-read-view--down-arrow,.react-datepicker__year-read-view:hover .react-datepicker__year-read-view--down-arrow { - border-top-color: #b3b3b3 -} - -.react-datepicker__month-read-view--down-arrow,.react-datepicker__month-year-read-view--down-arrow,.react-datepicker__year-read-view--down-arrow { - right: -16px; - top: 0; - transform: rotate(135deg) -} - -.react-datepicker__month-dropdown,.react-datepicker__month-year-dropdown,.react-datepicker__year-dropdown { - background-color: #f0f0f0; - border: 1px solid #aeaeae; - border-radius: .3rem; - left: 25%; - position: absolute; - text-align: center; - top: 30px; - width: 50%; - z-index: 1 -} - -.react-datepicker__month-dropdown:hover,.react-datepicker__month-year-dropdown:hover,.react-datepicker__year-dropdown:hover { - cursor: pointer -} - -.react-datepicker__month-dropdown--scrollable,.react-datepicker__month-year-dropdown--scrollable,.react-datepicker__year-dropdown--scrollable { - height: 150px; - overflow-y: scroll -} - -.react-datepicker__month-option,.react-datepicker__month-year-option,.react-datepicker__year-option { - display: block; - line-height: 20px; - margin-left: auto; - margin-right: auto; - width: 100% -} - -.react-datepicker__month-option:first-of-type,.react-datepicker__month-year-option:first-of-type,.react-datepicker__year-option:first-of-type { - border-top-left-radius: .3rem; - border-top-right-radius: .3rem -} - -.react-datepicker__month-option:last-of-type,.react-datepicker__month-year-option:last-of-type,.react-datepicker__year-option:last-of-type { - border-bottom-left-radius: .3rem; - border-bottom-right-radius: .3rem; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.react-datepicker__month-option:hover,.react-datepicker__month-year-option:hover,.react-datepicker__year-option:hover { - background-color: #ccc -} - -.react-datepicker__month-option:hover .react-datepicker__navigation--years-upcoming,.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-upcoming,.react-datepicker__year-option:hover .react-datepicker__navigation--years-upcoming { - border-bottom-color: #b3b3b3 -} - -.react-datepicker__month-option:hover .react-datepicker__navigation--years-previous,.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-previous,.react-datepicker__year-option:hover .react-datepicker__navigation--years-previous { - border-top-color: #b3b3b3 -} - -.react-datepicker__month-option--selected,.react-datepicker__month-year-option--selected,.react-datepicker__year-option--selected { - left: 15px; - position: absolute -} - -.react-datepicker__close-icon { - background-color: transparent; - border: 0; - cursor: pointer; - display: table-cell; - height: 100%; - outline: 0; - padding: 0 6px 0 0; - position: absolute; - right: 0; - top: 0; - vertical-align: middle -} - -.react-datepicker__close-icon:after { - background-color: #216ba5; - border-radius: 50%; - color: #fff; - content: "\00d7"; - cursor: pointer; - display: table-cell; - font-size: 12px; - height: 16px; - line-height: 1; - padding: 2px; - text-align: center; - vertical-align: middle; - width: 16px -} - -.react-datepicker__today-button { - background: #f0f0f0; - border-top: 1px solid #aeaeae; - clear: left; - cursor: pointer; - font-weight: 700; - padding: 5px 0; - text-align: center -} - -.react-datepicker__portal { - align-items: center; - background-color: rgba(0,0,0,.8); - display: flex; - height: 100vh; - justify-content: center; - left: 0; - position: fixed; - top: 0; - width: 100vw; - z-index: 2147483647 -} - -.react-datepicker__portal .react-datepicker__day,.react-datepicker__portal .react-datepicker__day-name,.react-datepicker__portal .react-datepicker__time-name { - line-height: 3rem; - width: 3rem -} - -@media (max-height: 550px),(max-width:400px) { - .react-datepicker__portal .react-datepicker__day,.react-datepicker__portal .react-datepicker__day-name,.react-datepicker__portal .react-datepicker__time-name { - line-height:2rem; - width: 2rem - } -} - -.react-datepicker__portal .react-datepicker-time__header,.react-datepicker__portal .react-datepicker__current-month { - font-size: 1.44rem -} - -.calendarPicker_d27f17 .react-datepicker { - background-color: var(--background-surface-higher); - border: 1px solid var(--border-subtle); - font-family: var(--font-primary); - padding: 20px -} - -.calendarPicker_d27f17 .react-datepicker__header { - background-color: var(--background-surface-higher); - border-bottom: 0; - padding-top: 0 -} - -.calendarPicker_d27f17 .react-datepicker__current-month { - border-bottom: 1px solid; - color: var(--text-strong); - font-size: 13px; - font-weight: var(--font-weight-semibold); - line-height: 20px; - margin: 0 0 10px; - padding: 0 0 20px; - text-transform: uppercase -} - -.calendarPicker_d27f17 .react-datepicker__navigation.react-datepicker__navigation--next,.calendarPicker_d27f17 .react-datepicker__navigation.react-datepicker__navigation--previous { - background-position: 50%; - background-repeat: no-repeat; - background-size: 6px 12px; - border: 1px solid; - border-radius: 2px; - height: 18px; - inset-inline-start: 20px; - top: 20px; - transform: rotate(180deg); - width: 18px -} - -.calendarPicker_d27f17 .react-datepicker__navigation.react-datepicker__navigation--next.react-datepicker__navigation--next,.calendarPicker_d27f17 .react-datepicker__navigation.react-datepicker__navigation--previous.react-datepicker__navigation--next { - inset-inline-end: 20px; - inset-inline-start: auto; - transform: rotate(0deg) -} - -.calendarPicker_d27f17 .react-datepicker__navigation.react-datepicker__navigation--next:focus-visible,.calendarPicker_d27f17 .react-datepicker__navigation.react-datepicker__navigation--previous:focus-visible { - box-shadow: 0 0 0 4px var(--blue-345) -} - -.calendarPicker_d27f17 .react-datepicker__navigation-icon:before { - border-width: 0 -} - -.calendarPicker_d27f17 .react-datepicker__month { - margin: 0 -} - -.calendarPicker_d27f17 .react-datepicker__week:first-of-type>.react-datepicker__day:first-of-type { - border-radius: 3px 0 0 -} - -.calendarPicker_d27f17 .react-datepicker__week:first-of-type>.react-datepicker__day:last-of-type { - border-radius: 0 3px 0 0 -} - -.calendarPicker_d27f17 .react-datepicker__week:last-of-type>.react-datepicker__day { - border-bottom: 1px solid var(--primary-200) -} - -.calendarPicker_d27f17 .react-datepicker__week:last-of-type>.react-datepicker__day:first-of-type { - border-radius: 0 0 0 3px -} - -.calendarPicker_d27f17 .react-datepicker__week:last-of-type>.react-datepicker__day:last-of-type { - border-radius: 0 0 3px -} - -.calendarPicker_d27f17 .react-datepicker__week>.react-datepicker__day:last-of-type { - border-inline-end:1px solid} - -.calendarPicker_d27f17 .react-datepicker__day-names { - margin-bottom: 10px -} - -.calendarPicker_d27f17 .react-datepicker__day-name { - font-size: 13px; - font-weight: var(--font-weight-medium); - line-height: 1em; - margin: 0; - text-transform: uppercase; - width: 40px -} - -.calendarPicker_d27f17 .react-datepicker__day { - border-inline-start:1px solid var(--border-subtle);border-top: 1px solid var(--border-subtle); - box-sizing: border-box; - color: var(--text-strong); - height: 40px; - line-height: 40px; - margin: 0; - position: relative; - width: 40px -} - -.calendarPicker_d27f17 .react-datepicker__day.react-datepicker__day--keyboard-selected,.calendarPicker_d27f17 .react-datepicker__day.react-datepicker__day--selected:hover,.calendarPicker_d27f17 .react-datepicker__day:hover { - background-color: var(--brand-500); - border-radius: 0; - color: var(--white) -} - -.calendarPicker_d27f17 .react-datepicker__day.react-datepicker__day--selected { - background: none; - border-radius: 0 -} - -.calendarPicker_d27f17 .react-datepicker__day.react-datepicker__day--selected:after { - background-color: var(--brand-500); - bottom: 0; - content: ""; - height: 3px; - inset-inline-start: 0; - position: absolute; - width: 100% -} - -.calendarPicker_d27f17 .react-datepicker__day:focus-visible { - border-top-color: var(--blue-345); - border: 1px solid var(--blue-345); - border-inline-start-color:var(--blue-345)} - -.calendarPicker_d27f17 .react-datepicker__day--disabled,.calendarPicker_d27f17 .react-datepicker__day--outside-month { - font-weight: var(--font-weight-medium) -} - -.calendarPicker_d27f17 .react-datepicker__day--disabled_d27f17,.calendarPicker_d27f17 .react-datepicker__day--outside-month_d27f17 { - background-color: var(--background-base-lower) -} - -.theme-light .calendarPicker_d27f17 .react-datepicker__header { - border-color: hsl(var(--primary-200-hsl)/.2) -} - -.theme-light .calendarPicker_d27f17 .react-datepicker__current-month { - border-bottom-color: hsl(var(--primary-200-hsl)/.2) -} - -.theme-light .calendarPicker_d27f17 .react-datepicker__navigation.react-datepicker__navigation--next,.theme-light .calendarPicker_d27f17 .react-datepicker__navigation.react-datepicker__navigation--previous { - border-color: var(--primary-200) -} - -.theme-light .calendarPicker_d27f17 .react-datepicker__week:last-of-type .react-datepicker__day { - border-bottom-color: var(--primary-200) -} - -.theme-light .calendarPicker_d27f17 .react-datepicker__week .react-datepicker__day:last-of-type { - border-inline-end-color:var(--primary-200)} - -.theme-light .calendarPicker_d27f17 .react-datepicker__day-name { - color: hsl(var(--primary-500-hsl)/.6) -} - -.theme-light .calendarPicker_d27f17 .react-datepicker__day.react-datepicker__day--disabled,.theme-light .calendarPicker_d27f17 .react-datepicker__day.react-datepicker__day--disabled:hover { - background-color: var(--opacity-black-4); - color: var(--opacity-black-20) -} - -.theme-light .calendarPicker_d27f17 .react-datepicker__day--disabled,.theme-light .calendarPicker_d27f17 .react-datepicker__day--outside-month { - color: hsl(var(--primary-500-hsl)/.3) -} - -.theme-dark .calendarPicker_d27f17 .react-datepicker__header { - border-color: hsl(var(--primary-800-hsl)/.2) -} - -.theme-dark .calendarPicker_d27f17 .react-datepicker__current-month { - border-bottom-color: hsl(var(--primary-800-hsl)/.3) -} - -.theme-dark .calendarPicker_d27f17 .react-datepicker__navigation.react-datepicker__navigation--next,.theme-dark .calendarPicker_d27f17 .react-datepicker__navigation.react-datepicker__navigation--previous { - border-color: var(--primary-800) -} - -.theme-dark .calendarPicker_d27f17 .react-datepicker__week:last-of-type .react-datepicker__day { - border-bottom-color: var(--primary-800) -} - -.theme-dark .calendarPicker_d27f17 .react-datepicker__week .react-datepicker__day:last-of-type { - border-inline-end-color:var(--primary-800)} - -.theme-dark .calendarPicker_d27f17 .react-datepicker__day-name { - color: hsl(var(--primary-100-hsl)/.6) -} - -.theme-dark .calendarPicker_d27f17 .react-datepicker__day.react-datepicker__day--disabled,.theme-dark .calendarPicker_d27f17 .react-datepicker__day.react-datepicker__day--disabled:hover { - background-color: var(--primary-630); - color: hsl(var(--primary-100-hsl)/.3) -} - -.theme-dark .calendarPicker_d27f17 .react-datepicker__day--disabled,.theme-dark .calendarPicker_d27f17 .react-datepicker__day--outside-month { - color: hsl(var(--primary-100-hsl)/.3) -} - -.enable-forced-colors .calendarPicker_d27f17 .react-datepicker { - border: 2px solid CanvasText -} - -.enable-forced-colors .calendarPicker_d27f17 .react-datepicker__day.react-datepicker__day--keyboard-selected,.enable-forced-colors .calendarPicker_d27f17 .react-datepicker__day.react-datepicker__day--selected:hover,.enable-forced-colors .calendarPicker_d27f17 .react-datepicker__day:hover { - background-color: HighlightText; - color: Highlight -} - -.enable-forced-colors .calendarPicker_d27f17 .react-datepicker__day.react-datepicker__day--selected:after { - background-color: Highlight -} - -.enable-forced-colors .calendarPicker_d27f17 .react-datepicker__day:focus-visible { - background-color: HighlightText; - border-color: Highlight; - color: Highlight -} - -.enable-forced-colors .calendarPicker_d27f17 .react-datepicker__day.react-datepicker__day--disabled,.enable-forced-colors .calendarPicker_d27f17 .react-datepicker__day.react-datepicker__day--disabled:hover { - color: GrayText -} - -.images-light .calendarPicker_d27f17 .react-datepicker__navigation.react-datepicker__navigation--next,.images-light .calendarPicker_d27f17 .react-datepicker__navigation.react-datepicker__navigation--previous { - background-image: url(/assets/f7d0845b63d81c46.svg) -} - -.images-dark .calendarPicker_d27f17 .react-datepicker__navigation.react-datepicker__navigation--next,.images-dark .calendarPicker_d27f17 .react-datepicker__navigation.react-datepicker__navigation--previous { - background-image: url(/assets/a46a54146b77fab4.svg) -} - -.calendarContainer__9bb02 { - box-shadow: var(--legacy-elevation-border),var(--legacy-elevation-high) -} - -.container__9bb02 { - cursor: pointer; - display: grid; - gap: var(--space-8); - grid-template-columns: 1fr auto; - min-height: var(--control-input-height-md); - padding: var(--space-8) var(--space-12) -} - -.richTooltipContent_a89985 { - align-items: center; - box-sizing: border-box; - display: flex; - gap: var(--space-8); - max-width: 272px; - padding: var(--space-8) var(--space-12) -} - -.assetContainer_a89985 { - align-items: center; - display: flex; - flex-shrink: 0; - justify-content: center -} - -.assetContainer_a89985>* { - height: auto; - max-width: 100% -} - -.textContent_a89985 { - display: flex; - flex: 1; - flex-direction: column; - min-width: 0 -} - -.noAsset_a89985 .textContent_a89985 { - text-align: center -} - -.heading-sm\/normal__0f084 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.heading-sm\/normal__0f084.fontScaling__0f084 { - font-size: .875rem -} - -.heading-sm\/medium__0f084 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.heading-sm\/medium__0f084.fontScaling__0f084 { - font-size: .875rem -} - -.heading-sm\/semibold__0f084 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.heading-sm\/semibold__0f084.fontScaling__0f084 { - font-size: .875rem -} - -.heading-sm\/bold__0f084 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.heading-sm\/bold__0f084.fontScaling__0f084 { - font-size: .875rem -} - -.heading-sm\/extrabold__0f084 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 800; - line-height: 1.2857142857142858 -} - -.heading-sm\/extrabold__0f084.fontScaling__0f084 { - font-size: .875rem -} - -.heading-md\/normal__0f084 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.heading-md\/normal__0f084.fontScaling__0f084 { - font-size: 1rem -} - -.heading-md\/medium__0f084 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.heading-md\/medium__0f084.fontScaling__0f084 { - font-size: 1rem -} - -.heading-md\/semibold__0f084 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.heading-md\/semibold__0f084.fontScaling__0f084 { - font-size: 1rem -} - -.heading-md\/bold__0f084 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.heading-md\/bold__0f084.fontScaling__0f084 { - font-size: 1rem -} - -.heading-md\/extrabold__0f084 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 800; - line-height: 1.25 -} - -.heading-md\/extrabold__0f084.fontScaling__0f084 { - font-size: 1rem -} - -.heading-lg\/normal__0f084 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.heading-lg\/normal__0f084.fontScaling__0f084 { - font-size: 1.25rem -} - -.heading-lg\/medium__0f084 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.heading-lg\/medium__0f084.fontScaling__0f084 { - font-size: 1.25rem -} - -.heading-lg\/semibold__0f084 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.heading-lg\/semibold__0f084.fontScaling__0f084 { - font-size: 1.25rem -} - -.heading-lg\/bold__0f084 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.heading-lg\/bold__0f084.fontScaling__0f084 { - font-size: 1.25rem -} - -.heading-lg\/extrabold__0f084 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 800; - line-height: 1.2 -} - -.heading-lg\/extrabold__0f084.fontScaling__0f084 { - font-size: 1.25rem -} - -.heading-xl\/normal__0f084 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xl\/normal__0f084.fontScaling__0f084 { - font-size: 1.5rem -} - -.heading-xl\/medium__0f084 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xl\/medium__0f084.fontScaling__0f084 { - font-size: 1.5rem -} - -.heading-xl\/semibold__0f084 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xl\/semibold__0f084.fontScaling__0f084 { - font-size: 1.5rem -} - -.heading-xl\/bold__0f084 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xl\/bold__0f084.fontScaling__0f084 { - font-size: 1.5rem -} - -.heading-xl\/extrabold__0f084 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xl\/extrabold__0f084.fontScaling__0f084 { - font-size: 1.5rem -} - -.heading-xxl\/normal__0f084 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xxl\/normal__0f084.fontScaling__0f084 { - font-size: 2rem -} - -.heading-xxl\/medium__0f084 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xxl\/medium__0f084.fontScaling__0f084 { - font-size: 2rem -} - -.heading-xxl\/semibold__0f084 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xxl\/semibold__0f084.fontScaling__0f084 { - font-size: 2rem -} - -.heading-xxl\/bold__0f084 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xxl\/bold__0f084.fontScaling__0f084 { - font-size: 2rem -} - -.heading-xxl\/extrabold__0f084 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xxl\/extrabold__0f084.fontScaling__0f084 { - font-size: 2rem -} - -.eyebrow__0f084 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - letter-spacing: .02em; - line-height: 1.3333333333333333; - text-transform: uppercase -} - -.eyebrow__0f084.fontScaling__0f084 { - font-size: .75rem -} - -.heading-deprecated-12\/normal__0f084 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/normal__0f084.fontScaling__0f084 { - font-size: .75rem -} - -.heading-deprecated-12\/medium__0f084 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/medium__0f084.fontScaling__0f084 { - font-size: .75rem -} - -.heading-deprecated-12\/semibold__0f084 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/semibold__0f084.fontScaling__0f084 { - font-size: .75rem -} - -.heading-deprecated-12\/bold__0f084 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/bold__0f084.fontScaling__0f084 { - font-size: .75rem -} - -.heading-deprecated-12\/extrabold__0f084 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 800; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/extrabold__0f084.fontScaling__0f084 { - font-size: .75rem -} - -.redesign\/heading-18\/bold__0f084 { - font-family: var(--font-display); - font-size: 18px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/heading-18\/bold__0f084.fontScaling__0f084 { - font-size: 1.125rem -} - -.text-xxs\/normal__0f084 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 400; - line-height: 1.2 -} - -.text-xxs\/normal__0f084.fontScaling__0f084 { - font-size: .625rem -} - -.text-xxs\/medium__0f084 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 500; - line-height: 1.2 -} - -.text-xxs\/medium__0f084.fontScaling__0f084 { - font-size: .625rem -} - -.text-xxs\/semibold__0f084 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 600; - line-height: 1.2 -} - -.text-xxs\/semibold__0f084.fontScaling__0f084 { - font-size: .625rem -} - -.text-xxs\/bold__0f084 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 700; - line-height: 1.2 -} - -.text-xxs\/bold__0f084.fontScaling__0f084 { - font-size: .625rem -} - -.text-xs\/normal__0f084 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.text-xs\/normal__0f084.fontScaling__0f084 { - font-size: .75rem -} - -.text-xs\/medium__0f084 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.text-xs\/medium__0f084.fontScaling__0f084 { - font-size: .75rem -} - -.text-xs\/semibold__0f084 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.text-xs\/semibold__0f084.fontScaling__0f084 { - font-size: .75rem -} - -.text-xs\/bold__0f084 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.text-xs\/bold__0f084.fontScaling__0f084 { - font-size: .75rem -} - -.text-sm\/normal__0f084 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.text-sm\/normal__0f084.fontScaling__0f084 { - font-size: .875rem -} - -.text-sm\/medium__0f084 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.text-sm\/medium__0f084.fontScaling__0f084 { - font-size: .875rem -} - -.text-sm\/semibold__0f084 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.text-sm\/semibold__0f084.fontScaling__0f084 { - font-size: .875rem -} - -.text-sm\/bold__0f084 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.text-sm\/bold__0f084.fontScaling__0f084 { - font-size: .875rem -} - -.text-md\/normal__0f084 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.text-md\/normal__0f084.fontScaling__0f084 { - font-size: 1rem -} - -.text-md\/medium__0f084 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.text-md\/medium__0f084.fontScaling__0f084 { - font-size: 1rem -} - -.text-md\/semibold__0f084 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.text-md\/semibold__0f084.fontScaling__0f084 { - font-size: 1rem -} - -.text-md\/bold__0f084 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.text-md\/bold__0f084.fontScaling__0f084 { - font-size: 1rem -} - -.text-lg\/normal__0f084 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.text-lg\/normal__0f084.fontScaling__0f084 { - font-size: 1.25rem -} - -.text-lg\/medium__0f084 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.text-lg\/medium__0f084.fontScaling__0f084 { - font-size: 1.25rem -} - -.text-lg\/semibold__0f084 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.text-lg\/semibold__0f084.fontScaling__0f084 { - font-size: 1.25rem -} - -.text-lg\/bold__0f084 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.text-lg\/bold__0f084.fontScaling__0f084 { - font-size: 1.25rem -} - -.redesign\/message-preview\/normal__0f084 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/normal__0f084.fontScaling__0f084 { - font-size: .9375rem -} - -.redesign\/message-preview\/medium__0f084 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/medium__0f084.fontScaling__0f084 { - font-size: .9375rem -} - -.redesign\/message-preview\/semibold__0f084 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/semibold__0f084.fontScaling__0f084 { - font-size: .9375rem -} - -.redesign\/message-preview\/bold__0f084 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/bold__0f084.fontScaling__0f084 { - font-size: .9375rem -} - -.redesign\/channel-title\/normal__0f084 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.375 -} - -.redesign\/channel-title\/normal__0f084.fontScaling__0f084 { - font-size: 1rem -} - -.redesign\/channel-title\/medium__0f084 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.375 -} - -.redesign\/channel-title\/medium__0f084.fontScaling__0f084 { - font-size: 1rem -} - -.redesign\/channel-title\/semibold__0f084 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.375 -} - -.redesign\/channel-title\/semibold__0f084.fontScaling__0f084 { - font-size: 1rem -} - -.redesign\/channel-title\/bold__0f084 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.375 -} - -.redesign\/channel-title\/bold__0f084.fontScaling__0f084 { - font-size: 1rem -} - -.display-sm__0f084 { - font-family: var(--font-headline); - font-size: 20px; - font-weight: 800; - line-height: 1 -} - -.display-sm__0f084.fontScaling__0f084 { - font-size: 1.25rem -} - -.display-md__0f084 { - font-family: var(--font-headline); - font-size: 34px; - font-weight: 800; - line-height: 1.0588235294117647 -} - -.display-md__0f084.fontScaling__0f084 { - font-size: 2.125rem -} - -.display-lg__0f084 { - font-family: var(--font-headline); - font-size: 44px; - font-weight: 800; - line-height: .9545454545454546 -} - -.display-lg__0f084.fontScaling__0f084 { - font-size: 2.75rem -} - -.code__0f084 { - font-family: var(--font-code); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.code__0f084.fontScaling__0f084 { - font-size: .75rem -} - -.button__0f084 { - align-items: center; - background: initial; - border: 1px solid transparent; - border-radius: var(--radius-sm); - box-sizing: border-box; - color: inherit; - cursor: pointer; - display: flex; - flex-grow: 0; - flex-shrink: 0; - font-size: medium; - font-weight: 400; - justify-content: center; - margin: 0; - max-height: -moz-min-content; - max-height: min-content; - max-width: 100%; - padding: 0; - position: relative; - text-align: start; - transition: background-color 50ms ease-in,color 50ms ease-in,border-color 50ms ease-in,opacity 50ms ease-in; - width: -moz-min-content; - width: min-content -} - -.button__0f084:hover { - transition: background-color .15s ease-out,color .15s ease-out,border-color .15s ease-out,opacity .15s ease-out -} - -.button__0f084:disabled { - opacity: .5; - pointer-events: none -} - -.highlight-mana-buttons .button__0f084 { - box-shadow: 0 0 4px 4px var(--opacity-white-60) -} - -.highlight-mana-buttons [data-button-hoisted-classname-wrapper] .button__0f084 { - box-shadow: 0 0 4px 4px var(--opacity-green-60) -} - -[data-button-hoisted-classname-wrapper] { - display: flex; - width: auto -} - -.buttonChildrenWrapper__0f084 { - border-radius: var(--radius-sm); - box-sizing: border-box; - justify-content: center; - position: relative; - width: 100% -} - -.buttonChildren__0f084,.buttonChildrenWrapper__0f084 { - align-items: center; - display: flex; - overflow: hidden -} - -.buttonChildren__0f084 { - gap: var(--space-4); - text-overflow: ellipsis; - transition: opacity .2s ease-out,transform .2s ease-out; - white-space: nowrap -} - -.icon__0f084 { - flex-shrink: 0 -} - -.buttonChildren__0f084 { - opacity: 1 -} - -.buttonChildren__0f084.loading__0f084 { - opacity: 0 -} - -.spinnerWrapper__0f084 { - animation-duration: .2s; - animation-fill-mode: forwards; - animation-timing-function: ease; - height: 100%; - position: absolute; - top: 0; - width: 100% -} - -.spinnerWrapper__0f084.fadeIn__0f084 { - animation-name: spinner-opacity-in__0f084 -} - -.spinnerWrapper__0f084.fadeOut__0f084 { - animation-name: spinner-opacity-out__0f084 -} - -.full-motion .spinnerWrapper__0f084.fadeIn__0f084 { - animation-name: spinner-transform-in__0f084,spinner-opacity-in__0f084 -} - -.full-motion .spinnerWrapper__0f084.fadeOut__0f084 { - animation-name: spinner-transform-out__0f084,spinner-opacity-out__0f084 -} - -.full-motion .buttonChildren__0f084 { - transform: translateY(0) -} - -.full-motion .buttonChildren__0f084.loading__0f084 { - transform: translateY(-100%) -} - -@keyframes spinner-opacity-in__0f084 { - 0% { - opacity: 0 - } - - to { - opacity: 1 - } -} - -@keyframes spinner-opacity-out__0f084 { - 0% { - opacity: 1 - } - - to { - opacity: 0 - } -} - -@keyframes spinner-transform-in__0f084 { - 0% { - transform: translateY(100%) - } - - to { - transform: translateY(0) - } -} - -@keyframes spinner-transform-out__0f084 { - 0% { - transform: translateY(0) - } - - to { - transform: translateY(100%) - } -} - -.xs__0f084 { - border-radius: var(--radius-xs) -} - -.xs__0f084 .buttonChildrenWrapper__0f084 { - min-height: 22px; - min-width: 22px -} - -.xs__0f084.hasText__0f084 { - min-width: var(--__button-min-width,60px) -} - -.xs__0f084.hasText__0f084 .buttonChildrenWrapper__0f084 { - padding: calc(var(--space-4) - 1px) calc(var(--space-8) - 1px) -} - -.sm__0f084 .buttonChildrenWrapper__0f084 { - min-height: 30px; - min-width: 30px -} - -.sm__0f084.hasText__0f084 { - min-width: var(--__button-min-width,60px) -} - -.sm__0f084.hasText__0f084 .buttonChildrenWrapper__0f084 { - padding: calc(var(--space-4) - 1px) calc(var(--space-12) - 1px) -} - -.md__0f084 .buttonChildrenWrapper__0f084 { - min-height: 38px; - min-width: 38px -} - -.md__0f084.hasText__0f084 { - min-width: var(--__button-min-width,100px) -} - -.md__0f084.hasText__0f084 .buttonChildrenWrapper__0f084 { - padding: calc(var(--space-8) - 1px) calc(var(--space-16) - 1px) -} - -.spinnerItem__0f084 { - background-color: currentColor!important -} - -.spinner__0f084 { - height: 100% -} - -.spinner-sm__0f084,.spinner-xs__0f084 { - min-height: 16px; - min-width: 16px; - transform: scale(.75) -} - -.spinner-md__0f084 { - transform: scale(.9) -} - -.spinner-lg__0f084,.spinner-md__0f084 { - min-height: 20px; - min-width: 20px -} - -.primary__0f084 { - background-color: var(--control-primary-background-default); - border-color: var(--control-primary-border-default); - color: var(--control-primary-text-default) -} - -.primary__0f084:hover { - background-color: var(--control-primary-background-hover); - border-color: var(--control-primary-border-hover); - color: var(--control-primary-text-hover) -} - -.primary__0f084:active { - background-color: var(--control-primary-background-active); - border-color: var(--control-primary-border-active); - color: var(--control-primary-text-active) -} - -.secondary__0f084 { - background-color: var(--control-secondary-background-default); - border-color: var(--control-secondary-border-default); - color: var(--control-secondary-text-default) -} - -.secondary__0f084:hover { - background-color: var(--control-secondary-background-hover); - border-color: var(--control-secondary-border-hover); - color: var(--control-secondary-text-hover) -} - -.secondary__0f084:active { - background-color: var(--control-secondary-background-active); - border-color: var(--control-secondary-border-active); - color: var(--control-secondary-text-active) -} - -.icon-only__0f084 { - background-color: transparent; - border-color: transparent; - color: var(--control-icon-only-icon-default) -} - -.icon-only__0f084:hover { - background-color: var(--control-icon-only-background-hover); - border-color: var(--control-icon-only-border-hover); - color: var(--control-icon-only-icon-hover) -} - -.icon-only__0f084:active { - background-color: var(--control-icon-only-background-active); - border-color: var(--control-icon-only-border-active); - color: var(--control-icon-only-icon-active) -} - -.color-mix__0f084 { - background-color: transparent; - border: transparent; - color: var(--icon-strong) -} - -.color-mix__0f084:hover { - color: var(--control-icon-only-icon-hover) -} - -.color-mix__0f084:active { - color: var(--control-icon-only-icon-active) -} - -.color-mix__0f084:before { - border: 1px solid transparent; - border-radius: var(--radius-sm); - box-sizing: border-box; - content: ""; - height: 100%; - left: 0; - mix-blend-mode: plus-lighter; - position: absolute; - top: 0; - transition: background-color 50ms ease-in,border-color 50ms ease-in,opacity 50ms ease-in; - width: 100% -} - -.color-mix__0f084:hover:before { - background-color: var(--control-icon-only-background-hover); - border-color: var(--border-subtle); - transition: background-color .15s ease-out,border-color .15s ease-out,opacity .15s ease-out -} - -.color-mix__0f084:active:before { - background-color: var(--control-icon-only-background-active); - border-color: var(--border-subtle) -} - -.input-accessory__0f084 { - border-radius: var(--radius-xs); - color: var(--icon-strong) -} - -.theme-light .color-mix__0f084:before { - mix-blend-mode: difference -} - -.critical-primary__0f084 { - background-color: var(--control-critical-primary-background-default); - border-color: var(--control-critical-primary-border-default); - color: var(--control-critical-primary-text-default) -} - -.critical-primary__0f084:hover { - background-color: var(--control-critical-primary-background-hover); - border-color: var(--control-critical-primary-border-hover); - color: var(--control-critical-primary-text-hover) -} - -.critical-primary__0f084:active { - background-color: var(--control-critical-primary-background-active); - border-color: var(--control-critical-primary-border-active); - color: var(--control-critical-primary-text-active) -} - -.critical-secondary__0f084 { - background-color: var(--control-critical-secondary-background-default); - border-color: var(--control-critical-secondary-border-default); - color: var(--control-critical-secondary-text-default) -} - -.critical-secondary__0f084:hover { - background-color: var(--control-critical-secondary-background-hover); - border-color: var(--control-critical-secondary-border-hover); - color: var(--control-critical-secondary-text-hover) -} - -.critical-secondary__0f084:active { - background-color: var(--control-critical-secondary-background-active); - border-color: var(--control-critical-secondary-border-active); - color: var(--control-critical-secondary-text-active) -} - -.active__0f084 { - background-color: var(--control-connected-background-default); - border-color: var(--control-connected-border-default); - color: var(--control-connected-text-default) -} - -.active__0f084:hover { - background-color: var(--control-connected-background-hover); - border-color: var(--control-connected-border-hover); - color: var(--control-connected-text-hover) -} - -.active__0f084:active { - background-color: var(--control-connected-background-active); - border-color: var(--control-connected-border-active); - color: var(--control-connected-text-active) -} - -.overlay-primary__0f084 { - background-color: var(--control-overlay-primary-background-default); - border-color: var(--control-overlay-primary-border-default); - color: var(--control-overlay-primary-text-default) -} - -.overlay-primary__0f084:hover { - background-color: var(--control-overlay-primary-background-hover); - border-color: var(--control-overlay-primary-border-hover); - color: var(--control-overlay-primary-text-hover) -} - -.overlay-primary__0f084:active { - background-color: var(--control-overlay-primary-background-active); - border-color: var(--control-overlay-primary-border-active); - color: var(--control-overlay-primary-text-active) -} - -.overlay-secondary__0f084 { - background-color: var(--control-overlay-secondary-background-default); - border-color: var(--control-overlay-secondary-border-default); - color: var(--control-overlay-secondary-text-default) -} - -.overlay-secondary__0f084:hover { - background-color: var(--control-overlay-secondary-background-hover); - border-color: var(--control-overlay-secondary-border-hover); - color: var(--control-overlay-secondary-text-hover) -} - -.overlay-secondary__0f084:active { - background-color: var(--control-overlay-secondary-background-active); - border-color: var(--control-overlay-secondary-border-active); - color: var(--control-overlay-secondary-text-active) -} - -.expressive__0f084>* { - pointer-events: none; - z-index: 1 -} - -.expressiveRive__0f084 { - height: calc(100% + var(--__glow-amount)*2 + 2px); - left: calc(var(--__glow-amount)*-1 - 1px); - position: absolute; - top: calc(var(--__glow-amount)*-1 - 1px); - width: calc(100% + var(--__glow-amount)*2 + 2px) -} - -.expressive__0f084 .expressiveBackground__0f084 { - filter: blur(10px) saturate(var(--saturation-factor,1)) -} - -.reduce-motion .expressive__0f084 .expressiveBackground__0f084 { - filter: blur(7px) saturate(var(--saturation-factor,1)) -} - -.expressive__0f084 { - color: var(--control-expressive-text-default) -} - -.expressive__0f084 .expressiveFill__0f084 { - --__glow-amount: 0px; - background-color: var(--control-expressive-background-default); - border-radius: 8px; - transition: background-color .15s ease,width .15s ease,height .15s ease,top .15s ease,left .15s ease -} - -.expressive__0f084:hover { - color: var(--control-expressive-text-hover) -} - -.expressive__0f084:hover .expressiveFill__0f084 { - background-color: var(--control-expressive-background-hover) -} - -.expressive__0f084:active { - color: var(--control-expressive-text-active) -} - -.expressive__0f084:active .expressiveFill__0f084 { - --__glow-amount: -1px; - background-color: var(--control-expressive-background-active) -} - -.expressive__0f084 .expressiveHoverContainer__0f084 { - filter: blur(8px); - mix-blend-mode: plus-lighter; - pointer-events: all -} - -.expressiveWrapper__0f084 { - --__glow-amount: 8px; - display: flex; - max-width: 100%; - position: relative; - width: -moz-min-content; - width: min-content -} - -.fullWidth__0f084.hasText__0f084 { - flex: 1; - width: 100% -} - -.rounded__0f084 { - border-radius: var(--radius-round) -} - -.enable-forced-colors .button__0f084 { - background-color: ButtonFace; - border-color: ButtonText; - color: ButtonText; - forced-color-adjust: none -} - -.enable-forced-colors .button__0f084:disabled { - background-color: Canvas; - border-color: GrayText; - color: GrayText; - opacity: 1 -} - -.enable-forced-colors .button__0f084 .expressiveFill__0f084 { - display: none -} - -.container__0f084 { - --custom-input-padding: 10px; - --custom-text-input-icon-spacing: var(--space-4); - min-height: var(--custom-text-input-height) -} - -.container__0f084 .icon__0f084 { - height: calc(var(--custom-text-input-height) - var(--custom-text-input-icon-spacing)*2 - 2px) -} - -.icon__0f084 { - align-items: center; - aspect-ratio: 1/1; - border: 1px solid transparent; - border-radius: var(--radius-xs); - box-sizing: border-box; - display: flex; - justify-content: center -} - -.image__0f084 { - height: 100%; - padding: 0 var(--space-4) -} - -.iconButton__0f084 { - color: var(--icon-strong) -} - -.clearButton__0f084 { - color: var(--icon-subtle); - cursor: pointer -} - -.clearButton__0f084:hover { - color: var(--icon-strong) -} - -.leadingText__0f084 { - padding-inline-start:var(--space-4)} - -.sm__0f084 { - --custom-text-input-height: var(--control-item-height-sm) -} - -.md__0f084 { - --custom-text-input-height: var(--control-input-height-md) -} - -.hasLeading__0f084 { - padding-inline-start:var(--custom-text-input-icon-spacing)} - -.hasLeading__0f084 .input__0f084 { - padding-inline-start: 0 -} - -.hasTrailing__0f084 { - padding-inline-end:var(--custom-text-input-icon-spacing)} - -.hasTrailing__0f084 .input__0f084 { - padding-inline-end: 0 -} - -.input__0f084 { - background-color: transparent; - border: none; - box-sizing: border-box; - color: inherit; - flex: 1; - font-size: inherit; - height: 100%; - min-width: 30%; - padding-inline:var(--space-12)} - -.input__0f084: disabled { - cursor:not-allowed; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.input__0f084::-moz-placeholder { - color: var(--input-placeholder-text-default); - -moz-user-select: none; - user-select: none -} - -.input__0f084::placeholder { - color: var(--input-placeholder-text-default); - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.hasTags__0f084 { - padding: var(--space-4) -} - -.hasTags__0f084 .input__0f084 { - height: calc(var(--custom-text-input-height) - 8px); - padding-inline-start:var(--space-8)} - -.calendar__2ffbd { - box-sizing: border-box; - color: var(--text-default); - max-width: 248px; - padding: var(--space-16); - width: 100% -} - -.calendarHeader__2ffbd { - align-items: center; - display: flex; - gap: var(--space-8); - justify-content: space-between; - margin-bottom: var(--space-12) -} - -.selectField__2ffbd { - height: 24px -} - -.select__2ffbd { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: transparent; - border: none -} - -.monthSelect__2ffbd:hover:not(:disabled),.yearSelect__2ffbd:hover:not(:disabled) { - border-color: var(--input-border-hover) -} - -.monthSelect__2ffbd:focus,.yearSelect__2ffbd:focus { - border-color: var(--input-border-focus); - box-shadow: 0 0 0 1px var(--input-border-focus); - outline: none -} - -.monthSelect__2ffbd:disabled,.yearSelect__2ffbd:disabled { - cursor: not-allowed; - opacity: .5 -} - -.calendarGrid__2ffbd { - border-collapse: collapse; - width: 100% -} - -.cell__2ffbd,.headerCell__2ffbd { - aspect-ratio: 1/1; - height: 32px -} - -.cell__2ffbd { - align-items: center; - background: transparent; - border-radius: var(--radius-sm); - cursor: pointer; - display: flex; - justify-content: center; - text-align: center; - transition: all .15s ease -} - -.cell__2ffbd[data-selected] { - background-color: var(--control-primary-background-default); - color: var(--control-primary-text-default); - outline: 1px solid var(--control-primary-border-default); - outline-offset: -1px -} - -.cell__2ffbd[data-disabled] { - color: var(--text-disabled); - cursor: not-allowed; - opacity: .4 -} - -.cell__2ffbd[data-focus-visible] { - box-shadow: inset 0 0 0 2px var(--border-focus); - outline: none -} - -.cell__2ffbd[data-outside-month] { - color: var(--text-subtle); - opacity: .4 -} - -.cell__2ffbd:hover:not([data-disabled]):not([data-selected]) { - background-color: var(--background-mod-subtle) -} - -.enable-forced-colors .calendar__2ffbd { - background-color: Canvas; - border-color: ButtonBorder -} - -.enable-forced-colors .cell__2ffbd { - color: CanvasText -} - -.enable-forced-colors .cell__2ffbd[data-selected] { - background-color: Highlight; - color: HighlightText -} - -.enable-forced-colors .cell__2ffbd[data-disabled] { - color: GrayText -} - -.enable-forced-colors .cell__2ffbd[data-focus-visible] { - box-shadow: inset 0 0 0 2px Highlight; - outline: none -} - -.enable-forced-colors .cell__2ffbd[data-outside-month] { - color: GrayText; - opacity: .4 -} - -.enable-forced-colors .cell__2ffbd:hover:not(.cellDisabled__2ffbd):not(.selected__2ffbd) { - background-color: ButtonFace -} - -.inputField_a14f87 { - align-items: center; - display: flex; - height: var(--control-input-height-md); - justify-content: space-between; - max-width: 180px; - padding-left: var(--space-8); - padding-right: var(--space-4) -} - -.datePicker_a14f87 { - height: 100%; - width: 100% -} - -.placeholder_a14f87 { - color: var(--text-subtle) -} - -.inputGroup_a14f87 { - align-items: center; - box-sizing: border-box; - display: flex; - height: 100%; - justify-content: space-between; - padding-bottom: var(--space-4); - padding-top: var(--space-4); - width: 100% -} - -.segment_a14f87 { - border-radius: var(--radius-xs); - padding: 0 2px -} - -.segment_a14f87:focus { - background-color: var(--opacity-blurple-8); - color: var(--text-strong); - outline: 1px solid var(--border-focus) -} - -.calendarButton_a14f87 { - align-items: center; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - aspect-ratio: 1/1; - background: transparent; - border-radius: var(--radius-xs); - color: var(--control-icon-only-icon-default); - display: flex; - height: 100%; - justify-content: center; - margin: 0; - padding: 0; - transition: all .15s ease -} - -.calendarButton_a14f87[data-hovered] { - background-color: var(--control-icon-only-background-hover); - border-color: var(--control-icon-only-border-hover); - color: var(--control-icon-only-icon-hover) -} - -.calendarButton_a14f87[data-pressed] { - background-color: var(--control-icon-only-background-active); - border-color: var(--control-icon-only-border-active); - color: var(--control-icon-only-icon-active) -} - -.calendarButton_a14f87[data-focus-visible] { - outline: 2px solid var(--border-focus); - outline-offset: -1px -} - -.dialog_a14f87,.popover_a14f87 { - z-index: 10 -} - -.dialog_a14f87 { - background-color: var(--background-surface-higher); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-shadow: 0 2px 8px 0 var(--opacity-black-16); - box-sizing: border-box; - max-height: 100%; - overflow: auto!important -} - -.enable-forced-colors .inputGroup_a14f87 { - background-color: Field; - border-color: ButtonBorder -} - -.enable-forced-colors .inputGroup_a14f87.error_a14f87 { - border-color: Mark -} - -.enable-forced-colors .segment_a14f87 { - color: FieldText -} - -.enable-forced-colors .segment_a14f87:focus { - background-color: Highlight; - color: HighlightText -} - -.enable-forced-colors .calendarButton_a14f87 { - color: ButtonText -} - -.enable-forced-colors .calendarButton_a14f87:hover { - background-color: ButtonFace -} - -.placeholder__84ae7 { - align-items: center; - background-color: var(--background-mod-subtle); - border: 1px dashed var(--border-subtle); - border-radius: var(--radius-md); - display: flex; - justify-content: center; - padding: var(--space-8) var(--space-12) -} - -.outerContainer_e44912 { - align-items: center; - container: outer-container/inline-size; - display: flex; - height: 100dvh; - justify-content: center; - width: calc(100vw - var(--devtools-sidebar-width, 0px)) -} - -.modal_e44912 { - --custom-viewport-padding: calc(var(--space-40) + var(--custom-app-top-bar-height)); - background: var(--background-base-lower); - box-sizing: border-box; - container: modal-container/inline-size; - height: calc(100% - (var(--custom-viewport-padding))*2); - max-width: 1400px; - overflow: hidden; - width: calc(100% - (var(--custom-viewport-padding))*2) -} - -@media (max-width: 1600px) { - .modal_e44912 { - --custom-viewport-padding:var(--space-40) - } -} - -@container outer-container (max-width: 1600px) { - .modal_e44912 { - --custom-viewport-padding: var(--space-40) - } -} - -@container outer-container (max-width: 1080px) { - .modal_e44912 { - border: none; - border-radius: 0; - height: 100%; - overflow-y: auto; - padding-top: var(--custom-app-top-bar-height); - width: 100% - } - - .is-mobile .modal_e44912 { - padding-top: 0 - } - - .modalContent_e44912 { - border-top: 1px solid var(--border-muted) - } -} - -.modalContent_e44912 { - box-sizing: border-box; - height: 100%; - width: 100% -} - -.modalContentInner_e44912 { - box-sizing: inherit; - height: inherit; - width: inherit -} - -.popover_d6f39b { - box-sizing: border-box; - display: flex; - flex-direction: column; - gap: var(--space-16); - max-width: var(--custom-popover-width); - padding: var(--space-16); - position: relative; - text-align: center; - width: var(--custom-popover-width); - --custom-popover-caret-gradient-color-start: transparent; - --custom-popover-caret-gradient-color-end: transparent -} - -.popover_d6f39b,.popoverGradientWrapper_d6f39b { - background: var(--background-surface-high); - border-radius: var(--radius-md); - box-shadow: inset 0 0 0 1px var(--border-subtle),var(--shadow-high) -} - -.popoverGradientWrapper_d6f39b { - --custom-popover-caret-gradient-color-start: var(--custom-gradient-color-start); - --custom-popover-caret-gradient-color-end: var(--custom-gradient-color-end) -} - -.popoverContentWithGradient_d6f39b { - background: transparent; - border-radius: var(--radius-md); - box-shadow: none; - box-sizing: border-box; - display: flex; - flex-direction: column; - gap: var(--space-16); - max-width: var(--custom-popover-width); - padding: var(--space-16); - position: relative; - text-align: center; - width: var(--custom-popover-width) -} - -.popover--video_d6f39b,.popoverContentWithGradient_d6f39b.popover--video_d6f39b { - padding: var(--space-4) -} - -.popover--video_d6f39b .footer_d6f39b,.popover--video_d6f39b .header_d6f39b { - padding: 0 var(--space-16) -} - -.popover--video_d6f39b .actionBar_d6f39b { - padding: 0 var(--space-16) var(--space-16) -} - -.closeButton_d6f39b { - inset-inline-end: var(--space-4); - position: absolute; - top: var(--space-4); - z-index: 2 -} - -.header_d6f39b { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-4); - margin: var(--space-20) 0; - position: relative; - z-index: 1 -} - -.headerWithBadge_d6f39b { -} - -.title_d6f39b { - margin: 0; - text-align: center -} - -.headerBody_d6f39b { - color: var(--text-subtle); - display: flex; - flex-direction: column; - gap: 2px; - margin: 0 -} - -.badgeContainer_d6f39b { - margin-bottom: var(--space-12) -} - -.content_d6f39b { - align-items: center; - display: flex; - flex-direction: column; - position: relative; - z-index: 1 -} - -.graphic_d6f39b { - margin: var(--space-16) auto var(--space-20); - width: 100% -} - -.graphic--md_d6f39b,.graphic--sm_d6f39b { - max-width: 144px -} - -.graphic--lg_d6f39b { - max-width: 160px -} - -.graphic--video_d6f39b { - max-width: 232px -} - -.footer_d6f39b { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-4); - text-align: center -} - -.footerText_d6f39b { - color: var(--text-muted); - margin: 0 -} - -.footerLink_d6f39b { - color: var(--text-link); - margin: 0 -} - -.actionBar_d6f39b { - align-items: stretch; - display: flex; - flex-direction: column; - gap: var(--space-8); - justify-content: center -} - -.actionBar_d6f39b .actions_d6f39b { - flex: 1; - min-width: unset -} - -.actions_d6f39b { - display: flex; - flex-direction: column; - gap: var(--space-8) -} - -.multistepIndicator_d6f39b { - color: var(--text-muted); - margin-inline-start:var(--space-4);margin-top: var(--space-4); - opacity: .7 -} - -.multistepActionLayout_d6f39b { - align-items: center; - display: flex; - justify-content: space-between; - width: 100% -} - -.caretIcon_d6f39b .caretFill_d6f39b { - fill: var(--background-surface-high) -} - -.caretIcon_d6f39b .caretGradient_d6f39b { - fill: var(--custom-popover-caret-gradient-color,transparent) -} - -.caretIcon_d6f39b .caretStroke_d6f39b { - stroke: var(--border-subtle); - stroke-opacity: 1 -} - -.caret__6ec79 { - --custom-caret-half-width: 11px; - --custom-caret-half-height: 7px; - --custom-caret-horizontal-distance: 18px; - --custom-caret-border-overlap: 1px; - color: transparent; - pointer-events: none; - position: absolute; - transform-origin: center -} - -.caret--bottom__6ec79 { - top: calc(100% - var(--custom-caret-border-overlap)); - transform: rotate(0deg) -} - -.caret--top__6ec79 { - bottom: calc(100% - var(--custom-caret-border-overlap)); - transform: rotate(180deg) -} - -.caret--left__6ec79 { - --custom-popover-caret-gradient-color: color-mix(in oklab,var(--custom-popover-caret-gradient-color-start) 25%,transparent); - inset-inline-start: calc(var(--custom-caret-horizontal-distance)*-1); - transform: rotate(90deg) -} - -.caret--right__6ec79 { - inset-inline-end: calc(var(--custom-caret-horizontal-distance)*-1); - transform: rotate(-90deg) -} - -.caret--bottom__6ec79.caret--center__6ec79,.caret--top__6ec79.caret--center__6ec79 { - inset-inline-start: calc(50% + var(--custom-caret-edge-offset-horizontal-nudge, 0)); - margin-inline-start:calc(var(--custom-caret-half-width)*-1)} - -.caret--bottom__6ec79.caret--start__6ec79,.caret--top__6ec79.caret--start__6ec79 { - inset-inline-start: calc(var(--custom-caret-edge-offset-horizontal) + var(--custom-caret-edge-offset-horizontal-nudge, 0)); - margin-inline-start:calc(var(--custom-caret-half-width)*-1)} - -.caret--bottom__6ec79.caret--end__6ec79,.caret--top__6ec79.caret--end__6ec79 { - inset-inline-end: calc(var(--custom-caret-edge-offset-horizontal) - var(--custom-caret-edge-offset-horizontal-nudge, 0)); - margin-inline-end:calc(var(--custom-caret-half-width)*-1)} - -.caret--left__6ec79.caret--center__6ec79,.caret--right__6ec79.caret--center__6ec79 { - margin-top: calc(var(--custom-caret-half-height)*-1); - top: 50% -} - -.caret--left__6ec79.caret--center__6ec79 { - inset-inline-start: calc(var(--custom-caret-horizontal-distance)*-1) -} - -.caret--left__6ec79.caret--start__6ec79,.caret--right__6ec79.caret--start__6ec79 { - margin-top: calc(var(--custom-caret-half-height)*-1); - top: var(--custom-caret-edge-offset-vertical) -} - -.caret--left__6ec79.caret--end__6ec79,.caret--right__6ec79.caret--end__6ec79 { - bottom: var(--custom-caret-edge-offset-vertical); - margin-bottom: calc(var(--custom-caret-half-height)*-1) -} - -.caret--custom__6ec79 { - --custom-caret-offset-x: 0; - --custom-caret-offset-y: 0 -} - -.caret--bottom__6ec79.caret--custom__6ec79,.caret--top__6ec79.caret--custom__6ec79 { - inset-inline-start: 50%; - margin-inline-start:calc(var(--custom-caret-offset-x) - var(--custom-caret-half-width))} - -.caret--left__6ec79.caret--custom__6ec79,.caret--right__6ec79.caret--custom__6ec79 { - margin-top: calc(var(--custom-caret-offset-y) - var(--custom-caret-half-height)); - top: 50% -} - -.caret--top__6ec79.caret--start__6ec79 { - --custom-popover-caret-gradient-color: color-mix(in oklab,var(--custom-popover-caret-gradient-color-end) 7%,var(--custom-popover-caret-gradient-color-start)) -} - -.caret--top__6ec79.caret--center__6ec79 { - --custom-popover-caret-gradient-color: color-mix(in oklab,var(--custom-popover-caret-gradient-color-end) 50%,var(--custom-popover-caret-gradient-color-start)) -} - -.caret--top__6ec79.caret--end__6ec79 { - --custom-popover-caret-gradient-color: color-mix(in oklab,var(--custom-popover-caret-gradient-color-end) 98%,var(--custom-popover-caret-gradient-color-start)) -} - -.caret--right__6ec79.caret--start__6ec79 { - --custom-popover-caret-gradient-color: color-mix(in oklab,var(--custom-popover-caret-gradient-color-end) 98%,transparent) -} - -.caret--right__6ec79.caret--center__6ec79 { - --custom-popover-caret-gradient-color: color-mix(in oklab,var(--custom-popover-caret-gradient-color-end) 25%,transparent) -} - -.caret--left__6ec79.caret--start__6ec79 { - --custom-popover-caret-gradient-color: color-mix(in oklab,var(--custom-popover-caret-gradient-color-start) 98%,transparent) -} - -.caret--left__6ec79.caret--center__6ec79 { - --custom-popover-caret-gradient-color: color-mix(in oklab,var(--custom-popover-caret-gradient-color-start) 25%,transparent) -} - -.media__9c640 { - border-radius: var(--radius-md) -} - -.assetContainer__9c640 { - border-radius: var(--radius-sm); - display: inline-block; - overflow: hidden; - position: relative; - width: 100% -} - -.playButton__9c640 { - inset-inline-start: 50%; - opacity: 0; - pointer-events: none; - position: absolute; - top: 50%; - transform: translate(-50%,-50%); - transition: opacity .2s ease-in-out; - z-index: 2 -} - -.assetContainer__9c640:focus-within .playButton__9c640,.assetContainer__9c640:hover .playButton__9c640 { - opacity: 1; - pointer-events: auto -} - -.closeButton__9c640 { - inset-inline-end: calc(var(--space-8) - 1px); - position: absolute; - top: calc(var(--space-8) - 1px); - z-index: 2 -} - -.variants_f72374 { - display: grid; - gap: var(--space-8); - grid-template-columns: repeat(auto-fill,minmax(200px,1fr)) -} - -.card_f72374 { - display: flex; - flex-direction: column; - justify-content: space-between; - overflow: hidden; - padding: var(--space-12) -} - -.layer__95d7b { - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-md); - box-shadow: var(--shadow-high); - padding: var(--space-16) -} - -.container__28842 { - overflow: hidden; - position: relative; - width: 100%; - --custom-edge-fade-width: 0 -} - -.children__28842 { - align-items: stretch; - flex-shrink: auto; - -webkit-mask-image: none; - mask-image: none; - overflow-x: scroll; - scrollbar-width: none; - transition: -webkit-mask-image .3s linear; - transition: mask-image .3s linear; - transition: mask-image .3s linear,-webkit-mask-image .3s linear -} - -.children__28842.scrollRight__28842 { - -webkit-mask-image: linear-gradient(90deg,#000,#000 calc(100% - var(--custom-edge-fade-width)),transparent); - mask-image: linear-gradient(90deg,#000,#000 calc(100% - var(--custom-edge-fade-width)),transparent) -} - -.children__28842.scrollLeft__28842 { - -webkit-mask-image: linear-gradient(90deg,transparent,#000 var(--custom-edge-fade-width)); - mask-image: linear-gradient(90deg,transparent,#000 var(--custom-edge-fade-width)) -} - -.children__28842.scrollBoth__28842 { - -webkit-mask-image: linear-gradient(90deg,transparent,#000 var(--custom-edge-fade-width),#000 calc(100% - var(--custom-edge-fade-width)),transparent); - mask-image: linear-gradient(90deg,transparent,#000 var(--custom-edge-fade-width),#000 calc(100% - var(--custom-edge-fade-width)),transparent) -} - -.children__28842.noScroll__28842 { - -webkit-mask-image: none; - mask-image: none -} - -.children__28842>* { - flex-shrink: 0 -} - -.actions__28842 { - align-items: center; - display: flex; - flex-direction: row; - inset: 0; - justify-content: space-between; - padding: var(--space-8); - pointer-events: none; - position: absolute; - z-index: 1 -} - -.actions__28842>* { - pointer-events: auto -} - -.button__28842 { - opacity: 1; - transition: opacity .1s linear -} - -.disabled__28842 { - opacity: 0; - pointer-events: none -} - -.hero__50e07 { - --custom-hero-min-height: 150px; - background-position: top; - background-repeat: no-repeat; - background-size: 100% auto; - border-radius: var(--radius-md); - box-sizing: border-box; - display: flex; - flex-direction: column; - height: 100%; - min-height: var(--custom-hero-min-height); - padding: var(--space-8); - position: relative; - width: 100% -} - -.logo__50e07 { - align-self: flex-start; - display: block; - height: auto; - justify-self: center; - max-height: calc(var(--custom-hero-min-height) - var(--space-8)*2); - -o-object-fit: contain; - object-fit: contain; - width: auto -} - -.children__50e07 { - padding-top: var(--space-24) -} - -.card__896d4 { - border-radius: var(--radius-lg); - flex-grow: 1; - overflow: hidden; - transition: filter .15s ease-in-out -} - -.card__896d4:hover { - filter: drop-shadow(0 8px 10px rgba(0,0,0,.14)) -} - -.overflowable__896d4 { - overflow: visible -} - -.grid__1b670 { - container-type: inline-size; - display: grid; - gap: 8px; - grid-auto-flow: dense; - width: 100% -} - -.columns1__1b670 { - grid-template-columns: repeat(1,1fr) -} - -.columns2__1b670 { - grid-template-columns: repeat(2,1fr) -} - -.columns3__1b670 { - grid-template-columns: repeat(3,1fr) -} - -.columns4__1b670 { - grid-template-columns: repeat(4,1fr) -} - -.columns5__1b670 { - grid-template-columns: repeat(5,1fr) -} - -.columns6__1b670 { - grid-template-columns: repeat(6,1fr) -} - -@container (max-width: 768px) { - .columns3__1b670,.columns4__1b670,.columns5__1b670,.columns6__1b670 { - grid-template-columns: repeat(2,1fr) - } -} - -@container (max-width: 480px) { - .columns2__1b670,.columns3__1b670,.columns4__1b670,.columns5__1b670,.columns6__1b670 { - grid-template-columns: repeat(1,1fr) - } -} - -.gridItem__1b670 { - display: block -} - -.gridItemColumns1__1b670 { - grid-column: span 1 -} - -.gridItemColumns2__1b670 { - grid-column: span 2 -} - -.gridItemColumns3__1b670 { - grid-column: span 3 -} - -.gridItemColumns4__1b670 { - grid-column: span 4 -} - -.gridItemColumns5__1b670 { - grid-column: span 5 -} - -.gridItemColumns6__1b670 { - grid-column: span 6 -} - -.gridItemRows1__1b670 { - grid-row: span 1 -} - -.gridItemRows2__1b670 { - grid-row: span 2 -} - -.gridItemRows3__1b670 { - grid-row: span 3 -} - -.gridItemRows4__1b670 { - grid-row: span 4 -} - -.gridItemRows5__1b670 { - grid-row: span 5 -} - -.gridItemRows6__1b670 { - grid-row: span 6 -} - -@container (max-width: 768px) { - .gridItemColumns3__1b670,.gridItemColumns4__1b670,.gridItemColumns5__1b670,.gridItemColumns6__1b670 { - grid-column: span 2 - } - - .gridItemRows3__1b670,.gridItemRows4__1b670,.gridItemRows5__1b670,.gridItemRows6__1b670 { - grid-row: span 2 - } -} - -@container (max-width: 480px) { - .gridItemColumns2__1b670,.gridItemColumns3__1b670,.gridItemColumns4__1b670,.gridItemColumns5__1b670,.gridItemColumns6__1b670 { - grid-column: span 1 - } - - .gridItemRows2__1b670,.gridItemRows3__1b670,.gridItemRows4__1b670,.gridItemRows5__1b670,.gridItemRows6__1b670 { - grid-row: span 1 - } -} - -.container_fec5bf { - display: flex; - gap: 8px -} - -.verticalContainer_fec5bf { - align-items: flex-start; - display: flex; - flex-direction: column -} - -.horizontalContainer_fec5bf { - align-items: center; - display: flex; - flex-direction: row -} - -.section_fec5bf { - background-color: var(--background-base-low); - border: 1px solid var(--border-subtle); - border-radius: 4px; - display: grid; - gap: 8px; - grid-template-columns: 1fr; - margin: 16px 0; - padding: 16px -} - -.sectionDivider_fec5bf { - background-color: var(--border-subtle); - border: 0; - height: 1px; - width: 100% -} - -.labelSpacing_fec5bf { - margin-bottom: 8px; - margin-top: 8px -} - -.inputGroup_fec5bf { - gap: 8px -} - -.fieldset_fec5bf,.inputGroup_fec5bf { - display: flex; - flex-direction: column -} - -.fieldset_fec5bf { - border: none; - gap: 16px; - padding: 0 -} - -.labelSpacing__5f877 { - margin-bottom: 8px; - margin-top: 8px -} - -.applyChangesButton__5f877 { - margin-bottom: 32px; - margin-top: 8px -} - -.markdown__5f877 { - display: block; - line-height: 20px; - margin-bottom: 16px -} - -.textarea__5f877 { - height: 180px; - width: 100% -} - -@media (max-width: 485px) and (max-height:450px) { - .form__5f877 { - display:block - } -} - -.modal__5f877 { - box-shadow: var(--legacy-elevation-border),var(--legacy-elevation-high); - margin: 0; - min-height: 180px; - overflow-x: hidden; - width: 440px -} - -.scrollerContent__5f877 { - padding: 0 16px 16px -} - -.choosePaymentTypeModal__5f877 { - align-items: center; - display: flex; - flex-direction: column; - width: 442px -} - -.choosePaymentTypeContainer__5f877 { - padding-bottom: 16px; - padding-top: 16px; - width: 408px -} - -.focusLock__49fc1 { - max-width: 100%; - min-height: 0 -} - -.focusLock__49fc1,.root__49fc1 { - display: flex; - flex-direction: column -} - -.root__49fc1 { - background-color: var(--modal-background); - border: 1px solid var(--border-normal); - border-radius: var(--radius-md); - margin: 0 auto; - max-height: 100%; - pointer-events: all; - position: relative -} - -.root__49fc1.root__49fc1 { - border-radius: var(--radius-md) -} - -.small__49fc1 { - max-height: 720px; - min-height: 220px; - width: var(--modal-width-small) -} - -.medium__49fc1 { - max-height: 800px; - width: var(--modal-width-medium) -} - -.large__49fc1,.medium__49fc1 { - min-height: 400px -} - -.large__49fc1 { - max-width: 962px; - min-width: var(--modal-width-large) -} - -@media screen and (max-height: 550px),screen and (max-width:485px) { - .root__49fc1 { - max-width:100%; - min-width: auto; - width: 100% - } - - .fullscreenOnMobile__49fc1 { - border: none!important; - border-radius: 0!important; - inset: 0; - max-height: none; - overflow-y: auto; - position: absolute; - width: 100% - } - - .fullscreenOnMobile__49fc1 .footer__49fc1,.fullscreenOnMobile__49fc1 .header__49fc1 { - border-radius: 0 - } -} - -.footer__49fc1,.header__49fc1 { - flex: 0 0 auto; - overflow-x: hidden; - padding: var(--modal-vertical-padding) var(--modal-horizontal-padding); - position: relative; - z-index: 1 -} - -.header__49fc1 { - border-radius: var(--radius-md) var(--radius-md) 0 0; - transition: box-shadow .1s ease-out; - word-wrap: break-word; - overflow: hidden; - padding-bottom: 0 -} - -.footer__49fc1 { - background-color: var(--modal-footer-background); - border-radius: 0 0 var(--radius-md) var(--radius-md); - padding-top: var(--space-16) -} - -.content__49fc1 { - overflow-x: hidden; - padding-inline:var(--modal-horizontal-padding);padding-top: var(--space-8); - position: relative -} - -.close__49fc1 { - border-radius: 3px; - box-sizing: content-box; - color: var(--icon-strong); - cursor: pointer; - height: 24px; - opacity: .5; - padding: 4px; - transition: opacity .2s ease-in-out -} - -.close__49fc1:hover { - color: var(--interactive-text-hover); - opacity: 1 -} - -.closeWithCircleBackground__49fc1 { - align-items: center; - background-color: var(--opacity-black-84); - border-radius: 50%; - display: flex; - height: 24px; - justify-content: center; - padding: 0; - width: 24px -} - -.closeWithCircleBackground__49fc1 .closeIcon__49fc1 { - color: var(--primary-330); - height: 16px; - width: 16px -} - -.closeWithCircleBackground__49fc1 .closeIcon__49fc1:hover { - color: var(--primary-230) -} - -.closeWithCircleBackground__49fc1:hover { - background-color: var(--black) -} - -@media screen and (max-height: 550px),screen and (max-width:485px) { - .hideOnFullscreen__49fc1 { - display:none - } - - .footer__49fc1 { - bottom: 0; - box-sizing: border-box; - position: static; - width: 100% - } -} - -.spinnerContainer__49fc1 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.footerSeparator__49fc1,.separator__49fc1 { - box-shadow: none -} - -.enable-forced-colors .root__49fc1 { - border: 2px solid CanvasText -} - -.enable-forced-colors .close__49fc1 { - opacity: 1 -} - -.rootWithShadow__49fc1 { - box-shadow: var(--shadow-medium) -} - -.child__8fdf6 { - align-items: center; - background: pink; - border-radius: 8px; - color: #000; - cursor: pointer; - display: flex; - font-size: 24px; - height: 100px; - justify-content: center; - padding: 12px; - width: 100px -} - -.chip__3bb21 { - align-items: flex-start; - background-position: 0; - background-repeat: no-repeat; - background-size: cover; - cursor: pointer; - display: flex; - flex-direction: column; - height: 48px; - justify-content: center; - padding: 16px -} - -.collection__38b7f { - container-type: inline-size; - flex-grow: 1; - padding-top: 12px; - position: relative -} - -.body__38b7f { - background-position: 0; - background-repeat: no-repeat; - background-size: cover; - border-radius: inherit; - cursor: pointer; - height: 224px; - padding: 24px -} - -.logo__38b7f { - height: auto; - max-width: 120px -} - -.supplemental__38b7f { - bottom: 0; - height: 100%; - inset-inline-end: 0; - opacity: 0; - pointer-events: none; - position: absolute; - top: 0 -} - -.left__38b7f { - align-items: center; - display: flex; - flex-direction: column; - gap: 16px; - height: 100%; - justify-content: space-between; - z-index: 5 -} - -@container (min-width: 460px) { - .supplemental__38b7f { - opacity: 1 - } - - .left__38b7f { - align-items: flex-start; - justify-content: flex-end - } -} - -.product__7c3bf { - align-items: flex-start; - background-position: 0; - background-repeat: no-repeat; - background-size: cover; - border-radius: var(--radius-lg); - cursor: pointer; - display: flex; - flex-direction: column; - flex-grow: 1; - height: 48px; - justify-content: center; - padding: 16px -} - -.layoutEditorContainer__71ca2 { - border: 1px solid var(--border-subtle); - border-radius: 4px; - font-family: Consolas,Monaco,Courier New,monospace; - font-size: 14px; - height: 400px; - line-height: 1.5; - overflow: hidden; - position: relative; - -moz-tab-size: 2; - -o-tab-size: 2; - tab-size: 2; - width: 100% -} - -.highlightLayer__71ca2 { - height: 100%; - inset-inline-start: 0; - overflow: auto; - padding: 12px; - pointer-events: none; - position: absolute; - top: 0; - white-space: pre-wrap; - width: 100%; - word-wrap: break-word; - background-color: transparent; - border: 1px solid transparent; - box-sizing: border-box; - color: transparent; - letter-spacing: normal; - overflow-wrap: break-word -} - -.highlightLayer__71ca2,.highlightLayer__71ca2 code { - font-family: inherit; - font-size: inherit; - line-height: inherit; - margin: 0; - -moz-tab-size: inherit; - -o-tab-size: inherit; - tab-size: inherit -} - -.highlightLayer__71ca2 code { - display: block; - white-space: inherit; - word-wrap: inherit; - background: transparent; - letter-spacing: inherit; - overflow-wrap: inherit; - padding: 0 -} - -.textareaLayer__71ca2 { - background-color: transparent; - border: 1px solid transparent; - caret-color: var(--text-link); - color: transparent; - font-family: inherit; - font-size: inherit; - height: 100%; - inset-inline-start: 0; - letter-spacing: normal; - line-height: inherit; - margin: 0; - outline: none; - overflow: auto; - padding: 12px; - position: absolute; - resize: none; - -moz-tab-size: inherit; - -o-tab-size: inherit; - tab-size: inherit; - top: 0; - white-space: pre-wrap; - width: 100%; - word-wrap: break-word; - box-sizing: border-box; - overflow-wrap: break-word -} - -.textareaLayer__71ca2::-moz-placeholder { - color: var(--text-muted); - opacity: .5 -} - -.textareaLayer__71ca2::placeholder { - color: var(--text-muted); - opacity: .5 -} - -.productCardBadge_b8a6bd { - flex: 1; - max-width: -moz-fit-content; - max-width: fit-content; - min-width: 0 -} - -.theme-dark .productCardBadge_b8a6bd { - background-color: #fff; - color: #000 -} - -.theme-light .productCardBadge_b8a6bd { - background-color: #000; - color: #fff -} - -.profileEffects__01370 { - height: 100%; - inset-inline-start: 0; - overflow: hidden; - pointer-events: none; - position: absolute; - top: 0; - transition: opacity .6s ease-in-out; - width: 100%; - z-index: var(--custom-user-profile-middle-layer-z-index,2) -} - -.hovered__01370 { - opacity: .35 -} - -.inner__01370 { - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.user-profile-modal-v2 .inner__01370 { - border-radius: var(--radius-lg) var(--radius-lg) 0 0 -} - -.innerNoRadius__01370 { - border-radius: 0 -} - -.effect__01370 { - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.previewContainer__1e458 { - display: flex; - justify-content: center; - overflow: hidden; - position: relative; - width: 100%; - z-index: 0 -} - -.previewContainerSetHeight__1e458 { - height: 390px -} - -.full-motion .previewContainerAnimation__1e458 { - animation: scaleIn__1e458 .5s -} - -.preview__1e458 { - height: 100%; - -o-object-fit: cover; - object-fit: cover; - -o-object-position: top; - object-position: top; - position: relative; - width: 100% -} - -@keyframes scaleIn__1e458 { - 0% { - transform: scale(.7) - } - - to { - transform: scale(1) - } -} - -.externalProductWrapper__320c6 { - align-items: center; - display: flex; - height: 252px; - justify-content: center; - -o-object-fit: contain; - object-fit: contain; - width: 100% -} - -.orbProfileBadgeAsset__320c6 { - height: 120px -} - -.fadeInAvatarImg_d71c71 { - animation: fadeIn_d71c71 1.5s -} - -.avatar_d71c71 { - margin: 0 auto -} - -@keyframes fadeIn_d71c71 { - 0% { - opacity: 0 - } - - to { - opacity: 1 - } -} - -.muted__20a53 .avatar__20a53 { - opacity: .3 -} - -.muted__20a53 { - color: var(--text-muted) -} - -.highlighted__20a53 { - color: var(--text-strong) -} - -.layout__20a53 { - border-radius: 4px; - height: 42px; - min-width: 0; - padding: 0 -} - -.avatar__20a53,.layout__20a53 { - align-items: center; - display: flex -} - -.avatar__20a53 { - flex: 0 0 auto; - height: unset; - justify-content: center; - margin-inline-end:12px;width: unset -} - -.content__20a53 { - flex: 1 1 auto; - min-width: 0 -} - -.content__20a53,.name__20a53 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.name__20a53 { - flex: 0 1 auto; - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px -} - -.highlighted__20a53 .name__20a53 { - font-weight: var(--font-weight-semibold-1x-light-theme,500) -} - -.nameAndDecorators__20a53 { - align-items: center; - display: flex; - justify-content: flex-start -} - -.subText__20a53 { - margin-top: -2px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.subText__20a53:empty { - display: none -} - -.enable-forced-colors .muted__20a53 { - color: inherit -} - -.enable-forced-colors .highlighted__20a53 { - color: inherit -} - -.density-cozy .layout__20a53 { - height: 48px -} - -.density-default .layout__20a53 { - height: 42px -} - -.density-compact .layout__20a53 { - height: 38px -} - -.withDisplayNameStyles__20a53 { - overflow: visible -} - -.withDisplayNameStyles__20a53 .name__20a53 { - flex: 1 1 0; - min-width: 0; - overflow: visible; - text-overflow: clip; - white-space: normal -} - -.tooltip_c36707 { - box-sizing: border-box; - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 16px; - max-width: 190px; - position: relative; - word-wrap: break-word; - border: 1px solid var(--border-subtle); - border-radius: 8px; - will-change: opacity,transform; - z-index: 1002 -} - -.tooltipDisablePointerEvents_c36707,.tooltipPointer_c36707 { - pointer-events: none -} - -.tooltipPointer_c36707 { - border: 5px solid transparent; - border-top: 5px solid var(--tooltip-pointer-bg); - height: 0; - width: 0 -} - -.tooltipContent_c36707 { - overflow: hidden; - padding: 8px 12px -} - -.tooltipContentAllowOverflow_c36707 { - overflow: visible -} - -.tooltipTop_c36707 { - transform-origin: 50% 100% -} - -.tooltipTop_c36707 .tooltipPointer_c36707 { - inset-inline-start: 50%; - margin-inline-start:-5px;position: absolute; - top: 100% -} - -.tooltipBottom_c36707,.tooltipCenter_c36707 { - transform-origin: 50% 0 -} - -.tooltipBottom_c36707 .tooltipPointer_c36707,.tooltipCenter_c36707 .tooltipPointer_c36707 { - border-top-width: 5px; - bottom: 100%; - inset-inline-start: 50%; - margin-inline-start:-5px;position: absolute; - transform: rotate(180deg) -} - -.tooltipLeft_c36707 { - transform-origin: 100% 50% -} - -.tooltipLeft_c36707 .tooltipPointer_c36707 { - border-right-width: 5px; - inset-inline-start: 100%; - margin-top: -5px; - position: absolute; - top: 50%; - transform: rotate(-90deg) -} - -.tooltipRight_c36707 { - transform-origin: 0 50% -} - -.tooltipRight_c36707 .tooltipPointer_c36707 { - border-left-width: 5px; - inset-inline-end: 100%; - margin-top: -5px; - position: absolute; - top: 50%; - transform: rotate(90deg) -} - -.enable-forced-colors .tooltip_c36707 { - border: 1px solid CanvasText -} - -.enable-forced-colors .tooltipPointer_c36707 { - background-color: CanvasText; - border: none; - border-radius: 100%; - height: 10px; - width: 10px -} - -.tooltip_c36707 { - box-shadow: var(--shadow-high); - color: var(--text-default) -} - -.tooltipPrimary_c36707 { - --tooltip-pointer-bg: var(--background-surface-high); - background-color: var(--background-surface-high) -} - -.tooltipPrimary_c36707 .tooltipPointer_c36707 { - border-top-color: var(--background-surface-high) -} - -.tooltipGrey_c36707 { - background-color: var(--primary-700); - color: var(--white); - --tooltip-pointer-bg: var(--primary-700) -} - -.tooltipGrey_c36707 .tooltipPointer_c36707 { - border-top-color: var(--primary-700) -} - -.tooltipBrand_c36707 { - background-color: var(--brand-500); - color: var(--white); - --tooltip-pointer-bg: var(--brand-500) -} - -.tooltipBrand_c36707 .tooltipPointer_c36707 { - border-top-color: var(--brand-500) -} - -.tooltipRed_c36707 { - background-color: var(--background-feedback-critical); - color: var(--white); - --tooltip-pointer-bg: var(--border-feedback-critical) -} - -.tooltipRed_c36707 .tooltipPointer_c36707 { - border-top-color: var(--border-feedback-critical) -} - -.tooltipGreen_c36707 { - background-color: var(--green-360); - color: var(--white); - --tooltip-pointer-bg: var(--green-360) -} - -.tooltipGreen_c36707 .tooltipPointer_c36707 { - border-top-color: var(--green-360) -} - -.tooltipPointerBg_c36707 { - display: none -} - -.tooltipPointer_c36707:not(.tooltipPointerBg_c36707) { - z-index: 1 -} - -.tooltipTop_c36707 .tooltipPointer_c36707:not(.tooltipPointerBg_c36707) { - margin-top: -1.5px -} - -.tooltipLeft_c36707 .tooltipPointer_c36707:not(.tooltipPointerBg_c36707) { - margin-inline-start:-1.5px} - -.tooltipRight_c36707 .tooltipPointer_c36707:not(.tooltipPointerBg_c36707) { - margin-inline-end:-1.5px} - -.tooltipBottom_c36707 .tooltipPointer_c36707:not(.tooltipPointerBg_c36707),.tooltipCenter_c36707 .tooltipPointer_c36707:not(.tooltipPointerBg_c36707) { - margin-bottom: -1.5px -} - -.tooltipPointer_c36707.tooltipPointerBg_c36707 { - display: block; - --border-width: 5px -} - -.tooltipPointer_c36707.tooltipPointerBg_c36707:after,.tooltipPointer_c36707.tooltipPointerBg_c36707:before { - border-inline-end:var(--border-width) solid transparent;border-inline-start: var(--border-width) solid transparent; - content: ""; - inset-inline-start: 50%; - margin-inline-start:calc(var(--border-width)*-1);position: absolute; - top: 100% -} - -.tooltipPointer_c36707.tooltipPointerBg_c36707:before { - border-top: var(--border-width) solid var(--tooltip-pointer-bg); - margin-top: -5px -} - -.tooltipPointer_c36707.tooltipPointerBg_c36707:after { - border-top: var(--border-width) solid var(--border-subtle); - margin-top: -5px; - z-index: 1 -} - -.custom-theme-background .tooltipPointer_c36707 { - display: none -} - -.custom-theme-background .tooltip_c36707 { - border: 1px solid var(--border-strong) -} - -.refresh-fast-follow-distinct-borders .tooltip_c36707 { - border-color: var(--app-frame-border) -} - -.buttonHighlighted__5d39a { - background: linear-gradient(95.07deg,var(--premium-tier-2-purple-for-gradients) 0,var(--premium-tier-2-purple-for-gradients-2) 49.96%,var(--premium-tier-2-pink-for-gradients) 95.93%) -} - -.button__5d39a { - align-items: center; - display: flex -} - -.buttonIcon__5d39a { - margin-inline-end:4px} - -.staticGlow__1bd8a,.videoContainer__1bd8a { - transform: translate(calc(-50% + 4px),calc(-50% + 4px)); - width: 1500px; - z-index: 0 -} - -.staticGlow__1bd8a,.video__1bd8a,.videoContainer__1bd8a { - inset-inline-start: 50%; - position: absolute; - top: 50% -} - -.video__1bd8a { - transform: translate(-50%,-50%); - width: 100% -} - -.video__1bd8a.hidden__1bd8a { - visibility: hidden -} - -.video__1bd8a.visible__1bd8a { - visibility: visible -} - -.entryAnimationContainer__1bd8a { - animation: wowMomentScaleIn__1bd8a .4s ease,wowMomentScaleSettle__1bd8a .4s ease .4s; - will-change: transform -} - -@keyframes wowMomentScaleIn__1bd8a { - 0% { - transform: scale(.5) - } - - to { - transform: scale(1.05) - } -} - -@keyframes wowMomentScaleSettle__1bd8a { - 0% { - transform: scale(1.05) - } - - to { - transform: scale(1) - } -} - -.exitAnimationContainer__1bd8a { - animation: wowMomentExit__1bd8a 175ms ease forwards; - pointer-events: none; - transform: scale(1); - will-change: transform -} - -@keyframes wowMomentExit__1bd8a { - 0% { - opacity: 1; - transform: scale(1); - visibility: visible - } - - to { - opacity: 0; - transform: scale(.5); - visibility: hidden - } -} - -.staticWumpusWithTrinkets__1bd8a,.wowAnimation__1bd8a { - height: 1028px; - inset-inline-start: 50%; - pointer-events: none; - position: absolute; - top: 50%; - transform: translate(-50%,calc(-50% - 24px)); - width: 1512px; - z-index: 1 -} - -.premiumSubscribeButton_e00877 { - align-items: center; - display: flex -} - -.premiumIcon_e00877 { - height: 20px; - margin-inline-end:4px} - -.buttonText_e00877 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.giftButton_e00877 { - align-items: center; - display: flex -} - -.giftIcon_e00877 { - height: 16px; - margin-inline-end:4px;min-height: 16px; - min-width: 16px; - width: 16px -} - -.tier1Gradient_e00877 { - background: var(--custom-premium-colors-premium-gradient-tier-1); - color: var(--white) -} - -.tier2Gradient_e00877 { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color); - color: var(--white) -} - -.tier1Gradient_e00877,.tier2Gradient_e00877 { - background-clip: padding-box; - border-radius: var(--radius-sm) -} - -.brandShine_e00877 { - color: hsl(var(--brand-500-hsl)/.1)!important -} - -.rightSparkle_fc1723 { - margin-top: 15px; - margin-inline-start:1px} - -.leftSparkle_fc1723 { - margin-inline-start:-36px;margin-bottom: -18px; - padding-inline-end:10px} - -.sparklesAndButton_fc1723 { - display: flex; - position: relative; - width: 103% -} - -.ctaFullWidth_fc1723 { - width: 100% -} - -.buttonSparkleStar1_fc1723 { - bottom: 35px; - height: 13px; - inset-inline-start: -8px; - position: absolute; - width: 14px -} - -.buttonSparkleStar2_fc1723 { - bottom: 12px; - height: 7px; - inset-inline-start: 7px; - position: absolute; - width: 8px -} - -.buttonSparkleStar3_fc1723 { - bottom: 37px; - height: 28px; - inset-inline-start: 8px; - position: absolute; - width: 14px -} - -.buttonSparkleStar4_fc1723 { - bottom: 26px; - height: 11px; - inset-inline-end: 11px; - position: absolute; - width: 12px -} - -.buttonSparkleStar5_fc1723 { - bottom: 41px; - height: 7px; - inset-inline-end: 4px; - position: absolute; - width: 8px -} - -.rimGlowVertical_fc1723 { - bottom: 22px; - height: 45px; - inset-inline-start: -1px; - position: absolute; - width: 1px -} - -.rimGlowVertical_fc1723.rimGlowVerticalTier0_fc1723 { - background: linear-gradient(180deg,var(--premium-tier-0-purple-for-gradients) 0,#fff 50.52%,var(--premium-tier-0-purple-for-gradients) 100%) -} - -.rimGlowVertical_fc1723.rimGlowVerticalTier2_fc1723 { - background: linear-gradient(180deg,var(--premium-tier-2-purple-for-gradients) 0,#fff 50.52%,var(--premium-tier-2-purple-for-gradients) 100%) -} - -.premiumCards_ac86f6 { - display: flex; - gap: 24px; - width: 100% -} - -.card_ac86f6 { - border-radius: 16px; - color: var(--white); - display: flex; - flex-direction: column; - justify-content: space-between; - padding: 24px; - position: relative; - width: 50% -} - -.card_ac86f6.withCardHover_ac86f6:hover { - box-shadow: var(--legacy-elevation-high) -} - -.subscriptionPlanInfo_ac86f6 { - display: flex; - flex-direction: column; - text-align: start -} - -.listItems_ac86f6,.listItemsBasic_ac86f6 { - display: flex; - flex-direction: column; - justify-content: space-between -} - -.tierCardFocused_ac86f6 { - top: 0; - z-index: 2 -} - -.tierCardHidden_ac86f6 { - box-shadow: var(--shadow-low); - opacity: 60%; - transform: scale(93%) translateY(9%); - z-index: 1 -} - -.wumpusImageContainer_ac86f6 { - border-start-end-radius: 16px; - filter: saturate(var(--saturation-factor,1)); - inset-inline-end: 0; - overflow: hidden; - position: absolute; - top: 0; - width: 40% -} - -.wumpusImage_ac86f6 { - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.tier0_ac86f6.withTier0Rim_ac86f6:before { - border-radius: var(--radius-lg); - content: ""; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - margin: -1px; - z-index: -1 -} - -.tier0_ac86f6 { - background-image: var(--custom-premium-colors-premium-gradient-tier-0) -} - -.tier2_ac86f6 { - background-image: var(--custom-premium-colors-premium-gradient-tier-2) -} - -.tier0ApplicationHomeBackground_ac86f6 { - background: linear-gradient(180deg,#000,#031a3b) -} - -.tier2ApplicationHomeBackground_ac86f6 { - background: linear-gradient(180deg,#000,#36266d) -} - -.theme-light .tier0ApplicationHomeBackground_ac86f6 { - background: linear-gradient(180deg,#f7f7fe 22.95%,#b6ddfc 153.33%) -} - -.theme-light .tier2ApplicationHomeBackground_ac86f6 { - background: linear-gradient(180deg,#f7f7fe,#b6b9fc 153.33%) -} - -.tier2_ac86f6.withTier2Rim_ac86f6:before { - border-radius: var(--radius-lg); - content: ""; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - margin: -1px; - z-index: -1 -} - -.applicationHomeTextLightMode_ac86f6 { - pointer-events: inherit -} - -.tier0Title_ac86f6 { - height: 40px; - width: 106px -} - -.tier0ApplicationHomeTitle_ac86f6 { - color: var(--white); - height: 16px; - width: 132px -} - -.tierCardHeaderDisplay_ac86f6 { - display: flex; - flex-direction: column -} - -.tier2Title_ac86f6 { - display: block; - height: 24px; - width: 96px -} - -.tier2ApplicationHomeTitle_ac86f6 { - color: var(--white); - height: 16px; - width: 64px -} - -.item_ac86f6,.title_ac86f6 { - margin-bottom: 8px -} - -.item_ac86f6 { - align-items: center; - display: flex; - flex-direction: row -} - -.item_ac86f6:last-child { - margin-bottom: 0 -} - -.tier2ApplicationHomeSubheader_ac86f6 { - color: var(--white); - margin-bottom: 6px -} - -.itemApplicationHome_ac86f6 { - color: var(--white) -} - -.applicationHomeTierCardSectionHeader_ac86f6 { - display: flex; - font-style: italic; - justify-content: center; - margin-bottom: 56px; - text-transform: uppercase -} - -.theme-light .applicationHomeTextLightMode_ac86f6 { - color: var(--black) -} - -.applicationHomeStarBackground_ac86f6 { - height: 100%; - inset-inline-end: 50px; - overflow: hidden; - position: absolute; - top: 20px; - width: 50% -} - -.itemWithWumpus_ac86f6 { -} - -.itemWithWumpus_ac86f6:first-of-type { - max-width: 100% -} - -.icon_ac86f6 { - height: 24px; - margin-inline-end:8px;width: 24px -} - -.smallIcon_ac86f6 { - height: 18px; - margin-inline-end:10px;width: 18px -} - -.newCircleIcon_ac86f6 { - filter: saturate(var(--saturation-factor,1)); - inset-inline-start: 250px; - position: absolute; - top: -24px -} - -.trialOfferPill_ac86f6 { - margin-bottom: 18px -} - -.listItemsBasic_ac86f6 { - min-height: 92% -} - -.freeTrialPillInlineBlock_ac86f6 { - background: var(--white); - border-radius: var(--radius-md); - color: var(--black); - display: inline-block; - font-weight: var(--font-weight-extra-bold); - line-height: 12px; - mix-blend-mode: screen; - padding-bottom: 4px; - padding-top: 4px; - padding-inline:8px;text-transform: uppercase -} - -.freeTrialPillInline_ac86f6 { - margin-top: 4px -} - -.freeTrialActivatedPillInline_ac86f6,.freeTrialPillInline_ac86f6 { - margin-bottom: 8px; - max-width: 60% -} - -.freeTrialActivatedPillInline_ac86f6 { - margin-top: 8px -} - -.taglineXPPrice_ac86f6 { - margin: 20px 0 10px -} - -.taglineXPPrices_ac86f6:first-of-type { - margin: 16px 0 8px -} - -.taglineXPPrices_ac86f6:last-of-type { - margin: 8px 0 16px -} - -.theme-dark .newCircleIcon_ac86f6 { - color: var(--white) -} - -.theme-light .newCircleIcon_ac86f6 { - color: var(--premium-perk-yellow) -} - -.newTagItem_ac86f6 { - background: #fff; - border-radius: 8px; - color: var(--premium-tier-2-purple-for-gradients); - margin-inline-start:8px} - -.mostPopularText_ac86f6 { - color: var(--white); - text-align: center; - text-transform: uppercase -} - -.mostPopularPill_ac86f6 { - background: linear-gradient(95.07deg,var(--premium-tier-2-purple-for-gradients) 0,var(--premium-tier-2-purple-for-gradients-2) 49.96%,var(--premium-tier-2-pink-for-gradients) 95.93%); - border-radius: 8px; - inset-inline-start: 16px; - margin: auto; - padding: 4px 8px; - position: absolute; - top: -12px; - z-index: 10 -} - -.freeTrialPillWithSparkles_ac86f6 { - align-items: center; - display: flex -} - -.sparkleStar1_ac86f6 { - height: 12px; - width: 14px -} - -.sparkleStar2_ac86f6 { - height: 7px; - margin-inline-start:-3px;margin-bottom: 15px; - width: 8px -} - -.sparkleStar3_ac86f6 { - height: 28px; - margin-inline:2px 10px;width: 14px -} - -.freeTrialPill_ac86f6 { - align-items: center; - background: var(--white); - border-radius: var(--radius-md); - display: flex; - flex-direction: row; - height: 20px; - justify-content: center -} - -.freeTrialPill_ac86f6.freeTrialPillTier0GradientFill_ac86f6 { - background: var(--custom-premium-colors-premium-gradient-tier-0) -} - -.freeTrialPill_ac86f6.freeTrialPillTier2GradientFill_ac86f6 { - background: var(--custom-premium-colors-premium-gradient-tier-2) -} - -.freeTrialPill_ac86f6.greyBackgroundPill_ac86f6 { - background: var(--primary-500) -} - -.freeTrialPill_ac86f6.modBackgroundPill_ac86f6 { - background: hsla(240,4%,61%,.2) -} - -.freeTrialPill_ac86f6.freeTrialPillTier2OldGradientFill_ac86f6 { - background: var(--custom-premium-colors-premium-gradient-tier-2-old) -} - -.freeTrialPill_ac86f6.lightBackgroundPill_ac86f6 { - background: linear-gradient(101deg,var(--premium-tier-2-purple-for-gradients) 25.21%,var(--premium-tier-2-purple-for-gradients-2) 62.43%,var(--premium-tier-2-pink-for-gradients) 95.57%); - border: 1px solid #b473f5 -} - -.freeTrialPill_ac86f6.freeTrialPillGap_ac86f6 { - gap: 10px -} - -.freeTrialPillText_ac86f6 { - cursor: default; - display: inline-block; - font-weight: var(--font-weight-extra-bold); - line-height: 12px; - padding-bottom: 4px; - padding-top: 4px; - padding-inline:8px;text-align: center; - text-transform: uppercase; - white-space: nowrap -} - -.freeTrialPillText_ac86f6.freeTrialPillTextTier0_ac86f6 { - color: var(--premium-tier-0-purple-for-gradients) -} - -.freeTrialPillText_ac86f6.freeTrialPillTextTier2_ac86f6 { - color: var(--premium-tier-2-purple-for-gradients) -} - -.freeTrialPillText_ac86f6.freeTrialPillTextInverted_ac86f6 { - color: var(--white) -} - -.freeTrialPillText_ac86f6.freeTrialPillTextDefault_ac86f6 { - color: var(--text-default) -} - -.sparkleStar4_ac86f6 { - height: 11px; - margin-inline-start:10px;width: 13px -} - -.sparkleStar5_ac86f6 { - height: 7px; - margin-inline-start:6px;margin-top: 1px; - width: 8px -} - -.topRimPill_ac86f6 { - inset-inline-start: 50%; - position: absolute; - top: -15px; - transform: translateX(-50%); - z-index: 2 -} - -.rimGlowTier0_ac86f6 { - background: linear-gradient(-90deg,var(--premium-tier-0-purple-for-gradients) 0,#fff 4.98%,#fff 50.52%,var(--premium-tier-0-blue-for-gradients) 100%); - inset-inline-end: 5%; - top: -1px; - width: 88% -} - -.rimGlowTier0_ac86f6,.rimGlowTier2_ac86f6 { - height: 1px; - position: absolute; - z-index: 1 -} - -.rimGlowTier2_ac86f6 { - background: linear-gradient(-90deg,var(--premium-tier-2-purple-for-gradients) 0,#fff 4.98%,#fff 50.52%,var(--premium-tier-2-pink-for-gradients) 100%); - inset-inline-end: 5%; - top: -1px; - width: 88% -} - -.withPromotionalGradientBanner_ac86f6 { - background: linear-gradient(100.98deg,#8547c6,#b845c1,#ab5d8a); - border-end-end-radius: 17px; - border-end-start-radius: 17px; - box-sizing: border-box; - overflow: hidden -} - -.withPromotionalGradientBanner_ac86f6.withPromotionalCardImage_ac86f6 { - background: linear-gradient(100.98deg,rgba(133,71,198,.82),rgba(184,69,193,.82),rgba(171,93,138,.82)) -} - -.promotionalCardImage_ac86f6 { - inset-inline-end: 0; - position: absolute; - top: 0; - transform: scale(50%); - transform-origin: top right; - z-index: -1 -} - -.promotionalBackgroundImage_ac86f6 { - inset-inline-end: 0; - position: absolute; - top: 0; - transform: scale(65%); - transform-origin: top right -} - -.tier2SideGradient_ac86f6 { - background: radial-gradient(80% 70% at 100% 50%,#2d01dc 0,transparent 100%),radial-gradient(80% 70% at 100% 50%,#b182ff 0,transparent 100%); - height: 1000px; - opacity: .4; - pointer-events: none; - position: absolute; - width: 100vw -} - -.subButton_ac86f6 { - margin-top: 60px -} - -.v2SubButtonText_ac86f6 { - color: var(--brand-500) -} - -.theme-light .v2SubButtonText_ac86f6 { - color: var(--white) -} - -.buttonShine_ac86f6 { - color: hsl(var(--brand-500-hsl)/.1) -} - -.subButtonText_ac86f6 { - color: var(--brand-500) -} - -.heading_e62f9d { - margin-bottom: 8px -} - -.context_e62f9d { - margin-bottom: 16px -} - -.gradientUpsellWrapper_e62f9d { - align-items: center; - border-radius: 8px; - box-sizing: border-box; - color: var(--white); - display: flex; - flex-direction: column; - margin-top: 16px; - padding: 16px; - width: 100% -} - -.gradientUpsellWrapper_e62f9d a,.gradientUpsellWrapper_e62f9d a:hover { - color: var(--white); - text-decoration: underline -} - -.gradientUpsellWrapperTier0_e62f9d { - background-image: linear-gradient(90deg,var(--premium-tier-0-purple-for-gradients) 0,var(--premium-tier-0-blue-for-gradients-2) 50%,var(--premium-tier-0-blue-for-gradients) 100%) -} - -.gradientUpsellWrapperTier2_e62f9d { - background-image: linear-gradient(90deg,var(--premium-tier-2-purple-for-gradients) 0,var(--premium-tier-2-pink-for-gradients) 100%) -} - -.gradientUpsellWrapperWithBottomMargin_e62f9d { - margin-bottom: 24px -} - -.logo_e62f9d { - align-items: center; - color: var(--white); - display: flex; - margin-bottom: 4px -} - -.logoIcon_e62f9d { - flex: 0 0 auto; - height: 24px; - margin-inline-end:2px;width: 24px -} - -.logoWordmark_e62f9d { - flex: 0 0 auto; - height: 16px; - width: auto -} - -.copy_e62f9d span { - cursor: pointer; - text-decoration: underline -} - -.copy_e62f9d,.copy_e62f9d span { - color: var(--white) -} - -.copy_e62f9d { - margin-bottom: 12px; - text-align: center -} - -.trialBadge_e62f9d { - background-color: var(--white); - border-radius: 30px; - padding: 2px 8px; - text-transform: uppercase -} - -.trialBadgeInner_e62f9d { - -webkit-background-clip: text; - background-clip: text; - color: transparent -} - -.gradientUpsellWrapperTier0_e62f9d .trialBadgeInner_e62f9d { - background-image: linear-gradient(90deg,var(--premium-tier-0-purple-for-gradients) 0,var(--premium-tier-0-blue-for-gradients-2) 50%,var(--premium-tier-0-blue-for-gradients) 100%) -} - -.gradientUpsellWrapperTier2_e62f9d .trialBadgeInner_e62f9d { - background-image: linear-gradient(90deg,var(--premium-tier-2-purple-for-gradients) 0,var(--premium-tier-2-pink-for-gradients) 100%) -} - -.trialBadgeGradientTier0_e62f9d { - background-image: linear-gradient(90deg,var(--premium-tier-0-purple-for-gradients) 0,var(--premium-tier-0-blue-for-gradients-2) 50%,var(--premium-tier-0-blue-for-gradients) 100%); - color: var(--white) -} - -.trialBadgeGradientTier2_e62f9d { - background-image: linear-gradient(90deg,var(--premium-tier-2-purple-for-gradients) 0,var(--premium-tier-2-pink-for-gradients) 100%); - color: var(--white) -} - -.contentContainer_e62f9d { - align-items: center; - display: flex; - flex-direction: column; - flex-grow: 1; - justify-content: center; - padding-inline-end:24px;padding-inline-start:24px} - -.countdownText_e62f9d { - color: #fff; - padding-top: 8px -} - -.footer_e62f9d { - background-color: var(--background-base-lowest); - display: flex; - flex-direction: row; - justify-content: space-between; - margin-top: 12px; - padding-bottom: 16px; - padding-top: 16px; - width: 100% -} - -.theme-dark .cancelButton_e62f9d { - color: #fff -} - -.cancelButton_e62f9d { - margin-inline-start:24px} - -.subscribeButton_e62f9d { - margin-inline-end:24px} - -.upsellButton_e62f9d { - margin-top: 16px; - width: 100% -} - -.divider_e62f9d { - background-color: var(--border-subtle); - height: 1px; - width: 100% -} - -@keyframes zoomInFromBottomRight_e62f9d { - 0% { - opacity: 0; - transform: translateX(80%) translateY(80%) scaleX(20%) scaleY(20%) - } - - to { - opacity: 100%; - transform: translateX(0) translateY(0) scaleX(100%) scaleY(100%) - } -} - -.messageLengthUpsellContainer_e62f9d { - align-items: center; - background-color: var(--background-surface-high); - border-radius: var(--radius-sm); - display: flex; - flex-direction: column; - overflow: hidden; - padding: 12px -} - -.full-motion .messageLengthUpsellContainer_e62f9d.messageLengthUpsellAppearAnimation_e62f9d { - animation-delay: 0s; - animation-duration: .4s; - animation-iteration-count: 1; - animation-name: zoomInFromBottomRight_e62f9d; - animation-timing-function: ease-in -} - -.messageLengthBrandedContainer_e62f9d { - margin-bottom: 16px; - max-width: 290px -} - -.messageLengthUpsellHeader_e62f9d { - margin-bottom: 16px; - margin-top: 4px -} - -.tryOutUpsellContainer_e62f9d { - align-items: center; - display: flex; - flex-direction: column -} - -.topRimPill_e62f9d { - margin-top: -15px; - padding-bottom: 16px -} - -.subscribeButtonWide_e62f9d { - height: 40px; - margin-top: 16px; - width: 100% -} - -.countdownTextInSetting_e62f9d { - color: var(--premium-tier-2-pink); - padding-top: 8px -} - -.theme-light .countdownTextInSetting_e62f9d { - color: var(--premium-tier-2-purple-for-gradients) -} - -.premiumTrialUpsellForModal_e62f9d { - display: flex; - flex-direction: column; - justify-content: flex-end; - width: 100% -} - -.upsellClose_e62f9d { - color: var(--interactive-text-default); - cursor: pointer; - inset-inline-end: 20px; - position: absolute; - top: 20px -} - -.upsellImage_e62f9d { - height: 60px; - margin-bottom: 16px; - width: -moz-fit-content; - width: fit-content -} - -.container__61733 { - align-items: center; - background: linear-gradient(90deg,hsla(0,0%,100%,0),hsla(0,0%,100%,.1) 50%,hsla(0,0%,100%,0)); - display: flex; - height: 50px; - justify-content: center -} - -.premiumBrandRefreshContainer__61733 { - align-items: center; - background: var(--background-mod-normal); - border-radius: var(--radius-md); - display: flex; - padding: var(--space-12) var(--space-16) -} - -.v2Container__61733 { - background: none; - justify-content: start -} - -.iconContainer__61733 { - display: flex -} - -.icon__61733+.icon__61733 { - margin-inline-start:-8px} - -.mask__61733 { - -webkit-mask-image: url(/assets/1f9b76e0279b91a4.svg); - mask-image: url(/assets/1f9b76e0279b91a4.svg); - -webkit-mask-position: 0 0; - mask-position: 0 0; - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-size: 100% 100%; - mask-size: 100% 100% -} - -.textContainer__61733 { - margin-inline-start:9px;text-align: start -} - -.container__790b6 { - align-items: center; - background: var(--background-base-lowest); - border: 1px solid var(--border-muted); - border-radius: 8px; - height: 44px; - justify-content: center; - margin: 24px auto 16px; - max-width: 80%; - min-width: 40%; - padding: 0 20px; - width: -moz-fit-content; - width: fit-content -} - -.container__790b6,.iconContainer__790b6 { - display: flex -} - -.icon__790b6+.icon__790b6 { - margin-inline-start:-8px} - -.textContainer__790b6 { - margin-inline-start:8px;text-align: start -} - -.root__3ec70 { - text-align: center -} - -.root__3ec70 p { - margin: 0 0 16px -} - -.contentContainer__3ec70 { - display: flex; - flex-direction: row -} - -.bodyContent__3ec70 { - align-content: center -} - -.header__3ec70 { - margin-bottom: 24px -} - -.enhancedHeader__3ec70,.subHeader__3ec70 { - padding: 0 32px -} - -.content__3ec70,.enhancedContent__3ec70 { - margin-bottom: 32px -} - -.enhancedContent__3ec70 { - padding: 0 -} - -.footer__3ec70 { - align-items: center; - justify-content: space-between -} - -.enhancedFooter__3ec70 { - background: var(--background-base-lowest) -} - -.primaryActions__3ec70 { - align-items: center; - display: flex -} - -.enhancedPrimaryActions__3ec70 { - justify-content: space-between; - width: 100% -} - -.secondaryAction__3ec70 { - margin-inline-end:8px} - -.enhancedSecondaryAction__3ec70 { - color: var(--premium-nitro-pink-text) -} - -.enhancedSecondaryAction__3ec70:hover { - text-decoration: underline -} - -.closeButton__3ec70 { - align-self: flex-end; - color: var(--interactive-text-active); - margin: 12px; - position: absolute -} - -.artContainer__3ec70 { - border-radius: 8px; - margin: -48px auto 24px; - position: relative; - width: 225px -} - -.artContainerBoxShadow__3ec70 { - box-shadow: var(--elevation-medium) -} - -.art__3ec70 { - width: 225px -} - -.sparkleBadge__3ec70 { - display: block; - filter: drop-shadow(0 1px 3px var(--opacity-black-28)); - height: 34px; - inset-inline-start: 0; - margin-top: -18px; - margin-inline-start:-12px;position: absolute; - top: 0; - width: 47px -} - -.newBadge__3ec70 { - display: inline-block; - margin-top: -2px; - vertical-align: middle -} - -.learnMoreLink__3ec70 { - color: var(--text-link); - cursor: pointer -} - -.context__3ec70 { - margin-bottom: 16px -} - -.betaTag__3ec70 { - display: inline-block; - margin-inline-start:.5em;vertical-align: baseline -} - -.artContainer__8162d { - align-items: flex-end; - display: flex; - height: 108px; - justify-content: center; - margin: -54px auto 32px; - width: 180px -} - -.art__8162d { - height: 100%; - width: 100% -} - -.body__8162d { - display: flex; - flex-direction: column; - gap: 16px; - white-space: pre-line -} - -.twoColorGradient_e5de78 { - background: linear-gradient(to right,var(--custom-gradient-color-1),var(--custom-gradient-color-2),var(--custom-gradient-color-1)) -} - -.threeColorGradient_e5de78 { - background: linear-gradient(to right,var(--custom-gradient-color-1),var(--custom-gradient-color-2),var(--custom-gradient-color-3),var(--custom-gradient-color-1)) -} - -.usernameGradient_e5de78 { - -webkit-background-clip: text; - background-clip: text; - background-size: 100px auto; - -webkit-text-fill-color: transparent -} - -.usernameGlow_e5de78 { - filter: blur(4px); - opacity: 0; - z-index: -1 -} - -.usernameGlow_e5de78,.usernameGlowActive_e5de78 { - transition: opacity .1s ease-in-out -} - -.usernameGlowActive_e5de78 { - opacity: .7 -} - -.emoji_e5de78 { - -webkit-text-fill-color: initial -} - -.convenienceGlowGradient_e5de78 { - position: relative; - z-index: 0 -} - -.theme-light .usernameGradient_e5de78 { - filter: brightness(.85) -} - -.convenienceGlowGradient_e5de78:after { - background: inherit; - -webkit-background-clip: text; - background-clip: text; - content: attr(data-text) /""; - inset: 0; - position: absolute; - -webkit-text-fill-color: transparent; - filter: blur(4px); - opacity: 0; - text-indent: 0; - transition: opacity .1s ease-in-out; - z-index: -1 -} - -.convenienceGlowGradientActive_e5de78:after { - opacity: .7; - transition: opacity .1s ease-in-out -} - -.full-motion.app-focused:not(.hardware-acceleration-disabled) .animateGradient_e5de78 { - animation: gradientUsernameAnimation_e5de78 1.5s linear infinite -} - -.full-motion.app-focused:not(.hardware-acceleration-disabled) .gradientDotAnimation_e5de78:hover { - animation: gradientDotAnimation_e5de78 2.5s linear infinite -} - -@keyframes gradientUsernameAnimation_e5de78 { - 0% { - background-position: 0 - } - - to { - background-position: 100px - } -} - -@keyframes gradientDotAnimation_e5de78 { - 0% { - background-position: 0 - } - - to { - background-position: 12px - } -} - -@keyframes gradientGlowFadeInAnimation_e5de78 { - 0% { - opacity: 0 - } - - to { - opacity: .7 - } -} - -.dnsFont__89a31 { - font-synthesis: none -} - -.cherryBomb__89a31 { - font-family: Cherry Bomb One,gg sans,Arial,sans-serif; - letter-spacing: .04em -} - -.cherryBomb__89a31.safari__89a31 { - transform: translateY(-1px) -} - -.chicle__89a31 { - font-family: Chicle,gg sans,Arial,sans-serif -} - -.museoModerno__89a31 { - font-family: Museo Moderno,gg sans,Arial,sans-serif; - letter-spacing: .01em -} - -.neoCastel__89a31 { - font-family: Neo Castel,gg sans,Arial,sans-serif; - letter-spacing: .02em -} - -.neoCastel__89a31.safari__89a31 { - transform: translateY(-1px) -} - -.pixelify__89a31 { - font-family: Pixelify Sans,gg sans,Arial,sans-serif; - letter-spacing: .02em -} - -.sinistre__89a31 { - font-family: Sinistre,gg sans,Arial,sans-serif; - letter-spacing: .01em -} - -.zillaSlab__89a31 { - font-family: Zilla Slab,gg sans,Arial,sans-serif; - letter-spacing: .03em -} - -.container_dfb989 { - display: inline-flex; - max-width: 100%; - min-width: 0; - opacity: var(--custom-display-name-styles-font-opacity,1); - perspective: 1px; - position: relative; - text-indent: 0; - z-index: 0 -} - -.container_dfb989.inProfile_dfb989 { - display: flex -} - -.innerContainer_dfb989 { - flex: 1 1 0%; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: var(--custom-display-name-styles-wrap) -} - -.underlineOnHover_dfb989:not(.pop_dfb989):hover { - text-decoration: underline; - text-decoration-color: var(--custom-display-name-styles-main-color) -} - -.glowContainer_dfb989 { - color: transparent; - height: 100%; - inset: 0; - overflow: hidden; - position: absolute; - text-overflow: ellipsis; - width: 100% -} - -.emoji_dfb989 { - position: relative; - z-index: 1 -} - -.showEffect_dfb989.container_dfb989 { - opacity: 1 -} - -.showEffect_dfb989.container_dfb989 .solid_dfb989 { - color: var(--custom-display-name-styles-main-color) -} - -.showEffect_dfb989.container_dfb989 .gradient_dfb989 { - background: linear-gradient(to bottom right,var(--custom-display-name-styles-gradient-start-color) 10%,var(--custom-display-name-styles-gradient-end-color) 90%); - background-clip: text; - -webkit-background-clip: text; - background-size: 100% auto; - -webkit-text-fill-color: transparent; - position: relative; - z-index: 0 -} - -.showEffect_dfb989.container_dfb989 .neon_dfb989 { - paint-order: stroke fill; - -webkit-text-stroke-width: calc(1px + .04em); - -webkit-text-stroke-color: var(--custom-display-name-styles-neon-stroke-color); - color: var(--white); - margin-inline-start:calc(-1px - .04em);margin-bottom: calc(-1px - .04em); - padding-bottom: calc(1px + .04em); - padding-inline-start:calc(1px + .04em);position: relative; - z-index: 0 -} - -.showEffect_dfb989.container_dfb989 .neonGlow_dfb989 { - background: linear-gradient(to bottom left,var(--custom-display-name-styles-light-2-color) 0,var(--custom-display-name-styles-light-2-color) 6%,var(--custom-display-name-styles-main-color) 20%,var(--custom-display-name-styles-light-1-color) 50%,var(--custom-display-name-styles-light-2-color) 56%,var(--custom-display-name-styles-main-color) 70%,var(--custom-display-name-styles-light-1-color) 100%); - background-clip: text; - -webkit-background-clip: text; - background-position: 100% 0; - background-size: 200% 200%; - -webkit-text-fill-color: transparent; - color: var(--custom-display-name-styles-main-color); - filter: blur(calc(1px + .12em)); - opacity: .8; - -webkit-text-stroke-width: calc(1px + .04em); - -webkit-text-stroke-color: transparent; - perspective: 1px; - z-index: -1 -} - -.showEffect_dfb989.container_dfb989 .toon_dfb989 { - --custom-toon-stroke-width: calc(1.6px + 0.04em); - --custom-toon-margin: calc(var(--custom-toon-stroke-width)*-1); - paint-order: stroke fill; - position: relative; - -webkit-text-stroke-width: var(--custom-toon-stroke-width); - -webkit-text-stroke-color: var(--custom-display-name-styles-toon-stroke-color); - color: var(--custom-display-name-styles-toon-stroke-color); - margin-inline-start:var(--custom-toon-margin);margin-bottom: var(--custom-toon-margin); - margin-inline-end:var(--custom-toon-margin);padding-bottom: var(--custom-toon-stroke-width); - padding-inline-end:var(--custom-toon-stroke-width);padding-inline-start: var(--custom-toon-stroke-width); - transition: color 266ms cubic-bezier(.43,.21,.27,.78) -} - -.showEffect_dfb989.container_dfb989 .toon_dfb989:before { - background: linear-gradient(180deg,var(--white) 0,var(--custom-display-name-styles-light-2-color) 8%,var(--custom-display-name-styles-light-1-color) 15%,var(--custom-display-name-styles-main-color) 25%,var(--custom-display-name-styles-light-2-color) 45%,var(--custom-display-name-styles-main-color) 55%,var(--white) 75%,var(--custom-display-name-styles-light-2-color) 83%,var(--custom-display-name-styles-light-1-color) 90%,var(--custom-display-name-styles-main-color) 100%); - background-clip: text; - -webkit-background-clip: text; - background-size: 100% 400%; - content: attr(data-username-with-effects); - inset: 0; - padding-inline:var(--custom-toon-stroke-width);padding-bottom: var(--custom-toon-margin); - position: absolute; - -webkit-text-fill-color: transparent; - -webkit-text-stroke-width: 0; - -webkit-text-stroke-color: transparent; - overflow: hidden; - text-overflow: ellipsis; - transition: opacity 266ms cubic-bezier(.43,.21,.27,.78); - white-space: var(--custom-display-name-styles-wrap) -} - -.showEffect_dfb989.container_dfb989 .pop_dfb989 { - --custom-pop-stroke-width: calc(1.2px + 0.04em); - --custom-pop-bottom-translate_3d: 0.08em; - color: var(--white); - paint-order: stroke fill; - position: relative; - -webkit-text-stroke-color: var(--custom-display-name-styles-dark-2-color); - margin-bottom: calc(var(--custom-pop-stroke-width)*-1 - var(--custom-pop-bottom-translate_3d)); - padding-bottom: calc(var(--custom-pop-stroke-width) + var(--custom-pop-bottom-translate_3d)); - padding-inline-start:var(--custom-pop-stroke-width)} - -.showEffect_dfb989.container_dfb989 .pop_dfb989,.showEffect_dfb989.container_dfb989 .pop_dfb989: before { - -webkit-text-stroke-width:var(--custom-pop-stroke-width); - margin-inline-start:calc(var(--custom-pop-stroke-width)*-1)} - -.showEffect_dfb989.container_dfb989 .pop_dfb989: before { - bottom:calc(var(--custom-pop-bottom-translate_3d)*-1 - var(--custom-pop-stroke-width)); - color: var(--custom-display-name-styles-main-color); - content: attr(data-username-with-effects); - padding-inline-start:var(--custom-pop-stroke-width);position: absolute; - top: 0; - width: calc(100% - var(--custom-pop-stroke-width)); - z-index: -1; - -webkit-text-stroke-color: transparent; - background: linear-gradient(to bottom left,var(--custom-display-name-styles-light-1-color) 0,var(--custom-display-name-styles-light-1-color) 6%,var(--custom-display-name-styles-main-color) 20%,var(--custom-display-name-styles-main-color) 50%,var(--custom-display-name-styles-light-1-color) 56%,var(--custom-display-name-styles-main-color) 70%,var(--custom-display-name-styles-main-color) 100%); - background-clip: text; - -webkit-background-clip: text; - background-position: 100% 0; - background-size: 200% 200%; - transform: translate3d(0,var(--custom-pop-bottom-translate_3d),0); - -webkit-text-fill-color: transparent; - overflow: hidden; - text-decoration: none; - text-overflow: ellipsis; - white-space: var(--custom-display-name-styles-wrap) -} - -.showEffect_dfb989.container_dfb989 .pop_dfb989.underlineOnHover_dfb989:hover:before { - text-decoration: underline; - text-decoration-color: var(--custom-display-name-styles-main-color); - text-underline-offset: calc(var(--custom-pop-bottom-translate_3d)) -} - -.animated_dfb989.loop_dfb989.loop_dfb989>*,.animated_dfb989.loop_dfb989.loop_dfb989>:before { - animation-iteration-count: infinite -} - -.animated_dfb989 .neon_dfb989 { - animation: neon-flicker-animation_dfb989 4s cubic-bezier(.24,.31,.36,.93); - animation-direction: normal; - animation-fill-mode: forwards -} - -.animated_dfb989 .neonGlow_dfb989 { - animation: neon-glow-flicker-animation_dfb989 1666ms linear; - animation-direction: normal; - animation-fill-mode: forwards -} - -.animated_dfb989 .toon_dfb989:before { - animation: toon-animation_dfb989 4s cubic-bezier(.44,.29,.48,1); - animation-direction: normal; - animation-fill-mode: forwards -} - -.animated_dfb989 .pop_dfb989 { - animation: pop-animation-main_dfb989 4s cubic-bezier(.44,.29,.48,1); - animation-direction: normal; - animation-fill-mode: forwards -} - -.animated_dfb989 .pop_dfb989:before { - animation: pop-animation-shadow_dfb989 4s cubic-bezier(.44,.29,.48,1); - animation-direction: normal; - animation-fill-mode: forwards -} - -.container_dfb989:not(.animated_dfb989),.container_dfb989:not(.animated_dfb989):before { - animation-play-state: paused!important; - transition: none!important -} - -@keyframes pop-animation-main_dfb989 { - 0% { - transform: translateZ(0) - } - - 18% { - perspective: 1px; - transform: translate3d(0,-.05em,0) - } - - 35% { - perspective: 1px; - transform: translate3d(0,.08em,0) - } - - 50%,to { - perspective: 1px; - transform: translateZ(0) - } -} - -@keyframes pop-animation-shadow_dfb989 { - 0% { - background-position: 100% 0; - perspective: 1px; - transform: translate3d(0,.08em,0) - } - - 18% { - perspective: 1px; - transform: translate3d(0,.13em,0) - } - - 35% { - perspective: 1px; - transform: translateZ(0) - } - - 50%,to { - background-position: 0 100%; - perspective: 1px; - transform: translate3d(0,.08em,0) - } -} - -@keyframes toon-animation_dfb989 { - 0%,5% { - background-position: 50% 0 - } - - 55%,to { - background-position: 50% 100% - } -} - -@keyframes neon-flicker-animation_dfb989 { - 0%,15%,18%,20%,23%,25%,50% { - color: var(--white) - } - - 16%,22%,28% { - color: hsl(from var(--custom-display-name-styles-main-color) h calc(min(1, s) * ((s * 1.1) + 10)) 85) - } - - 51%,to { - color: var(--white) - } -} - -@keyframes neon-glow-flicker-animation_dfb989 { - 0% { - background-position: 100% 0 - } - - to { - background-position: 0 100% - } -} - -.svg__2338f { - contain: paint -} - -.guildIconImage_d28ae0 { - height: 100%; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.guildIconImage_d28ae0.acronym_d28ae0 { - background-color: var(--background-base-lowest) -} - -.acronym_d28ae0 { - align-items: center; - color: var(--text-strong); - display: flex; - font-size: 14px; - font-weight: 500; - justify-content: center; - overflow: hidden; - white-space: nowrap; - width: 100% -} - -.container_dca174 { - align-items: flex-start; - background: var(--background-surface-highest); - border: 1px solid var(--border-subtle); - border-radius: 16px; - display: flex; - flex-direction: column; - overflow: hidden -} - -.banner_dca174 { - height: 120px; - width: 100% -} - -.avatarContainer_dca174 { - display: flex; - justify-content: flex-start; - margin-top: -34px; - padding: 0 16px -} - -.overlay_dca174 { - background-color: var(--opacity-black-40); - height: 100%; - inset-inline-end: 0; - top: 0; - width: 100%; - z-index: 0 -} - -.overlay_dca174,.overlayIcon_dca174 { - opacity: 0; - position: absolute; - transition: opacity .2s ease -} - -.clickable_dca174 { - cursor: pointer -} - -.clickable_dca174:hover .overlay_dca174,.clickable_dca174:hover .overlayIcon_dca174 { - opacity: 1 -} - -.avatarWrapper_dca174 { - align-items: center; - background: var(--background-surface-highest); - display: flex; - height: 70px; - justify-content: center; - width: 70px -} - -.avatarWrapper_dca174 .acronymText_dca174 { - font-size: 22px -} - -.emptyBody_dca174 { - height: 8px -} - -.body_dca174 { - flex-direction: column; - gap: 16px; - overflow-wrap: anywhere; - padding: 0 16px; - text-align: flex-start -} - -.body_dca174,.error_dca174 { - display: flex; - margin-bottom: 16px -} - -.error_dca174 { - color: var(--text-feedback-warning); - cursor: pointer; - gap: 4px -} - -.header_dca174 { - align-items: flex-start; - display: flex; - flex-direction: column; - margin-bottom: 8px; - margin-top: 8px; - padding: 0 16px -} - -.buttonContainer_dca174 { - box-sizing: border-box; - display: flex; - padding: 0 16px 16px; - width: 100% -} - -.container__4e338 { - display: flex; - flex-direction: row; - gap: 4px; - width: 100% -} - -.gameIconWrapper__4e338 { - cursor: pointer -} - -.gameIcon__4e338 { - flex-shrink: 0; - height: 26px; - position: relative; - width: 26px -} - -.gameIconImage__4e338 { - border: 1px solid var(--border-muted); - border-radius: 4px; - max-height: 100%; - max-width: 100%; - -o-object-fit: contain; - object-fit: contain -} - -.extraGamesContainer__4e338 { - display: flex; - flex-direction: column; - gap: 4px; - padding: 8px 0 -} - -.extraGameRow__4e338 { - gap: 8px -} - -.extraGameItem__4e338,.extraGameRow__4e338 { - align-items: center; - display: flex -} - -.extraGameItem__4e338 { - justify-content: center; - position: relative -} - -.extraGameIconImage__4e338 { - position: absolute; - z-index: 1 -} - -.extraGameOverlay__4e338 { - background: var(--background-scrim); - border-radius: 4px; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - z-index: 10 -} - -.extraGameText__4e338 { - z-index: 20 -} - -.gameActivityLevel__4e338 { - background-color: var(--background-surface-highest); - border-radius: 50%; - inset-inline-end: -4px; - position: absolute; - top: -4px -} - -.gameActivityLevel__4e338,.gameActivityLevelInner__4e338 { - align-items: center; - box-sizing: border-box; - display: flex; - justify-content: center -} - -.gameActivityLevelInner__4e338 { - height: 14px; - padding: 2px; - width: 14px -} - -.favoriteGameContainer__4e338 { - align-items: center; - display: flex; - gap: 8px -} - -.animationWrapper__41e0f { - height: 100%; - position: absolute; - width: 100% -} - -.lottieAnimation__41e0f { - bottom: 0; - height: 196px; - inset-inline-end: 0; - position: absolute; - width: 196px -} - -.emoji { - height: var(--custom-emoji-size-emoji); - -o-object-fit: contain; - object-fit: contain; - vertical-align: bottom; - width: var(--custom-emoji-size-emoji) -} - -.emoji.jumboable { - height: var(--custom-emoji-size-jumbo-emoji); - min-height: var(--custom-emoji-size-jumbo-emoji); - width: var(--custom-emoji-size-jumbo-emoji) -} - -@value traitMaxWidth: 268px;.container_a2b4d3 { - flex-wrap: wrap -} - -.container_a2b4d3,.trait_a2b4d3 { - display: flex; - gap: 4px -} - -.trait_a2b4d3 { - align-items: center; - border: 1px solid var(--border-subtle); - border-radius: 16px; - max-width: 268px; - padding: 4px 8px; - transition: border-color .2s ease -} - -.trait_a2b4d3:hover { - border-color: var(--border-strong) -} - -.traitEmoji_a2b4d3 { - height: 16px; - width: 16px -} - -.ellipsize_a2b4d3 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.flowerStarContainer__3e3b0 { - align-items: center; - display: flex; - justify-content: center; - position: relative -} - -.flowerStar__3e3b0 { - height: 100%; - width: 100% -} - -.childContainer__3e3b0 { - align-items: center; - display: flex; - inset-inline-start: 50%; - justify-content: center; - pointer-events: none; - position: absolute; - top: 50%; - transform: translate(-50%,-50%) -} - -.redesignIconChildContainer__3e3b0 { - height: 10px; - width: 10px -} - -.background__09691 { - height: 16px; - width: 16px -} - -.hubContainer__09691 { - align-items: center; - display: flex; - justify-content: center; - position: relative -} - -.verified__09691 { - color: var(--green-360) -} - -.staff__09691,.verified__09691 { -} - -.staff__09691 { - color: var(--icon-muted) -} - -.partnered__09691 { - color: var(--brand-500) -} - -.hub__09691 { -} - -.hub__09691>.icon__09691>circle { - fill: var(--background-mod-strong) -} - -.verifiedHub__09691 { -} - -.verifiedHub__09691>.icon__09691>circle { - fill: var(--green-360) -} - -.icon__09691 { - color: var(--white); - height: 100%; - width: 100% -} - -.drag__5c9fc { - display: none -} - -.root__5c9fc { - bottom: 0; - contain: strict; - position: absolute; - top: 0; - inset-inline: 0; - pointer-events: all -} - -.enter__5c9fc,.enterReducedMotion__5c9fc,.exit__5c9fc,.exitReducedMotion__5c9fc { - transition-duration: var(--custom-full-screen-layer-animation-duration); - transition-property: transform,opacity; - transition-timing-function: ease-in-out -} - -.enter__5c9fc { - transform: scale(1.1) -} - -.enter__5c9fc,.enterReducedMotion__5c9fc { - opacity: 0 -} - -.exit__5c9fc { - transform: scale(1) -} - -.exit__5c9fc,.exitReducedMotion__5c9fc { - opacity: 1 -} - -.enterActive__5c9fc,.enterDone__5c9fc { - opacity: 1; - transform: scale(1) -} - -.enterActiveReducedMotion__5c9fc,.enterDoneReducedMotion__5c9fc { - opacity: 1 -} - -.exitActive__5c9fc,.exitDone__5c9fc { - opacity: 0; - transform: scale(1.1) -} - -.exitActiveReducedMotion__5c9fc,.exitDoneReducedMotion__5c9fc { - opacity: 0 -} - -.container_c2b141 { - align-items: center; - display: flex; - flex-direction: column -} - -.keybind_c2b141 { - font-size: 13px; - font-weight: var(--font-weight-semibold); - margin-top: 8px; - text-align: center -} - -.closeButton_c2b141 { - align-items: center; - border-radius: 50%; - border-style: solid; - border-width: 2px; - cursor: pointer; - display: flex; - flex: 0 0 36px; - height: 36px; - justify-content: center; - width: 36px -} - -.closeButton_c2b141:active { - transform: translateY(1px) -} - -.closeButton_c2b141,.keybind_c2b141 { - color: var(--interactive-text-default) -} - -.closeButton_c2b141:hover { - background-color: var(--interactive-background-hover) -} - -.closeButton_c2b141:hover,.closeButton_c2b141:hover+.keybind_c2b141 { - color: var(--interactive-text-hover) -} - -.closeButton_c2b141:active { - background-color: var(--interactive-background-active) -} - -.closeButton_c2b141:active,.closeButton_c2b141:active+.keybind_c2b141 { - color: var(--interactive-text-active) -} - -.closeButtonSolid_c2b141 { - border: none; - flex: 0 0 24px; - height: 24px; - width: 24px -} - -.closeButtonSolid_c2b141:hover { - background-color: transparent -} - -.theme-dark .closeButtonBold_c2b141:hover { - background-color: hsl(var(--primary-300-hsl)/.3) -} - -.enable-forced-colors .closeButton_c2b141 { - background-color: ButtonFace; - border-color: ButtonText; - color: ButtonText -} - -.enable-forced-colors .closeButton_c2b141:active { - background-color: HighlightText; - border-color: Highlight; - color: Highlight -} - -.container__83f66 { - align-items: center; - background-color: #fff; - border-radius: var(--radius-md); - display: flex; - flex: 1; - gap: var(--space-12); - justify-content: space-between; - margin: 0 auto; - margin-bottom: var(--space-48); - padding: var(--space-8) var(--space-24); - padding-inline-end:var(--space-12);width: -moz-fit-content; - width: fit-content -} - -.themeResponsiveContainer__83f66 { - background-color: var(--background-mod-subtle); - width: 100% -} - -.text__83f66 { - color: var(--primary-600) -} - -.button__83f66 { - display: inline-block -} - -.highlight__83f66 { - color: var(--brand-500) -} - -.description__83f66 { - align-items: center; - display: inline-block; - flex-direction: row -} - -.responsiveText__83f66 { - color: var(--text-default) -} - -.wrapper__3add8 { - text-align: center -} - -.heading__3add8 { - margin-bottom: var(--space-32) -} - -.topPerksCards__3add8 { - display: flex; - flex-wrap: wrap; - gap: var(--space-16); - justify-content: center -} - -.topPerksCard__3add8 { - background-color: var(--card-background-default); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - box-sizing: border-box; - flex: 1 1 auto; - max-width: 362px; - min-width: 340px; - overflow: hidden; - padding: var(--space-32) var(--space-20); - position: relative; - width: 25% -} - -.full-motion .animatedTopPerksCard__3add8.animate__3add8 { - animation: fadeEffects__3add8 1.8s ease-in-out forwards -} - -.topPerksCardImageWrapper__3add8 { - height: 100%; - inset-inline-start: 0; - overflow: hidden; - position: absolute; - top: 0; - width: 100% -} - -.animatedTopPerksCard__3add8 { - border-radius: var(--radius-md); - height: 315px; - max-width: 480px -} - -.imageGradientOverlay__3add8 { - background: linear-gradient(to bottom,transparent 0,var(--card-background-default) 60%,var(--card-background-default) 100%); - border-radius: var(--radius-lg); - bottom: 0; - height: 300px; - inset-inline-start: 0; - position: absolute; - width: 100% -} - -.contentContainer__3add8 { - margin-top: var(--space-16); - overflow: hidden; - position: relative -} - -.buttonsContainer__3add8 { - display: flex; - flex-direction: row; - gap: 10px; - justify-content: center; - margin-top: var(--space-24); - overflow: hidden -} - -.button__3add8 { - flex: 1 1 0 -} - -.topPerksCardImage__3add8 { - height: 128px -} - -.topPerksCardHeading__3add8 { - margin-bottom: 4px -} - -.topPerksCardDescription__3add8 { - color: var(--text-default) -} - -.topPerksCardNew__3add8 { - inset-inline-end: var(--space-16); - position: absolute; - top: var(--space-16); - width: -moz-fit-content; - width: fit-content -} - -.topPerksCardLabelContainer__3add8 { - background-color: var(--background-base-low); - border-color: var(--border-muted); - border-radius: var(--radius-round); - border-style: solid; - border-width: 1px; - gap: 2px; - margin-top: var(--space-12); - padding: var(--space-4) 10px; - width: -moz-fit-content; - width: fit-content -} - -.labelsContainer__3add8,.topPerksCardLabelContainer__3add8 { - align-items: center; - display: flex; - flex-direction: row; - justify-self: center -} - -.labelsContainer__3add8 { - color: var(--text-feedback-positive); - gap: var(--space-4) -} - -.intObserver__3add8 { - bottom: 0; - height: 1px; - inset-inline-start: 0; - opacity: 0; - width: 1px -} - -.intObserver__3add8,.shineLine__3add8 { - pointer-events: none; - position: absolute -} - -.shineLine__3add8 { - background: linear-gradient(90deg,hsla(0,0%,100%,0) 0,hsla(0,0%,100%,.1) 50%,hsla(0,0%,100%,0)); - height: 300%; - inset-inline-start: -400%; - top: -100%; - transform: rotate(30deg); - width: 400px; - z-index: 1 -} - -.full-motion .shineLine__3add8 { - animation: shineEffect__3add8 1.5s ease-in-out -} - -@keyframes shineEffect__3add8 { - 0% { - inset-inline-start: -100% - } - - to { - inset-inline-start: 100% - } -} - -.container_ef0711 { - flex: 1; - flex-direction: column; - flex-wrap: wrap; - text-align: center -} - -.container_ef0711,.powerupsContainer_ef0711 { - display: flex; - justify-content: center -} - -.powerupsContainer_ef0711 { - align-items: center; - flex-wrap: wrap; - gap: var(--space-16); - margin-top: var(--space-32); - position: relative -} - -.powerupCard_ef0711 { - min-width: 500px -} - -.image_ef0711 { - position: absolute -} - -.gameServerImage_ef0711 { - width: 100% -} - -.contentContainer_ef0711 { - margin-top: 160px; - z-index: 1 -} - -.wrapper__4d7bb { - background: linear-gradient(45deg,var(--guild-boosting-blue),var(--guild-boosting-purple)); - border-radius: 8px; - isolation: isolate; - margin-bottom: 85px; - overflow: hidden; - padding: 50px 24px; - position: relative; - text-align: center -} - -.content__4d7bb { - position: relative; - z-index: 2 -} - -.heading__4d7bb { - margin-bottom: 32px -} - -.cards__4d7bb { - display: flex; - flex-wrap: wrap; - gap: 16px; - justify-content: center; - margin: 0 auto; - max-width: 768px -} - -.card__4d7bb { - align-items: center; - background-color: hsl(var(--primary-600-hsl)/.9); - border-radius: 8px; - box-sizing: border-box; - display: flex; - flex: 1 1 auto; - flex-direction: column; - max-width: 180px; - min-width: 165px; - padding: 24px 12px -} - -.theme-light .card__4d7bb { - background-color: hsl(var(--primary-100-hsl)/.9) -} - -.icon__4d7bb { - color: var(--guild-boosting-pink); - display: block; - height: 30px; - margin-bottom: 10px; - width: 30px -} - -.backgroundImages__4d7bb { - background: 0 100% url(/assets/5f5e47f0133f9ee5.svg) no-repeat,100% 100% url(/assets/383e4e1569554a59.svg) no-repeat; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 1 -} - -.backgroundImages__4d7bb,.iconImage__4d7bb { - filter: saturate(var(--saturation-factor,1)) -} - -.icon_f34534 { - background-clip: padding-box; - background-color: none; - background-position: 50%; - background-size: 100% 100%; - position: relative -} - -.icon_f34534 .guildIconBadge_f34534 { - bottom: -2px; - height: 14px; - inset-inline-end: -2px; - position: absolute; - width: 14px -} - -.iconInactive_f34534 { - border-radius: 50% -} - -.iconActiveMini_f34534 { - border-radius: 6px -} - -.iconActiveSmaller_f34534 { - border-radius: 7px -} - -.iconActiveSmall_f34534 { - border-radius: 9px -} - -.iconActiveMedium_f34534 { - border-radius: 12px -} - -.iconActiveLarge_f34534 { - border-radius: 15px -} - -.iconActiveLarger_f34534 { - border-radius: 16px -} - -.iconActiveXLarge_f34534 { - border-radius: 30px -} - -.iconSizeSmol_f34534 { - height: 16px; - width: 16px -} - -.iconSizeMini_f34534 { - height: 20px; - width: 20px -} - -.iconSizeSmaller_f34534 { - height: 24px; - width: 24px -} - -.iconSizeSmall_f34534 { - height: 30px; - width: 30px -} - -.iconSizeMedium_f34534 { - height: 40px; - width: 40px -} - -.iconSizeLarge_f34534 { - height: 50px; - width: 50px -} - -.iconSizeLarger_f34534 { - height: 64px; - width: 64px -} - -.iconSizeXLarge_f34534 { - height: 100px; - width: 100px -} - -.acronym_f34534 { - overflow: hidden; - white-space: nowrap; - width: 100% -} - -.noIcon_f34534 { - background-color: var(--background-base-lower); - color: var(--text-default); - text-align: center -} - -.noIcon_f34534.iconSizeSmol_f34534 { - line-height: 16px -} - -.noIcon_f34534.iconSizeMini_f34534 { - line-height: 20px -} - -.noIcon_f34534.iconSizeSmaller_f34534 { - line-height: 24px -} - -.noIcon_f34534.iconSizeSmall_f34534 { - line-height: 30px -} - -.noIcon_f34534.iconSizeMedium_f34534 { - line-height: 40px -} - -.noIcon_f34534.iconSizeLarge_f34534 { - line-height: 50px -} - -.noIcon_f34534.iconSizeLarger_f34534 { - line-height: 64px -} - -.noIcon_f34534.iconSizeXLarge_f34534 { - line-height: 100px -} - -.noAcronym_f34534 { - background-color: var(--background-mod-subtle) -} - -.guildBoostUnavailableNotice__0c507 { - align-self: center; - background-color: hsl(var(--white-hsl)/.1); - border: 2px solid var(--white); - border-radius: 9999px; - justify-self: center; - margin-bottom: 32px; - padding: 8px 16px; - width: -moz-fit-content; - width: fit-content -} - -.guildBoostUnavailableNotice__0c507.lightTheme__0c507 { - border: 2px solid var(--black-500) -} - -.guildBoostUnavailableNoticeText__0c507 { - font-size: 16px -} - -.guildStatus_f9f04c { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - margin-bottom: 50px; - text-align: center -} - -.guildIcon_f9f04c { - margin: 0 auto 10px -} - -.guildName_f9f04c { - margin-bottom: 4px -} - -.guildBoostCountWrapper_f9f04c { - color: var(--white); - margin-bottom: 20px -} - -.guildBoostCount_f9f04c { - align-items: center; - display: flex; - justify-content: center -} - -.guildBoostBadge_f9f04c { - color: inherit; - flex: 0 0 auto; - height: 16px; - margin-inline-end:4px;opacity: .6; - width: 16px -} - -.guildBoostBadgeWithBoosts_f9f04c { - color: var(--guild-boosting-pink); - opacity: 1 -} - -.guildStatusCopy_f9f04c { - color: inherit; - flex: 0 0 auto; - opacity: .6 -} - -.guildBoostCountCurrentUser_f9f04c { - color: inherit; - margin-top: 4px; - opacity: .6 -} - -.guildBoostCtas_f9f04c { - display: flex; - margin: 0 auto; - max-width: 470px; - width: 100% -} - -.guildBoostCta_f9f04c { - border-radius: 4px; - flex: 1 1 50% -} - -.guildBoostCtaBoostContent_f9f04c { - color: var(--brand-500) -} - -.guildBoostCta_f9f04c+.guildBoostCta_f9f04c { - margin-inline-start:10px} - -.guildBoostCtaBoostWrapper_f9f04c,.guildBoostCtaSecondary_f9f04c { - position: relative -} - -.full-motion .guildBoostCtaBoostWrapper_f9f04c,.full-motion .guildBoostCtaSecondary_f9f04c { - transition: transform .08s -} - -.full-motion .guildBoostCtaSecondary_f9f04c { - transition: background .08s,box-shadow .08s,transform .08s -} - -.guildBoostCtaBoost_f9f04c { - border-radius: 4px; - box-shadow: 0 1px 2px hsl(var(--primary-500-hsl)/.2),0 8px 16px hsl(var(--primary-500-hsl)/.3); - transition: box-shadow .08s; - width: 100% -} - -.full-motion .guildBoostCtaBoostWrapper_f9f04c:focus-within,.full-motion .guildBoostCtaBoostWrapper_f9f04c:hover,.full-motion .guildBoostCtaSecondary_f9f04c:focus,.full-motion .guildBoostCtaSecondary_f9f04c:hover { - transform: translateY(-3px) -} - -.full-motion .guildBoostCtaBoostWrapper_f9f04c:focus-within:after,.full-motion .guildBoostCtaBoostWrapper_f9f04c:hover:after,.full-motion .guildBoostCtaSecondary_f9f04c:focus:after,.full-motion .guildBoostCtaSecondary_f9f04c:hover:after { - content: ""; - height: 4px; - inset-inline-start: 0; - position: absolute; - top: 100%; - width: 100% -} - -.guildBoostCtaBoostWrapper_f9f04c:focus-within .guildBoostCtaBoost_f9f04c,.guildBoostCtaBoostWrapper_f9f04c:hover .guildBoostCtaBoost_f9f04c,.guildBoostCtaSecondary_f9f04c:focus,.guildBoostCtaSecondary_f9f04c:hover { - box-shadow: 0 1px 2px hsl(var(--primary-500-hsl)/.2),0 12px 24px hsl(var(--primary-500-hsl)/.4) -} - -.guildBoostCtaSecondary_f9f04c:focus,.guildBoostCtaSecondary_f9f04c:hover { - background-color: var(--opacity-white-4) -} - -.guildBoostCtaBoostShine_f9f04c { - color: hsl(var(--brand-500-hsl)/.1) -} - -.guildBoostCtaGiftContent_f9f04c { - display: inline-flex -} - -.guildBoostCtaGiftIcon_f9f04c { - height: 16px; - margin-inline-end:8px;width: 16px -} - -.FPContainer_f9f04c { - margin-bottom: 23px -} - -.wrapper__5b98e { - background-color: var(--background-mod-normal); - border-radius: 8px; - padding: 60px 50px -} - -.heading__5b98e { - margin-bottom: 30px; - text-align: center -} - -.list__5b98e { - border-top: 1px solid var(--border-muted) -} - -.listItem__5b98e { - border-bottom: 1px solid var(--border-muted) -} - -.questionWrapper__5b98e { - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - padding: 12px 0; - transition: color .1s -} - -.questionWrapper__5b98e:active,.questionWrapper__5b98e:hover,.questionWrapperExpanded__5b98e { - color: var(--interactive-text-active) -} - -.questionWrapperExpanded__5b98e { - padding-bottom: 8px -} - -.question__5b98e { - color: inherit; - flex: 1 1 auto; - padding-inline-end:12px;transition: color .1s -} - -.questionIcon__5b98e { - flex: 0 0 auto -} - -.answer__5b98e { - margin-bottom: 12px; - width: 80% -} - -@media (max-width: 700px) { - .answer__5b98e { - width:100% - } -} - -.wrapper_b4589b { - display: flex; - inset-inline-start: 0; - justify-content: center; - pointer-events: none; - position: absolute; - top: 100%; - width: 100%; - z-index: 1 -} - -.innerWrapper_b4589b { - align-items: center; - background-color: var(--background-mod-normal); - border-radius: 50px; - box-shadow: var(--shadow-border),var(--shadow-high); - box-sizing: border-box; - display: flex; - margin-bottom: 36px; - padding-block:12px;padding-inline:12px 22px;pointer-events: all; - position: relative; - width: 438px -} - -.innerWrapper_b4589b:after,.innerWrapper_b4589b:before { - border: 2px solid transparent; - border-radius: inherit; - box-sizing: border-box; - content: ""; - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.innerWrapper_b4589b:after { - border-color: var(--guild-boosting-blue); - -webkit-mask: linear-gradient(45deg,#000,transparent); - mask: linear-gradient(45deg,#000,transparent) -} - -.innerWrapper_b4589b:before { - border-color: var(--guild-boosting-purple) -} - -.theme-light .innerWrapper_b4589b { - background-color: var(--background-base-lower) -} - -.guildInfo_b4589b { - align-items: center; - display: flex; - flex: 1 1 auto; - overflow: hidden; - padding-inline-end:8px;text-overflow: ellipsis -} - -.guildIcon_b4589b { - background-color: var(--background-base-low); - flex: 0 0 auto; - margin-inline-end:14px} - -.ctaButton_b4589b { - border-radius: var(--custom-button-button-lg-height); - flex: 0 0 auto -} - -.guildName_b4589b { - flex: 1 1 auto; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.cannonWrapper_d00019 { - z-index: 1 -} - -.cannon_d00019,.cannonWrapper_d00019 { - height: 100%; - position: absolute; - width: 100% -} - -.confettiTriggerWrapper_d00019 { - cursor: pointer; - height: 100%; - position: relative; - width: 100%; - z-index: 2 -} - -.confettiTrigger_d00019 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.progressBarMarker__1f1cb { - align-items: center; - background-color: var(--opacity-white-40); - border-radius: 100%; - display: flex; - flex-direction: column; - height: var(--custom-guild-boosting-marketing-progress-bar-marker-marker-dimensions); - isolation: isolate; - justify-content: center; - position: absolute; - transform: translateX(-50%); - width: var(--custom-guild-boosting-marketing-progress-bar-marker-marker-dimensions); - z-index: 2 -} - -.progressBarMarker__1f1cb:after { - content: ""; - height: 50px; - inset-inline-start: 50%; - position: absolute; - top: 50%; - transform: translate(-50%,-50%); - width: 50px; - z-index: 1 -} - -.progressBarMarker__1f1cb.progressBarMarkerCurrent__1f1cb { - background-color: transparent -} - -.progressBarMarkerCurrent__1f1cb .progressBarMarkerIndicator__1f1cb { - box-shadow: 0 5px 11px rgba(0,0,0,.25) -} - -.progressBarMarkerUnlocked__1f1cb .progressBarMarkerIndicator__1f1cb { - background: var(--guild-boosting-pink); - border-radius: 100%; - content: ""; - height: var(--custom-guild-boosting-marketing-progress-bar-marker-marker-dimensions); - inset-inline-start: 50%; - position: absolute; - top: 50%; - transform: translate(-50%,-50%); - width: var(--custom-guild-boosting-marketing-progress-bar-marker-marker-dimensions); - z-index: 0 -} - -.progressBarMarkerUnlocked__1f1cb.progressBarMarkerLower__1f1cb .progressBarMarkerIndicator__1f1cb { - height: 20px; - inset-inline-start: 50%; - top: 50%; - transform: translate(-50%,-50%); - width: 20px; - z-index: 3 -} - -.progressBarMarkerLocked__1f1cb { - cursor: pointer -} - -.progressBarMarkerLabel__1f1cb { - color: var(--white); - inset-inline-start: 50%; - opacity: .4; - padding-top: 4px; - position: absolute; - top: 100%; - transform: translateX(-50%); - transition: opacity .125s; - white-space: nowrap -} - -.progressBarMarkerLabel__1f1cb:after { - content: ""; - height: 100%; - inset-inline-start: 50%; - padding: 3px; - position: absolute; - top: 50%; - transform: translate(-50%,-50%); - width: 100%; - z-index: 1 -} - -.progressBarMarkerUnlocked__1f1cb .progressBarMarkerLabel__1f1cb { - opacity: .8 -} - -.progressBarMarkerCurrent__1f1cb .progressBarMarkerLabel__1f1cb,.progressBarMarkerLocked__1f1cb:hover .progressBarMarkerLabel__1f1cb { - opacity: 1 -} - -.progressBarMarkerUnlockedIcon__1f1cb { - height: 16px; - inset-inline-end: 100%; - padding-inline-end:3px;position: absolute; - top: 50%; - transform: translateY(calc(-50% + 1px)); - width: 16px -} - -.boostedTierIconBackground__1f1cb { - background-color: var(--white); - border-radius: 20px; - height: 20px; - inset-inline-start: 50%; - opacity: .5; - position: absolute; - top: 50%; - transform: translate(-50%,-50%); - transition: opacity .125s; - width: 20px; - z-index: 2 -} - -.progressBarMarkerLocked__1f1cb:hover .boostedTierIconBackground__1f1cb { - opacity: 1 -} - -.boostedTierIcon__1f1cb { - color: var(--guild-boosting-pink); - height: 12px; - inset-inline-start: 50%; - position: absolute; - top: 50%; - transform: translate(-50%,-50%); - width: 12px; - z-index: 4 -} - -.progressBarMarkerUnlocked__1f1cb .boostedTierIcon__1f1cb { - color: var(--white) -} - -.progressBarMarkerCurrent__1f1cb .boostedTierIcon__1f1cb { - height: 16px; - width: 16px -} - -.tooltip__1f1cb { - text-align: center -} - -.disabledIndicator__1f1cb { - color: currentColor -} - -.progressBar_b28bb7 { - height: 54px; - isolation: isolate; - margin: 0 auto 48px; - max-width: 660px; - position: relative -} - -.progressBarScrubber_b28bb7 { - height: 8px; - inset-inline: calc(var(--custom-guild-boosting-marketing-progress-bar-marker-dimensions)/2 + var(--custom-guild-boosting-marketing-progress-bar-end-markers-margin)); - position: absolute; - top: 13px -} - -.progressBarTrack_b28bb7 { - background-color: var(--opacity-white-28); - width: 100%; - z-index: 0 -} - -.progressBarFill_b28bb7,.progressBarTrack_b28bb7 { - border-radius: 8px; - height: 100%; - position: absolute -} - -.progressBarFill_b28bb7 { - background-image: linear-gradient(90deg,var(--guild-boosting-purple) 0,var(--white) 100%); - inset-inline-start: 0; - z-index: 2 -} - -.progressBarCurrentProgressTooltip_b28bb7 { - margin-top: 100px -} - -.progressBarCurrentProgressTooltipMarker_b28bb7 { - border-radius: 100%; - height: 50px; - position: absolute; - top: 50%; - transform: translate(-50%,-50%); - width: 50px; - z-index: 3 -} - -.progressBarCurrentProgressTooltipHeading_b28bb7 { - font-weight: var(--font-weight-semibold); - margin-bottom: 2px -} - -.progressBarCurrentProgressTooltipSubheading_b28bb7 { - align-items: center; - display: flex -} - -.progressBarCurrentProgressTooltipSubheadingIcon_b28bb7 { - color: var(--guild-boosting-pink); - flex: 0 0 auto; - height: 16px; - margin-inline:-2px 4px;width: 16px -} - -.progressBarCurrentProgressTooltipSubheadingCopy_b28bb7 { - flex: 1 1 auto -} - -.container_bb1234 { - display: inline-block; - position: relative -} - -.sparkle_bb1234 { - position: absolute -} - -.sparkleStarTopRight_bb1234 { - bottom: calc(100% - 4px); - height: 12.75px; - inset-inline-start: calc(100% - 5px); - width: 12.75px -} - -.sparkleStarRight_bb1234 { - bottom: 3px; - inset-inline-start: calc(100% + 1.31px) -} - -.sparkleStarBottomLeft_bb1234,.sparkleStarRight_bb1234 { - height: 7.44px; - width: 7.44px -} - -.sparkleStarBottomLeft_bb1234 { - inset-inline-start: -1px; - top: calc(100% + .56px) -} - -.theme-dark .sparkle_bb1234 { - color: var(--white) -} - -.containerColored_bb1234 .sparkle_bb1234.sparkleStarTopRight_bb1234,.theme-light .sparkle_bb1234.sparkleStarTopRight_bb1234 { - color: #83ddc5 -} - -.containerColored_bb1234 .sparkle_bb1234.sparkleStarRight_bb1234,.theme-light .sparkle_bb1234.sparkleStarRight_bb1234 { - color: #e15be1 -} - -.containerColored_bb1234 .sparkle_bb1234.sparkleStarBottomLeft_bb1234,.theme-light .sparkle_bb1234.sparkleStarBottomLeft_bb1234 { - color: var(--yellow-260) -} - -.tag_fe5e5d { - background-color: var(--brand-500); - font-size: 12px; - line-height: 16px; - padding: 0 6px -} - -.tag_fe5e5d.inheritBackgroundColor_fe5e5d { - background: inherit -} - -.tag_fe5e5d.inheritTextColor_fe5e5d { - color: inherit -} - -.tierCards_be48d0 { - display: flex; - flex-wrap: wrap; - gap: 22px; - justify-content: center -} - -.tierCard_be48d0 { - border-radius: var(--custom-guild-boosting-marketing-tier-cards-tier-card-border-radius); - box-shadow: var(--shadow-border),var(--shadow-high); - box-sizing: border-box; - flex: 0 0 auto; - padding: 24px 36px 30px; - position: relative; - width: 338px -} - -.tierCard_be48d0,.tierCardCurrentTier_be48d0 { - background-image: linear-gradient(45deg,var(--guild-boosting-blue) 0,var(--guild-boosting-purple) 100%) -} - -.tierCardCurrentTier_be48d0 { - border-radius: 8px; - inset-inline-start: 28px; - padding: 4px 8px; - position: absolute; - text-transform: uppercase; - top: 0; - transform: translateY(-50%) -} - -.tierCardHeader_be48d0 { - align-items: baseline; - display: flex; - gap: 8px; - margin-bottom: 12px -} - -.tierCardHeading_be48d0 { - flex: 0 0 auto -} - -.tierCardBoostRequirement_be48d0 { - flex: 0 0 auto; - opacity: .7 -} - -.tierCardList_be48d0 { - display: flex; - flex-direction: column; - gap: 12px -} - -.tierCardListItem_be48d0 { - align-items: center; - display: flex; - gap: 8px -} - -.tierCardListIcon_be48d0 { - flex: 0 0 auto; - height: 24px; - width: 24px -} - -.tierCardSparkleHighlight_be48d0 { - background-image: radial-gradient(100% 100% at center,#fff 0,hsla(0,0%,100%,0) 50%); - color: var(--white); - height: 80px; - position: absolute; - width: 1px -} - -.theme-light .tierCardSparkleHighlight_be48d0 { - background-image: radial-gradient(100% 100% at center,hsl(var(--guild-boosting-pink-hsl)/1) 0,hsl(var(--guild-boosting-pink-hsl)/0) 50%); - color: var(--guild-boosting-pink) -} - -.tierCardSparkleHighlightTopRight_be48d0 { - inset-inline-end: 0; - top: 30px -} - -.tierCardSparkleHighlightBottomLeft_be48d0 { - bottom: 30px; - inset-inline-start: 0 -} - -.tierCardBorderHighlight_be48d0 { - border-radius: var(--custom-guild-boosting-marketing-tier-cards-tier-card-border-radius); - box-sizing: border-box; - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.tierCardBorderHighlightTopRight_be48d0 { - border: 1px solid var(--guild-boosting-blue); - -webkit-mask: radial-gradient(100% 100% at top right,#fff 0,hsla(0,0%,100%,0) 100%); - mask: radial-gradient(100% 100% at top right,#fff 0,hsla(0,0%,100%,0) 100%) -} - -.tierCardBorderHighlightBottomLeft_be48d0 { - border: 1px solid var(--guild-boosting-purple); - -webkit-mask: radial-gradient(100% 100% at bottom left,#fff 0,hsla(0,0%,100%,0) 100%); - mask: radial-gradient(100% 100% at bottom left,#fff 0,hsla(0,0%,100%,0) 100%) -} - -.tierCardSparkle1_be48d0 { - inset-inline-start: 0; - position: absolute; - top: 50%; - transform: translate(-50%,-50%) -} - -.tierCardSparkle2_be48d0 { - inset-inline-start: 11px; - position: absolute; - top: 10% -} - -.tierCardSparkle3_be48d0 { - inset-inline-end: 21px; - position: absolute; - top: -20px -} - -.tierCardPerkRow_be48d0 { - align-items: flex-start; - display: flex; - flex-direction: row -} - -.tierCardNewFeatureBadge_be48d0 { - align-self: center; - background: #fff; - border-radius: 8px; - color: var(--premium-tier-2-purple-for-gradients); - margin-inline-start:8px} - -.heading__686cf { - margin-bottom: 82px; - text-align: center -} - -.tableWrapper__686cf { - position: relative -} - -.table__686cf { - text-align: center; - width: 100% -} - -.columnHeading__686cf,.tableCell__686cf { - border-bottom: 1px solid var(--opacity-white-16) -} - -.theme-light .columnHeading__686cf,.theme-light .tableCell__686cf { - border-bottom-color: var(--opacity-black-16) -} - -.columnHeading__686cf { - padding-bottom: 16px -} - -.tableCell__686cf { - box-sizing: border-box; - padding: 22px 0; - vertical-align: middle; - width: 20% -} - -.tableCellWrapper__686cf { - padding: 0 -} - -.tableCellInner__686cf { - border-bottom: none; - width: 100% -} - -.tableRowHeading__686cf { - text-align: start -} - -.booleanValueIcon__686cf { - color: var(--icon-muted); - display: block; - margin: 0 auto -} - -.booleanValueTrue__686cf { - color: var(--text-strong) -} - -.recommendedTierHighlight__686cf { - bottom: -16px; - pointer-events: none; - position: absolute; - top: -38px; - width: 20% -} - -.recommendedTierHighlight__686cf:after,.recommendedTierHighlight__686cf:before { - border: 2px solid transparent; - border-radius: 16px; - box-sizing: border-box; - content: ""; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 1 -} - -.recommendedTierHighlight__686cf:after { - border-color: var(--guild-boosting-blue); - -webkit-mask: linear-gradient(70deg,#000,transparent); - mask: linear-gradient(70deg,#000,transparent) -} - -.recommendedTierHighlight__686cf:before { - border-color: var(--guild-boosting-purple) -} - -.recommendedTierHighlightTier2__686cf { - inset-inline-end: 20% -} - -.recommendedTierHighlightTier3__686cf { - inset-inline-end: 0 -} - -.recommendedTierHighlightTag__686cf { - background-image: linear-gradient(30deg,var(--guild-boosting-blue),var(--guild-boosting-purple)); - border-radius: 8px; - inset-inline-start: 50%; - padding: 4px 8px; - position: absolute; - text-transform: uppercase; - top: 1px; - transform: translate(-50%,-50%); - z-index: 2 -} - -.perkPreviewImage__686cf { - background-color: var(--background-mod-normal); - border-radius: 8px; - box-shadow: var(--shadow-border),var(--shadow-high); - display: block; - height: 168px; - width: 280px -} - -.headerWave__1dfae { - bottom: -1px; - color: var(--background-base-low); - max-height: 400px -} - -.bodyWave__1dfae,.headerWave__1dfae { - display: block; - inset-inline-start: 50%; - min-width: 1000px; - position: absolute; - transform: translateX(-50%); - width: 100%; - z-index: 1 -} - -.bodyWave__1dfae { - max-height: 1200px; - top: 10% -} - -.bodyWaveGradientStop1__1dfae { - stop-color: var(--background-base-low) -} - -.bodyWaveGradientStop2__1dfae { - stop-color: var(--background-mod-normal) -} - -.bodyWaveGradientStop3__1dfae { - stop-color: var(--background-base-low) -} - -.closeIconWrapper__6fd0e { - margin: 0 auto; - max-width: 1060px; - mix-blend-mode: screen; - position: relative; - z-index: 2 -} - -.theme-light .closeIconWrapper__6fd0e { - mix-blend-mode: multiply -} - -.closeIcon__6fd0e { - inset-inline-end: 24px; - position: absolute; - top: 48px; - z-index: 2 -} - -.scroller__6fd0e { - background: var(--background-base-low); - height: 100%; - position: relative; - z-index: 1 -} - -.content__6fd0e { - margin: 0 auto; - max-width: 1060px -} - -.headerContentWrapper__6fd0e { - padding: 100px 32px 32px; - position: relative; - z-index: 2 -} - -.header__6fd0e { - background: center 15%/90% auto url(/assets/ea2ad3ad1bfd2975.svg) no-repeat,linear-gradient(359.37deg,rgba(0,0,0,.5) 12.68%,hsla(0,0%,100%,0) 50.4%),linear-gradient(159.15deg,var(--premium-tier-0-header-gradient-1) 8.49%,var(--premium-tier-0-header-gradient-2) 32.21%,var(--premium-tier-0-header-gradient-3) 42.9%,var(--premium-tier-0-header-gradient-4) 66.06%,var(--premium-tier-0-header-gradient-5) 71.4%); - position: relative -} - -.theme-light .header__6fd0e { - background: center 15%/90% auto url(/assets/ea2ad3ad1bfd2975.svg) no-repeat,linear-gradient(359.37deg,hsla(0,0%,100%,.5) 12.68%,hsla(0,0%,100%,0) 50.4%),linear-gradient(159.15deg,var(--premium-tier-0-header-gradient-1) 8.49%,var(--premium-tier-0-header-gradient-2) 32.21%,var(--premium-tier-0-header-gradient-3) 42.9%,var(--premium-tier-0-header-gradient-4) 66.06%,var(--premium-tier-0-header-gradient-5) 71.4%) -} - -.heading__6fd0e { - margin: 0 auto 48px; - max-width: 960px; - text-align: center -} - -.middleBodyContentWrapper__6fd0e { - display: flex; - flex-direction: column; - gap: var(--space-64); - padding: 32px -} - -.lowerBody__6fd0e { - position: relative -} - -.lowerBodyBackgroundImage__6fd0e { - background: right 285px no-repeat url(/assets/32b48278e7e6c908.svg); - filter: saturate(var(--saturation-factor,1)); - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100%; - z-index: 2 -} - -@media (max-width: 1430px) { - .lowerBodyBackgroundImage__6fd0e { - display:none - } -} - -.lowerBodyContentWrapper__6fd0e { - padding: 32px; - position: relative; - z-index: 2 -} - -.persistentCtaSpacer__6fd0e { - height: 120px -} - -.tierComparisonTable__6fd0e { - margin-bottom: 64px -} - -.wrapper__3b770 { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-32); - margin: var(--space-80) 0; - width: 100% -} - -.heading__3b770 { - text-align: center -} - -.cards__3b770 { - display: flex; - gap: var(--space-24); - width: 100% -} - -.card__3b770 { - align-items: center; - background: var(--background-base-low); - border: 1px solid var(--border-subtle); - border-radius: 16px; - box-shadow: var(--shadow-low); - display: flex; - flex: 1 1 0; - flex-direction: column; - gap: var(--space-12); - justify-content: center; - min-width: 0; - padding: var(--space-24) -} - -.icon__3b770 { - color: var(--icon-muted); - display: block; - height: 48px; - width: 48px -} - -.iconImage__3b770 { - filter: saturate(var(--saturation-factor,1)) -} - -.description__3b770 { - text-align: center -} - -@media (max-width: 768px) { - .cards__3b770 { - flex-wrap:wrap - } - - .card__3b770 { - flex: 1 1 calc(50% - var(--space-12)); - min-width: 200px - } -} - -@media (max-width: 500px) { - .cards__3b770 { - flex-direction:column - } - - .card__3b770 { - flex: 1 1 auto - } -} - -.container__9dbc9 { - gap: var(--space-24); - margin-bottom: var(--space-80); - text-align: center -} - -.container__9dbc9,.serverSection__9dbc9 { - align-items: center; - display: flex; - flex-direction: column -} - -.serverSection__9dbc9 { - gap: var(--space-12) -} - -.guildIcon__9dbc9 { - border: 1px solid hsla(0,0%,100%,.16); - border-radius: 19px; - box-shadow: 0 1.6px 6.4px 0 rgba(0,0,0,.14); - height: 70px; - width: 70px -} - -.theme-light .guildIcon__9dbc9 { - border-color: rgba(0,0,0,.1) -} - -.guildName__9dbc9 { - color: var(--white) -} - -.theme-light .guildName__9dbc9 { - color: var(--text-default) -} - -.serverInfo__9dbc9 { - flex-direction: column; - gap: var(--space-4) -} - -.guildBoostCount__9dbc9,.serverInfo__9dbc9 { - align-items: center; - display: flex -} - -.guildBoostCount__9dbc9 { - gap: 2px -} - -.guildBoostBadge__9dbc9 { - color: var(--white); - flex: 0 0 auto; - height: 18px; - opacity: .6; - width: 18px -} - -.guildBoostBadgeWithBoosts__9dbc9 { - color: var(--guild-boosting-pink); - opacity: 1 -} - -.guildStatusCopy__9dbc9 { - color: var(--white) -} - -.theme-light .guildStatusCopy__9dbc9 { - color: var(--text-default) -} - -.guildBoostCountCurrentUser__9dbc9 { - color: var(--white); - margin-top: var(--space-4); - opacity: .6 -} - -.theme-light .guildBoostCountCurrentUser__9dbc9 { - color: var(--text-muted); - opacity: 1 -} - -.heroSection__9dbc9 { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-40) -} - -.heading__9dbc9 { - color: var(--text-strong); - font-family: var(--font-display-marketing-header); - font-size: 42px; - font-style: normal; - font-weight: 700; - letter-spacing: -1.26px; - line-height: 110%; - margin: 0; - max-width: 770px; - text-align: center; - text-shadow: 0 3px 0 rgba(0,0,0,.1); - text-transform: uppercase -} - -.theme-light .heading__9dbc9 { - text-shadow: none -} - -.guildBoostCtas__9dbc9 { - display: flex; - gap: var(--space-12); - justify-content: center -} - -.guildBoostCtaPrimary__9dbc9 { - flex: 0 0 auto -} - -.wrapper_bb27fe { - align-items: flex-start; - display: flex; - flex-direction: column; - justify-content: center -} - -.heading_bb27fe { - text-align: center; - width: 100% -} - -.list_bb27fe { - width: 100% -} - -.list_bb27fe,.listItem_bb27fe { - display: flex; - flex-direction: column; - gap: var(--space-4) -} - -.listItem_bb27fe { - align-items: flex-start; - align-self: stretch; - background: var(--background-base-low); - border: 1px solid var(--border-subtle); - border-radius: 8px; - box-shadow: var(--shadow-low); - padding: var(--space-12) -} - -.listItemExpanded_bb27fe { - background: var(--background-surface-highest); - border: 1px solid var(--border-strong) -} - -.questionWrapper_bb27fe { - align-items: center; - align-self: stretch; - display: flex; - gap: var(--space-12) -} - -.question_bb27fe { - flex: 1 1 auto -} - -.questionIcon_bb27fe { - flex: 0 0 auto -} - -.full-motion .questionIcon_bb27fe { - transition: transform .2s ease-in-out -} - -.answer_bb27fe { - align-self: stretch; - padding-top: var(--space-8) -} - -.closeButtonWrapper__00843 { - position: relative; - z-index: 2 -} - -.closeButton__00843 { - align-items: center; - background-color: rgba(16,16,18,.5); - border-radius: 12px; - color: var(--primary-330); - cursor: pointer; - display: flex; - height: 40px; - inset-inline-end: var(--space-24); - justify-content: center; - position: absolute; - top: var(--space-24); - width: 40px; - z-index: 2 -} - -.closeButton__00843:hover { - background-color: rgba(16,16,18,.7); - color: var(--primary-230) -} - -.theme-light .closeButton__00843 { - background-color: rgba(0,0,0,.08); - color: var(--primary-500) -} - -.theme-light .closeButton__00843:hover { - background-color: rgba(0,0,0,.12); - color: var(--primary-600) -} - -.scroller__00843 { - background: var(--background-base-lowest); - height: 100%; - position: relative; - transform: translateZ(0); - will-change: scroll-position; - z-index: 1 -} - -.content__00843 { - margin: 0 auto; - max-width: 1224px -} - -.headerContentWrapper__00843 { - isolation: isolate; - padding: var(--space-64) var(--space-32) var(--space-32); - position: relative; - z-index: 2 -} - -.header__00843 { - position: relative -} - -.headerBackground__00843 { - position: absolute; - top: 0; - inset-inline: 0; - min-height: 1280px; - overflow: hidden; - pointer-events: none -} - -.headerGradient__00843 { - background: radial-gradient(ellipse 100% 70% at 50% 0,rgba(156,89,209,.6) 0,rgba(88,47,140,.3) 50%,transparent 80%),var(--background-base-lowest); - inset: 0; - position: absolute -} - -.theme-light .headerGradient__00843 { - background: radial-gradient(ellipse 100% 70% at 50% 0,rgba(209,153,237,.5) 0,rgba(176,126,209,.25) 50%,transparent 80%),var(--background-base-lowest) -} - -.headerBackground__00843 img,.headerBackground__00843 video { - height: auto; - -o-object-fit: cover; - object-fit: cover; - position: relative; - width: 100% -} - -.middleBodyContentWrapper__00843 { - display: flex; - flex-direction: column; - gap: var(--space-64); - isolation: isolate; - padding: var(--space-32); - position: relative; - z-index: 2 -} - -.lowerBody__00843 { - position: relative -} - -.lowerBodyContentWrapper__00843 { - padding: var(--space-32); - position: relative; - z-index: 2 -} - -.persistentCtaSpacer__00843 { - height: 120px -} - -.perksTable__00843 { - margin-bottom: var(--space-80) -} - -.wrapper_c50892 { - display: flex; - gap: var(--space-24); - width: 100% -} - -.card_c50892 { - background: var(--background-base-lowest); - border: 1px solid; - border-radius: 16px; - box-shadow: 0 1px 4px 0 rgba(0,0,0,.14); - display: flex; - flex: 1 1 0; - flex-direction: column; - min-width: 0; - overflow: hidden; - transform: translateZ(0) -} - -.cardActive_c50892 { - border-color: hsla(240,4%,61%,.04) -} - -.cardInactive_c50892 { - border-color: var(--border-subtle) -} - -.cardHeader_c50892 { - height: 64px; - position: relative -} - -.progressBar_c50892 { - height: 8px; - position: absolute; - top: 40px; - inset-inline: 0 -} - -.progressBarStart_c50892 { - border-end-start-radius: 100px; - border-start-start-radius: 100px; - inset-inline-start: 40px -} - -.progressBarEnd_c50892 { - border-end-end-radius: 100px; - border-start-end-radius: 100px; - inset-inline-end: 24px -} - -.progressBarInactive_c50892 { - background: #474464 -} - -.progressBarActive_c50892 { - background: linear-gradient(90deg,#ff4ebb,rgba(255,78,187,.4)); - box-shadow: inset 0 0 4px 0 rgba(255,128,206,.3) -} - -.progressBarActive_c50892.progressBarStart_c50892 { - inset-inline-start: 64px -} - -.gemContainer_c50892 { - align-items: center; - border: 5px solid; - border-radius: 50%; - display: flex; - height: 40px; - inset-inline-start: 24px; - justify-content: center; - position: absolute; - top: 24px; - width: 40px -} - -.gemContainerActive_c50892.gemContainerLevel1_c50892 { - background: var(--guild-boosting-pink-refresh); - border-color: #202024 -} - -.gemContainerLevel1_c50892:not(.gemContainerActive_c50892) { - background: #474464; - border-color: #202024 -} - -.gemContainerLevel2_c50892 { - background: #474464; - border-color: #211e40 -} - -.gemContainerActive_c50892.gemContainerLevel2_c50892 { - background: var(--guild-boosting-pink-refresh); - border-color: #202024 -} - -.gemContainerLevel3_c50892 { - background: #474464; - border-color: #201d3d -} - -.gemContainerActive_c50892.gemContainerLevel3_c50892 { - background: var(--guild-boosting-pink-refresh); - border-color: #202024 -} - -.theme-light .progressBarInactive_c50892 { - background: #fff -} - -.theme-light .gemContainerActive_c50892.gemContainerLevel1_c50892 { - border-color: #d8d6de -} - -.theme-light .gemContainerLevel1_c50892:not(.gemContainerActive_c50892) { - background: #fff; - border-color: #d8d6de -} - -.theme-light .gemContainerLevel2_c50892 { - background: #fff; - border-color: #d4d1dc -} - -.theme-light .gemContainerActive_c50892.gemContainerLevel2_c50892 { - background: var(--guild-boosting-pink-refresh); - border-color: #d8d6de -} - -.theme-light .gemContainerLevel3_c50892 { - background: #fff; - border-color: #d5d2dc -} - -.theme-light .gemContainerActive_c50892.gemContainerLevel3_c50892 { - background: var(--guild-boosting-pink-refresh); - border-color: #d8d6de -} - -.cardBody_c50892 { - display: flex; - flex-direction: column; - gap: var(--space-12); - padding: var(--space-24) -} - -.cardTitleRow_c50892 { - align-items: center; - display: flex; - justify-content: space-between -} - -.cardTitle_c50892 { - flex-shrink: 0 -} - -.boostsBadge_c50892 { - align-items: center; - background: var(--background-mod-subtle); - border-radius: var(--radius-sm); - color: #aaaab1; - display: flex; - gap: var(--space-4); - height: 30px; - padding: 0 var(--space-8) -} - -.theme-light .boostsBadge_c50892 { - color: var(--text-muted) -} - -.cardTitleInactive_c50892 { - color: #94959c -} - -.theme-light .cardTitleInactive_c50892 { - color: var(--text-muted) -} - -.perkRowInactive_c50892 { - color: #94959c -} - -.theme-light .perkRowInactive_c50892 { - color: var(--text-muted) -} - -.andMoreText_c50892 { - color: #94959c -} - -.theme-light .andMoreText_c50892 { - color: var(--text-muted) -} - -.theme-light .card_c50892 { - background: var(--background-base-lowest) -} - -.perksContainer_c50892 { - display: flex; - flex-direction: column; - gap: var(--space-12) -} - -.perkRow_c50892 { - align-items: center; - display: flex; - gap: var(--space-8) -} - -.perkIcon_c50892 { - flex-shrink: 0; - height: 20px; - width: 20px -} - -@media (max-width: 900px) { - .wrapper_c50892 { - flex-direction:column - } - - .card_c50892 { - flex: 1 1 auto - } -} - -.container__52d93 { - align-items: center; - background-color: rgba(26,26,30,.5); - border-radius: 16px; - display: flex; - gap: var(--space-24); - justify-content: center; - margin-block-end:var(--space-48);margin-block-start: 0; - margin-inline:auto;padding-block-end:var(--space-12);padding-block-start: var(--space-12); - padding-inline-end:var(--space-12);padding-inline-start: var(--space-24); - width: -moz-fit-content; - width: fit-content -} - -.theme-light .container__52d93 { - background-color: hsla(0,0%,100%,.5) -} - -.description__52d93 { - color: hsla(0,0%,100%,.6) -} - -.theme-light .description__52d93 { - color: var(--text-muted) -} - -.highlight__52d93 { - color: var(--white) -} - -.theme-light .highlight__52d93 { - color: var(--text-default) -} - -.wrapper__65e57 { - gap: 32px; - z-index: 2 -} - -.headerSection__65e57,.wrapper__65e57 { - align-items: center; - display: flex; - flex-direction: column; - position: relative; - width: 100% -} - -.headerSection__65e57 { - gap: 4px; - text-align: center; - z-index: 1 -} - -.heading__65e57 { - color: var(--text-default); - font-size: 32px; - line-height: 40px -} - -.subheading__65e57 { - color: #aaaab1; - font-size: 16px; - line-height: 20px -} - -.theme-light .subheading__65e57 { - color: var(--text-muted) -} - -.subheading__65e57 a { - color: #5197ed; - text-decoration: none -} - -.subheading__65e57 a:hover { - text-decoration: underline -} - -.cardsContainer__65e57 { - display: flex; - gap: 24px; - width: 100% -} - -.card__65e57 { - align-items: center; - -webkit-backdrop-filter: blur(40px); - backdrop-filter: blur(40px); - background: radial-gradient(ellipse 355px 208px at 50% -31.5px,rgba(86,22,138,.5) 0,rgba(86,22,138,0) 100%),hsla(240,4%,61%,.08); - border: 1px solid hsla(240,4%,61%,.12); - border-radius: 16px; - box-shadow: 0 1px 4px 0 rgba(0,0,0,.14); - display: flex; - flex: 1 1 0; - flex-direction: column; - gap: 24px; - min-width: 0; - overflow: hidden; - padding: 24px; - position: relative; - transform: translateZ(0); - transition: box-shadow .2s ease-out; - will-change: transform -} - -.card__65e57:hover { - box-shadow: 0 8px 24px 0 rgba(0,0,0,.24) -} - -.full-motion .card__65e57 { - transition: box-shadow .2s ease-out,transform .2s ease-out -} - -.full-motion .card__65e57:hover { - transform: translate3d(0,-4px,0) -} - -.cardVisible__65e57 { - opacity: 1 -} - -.intObserver__65e57 { - bottom: 0; - height: 1px; - inset-inline-start: 0; - opacity: 0; - pointer-events: none; - position: absolute; - width: 1px -} - -.illustrationWrapper__65e57 { - align-items: center; - display: flex; - height: 140px; - justify-content: center; - overflow: hidden; - width: 250px -} - -.illustration__65e57 { - -o-object-fit: contain; - object-fit: contain; - transform-origin: center center -} - -.animatedAsset__65e57,.illustration__65e57 { - height: 100%; - width: 100% -} - -.content__65e57 { - gap: 24px; - position: relative; - z-index: 1 -} - -.content__65e57,.textContent__65e57 { - align-items: center; - display: flex; - flex-direction: column; - width: 100% -} - -.textContent__65e57 { - gap: 4px; - text-align: center -} - -.title__65e57 { - color: #efeff0; - font-size: 20px; - line-height: 24px -} - -.theme-light .title__65e57 { - color: var(--text-default) -} - -.description__65e57 { - color: #aaaab1; - font-size: 16px; - line-height: 20px -} - -.theme-light .description__65e57 { - color: var(--text-muted) -} - -.badges__65e57 { - gap: 12px -} - -.badge__65e57,.badges__65e57 { - align-items: center; - display: flex -} - -.badge__65e57 { - background: hsla(240,4%,61%,.08); - border: 1px solid hsla(240,4%,61%,.04); - border-radius: 8px; - gap: 4px; - justify-content: center; - padding: 6px 12px -} - -.badgeText__65e57 { - color: #94959c; - font-size: 14px; - line-height: 18px -} - -.theme-light .badgeText__65e57 { - color: var(--text-muted) -} - -.enabledBadge__65e57 { - color: #53ac66; - font-size: 14px; - line-height: 18px -} - -.buttonsContainer__65e57 { - bottom: 24px; - display: flex; - flex-direction: row; - gap: 8px; - inset-inline-end: 24px; - inset-inline-start: 24px; - pointer-events: none; - position: absolute -} - -.card__65e57:hover .buttonsContainer__65e57 { - pointer-events: auto -} - -.button__65e57 { - flex: 1 1 0 -} - -.newBadge__65e57 { - inset-inline-end: 16px; - position: absolute; - top: 16px -} - -@media (max-width: 900px) { - .cardsContainer__65e57 { - flex-direction:column - } - - .card__65e57 { - flex: 1 1 auto - } -} - -.wrapper__0a9d0 { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-48); - width: 100% -} - -.heading__0a9d0 { - text-align: center -} - -.tableWrapper__0a9d0 { - position: relative; - width: 100% -} - -.table__0a9d0 { - flex-direction: column; - width: 100% -} - -.row__0a9d0,.table__0a9d0 { - display: flex -} - -.row__0a9d0 { - align-items: center; - border-bottom: 1px solid var(--border-normal); - padding: var(--space-16) 0 -} - -.lastRow__0a9d0 { - border-bottom: none -} - -.headerRow__0a9d0 { - align-items: flex-start -} - -.cell__0a9d0 { - flex: 1 1 0; - min-width: 0; - padding: 0 var(--space-16) -} - -.perkCell__0a9d0 { - text-align: start -} - -.tierCell__0a9d0 { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - text-align: center -} - -.checkIcon__0a9d0 { - color: var(--text-strong); - display: block -} - -.xIcon__0a9d0 { - color: var(--interactive-muted); - display: block -} - -.recommendedTierHighlight__0a9d0 { - border: 2px solid var(--guild-boosting-pink-refresh); - border-radius: var(--radius-lg); - bottom: -20px; - pointer-events: none; - position: absolute; - top: -20px; - width: 20% -} - -.recommendedTierHighlightTier2__0a9d0 { - inset-inline-end: 20% -} - -.recommendedTierHighlightTier3__0a9d0 { - inset-inline-end: 0 -} - -.recommendedTierHighlightTag__0a9d0 { - background: var(--guild-boosting-pink-refresh); - border-radius: var(--radius-sm); - inset-inline-start: 50%; - padding: var(--space-4) var(--space-8); - position: absolute; - text-transform: uppercase; - top: 0; - transform: translate(-50%,-50%); - white-space: nowrap -} - -@media (max-width: 768px) { - .cell__0a9d0 { - padding:0 var(--space-8) - } -} - -@media (max-width: 600px) { - .wrapper__0a9d0 { - gap:var(--space-24) - } - - .row__0a9d0 { - padding: var(--space-12) 0 - } - - .cell__0a9d0 { - padding: 0 var(--space-4) - } - - .headerRow__0a9d0 .tierCell__0a9d0>:last-child { - display: none - } -} - -.wrapper__9ed0b { - inset-inline-start: 0; - isolation: isolate; - pointer-events: none; - position: absolute; - top: 100%; - width: 100%; - will-change: transform; - z-index: 10 -} - -.innerWrapper__9ed0b,.wrapper__9ed0b { - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - display: flex; - justify-content: center -} - -.innerWrapper__9ed0b { - align-items: center; - -webkit-backdrop-filter: blur(16px); - backdrop-filter: blur(16px); - background-color: hsla(0,0%,100%,.17); - border: 1px solid hsla(0,0%,100%,.16); - border-radius: 16px; - box-sizing: border-box; - gap: 40px; - margin-bottom: 36px; - padding: 12px; - pointer-events: all; - position: relative; - transform: translateZ(0) -} - -.theme-light .innerWrapper__9ed0b { - background-color: rgba(0,0,0,.08); - border-color: rgba(0,0,0,.1) -} - -.guildInfo__9ed0b { - align-items: center; - display: flex; - gap: 8px -} - -.guildIcon__9ed0b { - border: 1px solid hsla(0,0%,100%,.16); - border-radius: var(--radius-md); - box-shadow: var(--shadow-low); - flex: 0 0 auto -} - -.theme-light .guildIcon__9ed0b { - border-color: rgba(0,0,0,.1) -} - -.ctaButton__9ed0b { - flex: 0 0 auto -} - -.guildName__9ed0b { - color: var(--white); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.theme-light .guildName__9ed0b { - color: var(--primary-800) -} - -.premiumTooltipFooterBackground__7d7fe { - background: linear-gradient(90deg,#b473f5,#e292aa); - width: "100%" -} - -.tooltipBodyContainer__7d7fe { - padding: 8px 12px; - text-align: center -} - -.tooltipPremiumFooterContainer__7d7fe { - align-items: center; - background: linear-gradient(90deg,#b473f5,#e292aa); - border-radius: 0 0 5px 5px; - color: #fff; - display: flex; - justify-content: center -} - -.tooltipRemovePadding__7d7fe { - padding: 0 -} - -.gemIcon__7d7fe { - margin-top: 2px; - margin-inline-start:-4px} - -.tooltipPremiumFooterSegment__7d7fe { - align-items: center; - display: flex; - flex-basis: 0; - flex-grow: 1; - justify-content: center; - padding: 4px; - text-align: center -} - -.tooltipPremiumFooterTierSegment__7d7fe { - border-inline-end:1px solid;border-color: var(--background-surface-high) -} - -.clanBadgeContainer__7d7fe img { - margin: 0 -} - -.boostedGuildIconGem__97677 { - height: 12px; - transition: color .1s linear; - width: 12px -} - -:where(.boostedGuildIconGem__97677) { - height: 10px -} - -.iconBackgroundTierNone__97677,.iconBackgroundTierOne__97677,.iconBackgroundTierThree__97677,.iconBackgroundTierTwo__97677 { - color: var(--icon-muted) -} - -.iconTierNone__97677 { - color: var(--opacity-white-20) -} - -.iconTierOne__97677,.iconTierTwo__97677 { - color: var(--white) -} - -.iconTierThree__97677 { - color: var(--guild-boosting-pink) -} - -.guildIconContainer__85d16,.guildIconV2Container__85d16 { - display: flex; - justify-content: center; - min-width: 20px -} - -.guildBadge__85d16 .boostedGuildIconGem__85d16 { - transition: color .1s linear -} - -:where(.disableColor__85d16) { - color: var(--background-mod-muted) -} - -.tierTooltipTitle__85d16 { - font-weight: var(--font-weight-semibold) -} - -.boostedGuildIconGem__85d16 { - height: 10px -} - -.boostedGuildTierMutedIconWithVisibleBanner__85d16 { - color: var(--text-muted) -} - -.iconTierNone__85d16 { - color: var(--opacity-white-20) -} - -.boostedGuildTierIconBackgroundWithVisibleBanner__85d16 { - color: var(--interactive-text-active) -} - -.header__6e500 { - align-items: flex-start; - box-sizing: border-box; - display: flex; - flex-direction: column; - margin-bottom: 8px; - margin-top: 8px; - padding: 0 16px; - width: 100% -} - -.members__6e500 { - display: flex; - flex-wrap: wrap; - gap: 0 8px -} - -.memberCount__6e500 { - align-items: center; - display: flex; - gap: 4px -} - -.dot__6e500 { - background: var(--text-status-offline) -} - -.dot__6e500,.dotOnline__6e500 { - border-radius: 50%; - height: 8px; - width: 8px -} - -.dotOnline__6e500 { - background: var(--text-status-online) -} - -.established__6e500 { - display: flex -} - -.nameContainer__6e500 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px; - width: 100% -} - -.guildNameContainer__6e500 { - overflow: hidden -} - -.guildNameContainer__6e500:hover { - cursor: pointer -} - -.guildNameContainer__6e500:hover .guildName__6e500 { - text-decoration: underline -} - -.guildName__6e500 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.guildBadge__6e500 { - cursor: pointer; - flex-shrink: 0; - height: 16px; - width: 16px -} - -.container_d3846c { - border-radius: 16px; - box-shadow: var(--shadow-border),var(--shadow-high); - display: flex; - flex-direction: column; - overflow: hidden; - width: 300px -} - -.spinnerContainer_d3846c { - background: var(--background-surface-highest); - padding: 16px -} - -.chipletContainerInner__10651 { - align-items: center; - background: var(--background-mod-strong); - border-radius: 4px; - display: inline-flex; - line-height: 16px!important; - padding: 0 4px; - vertical-align: middle -} - -.chipletContainerInline__10651 { - margin-top: -2px -} - -.clickable__10651 { - cursor: pointer; - transition: background .1s ease-in-out -} - -.clickable__10651:hover { - background: var(--background-mod-muted) -} - -.text__10651 { - align-items: center; - display: inline-flex; - line-height: 16px!important; - max-width: 70px -} - -.badge__10651 { - margin-inline-end:2px;margin-top: 0 -} - -.tooltip__10651 { - background: var(--background-surface-highest); - border-radius: 8px -} - -.tooltip__10651,.tooltipContainer__10651 { - max-width: 360px; - padding: 0 -} - -.chipletContainerInner__10651.noTooltip__10651:hover { - background: var(--background-mod-strong) -} - -.tagText__10651 { - text-indent: 0; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.container__4bbc6 { - border-radius: inherit; - height: 100%; - inset-inline-start: 0; - overflow: hidden; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.fitInAccount__4bbc6 { - height: calc(100% + 2px); - inset-inline-start: 0; - top: -1px; - width: calc(100% + 1px) -} - -.fadeIn__4bbc6 { - animation: fadeIn__4bbc6 .12s ease-in forwards; - opacity: 0 -} - -@keyframes fadeIn__4bbc6 { - 0% { - opacity: 0 - } - - to { - opacity: 1 - } -} - -.img__4bbc6 { - height: 100%; - inset-inline-end: 0; - -o-object-position: right; - object-position: right; - pointer-events: none; - position: absolute; - transition: opacity .3s ease,transform .3s ease; - width: auto -} - -.img__4bbc6,.img__4bbc6.hover__4bbc6 { - opacity: .6 -} - -.img__4bbc6.selected__4bbc6 { - opacity: .9 -} - -.img__4bbc6.channel__4bbc6.hover__4bbc6 { - opacity: .4 -} - -.img__4bbc6.channel__4bbc6.selected__4bbc6 { - opacity: .9 -} - -.img__4bbc6.account__4bbc6,.img__4bbc6.account__4bbc6.hover__4bbc6 { - opacity: .4 -} - -.img__4bbc6.preview__4bbc6 { - opacity: .8 -} - -.img__4bbc6.mini_preview__4bbc6,.img__4bbc6.mini_preview__4bbc6.hover__4bbc6,.img__4bbc6.preview__4bbc6.hover__4bbc6 { - opacity: 1 -} - -.videoContainer__4bbc6 { - height: 100%; - overflow: hidden; - width: 100% -} - -.nameplatePreview_e144e0 { - align-items: center; - border-radius: var(--radius-sm); - display: flex; - height: 42px; - position: relative -} - -.nameplatePreview_e144e0.large_e144e0 { - height: 52px -} - -.nameplatePreview_e144e0.xlarge_e144e0 { - height: 62px -} - -.density-cozy .nameplatePreview_e144e0 { - height: 48px -} - -.density-cozy .nameplatePreview_e144e0.xsmall_e144e0 { - height: 26px -} - -.density-cozy .nameplatePreview_e144e0.small_e144e0 { - height: 30px -} - -.density-cozy .nameplatePreview_e144e0.large_e144e0 { - height: 58px -} - -.density-cozy .nameplatePreview_e144e0.xlarge_e144e0 { - height: 68px -} - -.density-default .nameplatePreview_e144e0 { - height: 42px -} - -.density-default .nameplatePreview_e144e0.xsmall_e144e0 { - height: 26px -} - -.density-default .nameplatePreview_e144e0.small_e144e0 { - height: 30px -} - -.density-default .nameplatePreview_e144e0.large_e144e0 { - height: 52px -} - -.density-default .nameplatePreview_e144e0.xlarge_e144e0 { - height: 62px -} - -.density-compact .nameplatePreview_e144e0 { - height: 38px -} - -.density-compact .nameplatePreview_e144e0.xsmall_e144e0 { - height: 26px -} - -.density-compact .nameplatePreview_e144e0.small_e144e0 { - height: 34px -} - -.density-compact .nameplatePreview_e144e0.large_e144e0 { - height: 48px -} - -.density-compact .nameplatePreview_e144e0.xlarge_e144e0 { - height: 58px -} - -.overlayContainer_e144e0 { - flex: 1; - height: 100%; - margin-block:0;margin-inline:8px 32px;position: relative -} - -.nameplatePreview_e144e0.large_e144e0 .overlayContainer_e144e0 { - margin-block:0;margin-inline:10px 38px} - -.nameplatePreview_e144e0.xlarge_e144e0 .overlayContainer_e144e0 { - margin-block:0;margin-inline:14px 44px} - -.avatarContainer_e144e0 { - align-items: center; - display: flex; - height: 100%; - max-width: 100%; - opacity: 0; - position: absolute; - transition: opacity .4s ease; - width: 100% -} - -.avatar_e144e0 { - margin-inline-end:12px} - -.avatarVisible_e144e0 { - opacity: 1 -} - -.placeholderUsername_e144e0 { - border-radius: 8px; - height: 14px; - opacity: .5; - width: 66% -} - -.nameplatePreview_e144e0.xsmall_e144e0 .placeholderUsername_e144e0 { - height: 8px -} - -.nameplatePreview_e144e0.small_e144e0 .placeholderUsername_e144e0 { - height: 10px -} - -.nameplatePreview_e144e0.large_e144e0 .placeholderUsername_e144e0 { - height: 16px -} - -.nameplatePreview_e144e0.xlarge_e144e0 .placeholderUsername_e144e0 { - height: 18px -} - -.theme-light .placeholderUsername_e144e0 { - background-color: #aaaab2 -} - -.theme-dark .placeholderUsername_e144e0 { - background-color: #706f74 -} - -.tagChiplet_e144e0 { - margin-inline-start:var(--space-4)} - -.inheritWidth_e144e0 { - width: inherit -} - -.container__6d099 { - align-items: center; - aspect-ratio: 1.1; - display: flex; - height: 100%; - justify-content: center; - position: relative -} - -.profileEffectShopPreview__6d099 { - border-radius: var(--radius-sm); - height: 80%; - inset-inline-start: 6%; - overflow: hidden; - position: absolute; - top: 10%; - transform-origin: top center; - width: auto -} - -.avatarDecorationPreview__6d099 { - filter: drop-shadow(0 2px 8px rgba(0,0,0,.15)); - inset-inline-end: 6%; - position: absolute; - transform: rotate(6deg) -} - -.twoItemBundle__6d099 .profileEffectShopPreview__6d099 { - aspect-ratio: .86; - transform: rotate(-6deg) -} - -.twoItemBundle__6d099 .avatarDecorationPreview__6d099 { - top: 22% -} - -.threeItemBundle__6d099 .profileEffectShopPreview__6d099 { - aspect-ratio: .8; - transform: rotate(-8deg) -} - -.threeItemBundle__6d099 .avatarDecorationPreview__6d099 { - top: 12% -} - -.threeItemBundle__6d099 .nameplatePreview__6d099 { - background-color: var(--background-base-low); - border-radius: var(--radius-sm); - bottom: 14%; - box-shadow: 0 2px 8px rgba(0,0,0,.15),0 1px 3px rgba(0,0,0,.1); - position: absolute; - width: 96% -} - -.theme-dark .avatar__6d099 { - background: #0c0e0e -} - -.theme-light .avatar__6d099 { - background: #a8a9aa -} - -.nameplatePlaceholderUserRow_e75aa6 { - align-items: center; - display: flex; - height: 42px; - padding: 0 8px; - width: 100% -} - -.nameplatePlaceholderAvatar_e75aa6,.nameplatePlaceholderBar_e75aa6 { - background-color: var(--background-mod-subtle) -} - -.nameplatePlaceholderBar_e75aa6 { - border-radius: 8px; - flex-grow: 1; - height: 14px; - margin-inline-start:12px} - -.smallRow_e75aa6 { - height: 28px -} - -.smallBar_e75aa6 { - height: 10px -} - -.nameplatePreviewContainer_f7b5db { - align-items: center; - -webkit-mask-image: linear-gradient(180deg,rgba(0,0,0,.2) 0,#000 30%,#000 70%,rgba(0,0,0,.2)); - mask-image: linear-gradient(180deg,rgba(0,0,0,.2) 0,#000 30%,#000 70%,rgba(0,0,0,.2)); - max-height: 100%; - max-width: 252px -} - -.nameplatePreviewContainer_f7b5db,.nameplatePreviewList_f7b5db { - display: flex; - height: -moz-fit-content; - height: fit-content; - width: 100% -} - -.nameplatePreviewList_f7b5db { - flex-direction: column; - gap: var(--space-4); - justify-content: center -} - -.nameplatePreviewSampleItem_f7b5db { - align-items: center; - display: flex; - height: 42px; - width: 100% -} - -.productPreview__00f8b { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.full-motion .productPreview__00f8b { - transition: opacity .15s ease-out -} - -.productPreview__00f8b.faded__00f8b { - opacity: .5 -} - -.productPreview__00f8b.fullPreview__00f8b { - align-items: flex-start; - inset: 0; - position: absolute -} - -.productPreviewIconOverlay__00f8b { - justify-self: center; - margin: auto; - opacity: 1; - position: absolute -} - -.full-motion .productPreviewIconOverlay__00f8b { - transition: opacity .15s ease-out -} - -.productPreviewIconOverlay__00f8b.hidden__00f8b { - opacity: 0 -} - -.avatarDecorationPreview__00f8b { - padding: var(--space-24) -} - -.container__8a8e7 { - align-items: center; - color: var(--text-strong); - display: inline-flex; - gap: var(--space-4); - position: relative; - white-space: nowrap -} - -.discount__8a8e7 { - color: var(--text-feedback-positive) -} - -.icon__8a8e7 { - min-width: -moz-fit-content; - min-width: fit-content -} - -.priceStrikethrough__8a8e7 { - text-decoration: line-through -} - -.discountPill__8a8e7 { - background: var(--green-new-34); - border-radius: 20px; - padding: 1px 8px 2px; - text-decoration-line: none -} - -.theme-light .discountPill__8a8e7 { - color: var(--white) -} - -.theme-light .discountBackgroundColor__8a8e7 { - color: #9a9a9d -} - -.theme-dark .discountPill__8a8e7 { - color: var(--neutral-71) -} - -.theme-dark .discountBackgroundColor__8a8e7 { - color: #8d8e91 -} - -.insufficientOrbs__3b1b0 { - color: var(--text-muted) -} - -.container_b7b2cf { - align-items: center; - display: flex; - flex-direction: column; - margin: auto; - text-align: center; - width: 400px -} - -.header_b7b2cf { - margin-bottom: 24px -} - -.description_b7b2cf { - margin-bottom: 40px -} - -.splashImage_b7b2cf { - width: 223px -} - -.settings_b7b2cf { - margin-top: 96px -} - -.modal_b7b2cf { - margin-bottom: 48px; - margin-top: 40px -} - -.blockedPaymentsModalContent_b7b2cf,.blockedPaymentsModalHeader_b7b2cf { - background-color: var(--modal-background) -} - -.blockedPaymentsModalHeader_b7b2cf { - border-radius: 4px 4px 0 0 -} - -.blockedPaymentsModalContent_b7b2cf { - border-radius: 0 0 4px 4px -} - -.blockedPaymentsWarningIcon_b7b2cf { - margin-inline-end:10px} - -.blockedPaymentsWarning_b7b2cf { - background-color: var(--background-base-lower); - color: var(--text-default); - display: flex; - justify-items: center; - margin-bottom: 16px; - margin-top: 16px; - padding: 16px 22px -} - -.theme-dark .blockedPaymentsWarning_b7b2cf { - border-color: var(--primary-700) -} - -.theme-light .blockedPaymentsWarning_b7b2cf { - border-color: var(--primary-200) -} - -.link__6a22b { - color: var(--interactive-text-active); - font-size: 14px; - font-weight: var(--font-weight-semibold); - line-height: 18px -} - -.checkoutFooter_ebbc17 { - padding: var(--space-16) var(--space-24) var(--space-24) -} - -.headerSubtitle_ebbc17,.headerTitle_ebbc17 { - overflow-wrap: break-word; - word-break: break-word -} - -.headerSubtitle_ebbc17 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px; - justify-content: flex-start -} - -.countryFlagEmoji_ebbc17 { - height: 18px -} - -.checkoutPill_ebbc17 { - align-items: center; - align-self: center; - background-color: var(--badge-expressive-background-default); - border-radius: var(--radius-round); - box-sizing: border-box; - color: var(--badge-expressive-text-default); - display: inline-flex; - gap: var(--space-4); - padding: 0 var(--space-8); - text-align: center; - text-transform: uppercase -} - -.checkoutHeaderContainer_ebbc17 { - align-items: center; - -moz-column-gap: var(--space-8); - column-gap: var(--space-8); - display: flex; - flex-direction: row; - justify-content: flex-start -} - -.checkoutModalBody_ebbc17 { - max-height: min(800px,100%); - max-width: 480px -} - -.checkoutModalBodyPadding_ebbc17 { - padding-bottom: 0 -} - -.shaker_e5f3a9 { - align-items: center; - display: flex; - flex-direction: column; - flex-grow: 1; - height: 100%; - justify-content: center; - max-height: inherit -} - -.root_e5f3a9>:last-child { - border-end-end-radius: var(--radius-sm); - border-end-start-radius: var(--radius-sm) -} - -.root_e5f3a9.withHeader_e5f3a9>:first-child { - background-color: transparent -} - -@media (max-width: 485px) { - .shaker_e5f3a9 { - position:absolute; - top: 0; - inset-inline: 0; - bottom: 0; - max-height: none - } - - .root_e5f3a9,.shaker_e5f3a9 { - justify-content: center - } - - .root_e5f3a9 { - border-radius: 0; - width: 100vw - } -} - -.halloweenModalHeight_e5f3a9 { - max-height: 800px -} - -.premiumBrandRefreshBackground_e5f3a9 { - background-color: var(--background-base-low)!important -} - -.full-motion .transition__5fbe9 { - transition: transform .2s ease -} - -.directionDown__5fbe9 { - transform: rotate3d(0,0,-1,0deg) -} - -.directionRight__5fbe9 { - transform: rotate3d(0,0,-1,90deg) -} - -.directionLeft__5fbe9 { - transform: rotate3d(0,0,-1,-90deg) -} - -.directionUp__5fbe9 { - transform: rotate3d(0,0,-1,180deg) -} - -.breadcrumbs__0f692 { - display: flex; - justify-content: center; - overflow: hidden -} - -.breadcrumbWrapper__0f692 { - align-items: center; - display: flex; - flex-grow: 0; - font-weight: var(--font-weight-semibold) -} - -.breadcrumbFinalWrapper__0f692 { - overflow: hidden -} - -.breadcrumbArrow__0f692 { - color: var(--text-muted); - height: 18px; - margin: 0 11px; - width: 18px -} - -.pill_e7b36e { - align-items: center; - border: 1px solid var(--border-normal); - border-radius: var(--radius-lg); - display: flex; - justify-content: center; - padding: var(--space-8) var(--space-12); - width: -moz-fit-content; - width: fit-content -} - -.pillGradient_e7b36e { - background: linear-gradient(90deg,rgba(155,29,165,.35) 0,rgba(30,35,83,.35) 120%),var(--background-base-lower) -} - -.theme-light .pillGradient_e7b36e { - background: linear-gradient(90deg,rgba(155,29,165,.1) 0,rgba(30,35,83,.1) 120%),var(--background-base-lower) -} - -.pillText_e7b36e { - line-height: 1; - padding-top: .75px; - text-transform: uppercase -} - -.badgeContainer_fc0249 { - margin-inline-start:var(--space-16);margin-top: var(--space-8) -} - -.container_d5200e { - position: relative; - z-index: 10 -} - -.headerContainer_d5200e { - align-items: flex-end; - border-start-end-radius: calc(var(--radius-md) - 1px); - border-start-start-radius: calc(var(--radius-md) - 1px); - display: flex; - height: 120px; - min-height: 120px; - padding: 0 var(--space-16) 0; - position: relative -} - -.headerContainer_d5200e.containerBottomPadding_d5200e { - padding-bottom: var(--space-12) -} - -.bigWumpus_d5200e { - height: 159px; - inset-inline-end: 41px; - position: absolute; - top: 28px; - z-index: 1000 -} - -.wumpus_d5200e { - height: 117px; - inset-inline-end: 35px; - position: absolute; - top: 12px -} - -.bigCloud_d5200e { - inset-inline-start: calc(50% - 60px); - opacity: .25; - position: absolute; - top: 39px; - transform: translateX(-50%); - width: 120px -} - -.mediumCloud_d5200e { - inset-inline-start: 28px; - opacity: .35; - position: absolute; - top: 22px; - width: 74px -} - -.smallCloud_d5200e { - inset-inline-end: 39px; - opacity: .25; - position: absolute; - top: 58px; - width: 59px -} - -.closeButtonPosition_d5200e { - inset-inline-end: 12px; - position: absolute; - top: 12px -} - -.textContainer_d5200e { - gap: var(--space-16); - max-width: 55%; - position: relative -} - -.headerTextWrapper_d5200e,.textContainer_d5200e { - display: flex; - flex-direction: column -} - -.headerTextWrapper_d5200e { - background-color: var(--background-base-low); - gap: var(--space-12); - justify-content: center; - padding-bottom: var(--space-12); - padding-top: var(--space-24); - text-align: center -} - -.oneStepCheckoutTextWrapper_d5200e { - background-color: var(--background-base-low); - padding: var(--space-16) var(--space-16) var(--space-12) -} - -.wordmark_d5200e { - height: 22px; - width: -moz-fit-content; - width: fit-content -} - -.bodyGradientPadding_d5200e { - height: 24px -} - -.bodyGradientContainer_d5200e { - bottom: -24px; - height: 30px; - margin: 0 var(--space-8); - overflow: hidden; - position: absolute; - width: calc(100% - 16px) -} - -.bodyGradient_d5200e { - background: linear-gradient(0deg,transparent 0,var(--background-base-low) 46.63%); - bottom: 0; - height: 48px; - position: absolute; - width: 100%; - z-index: -1 -} - -@media screen and (max-height: 550px),screen and (max-width:485px) { - .headerContainer_d5200e { - border-radius:0 - } -} - -.nitroText_d5200e { - font-size: 24px; - font-style: italic; - font-weight: 900; - line-height: 27px; - text-transform: uppercase -} - -.betaBadgeContainer_d5200e { - bottom: calc(100% + 8px); - inset-inline-start: 0; - position: absolute -} - -.headerBackground_b66356 { - border-start-end-radius: var(--radius-md); - border-start-start-radius: var(--radius-md); - box-sizing: border-box; - display: flex; - flex-direction: column; - margin-inline-start:-1px;margin-top: -2px; - min-height: 114px; - padding: var(--space-20); - position: relative; - width: calc(100% + 2px) -} - -@media screen and (max-height: 550px),screen and (max-width:485px) { - .headerBackground_b66356 { - border-radius:0 - } -} - -.tier2HeaderBackground_b66356 { - background-image: linear-gradient(41.27deg,var(--premium-tier-2-purple-for-gradients) 20.01%,var(--premium-tier-2-purple-for-gradients-2) 56.27%,var(--premium-tier-2-pink-for-gradients) 92.19%) -} - -.snow_b66356 { - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100%; - z-index: 1 -} - -.closeIcon_b66356 { - color: var(--text-strong); - opacity: 1 -} - -.headerTop_b66356 { - position: relative; - width: 100% -} - -.headerIcon_b66356 { - color: var(--white); - height: 22px; - width: auto -} - -.headerIcon_b66356.nonTier2_b66356 { - height: 35px -} - -.price_b66356 { - align-self: flex-end; - background-color: var(--white); - border-radius: 3px; - color: var(--brand-500); - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 1.71; - margin-top: 20px; - padding: 2px 12px; - position: relative -} - -.closeButton_b66356 { - color: var(--white)!important -} - -.closeButton_b66356:hover { - background-color: hsla(0,0%,100%,.04)!important -} - -.trialBadge_b66356 { - inset-inline-start: 0; - position: absolute; - top: 43px -} - -.trialBadgeContainer_b66356 { - align-items: center; - background: var(--white); - border-radius: 10px; - display: flex; - justify-content: center; - margin-top: 8px; - max-height: 32px; - max-width: 150px; - text-align: start; - text-transform: uppercase -} - -.trialOfferText_b66356 { - -webkit-background-clip: text; - background-clip: text; - background-image: linear-gradient(93.74deg,#8847c6 .65%,#ac46c3 50.46%,#ad5b91 91.96%); - color: transparent; - line-height: 12px; - padding: 4px 8px -} - -.trialOfferText_b66356.tier0TrialOffer_b66356 { - background-image: var(--custom-premium-colors-premium-gradient-tier-0) -} - -.trialBadgeSparkles_b66356 { - inset-inline-start: 4px; - position: absolute; - top: 43px -} - -.tier2Animation__387d4 { - position: relative -} - -.panningAnimation__387d4 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 22px; - overflow: hidden -} - -.panningAnimationInner__387d4 { - background-size: 100% 100%; - filter: saturate(var(--saturation-factor,1)); - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.tier2Background__387d4 { - background-image: url(/assets/083b1bde1c6b39a3.svg); - opacity: .2 -} - -.tier2Foreground__387d4 { - background-image: url(/assets/ae4a4071a85dcaa6.svg) -} - -.guildWrapper__387d4 { - position: relative -} - -.guildBackground__387d4 { - top: 0; - inset-inline: 0; - bottom: 0 -} - -.guildBackground__387d4,.guildStar__387d4 { - position: absolute -} - -.sequencedAnimation__387d4 { - filter: saturate(var(--saturation-factor,1)) -} - -.modalHeader_b54a5b { - background-image: linear-gradient(var(--modal-background),var(--modal-background)); - background-position: 0 10px; - background-repeat: no-repeat; - background-size: 100% calc(100% - 20px); - margin-bottom: -20px; - overflow: clip; - padding-bottom: 0 -} - -.modalHeaderCustomGift_b54a5b { - align-items: center; - background-image: unset; - background-size: unset; - border-bottom: none; - display: flex; - justify-content: space-between; - margin-bottom: 0; - padding-bottom: 0 -} - -.closeButton_b54a5b { - margin-inline-start:auto;padding: 0 -} - -.closeButtonInner_b54a5b { - height: 24px -} - -.header_b54a5b { - flex-shrink: 0 -} - -body .headerAnimation_b54a5b { - border-start-end-radius: 4px; - border-start-start-radius: 4px; - position: absolute; - top: 0; - inset-inline: 50% 0; - bottom: -22px; - min-width: 440px; - overflow: hidden; - transform: translateX(-50%); - width: 100% -} - -@media (max-width: 485px) { - .headerAnimation_b54a5b { - min-width:auto!important - } -} - -.headerAnimation_b54a5b { - border-start-end-radius: calc(var(--radius-md) - 1px); - border-start-start-radius: calc(var(--radius-md) - 1px) -} - -.paymentModalLockIcon__9a648 { - color: var(--primary-500); - padding: 0 var(--space-16) -} - -.theme-dark .paymentModalLockIcon__9a648 { - color: var(--primary-300) -} - -.lockIcon__9a648 { - margin-inline-end:2px} - -.lockIconText__9a648 { - white-space: nowrap -} - -.primaryIcon__962c6 { - height: 16px; - margin-inline-end:4px;width: 16px -} - -.button__962c6 { - display: flex -} - -.body_c6f1a4 { - background-color: var(--modal-background); - border-radius: var(--radius-md); - height: 100%; - padding: 0 var(--space-16) -} - -.sliderBody_c6f1a4 { - box-sizing: border-box; - flex-direction: row; - flex-grow: 0; - padding-bottom: var(--space-8); - width: 100% -} - -@media (max-width: 485px) { - .sliderBody_c6f1a4 { - flex-direction:column; - flex-grow: 1 - } -} - -.body_c6f1a4.reviewStep_c6f1a4 { - padding: 0 var(--modal-horizontal-padding) -} - -@media (max-width: 485px) { - .body_c6f1a4.reviewStep_c6f1a4 { - width:100% - } -} - -.body_c6f1a4.addPaymentStepForPremium_c6f1a4 { - max-width: 440px; - padding-inline:var(--space-16)} - -@media screen and (max-height: 550px),screen and (max-width:485px) { - .body_c6f1a4.addPaymentStepForPremium_c6f1a4 { - max-width:100% - } -} - -.sliderBodyLarge_c6f1a4 { - width: 804px -} - -.icon__9f2f4 { - align-self: center; - background-color: var(--background-surface-high); - border-radius: 50% -} - -.icon__9f2f4.small__9f2f4 { - height: 40px; - width: 40px -} - -.icon__9f2f4.medium__9f2f4 { - height: 66px; - width: 66px -} - -.icon__9f2f4.large__9f2f4 { - height: 128px; - width: 128px -} - -.container__9e80c { - display: flex -} - -.emojiContainer__9e80c { - margin-inline-end:16px} - -.name__9e80c { - margin-bottom: 2px -} - -.infoContainer__9e80c { - flex: 1 -} - -.emojiIcon__9e80c { - height: 20px; - width: 20px -} - -.container__4328f { - display: flex; - flex-direction: column -} - -.header__4328f { - color: var(--text-default); - margin-top: 16px; - text-align: center -} - -.header__4328f strong { - color: var(--text-strong) -} - -.divider__4328f { - background-color: var(--border-subtle); - border: none; - height: 1px; - margin: 28px 0; - width: 100% -} - -.benefitsContainer__4328f { - display: flex; - flex-direction: column; - gap: 8px; - margin-top: 16px -} - -.benefit__4328f { - background-color: var(--background-mod-subtle); - border-radius: 8px; - padding: 16px -} - -.headerContainer__2dea3 { - background-color: var(--background-base-low); - height: 112px; - margin-inline-start:-1px;margin-top: -2px; - position: relative; - width: calc(100% + 2px) -} - -.closeContainer__2dea3 { - align-items: center; - background-color: var(--opacity-black-84); - border-radius: 50%; - cursor: pointer; - display: flex; - height: 24px; - inset-inline-end: 16px; - justify-content: center; - position: absolute; - top: 16px; - width: 24px -} - -.closeIcon__2dea3 { - color: var(--interactive-text-default); - height: 16px; - width: 16px -} - -.headerImage__2dea3,.headerImageContainer__2dea3 { - height: 100%; - width: 100% -} - -.headerImage__2dea3 { - border-start-end-radius: 12px; - border-start-start-radius: 12px; - -o-object-fit: cover; - object-fit: cover -} - -.confirmationContainer__2dea3 { - margin-top: 32px -} - -.purchaseConfirmation__2dea3 { - align-items: center; - display: flex; - flex-direction: column; - text-align: center -} - -.confirmationTitle__2dea3 { - margin-top: 16px -} - -.confirmationSubtitle__2dea3 { - margin-top: 8px -} - -.seasonalGiftBoxHeaderIcon_ae16b8 { - align-self: center; - bottom: -11px; - position: absolute; - width: 260px; - z-index: 1 -} - -.container_ae16b8 { - align-items: center; - background-image: unset; - background-size: unset; - border-radius: 4px 4px 0 0; - display: flex; - justify-content: center; - position: relative -} - -.closeButton_ae16b8 { - margin: var(--modal-vertical-padding) var(--modal-horizontal-padding); - margin-inline-start:auto} - -.spinner_ca0af2 { - border-radius: 4px; - max-height: 720px; - min-height: 200px -} - -.cardIcon__29abc { - box-shadow: 0 1px 3px var(--opacity-black-8); - flex: 0 0 auto; - position: relative; - text-indent: -9999em; - transform-origin: 50% 50%; - transform-style: preserve-3d -} - -.full-motion .cardIcon__29abc { - transition: transform .3s ease-in-out -} - -.cardIcon__29abc:after,.cardIcon__29abc:before { - -webkit-backface-visibility: hidden; - backface-visibility: hidden -} - -.cardIcon__29abc:before { - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - background-image: url(/assets/22f7eeb9ea9904c1.svg); - background-size: 100% 100%; - border-radius: 2px; - transform: rotateY(0deg) -} - -.cardIcon__29abc:after,.cardIcon__29abc:before { - content: ""; - display: block; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.cardIcon__29abc:after { - background: url(/assets/fea1d83d02c90c67.svg); - background-size: 100% 100%; - transform: rotateY(180deg) -} - -.cardIcon__29abc.visa__29abc:before { - background-image: url(/assets/4c049942fdde71f8.svg) -} - -.cardIcon__29abc.american-express__29abc:before,.cardIcon__29abc.amex__29abc:before { - background-image: url(/assets/e415725e0e3a87fb.svg) -} - -.cardIcon__29abc.discover__29abc:before { - background-image: url(/assets/9a5413553cab204a.svg) -} - -.cardIcon__29abc.master-card__29abc:before,.cardIcon__29abc.mastercard__29abc:before { - background-image: url(/assets/ba865ff6773d1876.svg) -} - -.cardIcon__29abc.paypal__29abc:before { - background-image: url(/assets/0b87743361cfb797.svg) -} - -.cardIcon__29abc.paymentRequest__29abc:before { - background-image: url(/assets/a500e0b04a325513.svg) -} - -.cardIcon__29abc.gPay__29abc:before { - background-image: url(/assets/04e197da860c981f.svg) -} - -.cardIcon__29abc.sofort__29abc:before { - background-image: url(/assets/eb22e43f6f710d3b.svg) -} - -.cardIcon__29abc.przelewy24__29abc:before { - background-image: url(/assets/3d961164938e62d7.svg) -} - -.cardIcon__29abc.giropay__29abc:before { - background-image: url(/assets/fcb798c4bf712284.svg) -} - -.cardIcon__29abc.paysafecard__29abc:before { - background-image: url(/assets/0b47f9360f12b1fa.svg) -} - -.cardIcon__29abc.gcash__29abc:before { - background-image: url(/assets/7e5579c4c73ca390.svg) -} - -.cardIcon__29abc.grabpay__29abc:before { - background-image: url(/assets/224f56365db6763e.svg) -} - -.cardIcon__29abc.momo_wallet__29abc:before { - background-image: url(/assets/0dbebb1dde8de002.svg) -} - -.cardIcon__29abc.venmo__29abc:before { - background-image: url(/assets/9bb2cc8906a18ba2.svg) -} - -.cardIcon__29abc.kakaopay__29abc:before { - background-image: url(/assets/2a5fa2783cb39764.svg) -} - -.cardIcon__29abc.gopay_wallet__29abc:before { - background-image: url(/assets/637bf6beddb5589b.svg) -} - -.cardIcon__29abc.bancontact__29abc:before { - background-image: url(/assets/ce90dd2b5978b623.svg) -} - -.cardIcon__29abc.eps__29abc:before { - background-image: url(/assets/3f80791089be55d4.svg) -} - -.cardIcon__29abc.ideal__29abc:before { - background-image: url(/assets/160f818e8dc66a17.svg) -} - -.cardIcon__29abc.cash_app__29abc:before { - background-image: url(/assets/3de97fb87787f20b.svg) -} - -.cardIcon__29abc.apple__29abc:before,.cardIcon__29abc.apple_light__29abc:before { - background-image: url(/assets/824341c879ac6d59.svg) -} - -.theme-light .cardIcon__29abc.apple__29abc:before { - background-image: url(/assets/24de41d4bd8ef584.svg) -} - -.theme-light .cardIcon__29abc.apple_light__29abc:before { - background-image: url(/assets/824341c879ac6d59.svg) -} - -.theme-light .cardIcon__29abc.venmo__29abc:before { - background-image: url(/assets/1566a4fd747aba18.svg) -} - -.reduce-motion .cardIcon__29abc { - transition: unset -} - -.cardIconSmall__29abc { - height: 22px; - width: 32px -} - -.cardIconMedium__29abc { - height: 44px; - width: 64px -} - -.cardIconLarge__29abc { - height: 66px; - width: 96px -} - -.cardIconXLarge__29abc { - height: 88px; - width: 128px -} - -.flipped__29abc { - transform: rotateY(180deg) -} - -.buttonIcon__4151b { - margin-inline-end:8px} - -.button__4151b { - margin-bottom: 8px; - width: 200px!important -} - -.button__4151b:last-child { - margin-bottom: 0 -} - -.connectionInstructions__4151b { - margin-top: 8px; - text-align: center -} - -.paymentRequestIcon__4151b { - inset-inline-start: 8px; - margin: auto auto 25px; - top: 9px; - z-index: 1 -} - -.theme-light .appleConnectorIcon__4151b { - box-shadow: none -} - -.buttonIcon__92b20 { - margin-inline-end:8px} - -.allPaymentsToggleButton__92b20 { - color: var(--text-strong); - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 18px; - margin: 20px auto 0; - padding: 4px 4px 0 -} - -.allPaymentsToggleButton__92b20:hover { - cursor: pointer -} - -.allPaymentsSection__92b20 { - padding-top: 24px -} - -.hidden__92b20 { - display: none -} - -.SeparatorLower__92b20,.SeparatorUpper__92b20 { - background: var(--border-subtle); - border: 0; - height: 1px; - width: 100% -} - -.SeparatorUpper__92b20 { - margin: 0 0 16px -} - -.SeparatorLower__92b20 { - margin: 16px 0 0 -} - -.container__92b20 { - display: grid; - gap: var(--space-8); - grid-template-columns: 1fr 1fr -} - -@media (max-width: 485px) { - .container__92b20 { - grid-template-columns:1fr - } -} - -.venmoIcon__44362 { - inset-inline-start: 8px; - margin: auto auto 25px; - top: 9px; - z-index: 1 -} - -.connectionInstructions__44362 { - margin-top: 8px; - text-align: center -} - -.row__06c2b { - padding-top: 20px -} - -.row__06c2b:first-child { - padding-top: 0 -} - -.section__06c2b { - box-sizing: border-box; - flex: 0 1 auto; - padding-inline-start:20px} - -.section__06c2b:first-child { - padding-inline-start:0} - -.width100__06c2b { - width: 100% -} - -.width75__06c2b { - width: 75% -} - -.width60__06c2b { - width: 60% -} - -.width40__06c2b { - width: 40% -} - -.width30__06c2b { - width: 30% -} - -.width50__06c2b { - width: 50% -} - -.width25__06c2b { - width: 25% -} - -.modal__6181b { - overflow-x: hidden; - transform: translateZ(0) -} - -.planSelector__6181b { - margin-top: 20px -} - -.form__6181b { - display: flex; - flex-direction: column; - flex-grow: 1; - max-height: 660px -} - -.divider__6181b { - border-top: 1px solid; - margin: 0 20px; - position: relative -} - -.content__6181b { - margin-bottom: 20px -} - -.giftToggle__6181b { - margin-top: var(--space-8) -} - -.formTitle__6181b { - margin: 20px 0 10px -} - -.errorBlock__6181b,.purchaseTerms__6181b { - margin-top: 20px -} - -.invalidIcon__6181b { - height: 18px; - padding: 0 1px; - width: 18px -} - -.choosePaymentSourceType__6181b { - margin-top: 20px -} - -.buyButtonTooltipWrapper__6181b { - position: relative -} - -.buyButtonTooltipTarget__6181b { - cursor: not-allowed; - inset: 0; - position: absolute; - z-index: 1 -} - -.backButtonSize__6181b { - height: 38px; - width: auto -} - -.backButton__6181b { - padding: 0 -} - -.backButton__6181b:hover { - text-decoration: underline -} - -.modalContent__6181b { - -webkit-overflow-scrolling: touch -} - -@media (max-width: 485px) and (max-height:550px) { - .form__6181b { - display:block - } - - .modalContent__6181b { - height: auto - } -} - -.theme-dark .divider__6181b { - border-color: var(--primary-630) -} - -.theme-light .divider__6181b { - border-color: hsl(var(--primary-200-hsl)/.6) -} - -.backButtonColor__6181b { - color: var(--text-default) -} - -.note__2ef07 { - font-size: 12px; - margin-top: 20px; - position: relative -} - -.note__2ef07~.note__2ef07 { - margin-top: 8px -} - -.icon__2ef07 { - flex-shrink: 0; - margin-inline-end:8px} - -.large__2ef07 { - height: 20px; - width: 20px -} - -.small__2ef07 { - height: 14px; - width: 14px -} - -.colorWarning__2ef07 { - color: var(--yellow-300) -} - -.colorError__2ef07 { - color: var(--text-feedback-critical) -} - -.colorPrimary__2ef07 { - color: var(--text-muted) -} - -.colorSecondary__2ef07 { - color: var(--text-default) -} - -.wrapper__4106a { - background-color: var(--modal-background); - flex: 0 0 auto; - padding: var(--space-16) var(--space-16) var(--space-8); - padding-inline-start:var(--space-24)} - -.content_a8c622 { - background-color: var(--modal-background); - border-radius: inherit; - display: flex; - flex: 1 1 auto; - flex-direction: column; - min-height: 0 -} - -.breadcrumbsWrapper_a8c622 { - flex: 0 0 auto; - margin: 0 16px; - padding: 16px 0 -} - -.bodyWrapper_a8c622 { - display: flex; - flex: 1 1 auto; - flex-direction: column; - height: 100%; - min-height: 0; - position: relative -} - -.scroller_a8c622 { - height: 100%; - padding: 0 16px 16px -} - -.errorBlockWrapper_a8c622 { - padding: 16px 16px 0 -} - -.paymentNote_a8c622 { - padding: 0 16px -} - -.loadingBlock_a8c622 { - height: 192px -} - -.sequencer_a8c622,.sequencerStatic_a8c622 { - align-items: flex-start; - display: flex; - flex-direction: column -} - -.sequencerStatic_a8c622 { - flex: 1 1 auto -} - -.sequencerAnimatedNode_a8c622 { - display: flex; - flex: 1 1 auto; - flex-direction: column; - min-height: 0 -} - -.cardNumberWrapper__8b579 { - position: relative -} - -.cardIcon__8b579 { - inset-inline-start: 10px; - position: absolute; - top: 10px -} - -.cardInput__8b579 { - background-color: var(--input-background-default); - border: 1px solid var(--input-border-default); - border-radius: var(--radius-sm); - padding: 10px 9px -} - -.hiddenDiv__8b579 { - height: 0; - padding: 0; - visibility: hidden -} - -.cardNumberInput__8b579 { - padding-inline-start:55px} - -.cardInputFocused__8b579 { - border-color: var(--text-link) -} - -.cardInputError__8b579 { - border-color: var(--border-feedback-critical) -} - -.theme-dark .inputPrefix__8b579 { - opacity: .5 -} - -.enable-forced-colors .cardInput__8b579 { - border: 1px solid ButtonText; - display: block -} - -.enable-forced-colors .cardInput__8b579:focus,.enable-forced-colors .cardInput__8b579:hover { - border-color: Highlight -} - -.enable-forced-colors .cardInputFocused__8b579 { - border-color: Highlight -} - -.enable-forced-colors .inputPrefix__8b579 { - opacity: 1 -} - -.cardBrands__3e8d5 { - display: flex; - flex-wrap: wrap; - justify-content: end; - margin-bottom: -15px -} - -.cardFormHeader__3e8d5 { - margin-inline-start:5px} - -.cardFormHeader__3e8d5.jcb__3e8d5:before { - background-image: url(/assets/e8bf9bea0d018670.svg) -} - -.theme-light .cardFormHeader__3e8d5.jcb__3e8d5:before { - background-image: url(/assets/e8efae14b14ee8dd.svg) -} - -.cardFormHeader__3e8d5.jcb_monochrome__3e8d5:before { - background-image: url(/assets/dcebba6e03f7a1f8.svg) -} - -.theme-light .cardFormHeader__3e8d5.jcb_monochrome__3e8d5:before { - background-image: url(/assets/3ec57733d5de83bd.svg) -} - -.cardFormHeader__3e8d5.amex__3e8d5:before { - background-image: url(/assets/49f0d07c4a5edf19.svg) -} - -.theme-light .cardFormHeader__3e8d5.amex__3e8d5:before { - background-image: url(/assets/886d17f4a179ce9a.svg) -} - -.cardFormHeader__3e8d5.amex_monochrome__3e8d5:before { - background-image: url(/assets/9e62f4a1513dbf78.svg) -} - -.theme-light .cardFormHeader__3e8d5.amex_monochrome__3e8d5:before { - background-image: url(/assets/2a2717437ecb9409.svg) -} - -.cardFormHeader__3e8d5.mastercard__3e8d5:before { - background-image: url(/assets/61a8baaebf339a2c.svg) -} - -.theme-light .cardFormHeader__3e8d5.mastercard__3e8d5:before { - background-image: url(/assets/983c3fd5283d32f6.svg) -} - -.cardFormHeader__3e8d5.mastercard_monochrome__3e8d5:before { - background-image: url(/assets/929007941f982dc3.svg) -} - -.theme-light .cardFormHeader__3e8d5.mastercard_monochrome__3e8d5:before { - background-image: url(/assets/2d35b5c00b617a15.svg) -} - -.cardFormHeader__3e8d5.visa__3e8d5:before { - background-image: url(/assets/7149accdcaae7d2f.svg) -} - -.theme-light .cardFormHeader__3e8d5.visa__3e8d5:before { - background-image: url(/assets/4ae102bdf144a582.svg) -} - -.cardFormHeader__3e8d5.visa_monochrome__3e8d5:before { - background-image: url(/assets/c58095d35f0201e3.svg) -} - -.theme-light .cardFormHeader__3e8d5.visa_monochrome__3e8d5:before { - background-image: url(/assets/bb1e18bcf1497322.svg) -} - -.cardFormHeader__3e8d5.discover__3e8d5:before { - background-image: url(/assets/e449e7cba36f144c.svg) -} - -.theme-light .cardFormHeader__3e8d5.discover__3e8d5:before { - background-image: url(/assets/75b4bad7b0654bd8.svg) -} - -.cardFormHeader__3e8d5.discover_monochrome__3e8d5:before { - background-image: url(/assets/9f626707af59b089.svg) -} - -.theme-light .cardFormHeader__3e8d5.discover_monochrome__3e8d5:before { - background-image: url(/assets/3be3e11e2b0d8996.svg) -} - -.cardFormHeader__3e8d5.dinersclub__3e8d5:before { - background-image: url(/assets/b596cb9f6ccc2408.svg) -} - -.theme-light .cardFormHeader__3e8d5.dinersclub__3e8d5:before { - background-image: url(/assets/fc8d44ec7c50ab0e.svg) -} - -.cardFormHeader__3e8d5.dinersclub_monochrome__3e8d5:before { - background-image: url(/assets/2ce5f42c5062092f.svg) -} - -.theme-light .cardFormHeader__3e8d5.dinersclub_monochrome__3e8d5:before { - background-image: url(/assets/3b5c40ec70355fde.svg) -} - -.cardFormHeader__3e8d5.unionpay__3e8d5:before { - background-image: url(/assets/3472374445e1b6b0.svg) -} - -.theme-light .cardFormHeader__3e8d5.unionpay__3e8d5:before { - background-image: url(/assets/3472374445e1b6b0.svg) -} - -.cardFormHeader__3e8d5.unionpay_monochrome__3e8d5:before { - background-image: url(/assets/f4a4e394e0c0b9f2.svg) -} - -.theme-light .cardFormHeader__3e8d5.unionpay_monochrome__3e8d5:before { - background-image: url(/assets/d1fe91ba5e5f4702.svg) -} - -.body_af4c15 { - padding-bottom: 16px; - padding-top: 16px -} - -.body_d31d57 { - padding-bottom: 16px; - padding-top: 16px -} - -.nonTopInputWrapper_d31d57 { - padding-top: 20px -} - -.bankSelectionStub_d31d57 { - display: flex; - height: 243px; - justify-content: center -} - -.body_ceece4 { - margin: var(--space-16) 0; - padding: 0 4px -} - -.addressElementContainer_ceece4,.loadingContainer_ceece4 { - flex-direction: column -} - -.loadingContainer_ceece4 { - display: flex; - justify-content: center; - margin: var(--space-16) 0; - min-height: 240px -} - -.visible_ceece4 { - transform: translateX(0) -} - -.full-motion .visible_ceece4 { - transition: transform .2s ease-out -} - -.rightToLeftEntry_ceece4 { - transform: translateX(100%) -} - -.leftToRightEntry_ceece4 { - transform: translateX(-100%) -} - -.hidden_ceece4 { - height: 0; - visibility: hidden -} - -.full-motion .hidden_ceece4 { - transition: transform .2s ease-out -} - -.cardElementContainer_ceece4.visible_ceece4,.defaultPaymentElementContainer_ceece4.visible_ceece4 { - min-height: 240px -} - -.customPaymentElementContainer_ceece4.visible_ceece4 { - min-height: 160px -} - -.addressElementContainer_ceece4.visible_ceece4 { - min-height: 500px -} - -.addressElementContainer_ceece4.hidden_ceece4 { - height: 0; - min-height: 0 -} - -.body__949a0 { - align-items: center; - display: flex; - flex-direction: column -} - -.description__949a0 { - margin-top: 8px; - text-align: center -} - -.body__9e82b { - padding-bottom: 16px; - padding-top: 16px -} - -.paymentRestrictionBannerContainer__52623 { - align-items: center; - border-radius: 4px; - display: flex; - flex-direction: row; - margin-bottom: 16px; - margin-top: 16px; - min-height: 48px; - padding-inline:20px;padding-bottom: 10px; - padding-top: 10px; - position: relative -} - -.theme-light .paymentRestrictionBannerContainer__52623 { - background: linear-gradient(90deg,#475cc6 0,#6045c1) -} - -.theme-dark .paymentRestrictionBannerContainer__52623 { - background: linear-gradient(90deg,#475cc6 0,#6045c1) -} - -.paymentRestrictionBannerBackgroundImage__52623 { - background-image: url(/assets/bb502dc7cfcbbacc.svg); - background-repeat: no-repeat; - bottom: 0; - position: absolute; - top: 40%; - inset-inline: 78% 0 -} - -.appsIconContainer__52623 { - height: 28px; - padding-inline-end:12px;width: 28px -} - -.theme-light .icon__52623 { - display: none -} - -.theme-light .iconDark__52623 { - filter: drop-shadow(0 1px 2px rgb(0 0 0/.4)); - height: 28px; - width: 28px -} - -.theme-dark .icon__52623 { - filter: drop-shadow(0 1px 2px rgb(0 0 0/.4)); - height: 28px; - width: 28px -} - -.theme-dark .iconDark__52623 { - display: none -} - -.adyen-checkout__spinner__wrapper { - align-items: center; - display: flex; - height: 100%; - justify-content: center -} - -.adyen-checkout__spinner__wrapper--inline { - display: inline-block; - height: auto; - margin-right: 8px -} - -[dir=rtl] .adyen-checkout__spinner__wrapper--inline { - margin-left: 8px; - margin-right: 0 -} - -.adyen-checkout__spinner { - animation: rotate-spinner 1.5s linear infinite; - border: 3px solid #0075ff; - border-radius: 50%; - border-top-color: transparent; - height: 43px; - width: 43px -} - -.adyen-checkout__spinner--large { - height: 43px; - width: 43px -} - -.adyen-checkout__spinner--small { - border-width: 2px; - height: 16px; - width: 16px -} - -.adyen-checkout__spinner--medium { - height: 28px; - width: 28px -} - -@keyframes rotate-spinner { - 0% { - transform: rotate(0deg) - } - - to { - transform: rotate(1turn) - } -} - -.adyen-checkout__button { - background: #00112c; - border: 0; - border-radius: 6px; - color: #fff; - cursor: pointer; - font-size: 1em; - font-weight: 500; - height: 48px; - margin: 0; - padding: 15px; - text-decoration: none; - transition: background .3s ease-out,box-shadow .3s ease-out; - width: 100% -} - -.adyen-checkout__button:focus { - box-shadow: 0 0 0 2px #3070ed; - outline: 0 -} - -.adyen-checkout__button:hover { - background: #1c3045; - box-shadow: 0 0,0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14) -} - -.adyen-checkout__button:active { - background: #3a4a5c -} - -.adyen-checkout__button:hover:focus { - box-shadow: 0 0 0 2px #3070ed,0 3px 4px rgba(0,15,45,.2) -} - -.adyen-checkout__button:disabled,.adyen-checkout__button:disabled:hover { - box-shadow: none; - cursor: not-allowed; - opacity: .4; - -webkit-user-select: all; - -moz-user-select: all; - user-select: all -} - -.adyen-checkout__button.adyen-checkout__button--loading { - background: #687282; - box-shadow: none; - pointer-events: none; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.adyen-checkout__button.adyen-checkout__button--pay { - display: flex; - justify-content: center; - margin-top: 24px -} - -.adyen-checkout__button.adyen-checkout__button--pay:disabled { - opacity: .4 -} - -.adyen-checkout__button.adyen-checkout__button--standalone { - margin-top: 0 -} - -.adyen-checkout__button.adyen-checkout__button--inline { - display: block; - font-size: .81em; - height: auto; - padding: 10px 8px; - width: auto -} - -.adyen-checkout__button.adyen-checkout__button--ghost { - background: none; - border: 0; - color: #00112c -} - -.adyen-checkout__button.adyen-checkout__button--ghost:hover { - background: #f7f8f9; - box-shadow: none -} - -.adyen-checkout__button.adyen-checkout__button--ghost:active { - background: #e6e9eb; - box-shadow: none -} - -.adyen-checkout__button.adyen-checkout__button--secondary { - background: #fff; - border: 1px solid #00112c; - color: #00112c; - padding: 10px 12px -} - -.adyen-checkout__button.adyen-checkout__button--secondary:hover { - background: #f7f8f9; - box-shadow: 0 2px 4px rgba(27,42,60,.2),0 4px 5px rgba(27,42,60,.14) -} - -.adyen-checkout__button.adyen-checkout__button--secondary:active,.adyen-checkout__button.adyen-checkout__button--secondary:active:hover { - background: #f7f8f9; - box-shadow: none -} - -.adyen-checkout__button.adyen-checkout__button--secondary:disabled,.adyen-checkout__button.adyen-checkout__button--secondary:disabled:hover { - background-color: #f7f8f9; - border-color: #99a3ad; - box-shadow: none; - cursor: not-allowed; - opacity: .5; - -webkit-user-select: all; - -moz-user-select: all; - user-select: all -} - -.adyen-checkout__button.adyen-checkout__button--secondary .adyen-checkout__spinner { - border-color: transparent #00112c #00112c -} - -.adyen-checkout__button.adyen-checkout__button--action { - background: rgba(0,102,255,.1); - border: 1px solid transparent; - color: #0075ff; - padding: 10px 12px -} - -.adyen-checkout__button.adyen-checkout__button--action:hover { - background: rgba(0,102,255,.2); - box-shadow: none -} - -.adyen-checkout__button.adyen-checkout__button--action:active,.adyen-checkout__button.adyen-checkout__button--action:active:hover { - background: rgba(0,102,255,.3); - box-shadow: none -} - -.adyen-checkout__button.adyen-checkout__button--link { - background: transparent; - border: 1px solid transparent; - border-radius: 3px; - color: #0075ff; - font-weight: 400; - padding: 2px -} - -.adyen-checkout__button.adyen-checkout__button--link:hover { - background: transparent; - box-shadow: none; - text-decoration: underline -} - -.adyen-checkout__button.adyen-checkout__button--completed,.adyen-checkout__button.adyen-checkout__button--completed:active,.adyen-checkout__button.adyen-checkout__button--completed:active:hover,.adyen-checkout__button.adyen-checkout__button--completed:hover { - background: #089a43; - color: #fff -} - -.adyen-checkout__button.adyen-checkout__button--completed .adyen-checkout__button__icon { - filter: brightness(0) invert(1) -} - -.adyen-checkout__button__content { - align-items: center; - display: flex; - height: 100%; - justify-content: center -} - -.adyen-checkout__button__icon { - margin-right: 12px -} - -[dir=rtl] .adyen-checkout__button__icon { - margin-left: 12px; - margin-right: 0 -} - -.adyen-checkout__button__text { - display: block; - justify-content: center; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.adyen-checkout__button .adyen-checkout__spinner { - border-color: transparent #fff #fff -} - -.checkout-secondary-button__text { - font-size: .85em; - margin-left: 5px; - margin-top: 1px -} - -.adyen-checkout__fieldset { - -webkit-margin-start: 0; - -webkit-margin-end: 0; - -webkit-padding-before: 0; - -webkit-padding-after: 0; - -webkit-padding-start: 0; - -webkit-padding-end: 0; - border: none; - display: block; - margin-inline-end:0;margin-inline-start:0;margin: 0; - padding-block-start:0;padding-bottom: 8px; - padding-block-end:0;padding-inline-end:0;padding-inline-start:0;width: 100% -} - -.adyen-checkout__fieldset:last-of-type { - padding-bottom: 0 -} - -.adyen-checkout__fieldset+.adyen-checkout__fieldset { - margin-top: 16px -} - -.adyen-checkout__fieldset__title { - color: #687282; - display: block; - font-size: .68em; - font-weight: 700; - letter-spacing: 1px; - margin: 0; - padding: 0 0 12px; - text-transform: uppercase -} - -.adyen-checkout__field-group,.adyen-checkout__fieldset__fields { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - width: 100% -} - -@media (min-width: 480px) { - .adyen-checkout__field-group:last-of-type .adyen-checkout__field { - margin-bottom:0 - } -} - -.adyen-checkout__field-group:last-of-type .adyen-checkout__field:last-of-type,:not(.adyen-checkout__field-group)>.adyen-checkout__field:last-of-type { - margin-bottom: 0 -} - -.adyen-checkout__fieldset--readonly .adyen-checkout__fieldset__fields { - color: #00112c; - font-size: .81em; - line-height: 19px; - margin: 0 -} - -.adyen-checkout__field { - display: block; - margin-bottom: 16px; - width: 100% -} - -.adyen-checkout__label { - display: block; - position: relative -} - -.adyen-checkout__helper-text,.adyen-checkout__label__text { - color: #00112c; - display: block; - font-size: .81em; - font-weight: 400; - line-height: 13px; - padding-bottom: 5px -} - -.adyen-checkout__label-adornment--end { - position: absolute; - right: 0; - top: 0 -} - -.adyen-checkout__helper-text { - color: #687282 -} - -.adyen-checkout__label__text { - display: block; - overflow: hidden; - text-overflow: ellipsis; - transition: color .1s ease-out; - white-space: nowrap -} - -.adyen-checkout__label__text--error { - color: #c12424 -} - -.adyen-checkout__label--focused .adyen-checkout__label__text { - color: #0075ff -} - -.adyen-checkout__error-text { - align-items: center; - color: #c12424; - display: flex; - font-size: .75em; - font-weight: 400; - margin-top: 4px -} - -.adyen-checkout__field-wrapper { - display: flex; - width: 100% -} - -@media (min-width: 480px) { - .adyen-checkout__field--20 { - width:20% - } - - .adyen-checkout__field--30 { - width: 30% - } - - .adyen-checkout__field--40 { - width: 40% - } - - .adyen-checkout__field--50 { - width: 50% - } - - .adyen-checkout__field--60 { - width: 60% - } - - .adyen-checkout__field--70 { - width: 70% - } - - .adyen-checkout__field--80 { - width: 80% - } - - .adyen-checkout__field--col-70 { - width: calc(70% - 8px) - } - - .adyen-checkout__field--col-30 { - width: calc(30% - 8px) - } - - .adyen-checkout__field--col-50 { - width: calc(50% - 8px) - } -} - -.adyen-checkout__field-wrapper>.adyen-checkout__field:first-child { - margin-right: 8px -} - -[dir=rtl] .adyen-checkout__field-wrapper>.adyen-checkout__field:first-child { - margin-left: 8px; - margin-right: 0 -} - -.adyen-checkout__field-wrapper>.adyen-checkout__field:nth-child(2) { - margin-left: 8px -} - -[dir=rtl] .adyen-checkout__field-wrapper>.adyen-checkout__field:nth-child(2) { - margin-left: 0; - margin-right: 8px -} - -.adyen-checkout__field-wrapper:last-of-type>.adyen-checkout__field { - margin-bottom: 0 -} - -.adyen-checkout__input { - background: #fff; - border: 1px solid #b9c4c9; - border-radius: 6px; - box-sizing: border-box; - caret-color: #0075ff; - color: #00112c; - display: block; - font-family: inherit; - font-size: 1em; - height: 40px; - outline: none; - padding: 5px 8px; - position: relative; - transition: border .2s ease-out,box-shadow .2s ease-out; - width: 100% -} - -.adyen-checkout__input:hover { - border-color: #99a3ad -} - -.adyen-checkout__input:required { - box-shadow: none -} - -.adyen-checkout__input--disabled,.adyen-checkout__input[readonly] { - background: #e6e9eb; - border-color: #e6e9eb -} - -.adyen-checkout__input--disabled:hover { - border-color: #e6e9eb -} - -.adyen-checkout__input-wrapper { - display: block; - position: relative -} - -.adyen-checkout__input-wrapper--block { - display: block -} - -.adyen-checkout-input__inline-validation { - height: 16px; - position: absolute; - right: 14px; - top: 50%; - transform: translateY(-50%); - width: 16px -} - -[dir=rtl] .adyen-checkout-input__inline-validation { - left: 14px; - right: auto -} - -[dir=ltr] .adyen-checkout-input__inline-validation { - left: auto; - right: 14px -} - -.adyen-checkout-input__inline-validation--valid { - color: #089a43 -} - -.adyen-checkout-input__inline-validation--invalid { - color: #c12424 -} - -.adyen-checkout__input--valid { - border-bottom-color: #089a43 -} - -.adyen-checkout__input--error,.adyen-checkout__input--error:hover,.adyen-checkout__input--invalid,.adyen-checkout__input--invalid:hover { - border-color: #c12424 -} - -.adyen-checkout__input::-moz-placeholder { - color: #707070; - font-weight: 200 -} - -.adyen-checkout__input::placeholder { - color: #707070; - font-weight: 200 -} - -.adyen-checkout__input--date { - padding-right: 30px -} - -.adyen-checkout__input--focus,.adyen-checkout__input--focus:hover,.adyen-checkout__input:active,.adyen-checkout__input:active:hover,.adyen-checkout__input:focus,.adyen-checkout__input:focus:hover { - border: 1px solid #0075ff; - box-shadow: 0 0 0 2px #3070ed -} - -.adyen-checkout__input[readonly],.adyen-checkout__input[readonly]:hover { - background-color: #e6e9eb; - border-color: transparent; - color: #687282; - cursor: default -} - -.adyen-checkout__fieldset--personalDetails .adyen-checkout__field--gender .adyen-checkout__radio_group { - display: flex -} - -.adyen-checkout__fieldset--personalDetails .adyen-checkout__radio_group { - display: flex; - margin: 8px 0 -} - -.adyen-checkout__fieldset--personalDetails .adyen-checkout__radio_group__input-wrapper { - margin-right: 20px -} - -.adyen-checkout__fieldset--personalDetails .adyen-checkout__radio_group__input-wrapper:last-child { - margin: 0 -} - -.adyen-checkout__radio_group+.adyen-checkout-input__inline-validation { - display: none -} - -.adyen-checkout__radio_group__input { - opacity: 0; - position: absolute -} - -.adyen-checkout__radio_group__label { - color: inherit; - display: block; - font-size: .81em; - font-weight: 400; - line-height: 16px; - overflow: visible; - padding-bottom: 0; - padding-left: 24px; - position: relative -} - -.adyen-checkout__label--focused .adyen-checkout__radio_group__label { - color: inherit -} - -.adyen-checkout__radio_group__label:before { - background-color: #fff; - border: 1px solid #b9c4c9; - border-radius: 50%; - content: ""; - height: 16px; - left: 0; - position: absolute; - top: 0; - transition: border-color .2s ease-out,box-shadow .2s ease-out; - width: 16px -} - -.adyen-checkout__radio_group__label:hover:before { - border-color: #99a3ad; - box-shadow: 0 0 0 2px #d4d9db; - cursor: pointer -} - -.adyen-checkout__radio_group__label:after { - background-color: #fff; - border-radius: 50%; - box-shadow: 0 1px 1px rgba(0,15,45,.25); - content: ""; - display: block; - height: 6px; - left: 5px; - margin: 0 auto; - position: absolute; - top: 5px; - transform: scale(0); - transition: transform .2s ease-out; - width: 6px -} - -.adyen-checkout__radio_group__label:hover { - border-color: #0075ff; - cursor: pointer -} - -.adyen-checkout__radio_group__input:checked+.adyen-checkout__radio_group__label:before,.adyen-checkout__radio_group__label--selected { - background-color: #0075ff; - border: 0; - transition: all .2s ease-out -} - -.adyen-checkout__radio_group__input:checked+.adyen-checkout__radio_group__label:after { - transform: scale(1) -} - -.adyen-checkout__radio_group__input:focus+.adyen-checkout__radio_group__label:before { - border-color: #0075ff; - box-shadow: 0 0 0 2px rgba(0,102,255,.4) -} - -.adyen-checkout__radio_group__input:checked+.adyen-checkout__radio_group__label:hover:before,.adyen-checkout__radio_group__input:checked:active+.adyen-checkout__radio_group__label:before,.adyen-checkout__radio_group__input:checked:focus+.adyen-checkout__radio_group__label:before { - box-shadow: 0 0 0 2px rgba(0,102,255,.4) -} - -.adyen-checkout__radio_group__label.adyen-checkout__radio_group__label--invalid:before { - border: 1px solid #c12424 -} - -.Select-module_adyen-checkout__dropdown__0Mj-n { - position: relative -} - -.Select-module_adyen-checkout__dropdown__button__yTyqq { - align-items: center; - cursor: pointer; - display: flex -} - -.Select-module_adyen-checkout__dropdown__button__yTyqq:after { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='7' fill='none'%3E%3Cpath fill='%23687282' d='M3.195 6.565a1 1 0 0 0 1.6 0l2.992-3.98a1 1 0 0 0-.8-1.602H1.013a1 1 0 0 0-.8 1.6z'/%3E%3C/svg%3E"); - background-position: 50%; - background-repeat: no-repeat; - content: ""; - height: 6px; - position: absolute; - right: 16px; - width: 8px -} - -[dir=rtl] .Select-module_adyen-checkout__dropdown__button__yTyqq:after { - left: 16px; - right: auto -} - -.Select-module_adyen-checkout__dropdown__button--active__Ej-JR:after { - transform: rotate(180deg) -} - -.Select-module_adyen-checkout__filter-input__CwPBS { - background: #fff; - border: 0; - caret-color: #0075ff; - color: #00112c; - font-family: inherit; - font-size: 1em; - height: 100%; - padding: 0; - width: 100% -} - -.Select-module_adyen-checkout__filter-input__CwPBS::-moz-placeholder { - color: #b9c4c9; - font-weight: 200 -} - -.Select-module_adyen-checkout__filter-input__CwPBS::placeholder { - color: #b9c4c9; - font-weight: 200 -} - -.Select-module_adyen-checkout__filter-input__CwPBS:active,.Select-module_adyen-checkout__filter-input__CwPBS:focus { - outline: 0 -} - -.Select-module_adyen-checkout__filter-input__CwPBS[readonly] { - background: #e6e9eb; - border-color: transparent; - color: #00112c; - cursor: not-allowed -} - -.Select-module_adyen-checkout__dropdown__list__YtEzj { - background: #fff; - display: none; - list-style: none; - margin: 0 0 50px; - overflow-y: auto; - padding: 0; - position: absolute; - width: 100%; - z-index: 1 -} - -.Select-module_adyen-checkout__dropdown__list__YtEzj.Select-module_adyen-checkout__dropdown__list--active__Gegw2 { - display: block -} - -.Select-module_adyen-checkout__dropdown__element__ORU4- { - align-items: center; - display: flex -} - -.adyen-checkout__image { - opacity: 0; - transition: opacity .6s ease-out -} - -.adyen-checkout__image--loaded { - opacity: 1 -} - -.adyen-checkout__dropdown__button-icon--left { - flex-direction: row-reverse; - justify-content: flex-end -} - -.adyen-checkout__dropdown__button-icon--left>img { - margin-left: 0; - margin-right: 12px -} - -.adyen-checkout__dropdown { - font-size: 1em; - max-width: 100%; - width: 100% -} - -.adyen-checkout__dropdown__button { - background: #fff; - border: 1px solid #b9c4c9; - border-radius: 6px; - color: #00112c; - font-size: 1em; - height: 40px; - line-height: 20px; - outline: 0; - padding: 7px 24px 7px 12px; - text-decoration: none; - transition: border .2s ease-out,box-shadow .2s ease-out; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - width: 100% -} - -[dir=rtl] .adyen-checkout__dropdown__button { - padding: 7px 12px 7px 24px -} - -.adyen-checkout__dropdown__button:hover { - border-color: #99a3ad -} - -.adyen-checkout__dropdown__button__icon { - border-radius: 3px; - height: 26px; - margin-right: 12px; - max-width: 40px -} - -.adyen-checkout__dropdown__button--disabled { - opacity: .4 -} - -.adyen-checkout__dropdown__button--active,.adyen-checkout__dropdown__button--active:hover,.adyen-checkout__dropdown__button:active,.adyen-checkout__dropdown__button:focus { - border-color: #0075ff; - box-shadow: 0 0 0 2px #3070ed -} - -.adyen-checkout__dropdown__button--readonly,.adyen-checkout__dropdown__button--readonly--active,.adyen-checkout__dropdown__button--readonly:focus,.adyen-checkout__dropdown__button--readonly:hover { - background: #e6e9eb; - border-color: transparent; - color: #00112c; - cursor: not-allowed -} - -.adyen-checkout__dropdown__button--readonly:after { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='7' fill='none'%3E%3Cpath fill='%23B9C4C9' d='M3.195 6.565a1 1 0 0 0 1.6 0l2.992-3.98a1 1 0 0 0-.8-1.602H1.013a1 1 0 0 0-.8 1.6z'/%3E%3C/svg%3E") -} - -.adyen-checkout__dropdown__button--invalid { - border-color: #c12424 -} - -.adyen-checkout__dropdown__button--valid { - border-bottom-color: #089a43 -} - -.adyen-checkout__dropdown__button__text { - flex-grow: 1; - overflow: hidden; - pointer-events: none; - text-align: left; - text-overflow: ellipsis; - white-space: nowrap -} - -.adyen-checkout__dropdown__button__secondary-text { - margin-right: 16px -} - -.adyen-checkout__dropdown__list { - border-radius: 6px; - box-shadow: 0 2px 7px rgba(0,15,45,.3); - max-height: 375px; - z-index: 2 -} - -.adyen-checkout__dropdown__list.adyen-checkout__dropdown__list--active { - margin-top: 2px -} - -.adyen-checkout__dropdown__element { - border: 1px solid transparent; - cursor: pointer; - font-size: .81em; - -webkit-hyphens: auto; - hyphens: auto; - line-height: 20px; - outline: 0; - padding: 8px; - transition: background .2s ease-out,border-color .2s ease-out; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - word-break: break-word -} - -.adyen-checkout__dropdown__element .adyen-checkout__icon { - position: absolute; - right: 8px -} - -.adyen-checkout__dropdown__element:last-child { - border-bottom: 0 -} - -.adyen-checkout__dropdown__element.adyen-checkout__dropdown__element--active { - background: rgba(230,233,235,.6) -} - -.adyen-checkout__dropdown__element.adyen-checkout__dropdown__element--selected { - background: rgba(0,102,255,.1) -} - -.adyen-checkout__dropdown__element.adyen-checkout__dropdown__element--selected:active,.adyen-checkout__dropdown__element.adyen-checkout__dropdown__element--selected:focus,.adyen-checkout__dropdown__element.adyen-checkout__dropdown__element--selected:hover { - background: rgba(0,102,255,.15) -} - -.adyen-checkout__dropdown__element--disabled { - cursor: not-allowed; - opacity: .4 -} - -.adyen-checkout__dropdown__element__icon { - border-radius: 3px; - margin-right: 12px; - max-height: 26px; - max-width: 40px -} - -.adyen-checkout__dropdown__element__text { - flex-grow: 1 -} - -.adyen-checkout__dropdown__element__secondary-text:not(:last-child) { - margin-right: 8px -} - -.adyen-checkout__dropdown__element__flag { - margin-left: 8px; - margin-right: 10px; - max-height: 18px; - max-width: 27px -} - -.adyen-checkout__dropdown+.adyen-checkout-input__inline-validation { - right: 32px -} - -.adyen-checkout__address-search { - position: relative -} - -.adyen-checkout__address-search .adyen-checkout__dropdown__button:after { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none'%3E%3Cpath fill='%23687282' d='M6.5 11.9a4.5 4.5 0 0 0 2.6-.83l2.77 2.74c.13.13.3.19.48.19.38 0 .65-.29.65-.66a.63.63 0 0 0-.19-.46l-2.75-2.73a4.4 4.4 0 0 0 .92-2.7 4.48 4.48 0 0 0-8.98 0 4.48 4.48 0 0 0 4.5 4.45m0-.96a3.53 3.53 0 0 1-3.53-3.49 3.52 3.52 0 0 1 7.04 0c0 1.9-1.59 3.49-3.52 3.49Z'/%3E%3C/svg%3E"); - background-position: 50%; - background-repeat: no-repeat; - content: ""; - height: 16px; - left: 12px; - position: absolute; - width: 16px -} - -.adyen-checkout__address-search .adyen-checkout__dropdown__button--active:after { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none'%3E%3Cpath fill='%2300112C' d='M6.5 11.9a4.5 4.5 0 0 0 2.6-.83l2.77 2.74c.13.13.3.19.48.19.38 0 .65-.29.65-.66a.63.63 0 0 0-.19-.46l-2.75-2.73a4.4 4.4 0 0 0 .92-2.7 4.48 4.48 0 0 0-8.98 0 4.48 4.48 0 0 0 4.5 4.45m0-.96a3.53 3.53 0 0 1-3.53-3.49 3.52 3.52 0 0 1 7.04 0c0 1.9-1.59 3.49-3.52 3.49Z'/%3E%3C/svg%3E"); - transform: none -} - -.adyen-checkout__address-search .adyen-checkout__filter-input { - padding-left: 24px -} - -.adyen-checkout__address-search__manual-add { - position: absolute; - right: 0; - top: 0 -} - -.adyen-checkout__address-search__manual-add .adyen-checkout__address-search__manual-add__button { - border: 0; - padding: 0 -} - -.adyen-checkout__checkbox { - display: block -} - -.adyen-checkout__checkbox__label { - color: #00112c; - cursor: pointer; - display: inline-block; - font-size: .81em; - font-weight: 400; - line-height: 19px; - padding-left: 24px; - position: relative; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -[dir=rtl] .adyen-checkout__checkbox__label { - padding-left: 0; - padding-right: 24px -} - -.adyen-checkout__checkbox__input { - opacity: 0; - pointer-events: none; - position: absolute -} - -.adyen-checkout__checkbox__input:checked+.adyen-checkout__checkbox__label:before { - opacity: 1 -} - -.adyen-checkout__checkbox__input:checked+.adyen-checkout__checkbox__label:after { - background-color: #0075ff; - border: 1px solid #0075ff -} - -.adyen-checkout__checkbox__input:checked:hover+.adyen-checkout__checkbox__label:after { - border-color: #0075ff; - box-shadow: 0 0 0 2px rgba(0,102,255,.4) -} - -.adyen-checkout__checkbox__input:focus+.adyen-checkout__checkbox__label:after { - border: 1px solid #0075ff; - box-shadow: 0 0 0 2px #3070ed -} - -.adyen-checkout__checkbox__input:hover:not(:focus)+.adyen-checkout__checkbox__label:after { - border-color: #99a3ad; - box-shadow: 0 0 0 2px #d4d9db -} - -.adyen-checkout__checkbox__input+.adyen-checkout__checkbox__label:before { - border-color: transparent #fff #fff transparent; - border-radius: 0 2px 1px; - border-style: solid; - border-width: 1px 2px 2px 1px; - content: ""; - height: 11px; - left: 1px; - opacity: 0; - position: absolute; - top: 2px; - transform: rotate(37deg); - transform-origin: 100% 100%; - transition: opacity .2s ease-out; - width: 6px; - z-index: 1 -} - -[dir=rtl] .adyen-checkout__checkbox__input+.adyen-checkout__checkbox__label:before { - left: auto; - right: 8px -} - -.adyen-checkout__checkbox__input+.adyen-checkout__checkbox__label:after { - background-color: #fff; - border: 1px solid #b9c4c9; - border-radius: 3px; - content: ""; - height: 16px; - left: 0; - position: absolute; - top: 0; - transition: background .15s ease-out,border .05s ease-out,box-shadow .1s ease-out; - width: 16px; - z-index: 0 -} - -[dir=rtl] .adyen-checkout__checkbox__input+.adyen-checkout__checkbox__label:after { - left: auto; - right: 0 -} - -.adyen-checkout__field--consentCheckbox { - background: #e6e9eb; - border: 1px solid #e6e9eb; - border-radius: 6px; - padding: 14px 14px 13px -} - -[dir=rtl] .adyen-checkout__field--consentCheckbox { - padding: 14px 14px 13px -} - -.adyen-checkout__field--consentCheckbox.adyen-checkout__field--error { - border-color: #c12424 -} - -.adyen-checkout__field--consentCheckbox .adyen-checkout-input__inline-validation { - right: -5px; - top: 10px -} - -.adyen-checkout__open-invoice .adyen-checkout__fieldset--billingAddress { - padding-bottom: 8px -} - -.adyen-checkout__open-invoice .adyen-checkout__fieldset--deliveryAddress { - margin-top: 24px; - padding-bottom: 8px -} - -.adyen-checkout__open-invoice .adyen-checkout__input--separateDeliveryAddress { - margin-bottom: 0 -} - -.adyen-checkout__open-invoice .adyen-checkout__field--consentCheckbox { - margin-top: 22px -} - -.adyen-checkout__input--separateDeliveryAddress+.adyen-checkout__checkbox__label { - margin-top: 16px -} - -.adyen-checkout-form-instruction { - color: #687282; - font-size: .81em; - font-weight: 400; - line-height: 19px; - margin-top: 0 -} - -[dir=rtl] .adyen-checkout-form-instruction { - padding-right: 0 -} - -.adyen-checkout__amazonpay__button { - margin: auto -} - -.adyen-checkout__amazonpay .adyen-checkout__button--ghost { - display: block; - margin: 8px auto 0; - width: auto -} - -@supports (-webkit-appearance: -apple-pay-button) { - .ApplePayButton-module_apple-pay-button__l5g-d,.ApplePayButton-module_apple-pay__gYjuP { - -webkit-appearance:-apple-pay-button - } - - .ApplePayButton-module_apple-pay-button__l5g-d { - cursor: pointer; - display: inline-block - } - - .ApplePayButton-module_apple-pay-button-black__istwW { - -apple-pay-button-style: #000 - } - - .ApplePayButton-module_apple-pay-button-white__-wLaE { - -apple-pay-button-style: #fff - } - - .ApplePayButton-module_apple-pay-button-white-with-line__MlRq7 { - -apple-pay-button-style: white-outline - } - - .ApplePayButton-module_apple-pay-button--type-plain__ycfNl { - -apple-pay-button-type: plain - } - - .ApplePayButton-module_apple-pay-button--type-buy__9m8AB { - -apple-pay-button-type: buy - } - - .ApplePayButton-module_apple-pay-button--type-donate__HmRdK { - -apple-pay-button-type: donate - } - - .ApplePayButton-module_apple-pay-button--type-check-out__XdGWd { - -apple-pay-button-type: check-out - } - - .ApplePayButton-module_apple-pay-button--type-book__-v-VY { - -apple-pay-button-type: book - } - - .ApplePayButton-module_apple-pay-button--type-subscribe__WxWIF { - -apple-pay-button-type: subscribe - } - - .ApplePayButton-module_apple-pay-button--type-add-money__zeBA8 { - -apple-pay-button-type: add-money - } - - .ApplePayButton-module_apple-pay-button--type-contribute__G3E8e { - -apple-pay-button-type: contribute - } - - .ApplePayButton-module_apple-pay-button--type-order__ggI6j { - -apple-pay-button-type: order - } - - .ApplePayButton-module_apple-pay-button--type-reload__QbgLd { - -apple-pay-button-type: reload - } - - .ApplePayButton-module_apple-pay-button--type-rent__VzC-E { - -apple-pay-button-type: rent - } - - .ApplePayButton-module_apple-pay-button--type-support__6EjmY { - -apple-pay-button-type: support - } - - .ApplePayButton-module_apple-pay-button--type-tip__bdzGK { - -apple-pay-button-type: tip - } - - .ApplePayButton-module_apple-pay-button--type-top-up__Eb3qR { - -apple-pay-button-type: top-up - } -} - -@supports not (-webkit-appearance: -apple-pay-button) { - .ApplePayButton-module_apple-pay-button__l5g-d { - background-position:50% 50%; - background-repeat: no-repeat; - background-size: 100% 60%; - border-radius: 5px; - box-sizing: border-box; - display: inline-block; - max-height: 64px; - min-height: 32px; - min-width: 200px; - padding: 0 - } - - .ApplePayButton-module_apple-pay-button-black__istwW { - background-color: #000; - background-image: -webkit-named-image(apple-pay-logo-white) - } - - .ApplePayButton-module_apple-pay-button-white-with-line__MlRq7,.ApplePayButton-module_apple-pay-button-white__-wLaE { - background-color: #fff; - background-image: -webkit-named-image(apple-pay-logo-black) - } - - .ApplePayButton-module_apple-pay-button-white-with-line__MlRq7 { - border: .5px solid #000 - } -} - -.adyen-checkout__applepay__button { - height: 48px; - width: 240px -} - -.adyen-checkout__dropin .adyen-checkout__applepay__button { - width: 100% -} - -.adyen-checkout__issuer-button { - align-items: center; - background-color: #fff; - border: none; - border-radius: 6px; - box-shadow: inset 0 0 0 1px #b9c4c9; - cursor: pointer; - display: flex; - flex-basis: 47%; - flex-grow: 2; - font-size: .81em; - height: 40px; - padding: 0 12px; - transition: background .3s ease-out,box-shadow .3s ease-out -} - -.adyen-checkout__issuer-button:active { - color: #000 -} - -.adyen-checkout__issuer-button:not(.adyen-checkout__issuer-button--selected):focus { - box-shadow: inset 0 0 0 2px #99a3ad; - outline: none -} - -.adyen-checkout__issuer-button:not(.adyen-checkout__issuer-button--selected):focus-visible { - box-shadow: inset 0 0 0 2px #99a3ad; - outline: none -} - -.adyen-checkout__issuer-button:not(.adyen-checkout__issuer-button--selected):hover { - box-shadow: inset 0 0 0 2px #99a3ad; - outline: none -} - -.adyen-checkout__issuer-button--selected { - background: #fff; - box-shadow: inset 0 0 0 2px #0075ff; - color: #0075ff; - font-weight: 500; - height: 40px; - transition: none -} - -.adyen-checkout__issuer-button-img { - margin-right: 8px; - max-height: 26px -} - -.adyen-checkout__issuer-button-group { - display: flex; - flex-wrap: wrap; - gap: 16px 16px -} - -.adyen-checkout__content-separator { - align-items: center; - color: #687282; - display: flex; - font-size: 13px; - justify-content: center; - line-height: 19px; - margin-bottom: 16px; - margin-top: 16px; - white-space: nowrap -} - -.adyen-checkout__content-separator:after,.adyen-checkout__content-separator:before { - background: #e6e9eb; - content: ""; - display: block; - height: 1px; - width: 100% -} - -.adyen-checkout__content-separator:after { - margin-left: 20px -} - -.adyen-checkout__content-separator:before { - margin-right: 20px -} - -.adyen-checkout__field--issuer-list { - margin-bottom: 0 -} - -.adyen-checkout__issuer-list__termsAndConditions { - text-align: center -} - -.adyen-checkout-disclaimer__label { - color: #687282; - display: inline-block; - font-size: .81em; - font-weight: 400; - line-height: 19px; - margin-top: 16px; - padding-left: 0 -} - -[dir=rtl] .adyen-checkout-disclaimer__label { - padding-right: 0 -} - -.adyen-checkout__card-input__form { - transition: opacity .25s ease-out -} - -.adyen-checkout__card__cardNumber { - max-width: 400px -} - -.adyen-checkout__card__cardNumber__input { - padding: 5px 8px -} - -.adyen-checkout__card__exp-date__input--oneclick { - font-weight: 400; - line-height: 30px; - overflow: hidden; - text-align: left; - text-overflow: ellipsis; - white-space: nowrap -} - -.adyen-checkout__field--storedCard .adyen-checkout__input[readonly],.adyen-checkout__field--storedCard .adyen-checkout__input[readonly]:hover { - color: #00112c -} - -.adyen-checkout__card__holderName,.adyen-checkout__field--expiryDate,.adyen-checkout__field--storedCard { - margin-bottom: 0 -} - -.adyen-checkout__card-input .adyen-checkout__fieldset--billingAddress,.adyen-checkout__card__holderName,.adyen-checkout__card__kcp-authentication,.adyen-checkout__card__socialSecurityNumber,.adyen-checkout__installments,.adyen-checkout__store-details { - margin-top: 16px -} - -.adyen-checkout__card-input.adyen-checkout__card-input--loading { - pointer-events: none -} - -.adyen-checkout__card__holderName:first-child { - margin: 0 0 16px -} - -.adyen-checkout__field--cardNumber .adyen-checkout__input--error .adyen-checkout__card__cardNumber__brandIcon,.adyen-checkout__field--cardNumber .adyen-checkout__input--valid:not(.adyen-checkout__card__cardNumber__input--noBrand)+.adyen-checkout-input__inline-validation--valid { - display: none -} - -.adyen-checkout__field--securityCode.adyen-checkout__field--error .adyen-checkout__card__cvc__hint,.adyen-checkout__field--securityCode.adyen-checkout__field--valid .adyen-checkout__card__cvc__hint { - opacity: 0 -} - -@keyframes cvc-indicate-location { - 0% { - opacity: 1 - } - - to { - opacity: .3 - } -} - -.adyen-checkout__label--focused .adyen-checkout__field__cvc--back-hint .adyen-checkout__card__cvc__hint--back .adyen-checkout__card__cvc__hint__location,.adyen-checkout__label--focused .adyen-checkout__field__cvc--front-hint .adyen-checkout__card__cvc__hint--front .adyen-checkout__card__cvc__hint__location { - animation-direction: alternate; - animation-duration: 1s; - animation-iteration-count: infinite; - animation-name: cvc-indicate-location -} - -.adyen-checkout__card__cvc__hint__wrapper { - align-items: center; - -webkit-backface-visibility: visible; - backface-visibility: visible; - display: flex; - height: 100%; - margin: 0 10px; - position: absolute; - right: 0; - top: 0; - transform: translateZ(0); - transform-origin: center; - transform-style: preserve-3d; - transition: transform .3s cubic-bezier(.455,.03,.515,.955); - width: 27px; - will-change: transform -} - -.adyen-checkout__field__cvc--front-hint.adyen-checkout__card__cvc__hint__wrapper { - transform: rotateY(180deg) -} - -.adyen-checkout__card__cvc__hint { - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - position: absolute; - transition: opacity .1s linear -} - -.adyen-checkout__field__exp-date_hint_wrapper { - align-items: center; - bottom: 0; - display: flex; - position: absolute; - right: 0; - top: 0; - transition: opacity .1s linear -} - -.adyen-checkout__field__exp-date_hint_wrapper.adyen-checkout__field__exp-date_hint_wrapper--hidden { - opacity: 0 -} - -.adyen-checkout__field__exp-date_hint { - height: 18px; - margin: 0 10px 0 0; - width: 27px -} - -.adyen-checkout__card__cvc__hint--front { - transform: rotateY(180deg) -} - -@media (prefers-reduced-motion:reduce) { - .adyen-checkout__card__cvc__hint__wrapper { - transition: none - } -} - -.adyen-checkout__fieldset--revolving-plan .adyen-checkout__fieldset__fields { - justify-content: left -} - -.adyen-checkout__fieldset--revolving-plan .adyen-checkout__radio_group { - display: flex; - flex-direction: column -} - -.adyen-checkout__fieldset--revolving-plan .adyen-checkout__radio_group__input-wrapper { - margin-top: 20px -} - -.adyen-checkout__fieldset--revolving-plan .adyen-checkout__field--revolving-plan-installments { - margin-left: 15px; - position: relative; - top: 42px; - width: 30% -} - -.LoadingWrapper-module_loading-input__form__ffCKa { - opacity: 1 -} - -.LoadingWrapper-module_loading-input__form--loading__7GmVo { - opacity: 0 -} - -.LoadingWrapper-module_loading-input__spinner__GxA51 { - display: none; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 1 -} - -.LoadingWrapper-module_loading-input__spinner--active__ENNBS { - display: block -} - -.CardInput-module_card-input__wrapper__wXSCw { - position: relative -} - -.CardInput-module_card-input__wrapper__wXSCw *,.CardInput-module_card-input__wrapper__wXSCw :after,.CardInput-module_card-input__wrapper__wXSCw :before { - box-sizing: border-box -} - -.CardInput-module_card-input__icon__3Cz5M { - border-radius: 3px; - height: 18px; - margin-left: 7px; - position: absolute; - right: 10px; - top: 50%; - transform: translateY(-50%); - width: 27px -} - -.CardInput-module_card-input__form__fRo1r { - opacity: 1 -} - -.CardInput-module_card-input__spinner__-j2Qi { - display: none; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 1 -} - -.CardInput-module_card-input__spinner--active__slD7w { - display: block -} - -.CardInput-module_card-input__form--loading__rrmdj { - opacity: 0 -} - -.CardInput-module_adyen-checkout__input__11tlB { - display: block; - max-height: 100px -} - -.CardInput-module_adyen-checkout__card__cvc__input--hidden__VIlHV,.CardInput-module_adyen-checkout__card__exp-date__input--hidden__evi6- { - display: none -} - -.CardInput-module_adyen-checkout__card__exp-cvc__exp-date__input--hidden__YC3VT { - justify-content: flex-end -} - -.CardInput-module_revolving-plan-installments__disabled__VhNj2 { - opacity: .4; - pointer-events: none -} - -.adyen-checkout__card__dual-branding__buttons { - display: flex; - opacity: .4; - pointer-events: none -} - -.adyen-checkout__card__dual-branding__buttons--active { - opacity: 1; - pointer-events: auto -} - -.adyen-checkout__card__dual-branding__buttons .adyen-checkout__card__cardNumber__brandIcon { - cursor: pointer; - opacity: 1 -} - -.adyen-checkout__card__dual-branding__buttons .adyen-checkout__card__cardNumber__brandIcon:first-child { - right: 40px -} - -.adyen-checkout__card__dual-branding__buttons .adyen-checkout__card__cardNumber__brandIcon--not-selected { - opacity: .5 -} - -.adyen-checkout__card__brands { - display: flex; - flex-basis: auto; - flex-shrink: 1; - flex-wrap: wrap; - gap: 4px; - height: 16px; - margin-bottom: 16px; - margin-top: -8px; - overflow: hidden; - transition: all .2s ease-out -} - -.adyen-checkout__card__brands--hidden { - height: 0; - margin: -8px 0 8px; - opacity: 0 -} - -.adyen-checkout__card__brands img { - border-radius: 3px; - height: 16px; - width: 24px -} - -.adyen-checkout__card__brands__brand-wrapper { - display: inline-block; - height: 16px; - position: relative; - width: 24px -} - -.adyen-checkout__card__brands__brand-wrapper:after { - border: 1px solid rgba(0,27,43,.17); - border-radius: 3px; - content: ""; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100% -} - -.adyen-checkout-ctp__otp-resend-code { - color: #0075ff; - cursor: pointer; - font-size: 13px; - font-weight: 400; - margin-left: auto -} - -.adyen-checkout-ctp__otp-resend-code--confirmation,.adyen-checkout-ctp__otp-resend-code--disabled { - color: #687282; - cursor: default; - font-size: 13px; - font-weight: 400; - margin-left: auto; - pointer-events: none -} - -.adyen-checkout-ctp__otp-resend-code--confirmation { - align-items: center; - display: flex -} - -.adyen-checkout-ctp__otp-resend-code--confirmation>img { - margin-left: 4px -} - -.adyen-checkout-ctp__otp-resend-code-counter { - color: #000; - cursor: default; - display: inline-block; - font-size: 13px; - font-weight: 400; - margin-left: auto; - text-align: right -} - -.adyen-checkout-ctp__section>.adyen-checkout__field.adyen-checkout__field--otp { - margin-bottom: 20px -} - -.adyen_checkout-ctp__brand-wrapper { - align-items: center; - display: flex; - height: 18px -} - -.adyen_checkout-ctp__brand-logo { - margin-right: 6px; - width: 24px -} - -.adyen_checkout-ctp__brand-pipe { - height: 15px; - margin-right: 6px -} - -.adyen_checkout-ctp__brand-scheme { - margin-right: 6px; - -o-object-fit: none; - object-fit: none -} - -.adyen_checkout-ctp__brand-scheme-mc { - width: 27px -} - -.adyen_checkout-ctp__brand-scheme-visa { - width: 35px -} - -.adyen-checkout__modal-wrapper { - align-items: center; - display: flex; - height: 100%; - left: 0; - overflow-y: auto; - overscroll-behavior-y: contain; - padding: 24px; - position: fixed; - top: 0; - visibility: hidden; - width: 100%; - z-index: 10 -} - -.adyen-checkout__modal-wrapper:before { - background: rgba(0,17,44,.5); - content: ""; - height: 100%; - left: 0; - opacity: 0; - position: fixed; - top: 0; - transition: opacity .3s linear; - width: 100%; - z-index: 10 -} - -.adyen-checkout__modal-wrapper--open { - visibility: visible -} - -.adyen-checkout__modal-wrapper--open .adyen-checkout__modal,.adyen-checkout__modal-wrapper--open:before { - opacity: 1 -} - -.adyen-checkout__modal { - background-color: #fff; - border-radius: 12px; - box-shadow: 0 8px 24px rgba(0,17,44,.15); - margin: auto; - opacity: 0; - padding: 16px; - position: relative; - transition: opacity .2s ease,visibility .2s ease; - z-index: 11 -} - -@media (max-width: 480px) { - .adyen-checkout__modal-wrapper { - padding:0 - } - - .adyen-checkout__modal { - border-radius: 0; - height: 100% - } -} - -.adyen-checkout__ctp-modal-header-image { - display: block; - margin: auto -} - -.adyen-checkout__ctp-modal-title { - font-size: 20px; - font-weight: 700; - line-height: 24px; - margin: 0 0 12px; - padding: 0 -} - -.adyen-checkout__ctp-modal-text { - font-size: 13px; - font-weight: 400; - line-height: 19px; - margin-bottom: 16px -} - -.adyen-checkout__ctp-modal-benefits { - margin-left: 0; - padding-left: 20px -} - -.adyen-checkout__ctp-modal-benefits li { - list-style: disc; - margin-bottom: 16px -} - -.adyen-checkout__modal-wrapper--ctp .adyen-checkout__modal { - max-width: 464px -} - -.adyen_checkout-ctp__brand-wrapper--popup { - justify-content: center; - margin-bottom: 24px -} - -.adyen-web__ctp-info-button { - background-color: transparent; - border: 0; - cursor: pointer; - padding: 0 -} - -.adyen-checkout-ctp__section-logout-button { - color: #0075ff; - cursor: pointer; - font-size: 13px; - font-weight: 400; - line-height: 19px; - margin-left: auto -} - -.adyen-checkout-ctp__section-logout-button--disabled { - color: #687282; - pointer-events: none -} - -.adyen-checkout-ctp__section { - background-color: #fff; - border-radius: 12px; - box-shadow: 0 8px 24px rgba(0,0,0,.15); - padding: 16px; - position: relative -} - -.adyen-checkout-ctp__section-brand { - align-items: center; - display: flex; - height: 18px; - margin-bottom: 14px -} - -.adyen-checkout-ctp__section--standalone { - all: unset -} - -.adyen-checkout-ctp__section .adyen-checkout__fieldset { - margin-bottom: 24px -} - -.adyen-checkout-ctp__section-header { - align-items: center; - display: flex -} - -.adyen-checkout-ctp__section-header-title { - font-size: 17px; - font-weight: 600; - line-height: 22px; - margin: 0 0 4px; - padding: 0; - width: auto -} - -@media screen and (max-width: 400px) { - .adyen-checkout-ctp__section-header-title { - font-size:15px - } -} - -.adyen-checkout-ctp__section-header-adornment { - margin-left: 5px -} - -.adyen-checkout-ctp__section-text { - color: #687282; - font-size: 13px; - font-weight: 400; - line-height: 19px; - margin: 0 0 16px -} - -.adyen-checkout-ctp__separator { - color: #00112c; - font-size: 13px; - font-weight: 400 -} - -.adyen-checkout-ctp__otp-subtitle--highlighted { - color: #00112c; - font-weight: 500 -} - -.adyen-checkout-ctp__card-list-single-card { - align-items: center; - background-color: #f7f8f9; - border-radius: 6px; - display: flex; - font-size: 13px; - font-weight: 400; - height: 40px; - line-height: 19px; - padding: 12px -} - -.adyen-checkout-ctp__card-list-single-card-expired { - color: #687282; - text-decoration: line-through -} - -.adyen-checkout-ctp__expired-label { - color: #687282; - font-weight: 500; - line-height: 17px; - margin-left: auto -} - -.adyen-checkout-ctp__card-image { - border-radius: 3px; - margin-right: 8px -} - -.adyen-checkout-ctp__cards-list-dropdown .adyen-checkout__dropdown__element--disabled { - opacity: 1 -} - -.adyen-checkout-ctp__cards-list-dropdown .adyen-checkout__dropdown__element--disabled .adyen-checkout__dropdown__element__text { - margin: 0; - opacity: .4; - text-decoration: line-through -} - -.adyen-checkout-ctp__cards-list-dropdown .adyen-checkout__dropdown__element--disabled .adyen-checkout__dropdown__element__secondary-text { - color: #687282; - font-weight: 500; - line-height: 17px -} - -.adyen-checkout-ctp__cards-list-dropdown .adyen-checkout__dropdown__button--disabled { - opacity: 1 -} - -.adyen-checkout-ctp__cards-list-dropdown .adyen-checkout__dropdown__button--disabled .adyen-checkout__dropdown__button__text { - opacity: .4; - text-decoration: line-through -} - -.adyen-checkout-ctp__cards-list-dropdown .adyen-checkout__dropdown__button--disabled .adyen-checkout__dropdown__button__secondary-text { - color: #687282; - font-weight: 500; - opacity: 1 -} - -.adyen-checkout-ctp__card { - background-color: #fff; - border: none; - border-radius: 4px; - box-shadow: 0 0 0 2px #999595; - cursor: pointer; - height: 40px; - margin: 0 0 20px; - width: 100% -} - -.adyen-checkout-ctp__empty-cards { - align-items: center; - background-color: #f7f8f9; - border-radius: 6px; - color: #687282; - display: flex; - font-size: 13px; - font-weight: 400; - height: 40px; - line-height: 19px; - margin-bottom: 24px; - padding: 12px 16px -} - -.adyen-checkout__iframe--ctpIframe { - pointer-events: auto -} - -.adyen-checkout__iframe--ctpIframe-hidden { - display: none -} - -.adyen-checkout-ctp__loading-image { - display: block; - margin: 30px auto auto -} - -.adyen-checkout-ctp__loading-subtitle { - font-size: 16px; - line-height: 19px; - margin: 0 auto 58px; - max-width: 280px; - text-align: center -} - -.adyen-checkout-ctp__section>.adyen-checkout__field.adyen-checkout__field--shopperLogin { - margin-bottom: 20px -} - -.adyen-checkout__cashapp>.adyen-checkout__store-details { - margin-bottom: 16px; - margin-top: 0 -} - -.adyen-checkout__button-group { - background: transparent; - display: flex; - justify-content: space-between -} - -.adyen-checkout__button-group .adyen-checkout__button { - background: transparent; - border: 0; - box-shadow: inset 0 0 0 1px #99a3ad; - color: #00112c; - font-size: .81em; - font-weight: 400; - height: 40px; - line-height: 40px; - margin-right: 8px; - padding: 0; - text-align: center -} - -.adyen-checkout__button-group .adyen-checkout__button:last-child { - margin-right: 0 -} - -.adyen-checkout__button-group .adyen-checkout__button:hover { - background: transparent; - box-shadow: inset 0 0 0 2px #99a3ad -} - -.adyen-checkout__button-group .adyen-checkout__button:active { - background: #f7f8f9; - box-shadow: inset 0 0 0 2px #99a3ad -} - -.adyen-checkout__button-group .adyen-checkout__button--disabled,.adyen-checkout__button-group .adyen-checkout__button--disabled:hover { - cursor: not-allowed; - opacity: .4; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.adyen-checkout__button-group .adyen-checkout__button--selected,.adyen-checkout__button-group .adyen-checkout__button--selected:active,.adyen-checkout__button-group .adyen-checkout__button--selected:active:hover,.adyen-checkout__button-group .adyen-checkout__button--selected:hover { - background: #e5efff; - box-shadow: inset 0 0 0 2px #0075ff; - color: #0075ff; - font-weight: 500; - height: 40px; - transition: none -} - -.adyen-checkout__button-group .adyen-checkout__button .adyen-checkout__button-group__input { - opacity: 0; - pointer-events: none; - position: absolute -} - -.adyen-checkout__adyen-giving .adyen-checkout__status__icon { - display: block; - margin: 56px auto 32px -} - -.adyen-checkout__adyen-giving .adyen-checkout__status__text { - color: #00112c; - margin-bottom: 56px; - text-align: center -} - -.adyen-checkout__campaign { - background: #00112c; - border-radius: 6px; - height: 227px; - overflow: hidden; - position: relative -} - -.adyen-checkout__campaign-link:hover .adyen-checkout__campaign-description { - text-decoration: underline -} - -.adyen-checkout__campaign-container { - height: 100% -} - -.adyen-checkout__campaign-logo { - border: 2px solid hsla(0,0%,100%,.4); - border-radius: 3px; - display: block; - height: 48px; - margin-bottom: 16px; - overflow: hidden; - width: 48px -} - -.adyen-checkout__campaign-background-image { - background-color: #00112c; - background-position: 50%; - background-size: cover; - height: 100% -} - -.adyen-checkout__campaign-link .adyen-checkout__campaign-background-image:before { - background: inherit; - content: ""; - height: 100%; - position: absolute; - transition: transform .6s ease-out; - width: 100% -} - -.adyen-checkout__campaign-link .adyen-checkout__campaign-background-image:hover:before { - transform: scale(1.1) -} - -.adyen-checkout__campaign-link .adyen-checkout__campaign-content { - pointer-events: none -} - -.adyen-checkout__campaign-content { - bottom: 0; - padding: 16px; - position: absolute; - z-index: 2 -} - -.adyen-checkout__campaign-description,.adyen-checkout__campaign-title { - color: #fff; - font-weight: 400; - margin: 0 -} - -.adyen-checkout__campaign-title { - font-size: 1em; - margin-bottom: 8px -} - -.adyen-checkout__campaign-description { - font-size: .81em; - line-height: 19px -} - -.adyen-checkout__adyen-giving-actions { - margin-top: 16px; - text-align: center -} - -.adyen-checkout__button.adyen-checkout__button--donate { - margin: 16px auto 8px -} - -.adyen-checkout__button.adyen-checkout__button--decline { - display: block; - margin: auto; - width: auto -} - -.adyen-checkout__paywithgoogle { - height: 48px -} - -.adyen-checkout__paywithgoogle>div>button,.adyen-checkout__paywithgoogle>div>button.long,.adyen-checkout__paywithgoogle>div>button.short { - height: 48px; - transition: background-color .3s ease-out,box-shadow .3s ease-out -} - -.adyen-checkout__paywithgoogle>div>button.long:focus,.adyen-checkout__paywithgoogle>div>button.short:focus,.adyen-checkout__paywithgoogle>div>button:focus { - box-shadow: 0 0 0 2px #99c2ff; - outline: 0 -} - -.adyen-checkout__paywithgoogle>div>button.gpay-button { - padding: 15px 24px 13px -} - -.adyen-checkout__econtext-input__field>.adyen-checkout__button--pay:only-child { - margin-top: 0 -} - -.adyen-checkout__voucher-result { - border-radius: 12px; - box-sizing: border-box; - position: relative; - text-align: center -} - -.adyen-checkout__voucher-result__bottom,.adyen-checkout__voucher-result__top { - background: #fff; - border: 1px solid #d4d9db -} - -.adyen-checkout__voucher-result__top { - border-bottom: 0; - border-radius: 12px 12px 0 0; - padding: 40px 0 24px -} - -.adyen-checkout__voucher-result__bottom { - border-radius: 0 0 12px 12px; - border-top: 0 -} - -.adyen-checkout__voucher-result__separator { - align-items: center; - background: #fff; - display: flex; - height: 13px; - margin: 0 auto; - position: relative; - width: calc(100% - 14px) -} - -.adyen-checkout__voucher-result__separator:after,.adyen-checkout__voucher-result__separator:before { - background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3IiBoZWlnaHQ9IjEzIiBmaWxsPSJub25lIj48ZyBjbGlwLXBhdGg9InVybCgjYSkiPjxwYXRoIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0ibTAgMCA1IDIgMS41IDRIN1Ywem0wIDEzIDUtMiAxLjUtNEg3djZ6IiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBzdHJva2U9IiNENEQ5REIiIGQ9Ik02LjQyMyA2LjVDNi40MjMgMy4zMTIgMy43ODMuNzU2LjUuNTE4YzMuMzg2LjIzNiA2IDIuODU1IDYgNS45ODJzLTIuNjE0IDUuNzQ2LTYgNS45ODN2LS4wMDFjMy4yODQtLjIzNyA1LjkyMy0yLjc5NCA1LjkyMy01Ljk4MloiLz48cGF0aCBmaWxsPSIjRDREOURCIiBkPSJNMCAwaDF2MUgwem0wIDEyaDF2MUgweiIvPjwvZz48ZGVmcz48Y2xpcFBhdGggaWQ9ImEiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0wIDBoN3YxM0gweiIvPjwvY2xpcFBhdGg+PC9kZWZzPjwvc3ZnPg==) -} - -.adyen-checkout__voucher-result__separator:before { - left: -7px -} - -.adyen-checkout__voucher-result__separator:after,.adyen-checkout__voucher-result__separator:before { - background-position: 100%; - background-repeat: no-repeat; - content: ""; - height: 13px; - position: absolute; - top: 0; - width: 7px -} - -.adyen-checkout__voucher-result__separator:after { - right: -7px; - transform: rotate(-180deg) -} - -.adyen-checkout__voucher-result__separator__inner { - border-top: 1px solid #e6e9eb; - width: 100% -} - -.adyen-checkout__voucher-result__image { - align-items: center; - display: flex; - justify-content: center; - margin-bottom: 40px; - width: 100% -} - -.adyen-checkout__link--voucher-result-instructions { - display: inline-block -} - -.adyen-checkout__voucher-result__image__wrapper { - display: block; - height: 48px; - margin: 0 24px; - position: relative -} - -.adyen-checkout__voucher-result__image__wrapper:after { - border: 1px solid rgba(0,27,43,.17); - border-radius: 3px; - content: ""; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100% -} - -.adyen-checkout__voucher-result__image__wrapper:nth-child(2):before { - border-left: 1px solid #d4d9db; - content: ""; - height: 64px; - left: -24.5px; - position: absolute; - top: -8px; - width: 1px -} - -.adyen-checkout__voucher-result__image__brand,.adyen-checkout__voucher-result__image__issuer { - border-radius: 3px; - height: 48px -} - -.adyen-checkout__voucher-result__introduction { - color: #00112c; - font-size: .81em; - line-height: 19px; - margin: 0 auto; - max-width: 400px; - text-align: center -} - -.adyen-checkout__voucher-result__amount { - color: #00112c; - font-size: 1em; - font-weight: 700; - margin: 24px auto 0; - text-align: center -} - -.adyen-checkout__voucher-result__surcharge { - color: #687282; - display: block; - font-size: .81em; - font-weight: 400; - line-height: 19px; - text-align: center -} - -.adyen-checkout__voucher-result__code__label { - display: block; - font-weight: 400; - left: 0; - line-height: 19px; - margin: 0 auto; - position: absolute; - right: 0; - top: -2px; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - width: auto -} - -.adyen-checkout__voucher-result__code__label:before { - content: ""; - position: absolute -} - -.adyen-checkout__voucher-result__code__label__text { - background: #fff; - color: #00112c; - font-size: 13px; - letter-spacing: normal; - line-height: 1; - padding: 0 8px -} - -.adyen-checkout__voucher-result__code__barcode { - display: block; - height: 56px; - margin: 0 auto 8px; - max-width: 100%; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.adyen-checkout__voucher-result__code { - border-width: 1px 0; - color: #00112c; - display: inline-block; - font-size: 1.5em; - font-weight: 700; - letter-spacing: 1px; - line-height: 1.2; - margin: 0 auto; - padding: 16px 48px; - position: relative; - text-align: center; - -webkit-user-select: all; - -moz-user-select: all; - user-select: all; - width: 100%; - word-break: break-word -} - -.adyen-checkout__voucher-result__details { - list-style: none; - margin: -1px auto 0; - padding: 0 -} - -.adyen-checkout__voucher-result__details__item { - border-top: 1px solid #e6e9eb; - color: #00112c; - display: flex; - font-size: .81em; - justify-content: space-between; - padding: 16px 24px; - word-break: break-word -} - -.adyen-checkout__voucher-result__details__item:last-child { - margin-bottom: 0 -} - -.adyen-checkout__voucher-result__details__label { - max-width: 50%; - text-align: left -} - -.adyen-checkout__voucher-result__details__value { - font-weight: 700; - max-width: 50%; - text-align: right -} - -.adyen-checkout__voucher-result__actions { - align-items: center; - display: flex; - justify-content: center; - list-style: none; - margin: 0 auto 32px; - max-width: 100%; - min-width: 200px; - padding: 0; - width: 300px -} - -.adyen-checkout__voucher-result__actions__item { - margin: 0 4px -} - -.adyen-checkout__paypal__buttons { - position: relative; - z-index: 0 -} - -.adyen-checkout__paypal__button { - display: flex; - margin-bottom: 16px -} - -.adyen-checkout__paypal__button:empty { - display: none -} - -.adyen-checkout__paypal__status--pending { - margin: 16px 0 -} - -.adyen-checkout__paypal__status--processing { - align-items: center; - display: flex; - font-size: 13px; - justify-content: center; - padding: 24px 0 -} - -.adyen-checkout__paypal-processing .adyen-checkout__paypal__button { - display: none -} - -.adyen-checkout__payment-method .adyen-checkout__paypal__status--pending { - margin: -16px 0 38px -} - -.adyen-checkout__payment-method .adyen-checkout__paypal__status--processing { - padding: 20px 0 65px -} - -.adyen-checkout__phone-input { - direction: ltr -} - -.adyen-checkout__phone-input .adyen-checkout__input-wrapper { - width: 100% -} - -.adyen-checkout__phone-input .adyen-checkout__input-wrapper .adyen-checkout__input { - height: auto; - padding: 0 -} - -.adyen-checkout__phone-input .adyen-checkout__input-wrapper .adyen-checkout__input:focus { - border: 1px solid #0075ff; - box-shadow: 0 0 0 2px #99c2ff -} - -.adyen-checkout__phone-input .adyen-checkout__input-wrapper .adyen-checkout__dropdown__button { - border: 0; - border-bottom-right-radius: 0; - border-top-right-radius: 0; - height: 35px; - width: auto -} - -.adyen-checkout__phone-input .adyen-checkout__input-wrapper .adyen-checkout__dropdown__button:after { - box-sizing: revert; - height: 10px; - left: 40px -} - -.adyen-checkout__phone-input .adyen-checkout__input-wrapper .adyen-checkout__input--phoneNumber { - border: 1px solid transparent; - height: 35px; - margin-left: 8px; - padding-left: 15px -} - -.adyen-checkout__phone-input .adyen-checkout__input-wrapper .adyen-checkout__input-wrapper--phoneInput { - align-items: center; - display: flex -} - -.adyen-checkout__phone-input .adyen-checkout__input-wrapper .adyen-checkout__input-wrapper--phoneInput:focus { - border: 1px solid #0075ff; - box-shadow: 0 0 0 2px #99c2ff -} - -.adyen-checkout__phone-input .adyen-checkout__input-wrapper .adyen-checkout__phoneNumber { - align-items: center; - display: flex; - margin-left: 65px; - width: 100% -} - -.adyen-checkout__phone-input .adyen-checkout__input-wrapper .adyen-checkout__countryFlag { - position: absolute -} - -.adyen-checkout__phone-input .adyen-checkout__input-wrapper .adyen-checkout__dropdown__button--active,.adyen-checkout__phone-input .adyen-checkout__input-wrapper .adyen-checkout__dropdown__button--active:hover { - box-shadow: none -} - -.adyen-checkout__threeds2__challenge,.adyen-checkout__threeds2__challenge-container { - background-color: transparent; - box-sizing: border-box; - display: block; - height: inherit; - min-height: 400px; - overflow: hidden; - position: relative; - width: 100% -} - -.adyen-checkout__threeds2__challenge--01,.adyen-checkout__threeds2__challenge--01 .adyen-checkout__iframe--threeDSIframe { - height: 400px; - width: 250px -} - -.adyen-checkout__threeds2__challenge--02,.adyen-checkout__threeds2__challenge--02 .adyen-checkout__iframe--threeDSIframe { - height: 400px; - width: 390px -} - -.adyen-checkout__threeds2__challenge--03,.adyen-checkout__threeds2__challenge--03 .adyen-checkout__iframe--threeDSIframe { - height: 600px; - width: 500px -} - -.adyen-checkout__threeds2__challenge--04,.adyen-checkout__threeds2__challenge--04 .adyen-checkout__iframe--threeDSIframe { - height: 400px; - width: 600px -} - -.adyen-checkout__threeds2__challenge--05,.adyen-checkout__threeds2__challenge--05 .adyen-checkout__iframe--threeDSIframe { - height: 100%; - width: 100% -} - -.adyen-checkout__iframe--threeDSIframe { - border: 0; - left: 0; - position: absolute; - top: 0 -} - -.adyen-checkout__threeds2-challenge-error .adyen-checkout__status__icon { - display: block; - margin: 56px auto 32px -} - -.adyen-checkout__threeds2-challenge-error .adyen-checkout__status__text { - color: #c12424; - margin-bottom: 56px; - text-align: center -} - -.adyen-checkout__qr-loader { - background: #fff; - border: 1px solid #d4d9db; - border-radius: 12px; - padding: 40px; - text-align: center -} - -.adyen-checkout__qr-loader--result { - padding: 100px -} - -.adyen-checkout__qr-loader__brand-logo { - border-radius: 3px; - width: 74px -} - -.adyen-checkout__qr-loader__subtitle { - margin: 32px auto 0; - max-width: 400px -} - -.adyen-checkout__qr-loader__subtitle--result { - margin-bottom: 32px -} - -.adyen-checkout__qr-loader__payment_amount,.adyen-checkout__qr-loader__subtitle { - color: #00112c; - font-size: 1em; - line-height: 19px -} - -.adyen-checkout__qr-loader__icon { - height: 88px; - width: 88px -} - -.adyen-checkout__qr-loader__payment_amount { - font-weight: 700 -} - -.adyen-checkout__qr-loader__progress { - background: #d4d9db; - border-radius: 24px; - height: 4px; - margin: 32px auto 12px; - padding-right: 3%; - width: 152px -} - -[dir=rtl] .adyen-checkout__qr-loader__progress { - padding-left: 3%; - padding-right: 0 -} - -.adyen-checkout__qr-loader__percentage { - background: #0075ff; - border-radius: 24px; - display: block; - height: 100% -} - -.adyen-checkout__qr-loader__countdown { - color: #687282; - font-size: .81em -} - -.adyen-checkout__qr-loader>.adyen-checkout__spinner__wrapper { - margin: 60px 0 -} - -.adyen-checkout__qr-loader__app-link { - display: none; - margin-top: 16px -} - -.adyen-checkout__button.adyen-checkout__button--qr-loader { - margin-top: 24px; - text-decoration: none -} - -.adyen-checkout__qr-loader__instructions { - color: #687282; - font-size: 1em; - line-height: 1.5; - margin-top: 32px -} - -.adyen-checkout__qr-loader__actions { - align-items: center; - display: flex; - justify-content: center; - margin-top: 32px -} - -@media only screen and (max-device-width: 1200px) { - .adyen-checkout__qr-loader__app-link { - display:block - } -} - -.adyen-checkout__voucher-result--boletobancario .adyen-checkout__voucher-result__code,.adyen-checkout__voucher-result--oxxo .adyen-checkout__voucher-result__code { - font-size: .81em; - line-height: 19px; - padding: 24px; - word-break: break-all -} - -.adyen-checkout__alert-message { - border-radius: 6px; - display: flex; - font-size: .81em; - margin: 0 0 16px; - padding: 12px; - text-align: left -} - -.adyen-checkout__alert-message--error { - background: #fbe6ed -} - -.adyen-checkout__alert-message--warning { - background: #ffeacc -} - -.adyen-checkout__alert-message--info { - background: #e5efff -} - -.adyen-checkout__alert-message__icon { - height: 14px; - margin-right: 8px; - width: 14px -} - -.adyen-checkout__giftcard-result__header { - align-items: center; - display: flex; - flex-wrap: nowrap; - font-size: 1em; - font-weight: 400; - justify-content: space-between; - position: relative; - width: 100% -} - -.adyen-checkout__giftcard-result__header__title { - align-items: center; - display: flex -} - -.adyen-checkout__giftcard-result__name { - margin-left: 8px -} - -.adyen-checkout__giftcard-result__balance { - list-style: none; - margin: 16px 0 0; - padding: 0 -} - -.adyen-checkout__giftcard-result__balance__item { - display: flex; - justify-content: space-between; - margin-bottom: 8px -} - -.adyen-checkout__giftcard-result__balance__item .adyen-checkout__giftcard-result__balance__title--transactionLimit { - color: #687282 -} - -.adyen-checkout__giftcard-result__balance__item:last-child { - margin-bottom: 0 -} - -.adyen-checkout__giftcard-result__balance__value--amount { - font-weight: 700 -} - -.adyen-checkout__giftcard-result__remaining-balance { - color: #687282; - font-size: 13px; - line-height: 19px; - margin: 8px auto 0; - text-align: center -} - -.DropinComponent-module_adyen-checkout__payment-methods-list__mAjAm { - list-style: none; - margin: 0; - padding: 0 -} - -.DropinComponent-module_adyen-checkout__payment-method__nWdwg { - display: block; - max-height: 60px -} - -.DropinComponent-module_adyen-checkout__payment-method__details__-rsW7 { - display: none -} - -.DropinComponent-module_adyen-checkout__payment-method__image__nB80V { - height: 26px; - width: 40px -} - -.DropinComponent-module_adyen-checkout__payment-method__image__wrapper__6NWzA { - margin-right: 8px -} - -[dir=rtl] .DropinComponent-module_adyen-checkout__payment-method__image__wrapper__6NWzA { - margin-left: 8px; - margin-right: 0 -} - -.DropinComponent-module_adyen-checkout__payment-method--selected__6egZF { - max-height: 100% -} - -.DropinComponent-module_adyen-checkout__payment-method--selected__6egZF .DropinComponent-module_adyen-checkout__payment-method__details__-rsW7 { - display: block -} - -.adyen-checkout__payment-method__disable-confirmation { - background: #c12424; - border-left: 1px solid #b82222; - border-right: 1px solid #b82222; - color: #fff; - font-size: .81em; - margin: 0 -17px; - max-height: 0; - opacity: 0; - overflow: hidden; - transition: opacity .15s ease-out,max-height .15s linear,margin-bottom .1s linear -} - -.adyen-checkout__payment-method__disable-confirmation.adyen-checkout__payment-method__disable-confirmation--open { - margin-bottom: 16px; - max-height: 62px; - opacity: 1 -} - -.adyen-checkout__payment-method__disable-confirmation__content { - align-items: center; - display: flex; - justify-content: space-between; - padding: 8px 16px -} - -.adyen-checkout__payment-method__disable-confirmation__buttons { - display: flex -} - -.adyen-checkout__payment-method__disable-confirmation__button { - background: #c12424; - border: 1px solid transparent; - border-radius: 6px; - color: #fff; - cursor: pointer; - display: block; - height: auto; - line-height: 14px; - margin: 0 0 0 8px; - padding: 8px; - width: auto -} - -.adyen-checkout__payment-method__disable-confirmation__button:hover,.adyen-checkout__payment-method__disable-confirmation__button:hover:focus { - background: #ac2020; - box-shadow: none -} - -.adyen-checkout__payment-method__disable-confirmation__button:active,.adyen-checkout__payment-method__disable-confirmation__button:hover:active { - background: #961c1c; - box-shadow: none -} - -.adyen-checkout__payment-method__disable-confirmation__button--remove,.adyen-checkout__payment-method__disable-confirmation__button--remove:disabled { - border-color: #fff -} - -.adyen-checkout__payment-method__disable-confirmation__button--cancel,.adyen-checkout__payment-method__disable-confirmation__button--cancel:disabled { - border-color: transparent -} - -.adyen-checkout__payment-method { - background: #fff; - border: 1px solid #e6e9eb; - cursor: pointer; - margin-top: -1px; - position: relative; - transition: opacity .3s ease-out; - width: 100% -} - -.adyen-checkout__payment-method:focus { - outline: 0 -} - -.adyen-checkout__payment-method--selected+.adyen-checkout__payment-method,.adyen-checkout__payment-method:first-child { - border-top-left-radius: 12px; - border-top-right-radius: 12px; - margin-top: 0 -} - -.adyen-checkout__payment-method--next-selected,.adyen-checkout__payment-method:last-child { - border-bottom-left-radius: 12px; - border-bottom-right-radius: 12px; - margin-bottom: 0 -} - -.adyen-checkout__payment-method--loading { - opacity: .2 -} - -.adyen-checkout__payment-method--selected.adyen-checkout__payment-method--loading { - opacity: .9 -} - -.adyen-checkout__payment-method--confirming .adyen-checkout__payment-method__details__content,.adyen-checkout__payment-method--disabling { - pointer-events: none; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.adyen-checkout__payment-method--disabling { - opacity: .3 -} - -.adyen-checkout__payment-method__header { - align-items: center; - color: #00112c; - display: flex; - flex-wrap: nowrap; - font-size: 1em; - font-weight: 400; - justify-content: space-between; - padding: 12px 16px 12px 44px; - position: relative; - transition: background .1s ease-out; - width: 100% -} - -[dir=rtl] .adyen-checkout__payment-method__header { - padding: 12px 44px 12px 12px -} - -.adyen-checkout__payment-method--standalone .adyen-checkout__payment-method__header { - padding: 16px -} - -.adyen-checkout__payment-method__header__title { - align-items: center; - background: none; - border: none; - color: #00112c; - cursor: pointer; - display: flex; - flex-shrink: 0; - font-size: 1em; - font-weight: 400; - margin-right: 16px; - max-width: 100%; - padding: 4px -} - -[dir=rtl] .adyen-checkout__payment-method__header__title { - margin-left: 16px; - margin-right: 0 -} - -.adyen-checkout__payment-method__surcharge { - color: #687282; - margin-left: 5px -} - -.adyen-checkout__payment-method--selected { - background: #f7f8f9; - border: 1px solid #e6e9eb; - border-radius: 12px; - cursor: default; - margin: 8px 0; - transition: margin .15s cubic-bezier(.4,0,.2,1) 0s,opacity .3s ease-out -} - -.adyen-checkout__payment-method--selected .adyen-checkout__payment-method__header { - flex-wrap: wrap -} - -.adyen-checkout__payment-method__details { - padding: 0 16px; - position: relative -} - -.adyen-checkout__payment-method__details__content { - margin: 0 0 16px -} - -.adyen-checkout__payment-method__image__wrapper { - height: 26px; - position: relative; - width: 40px -} - -.adyen-checkout__payment-method__image__wrapper--outline:after { - border: 1px solid rgba(0,27,43,.17); - border-radius: 3px; - content: ""; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100% -} - -.adyen-checkout__payment-method__image { - border-radius: 3px; - display: block -} - -.adyen-checkout__payment-method__brands { - display: flex; - flex-basis: auto; - flex-shrink: 1; - flex-wrap: wrap; - height: 16px; - margin: 4px 0; - overflow: hidden; - text-align: right -} - -.adyen-checkout__payment-method__brands .adyen-checkout__payment-method__brand-number { - color: #687282; - font-size: 13px -} - -.adyen-checkout__payment-method--selected .adyen-checkout__payment-method__brands { - height: auto; - overflow: visible; - text-align: left -} - -.adyen-checkout__payment-method__brands .adyen-checkout__payment-method__image__wrapper { - display: inline-block; - height: 16px; - margin-right: 4px; - transition: opacity .2s ease-out; - width: 24px -} - -.adyen-checkout__payment-method__brands .adyen-checkout__payment-method__image__wrapper:last-child { - margin: 0 -} - -.adyen-checkout__payment-method--selected .adyen-checkout__payment-method__brands .adyen-checkout__payment-method__image__wrapper { - margin-bottom: 4px -} - -.adyen-checkout__payment-method__brands img { - height: 16px; - width: 24px -} - -.adyen-checkout__payment-method__image__wrapper--disabled { - opacity: .25 -} - -.adyen-checkout__payment-method__radio { - background-color: #fff; - border: 1px solid #b9c4c9; - border-radius: 50%; - height: 16px; - left: 16px; - position: absolute; - transition: border-color .2s ease-out,box-shadow .2s ease-out; - width: 16px -} - -[dir=rtl] .adyen-checkout__payment-method__radio { - left: auto; - right: 16px -} - -.adyen-checkout__payment-method--standalone .adyen-checkout__payment-method__radio { - display: none -} - -.adyen-checkout__payment-method__radio:after { - background-color: #fff; - border-radius: 50%; - content: ""; - display: block; - height: 6px; - left: 0; - margin: 0 auto; - position: absolute; - right: 0; - top: 50%; - transform: translateY(-50%) scale(0); - transition: transform .3s ease-out; - width: 6px -} - -.adyen-checkout__payment-method:hover:not(.adyen-checkout__payment-method--selected) .adyen-checkout__payment-method__radio { - border-color: #99a3ad; - box-shadow: 0 0 0 2px #d4d9db; - cursor: pointer -} - -.adyen-checkout__payment-method__radio--selected { - background-color: #0075ff; - border: 0; - transition: all .3s ease-out -} - -.adyen-checkout__payment-method__radio--selected:hover { - box-shadow: 0 0 0 2px rgba(0,102,255,.4) -} - -.adyen-checkout__payment-method__radio--selected:after { - transform: translateY(-50%) scale(1) -} - -.adyen-checkout__payment-method__name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.adyen-checkout__payment-method__name--selected { - font-weight: 500 -} - -.adyen-checkout__payment-method__additional-info { - color: #687282; - font-size: .81em -} - -.adyen-checkout__payment-method__name_wrapper { - align-items: flex-start; - display: flex; - flex-direction: column -} - -.adyen-checkout__order-payment-methods-list { - list-style: none; - margin: 0 auto 16px; - padding: 0 -} - -.adyen-checkout__order-payment-method { - background: #fff; - border: 1px solid #e6e9eb; - margin-top: -1px; - position: relative; - width: 100% -} - -.adyen-checkout__order-payment-method:first-child { - border-top-left-radius: 12px; - border-top-right-radius: 12px -} - -.adyen-checkout__order-payment-method:last-child { - border-bottom-left-radius: 12px; - border-bottom-right-radius: 12px -} - -.adyen-checkout__order-payment-method__header { - align-items: center; - color: #00112c; - display: flex; - flex-wrap: nowrap; - font-size: 1em; - font-weight: 500; - justify-content: space-between; - padding: 16px; - position: relative; - transition: background .1s ease-out; - width: 100% -} - -.adyen-checkout__order-payment-method__header .adyen-checkout__payment-method__header__title { - padding: 0 -} - -.adyen-checkout__order-payment-method__details { - padding: 0 16px 16px -} - -.adyen-checkout__order-payment-method__deducted-amount { - display: flex; - font-size: 1em; - justify-content: space-between; - line-height: 1em -} - -.adyen-checkout__order-payment-method__deducted-amount__label { - font-size: .81em -} - -.adyen-checkout__order-payment-method__deducted-amount__value { - font-weight: 500 -} - -.adyen-checkout__order-remaining-amount { - background: #ffeacc; - border-radius: 6px; - color: #7f4a00; - display: block; - font-size: .81em; - margin-bottom: 16px; - padding: 8px 16px; - width: 100% -} - -.adyen-checkout__order-remaining-amount strong { - font-weight: 700 -} - -.adyen-checkout__status { - align-items: center; - background-color: #fff; - border: 1px solid #d4d9db; - border-radius: 6px; - color: #00112c; - display: flex; - flex-direction: column; - font-size: 1em; - height: 350px; - justify-content: center; - margin: 0; - padding: 32px; - text-align: center -} - -.adyen-checkout__status__icon { - margin-bottom: 24px -} - -.adyen-checkout__status .adyen-checkout__spinner__wrapper { - max-height: 88px -} - -.adyen-checkout__dropin,.adyen-checkout__dropin *,.adyen-checkout__dropin :after,.adyen-checkout__dropin :before { - box-sizing: border-box -} - -.adyen-checkout__payment-methods-list--loading { - pointer-events: none; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.adyen-checkout__instant-payment-methods-list { - list-style: none; - margin: 0; - padding: 0 -} - -.adyen-checkout__instant-payment-methods-list li:not(:last-child) { - margin-bottom: 8px -} - -.adyen-checkout__link { - color: #0075ff; - text-decoration: none -} - -.adyen-checkout__link:hover { - text-decoration: underline -} - -.AchInput-module_sf-input__wrapper__lfdiv { - position: relative -} - -.AchInput-module_sf-input__wrapper__lfdiv *,.AchInput-module_sf-input__wrapper__lfdiv :after,.AchInput-module_sf-input__wrapper__lfdiv :before { - box-sizing: border-box -} - -.AchInput-module_adyen-checkout__input__8WwCR { - display: block; - max-height: 100px -} - -.adyen-checkout__pm__holderName { - margin-bottom: 0 -} - -.adyen-checkout__fieldset__title+.adyen-checkout__ach-sf__form { - margin-top: 0 -} - -.adyen-checkout__ach-input .adyen-checkout__fieldset--address,.adyen-checkout__ach-sf__form { - margin-top: 16px -} - -.adyen-checkout__loading-input__form { - transition: opacity .25s ease-out -} - -.adyen-checkout-phone-input--new { - direction: ltr -} - -.adyen-checkout-phone-input--new .adyen-checkout__input-wrapper { - width: 100% -} - -.adyen-checkout-phone-input--new .adyen-checkout__input-wrapper .adyen-checkout__input { - height: auto; - padding: 0 -} - -.adyen-checkout-phone-input--new .adyen-checkout__input-wrapper .adyen-checkout__input:focus-within { - border: 1px solid #0075ff -} - -.adyen-checkout-phone-input--new .adyen-checkout__input-wrapper .adyen-checkout__input:focus-within .adyen-checkout-dropdown--countrycode-selector { - border-right: 1px solid #0075ff -} - -.adyen-checkout-phone-input--new .adyen-checkout__input-wrapper .adyen-checkout__dropdown__button { - border: 0; - border-bottom-right-radius: 0; - border-top-right-radius: 0; - height: 35px; - width: auto -} - -.adyen-checkout-phone-input--new .adyen-checkout__input-wrapper .adyen-checkout__dropdown__button:after { - box-sizing: revert; - height: 10px -} - -.adyen-checkout-phone-input--new .adyen-checkout__input-wrapper .adyen-checkout__dropdown__button--active,.adyen-checkout-phone-input--new .adyen-checkout__input-wrapper .adyen-checkout__dropdown__button--active:hover { - box-shadow: none -} - -.adyen-checkout-phone-input--new .adyen-checkout__input-wrapper .adyen-checkout-input--phone-number { - border: 1px solid transparent; - height: 35px; - line-height: 35px; - min-height: 35px; - padding-bottom: 0; - padding-left: 15px; - padding-top: 0 -} - -.adyen-checkout-phone-input--new .adyen-checkout__input-wrapper .adyen-checkout-input--phone-number:focus-within { - border: 1px solid #0075ff; - box-shadow: 0 0 0 2px #99c2ff -} - -.adyen-checkout-phone-input--new .adyen-checkout__input-wrapper .adyen-checkout-dropdown--countrycode-selector { - border-right: 1px solid #dce0e5; - min-width: 144px; - width: 144px -} - -.adyen-checkout-phone-input--new .adyen-checkout__input-wrapper .adyen-checkout-input-holder--phone-input { - align-items: center; - display: flex -} - -.adyen-checkout-phone-input--new .adyen-checkout__input-wrapper .adyen-checkout-phone-number { - align-items: center; - display: flex; - flex: 3 -} - -.adyen-checkout-phone-input--new .adyen-checkout-phone-input__error-holder { - margin-top: -10px -} - -.adyen-checkout__await { - background: #fff; - border: 1px solid #d4d9db; - border-radius: 12px; - padding: 40px; - text-align: center -} - -.adyen-checkout__await--result { - padding: 100px -} - -.adyen-checkout__qr-loader--app { - border: 0; - border-radius: 0; - padding: 0 -} - -.adyen-checkout__await__brand-logo { - border-radius: 3px; - width: 74px -} - -.adyen-checkout__await__indicator-text,.adyen-checkout__await__subtitle { - color: #00112c; - font-size: 1em; - line-height: 19px; - margin-top: 32px -} - -.adyen-checkout__await__indicator-holder .adyen-checkout__await__indicator-text { - margin-left: 10px; - margin-top: 6px -} - -.adyen-checkout__await__indicator-holder { - display: flex; - justify-content: center; - margin-bottom: 20px; - margin-top: 32px -} - -.adyen-checkout__await__subtitle--result { - margin-bottom: 32px -} - -.adyen-checkout__await__icon { - height: 88px; - width: 88px -} - -.adyen-checkout__await__progress { - background: #d4d9db; - border-radius: 24px; - height: 4px; - margin: 32px auto 12px; - width: 152px -} - -.adyen-checkout__await__percentage { - background: #0075ff; - border-radius: 24px; - display: block; - height: 100% -} - -.adyen-checkout__await__countdown { - color: #687282; - font-size: .81em -} - -.adyen-checkout__await>.adyen-checkout__spinner__wrapper { - margin: 60px 0 -} - -.adyen-checkout__await__app-link { - display: none; - margin-top: 16px -} - -@media only screen and (max-device-width: 1200px) { - .adyen-checkout__await__app-link { - display:block - } -} - -.adyen-checkout__blik__helper { - color: #00112c; - font-size: 1em; - font-weight: 400; - margin: 0 0 16px; - padding: 0 -} - -.adyen-checkout__bankTransfer__introduction { - color: #00112c; - font-size: .81em; - font-weight: 400; - margin: 0 0 16px; - padding: 0 -} - -.adyen-checkout__bankTransfer__emailField { - margin: 0 0 16px -} - -.adyen-checkout__bacs--confirm { - position: relative -} - -.adyen-checkout__bacs--confirm .adyen-checkout-input__inline-validation--valid { - display: none -} - -.adyen-checkout__bacs .adyen-checkout__field--inactive { - pointer-events: none -} - -.adyen-checkout__bacs .adyen-checkout__bacs--edit { - cursor: pointer; - position: absolute; - right: 0; - top: -25px; - width: 20% -} - -.adyen-checkout__bacs .adyen-checkout__bacs--edit-dropin { - top: -50px -} - -.adyen-checkout__bacs .adyen-checkout__bacs--edit .adyen-checkout__bacs--edit-button { - background: none; - border: none; - color: #0075ff; - cursor: pointer; - text-align: end; - text-decoration: underline -} - -.adyen-checkout__voucher-result__introduction { - font-size: 1em; - max-width: 420px -} - -.adyen-checkout__klarna-widget { - pointer-events: all -} - -.adyen-checkout__field--vpa { - margin-bottom: 0 -} - -.adyen-checkout__segmented-control { - background: #fff; - border: 1px solid #b9c4c9; - border-radius: 6px; - display: flex; - gap: 4px; - justify-content: space-between; - padding: 4px 5px -} - -.adyen-checkout__segmented-control--disabled { - pointer-events: none -} - -.adyen-checkout__segmented-control--disabled>.adyen-checkout__segmented-control-segment { - color: #8390a3 -} - -.adyen-checkout__segmented-control--disabled>.adyen-checkout__segmented-control-segment--selected { - background: #f3f6f9; - border: 1.5px solid #8390a3 -} - -.adyen-checkout__segmented-control-segment { - background: #fff; - border: 0; - border-radius: 6px; - color: #0075ff; - cursor: pointer; - flex-grow: 1; - font-weight: 500; - height: 40px; - text-align: center; - transition: background .3s ease-out; - width: 100% -} - -.adyen-checkout__segmented-control-segment:not(.adyen-checkout__segmented-control-segment--selected):hover { - background-color: #f7f8f9 -} - -.adyen-checkout__segmented-control-segment:active { - background-color: #f7f8f9; - border: 1.5px solid #687282 -} - -.adyen-checkout__segmented-control-segment--selected { - background: #e5f1ff; - border: 1.5px solid #0075ff; - color: #0075ff; - font-weight: 700 -} - -.adyen-checkout_upi-mode-selection-text { - font-size: 13px; - font-weight: 400; - line-height: 19px; - margin-bottom: 7px; - margin-top: 0 -} - -.adyen-checkout-trustly,.adyen-checkout__segmented-control--upi-margin-bottom { - margin-bottom: 16px -} - -.adyen-checkout-trustly__descriptor { - font-size: 1em; - font-weight: 500; - margin: 0 0 4px -} - -.adyen-checkout-trustly__description-list { - font-size: .81em; - line-height: 1.5; - list-style-type: disc; - margin: 0; - padding-left: 20px -} - -.adyen-checkout-sr-panel { - font-size: .75em; - margin-bottom: 20px -} - -.adyen-checkout-sr-panel--sr-only { - clip: rect(0 0 0 0); - border: 0; - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px -} - -.icon_d2b05e { - inset-inline-start: 8px; - margin: auto auto 25px; - top: 9px; - z-index: 1 -} - -.connectionInstructions_d2b05e { - margin-top: 8px; - text-align: center -} - -.body_b25217 { - margin: var(--space-16) 0 -} - -.flexColumn_b25217 { - display: flex; - flex-direction: column -} - -.infoNotice_b25217 { - margin-bottom: 16px -} - -.paymentRequestButton_b25217 { - display: flex; - flex: 1; - flex-direction: row -} - -.paymentModalBreadcrumbs_b25217:after { - padding: 0 -} - -.paymentModalError_b25217 { - background-color: var(--modal-background); - padding-top: 12px -} - -.awaitingAuthenticationStep__4ede8 { - padding-bottom: 16px; - padding-top: 16px -} - -.awaitingWrapper__8a7af { - align-items: center; - display: flex; - flex-direction: column; - text-align: center -} - -.body__96722 { - padding-bottom: 16px; - padding-top: 16px -} - -.notification_e1e832 { - align-items: center; - background-color: var(--status-positive-background); - border-radius: 8px; - box-sizing: border-box; - display: flex; - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 1.3; - padding: 8px; - width: 100% -} - -.notification_e1e832 a { - color: var(--white); - font-weight: var(--font-weight-semibold) -} - -.notification_e1e832 a:hover { - text-decoration: underline -} - -.icon_e1e832 { - flex-shrink: 0; - height: 24px; - width: 24px -} - -.icon_e1e832,.text_e1e832 { - color: var(--status-positive-text) -} - -.text_e1e832 { - flex: 1; - margin-inline-start:8px} - -.confirmation__6220d { - align-items: center; - display: flex; - flex-direction: column; - padding: 30px 24px 10px -} - -.confirmationHeader__6220d { - margin-bottom: 16px -} - -.divider__6220d { - background-color: var(--border-subtle); - height: 1px; - margin: 24px 0; - width: 100% -} - -.legalFinePrint__1048e { - display: flex; - flex-direction: column; - gap: 8px -} - -.paymentIcon__84bdf { - height: 24px; - -o-object-fit: contain; - object-fit: contain; - width: 24px -} - -.clickableContainer__84bdf { - cursor: pointer -} - -.paymentSourceLabel_f38e43 { - line-height: 22px; - margin-inline-start:8px;overflow: hidden; - text-overflow: ellipsis -} - -.paymentSourceLabel_f38e43:first-child { - margin-inline-start:0} - -.paymentSourceSelectedOption_f38e43 { - align-items: center; - display: flex; - overflow: hidden -} - -.paymentSourceWarning_f38e43 { - align-items: flex-start; - display: flex; - gap: 8px; - margin-top: 8px -} - -.paymentSourceWarningIcon_f38e43 { - flex-shrink: 0 -} - -.error_f38e43 { - color: var(--red-400) -} - -.dropdownLoadingContainer_f38e43 { - align-items: center; - display: flex; - justify-content: center; - padding: 8px -} - -.formTitle__553e7 { - margin-bottom: 8px -} - -.checkbox__553e7 { - margin-bottom: 10px -} - -.checkbox__553e7:last-child { - margin-bottom: 0 -} - -.checkboxLabel__553e7 { - color: var(--interactive-text-default) -} - -.finePrint__553e7 { - color: var(--text-muted); - font-size: 12px; - line-height: 14px; - margin-bottom: 16px; - margin-top: 8px -} - -.container__65536 { - background: var(--background-surface-highest); - border: 1px solid var(--border-normal,hsla(240,4%,61%,.32)); - border-radius: 12px; - gap: 12px; - margin-bottom: var(--space-12); - padding-block:8px;padding-inline:8px 16px} - -.container__65536,.content__65536 { - align-items: center; - display: flex -} - -.content__65536 { - gap: var(--space-12); - justify-content: center -} - -.attributionBannerContainer__7b5f3 { - align-items: center; - border-radius: 4px; - display: flex; - flex-direction: row; - margin-top: 16px; - min-height: 48px; - padding-bottom: 8px; - padding-inline-start:28px;padding-top: 8px; - position: relative -} - -.avatarContainer__7b5f3 { - height: 32px; - width: 32px -} - -.bannerHeader__7b5f3 { - padding-inline:8px} - -.bannerIcon__7b5f3 { - border-radius: 4px; - bottom: 0; - inset-inline-end: 0; - position: absolute; - z-index: -1 -} - -.bannerCopy__7b5f3 { - border-bottom: 1px solid rgba(78,80,88,.48); - padding-bottom: 16px; - padding-top: 8px -} - -.theme-light .attributionBannerContainer__7b5f3 { - background: linear-gradient(90deg,rgba(133,71,198,.4),rgba(184,69,193,.4) 50.24%,rgba(171,93,138,.4)) -} - -.theme-dark .attributionBannerContainer__7b5f3 { - background: linear-gradient(90deg,rgba(133,71,198,.5),rgba(184,69,193,.5) 50.24%,rgba(171,93,138,.5)) -} - -.table_fda3e4 { - background-color: var(--background-mod-subtle); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - padding: var(--space-12) -} - -.table_fda3e4.premiumRebrand_fda3e4 { - border: 1px solid var(--border-muted) -} - -.table_fda3e4.hasError_fda3e4 { - margin-bottom: var(--space-16) -} - -.flex_fda3e4 { - display: flex -} - -.header_fda3e4 { - color: var(--interactive-text-active); - font-weight: var(--font-weight-semibold); - margin-bottom: 8px -} - -.header_fda3e4,.rowBase_fda3e4 { - font-size: 16px; - line-height: 20px -} - -.rowBase_fda3e4 { - display: flex; - font-weight: var(--font-weight-medium) -} - -.rowBase_fda3e4:not(:last-child) { - margin-bottom: 8px -} - -.row_fda3e4 { - align-items: flex-start; - color: var(--interactive-text-default); - justify-content: space-between -} - -.row_fda3e4.canceled_fda3e4 { - color: var(--text-feedback-critical) -} - -.rowAmount_fda3e4 { - align-items: center; - display: flex; - white-space: nowrap -} - -.rowLabel_fda3e4 { - margin-inline-end:10px} - -.rowPercentDiscount_fda3e4 { - background-color: var(--green-360); - border-radius: 2px; - color: var(--white); - font-size: 12px; - line-height: 16px; - margin-inline-start:8px;padding: 2px 4px -} - -.rowDiscountOriginalPrice_fda3e4 { - color: var(--text-muted); - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 16px; - margin: -4px 0 4px; - text-align: end; - text-decoration: line-through -} - -.entitlementDiscountRow_fda3e4 { - color: var(--text-feedback-positive); - justify-content: flex-end -} - -.divider_fda3e4 { - background-color: var(--border-muted); - height: 1px; - margin: 16px 0 -} - -.divider_fda3e4.negativeMarginTop_fda3e4 { - margin-top: -16px -} - -.divider_fda3e4.negativeMarginBottom_fda3e4 { - margin-bottom: -4px -} - -.dividerExtended_fda3e4 { - height: 4px; - margin-inline:-16px} - -.totalRow_fda3e4 { - align-items: center; - display: flex; - justify-content: space-between -} - -.totalLabel_fda3e4 { - color: var(--interactive-text-default); - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px -} - -.totalAmount_fda3e4,.totalLabel_fda3e4 strong { - color: var(--interactive-text-active); - font-weight: var(--font-weight-semibold) -} - -.finePrint_fda3e4 { - color: var(--text-default); - font-size: 12px; - font-weight: var(--font-weight-medium); - line-height: 16px; - margin: 16px 0 -} - -.annualPlanDiscountPriceSection_fda3e4 { - align-self: flex-end; - display: flex; - flex-direction: row -} - -.annualPlanOptionDiscount_fda3e4 { - background-color: var(--green-360); - border-radius: 8px; - font-weight: var(--font-weight-semibold); - margin-top: 2px; - margin-inline-end:8px;max-height: 16px; - padding: 0 6px; - position: relative -} - -.annualDiscountValues_fda3e4 { - display: flex; - flex-direction: column -} - -.trialEndPrice_fda3e4 { - text-align: end -} - -.wrapper_c0e5ec { - border-radius: 4px; - transition: background-color .2s ease -} - -.wrapperActive_c0e5ec { - background-color: var(--background-mod-subtle) -} - -.container_d0a022 { - display: flex; - gap: 8px; - margin-top: 8px -} - -.icon_d0a022 { - flex-shrink: 0 -} - -.trigger_e6ce07 { - all: unset -} - -.triggerContent_e6ce07 { - align-items: center; - cursor: pointer; - display: flex; - gap: var(--space-4) -} - -.full-motion .chevron_e6ce07 { - transition: transform .1s ease-in-out -} - -[data-expanded] .chevron_e6ce07 { - transform: rotate(-180deg) -} - -.panel_e6ce07 { - height: var(--disclosure-panel-height); - overflow: clip; - width: 100% -} - -.full-motion .panel_e6ce07 { - transition: height .1s ease-in-out -} - -.header_b963df { - align-items: center; - display: flex; - gap: var(--space-24); - justify-content: space-between; - width: 100% -} - -.lineItemsContainer_b963df { - display: flex; - flex-direction: column; - gap: var(--space-8); - margin-top: var(--space-12); - width: 100% -} - -.lineItem_b963df { - align-items: flex-start; - display: flex; - gap: var(--space-24); - justify-content: space-between; - width: 100% -} - -.lineItemLabel_b963df { - align-items: center; - display: flex; - flex: 1; - gap: var(--space-4); - min-width: 0 -} - -.divider_ad82d8 { - background-color: var(--border-muted); - height: 1px; - margin-bottom: var(--space-16); - width: 100% -} - -.totalDue_ad82d8 { - display: flex; - gap: var(--space-12); - justify-content: space-between; - margin-top: var(--space-12) -} - -.cardGroup__02e50 { - align-items: stretch; - display: flex; - gap: var(--space-8); - width: 100% -} - -.cardGroupWithHeadingSpacing__02e50 { - margin-top: var(--space-12) -} - -.card__02e50 { - all: unset; - background-color: var(--background-mod-muted); - border: 1px solid var(--border-muted); - border-radius: var(--radius-md); - cursor: pointer; - display: flex; - flex: 1 0 0; - flex-direction: column; - gap: var(--space-16); - justify-content: space-between; - padding: var(--space-12); - position: relative; - transition: background-color .1s -} - -.card__02e50[data-disabled] { - cursor: not-allowed -} - -.card__02e50[data-selected] { - background-color: var(--background-mod-subtle) -} - -.selectionIndicator__02e50 { - border: 2px solid var(--input-border-hover); - border-radius: var(--radius-md); - inset: -1px; - position: absolute; - z-index: -1 -} - -.title__02e50 { - margin-inline-end:var(--space-24)} - -.text__02e50 { - align-items: baseline; - display: flex; - flex-wrap: wrap; - gap: 0 var(--space-8) -} - -.strikethrough__02e50 { - text-decoration: line-through -} - -.selectionIcon__02e50 { - display: block; - inset-inline-end: var(--space-8); - position: absolute; - top: var(--space-8) -} - -.text_eaab0b { - font-style: italic; - text-transform: uppercase -} - -.container_b776a7 { - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg,16px); - display: flex; - flex-direction: column; - gap: var(--space-12,12px); - padding: var(--space-16,16px) -} - -.separator_b776a7 { - background-color: var(--border-subtle); - height: 1px; - width: 100% -} - -.container_da8ac7 { - align-items: center; - display: flex; - gap: var(--space-12); - width: 100% -} - -.content_da8ac7 { - width: 100% -} - -.header_da8ac7 { - align-items: center; - display: flex; - gap: var(--space-4); - margin-bottom: var(--space-4) -} - -.headerIcon_da8ac7 { - border-radius: var(--radius-xs); - height: 16px; - width: 16px -} - -.details_da8ac7 { - align-self: center; - display: flex; - flex: 1; - gap: var(--space-24); - min-width: 0 -} - -.text_da8ac7 { - align-items: flex-start; - flex-direction: column; - justify-content: space-between; - width: 100% -} - -.giftMessage_da8ac7,.text_da8ac7 { - display: flex; - gap: var(--space-4) -} - -.giftMessage_da8ac7 { - align-items: center -} - -.giftAvatar_da8ac7 { - border-radius: var(--radius-round); - height: 16px; - width: 16px -} - -.priceContainer_da8ac7 { - align-items: flex-end; - display: flex; - flex-direction: column; - flex-shrink: 0 -} - -.currentPrice_da8ac7 { - align-items: center; - display: flex; - gap: var(--space-4); - white-space: nowrap -} - -.originalPrice_da8ac7 { - text-decoration: line-through; - -webkit-text-decoration-skip-ink: none; - text-decoration-skip-ink: none; - white-space: nowrap -} - -.icon_da8ac7 { - align-items: center; - align-self: flex-start; - aspect-ratio: 1/1; - background: var(--background-mod-subtle); - border-radius: var(--radius-md); - display: flex; - height: 64px; - justify-content: center; - overflow: clip; - position: relative; - width: 64px -} - -.icon_da8ac7 .expressiveGradient_da8ac7,.icon_da8ac7 .nitroCredit_da8ac7 { - align-items: center; - display: flex; - inset: 0; - justify-content: center; - position: absolute -} - -.icon_da8ac7 .nitroCredit_da8ac7 { - background: linear-gradient(322deg,#e978e6 6.26%,#2f3ebb 79.77%) -} - -.icon_da8ac7 .nitroCredit_da8ac7 svg { - filter: drop-shadow(0 4px 10px var(--shadow-low-hover)) -} - -.container_e7911b { - display: flex; - flex-direction: column; - gap: var(--space-16) -} - -.container__33980 { - display: flex; - gap: 8px; - margin-top: 20px -} - -.iconBackground__33980 { - align-items: center; - background-color: var(--background-mod-muted); - border-radius: var(--radius-round); - display: flex; - justify-content: center; - padding: 8px -} - -.link__33980 { - cursor: pointer; - display: inline; - text-decoration: underline -} - -.pricePerInterval_b39acb { - color: var(--interactive-text-default); - font-size: 16px; - font-weight: var(--font-weight-normal); - line-height: 20px -} - -.pricePerInterval_b39acb strong { - color: var(--interactive-text-active); - font-size: 24px; - font-weight: var(--font-weight-semibold); - line-height: 30px -} - -.activeFractionalPremiumBannerContainer__0687d { - align-items: center; - border-radius: 4px; - display: flex; - flex-direction: row; - margin-bottom: 16px; - margin-top: 5px; - min-height: 48px; - padding-inline:20px;padding-bottom: 10px; - padding-top: 10px; - position: relative -} - -.theme-light .activeFractionalPremiumBannerContainer__0687d { - background: linear-gradient(90deg,rgba(133,71,198,.4),rgba(184,69,193,.4) 50.24%,rgba(171,93,138,.4)) -} - -.theme-dark .activeFractionalPremiumBannerContainer__0687d { - background: linear-gradient(90deg,rgba(133,71,198,.5),rgba(184,69,193,.5) 50.24%,rgba(171,93,138,.5)) -} - -.activeFractionalPremiumBannerBackgroundImage__0687d { - background-image: url(/assets/bb502dc7cfcbbacc.svg); - background-repeat: no-repeat; - bottom: 0; - position: absolute; - top: 40%; - inset-inline: 78% 0 -} - -.nitroIconContainer__0687d { - height: 28px; - padding-inline-end:12px;width: 28px -} - -.theme-light .iconFractional__0687d { - display: none -} - -.theme-light .iconFractionalDark__0687d { - filter: drop-shadow(0 1px 2px rgb(0 0 0/.4)); - height: 28px; - width: 28px -} - -.theme-dark .iconFractional__0687d { - filter: drop-shadow(0 1px 2px rgb(0 0 0/.4)); - height: 28px; - width: 28px -} - -.theme-dark .iconFractionalDark__0687d { - display: none -} - -.premiumBrandRefreshContainer__0687d { - align-items: center; - background-color: var(--background-surface-highest); - border-radius: var(--radius-md); - display: flex; - flex-direction: row; - margin-bottom: 16px; - margin-top: 5px; - padding: var(--space-8); - position: relative -} - -.premiumBrandRefreshIcon__0687d { - margin-inline-end:var(--space-12)} - -.trialForAllHeader__0687d { - padding-bottom: 12px -} - -.trialForAllSeparator__0687d { - background: var(--border-subtle); - border: 0; - height: 1px; - margin: 0 0 4px; - width: 100% -} - -:where(.contentImage__42bf5) { - border-radius: var(--radius-xs); - -o-object-fit: contain; - object-fit: contain -} - -.imageWrapper_af017a { - border-radius: 3px; - display: block; - overflow: hidden; - position: relative; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.clickableWrapper_af017a,.loadingOverlay_af017a { - height: 100%; - width: 100% -} - -.imageWrapperBackground_af017a { - background: var(--opacity-black-4) -} - -.imagePlaceholder_af017a { - background: var(--background-gradient-chat,var(--background-base-low)); - display: block; - position: absolute; - top: 0 -} - -.imageErrorWrapper_af017a { - align-items: center; - display: flex; - justify-content: center -} - -.imageError_af017a { - flex-basis: content; - max-height: 100%; - max-width: 200px; - width: 100% -} - -.imageLoadingOverlay_af017a { - background-color: var(--opacity-black-48); - border-radius: 100%; - box-sizing: border-box; - display: flex; - height: 24px; - inset-inline-end: 0; - margin: 4px; - overflow: hidden; - padding: 3px; - position: absolute; - top: 0; - width: 24px -} - -.cornerLoadingSpinner_af017a { - height: 100%; - opacity: .75; - width: 100% -} - -.theme-dark .cornerLoadingSpinner_af017a circle,.theme-light .cornerLoadingSpinner_af017a circle { - stroke: var(--white) -} - -.imageAccessory_af017a { - inset-inline-start: 6px; - position: absolute; - top: 6px; - z-index: 3 -} - -.imageZoom_af017a { - cursor: nesw-resize; - cursor: zoom-in -} - -.clickable_af017a { - cursor: pointer -} - -.originalLink_af017a { - inset: 0; - position: absolute; - z-index: 1 -} - -.imageClickable_af017a { - border-radius: inherit -} - -.background-opacity-low .imageWrapper_af017a,.background-opacity-medium .imageWrapper_af017a { - opacity: .6 -} - -.overlay-unlocked .imageWrapper_af017a { - opacity: 1 -} - -.enable-forced-colors .originalLink_af017a { - border: 2px solid ButtonText -} - -.gifTag_f60819 { - background-image: url(/assets/9811b405dc8e999f.svg); - height: 22px; - width: 29px -} - -.fontCode__230d2 { - font-family: var(--font-code) -} - -.bold__230d2 { - font-weight: 700 -} - -.inlineTimestamp__230d2 { - display: inline -} - -.badgeContainer__635ed { - align-items: center; - display: flex; - flex-shrink: 0; - gap: 4px; - max-width: 100%; - overflow: hidden -} - -.badgeContainer__635ed svg { - flex-shrink: 0 -} - -.badgeLabel__635ed { - overflow-x: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -:where(.badgesContainer__635ed) { - align-items: center; - display: flex; - gap: 0 8px -} - -:where(.badgesContainer__635ed):empty { - display: none -} - -.badgesContainerCard__635ed>:nth-child(n+3) { - display: none -} - -.badgesContainerCard__635ed>:nth-child(2) { - flex-shrink: 1 -} - -.badgesContainerGameProfile__635ed,.badgesContainerPopout__635ed { - flex-wrap: wrap; - row-gap: 4px -} - -.appWidgetPreviewIcon__635ed { - display: inline-block; - height: 1lh; - vertical-align: bottom; - width: 1lh -} - -.container__0f2e8 { - --custom-content-card-padding: 8px; - align-items: center; - background: var(--background-gradient-high,var(--background-surface-high)); - border: 1px solid var(--border-muted); - border-radius: var(--radius-md); - box-sizing: border-box; - cursor: pointer; - display: flex; - gap: 8px; - justify-content: space-between; - margin-inline-start:8px;margin-top: 8px; - max-height: 120px; - max-width: calc(var(--custom-member-list-width) - 16px); - padding: var(--custom-content-card-padding); - position: relative -} - -.container__0f2e8:hover { - background: var(--background-gradient-higher,var(--interactive-background-hover)) -} - -.container__0f2e8:hover .contentTitle__0f2e8 { - color: var(--interactive-text-hover) -} - -.container__0f2e8:hover .reply__0f2e8 { - opacity: 1 -} - -.container__0f2e8:hover .additionalParticipantBadge__0f2e8 { - background: var(--background-gradient-high,var(--background-mod-muted)) -} - -.usesCardRows__0f2e8 { - flex-direction: column -} - -.cardRow__0f2e8 { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px; - justify-content: space-between; - width: 100% -} - -.openOnHover__0f2e8 { - cursor: auto -} - -.reply__0f2e8 { - align-items: center; - background: var(--background-base-low); - border: 1px solid var(--background-base-lower); - border-radius: var(--radius-xs); - display: flex; - height: 24px; - inset-inline-end: 8px; - justify-content: center; - margin: auto; - opacity: 0; - position: absolute; - top: 4px; - width: 24px -} - -.reply__0f2e8:hover .icon__0f2e8 { - color: var(--interactive-text-hover) -} - -.icon__0f2e8 { - color: var(--interactive-text-default); - display: block; - height: 16px; - -o-object-fit: contain; - object-fit: contain -} - -.selected__0f2e8,.selected__0f2e8:hover { - background-color: var(--interactive-background-selected) -} - -.selected__0f2e8 .contentDescription__0f2e8 { - color: var(--interactive-text-hover) -} - -.infoSection__0f2e8 { - display: flex; - flex: 1; - flex-direction: column; - margin-bottom: -2px; - min-width: 0 -} - -.userSection__0f2e8 { - align-items: center; - display: flex; - margin-bottom: 2px -} - -.userName__0f2e8 { - color: var(--channels-default); - font-family: var(--font-primary); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858; - max-width: 85%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.userName__0f2e8.fontScaling__0f2e8 { - font-size: .875rem -} - -.facePile__0f2e8 { - display: flex; - margin-inline-end:6px} - -.facePileItem__0f2e8:not(:first-child) { - margin-inline-start:-3px} - -.additionalParticipantBadge__0f2e8 { - background: var(--background-mod-subtle); - border-radius: 8px; - margin-inline-start:4px;padding-block:2.25px 2.5px;padding-inline:4.2px 4.4px} - -.additionalParticipantBadgeText__0f2e8 { - line-height: 10px -} - -img.thumbnail__0f2e8 { - -o-object-fit: cover; - object-fit: cover; - width: 48px -} - -.divider__0f2e8 { - background-color: var(--background-mod-subtle); - height: 1px; - width: 100% -} - -.nowrap__5db79 { - white-space: nowrap -} - -.icon_b75563 { - height: 1em; - margin-bottom: .2rem; - margin-inline-end:var(--space-4);vertical-align: middle; - width: 1em -} - -.name_b75563 { - overflow: hidden; - text-overflow: ellipsis -} - -.channelWithIcon_b75563 .iconMentionText_b75563 { - gap: 0!important -} - -.icon_d2d51d { - display: inline-block; - margin-inline-end:3px;margin-bottom: .2rem; - vertical-align: middle -} - -.textIcon_d2d51d { - font-size: .5rem!important; - height: 1.05rem; - line-height: 1.05rem!important; - width: 1.05rem -} - -.imageIcon_d2d51d { - height: 1em; - width: 1em -} - -.name_d2d51d { - overflow: hidden; - text-overflow: ellipsis -} - -.wrapper_f61d60 { - background: var(--mention-background); - border-radius: 3px; - color: var(--mention-foreground); - font-weight: var(--font-weight-medium); - padding: 0 2px; - unicode-bidi: plaintext -} - -.theme-dark.low-contrast .wrapper_f61d60 { - background: var(--brand-05a); - color: var(--brand-300) -} - -.interactive { - cursor: pointer; - transition: background-color 50ms ease-out,color 50ms ease-out -} - -.interactive:hover { - background-color: var(--brand-500); - color: var(--white) -} - -.icon_c76ab6 { - height: .5em; - margin-inline:4px 2px;margin-bottom: 1px; - width: .5em -} - -.icon__3173f { - height: .95rem; - padding-inline-start:4px;position: relative; - top: 2px; - width: .95rem -} - -.overflow_b0dfc2 { - overflow: hidden; - position: relative; - text-overflow: ellipsis; - white-space: nowrap -} - -.root,[data-popout-root],:root { - --__spoiler-background-color--hidden: var(--spoiler-hidden-background); - --__spoiler-background-color--hidden--hover: var(--spoiler-hidden-background-hover); - --__spoiler-background-color--revealed: var(--background-mod-subtle); - --__spoiler-text-color--hidden: transparent; - --__spoiler-warning-text-color: var(--primary-200); - --__spoiler-warning-text-color--hover: var(--white); - --__spoiler-warning-background-color: var(--opacity-black-60); - --__spoiler-warning-background-color--hover: var(--opacity-black-88); - --__spoiler-container-box-shadow-color: var(--opacity-black-8); - --__obscured-background-blur-radius: 40px; - --__obscured-background-brightness: 0.55 -} - -.enable-forced-colors.enable-forced-colors.enable-forced-colors { - --__spoiler-background-color--hidden: ButtonFace; - --__spoiler-background-color--hidden--hover: ButtonFace; - --__spoiler-background-color--revealed: transparent; - --__spoiler-text-color--hidden: ButtonFace; - --__spoiler-border-color--hidden: CanvasText; - --__spoiler-border-color--hidden--hover: ButtonText; - --__spoiler-border-color--revealed: CanvasText; - --__spoiler-warning-text-color: CanvasText; - --__spoiler-warning-text-color--hover: CanvasText; - --__spoiler-warning-background-color: Canvas; - --__spoiler-warning-background-color--hover: Canvas; - --__spoiler-container-box-shadow-color: transparent -} - -.spoilerContent__299eb { - --__current--spoiler-content-opacity: 1; - --__current--spoiler-background-color: var(--__spoiler-background-color--revealed); - --__current--spoiler-border-color: var(--__spoiler-border-color--revealed); - --__current--spoiler-pointer-events: all -} - -.spoilerContent__299eb.hidden__299eb { - --__current--spoiler-content-opacity: 0; - --__current--spoiler-text-color: var(--__spoiler-text-color--hidden); - --__current--spoiler-background-color: var(--__spoiler-background-color--hidden); - --__current--spoiler-border-color: var(--__spoiler-border-color--hidden); - --__current--spoiler-pointer-events: none; - --__current--spoiler-warning-text-color: var(--__spoiler-warning-text-color); - --__current--spoiler-warning-background-color: var(--__spoiler-warning-background-color) -} - -.spoilerContent__299eb.hidden__299eb:hover { - --__current--spoiler-background-color: var(--__spoiler-background-color--hidden--hover); - --__current--spoiler-border-color: var(--__spoiler-border-color--hidden--hover); - --__current--spoiler-warning-text-color: var(--__spoiler-warning-text-color--hover); - --__current--spoiler-warning-background-color: var(--__spoiler-warning-background-color--hover) -} - -.spoilerContent__299eb.hidden__299eb.opaque__299eb { - --__obscured-background-brightness: 0; - --__obscured-background-blur-radius: 500px -} - -.spoilerContent__299eb.hidden__299eb.opaque__299eb:hover { - --__current--spoiler-background-color: var(--__spoiler-background-color--hidden); - --__current--spoiler-border-color: var(--__spoiler-border-color--hidden); - --__current--spoiler-warning-text-color: var(--__spoiler-warning-text-color); - --__current--spoiler-warning-background-color: var(--__spoiler-warning-background-color) -} - -.spoilerContainer__299eb { - background-color: var(--__current--spoiler-background-color); - filter: blur(0); - position: relative -} - -.spoilerContainer__299eb.embedContainer__299eb { - border-radius: var(--radius-xs) -} - -.spoilerContainer__299eb.attachmentContainer__299eb { - border-radius: var(--radius-sm) -} - -.spoilerContainer__299eb.hiddenSpoiler__299eb { - cursor: pointer -} - -.spoilerContainer__299eb.hidden__299eb { - overflow: hidden -} - -.spoilerContainer__299eb.hidden__299eb article { - background-color: var(--__current--spoiler-background-color); - border-color: var(--__current--spoiler-background-color) -} - -.spoilerContainer__299eb.hidden__299eb:not(:focus) { - box-shadow: .5px .5px 1px 1px var(--__spoiler-container-box-shadow-color) -} - -.enable-forced-colors .spoilerContainer__299eb { - border: 1px solid var(--__current--spoiler-border-color) -} - -.enable-forced-colors .spoilerContainer__299eb article { - border-color: var(--__current--spoiler-border-color) -} - -.spoilerInnerContainer__299eb { - height: 100%; - width: 100% -} - -.obscureWarning__299eb { - align-items: center; - color: var(--__current--spoiler-warning-text-color); - display: flex; - flex-direction: column; - inset-inline-start: 50%; - padding: 8px 12px; - position: absolute; - top: 50%; - transform: translate(-50%,-50%); - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - z-index: 1 -} - -.constrainedObscureContent__299eb { - align-items: center; - display: flex; - justify-content: center; - min-height: 212px; - min-width: 146px -} - -.explicitContentWarning__299eb { - text-align: center; - width: 100% -} - -.explicitContentWarningText__299eb { - margin-top: var(--space-4) -} - -.spoilerWarning__299eb { - background-color: var(--__current--spoiler-warning-background-color); - border-radius: 20px; - cursor: pointer; - font-size: 15px; - font-weight: var(--font-weight-semibold); - letter-spacing: .5px; - text-transform: uppercase -} - -.spoilerWarning__299eb.embed__299eb { - margin-inline-start:4px} - -.spoilerMarkdownContent__299eb { - background-color: var(--__current--spoiler-background-color); - border-radius: var(--radius-xs); - -webkit-box-decoration-break: clone; - box-decoration-break: clone; - transition: background-color .2s ease -} - -.spoilerMarkdownContent__299eb.hidden__299eb { - cursor: pointer -} - -.spoilerMarkdownContent__299eb.hidden__299eb .obscuredBlockContent__299eb,.spoilerMarkdownContent__299eb.hidden__299eb .obscuredTextContent__299eb { - cursor: pointer; - pointer-events: none -} - -.enable-forced-colors .obscuredTextContent__299eb { - border: 1px solid var(--__current--spoiler-border-color); - border-radius: var(--radius-xs); - -webkit-box-decoration-break: clone; - box-decoration-break: clone; - padding: 0 2px -} - -.obscuredTextContentInner__299eb { - color: var(--__current--spoiler-text-color) -} - -.obscuredTextContentInner__299eb code,.obscuredTextContentInner__299eb span { - opacity: var(--__current--spoiler-content-opacity); - transition: opacity .1s ease -} - -.enable-forced-colors .spoilerMarkdownContent__299eb a { - background-color: var(--__current--spoiler-background-color) -} - -.spoilerMarkdownContent__299eb blockquote { - color: var(--__current--spoiler-text-color); - pointer-events: all; - position: relative -} - -.spoilerMarkdownContent__299eb blockquote:before { - background-color: var(--__current--spoiler-background-color); - border-radius: var(--radius-xs); - content: ""; - height: 100%; - inset-inline-start: 0; - position: absolute; - width: 100% -} - -.enable-forced-colors .spoilerMarkdownContent__299eb blockquote:before { - border: 1px solid var(--__current--spoiler-border-color) -} - -.spoilerMarkdownContent__299eb pre { - pointer-events: all; - position: relative -} - -.spoilerMarkdownContent__299eb pre:before { - background-color: var(--__current--spoiler-background-color); - border-radius: var(--radius-xs); - color: var(--__current--spoiler-text-color); - content: ""; - height: 100%; - position: absolute; - width: 100% -} - -.enable-forced-colors .spoilerMarkdownContent__299eb pre:before { - border: 1px solid var(--__current--spoiler-border-color) -} - -.spoilerMarkdownContent__299eb pre code { - opacity: var(--__current--spoiler-content-opacity) -} - -.spoilerMarkdownContent__299eb ol li::marker,.spoilerMarkdownContent__299eb ul li::marker { - color: var(--text-default) -} - -.spoilerMarkdownContent__299eb h1>span,.spoilerMarkdownContent__299eb h2>span,.spoilerMarkdownContent__299eb h3>span,.spoilerMarkdownContent__299eb h4>span,.spoilerMarkdownContent__299eb h5>span,.spoilerMarkdownContent__299eb ol li>span,.spoilerMarkdownContent__299eb small>span,.spoilerMarkdownContent__299eb ul li>span { - background: var(--__current--spoiler-background-color); - border-radius: var(--radius-xs); - -webkit-box-decoration-break: clone; - box-decoration-break: clone; - color: var(--__current--spoiler-text-color)!important; - opacity: 1!important; - pointer-events: all -} - -.enable-forced-colors .spoilerMarkdownContent__299eb h1>span,.enable-forced-colors .spoilerMarkdownContent__299eb h2>span,.enable-forced-colors .spoilerMarkdownContent__299eb h3>span,.enable-forced-colors .spoilerMarkdownContent__299eb h4>span,.enable-forced-colors .spoilerMarkdownContent__299eb h5>span,.enable-forced-colors .spoilerMarkdownContent__299eb ol li>span,.enable-forced-colors .spoilerMarkdownContent__299eb small>span,.enable-forced-colors .spoilerMarkdownContent__299eb ul li>span { - border: 1px solid var(--__current--spoiler-border-color) -} - -.spoilerMarkdownContent__299eb h1>span>span,.spoilerMarkdownContent__299eb h2>span>span,.spoilerMarkdownContent__299eb h3>span>span,.spoilerMarkdownContent__299eb h4>span>span,.spoilerMarkdownContent__299eb h5>span>span,.spoilerMarkdownContent__299eb ol li>span>span,.spoilerMarkdownContent__299eb small>span>span,.spoilerMarkdownContent__299eb ul li>span>span { - pointer-events: var(--__current--spoiler-pointer-events) -} - -.obscureButtonContainer__299eb { - bottom: var(--space-4); - inset-inline-end: var(--space-4); - position: absolute; - transition: bottom .2s ease-in-out; - z-index: 1 -} - -.obscureHoverButton__299eb { - background-color: rgba(0,0,0,.6); - border-radius: var(--radius-sm); - color: var(--white); - cursor: pointer; - display: flex; - padding: var(--space-4); - transition: background-color .2s ease-in-out -} - -.obscureHoverButton__299eb:hover { - background-color: rgba(0,0,0,.8); - transition: background-color .2s ease-in-out -} - -.obscureHoverButton__299eb:focus { - background-color: #000; - transition: background-color .2s ease-in-out -} - -.obscureHoverButton__299eb:active { - background-color: var(--background-mod-normal); - transition: background-color .2s ease-in-out -} - -.singleItemWrapper_ed6d69 { - width: 100% -} - -.carousel_ed6d69,.singleItemWrapper_ed6d69 { - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.carousel_ed6d69 { - display: flex; - position: relative -} - -.item_ed6d69 { - flex: 1 0 100% -} - -.singleItem_ed6d69 { - margin: 0 auto -} - -.viewport_ed6d69 { - overflow: hidden -} - -@keyframes fadeIn__5cd44 { - 0% { - opacity: 0 - } - - to { - opacity: 1 - } -} - -@keyframes fadeOut__5cd44 { - 0% { - opacity: 1 - } - - to { - opacity: 0 - } -} - -.base__5cd44 { - animation: fadeIn__5cd44 .15s ease-out 50ms forwards; - opacity: 0 -} - -.base__5cd44.hidden__5cd44:not(.focusSensitive__5cd44:focus-within) { - animation: fadeOut__5cd44 .15s ease-out forwards; - pointer-events: none -} - -.horizontal_e03578 { - grid-column: 1/span 5; - grid-row: 2; - margin: 24px -} - -.vertical_e03578 { - grid-column: 3; - grid-row: 1/span 3; - margin: 24px -} - -.mediaArea_e03578 { - grid-column: 1/span 5; - grid-row: 1/span 3; - place-self: center -} - -.controlIcon_f1ceac { - color: var(--interactive-text-default); - display: flex; - height: 24px; - width: 24px -} - -.controlIcon_f1ceac.active_f1ceac,.controlIcon_f1ceac:hover { - color: var(--interactive-text-active) -} - -.controlIcon_f1ceac.themeable_f1ceac { - color: var(--interactive-text-default) -} - -.controlIcon_f1ceac.themeable_f1ceac.active_f1ceac,.controlIcon_f1ceac.themeable_f1ceac:hover { - color: var(--interactive-text-active) -} - -.centerIcon_f1ceac { - color: var(--interactive-text-default); - display: flex; - height: 20px; - width: 20px -} - -.centerIcon_f1ceac.active_f1ceac,.centerIcon_f1ceac:hover { - color: var(--interactive-text-active) -} - -.centerIcon_f1ceac.themeable_f1ceac { - color: var(--interactive-text-default) -} - -.centerIcon_f1ceac.themeable_f1ceac.active_f1ceac,.centerIcon_f1ceac.themeable_f1ceac:hover { - color: var(--interactive-text-active) -} - -.colorable_f1ceac { - transition: background-color 50ms ease-in,color 50ms ease-in,border-color 50ms ease-in,opacity 50ms ease-in -} - -.colorable_f1ceac:hover { - transition: background-color .15s ease-out,color .15s ease-out,border-color .15s ease-out,opacity .15s ease-out -} - -.colorable_f1ceac.red_f1ceac,.colorable_f1ceac.red_f1ceac .centerIcon_f1ceac { - color: var(--red-400) -} - -.colorable_f1ceac.red_f1ceac.active_f1ceac,.colorable_f1ceac.red_f1ceac:hover { - background: hsla(0,0%,100%,.1) -} - -.colorable_f1ceac.disconnect_f1ceac { - background: var(--control-critical-primary-background-default); - transition: background .15s ease-in-out -} - -.colorable_f1ceac.disconnect_f1ceac,.colorable_f1ceac.disconnect_f1ceac .centerIcon_f1ceac { - color: var(--control-critical-primary-text-default) -} - -.colorable_f1ceac.disconnect_f1ceac:hover { - background: var(--control-critical-primary-background-hover) -} - -.colorable_f1ceac.disconnect_f1ceac:active { - background: var(--control-critical-primary-background-active) -} - -.colorable_f1ceac.redGlow_f1ceac { - background: var(--opacity-red-12) -} - -.colorable_f1ceac.redGlow_f1ceac,.colorable_f1ceac.redGlow_f1ceac .centerIcon_f1ceac { - color: var(--red-400) -} - -.colorable_f1ceac.redGlow_f1ceac:active { - background: var(--opacity-red-24) -} - -.colorable_f1ceac.redGlow_f1ceac.popoutOpen_f1ceac,.colorable_f1ceac.redGlow_f1ceac:hover { - background: var(--opacity-red-16) -} - -.colorable_f1ceac.primaryLight_f1ceac { - background: var(--primary-130) -} - -.colorable_f1ceac.primaryLight_f1ceac,.colorable_f1ceac.primaryLight_f1ceac .centerIcon_f1ceac { - color: var(--primary-860) -} - -.colorable_f1ceac.primaryLight_f1ceac.active_f1ceac,.colorable_f1ceac.primaryLight_f1ceac:hover { - background: var(--primary-230) -} - -.colorable_f1ceac.white_f1ceac { - background: var(--white) -} - -.colorable_f1ceac.white_f1ceac,.colorable_f1ceac.white_f1ceac .centerIcon_f1ceac { - color: var(--primary-860) -} - -.colorable_f1ceac.white_f1ceac.active_f1ceac,.colorable_f1ceac.white_f1ceac:hover { - background: var(--primary-130) -} - -.colorable_f1ceac.green_f1ceac { - background: var(--green-360) -} - -.colorable_f1ceac.green_f1ceac,.colorable_f1ceac.green_f1ceac .centerIcon_f1ceac { - color: var(--white) -} - -.colorable_f1ceac.green_f1ceac.active_f1ceac,.colorable_f1ceac.green_f1ceac:hover,.colorable_f1ceac.join_f1ceac { - background: var(--green-360) -} - -.colorable_f1ceac.join_f1ceac,.colorable_f1ceac.join_f1ceac .centerIcon_f1ceac { - color: var(--white) -} - -.colorable_f1ceac.join_f1ceac.active_f1ceac,.colorable_f1ceac.join_f1ceac:hover { - background: var(--green-360) -} - -.colorable_f1ceac.greenGlow_f1ceac { - background: var(--opacity-green-12) -} - -.colorable_f1ceac.greenGlow_f1ceac,.colorable_f1ceac.greenGlow_f1ceac .centerIcon_f1ceac { - color: var(--green-300) -} - -.colorable_f1ceac.greenGlow_f1ceac:active { - background: var(--opacity-green-36) -} - -.colorable_f1ceac.greenGlow_f1ceac.popoutOpen_f1ceac,.colorable_f1ceac.greenGlow_f1ceac:hover { - background: var(--opacity-green-24) -} - -.colorable_f1ceac.premiumGradient_f1ceac { - background: linear-gradient(to right,var(--premium-tier-2-purple-for-gradients),var(--premium-tier-2-purple-for-gradients-2),var(--premium-tier-2-pink-for-gradients)) -} - -.colorable_f1ceac.premiumGradient_f1ceac,.colorable_f1ceac.premiumGradient_f1ceac .centerIcon_f1ceac { - color: var(--white) -} - -.colorable_f1ceac.yellow_f1ceac { - background: var(--yellow-400) -} - -.colorable_f1ceac.yellow_f1ceac,.colorable_f1ceac.yellow_f1ceac .centerIcon_f1ceac { - color: var(--white) -} - -.colorable_f1ceac.yellow_f1ceac.active_f1ceac,.colorable_f1ceac.yellow_f1ceac:hover { - background: var(--yellow-530) -} - -.colorable_f1ceac.primaryDark_f1ceac,.colorable_f1ceac.primaryDark_f1ceac .centerIcon_f1ceac { - color: var(--white) -} - -.colorable_f1ceac.primaryDark_f1ceac.active_f1ceac:not(.disabled_f1ceac),.colorable_f1ceac.primaryDark_f1ceac:hover:not(.disabled_f1ceac) { - background: hsla(0,0%,100%,.1) -} - -.colorable_f1ceac.activeLight_f1ceac { - background: var(--white) -} - -.colorable_f1ceac.activeLight_f1ceac,.colorable_f1ceac.activeLight_f1ceac .centerIcon_f1ceac { - color: var(--primary-860) -} - -.colorable_f1ceac.activeLight_f1ceac.active_f1ceac:not(.disabled_f1ceac),.colorable_f1ceac.activeLight_f1ceac:hover:not(.disabled_f1ceac) { - background: var(--primary-230) -} - -.buttonInnerWithText_f1ceac { - align-items: center; - display: flex; - gap: 4px -} - -.centerButton_f1ceac { - border-radius: 8px; - padding: 10px; - position: static; - transition: background .15s ease-in-out,color .15s ease-in-out -} - -.centerButton_f1ceac .centerIcon_f1ceac { - transition: color .15s ease-in-out -} - -.attachedButton_f1ceac { - border-radius: 8px 0 0 8px -} - -.fullRegionButton_f1ceac,.fullRegionDropdownButton_f1ceac { - border: 1px solid var(--border-muted); - border-radius: 12px -} - -.fullRegionDropdownButton_f1ceac { - align-items: center; - display: flex; - justify-content: center; - padding: 4px -} - -.fullRegionDropdownButton_f1ceac.green_f1ceac,.fullRegionDropdownButton_f1ceac.join_f1ceac { - background-color: var(--green-360) -} - -.fullRegionDropdownButton_f1ceac.disconnect_f1ceac { - background-color: var(--control-critical-primary-background-default) -} - -.fullRegionIcon_f1ceac { - padding: 4px 12px -} - -.buttonPremiumGlow_f1ceac { - filter: drop-shadow(0 0 12px #a944b0) drop-shadow(0 0 12px #a944b0) -} - -@media (max-width: 456px) { - .centerButton_f1ceac { - padding:8px - } - - .contextMenuContainer_f1ceac { - display: none - } -} - -@media (min-width: 457px) { - .unmasked_f1ceac { - display:none - } -} - -.lineHeightReset_f1ceac { - line-height: 0 -} - -.contextMenuContainer_f1ceac { - position: relative -} - -.attachedCaretButtonContainer_f1ceac { - align-items: center; - direction: row; - display: flex -} - -.attachedCaretButtonContainer_f1ceac.popoutOpen_f1ceac .primaryDark_f1ceac,.attachedCaretButtonContainer_f1ceac:hover .primaryDark_f1ceac { - background-color: var(--background-mod-muted) -} - -.attachedCaretButtonContainer_f1ceac.disabled_f1ceac { - background-color: transparent!important; - color: var(--text-muted)!important; - cursor: not-allowed!important; - pointer-events: none -} - -.attachedCaretButtonContainer_f1ceac.disabled_f1ceac:hover { - background-color: transparent!important -} - -.contextMenuNub_f1ceac { - align-items: center; - border-radius: 8px; - cursor: pointer; - display: flex; - height: 20px; - justify-content: center; - padding: 10px 2px; - transition: background .15s ease-in-out,color .15s ease-in-out; - width: 20px -} - -.contextMenuNub_f1ceac.disabled_f1ceac { - cursor: not-allowed -} - -.contextMenuNub_f1ceac.attachedCaret_f1ceac { - border-radius: 0 8px 8px 0; - margin-inline-start:1px;padding: 10px 0 -} - -.contextMenuCaret_f1ceac { - cursor: pointer; - height: 16px; - transition: none; - width: 16px -} - -.contextMenuCaret_f1ceac.disabled_f1ceac { - color: var(--interactive-text-default)!important; - cursor: not-allowed -} - -.contextMenuCaret_f1ceac.open_f1ceac { - transform: rotateX(180deg) -} - -.enable-forced-colors .contextMenuNub_f1ceac,.enable-forced-colors .contextMenuNub_f1ceac:hover { - background-color: ButtonFace; - border: 1px solid Canvas; - color: ButtonText -} - -.enable-forced-colors .contextMenuNub_f1ceac.active_f1ceac { - background-color: HighlightText; - border-color: Highlight; - color: Highlight -} - -.buttonMask_f1ceac { - height: inherit; - width: inherit -} - -.glow_f1ceac { - height: 190%; - inset-inline-start: 50%; - opacity: 1; - position: absolute; - top: 50%; - transform: translate(-50%,-50%); - width: 190% -} - -.glow_f1ceac,.glowVideo_f1ceac { - pointer-events: none -} - -.glowVideo_f1ceac { - height: 100%; - -o-object-fit: fill; - object-fit: fill; - width: 100% -} - -.mediaBarInteraction_b26b79,.mediaBarInteractionDragging_b26b79 { - align-items: center; - align-self: stretch; - cursor: pointer; - display: flex; - flex: 1 1 auto; - margin: 0 7px; - position: relative -} - -.mediaBarInteraction_b26b79:hover .mediaBarWrapper_b26b79,.mediaBarInteractionDragging_b26b79:hover .mediaBarWrapper_b26b79 { - box-shadow: 0 1px 1px var(--opacity-black-28) -} - -.mediaBarInteraction_b26b79:hover .bubble_b26b79,.mediaBarInteractionDragging_b26b79:hover .bubble_b26b79 { - opacity: 1 -} - -.mediaBarInteraction_b26b79:hover .mediaBarGrabber_b26b79 { - background-color: var(--brand-560); - transform: scale(1) -} - -.mediaBarInteraction_b26b79:hover .mediaBarPreview_b26b79 { - opacity: .3 -} - -.mediaBarInteraction_b26b79:hover .bubble_b26b79,.mediaBarInteractionDragging_b26b79 .bubble_b26b79 { - opacity: 1 -} - -.mediaBarInteractionDragging_b26b79 .mediaBarGrabber_b26b79 { - background-color: var(--brand-560); - transform: scale(1) -} - -.mediaBarInteractionVolume_b26b79 { - align-self: center; - background-color: var(--opacity-black-68); - border-radius: 8px; - flex: none; - margin-block:0;margin-inline:0 4px;padding: 4px 8px; - width: 72px -} - -.vertical_b26b79 { - align-items: center; - display: flex; - height: 54px; - transform: rotate(-90deg); - transform-origin: top; - width: 140px -} - -.horizontal_b26b79 { - align-self: stretch; - display: flex; - width: 100% -} - -.fakeEdges_b26b79 { - position: relative -} - -.fakeEdges_b26b79:after,.fakeEdges_b26b79:before { - content: ""; - height: 100%; - position: absolute; - top: 0; - width: 3px; - z-index: 1 -} - -.fakeEdges_b26b79:before { - border-radius: 3px 0 0 3px; - inset-inline-start: -3px -} - -.fakeEdges_b26b79:after { - border-radius: 0 3px 3px 0; - inset-inline-end: -3px -} - -.buffer_b26b79 { - height: 100%; - inset-inline-start: 0; - opacity: .3; - position: absolute; - top: 0 -} - -.buffer_b26b79,.buffer_b26b79:after,.buffer_b26b79:before { - background-color: var(--white) -} - -.mediaBarWrapper_b26b79 { - flex: 1 1 auto; - height: 6px; - position: relative -} - -.mediaBarWrapper_b26b79,.mediaBarWrapper_b26b79:after,.mediaBarWrapper_b26b79:before { - background-color: hsl(var(--primary-300-hsl)/.3) -} - -.mediaBarWrapperVolume_b26b79 { - display: flex; - flex: none; - justify-content: center; - width: 72px -} - -.mediaBarPreview_b26b79,.mediaBarProgress_b26b79 { - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.mediaBarPreview_b26b79,.mediaBarProgress_b26b79 { -} - -.mediaBarPreview_b26b79 { - opacity: 0; - z-index: 0 -} - -.mediaBarPreview_b26b79,.mediaBarPreview_b26b79:after,.mediaBarPreview_b26b79:before { - background-color: var(--white) -} - -.mediaBarProgress_b26b79 { - z-index: 3 -} - -.mediaBarGrabber_b26b79,.mediaBarProgress_b26b79,.mediaBarProgress_b26b79:after,.mediaBarProgress_b26b79:before { - background-color: var(--brand-500) -} - -.mediaBarGrabber_b26b79 { - border-radius: 5px; - cursor: grab; - height: 10px; - inset-inline-end: 0; - margin-top: -5px; - margin-inline-end:-5px;position: absolute; - top: 50%; - transform: scale(0); - transform-origin: 50% 50%; - width: 10px; - z-index: 2 -} - -.full-motion .mediaBarGrabber_b26b79 { - transition: transform .25s ease-in-out,background-color .25s linear -} - -.bubble_b26b79 { - background-color: var(--black); - border-radius: 3px; - color: var(--primary-100); - font-size: 12px; - font-weight: var(--font-weight-semibold); - height: 18px; - line-height: 18px; - opacity: 0; - padding: 0 8px; - text-align: center; - top: -28px; - transform: translateX(-50%); - transition: opacity .2s ease-out; - width: auto -} - -.bubble_b26b79,.bubble_b26b79:before { - pointer-events: none; - position: absolute -} - -.bubble_b26b79:before { - border: 5px solid transparent; - border-top: 5px solid var(--black); - content: " "; - height: 0; - inset-inline-start: 50%; - margin-inline-start:-5px;top: 100%; - width: 0 -} - -.enable-forced-colors .mediaBarGrabber_b26b79,.enable-forced-colors .mediaBarProgress_b26b79 { - background-color: ButtonText!important -} - -.enable-forced-colors .mediaBarInteractionVolume_b26b79 { - background-color: ButtonFace -} - -.cover__6eb54 { - align-items: center; - display: flex; - inset: 0; - justify-content: center; - pointer-events: none; - position: absolute -} - -.cover__6eb54.active__6eb54 { - cursor: pointer; - pointer-events: auto -} - -.cover__6eb54.active__6eb54:hover .iconWrapper__6eb54 { - opacity: .8 -} - -.cover__6eb54.active__6eb54:active .iconWrapper__6eb54 { - transform: translateY(1px) -} - -.iconWrapper__6eb54 { - background-color: var(--black); - border-radius: 24px; - color: var(--white); - opacity: .6; - padding: 12px; - transition: opacity .25s,color .25s -} - -.icon__6eb54 { - display: block; - height: 24px; - margin-inline:1px -1px;width: 24px -} - -.enable-forced-colors .iconWrapper__6eb54 { - background-color: ButtonFace; - color: ButtonText; - opacity: 1 -} - -.wrapper__926d7 { - background-color: var(--opacity-black-60); - border-radius: 24px; - box-sizing: border-box; - color: var(--white); - display: flex; - flex: 0 0 auto; - height: 48px; - padding: 12px; - pointer-events: none -} - -.wrapper__926d7 a:link,.wrapper__926d7 a:visited { - color: var(--white)!important; - cursor: pointer; - display: block; - pointer-events: auto -} - -.iconWrapper__926d7 { - align-items: center; - cursor: pointer; - display: flex; - flex: 0 0 auto; - opacity: .6; - pointer-events: auto; - transition: opacity .25s,color .25s -} - -.disableInteractions__926d7 .iconWrapper__926d7,.disableInteractions__926d7 a:link,.disableInteractions__926d7 a:visited { - pointer-events: none -} - -.iconWrapperActive__926d7 { -} - -.iconWrapperActive__926d7:hover { - opacity: 1 -} - -.iconWrapperActive__926d7:active { - transform: translateY(1px) -} - -.text__926d7 { - font-size: 16px; - padding-inline-start:4px} - -.icon__926d7 { - display: block; - height: 24px; - width: 24px -} - -.iconPlay__926d7 { - margin-inline:1px -1px} - -.iconExternal__926d7,.iconExternalMargins__926d7,.iconPlay__926d7 { -} - -.iconExternalMargins__926d7 { - margin-inline:2px 4px} - -.enable-forced-colors .iconWrapper__926d7 { - background-color: ButtonFace; - color: ButtonText; - opacity: 1 -} - -.statsOverlay__1219f { - background-color: var(--opacity-black-72); - border-radius: 8px; - color: var(--text-default); - display: flex; - flex-direction: column; - font-size: 14px; - inset-inline-start: 8px; - line-height: 18px; - padding: 8px; - pointer-events: auto; - position: absolute; - top: 8px; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text; - z-index: 100 -} - -.header__1219f { - align-items: center; - display: flex; - justify-content: space-between; - margin-bottom: 8px -} - -.title__1219f { - font-size: 16px; - font-weight: var(--font-weight-bold) -} - -.headerButtons__1219f { - align-items: center; - display: flex; - gap: 4px -} - -.closeButton__1219f,.copyButton__1219f { - align-items: center; - border-radius: 4px; - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - height: 24px; - justify-content: center; - width: 24px -} - -.full-motion .closeButton__1219f,.full-motion .copyButton__1219f { - transition: all .1s ease -} - -.closeButton__1219f:hover,.copyButton__1219f:hover { - background-color: var(--custom-opacity-white-10); - color: var(--interactive-text-hover) -} - -.full-motion .closeButton__1219f:active,.full-motion .copyButton__1219f:active { - background-color: var(--custom-opacity-white-15); - transform: translateY(1px) -} - -.closeButton__1219f:active,.copyButton__1219f:active { - background-color: var(--custom-opacity-white-15) -} - -.content__1219f { - display: flex; - flex-direction: column -} - -.statRow__1219f { - display: flex; - justify-content: space-between; - margin-bottom: 4px -} - -.statRow__1219f span { - margin-inline-end:8px} - -.statLabel__1219f { - font-size: 14px; - line-height: 18px -} - -.statValue__1219f { - font-weight: var(--font-weight-bold); - text-align: end; - word-break: break-all -} - -.errorSection__1219f { - background-color: var(--custom-opacity-red-8); - border-inline-start:3px solid var(--border-feedback-critical);border-radius: 3px; - margin-top: 4px; - padding: 6px 8px -} - -.errorSection__1219f .statLabel__1219f,.errorSection__1219f .statValue__1219f { - color: var(--text-feedback-critical) -} - -.container__2d263 { - align-items: center; - display: flex; - flex-direction: column; - justify-content: flex-end; - position: relative -} - -.volumeButton__2d263 { - cursor: pointer; - line-height: 0 -} - -.volumeButtonSlider__2d263 { - bottom: calc(100% + 16px); - display: none; - inset-inline: -78px 0; - position: absolute; - -webkit-app-region: no-drag; - pointer-events: none -} - -.volumeButtonSlider__2d263.sliderVisible__2d263 { - display: initial -} - -.mediaBar__2d263 { - overflow: hidden; - pointer-events: auto -} - -.wrapper_cf09d8 { - background-color: var(--background-base-lower); - border-radius: 3px; - color: var(--white); - overflow: hidden; - position: relative; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.wrapperAudio_cf09d8,.wrapperControlsHidden_cf09d8,.wrapperPaused_cf09d8,.wrapperPlaying_cf09d8 { -} - -.wrapperAudio_cf09d8 { - background-color: var(--background-surface-high) -} - -.wrapperMediaMosaic_cf09d8 { - height: 100%; - max-height: inherit; - width: 100% -} - -.wrapperControlsHidden_cf09d8 { - cursor: default -} - -.wrapperAudio_cf09d8 { - background-color: var(--background-base-lower); - border-color: var(--border-subtle); - border-style: solid; - border-width: 1px; - box-sizing: border-box; - display: flex; - flex-direction: column; - justify-content: space-between; - max-width: 100%; - overflow: visible; - padding: 10px -} - -.wrapperAudio_cf09d8.newMosaicStyle_cf09d8 { - border-radius: 8px; - padding: 16px; - width: 432px -} - -.videoControls_cf09d8 { - background-color: var(--opacity-black-60); - inset-inline: 0; - bottom: -10px; - height: 32px; - padding-bottom: 10px; - position: absolute -} - -.audioControls_cf09d8,.videoControls_cf09d8 { - align-items: center; - display: flex; - width: 100% -} - -.audioControls_cf09d8 { - background-color: var(--opacity-black-40); - border-radius: var(--radius-sm); - margin-top: 4px -} - -.controlIcon_cf09d8 { - color: var(--interactive-text-default); - cursor: pointer; - display: block; - flex: 0 0 auto; - height: 24px; - padding: 4px; - width: 24px -} - -.controlIcon_cf09d8:hover { - color: var(--interactive-text-active) -} - -.controlIcon_cf09d8:active { - transform: translateY(1px) -} - -.volumeSliderWrapper_cf09d8 { - margin-bottom: 4px; - margin-inline-start:-4px} - -.durationTimeWrapper_cf09d8 { - flex: 0 0 auto; - height: 12px; - margin: 4px -} - -.durationTimeDisplay_cf09d8,.durationTimeSeparator_cf09d8 { - display: inline-block; - font-family: var(--font-code); - font-size: 12px; - font-weight: var(--font-weight-medium); - line-height: 12px; - vertical-align: text-top -} - -.durationTimeSeparator_cf09d8 { - margin: 0 2px -} - -.video_cf09d8 { - border-radius: 3px; - display: block; - height: 100%; - -o-object-fit: contain; - object-fit: contain; - position: relative; - width: 100% -} - -.video_cf09d8:-webkit-full-screen { - z-index: 99999 -} - -.video_cf09d8::-webkit-media-controls-enclosure { - display: none!important -} - -.audio_cf09d8 { - display: block; - height: 0; - position: absolute; - width: 0 -} - -.audioMetadata_cf09d8 { - display: flex -} - -.audioMetadata_cf09d8:before { - background-image: url(/assets/a6db1d05d35f13d0.svg); - background-repeat: no-repeat; - background-size: 100% auto; - content: ""; - height: 40px; - width: 24px -} - -.audioMetadata_cf09d8 .metadataContent_cf09d8 { - padding: 0 8px -} - -.audioMetadata_cf09d8 .metadataDownload_cf09d8 { - height: 24px; - opacity: 1 -} - -.audioMetadata_cf09d8 .metadataIcon_cf09d8 { - color: var(--interactive-text-default) -} - -.audioMetadata_cf09d8 .metadataIcon_cf09d8:hover { - color: var(--interactive-text-hover) -} - -.audioMetadata_cf09d8 .metadataSize_cf09d8 { - color: var(--text-muted) -} - -.metadata_cf09d8 { - align-items: flex-start; - background-image: linear-gradient(0deg,transparent,rgba(0,0,0,.9)); - box-sizing: border-box; - display: flex; - height: 80px; - position: absolute; - top: -10px; - inset-inline: 0; - padding: 22px 12px 12px; - z-index: 1 -} - -.metadataContent_cf09d8 { - flex: 1 1 auto; - overflow: hidden; - white-space: nowrap -} - -.metadataName_cf09d8 { - font-size: 16px; - line-height: 20px -} - -.metadataName_cf09d8,.metadataSize_cf09d8 { - font-weight: var(--font-weight-medium); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.metadataSize_cf09d8 { - font-size: 12px; - line-height: 16px; - opacity: .7 -} - -.metadataDownload_cf09d8 { - flex: 0 0 auto; - opacity: .6 -} - -.metadataDownload_cf09d8:hover { - opacity: 1 -} - -.metadataIcon_cf09d8 { - color: var(--white); - height: 25px; - width: 24px -} - -.playCenter_cf09d8 { - align-items: center; - display: flex; - justify-content: center; - top: 0; - inset-inline: 0; - bottom: 0; - z-index: 1 -} - -.playCenter_cf09d8,.playPausePop_cf09d8 { - pointer-events: none; - position: absolute -} - -.playPausePop_cf09d8 { - background-color: var(--opacity-black-60); - border-radius: 50%; - color: var(--white); - height: 24px; - inset-inline-start: 50%; - margin-inline-start:-23px;margin-top: -23px; - padding: 12px; - top: 50%; - width: 24px -} - -.playPausePop_cf09d8 .playPausePopIcon_cf09d8 { - display: block; - height: 24px; - width: 24px -} - -.videoButton_cf09d8 { - margin-inline-end:8px} - -@keyframes overlayContentHidden_cf09d8 { - 0% { - opacity: 1 - } - - to { - opacity: 0 - } -} - -.overlayContentHidden_cf09d8 { - animation: overlayContentHidden_cf09d8 .2s ease-in-out forwards -} - -.background-opacity-high .audioControls_cf09d8,.background-opacity-low .audioControls_cf09d8,.background-opacity-medium .audioControls_cf09d8 { - background-color: var(--opacity-black-28) -} - -.background-opacity-high .audioMetadata_cf09d8:before,.background-opacity-low .audioMetadata_cf09d8:before,.background-opacity-medium .audioMetadata_cf09d8:before { - opacity: .6 -} - -.enable-forced-colors .wrapper_cf09d8 { - outline: 2px solid CanvasText -} - -.enable-forced-colors .videoButton_cf09d8 { - background-color: ButtonFace; - border-radius: 4px; - color: ButtonText -} - -.enable-forced-colors .metadataDownload_cf09d8 { - opacity: 1 -} - -.enable-forced-colors .videoControls_cf09d8 { - background-color: ButtonFace -} - -.dimensionlessImage_a22bfd { - height: 100%; - -o-object-fit: scale-down; - object-fit: scale-down; - width: 100% -} - -.wrapper_a22bfd { - cursor: zoom-in -} - -.zoomed_a22bfd { - cursor: zoom-out -} - -.media_a22bfd { - border-radius: 0 -} - -.galleryContainer_d75751 { - align-self: flex-start; - display: flex; - grid-column: 2/span 3; - grid-row: 3; - justify-content: center; - justify-self: center; - translate: 0 -4px; - width: 100% -} - -.gallery_d75751 { - display: flex; - flex-shrink: 1; - gap: 2px; - max-width: -moz-fit-content; - max-width: fit-content -} - -.galleryItemOverlay_d75751 { - background-color: var(--black); - height: 40px; - opacity: .5; - position: absolute; - transition: opacity .2s ease-in-out; - width: 40px; - z-index: 1 -} - -.galleryItemOverlay_d75751:hover { - opacity: .15 -} - -.galleryItemOverlay_d75751.selected_d75751 { - opacity: 0 -} - -.galleryItem_d75751 { - background-color: var(--background-base-low); - border-radius: 2px; - cursor: pointer; - height: 40px; - margin: 4px 0; - position: relative; - width: 40px -} - -.galleryItem_d75751.first_d75751 { - border-radius: 8px 2px 2px 8px; - margin-inline-start:8px} - -.galleryItem_d75751.last_d75751 { - border-radius: 2px 8px 8px 2px; - margin-inline-end:8px} - -.galleryItem_d75751 * { - border-radius: inherit -} - -.galleryItem_d75751.inactive_d75751:after { - background-color: var(--black); - border-radius: inherit; - content: ""; - height: 100%; - inset-inline-start: 0; - opacity: .5; - position: absolute; - top: 0; - transition: opacity .2s ease-in-out; - width: 100% -} - -.galleryItem_d75751.inactive_d75751:after:hover { - opacity: .15 -} - -.obscured_d75751 { - background-color: var(--spoiler-hidden-background); - border-color: var(--spoiler-hidden-background) -} - -.button_aec7ab { - background-color: var(--background-surface-highest); - border-radius: var(--radius-md); - cursor: pointer; - display: grid; - height: 40px; - place-items: center; - width: 40px -} - -.buttonInner_aec7ab { - border: 1px solid var(--border-muted); - border-radius: inherit; - color: var(--interactive-text-default); - display: grid; - height: 38px; - place-items: center; - transition: color .2s ease-in-out,background-color .2s ease-in-out; - width: 38px -} - -.buttonInner_aec7ab:hover { - background-color: var(--background-mod-muted); - color: var(--interactive-text-hover) -} - -.buttonInner_aec7ab:active { - background-color: var(--background-mod-subtle); - color: var(--interactive-text-active) -} - -.mediaContainer_b2eddf { - flex-grow: 1; - grid-column: 1/span 5; - grid-row: 1/span 3; - position: relative -} - -.mediaContainer_b2eddf,.mediaWrapper_b2eddf { - align-items: center; - display: flex; - justify-content: center -} - -.mediaWrapper_b2eddf { - height: 100%; - margin: auto; - -o-object-fit: contain; - object-fit: contain; - width: 100% -} - -.nav_b2eddf { - grid-row: 1/span 3; - place-self: center; - position: relative; - z-index: 1 -} - -.navPrev_b2eddf { - grid-column: 1 -} - -.navNext_b2eddf { - grid-column: 5 -} - -.obscureWrapper_b2eddf { - display: flex; - height: 100%; - justify-content: center; - overflow: hidden -} - -.obscureWrapper_b2eddf.obscure_b2eddf { - filter: blur(var(--__obscured-background-blur-radius)) brightness(var(--__obscured-background-brightness)); - pointer-events: none -} - -.fadeInWrapper_b2eddf { - margin: auto -} - -.fileWrapper__0ccae { - max-width: 100%; - position: relative; - width: 432px -} - -.file__0ccae { - align-items: center; - background-color: var(--background-surface-high); - border: 1px solid transparent; - border-color: var(--border-subtle); - border-radius: 8px; - box-shadow: var(--shadow-low); - box-sizing: border-box; - display: flex; - flex-direction: row; - letter-spacing: 0; - padding: 16px; - width: 100% -} - -.fileInner__0ccae { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.icon__0ccae { - flex-shrink: 0; - height: 40px; - margin-inline-end:8px;width: 30px -} - -.filenameWrapper__0ccae { - align-items: flex-end; - display: flex -} - -.filename__0ccae { - font-size: 16px; - font-weight: var(--font-weight-normal); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .filename__0ccae { - font-weight:var(--font-weight-medium) - } -} - -.metadata__0ccae { - color: var(--text-muted); - font-size: 12px; - font-weight: var(--font-weight-normal); - line-height: 16px; - margin-inline-end:8px} - -.rate__0ccae { - flex-shrink: 0; - margin-inline-start:8px;min-width: 60px; - text-align: end -} - -.progressContainer__0ccae { - align-items: center; - display: flex; - height: 16px -} - -.cancelButton__0ccae,.size__0ccae { - margin-inline-start:4px} - -.cancelButton__0ccae { - color: var(--interactive-text-default); - cursor: pointer -} - -.cancelButton__0ccae:hover { - color: var(--interactive-text-hover) -} - -.filenameLinkWrapper__0ccae { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.fileNameLink__0ccae:hover { - text-decoration: underline -} - -.filename__0ccae { - color: var(--interactive-text-active) -} - -.filenameLinkWrapper__0ccae { - color: var(--text-link) -} - -.size__0ccae { - color: var(--text-muted) -} - -.custom-theme-background .file__0ccae { - background-color: var(--background-mod-subtle); - border-color: var(--border-subtle) -} - -@use postcss-pxtorem;.spinner__4d95d { - height: 100%; - width: 100% -} - -.container__4d95d { - display: grid; - max-width: 50vw; - width: 100% -} - -.textContainer__4d95d { - background-color: var(--background-base-lower); - border: 1px solid var(--border-subtle); - border-color: var(--border-subtle); - border-radius: 4px 4px 0 0; - box-sizing: border-box; - height: 100%; - max-height: 500px; - min-height: 118px; - overflow: hidden -} - -.textContainer__4d95d pre { - border-radius: 4px 4px 0 0; - box-sizing: border-box -} - -.newMosaicStyle__4d95d .textContainer__4d95d { - border-radius: 8px 8px 0 0 -} - -.codeView__4d95d { - background-color: transparent; - font-size: .875rem; - line-height: 1rem; - min-height: 100px; - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4 -} - -.codeView__4d95d::-webkit-scrollbar-track { - margin: 0 .5em -} - -.wordWrap__4d95d { - white-space: pre-wrap; - word-wrap: break-word; - overflow-wrap: break-word -} - -.newMosaicStyle__4d95d .codeView__4d95d { - margin: 8px 8px 0; - padding: 8px -} - -.expanded__4d95d { - overflow: auto -} - -.footer__4d95d { - align-items: center; - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: 0 0 8px 8px; - border-top: 0; - border-color: var(--border-subtle); - box-sizing: border-box; - color: var(--text-default); - display: flex; - flex-shrink: 0; - font-size: .875rem; - justify-content: flex-end; - min-width: 0; - padding: var(--space-12) -} - -.openFullPreviewSection__4d95d { - cursor: pointer; - margin-inline-start:var(--space-16)} - -.footerGap__4d95d { - flex: 200 0 auto; - min-width: var(--space-16) -} - -.nameSection__4d95d { - min-width: 0; - overflow: hidden -} - -.nameContainer__4d95d { - align-items: center; - display: flex; - flex-direction: row; - gap: 2px; - min-width: 0 -} - -.fileName__4d95d { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.expandCollapseSection__4d95d { - margin-inline-end:var(--space-8)} - -.languageSelector__4d95d { - background-color: var(--background-surface-high); - border-radius: 4px; - box-shadow: var(--shadow-border),var(--shadow-high); - margin-top: 4px; - max-width: 176px; - overflow: hidden; - padding: 8px 8px 4px -} - -.languageIcon__4d95d { - cursor: pointer; - display: flex -} - -.downloadAnchor__4d95d { - display: none -} - -.overflowIcon__4d95d { - cursor: pointer; - display: flex; - margin-inline-end:var(--space-4);margin-inline-start: var(--space-16) -} - -.modalContent__4d95d { - display: grid; - height: 100%; - min-height: 0; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text; - width: 100% -} - -.modalContent__4d95d .codeView__4d95d { - background-color: var(--background-surface-high) -} - -.modalFooter__4d95d { - align-items: center; - border-top: 1px solid var(--border-subtle); - border-color: var(--border-subtle); - box-sizing: border-box; - display: flex; - flex-shrink: 0; - font-size: .875rem; - justify-content: flex-end; - min-width: 0; - padding: var(--custom-modal-padding-md) var(--custom-modal-padding-md) 0 -} - -.modalTextContainer__4d95d { - background-color: var(--background-base-lower); - overflow: scroll -} - -.wrapper__58105 { - align-items: center; - contain: layout size; - display: flex; - height: 100%; - justify-content: flex-start; - overflow: hidden; - position: relative; - width: 8px -} - -.item__58105 { - background-color: var(--text-strong); - border-radius: 0 4px 4px 0; - display: block; - margin-inline-start:-4px;position: absolute; - width: 8px -} - -.enable-forced-colors .item__58105 { - background-color: Highlight -} - -.wrapper_b1fb0b { - align-items: center; - justify-content: flex-end -} - -.partyMembers_b1fb0b,.wrapper_b1fb0b { - display: flex -} - -.partyMember_b1fb0b,.partyMembers_b1fb0b,.wrapper_b1fb0b { - height: var(--custom-summary-avatars-avatar-diameter) -} - -.partyMember_b1fb0b { - display: inline-block; - margin-inline-start:-2px;-webkit-mask: url(/assets/eae6388e2d5a721a.svg); - mask: url(/assets/eae6388e2d5a721a.svg); - -webkit-mask-size: 100%; - mask-size: 100%; - mask-type: luminance; - width: var(--custom-summary-avatars-avatar-diameter) -} - -.partyMember_b1fb0b:first-child { - margin-inline-start:0} - -.partyMember_b1fb0b:last-child { - -webkit-mask: none; - mask: none -} - -.partyMemberOverflow_b1fb0b { - align-items: center; - background-color: var(--background-base-lower); - border-radius: 12px; - color: var(--text-default); - display: flex; - font-size: 14px; - font-weight: var(--font-weight-semibold); - height: var(--custom-summary-avatars-avatar-diameter); - justify-content: center; - line-height: 16px; - margin-inline-start:-2px;min-width: var(--custom-summary-avatars-avatar-diameter); - padding: 0 4px -} - -.theme-dark .partyMemberBackground_b1fb0b { - background-color: var(--primary-500) -} - -.theme-dark .partyMemberUnknown_b1fb0b { - background-color: var(--primary-500) -} - -.theme-light .partyMemberBackground_b1fb0b { - background-color: var(--primary-160) -} - -.theme-light .partyMemberUnknown_b1fb0b { - background-color: var(--primary-160) -} - -.partyMemberUnknownIcon_b1fb0b { - color: var(--text-muted) -} - -.container__0a12b { - align-items: center; - display: flex; - flex: 1; - flex-direction: column; - justify-content: center; - min-height: 300px; - padding: 32px; - text-align: center -} - -.iconContainer__0a12b { - margin-bottom: 16px; - position: relative -} - -.icon__0a12b { - background-color: var(--background-base-low); - border-radius: 80px; - color: var(--interactive-text-default); - display: inline-block; - padding: 22px -} - -.iconOffset__0a12b { - margin-inline-start:8px;margin-top: 4px -} - -.stars__0a12b { - inset-inline-start: -10px; - position: absolute -} - -.header__0a12b { - margin-bottom: 8px -} - -.header__0a12b,.text__0a12b { - text-transform: none -} - -.container_e97453 { - border-radius: 8px; - cursor: pointer; - margin-top: 4px; - margin-inline-start:8px;padding-inline:12px 8px;padding-bottom: 4px; - position: relative -} - -.container_e97453:hover { - background: var(--background-gradient-low,var(--background-base-lowest)) -} - -.rowHeader_e97453 { - flex-direction: row; - justify-content: space-between; - padding-bottom: 8px; - padding-top: 12px -} - -.rowHeader_e97453,.rowHeaderLeft_e97453 { - align-items: center; - display: flex -} - -.dot_e97453,.timestamp_e97453 { - padding-inline-end:8px} - -.dot_e97453 { - color: var(--border-subtle); - justify-content: center -} - -.icon_e97453 { - color: var(--text-default); - display: flex -} - -.count_e97453 { - min-width: -moz-fit-content; - min-width: fit-content; - padding-inline:5px 8px} - -.title_e97453 { - padding-bottom: 4px -} - -.subtitle_e97453 { - padding-bottom: 12px; - text-transform: none -} - -.unreadPill_e97453 { - inset-inline-start: -8px; - position: absolute; - top: 24px -} - -.feedbackContainer_e97453 { - display: flex; - flex-direction: column; - gap: 6px; - height: 100%; - inset-inline-end: 8px; - justify-content: center; - position: absolute; - top: 0 -} - -.thumbIcon_e97453 { - background: var(--background-mod-normal); - border-radius: 16px; - color: var(--interactive-text-default); - cursor: pointer; - padding: 8px -} - -.thumbIcon_e97453:active,.thumbIcon_e97453:hover { - color: var(--interactive-text-active) -} - -.backupContainer__0efdf { - display: flex; - justify-content: center -} - -.canvas__0efdf { - cursor: pointer; - flex: 1; - height: 32px; - margin-bottom: -4px; - margin-top: -4px; - min-width: 0 -} - -.container_a8e786 { - align-items: center; - background-color: var(--background-mod-subtle); - border-radius: 24px; - box-sizing: border-box; - display: flex; - flex-direction: row; - gap: var(--space-12); - max-width: 100%; - min-width: 0; - padding: 12px; - position: relative; - transition: background-color .2s ease-in-out -} - -.playIcon_a8e786 { - color: var(--white) -} - -.playing_a8e786 .playIcon_a8e786 { - color: var(--brand-500) -} - -.playButtonContainer_a8e786 { - align-items: center; - border-radius: 50%; - cursor: pointer; - display: flex; - flex-shrink: 0; - height: 32px; - justify-content: center; - margin-inline-start:-4px;margin-bottom: -4px; - margin-top: -4px; - position: relative; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - width: 32px -} - -.playButtonContainer_a8e786:active { - transform: scale(.875) -} - -.playButtonContainer_a8e786:active .playIcon_a8e786 { - color: var(--brand-360) -} - -.playing_a8e786 .playButtonContainer_a8e786 { - background: var(--white) -} - -.playing_a8e786 .playButtonContainer_a8e786:active { - background-color: var(--brand-360) -} - -.playing_a8e786 .playButtonContainer_a8e786:active .playIcon_a8e786 { - color: var(--brand-500) -} - -.theme-light .container_a8e786:not(.playing_a8e786) .playButtonContainer_a8e786 { - background: linear-gradient(151.11deg,var(--brand-400) 0,var(--brand-500) 100%) -} - -.theme-light .container_a8e786:not(.playing_a8e786) .playButtonContainer_a8e786:hover { - background: linear-gradient(135deg,var(--brand-360) 0,var(--brand-400) 100%) -} - -.theme-light .container_a8e786:not(.playing_a8e786) .playButtonContainer_a8e786:active { - background: linear-gradient(0deg,rgba(0,0,0,.2),a949CF7,rgba(0,0,0,.2)),linear-gradient(135deg,var(--brand-400) 0,var(--brand-500) 100%) -} - -.theme-dark .container_a8e786:not(.playing_a8e786) .playButtonContainer_a8e786 { - background: linear-gradient(151.11deg,var(--brand-460) 16.55%,var(--brand-560) 104.36%) -} - -.theme-dark .container_a8e786:not(.playing_a8e786) .playButtonContainer_a8e786:hover { - background: linear-gradient(151.11deg,var(--brand-400) 17.78%,var(--brand-460) 82.22%) -} - -.theme-dark .container_a8e786:not(.playing_a8e786) .playButtonContainer_a8e786:active { - background: linear-gradient(0deg,rgba(0,0,0,.2),rgba(0,0,0,.2)),linear-gradient(151.11deg,var(--brand-460) 16.55%,var(--brand-560) 104.36%) -} - -.playing_a8e786 { - border-color: var(--opacity-blurple-60) -} - -.oldPlayIconSpacing_a8e786 { - margin-inline:1px -1px} - -.audioElement_a8e786 { - display: none -} - -.playbackRate_a8e786 { - background-color: var(--background-mod-subtle); - border-radius: 4px; - color: var(--interactive-text-default); - text-align: center; - transition: background-color .2s ease-in,color .2s ease-in; - width: 32px -} - -.playbackRateContainer_a8e786 { - cursor: pointer; - padding: 4px 0; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - z-index: 1 -} - -.playbackRateContainer_a8e786:hover .playbackRate_a8e786 { - background-color: var(--interactive-background-hover); - color: var(--interactive-text-active); - transition: none -} - -.playing_a8e786 .playbackRateContainer_a8e786 .playbackRate_a8e786 { - background-color: var(--interactive-background-active); - color: var(--brand-200) -} - -.playing_a8e786 .playbackRateContainer_a8e786:hover .playbackRate_a8e786 { - background-color: var(--interactive-background-hover); - color: var(--white) -} - -.volumeButton_a8e786 { - flex-shrink: 0 -} - -.volumeButtonIcon_a8e786 { - color: var(--interactive-text-default); - transition: color .2s ease-in-out -} - -.volumeButtonIcon_a8e786:hover { - color: var(--interactive-text-hover); - transition: none -} - -.volumeButtonIcon_a8e786:active { - color: var(--interactive-text-active); - transition: none -} - -.playing_a8e786 .volumeButtonIcon_a8e786 { - color: var(--brand-200) -} - -.playing_a8e786 .volumeButtonIcon_a8e786:hover { - color: var(--white) -} - -.playing_a8e786 .volumeButtonIcon_a8e786:active { - color: var(--brand-200) -} - -.volumeSlider_a8e786 { - margin-bottom: 4px; - margin-inline-start:-8px;z-index: 2 -} - -.duration_a8e786 { - color: var(--interactive-text-default); - flex-shrink: 0; - transition: color .2s ease-in-out; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - z-index: 1 -} - -.playing_a8e786 .duration_a8e786 { - color: var(--white) -} - -.waveform_a8e786 { - z-index: 1 -} - -.rippleContainer_a8e786 { - border: 1px solid var(--border-subtle); - border-radius: 24px; - bottom: 0; - position: absolute; - top: 0; - inset-inline: 0; - overflow: hidden; - transition: border-color .2s ease-in-out -} - -.ripple_a8e786 { - aspect-ratio: 1/1; - background-color: var(--brand-530); - border-radius: 50%; - inset-inline-start: 24px; - position: absolute; - top: 50%; - transform: translate(-50%,-50%) -} - -.ripple_a8e786.reducedMotion_a8e786 { - opacity: 0; - transition: opacity .2s ease-in-out; - width: 200% -} - -.playing_a8e786 .ripple_a8e786.reducedMotion_a8e786 { - opacity: 1 -} - -.ripple_a8e786:not(.reducedMotion_a8e786) { - transition: width .2s ease-in-out; - width: 0 -} - -.playing_a8e786 .ripple_a8e786:not(.reducedMotion_a8e786) { - width: 200% -} - -.barBase__0f481 { - align-items: center; - border: 1px solid var(--border-subtle); - cursor: pointer; - display: flex; - inset-inline: var(--space-8) var(--space-16); - min-height: 24px; - position: absolute; - text-transform: capitalize; - transition: opacity .15s; - z-index: 2 -} - -.barBase__0f481,.barBase__0f481:hover { - opacity: 1 -} - -.barButtonBase__0f481 { - align-items: center; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background: none; - color: inherit; - display: flex; - font-size: 14px; - font-weight: var(--font-weight-medium); - justify-content: flex-start; - line-height: 18px; - padding: 0 12px; - padding-inline-start:var(--space-4);text-align: start -} - -.barButtonIcon__0f481 { - display: block; - height: 16px; - margin-inline-start:8px;position: relative; - top: -1px; - width: 16px -} - -.bottomBar__0f481 { - background-color: var(--background-surface-highest); - border: 1px solid var(--border-normal); - border-radius: var(--radius-lg); - bottom: calc(var(--custom-channel-textarea-text-area-height) + 28px); - box-shadow: var(--shadow-medium); - box-sizing: border-box; - inset-inline: 0; - margin-block:0 var(--space-8);margin-inline: auto; - min-height: 44px; - min-width: 284px; - padding-block:var(--space-8) var(--space-8);padding-inline: var(--space-16) var(--space-8); - width: -moz-max-content; - width: max-content -} - -.bottomBar__0f481:before { - content: ""; - inset-inline: -16px; - bottom: -9px; - box-sizing: var(--shadow-high); - height: 8px; - -webkit-mask-image: linear-gradient(to left,transparent,var(--background-surface-highest) 10%,var(--background-surface-highest) 90%,transparent); - mask-image: linear-gradient(to left,transparent,var(--background-surface-highest) 10%,var(--background-surface-highest) 90%,transparent); - position: absolute -} - -.jumpToPresentBar__0f481 { - bottom: 28px; - color: var(--text-default); - padding-bottom: var(--space-8) -} - -.jumpToPresentBar__0f481:active { - margin-bottom: calc(var(--space-8) - 1px) -} - -.jumpToPresentBar__0f481 .spinner__0f481 { - padding-inline-end:12px} - -.jumpToPresentBar__0f481 .spinnerItem__0f481 { - background-color: var(--white) -} - -.messagesErrorBar__0f481 { - background-color: var(--notice-background-critical); - border-color: var(--border-feedback-critical); - color: var(--notice-text-critical); - padding: var(--space-4) var(--space-8) -} - -.messagesErrorBar__0f481:active { - margin-bottom: -1px -} - -.messagesErrorBar__0f481 .spinner__0f481 { - padding-inline-end:12px} - -.messagesErrorBar__0f481 .spinnerItem__0f481 { - background-color: var(--notice-text-critical) -} - -.newMessagesBar__0f481 { - background-color: var(--brand-500); - border-radius: 0 0 8px 8px; - box-shadow: var(--elevation-low); - color: var(--white); - height: 32px; - padding-inline:var(--space-16);top: 0; - z-index: 3 -} - -.newMessagesBar__0f481:active { - box-shadow: 0 0 0 hsl(var(--black-hsl)/0); - padding-top: 1px -} - -.newMessagesPillContainer__0f481 { - align-items: center; - display: flex; - position: absolute; - top: 16px; - inset-inline: 0; - justify-content: center; - pointer-events: none; - z-index: 2 -} - -.containerMarginTop__0f481 { - top: 48px -} - -.newMessagesPill__0f481 { - align-items: center; - background-color: var(--background-surface-high); - border-radius: 18px 0 0 18px; - cursor: pointer; - display: flex; - padding-block:9px;padding-inline:16px 12px;pointer-events: auto -} - -.newMessagesPill__0f481:hover { - background-color: var(--background-base-lowest) -} - -.theme-light .newMessagesPill__0f481 { - background-color: var(--background-base-lower) -} - -.theme-light .newMessagesPill__0f481:hover { - background-color: var(--background-base-lowest) -} - -.newMessagesClear__0f481 { - align-items: center; - background-color: var(--background-surface-high); - border-inline-start:solid 1px var(--border-subtle);border-radius: 0 18px 18px 0; - cursor: pointer; - display: flex; - padding-block:10px;padding-inline:8px 10px;pointer-events: auto -} - -.newMessagesClear__0f481:hover { - background-color: var(--background-base-lowest) -} - -.theme-light .newMessagesClear__0f481 { - background-color: var(--background-base-lower) -} - -.theme-light .newMessagesClear__0f481:hover { - background-color: var(--background-base-lowest) -} - -.newMessagesClearIcon__0f481 { - color: var(--text-default); - height: 16px; - width: 16px -} - -.jumpToPresentButtonContainer__0f481 { - align-items: center; - bottom: 16px; - display: flex; - inset-inline: 0 16px; - justify-content: flex-end; - pointer-events: none; - position: absolute; - z-index: 2 -} - -.jumpToPresentButton__0f481 { - align-items: center; - background-color: var(--background-surface-high); - border-radius: 18px; - cursor: pointer; - display: flex; - padding: 6px; - pointer-events: auto -} - -.jumpToPresentButton__0f481:hover { - background-color: var(--background-base-lowest) -} - -.theme-light .jumpToPresentButton__0f481 { - background-color: var(--background-base-lower) -} - -.theme-light .jumpToPresentButton__0f481:hover { - background-color: var(--background-base-lowest) -} - -.jumpToPresentButtonIcon__0f481 { - color: var(--text-default); - height: 24px; - width: 24px -} - -.jumpToPresentSpinner__0f481 { - background-color: var(--background-surface-high); - border-radius: 18px; - height: 24px; - padding: 6px; - width: 24px -} - -.jumpToPresentSpinnerItem__0f481 { - color: var(--text-default) -} - -.barButtonMain__0f481 { - display: block; - flex: 1 1 auto; - position: relative -} - -.barButtonMain__0f481,.span__0f481 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.barButtonAlt__0f481 { - background-color: var(--control-secondary-background-default); - border-radius: var(--radius-sm); - flex: 0 0 auto; - font-weight: var(--font-weight-semibold); - padding: var(--space-4) var(--space-8); - position: relative -} - -.barButtonAlt__0f481:hover { - background-color: var(--control-secondary-background-hover) -} - -.loadingMore__0f481 { - align-items: center; - display: flex; - flex: 0 0 auto; - justify-content: center -} - -.loadingMore__0f481 .spinner__0f481 { - display: inline-block; - margin: 0 -} - -.hasMore__0f481,.loadingMore__0f481 { - height: 32px; - margin-block:16px;margin-inline:16px 6px} - -.hasMore__0f481 { - border-radius: 3px; - box-shadow: inset 0 0 0 1px var(--border-subtle); - color: var(--brand-500); - cursor: pointer; - font-size: 12px; - font-weight: var(--font-weight-medium); - line-height: 32px; - text-align: center; - text-transform: uppercase -} - -.hasMore__0f481:hover { - background-color: var(--interactive-background-hover) -} - -.messageGroupBlocked__0f481 { - background-color: var(--opacity-black-4); - color: var(--text-muted); - flex: 0 0 auto; - justify-content: center; - margin: 6px 0; - overflow: hidden -} - -.messageGroupBlocked__0f481:last-child { - margin-bottom: 12px -} - -.messageGroupBlockedBtn__0f481 { - color: var(--interactive-text-default); - cursor: pointer; - font-size: 12px; - font-weight: var(--font-weight-medium); - margin: 0; - padding: 9px 0; - text-align: center; - text-transform: uppercase -} - -.messageGroupBlockedBtn__0f481:hover { - background-color: var(--opacity-black-4) -} - -.revealed__0f481 .messageGroupBlockedBtn__0f481 { - color: var(--interactive-text-active) -} - -.tooltip__0f481 { - align-items: center; - display: flex; - flex-direction: column; - text-align: center -} - -.tooltipEmojiName__0f481 { - width: 100% -} - -.imageContent__0f481 { - display: flex; - flex: auto; - flex-flow: column nowrap -} - -.imageContainer__0f481 { - display: flex; - flex: auto; - flex-flow: row nowrap; - height: 100%; - width: 100% -} - -.altText__0f481 { - color: var(--text-muted); - display: inline-block; - font-size: 12px; - font-weight: var(--font-weight-medium); - line-height: 16px; - margin: .25rem 0 .75rem -} - -.mediaMosaicAltTextContainer__0f481 { - position: relative -} - -.mediaMosaicVideoAltTextContainer__0f481 { - inset-inline-start: 4.5px; - position: absolute; - top: 4px -} - -.mediaMosaicAltText__0f481 { - background-color: rgba(0,0,0,.6); - border-radius: 4px; - bottom: 4px; - color: var(--white); - font-size: 14px; - font-weight: var(--font-weight-semibold); - inset-inline-start: 4.5px; - line-height: 18px; - padding: 7px 8px; - position: absolute; - transition: background-color .2s ease-in-out; - z-index: 1 -} - -.mediaMosaicAltText__0f481.reducedSize__0f481 { - font-size: 12px; - padding: 0 4px -} - -.mediaMosaicVideoAltText__0f481 { - bottom: inherit; - inset-inline-start: inherit; - position: inherit -} - -.mediaMosaicAltText__0f481:hover { - background-color: rgba(0,0,0,.8); - transition: background-color .2s ease-in-out -} - -.mediaMosaicAltText__0f481:focus { - background-color: #000; - transition: background-color .2s ease-in-out -} - -.mediaMosaicAltText__0f481:active { - background-color: var(--background-mod-normal); - transition: background-color .2s ease-in-out -} - -.mediaMosaicAltTextPopout__0f481 { - background-color: var(--background-surface-high); - border-radius: 8px; - box-shadow: var(--shadow-border),var(--shadow-high); - color: var(--text-default); - display: flex; - flex-direction: column; - max-width: 360px; - padding: 13.5px 12px -} - -.mediaMosaicAltTextPopoutTitle__0f481 { - font-size: 12px; - font-weight: var(--font-weight-bold); - line-height: 16px; - margin-bottom: 9.5px; - text-transform: uppercase -} - -.mediaMosaicAltTextPopoutDescription__0f481 { - font-size: 16px; - font-weight: var(--font-weight-normal); - line-height: 20px; - word-break: break-word -} - -.clickCTA__0f481 { - color: var(--primary-300) -} - -.popoutContainer__0f481 { - -webkit-backdrop-filter: blur(8px); - backdrop-filter: blur(8px); - background-color: color-mix(in oklab,var(--background-surface-highest),transparent 10%); - border-radius: 8px; - box-shadow: var(--shadow-border),var(--shadow-low); - overflow: hidden; - width: var(--custom-message-helpers-popout-container-width) -} - -@keyframes popout-loading__0f481 { - 0% { - transform: translate3d(calc(var(--custom-message-helpers-popout-content-width)*-1),0,0) - } - - 50% { - transform: translateZ(0) - } - - to { - transform: translate3d(calc(var(--custom-message-helpers-popout-content-width)*-1),0,0) - } -} - -.popoutLoadingBackground__0f481 { - height: 78px; - margin: var(--custom-message-helpers-popout-padding-width); - -webkit-mask: url(data:image/svg+xml;utf8,%3Csvg%20width%3D%22256%22%20height%3D%2278%22%20viewBox%3D%2232%2024%20256%2078%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M36%2024C33.7909%2024%2032%2025.7909%2032%2028V78C32%2080.2091%2033.7909%2082%2036%2082H86C88.2091%2082%2090%2080.2091%2090%2078V28C90%2025.7909%2088.2091%2024%2086%2024H36ZM110%2024C107.791%2024%20106%2025.7909%20106%2028V38C106%2040.2091%20107.791%2042%20110%2042H179C181.209%2042%20183%2040.2091%20183%2038V28C183%2025.7909%20181.209%2024%20179%2024H110ZM106%2058C106%2055.7909%20107.791%2054%20110%2054H284C286.209%2054%20288%2055.7909%20288%2058V68C288%2070.2091%20286.209%2072%20284%2072H110C107.791%2072%20106%2070.2091%20106%2068V58ZM110%2084C107.791%2084%20106%2085.7909%20106%2088V98C106%20100.209%20107.791%20102%20110%20102H223C225.209%20102%20227%20100.209%20227%2098V88C227%2085.7909%20225.209%2084%20223%2084H110Z%22%20fill%3D%22%23000000%22%20%2F%3E%3C%2Fsvg%3E); - mask: url(data:image/svg+xml;utf8,%3Csvg%20width%3D%22256%22%20height%3D%2278%22%20viewBox%3D%2232%2024%20256%2078%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M36%2024C33.7909%2024%2032%2025.7909%2032%2028V78C32%2080.2091%2033.7909%2082%2036%2082H86C88.2091%2082%2090%2080.2091%2090%2078V28C90%2025.7909%2088.2091%2024%2086%2024H36ZM110%2024C107.791%2024%20106%2025.7909%20106%2028V38C106%2040.2091%20107.791%2042%20110%2042H179C181.209%2042%20183%2040.2091%20183%2038V28C183%2025.7909%20181.209%2024%20179%2024H110ZM106%2058C106%2055.7909%20107.791%2054%20110%2054H284C286.209%2054%20288%2055.7909%20288%2058V68C288%2070.2091%20286.209%2072%20284%2072H110C107.791%2072%20106%2070.2091%20106%2068V58ZM110%2084C107.791%2084%20106%2085.7909%20106%2088V98C106%20100.209%20107.791%20102%20110%20102H223C225.209%20102%20227%20100.209%20227%2098V88C227%2085.7909%20225.209%2084%20223%2084H110Z%22%20fill%3D%22%23000000%22%20%2F%3E%3C%2Fsvg%3E); - -webkit-mask-size: 100%; - mask-size: 100%; - mask-type: luminance; - overflow: hidden; - position: relative; - width: var(--custom-message-helpers-popout-content-width) -} - -.popoutLoadingForeground__0f481 { - animation: popout-loading__0f481 4s ease-in-out infinite; - background-image: linear-gradient(to right,var(--primary-600),var(--primary-500),var(--primary-600)); - bottom: 0; - inset-inline-start: 0; - position: absolute; - top: 0; - width: calc(var(--custom-message-helpers-popout-content-width)*2) -} - -.theme-light .popoutLoadingForeground__0f481 { - background-image: linear-gradient(to right,var(--primary-100),var(--primary-200),var(--primary-100)) -} - -.theme-light .topicsPillMiddle__0f481:hover { - background-color: var(--background-base-lower) -} - -&.background-opacity-high .messageGroupBlocked__0f481,&.background-opacity-low .messageGroupBlocked__0f481,&.background-opacity-medium .messageGroupBlocked__0f481 { - background-color: hsl(var(--primary-600-hsl)/.3); - border-color: hsl(var(--primary-630-hsl)/.2) -} - -&.background-opacity-high .messageGroupBlocked__0f481 .messageGroupBlockedBtn__0f481,&.background-opacity-low .messageGroupBlocked__0f481 .messageGroupBlockedBtn__0f481,&.background-opacity-medium .messageGroupBlocked__0f481 .messageGroupBlockedBtn__0f481 { - background-color: transparent; - color: var(--primary-100) -} - -&.background-opacity-high .messageGroupBlocked__0f481 .messageGroupBlockedBtn__0f481:hover,&.background-opacity-low .messageGroupBlocked__0f481 .messageGroupBlockedBtn__0f481:hover,&.background-opacity-medium .messageGroupBlocked__0f481 .messageGroupBlockedBtn__0f481:hover { - background: hsl(var(--primary-600-hsl)/.4) -} - -.background-opacity-low .divider__0f481:not(.dividerRed__0f481) { - opacity: .8 -} - -.background-opacity-low .divider__0f481:not(.dividerRed__0f481) .dividerContent__0f481 { - color: var(--white); - opacity: .8 -} - -.background-opacity-low .divider__0f481:not(.dividerRed__0f481) .dividerContent__0f481:before { - border-color: currentColor -} - -.background-opacity-medium .divider__0f481 { - opacity: .8 -} - -.background-opacity-medium .divider__0f481:not(.dividerRed__0f481) .dividerContent__0f481 { - color: var(--primary-200) -} - -.background-opacity-medium .divider__0f481:not(.dividerRed__0f481) .dividerContent__0f481:before { - background-color: currentColor; - opacity: .8 -} - -.background-opacity-high .divider__0f481 { - opacity: .5 -} - -.background-opacity-high .divider__0f481:not(.dividerRed__0f481) .dividerContent__0f481 { - color: var(--primary-200) -} - -.background-opacity-high .divider__0f481:not(.dividerRed__0f481) .dividerContent__0f481:before { - background-color: currentColor; - opacity: .8 -} - -.disableInteractions__0f481.hasMore__0f481 { - visibility: hidden -} - -.newTopicsBarContainer__0f481 { - height: 32px; - background-color: var(--brand-500); - border-radius: 0 0 8px 8px; - box-shadow: var(--elevation-low); - color: var(--white); - justify-content: space-between; - top: 0 -} - -.newTopicsBarContainer__0f481:active { - box-shadow: 0 0 0 hsl(var(--black-hsl)/0) -} - -@media (max-width: 914px) { - .newTopicsBarCompact__0f481 { - display:none - } - - .newTopicsBarText__0f481 .topicsPillDropdownCaret__0f481 { - padding-inline-start:0} -} - -.newTopicsBarTopicSection__0f481 { - align-items: center; - display: flex; - margin-inline-start:8px;max-width: 33%; - z-index: 2 -} - -.newTopicsBarTextClickable__0f481 { - overflow: hidden; - white-space: nowrap -} - -.newTopicsBarTextClickable__0f481:hover .newTopicsBarCaret__0f481 { - opacity: 1 -} - -.newTopicsBarCaret__0f481 { - margin-inline-end:4px;opacity: .7; - padding-inline-start:8px} - -.newTopicsBarText__0f481 { - color: var(--white); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.newTopicsBarIcon__0f481 { - margin-inline:4px;min-height: 16px; - min-width: 16px -} - -.newTopicsBarInitial__0f481 { - overflow: hidden; - text-overflow: ellipsis; - text-transform: none; - white-space: nowrap -} - -.newTopicsBarInitial__0f481,.newTopicsBarTextSelected__0f481,.newTopicsBarTextUnselected__0f481 { - align-items: center; - display: flex -} - -.newTopicsBarTextSelected__0f481 { - background-color: rgba(0,0,0,.1); - border-radius: 16px; - height: 20px -} - -.newTopicsBarTextSelected__0f481 .topicsPillDropdownCaret__0f481 { - color: var(--interactive-text-active) -} - -.flexEnd__0f481 { - justify-content: flex-end -} - -.flexStart__0f481 { - justify-content: flex-start -} - -.topicsPillContainer__0f481 { - background: var(--background-gradient-lower,var(--background-surface-high)); - border-radius: 0 0 8px 8px; - border-top: 0; - box-shadow: var(--shadow-low); - height: 32px; - min-height: 32px; - opacity: 1; - top: 0 -} - -.topicsPill__0f481 { - align-items: center; - display: flex; - width: 100%; - z-index: 2 -} - -.topicsPillText__0f481 { - cursor: pointer; - display: flex; - flex: 1; - overflow: hidden; - padding-block:8px;padding-inline:10px 8px;text-align: center -} - -.topicsPillText__0f481,.topicsPillText__0f481:hover .topicsPillDropdownCaret__0f481 { - color: var(--interactive-text-active) -} - -.topicsPillTextUnselected__0f481 { - align-items: center; - display: flex; - overflow: hidden; - padding-inline-start:4px;text-overflow: ellipsis; - white-space: nowrap -} - -.topicsPillTextSelected__0f481 { - background: var(--background-gradient-low,rgba(0,0,0,.1)); - border-radius: 16px; - display: flex; - height: 26px -} - -.topicsPillTextSelected__0f481 .topicsPillDropdownCaret__0f481 { - color: var(--interactive-text-active) -} - -.topicsPillSummaryIcon__0f481 { - margin-inline-end:4px;min-height: 16px; - min-width: 16px -} - -.topicsPillTextTitle__0f481 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.topicsPillDropdownCaret__0f481 { - color: var(--interactive-text-default); - margin-inline-end:4px;padding-inline-start:8px} - -.topicsPillCarets__0f481 { - display: flex; - height: 24px; - justify-content: space-between; - padding-inline-end:16px;top: 4px -} - -.topicsPillCaret__0f481 { - align-items: center; - background: var(--control-secondary-background-default); - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - justify-content: center; - width: 32px -} - -.topicsPillCaret__0f481:hover { - background: var(--control-secondary-background-hover); - color: var(--interactive-text-active) -} - -.topicsPillCaret__0f481:active { - background: var(--control-secondary-background-active) -} - -.topicsCaretRight__0f481 { - border-radius: 0 20px 20px 0 -} - -.topicsCaretLeft__0f481 { - border-inline-end:1px solid var(--border-subtle);border-radius: 20px 0 0 20px -} - -.topicsPillCaretDisabled__0f481 { - opacity: .5; - pointer-events: none -} - -.topicsDropdownBase__0f481 { - border: 1px solid var(--border-subtle); - border-radius: 8px; - cursor: default; - opacity: 1; - position: absolute; - width: 412px -} - -.topicsDropdown__0f481 { - top: 40px -} - -.topicsDropdownHeading__0f481 { - background: var(--background-gradient-low,var(--background-base-lowest)); - border-start-end-radius: 8px; - border-start-start-radius: 8px; - display: flex; - justify-content: space-between; - padding-block:14px;padding-inline:20px 14px} - -.topicsDropdownHeadingText__0f481 { - align-items: center; - display: flex; - padding-inline-end:16px;padding-top: 6px; - pointer-events: none -} - -.topicsPillHeadingIcon__0f481 { - color: var(--interactive-text-default); - padding-inline-end:8px} - -.summariesBetaTag__0f481 { - margin-inline-start:4px} - -.topicsDropdownClose__0f481 { - cursor: pointer; - opacity: .5; - transition: opacity .2s; - -webkit-app-region: no-drag; - color: var(--text-default) -} - -.topicsDropdownClose__0f481:hover { - opacity: 1 -} - -.topicsScroller__0f481 { - background: var(--background-gradient-lower,var(--background-base-lower)); - border-end-end-radius: 8px; - border-end-start-radius: 8px; - display: flex; - flex-direction: column; - max-height: 412px; - padding-bottom: 4px; - padding-top: 4px -} - -.topicsDotSpacer__0f481,.topicsTimeAgo__0f481 { - padding-inline-end:8px} - -.topicsDotSpacer__0f481 { - color: var(--border-subtle); - justify-content: center -} - -.topicsMessageCount__0f481 { - min-width: -moz-fit-content; - min-width: fit-content; - padding-inline:5px 8px} - -.topicsChatBubbleIcon__0f481 { - color: var(--text-default); - display: flex; - padding-inline-start:8px} - -.topicsTopicTitle__0f481 { - padding-bottom: 4px -} - -.topicsSubtitle__0f481 { - padding-bottom: 12px -} - -.emojiSection_d5cd2d { - padding: var(--custom-message-helpers-popout-padding-width); - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.customEmojiLabel_d5cd2d { - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden -} - -.theme-dark .betaTag_d5cd2d { - background-color: #fff; - color: var(--premium-tier-2-pink-for-gradients-2) -} - -.theme-light .betaTag_d5cd2d { - background: linear-gradient(to right,var(--premium-tier-2-pink-for-gradients),var(--premium-tier-2-pink-for-gradients-2),var(--premium-tier-2-purple-for-gradients)); - color: var(--white) -} - -.primaryEmoji_d5cd2d { - margin-top: 4px; - min-height: var(--custom-emoji-size-jumbo-emoji); - min-width: var(--custom-emoji-size-jumbo-emoji) -} - -.ctaButton_d5cd2d { - margin: 16px auto 0 -} - -.guildSection_d5cd2d { - background-color: var(--background-base-lower); - padding: var(--custom-message-helpers-popout-padding-width) -} - -.guildTitle_d5cd2d { - color: var(--text-default); - margin-bottom: 8px -} - -.guildIcon_d5cd2d { - cursor: pointer -} - -.guildIcon_d5cd2d,.guildIconNotClickable_d5cd2d { - height: 100%; - width: 100% -} - -.guildBadge_d5cd2d { - margin-inline-end:4px;min-width: 16px -} - -.guildName_d5cd2d { - cursor: pointer -} - -.showMoreEmojis_d5cd2d { - color: var(--interactive-text-default); - cursor: pointer; - font-weight: var(--font-weight-semibold); - margin-top: 8px; - transition: color .125s -} - -.showMoreEmojis_d5cd2d:hover { - color: var(--interactive-text-active) -} - -.showMoreEmojisArrow_d5cd2d { - height: 16px; - inset-inline-start: 4px; - position: relative; - width: 16px -} - -.full-motion .showMoreEmojisArrow_d5cd2d { - transition: transform .1s -} - -.showMoreEmojisArrow_d5cd2d.showMoreEmojisArrowCollapsed_d5cd2d { - transform: rotate(-90deg) -} - -.otherEmojisContainer_d5cd2d { - align-items: center; - display: flex; - flex-wrap: wrap; - margin-top: 4px -} - -.otherEmoji_d5cd2d { - flex-basis: calc(16.66667% - 4px); - height: 32px; - margin: 4px 2px; - min-width: 32px; - width: 32px -} - -.truncatingText_d5cd2d { - min-width: 0 -} - -.dotSeparator_d5cd2d { - background-color: var(--background-mod-muted); - border-radius: 50%; - flex-shrink: 0; - height: 4px; - margin: 0 8px; - width: 4px -} - -.theme-light .popoutContent_d5cd2d,.theme-light .popoutLoader_d5cd2d { - background-color: var(--background-surface-high); - box-shadow: var(--shadow-border),var(--shadow-high) -} - -.joinGuildLink_d5cd2d { - color: var(--text-link); - cursor: pointer -} - -.joinGuildLink_d5cd2d:focus,.joinGuildLink_d5cd2d:hover { - text-decoration: underline -} - -.reactionEmojiDetailsUnfurlGuildDetails_d5cd2d { - margin: 16px 0 8px -} - -.reactionEmojiDetailsClickable_d5cd2d { - border-radius: var(--radius-xs); - color: var(--interactive-text-hover); - cursor: pointer; - display: flex; - flex-direction: row; - font-weight: var(--font-weight-semibold); - justify-content: center; - margin-top: 8px; - padding: 6px 0; - transition: color .125s -} - -.reactionEmojiDetailsClickable_d5cd2d:hover { - color: var(--interactive-text-active) -} - -.theme-dark .reactionEmojiDetailsClickable_d5cd2d { - background-color: hsl(var(--primary-300-hsl)/.1) -} - -.theme-light .reactionEmojiDetailsClickable_d5cd2d { - background-color: hsl(var(--primary-700-hsl)/.1) -} - -.emojiDetailsLoader_d5cd2d { - box-shadow: none -} - -.reactionEmojiDetailsArrow_d5cd2d { - margin-top: 2px -} - -.full-motion .reactionEmojiDetailsArrow_d5cd2d { - transition: transform .1s -} - -.reactionEmojiDetailsArrow_d5cd2d.reactionEmojiDetailsArrowCollapsed_d5cd2d { - transform: rotate(-90deg) -} - -.reactionEmojiDetailsDivider_d5cd2d { - border-bottom: 1px solid var(--border-subtle); - margin: 16px 0 -} - -.inventoryCtaButton_d5cd2d { - margin: 8px auto 0 -} - -.ctaDescription_d5cd2d { - align-items: center; - display: flex; - flex-direction: row; - margin-top: 8px -} - -.nitroWheel_d5cd2d { - margin-inline-end:8px} - -.betaTag_d5cd2d { - inset-inline-end: -8px; - position: absolute; - top: -4px -} - -.effect__68185 { - bottom: 0; - pointer-events: none; - position: absolute; - width: var(--custom-voice-channel-effect-voice-channel-effect-animation-size); - z-index: 1001 -} - -.effects__58042 { - border-radius: var(--radius-sm); - bottom: 0; - inset-inline: 0; - overflow: hidden; - pointer-events: none; - position: absolute; - top: 0 -} - -.soundButtonWrapper__9be63 { - background: var(--background-base-lower); - list-style: none -} - -.soundButton__9be63,.soundButtonWrapper__9be63 { - border-radius: var(--radius-sm); - position: relative -} - -.soundButton__9be63 { - align-items: center; - background: var(--background-mod-subtle); - border: 1px solid transparent; - box-sizing: border-box; - display: flex; - flex-direction: row; - font-weight: var(--font-weight-semibold); - height: 40px; - justify-content: center; - overflow: hidden; - text-align: center; - width: 148px -} - -.soundButton__9be63.focused__9be63,.soundButton__9be63:active:not(:focus-within),.soundButton__9be63:hover { - border-color: var(--border-subtle) -} - -.high-contrast-mode .soundButton__9be63 { - border-color: var(--border-muted) -} - -.animated__9be63:active:not(:focus-within) { - box-shadow: none; - outline-width: 1px -} - -.full-motion .animated__9be63:active:not(:focus-within) { - transform: translateY(2px) -} - -.addButton__9be63 { - align-items: center; - display: flex; - margin: 3px -} - -.plusSign__9be63 { - color: var(--text-muted); - height: 18px; - width: 18px -} - -.addButton__9be63>* { - margin-inline-end:4px} - -.hoverActiveBackground__9be63.focused__9be63,.hoverActiveBackground__9be63:focus-within { - background: var(--background-mod-normal) -} - -.hoverActiveBackground__9be63:hover { - background: var(--background-mod-strong) -} - -.soundButton__9be63.focused__9be63 .buttonOverlay__9be63,.soundButton__9be63:focus-within .buttonOverlay__9be63,.soundButton__9be63:hover .buttonOverlay__9be63 { - opacity: 1; - visibility: visible -} - -.soundButton__9be63.focused__9be63 .addButtonOverlay__9be63,.soundButton__9be63:focus-within .addButtonOverlay__9be63,.soundButton__9be63:hover .addButtonOverlay__9be63 { - opacity: 1; - visibility: visible -} - -.soundInfo__9be63 { - align-items: center; - display: flex; - flex: 1; - flex-direction: row; - gap: 8px; - justify-content: center; - overflow: hidden; - padding: 8px -} - -.soundName__9be63 { - color: var(--text-strong); - display: -webkit-box; - flex-grow: 0; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - word-break: break-word -} - -.soundName__9be63.hasEmoji__9be63 { - text-align: start -} - -.emoji__9be63 { - flex-shrink: 0; - height: 20px; - width: 20px -} - -.soundButtonInteractive__9be63 { - cursor: pointer -} - -.soundButtonInteractive__9be63.playing__9be63 { - border-color: var(--status-positive-background) -} - -.buttonDisabledSecondaryActionsEnabled__9be63 { - cursor: default -} - -.buttonDisabled__9be63 { - pointer-events: none -} - -.premiumDisabled__9be63 { - opacity: .5 -} - -.absoluteFill__9be63 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.addButtonOverlay__9be63,.buttonOverlay__9be63 { - opacity: 0; - transition: opacity .2s ease; - visibility: hidden -} - -.buttonOverlayBackground__9be63 { - background: var(--background-surface-highest); - opacity: .8 -} - -.buttonOverlayActions__9be63 { - align-items: center; - display: flex; - justify-content: space-between; - padding: 0 8px -} - -.secondaryButton__9be63 { - color: var(--interactive-text-default); - display: flex -} - -.secondaryButton__9be63:focus,.secondaryButton__9be63:hover { - color: var(--interactive-text-hover); - cursor: pointer -} - -.secondaryIcon__9be63 { - height: 16px; - width: 16px -} - -.favoriteIconFavorite__9be63 { - color: var(--text-feedback-warning) -} - -.primaryIcon__9be63 { - color: var(--interactive-text-active); - flex: 1; - height: 20px; - width: 20px -} - -.lockIcon__9be63,.primaryIcon__9be63 { - pointer-events: none -} - -.primaryIconTopLevel__9be63 { - position: absolute; - z-index: 9999 -} - -.unavailableTooltip__9be63 { - align-items: center; - display: flex; - height: 40px; - position: absolute; - top: 0; - inset-inline: 0; - inset-inline-end: 0; - justify-content: space-between; - padding: 0 8px -} - -.unavailableTooltip__9be63:hover .unavailableTooltipActions__9be63 { - opacity: 1; - visibility: visible -} - -.unavailableTooltipActions__9be63 { - align-items: center; - display: flex; - justify-content: space-between; - opacity: 0; - pointer-events: auto; - transition: opacity .2s ease; - visibility: hidden; - width: 100% -} - -.icon__14ad3 { - height: 16px; - width: 16px -} - -.sliderContainer__14ad3 { - background-color: var(--background-surface-high); - border-radius: var(--radius-sm); - display: flex; - flex-direction: column; - min-width: 188px; - padding: var(--space-12) -} - -.slider__14ad3 { - margin-bottom: calc(var(--space-8)*-1) -} - -.betaBadge__14ad3 { - inset-inline-start: var(--space-8); - position: absolute; - top: -8px -} - -.infoContainer__14ad3 { - border-radius: var(--radius-sm); - max-width: 300px -} - -.infoTooltip__14ad3 { - overflow: hidden -} - -.infoNitroContainer__14ad3 { - background-color: var(--background-surface-high); - position: relative -} - -.infoNitroBackground__14ad3 { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color); - inset: 0; - opacity: .64; - position: absolute; - z-index: 1 -} - -.infoNitroTextContainer__14ad3 { - align-items: center; - display: flex; - flex-direction: row; - padding: var(--space-8); - position: relative; - z-index: 2 -} - -.infoNitroIcon__14ad3 { - margin-inline-end:var(--space-4)} - -.infoNitroText__14ad3 { - flex: 1; - margin-inline-end:var(--space-16)} - -.infoNitroBadge__14ad3 { - margin-inline-start: 0 auto -} - -.infoSoundContainer__14ad3 { - background-color: var(--background-surface-high); - display: flex; - flex-direction: column; - gap: var(--space-4); - padding: var(--space-12) -} - -.infoSoundInnerContainer__14ad3 { - align-items: center; - display: flex; - flex-direction: row -} - -.infoSoundIcon__14ad3 { - margin-inline-end:var(--space-4)} - -.infoExpandedSoundContainer__14ad3 { - align-items: center; - background-color: var(--background-base-lower); - display: flex; - flex-direction: row; - gap: var(--space-16); - padding: var(--space-16) -} - -.infoExpandedGuildContainer__14ad3 { - align-items: start; - background-color: var(--background-mod-normal); - display: flex; - flex-direction: column; - gap: var(--space-8); - padding: var(--space-16) -} - -.infoExpandedGuildInfo__14ad3 { - width: 100% -} - -.infoExpandedGuildTitle__14ad3 { - align-self: flex-start -} - -.infoExpandedGuildInfoContainer__14ad3 { - align-items: center; - align-self: stretch; - display: flex; - flex-direction: row; - gap: var(--space-8) -} - -.jumboContainer__5cc6a { - margin: 2px; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.jumboButton__5cc6a,.jumboContainer__5cc6a { - display: inline-flex -} - -.inlineButton__5cc6a { - background: var(--card-background-default); - border: 1px solid var(--border-strong); - box-shadow: inset 0 -4px 0 0 rgba(0,0,0,.12),0 2px 0 0 rgba(0,0,0,.2); - display: inline-block -} - -.full-motion .inlineButton__5cc6a { - transition: box-shadow .1s ease-in-out,transform .2s ease -} - -.inlineButton__5cc6a:active,.inlineButton__5cc6a:hover { - border: 1px solid hsla(0,0%,100%,.5) -} - -.inlineButton__5cc6a:active { - background: var(--card-primary-pressed-bg); - box-shadow: none -} - -.full-motion .inlineButton__5cc6a:active { - transform: translateY(2px) -} - -.inlineButton__5cc6a.playing__5cc6a { - border: 1px solid var(--control-connected-border-default) -} - -.inlineContainer__5cc6a { - align-items: center; - border-radius: var(--radius-sm); - box-sizing: border-box; - cursor: pointer; - line-height: 1.5em; - margin: 2px 0; - padding: 0 var(--space-4); - position: relative; - text-align: center; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.inlineTextArea__5cc6a { - margin: 2px var(--space-4) -} - -.soundmojiEmoji__5cc6a { - line-height: 1.5em; - margin-bottom: 1.5px; - width: 1em!important -} - -.unknownSound__5cc6a { - height: 1em; - position: relative; - top: 2px; - width: 1em -} - -.tooltip__5cc6a { - border-radius: var(--radius-sm) -} - -.tooltipContainer__5cc6a { - padding: 0 -} - -.emojiContainer__75abc { - display: inline-block -} - -.emojiContainerClickable__75abc { - cursor: pointer -} - -.emojiJumbo__75abc { - vertical-align: middle -} - -.hljs-ansi-control-sequence { - display: none -} - -.hljs-ansi-style-bold { - font-weight: var(--font-weight-bold) -} - -.hljs-ansi-style-underline { - text-decoration: underline -} - -.hljs-ansi-foreground-black { - color: var(--text-muted) -} - -.hljs-ansi-foreground-black .hljs-ansi-background-black,.hljs-ansi-foreground-black .hljs-ansi-background-blue,.hljs-ansi-foreground-black .hljs-ansi-background-cyan,.hljs-ansi-foreground-black .hljs-ansi-background-green,.hljs-ansi-foreground-black .hljs-ansi-background-magenta,.hljs-ansi-foreground-black .hljs-ansi-background-red,.hljs-ansi-foreground-black .hljs-ansi-background-white,.hljs-ansi-foreground-black .hljs-ansi-background-yellow { - color: #073642 -} - -.hljs-ansi-foreground-red { - color: #dc322f -} - -.hljs-ansi-foreground-green { - color: #859900 -} - -.hljs-ansi-foreground-yellow { - color: #b58900 -} - -.hljs-ansi-foreground-blue { - color: #268bd2 -} - -.hljs-ansi-foreground-magenta { - color: #d33682 -} - -.hljs-ansi-foreground-cyan { - color: #2aa198 -} - -.hljs-ansi-foreground-white { - color: var(--interactive-text-active) -} - -.hljs-ansi-foreground-white .hljs-ansi-background-black,.hljs-ansi-foreground-white .hljs-ansi-background-blue,.hljs-ansi-foreground-white .hljs-ansi-background-cyan,.hljs-ansi-foreground-white .hljs-ansi-background-green,.hljs-ansi-foreground-white .hljs-ansi-background-magenta,.hljs-ansi-foreground-white .hljs-ansi-background-red,.hljs-ansi-foreground-white .hljs-ansi-background-white,.hljs-ansi-foreground-white .hljs-ansi-background-yellow { - color: #eee8d5 -} - -.hljs-ansi-background-black { - background-color: #002b36 -} - -.hljs-ansi-background-red { - background-color: #cb4b16 -} - -.hljs-ansi-background-green { - background-color: #586e75 -} - -.hljs-ansi-background-yellow { - background-color: #657b83 -} - -.hljs-ansi-background-blue { - background-color: #839496 -} - -.hljs-ansi-background-magenta { - background-color: #6c71c4 -} - -.hljs-ansi-background-cyan { - background-color: #93a1a1 -} - -.hljs-ansi-background-white { - background-color: #fdf6e3 -} - -@use postcss-pxtorem;.markup__75297 { - font-size: 1rem; - line-height: var(--chat-markup-line-height); - white-space: break-spaces; - word-wrap: break-word; - color: var(--text-default); - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.markup__75297 a { - color: var(--text-link); - cursor: pointer; - -webkit-text-decoration: var(--link-decoration); - text-decoration: var(--link-decoration); - word-break: break-word -} - -.markup__75297 a:hover { - text-decoration: underline -} - -.markup__75297 strong { - font-weight: var(--font-weight-bold) -} - -.markup__75297 em { - font-style: italic -} - -.markup__75297 pre { - background-clip: border-box; - border-radius: 4px; - font-family: var(--font-code); - font-size: .75rem; - line-height: 1rem; - margin-top: 6px; - padding: 0; - white-space: pre-wrap -} - -.markup__75297 blockquote,.markup__75297 pre { - box-sizing: border-box; - max-width: 90% -} - -.markup__75297 blockquote { - color: var(--text-subtle); - text-indent: 0 -} - -.markup__75297 blockquote pre { - max-width: 100% -} - -.markup__75297 small:not(.inlineFormat__75297) { - color: var(--text-subtle); - display: block; - font-size: .875rem; - line-height: 1.20313rem -} - -.markup__75297 small.inlineFormat__75297:after { - content: " " -} - -.markup__75297 code { - background: var(--background-code); - border: 1px solid var(--border-normal); - font-size: .875rem; - line-height: 1.125rem; - text-indent: 0; - white-space: pre-wrap -} - -.markup__75297 .no-webkit-scrollbar code { - scrollbar-color: var(--background-base-lowest) var(--background-base-lower); - scrollbar-width: thin -} - -.markup__75297 code.inline { - border-radius: 4px; - font-family: var(--font-code); - font-size: 85%; - height: auto; - margin: -.2em 0; - padding: 0 .2em; - text-indent: 0; - white-space: pre-wrap; - width: auto -} - -.markup__75297 .codeContainer__75297 { - max-width: 50vw; - position: relative -} - -.markup__75297 .codeActions__75297 { - display: none; - inset-inline-end: 4px; - position: absolute; - top: 8px -} - -.markup__75297 .codeContainer__75297:hover .codeActions__75297 { - display: block -} - -.markup__75297 .inlineFormat__75297 li,.markup__75297.inlineFormat__75297 li { - display: inline; - margin: 0; - padding-inline-end:8px} - -.markup__75297 .inlineFormat__75297 li:before,.markup__75297.inlineFormat__75297 li:before { - content: "•"; - padding-inline-end:4px} - -.markup__75297 .inlineFormat__75297 li li:before,.markup__75297.inlineFormat__75297 li li:before { - content: "○"; - font-size: .625rem; - line-height: 1rem; - padding: 0 4px 4px -} - -.markup__75297 li { - margin-bottom: 4px; - white-space: break-spaces -} - -.markup__75297 ol,.markup__75297 ul { - list-style-position: outside; - margin-block:4px 0;margin-inline:16px 0} - -.markup__75297 ol.inlineFormat__75297,.markup__75297 ul.inlineFormat__75297 { - display: inline; - margin: 0; - padding-block:0;padding-inline:0 8px} - -.markup__75297 ul { - list-style-type: disc -} - -.markup__75297 ol { - list-style-type: decimal; - margin-inline-start:calc(.4em + var(--totalCharacters)*.6em)} - -.markup__75297 ol ul,.markup__75297 ul ul { - list-style-type: circle; - margin-bottom: 0 -} - -.markup__75297 ol ol,.markup__75297 ul ol { - margin-bottom: 0 -} - -.markup__75297 h1,.markup__75297 h2,.markup__75297 h3,.markup__75297 h4,.markup__75297 h5,.markup__75297 h6 { - color: var(--text-strong); - font-family: var(--font-display); - font-weight: var(--font-weight-bold); - line-height: 1.375em; - margin: 16px 0 4px -} - -.markup__75297 h1.inlineFormat__75297,.markup__75297 h2.inlineFormat__75297,.markup__75297 h3.inlineFormat__75297,.markup__75297 h4.inlineFormat__75297,.markup__75297 h5.inlineFormat__75297,.markup__75297 h6.inlineFormat__75297 { - color: inherit; - display: inline; - font-size: inherit; - font-weight: var(--font-weight-semibold); - line-height: 1.25em; - margin: 0!important; - padding-inline-end:16px;white-space: normal -} - -.markup__75297 h1 { - font-size: 1.5rem; - margin: 16px 0 8px -} - -.markup__75297 h2 { - font-size: 1.25rem; - margin: 16px 0 8px -} - -.markup__75297 h3 { - font-size: 1rem; - margin: 16px 0 8px -} - -.markup__75297 h1:first-child,.markup__75297 h2:first-child { - margin-top: 8px -} - -.markup__75297 h3:first-child,.markup__75297 h4:first-child,.markup__75297 h5:first-child,.markup__75297 h6:first-child { - margin-top: 4px -} - -.custom-theme-background .markup__75297 code { - background: color-mix(in oklab,var(--background-code) 95%,var(--white)) -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .markup__75297 { - font-weight:var(--font-weight-medium) - } -} - -.blockquoteContainer__75297 { - display: flex; - margin-bottom: var(--space-4); - margin-top: var(--space-4) -} - -.blockquoteContainer__75297 .blockquoteDivider__75297 { - border-radius: 4px; - min-width: 4px; - width: 4px -} - -.blockquoteContainer__75297 blockquote { - box-sizing: border-box; - padding-block:0;padding-inline:12px 8px;text-indent: 0 -} - -.blockquoteDivider__75297 { - background-color: var(--spine-default) -} - -.slateBlockquoteContainer__75297 { - margin-bottom: 0; - margin-top: 0; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.slateBlockquoteContainer__75297 .blockquoteDivider__75297,.slateBlockquoteContainer__75297 blockquote { - border-radius: 0; - margin: 0 -} - -.slateBlockquoteContainer__75297 blockquote { - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.background-opacity-low .markup__75297,.background-opacity-medium .markup__75297 { - font-weight: var(--font-weight-normal) -} - -.background-opacity-low .markup__75297 { - color: var(--white); - text-shadow: 0 0 1px var(--primary-700),1px 1px 0 var(--primary-700) -} - -.background-opacity-low .markup__75297 a { - color: var(--text-link); - text-shadow: 0 0 1px hsl(var(--primary-700-hsl)/.7),1px 1px 0 hsl(var(--primary-700-hsl)/.7) -} - -.background-opacity-medium .markup__75297 { - color: var(--primary-100); - text-shadow: 0 0 1px var(--primary-600),1px 1px 0 var(--primary-600) -} - -.background-opacity-medium .markup__75297 a { - color: var(--text-link); - text-shadow: 0 0 1px hsl(var(--primary-700-hsl)/.8),1px 1px 0 hsl(var(--primary-700-hsl)/.8) -} - -.background-opacity-high .markup__75297 { - color: var(--primary-100); - text-shadow: 0 0 1px var(--primary-600),1px 1px 0 var(--primary-600) -} - -.background-opacity-low pre,.background-opacity-medium pre { - border-color: hsl(var(--primary-500-hsl)/.2) -} - -.background-opacity-high pre,.background-opacity-low pre,.background-opacity-medium pre { - background: hsl(var(--primary-630-hsl)/.3); - border-color: hsl(var(--primary-700-hsl)/.1) -} - -.background-opacity-high code,.background-opacity-low code,.background-opacity-medium code { - background-color: transparent -} - -.background-opacity-high code.inline,.background-opacity-low code.inline,.background-opacity-medium code.inline { - background-color: hsl(var(--primary-630-hsl)/.3) -} - -.background-opacity-high .mention,.background-opacity-low .mention,.background-opacity-medium .mention { - text-shadow: 0 1px 1px var(--brand-600),0 1px 0 var(--brand-600) -} - -.overlay-unlocked code,.overlay-unlocked code.inline { - background: var(--background-base-lower); - border-color: var(--background-base-lowest) -} - -.roleMention__75297 { - filter: saturate(var(--saturation-factor,1)); - font-weight: var(--font-weight-medium) -} - -.rolePopout__75297 { - background-color: var(--background-surface-higher); - border: 1px solid var(--border-subtle); - border-radius: 8px; - box-shadow: var(--shadow-high); - display: flex; - max-height: calc(100vh - 20px); - width: 240px -} - -.roleHeader__75297 { - height: 40px; - padding-block:12px 0;padding-inline:16px 8px} - -.roleScroller__75297 { - margin-bottom: 8px; - margin-top: 4px -} - -.timestamp__75297 { - background-color: var(--background-mod-normal); - border-radius: 3px; - padding: 0 2px -} - -.timestampTooltip__75297 { - max-width: unset -} - -.enable-forced-colors .blockquoteDivider__75297 { - background-color: CanvasText -} - -.enable-forced-colors code.inline { - position: relative -} - -.enable-forced-colors code.inline:after,.enable-forced-colors code.inline:before { - border: 1px solid CanvasText; - content: ""; - display: inline-block; - height: 1em; - margin-bottom: -3px; - width: 2px -} - -.enable-forced-colors code.inline:before { - border-right-width: 0 -} - -.enable-forced-colors code.inline:after { - border-left-width: 0 -} - -.enable-forced-colors .roleMention__75297,.enable-forced-colors .mention { - background-color: ButtonFace; - color: ButtonText; - text-decoration: underline -} - -.icon_a9c2d1 { - margin-inline-end:.1rem} - -.svgContentRight__7b484 { - fill: var(--background-base-low) -} - -.svgContentLeft__7b484 { - fill: var(--background-base-lowest) -} - -.svgContentLines__7b484,.svgDots__7b484 { - fill: var(--background-mod-strong) -} - -.svgTag__7b484 { - fill: var(--text-subtle) -} - -.wrapper__124c8 { - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-sizing: border-box; - display: flex; - flex-direction: column; - gap: 16px; - max-width: 432px; - min-width: 160px; - padding: 16px; - text-indent: 0 -} - -.titleRegion__124c8 { - align-items: center; - display: flex; - justify-content: flex-start; - text-transform: uppercase -} - -.infoIcon__124c8,.title__124c8 { - display: block; - flex: 0 0 auto -} - -.infoIcon__124c8 { - height: 16px; - margin-inline-start:4px;width: 16px -} - -.copyLink__124c8 { - align-items: center; - cursor: pointer; - display: flex; - font-weight: var(--font-weight-semibold); - justify-content: flex-start; - margin-inline-start:16px;padding-inline-end:8px;position: relative -} - -.copyLink__124c8:before { - content: ""; - display: block; - height: 12px; - inset-inline-start: -8px; - margin-inline-end:8px;pointer-events: none; - position: absolute; - top: 2px; - width: 1px -} - -.copyLink__124c8.copied__124c8 { - cursor: default -} - -.copyLinkIcon__124c8 { - display: block; - height: 16px; - margin-inline-end:4px;width: 16px -} - -.content__124c8 { - align-items: center; - display: flex; - flex: 0 0 auto; - flex-flow: row wrap; - gap: 16px -} - -.infoLink__124c8.infoLink__124c8 { - color: var(--text-default); - cursor: pointer -} - -.buildInfo__124c8 { - flex: 1000 0 auto; - height: 40px; - overflow: hidden -} - -.button__124c8 { - flex: 1 0 auto -} - -.buttonSize__124c8 { - height: 40px; - width: 80px -} - -.subHead__124c8 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.icon__124c8 { - display: block; - flex: 0 0 auto -} - -.buildDetails__124c8 { - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.barLoader__124c8 { - border-radius: 8px; - flex: 0 0 auto; - height: 16px; - width: 84% -} - -.barTitle__124c8 { - margin-bottom: 4px; - margin-top: 2px; - width: 45% -} - -.buttonLoader__124c8 { - border-radius: 4px; - height: 40px; - width: 80px -} - -.theme-light .disabledButtonOverride__124c8 { - background-color: var(--primary-300)!important -} - -.copyLink__124c8:before { - background-color: var(--background-base-low) -} - -.buildDetails__124c8,.copyLink__124c8:hover,.infoLink__124c8:hover { - color: var(--text-strong) -} - -.copyLink__124c8.copied__124c8,.copyLink__124c8.copied__124c8:hover { - color: var(--text-feedback-positive) -} - -.barLoader__124c8,.buttonLoader__124c8 { - background-color: var(--background-base-low) -} - -.root__5c1cf { - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-sizing: border-box; - display: flex; - flex-direction: column; - gap: var(--space-8); - margin-top: var(--space-8); - max-width: 360px; - min-width: 160px; - padding: var(--space-12); - width: auto -} - -.root__5c1cf div[role=button] { - margin-inline-start:0} - -.header__5c1cf { - align-items: center; - display: grid -} - -.experimentOverride__5c1cf { - flex: 1 -} - -.copyLinkButton__5c1cf { - color: var(--icon-subtle); - cursor: pointer -} - -.copyLinkButton__5c1cf:hover { - color: var(--icon-strong) -} - -.gameArtHero__15328 { - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - height: 106px; - -webkit-mask-image: linear-gradient(183deg,#000,#000 50px,transparent 325px); - mask-image: linear-gradient(183deg,#000,#000 50px,transparent 325px); - position: absolute; - top: 0; - inset-inline: 0; - pointer-events: none; - width: 100% -} - -.iconContainer__15328 { - background: var(--background-surface-high); - border-radius: 14px; - height: 80px; - margin-inline-start:16px;margin-top: 64px; - padding: 6px; - width: 80px; - z-index: 10 -} - -.headingContainer__15328 { - padding: 8px 16px 0 -} - -.bodyContainer__15328 { - gap: 12px; - padding: 12px 16px 16px -} - -.bodyContainer__15328,.summaryContainer__15328 { - display: flex; - flex-direction: column -} - -.summaryContainer__15328 { - gap: 8px -} - -.clickable__15328 { - cursor: pointer -} - -.gameIcon__15328 { - background: var(--background-surface-high); - border-radius: 8px -} - -.gameIconFallback__15328 { - border-radius: 8px; - padding: 24px -} - -.container__15328 { - align-items: flex-start; - background: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: 16px; - flex-direction: column; - overflow: hidden; - position: relative -} - -.container__15328,.error__15328 { - display: flex -} - -.actionButtonsContainer__15328 { - display: flex; - flex-direction: row; - gap: 8px; - inset-inline-end: 16px; - position: absolute; - top: 16px; - z-index: 10 -} - -.actionButton__15328 { - align-items: center; - background: rgba(0,0,0,.5); - border: 1px solid var(--border-subtle); - border-radius: 60px; - box-sizing: border-box; - cursor: pointer; - display: flex; - height: 32px; - justify-content: center; - padding: 6px; - width: 32px -} - -.container_be1af1 { - border-radius: 16px; - box-shadow: var(--elevation-high); - display: flex; - flex-direction: column; - overflow: hidden; - width: 300px -} - -.spinnerContainer_be1af1 { - background: var(--background-surface-highest); - padding: 16px -} - -.icon__1bb46 { - display: inline-block; - filter: saturate(var(--saturation-factor,1)); - margin-inline-end:3px;margin-bottom: .2rem; - vertical-align: middle; - -webkit-user-drag: none; - background-size: 100% -} - -.imageIcon__1bb46 { - height: 1em; - width: 1em -} - -.name__1bb46 { - overflow: hidden; - text-overflow: ellipsis -} - -.hljs { - border-radius: 4px; - display: block; - overflow-x: auto; - padding: .5em; - -webkit-text-size-adjust: none; - -moz-text-size-adjust: none; - text-size-adjust: none; - background: var(--background-base-lower); - color: var(--text-default) -} - -.theme-dark .hljs-doctag,.theme-dark .hljs-keyword,.theme-dark .hljs-meta .hljs-keyword,.theme-dark .hljs-template-tag,.theme-dark .hljs-template-variable,.theme-dark .hljs-type,.theme-dark .hljs-variable.language_ { - color: #ff7b72 -} - -.theme-dark .hljs-title,.theme-dark .hljs-title.class_,.theme-dark .hljs-title.class_.inherited__,.theme-dark .hljs-title.function_ { - color: #d2a8ff -} - -.theme-dark .hljs-attr,.theme-dark .hljs-attribute,.theme-dark .hljs-literal,.theme-dark .hljs-meta,.theme-dark .hljs-number,.theme-dark .hljs-operator,.theme-dark .hljs-selector-attr,.theme-dark .hljs-selector-class,.theme-dark .hljs-selector-id,.theme-dark .hljs-variable { - color: #79c0ff -} - -.theme-dark .hljs-meta .hljs-string,.theme-dark .hljs-regexp,.theme-dark .hljs-string { - color: #a5d6ff -} - -.theme-dark .hljs-built_in,.theme-dark .hljs-symbol { - color: #ffa657 -} - -.theme-dark .hljs-code,.theme-dark .hljs-comment,.theme-dark .hljs-formula { - color: #8b949e -} - -.theme-dark .hljs-name,.theme-dark .hljs-quote,.theme-dark .hljs-selector-pseudo,.theme-dark .hljs-selector-tag { - color: #7ee787 -} - -.theme-dark .hljs-subst { - color: #c9d1d9 -} - -.theme-dark .hljs-section { - color: #1f6feb; - font-weight: 700 -} - -.theme-dark .hljs-bullet { - color: #f2cc60 -} - -.theme-dark .hljs-emphasis { - color: #c9d1d9; - font-style: italic -} - -.theme-dark .hljs-strong { - color: #c9d1d9; - font-weight: 700 -} - -.theme-dark .hljs-addition { - background-color: #033a16; - color: #aff5b4 -} - -.theme-dark .hljs-deletion { - background-color: #67060c; - color: #ffdcd7 -} - -.theme-light .hljs-doctag,.theme-light .hljs-keyword,.theme-light .hljs-meta .hljs-keyword,.theme-light .hljs-template-tag,.theme-light .hljs-template-variable,.theme-light .hljs-type,.theme-light .hljs-variable.language_ { - color: #d73a49 -} - -.theme-light .hljs-title,.theme-light .hljs-title.class_,.theme-light .hljs-title.class_.inherited__,.theme-light .hljs-title.function_ { - color: #6f42c1 -} - -.theme-light .hljs-attr,.theme-light .hljs-attribute,.theme-light .hljs-literal,.theme-light .hljs-meta,.theme-light .hljs-number,.theme-light .hljs-operator,.theme-light .hljs-selector-attr,.theme-light .hljs-selector-class,.theme-light .hljs-selector-id,.theme-light .hljs-variable { - color: #005cc5 -} - -.theme-light .hljs-meta .hljs-string,.theme-light .hljs-regexp,.theme-light .hljs-string { - color: #032f62 -} - -.theme-light .hljs-built_in,.theme-light .hljs-symbol { - color: #e36209 -} - -.theme-light .hljs-code,.theme-light .hljs-comment,.theme-light .hljs-formula { - color: #6a737d -} - -.theme-light .hljs-name,.theme-light .hljs-quote,.theme-light .hljs-selector-pseudo,.theme-light .hljs-selector-tag { - color: #22863a -} - -.theme-light .hljs-subst { - color: #24292e -} - -.theme-light .hljs-section { - color: #005cc5; - font-weight: 700 -} - -.theme-light .hljs-bullet { - color: #735c0f -} - -.theme-light .hljs-emphasis { - color: #24292e; - font-style: italic -} - -.theme-light .hljs-strong { - color: #24292e; - font-weight: 700 -} - -.theme-light .hljs-addition { - background-color: #f0fff4; - color: #22863a -} - -.theme-light .hljs-deletion { - background-color: #ffeef0; - color: #b31d28 -} - -.hljs_cc310a { - border-radius: 4px; - display: block; - overflow-x: auto; - padding: .5em; - -webkit-text-size-adjust: none; - -moz-text-size-adjust: none; - text-size-adjust: none; - color: var(--text-default) -} - -.hljs-doctag_cc310a,.hljs-keyword_cc310a,.hljs-meta_cc310a .hljs-keyword_cc310a,.hljs-template-tag_cc310a,.hljs-template-variable_cc310a,.hljs-type_cc310a,.hljs-variable_cc310a.language__cc310a { - color: var(--text-code-keyword) -} - -.hljs-title_cc310a,.hljs-title_cc310a.class__cc310a,.hljs-title_cc310a.class__cc310a.inherited___cc310a,.hljs-title_cc310a.function__cc310a { - color: var(--text-code-title) -} - -.hljs-attr_cc310a,.hljs-attribute_cc310a,.hljs-literal_cc310a,.hljs-meta_cc310a,.hljs-number_cc310a,.hljs-operator_cc310a,.hljs-selector-attr_cc310a,.hljs-selector-class_cc310a,.hljs-selector-id_cc310a,.hljs-variable_cc310a { - color: var(--text-code-variable) -} - -.hljs-meta_cc310a .hljs-string_cc310a,.hljs-regexp_cc310a,.hljs-string_cc310a { - color: var(--text-code-string) -} - -.hljs-built_in_cc310a,.hljs-symbol_cc310a { - color: var(--text-code-builtin) -} - -.hljs-code_cc310a,.hljs-comment_cc310a,.hljs-formula_cc310a { - color: var(--text-code-comment) -} - -.hljs-name_cc310a,.hljs-quote_cc310a,.hljs-selector-pseudo_cc310a,.hljs-selector-tag_cc310a { - color: var(--text-code-tag) -} - -.hljs-subst_cc310a { - color: var(--text-code) -} - -.hljs-section_cc310a { - color: var(--text-code-section); - font-weight: 700 -} - -.hljs-bullet_cc310a { - color: var(--text-code-bullet) -} - -.hljs-emphasis_cc310a { - color: var(--text-code); - font-style: italic -} - -.hljs-strong_cc310a { - color: var(--text-code); - font-weight: 700 -} - -.hljs-addition_cc310a { - background-color: var(--background-code-addition); - color: var(--text-code-addition) -} - -.hljs-deletion_cc310a { - background-color: var(--background-code-deletion); - color: var(--text-code-deletion) -} - -.botTag__82f07 { - align-items: center; - display: inline-flex; - flex-shrink: 0; - font-size: .625rem; - text-indent: 0; - text-transform: uppercase; - vertical-align: top -} - -.px__82f07.botTag__82f07 { - border-radius: 4px; - height: 16px; - padding: 0 4px -} - -.rem__82f07.botTag__82f07 { - border-radius: 4px; - height: .9375rem; - margin-top: .2em; - padding: 0 .275rem -} - -.rem__82f07.botTag__82f07.botTagOP__82f07 { - margin-top: .25em -} - -.botTagRegular__82f07 { - background: var(--background-brand); - color: var(--white) -} - -.botTagInvert__82f07 { - background: var(--white); - color: var(--brand-500) -} - -.botTagAI__82f07 { - background: var(--text-feedback-positive); - color: var(--white) -} - -.botTagNotStaffWarning__82f07 { - background: var(--notice-background-warning); - color: var(--notice-text-warning) -} - -.botTagVerified__82f07 { - display: inline-block -} - -.px__82f07 .botTagVerified__82f07 { - height: 16px; - margin-inline-start:-2px;width: 16px -} - -.rem__82f07 .botTagVerified__82f07 { - height: 1rem; - margin-inline-start:-.2rem;margin-top: -.02rem; - width: 1rem -} - -.botText__82f07 { - font-weight: var(--font-weight-semibold); - position: relative; - vertical-align: top -} - -.px__82f07 .botText__82f07 { - font-size: 12px; - line-height: 16px -} - -.rem__82f07 .botText__82f07 { - font-size: .8rem; - line-height: .9375rem -} - -.botTagOP__82f07 { - background-color: var(--brand-260); - border-radius: 8px; - color: var(--brand-560) -} - -.nameTag__05e81 { - align-items: center; - display: flex; - justify-content: flex-start; - line-height: 1.1; - overflow: hidden -} - -.username__05e81 { - display: block; - flex: 0 1 auto; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.bot__05e81 { - display: block; - flex: 0 0 auto; - margin-inline-start:1ch} - -.info_f4bc97 { - align-items: center; - display: flex; - flex-direction: row; - justify-content: flex-start; - overflow: hidden; - white-space: nowrap -} - -.info_f4bc97.withDisplayNameStyles_f4bc97 { - overflow: visible -} - -.infoSpacing_f4bc97 { - margin-inline-start:5px} - -:where(.avatar__75742) { - border-radius: var(--radius-round); - position: absolute; - z-index: var(--custom-user-profile-base-layer-z-index) -} - -.user-profile-popout :where(.avatar__75742) { - inset-inline-start: 16px; - top: 61px -} - -.custom-user-profile-theme.user-profile-popout :where(.avatar__75742) { - inset-inline-start: 12px; - top: 57px -} - -.user-profile-modal :where(.avatar__75742) { - inset-inline-start: 24px; - top: -68px -} - -.user-profile-modal-v2 :where(.avatar__75742) { - inset-inline-start: 32px; - top: 80px -} - -.user-profile-sidebar :where(.avatar__75742) { - inset-inline-start: 16px; - top: 72px -} - -:where(.avatar__75742).withReactReply__75742 { - position: static -} - -:where(.avatar__75742).hoisted__75742 { - pointer-events: none; - z-index: var(--custom-user-profile-hoist-z-index) -} - -.clickable__75742 { - cursor: pointer -} - -.overlay__75742:after { - background-color: var(--black); - border-radius: var(--radius-round); - content: ""; - height: 100%; - inset-inline-end: 0; - opacity: 0; - position: absolute; - top: 0; - width: 100% -} - -.full-motion .overlay__75742:after { - transition: opacity var(--custom-button-transition-duration) ease -} - -.clickable__75742:hover .overlay__75742:after { - opacity: .4 -} - -.focusRing__75742 { - border-radius: var(--radius-round) -} - -:where(.banner__68edb) { - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - border-radius: var(--radius-xs) var(--radius-xs) 0 0; - transition: background-color .1s; - width: 100% -} - -.user-profile-modal-v2 :where(.banner__68edb) { - border-radius: var(--radius-lg) var(--radius-lg) 0 0 -} - -:where(.banner__68edb):before { - border-bottom: 1px solid var(--border-muted); - bottom: 0; - content: ""; - position: absolute; - width: 100% -} - -:where(.banner__68edb):hover .gifTag__68edb { - opacity: 0 -} - -.gifTag__68edb { - inset-inline-start: 8px; - opacity: 1; - pointer-events: none; - position: absolute; - top: 8px; - z-index: 1 -} - -.gifTag__68edb .custom-user-profile-theme { - inset-inline-start: 16px; - top: 16px -} - -.mask__68edb { - contain: paint; - z-index: 0 -} - -.enable-forced-colors .banner__68edb { - forced-color-adjust: none -} - -:root { - --custom-user-profile-banner-height: 0; - --custom-user-profile-theme-padding: 0; - --custom-user-profile-base-layer-z-index: 0; - --custom-user-profile-bottom-layer-z-index: 1; - --custom-user-profile-middle-layer-z-index: 2; - --custom-user-profile-top-layer-z-index: 3; - --custom-user-profile-hoist-z-index: 4; - --custom-user-profile-toast-z-index: 5 -} - -:root .user-profile-popout { - --custom-user-profile-banner-height: 105px -} - -:root .user-profile-modal { - --custom-user-profile-banner-height: 210px -} - -:root .user-profile-modal-v2 { - --custom-user-profile-banner-height: 140px -} - -:root .user-profile-sidebar { - --custom-user-profile-banner-height: 120px -} - -:root .custom-user-profile-theme.user-profile-modal,:root .custom-user-profile-theme.user-profile-popout { - --custom-user-profile-theme-padding: 4px -} - -:root .custom-user-profile-theme.theme-dark { - --control-primary-background-default: var(--profile-gradient-button-color); - --control-primary-background-hover: color-mix(in srgb,var(--profile-gradient-button-color) 90%,var(--black)); - --control-primary-background-active: color-mix(in srgb,var(--profile-gradient-button-color) 80%,var(--black)); - --control-secondary-background-default: var(--opacity-white-8); - --control-secondary-background-hover: var(--opacity-white-16); - --control-secondary-background-active: var(--opacity-white-12) -} - -:root .custom-user-profile-theme.theme-light { - --control-primary-background-default: var(--profile-gradient-button-color); - --control-primary-background-hover: color-mix(in srgb,var(--profile-gradient-button-color) 90%,var(--white)); - --control-primary-background-active: color-mix(in srgb,var(--profile-gradient-button-color) 80%,var(--white)); - --control-secondary-background-default: var(--opacity-white-72); - --control-secondary-background-hover: var(--opacity-white-92); - --control-secondary-background-active: var(--opacity-white-84) -} - -:where(.outer_c0bea0) { - background: var(--background-surface-high); - display: flex; - flex-direction: column; - overflow: hidden; - position: relative -} - -:where(.outer_c0bea0).user-profile-popout { - border-radius: var(--radius-sm); - box-shadow: var(--shadow-border),var(--shadow-high); - max-height: calc(100vh - 20px); - width: 300px -} - -:where(.outer_c0bea0).user-profile-modal { - border-radius: var(--radius-sm); - height: 780px; - width: 600px -} - -:where(.outer_c0bea0).user-profile-modal-v2 { - background: var(--background-base-lower); - border-radius: var(--radius-lg); - box-sizing: border-box; - height: 800px; - max-width: 100%; - min-height: 500px; - outline: 1px solid var(--border-normal); - outline-offset: -1px; - width: 960px -} - -@media (max-height: 900px) { - :where(.outer_c0bea0).user-profile-modal-v2 { - height:calc(100vh - var(--space-24)*2) - } -} - -:where(.outer_c0bea0).user-profile-sidebar { - height: 100%; - overflow: initial; - width: 340px -} - -:where(.outer_c0bea0).custom-user-profile-theme { - background: linear-gradient(var(--profile-gradient-primary-color),var(--profile-gradient-secondary-color)); - padding: var(--custom-user-profile-theme-padding) -} - -:where(.outer_c0bea0).custom-user-profile-theme.user-profile-modal-v2 { - background: linear-gradient(var(--profile-gradient-modal-background-color),var(--profile-gradient-modal-background-color)) -} - -.inner_c0bea0 { - border-radius: var(--radius-xs); - display: flex; - flex-direction: column; - flex-grow: 1; - overflow: hidden -} - -.user-profile-popout .inner_c0bea0 { - gap: 8px; - padding-bottom: 4px -} - -.user-profile-modal .inner_c0bea0 { - height: 100% -} - -.user-profile-modal .inner_c0bea0:before { - border-radius: var(--radius-xs); - height: calc(100% - 8px); - width: calc(100% - 8px) -} - -.user-profile-modal-v2 .inner_c0bea0 { - border-radius: 0; - flex-direction: row; - gap: 0; - height: 100%; - padding-block:48px 0;padding-inline:48px 32px} - -@media (max-width: 960px) { - .user-profile-modal-v2 .inner_c0bea0 { - padding-block:48px 0; - padding-inline:16px 4px} -} - -.user-profile-sidebar .inner_c0bea0 { - border-radius: 0; - height: 100% -} - -.user-profile-sidebar .inner_c0bea0:before { - height: 100%; - width: 100% -} - -.custom-user-profile-theme .inner_c0bea0 { - --profile-gradient-start: color-mix(in oklab,var(--profile-gradient-overlay-color) 100%,var(--profile-gradient-primary-color)); - --profile-gradient-end: color-mix(in oklab,var(--profile-gradient-overlay-color) 100%,var(--profile-gradient-secondary-color)); - background: linear-gradient(var(--profile-gradient-start),var(--profile-gradient-start) var(--custom-user-profile-banner-height),var(--profile-gradient-end)) -} - -.custom-user-profile-theme.user-profile-modal-v2 .inner_c0bea0 { - background: linear-gradient(color-mix(in oklab,var(--profile-gradient-overlay-color) 80%,var(--profile-gradient-modal-background-color)),color-mix(in oklab,var(--profile-gradient-overlay-color) 20%,var(--profile-gradient-modal-background-color)) 300px) -} - -:where(.overlay_c0bea0) { - background: var(--user-profile-overlay-background); - border-radius: var(--radius-sm); - display: flex; - flex-direction: column; - overflow: hidden; - position: relative -} - -.theme-light :where(.overlay_c0bea0),:where(.overlay_c0bea0) .theme-dark { - border: 1px solid var(--border-muted) -} - -:where(.overlay_c0bea0) ::-webkit-scrollbar-thumb { - background-color: var(--user-profile-border) -} - -.no-webkit-scrollbar :where(.overlay_c0bea0) * { - scrollbar-color: var(--user-profile-border) var(--scrollbar-thin-track) -} - -.enable-forced-colors .user-profile-modal .inner_c0bea0,.enable-forced-colors .user-profile-modal.outer_c0bea0,.enable-forced-colors .user-profile-popout .inner_c0bea0,.enable-forced-colors .user-profile-popout.outer_c0bea0 { - border: 2px solid CanvasText -} - -.enable-forced-colors .outer_c0bea0,.enable-forced-colors .outer_c0bea0:before,.enable-forced-colors .overlay_c0bea0 { - background-color: Canvas -} - -.row__19fd1 { - align-items: center; - border-bottom: 1px solid var(--border-subtle); - border-color: var(--border-muted); - display: flex; - gap: var(--space-16); - justify-content: space-between; - margin: 0 var(--space-16); - padding: var(--space-16) 0; - position: relative -} - -.user-profile-modal-v2 .row__19fd1 { - border: none -} - -.user-profile-modal-v2 .row__19fd1:not(:last-child) { - margin-bottom: 1px -} - -.user-profile-modal-v2 .row__19fd1:not(:last-child):after { - background-color: var(--border-subtle); - bottom: 0; - content: ""; - height: 1px; - inset-inline: calc(var(--space-16) + var(--space-24)) calc(var(--space-16)*-1); - position: absolute -} - -.clickable__19fd1,.row__19fd1:last-child { - border-bottom: none -} - -.clickable__19fd1 { - background-color: var(--background-mod-subtle); - border-radius: var(--radius-sm); - cursor: pointer; - margin: 0; - padding: var(--space-16) -} - -.clickable__19fd1.disabled__19fd1 { - opacity: .5; - pointer-events: none -} - -.clickable__19fd1:hover { - background-color: var(--background-mod-strong) -} - -.noInset__19fd1 { - margin: 0; - padding: 0 -} - -.iconContainer__19fd1 { - display: flex; - justify-content: center -} - -.icon__19fd1,.iconContainer__19fd1 { - height: var(--space-24); - width: var(--space-24) -} - -.icon__19fd1 { - color: var(--icon-subtle) -} - -.textContainer__19fd1 { - display: flex; - flex-direction: column; - gap: 2px -} - -.button__19fd1 { - flex-shrink: 0; - margin-inline-start:auto;min-width: 75px; - padding: var(--space-8) var(--space-12) -} - -.number__19fd1 { - align-items: center; - background-color: var(--background-mod-subtle); - border-radius: var(--radius-round); - height: 32px; - justify-content: center; - min-width: 32px -} - -.number__19fd1,.tableRowGroup__19fd1 { - display: flex; - flex-direction: column -} - -.tableRowGroup__19fd1 { - flex-shrink: 0; - gap: var(--space-8) -} - -.tableRowGroup__19fd1 .content__19fd1 { - background-color: var(--background-base-lower); - border-radius: var(--radius-sm); - flex-grow: 1; - flex-shrink: 0; - overflow: hidden; - padding: 0 -} - -.content__19fd1 { - border: 1px solid var(--border-muted) -} - -.buttonContainer__19fd1 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-4) -} - -.buttonIcon__19fd1 { - height: 16px; - width: 16px -} - -.bannerButton_fb7f94 { - align-items: center; - background: var(--control-overlay-secondary-background-default); - border: 1px solid var(--opacity-white-8); - border-radius: 50%; - box-sizing: border-box; - color: var(--white); - cursor: pointer; - display: flex; - height: var(--custom-button-button-sm-height); - justify-content: center; - transition: background 50ms ease-in; - width: var(--custom-button-button-sm-height) -} - -.bannerButton_fb7f94:active,.bannerButton_fb7f94:hover { - background: var(--control-overlay-secondary-background-active); - transition: background .15s ease-out -} - -.bannerButton_fb7f94.disabled_fb7f94 { - cursor: normal; - opacity: .5 -} - -.wrapper_da5890 { - display: flex; - gap: 8px; - position: absolute; - transition: opacity .6s ease; - z-index: var(--custom-user-profile-top-layer-z-index) -} - -.wrapper_da5890:empty { - display: none -} - -.user-profile-popout .wrapper_da5890 { - inset-inline-end: 12px; - top: 12px -} - -.user-profile-modal .wrapper_da5890 { - inset-inline-end: 16px; - top: 12px -} - -.user-profile-sidebar .wrapper_da5890 { - inset-inline-end: 8px; - top: 8px -} - -.container_c9d15c { - align-items: center; - display: flex; - gap: 4px -} - -.container_c9d15c.textXs_c9d15c { - --custom-activity-status-icon-size: 12px; - --custom-activity-status-emoji-size: 14px -} - -.container_c9d15c.textSm_c9d15c { - --custom-activity-status-icon-size: 14px; - --custom-activity-status-emoji-size: 16px -} - -.container_c9d15c:empty { - display: none -} - -.activityContainer_c9d15c { - align-items: center; - display: flex; - flex-shrink: 0; - gap: 4px; - max-width: 100%; - overflow: hidden -} - -.activityContainer_c9d15c.iconOnly_c9d15c { - gap: 1px -} - -.activitiesTooltip_c9d15c,.tooltipText_c9d15c { - word-break: break-word -} - -.activitiesTooltip_c9d15c { - align-items: flex-start; - display: flex; - gap: 4px; - padding: 8px -} - -.activitiesTooltip_c9d15c .icon_c9d15c { - height: 14px; - position: relative; - top: 2px; - width: 14px -} - -.activitiesTooltip_c9d15c.hasMultipleActivities_c9d15c { - align-items: start; - display: grid; - grid-template-columns: 14px 1fr -} - -.icon_c9d15c { - flex-shrink: 0; - height: var(--custom-activity-status-icon-size); - width: var(--custom-activity-status-icon-size) -} - -.truncated_c9d15c { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.textWithIconContainer_c9d15c { - max-width: 100%; - min-width: var(--custom-activity-status-icon-size) -} - -.activityCounter_c9d15c { - flex-shrink: 0 -} - -.emoji_c9d15c { - flex: 0 0 auto; - height: var(--custom-activity-status-emoji-size); - margin-inline-end:0;width: var(--custom-activity-status-emoji-size) -} - -.dot_c9d15c { - flex-shrink: 0 -} - -.dot_c9d15c:first-child,.dot_c9d15c:last-child { - display: none -} - -.questsIcon_c9d15c { - flex-shrink: 0 -} - -.hangStatusIcon_c9d15c { - height: 14px; - width: 14px -} - -.customStatusLabel_c9d15c { - white-space: nowrap -} - -.wrapper_f7ecac { - align-items: center; - background-color: var(--background-surface-highest); - border-radius: 4px; - box-shadow: var(--shadow-border),var(--shadow-low); - box-sizing: border-box; - display: grid; - grid-auto-flow: column; - height: 32px; - justify-content: flex-start; - overflow: hidden; - position: relative; - transition: box-shadow .1s ease-out; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.full-motion .wrapper_f7ecac:hover { - box-shadow: var(--shadow-border),var(--shadow-medium) -} - -.button_f7ecac { - align-items: center; - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - flex: 0 0 auto; - height: 24px; - justify-content: center; - min-width: 24px; - padding: 4px; - position: relative -} - -.button_f7ecac:hover { - background-color: var(--interactive-background-hover); - color: var(--interactive-text-hover) -} - -.button_f7ecac:active { - background-color: var(--interactive-background-active); - padding-bottom: 3px; - padding-top: 5px -} - -.button_f7ecac.selected_f7ecac,.button_f7ecac:active { - color: var(--interactive-text-active) -} - -.button_f7ecac.selected_f7ecac { - background-color: var(--interactive-background-selected) -} - -.separator_f7ecac { - background-color: var(--border-subtle); - flex: 0 0 auto; - height: 100%; - margin: 0 4px; - width: 1px -} - -.button_f7ecac.disabled_f7ecac,.button_f7ecac.disabled_f7ecac:active,.button_f7ecac.disabled_f7ecac:hover { - background-color: transparent; - color: var(--interactive-text-default); - cursor: default; - opacity: .5; - padding: 4px -} - -.button_f7ecac.dangerous_f7ecac { - color: var(--control-critical-secondary-text-default) -} - -.button_f7ecac.dangerous_f7ecac:hover { - color: var(--control-critical-secondary-text-hover) -} - -.button_f7ecac.dangerous_f7ecac:active { - color: var(--control-critical-secondary-text-active) -} - -.enable-forced-colors .wrapper_f7ecac { - border: 1px solid CanvasText; - height: 36px -} - -.enable-forced-colors .button_f7ecac { - background-color: ButtonFace; - border: 1px solid ButtonFace; - color: ButtonText; - height: 26px; - min-width: 26px -} - -.enable-forced-colors .button_f7ecac.selected_f7ecac,.enable-forced-colors .button_f7ecac:active,.enable-forced-colors .button_f7ecac:hover { - background-color: HighlightText; - border-color: Highlight; - color: Highlight -} - -.popover__2d0ab { - background-color: var(--user-profile-toolbar-background); - border: 1px solid var(--user-profile-toolbar-border); - border-radius: 100px; - box-shadow: var(--shadow-low); - display: inline-flex; - gap: 2px; - height: unset; - opacity: 0; - padding: 2px; - pointer-events: none; - position: absolute; - top: -16px; - z-index: var(--custom-user-profile-hoist-z-index) -} - -.custom-user-profile-theme.theme-dark .popover__2d0ab { - -webkit-backdrop-filter: blur(36px); - backdrop-filter: blur(36px) -} - -.custom-user-profile-theme.theme-light .popover__2d0ab { - -webkit-backdrop-filter: blur(24px); - backdrop-filter: blur(24px) -} - -.popover__2d0ab.visible__2d0ab,.popover__2d0ab:hover { - opacity: 1 -} - -.keyboard-mode .popover__2d0ab:focus-within { - opacity: 1 -} - -.reduce-motion .popover__2d0ab { - pointer-events: all -} - -.popover__2d0ab>.tooltipContainer__2d0ab:first-of-type>.button__2d0ab { - border-radius: 100px 6px 6px 100px; - margin-inline-start:0;padding-inline-start:6px} - -.popover__2d0ab>.tooltipContainer__2d0ab:last-of-type>.button__2d0ab { - border-radius: 6px 100px 100px 6px; - margin-inline-end:0;padding-inline-end:6px} - -.tooltipContainer__2d0ab:empty { - display: none -} - -.activityPopover__2d0ab { - background-color: var(--user-profile-activity-toolbar-background); - inset-inline-end: 8px -} - -.custom-user-profile-theme .activityPopover__2d0ab { - -webkit-backdrop-filter: blur(16px); - backdrop-filter: blur(16px) -} - -.statusPopover__2d0ab { - inset-inline-end: min(8px,calc(50% - 29px)) -} - -.avatarPopover__2d0ab { - inset-inline: 0; - margin: auto; - width: -moz-fit-content; - width: fit-content -} - -:not(.keyboard-mode).full-motion .popover__2d0ab.visible__2d0ab,:not(.keyboard-mode).full-motion .popover__2d0ab:hover { - animation: fadeIn__2d0ab .15s ease forwards; - opacity: 0 -} - -:not(.keyboard-mode).full-motion .popover__2d0ab.visible__2d0ab.expandable__2d0ab,:not(.keyboard-mode).full-motion .popover__2d0ab:hover.expandable__2d0ab { - animation-delay: .45s -} - -@keyframes fadeIn__2d0ab { - 0% { - opacity: 0; - pointer-events: none; - top: -14px - } - - 99% { - pointer-events: none - } - - to { - opacity: 1; - pointer-events: all; - top: -16px - } -} - -.button__2d0ab { - height: unset; - min-width: unset; - padding: 4px -} - -.full-motion .button__2d0ab .icon__2d0ab { - transition: transform .4s cubic-bezier(.16,1,.3,1) -} - -.custom-user-profile-theme .button__2d0ab .icon__2d0ab { - color: var(--icon-strong) -} - -.button__2d0ab:hover { - background: var(--background-mod-normal) -} - -.button__2d0ab:hover .icon__2d0ab { - fill: var(--interactive-text-hover); - transform: scale(1.1) -} - -.button__2d0ab:active { - background: var(--background-mod-strong) -} - -.button__2d0ab:active .icon__2d0ab { - fill: var(--interactive-text-active); - transform: scale(1) -} - -.container__52c9d { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - z-index: 101 -} - -.background__52c9d { - background-color: var(--background-base-lower); - opacity: .98; - position: absolute -} - -.background__52c9d,.content__52c9d { - height: 100%; - width: 100% -} - -.content__52c9d { - align-items: center; - box-sizing: border-box; - display: flex; - flex-direction: column; - justify-content: center; - padding: 16px; - position: relative -} - -.closeButton__52c9d { - cursor: pointer; - inset-inline-end: 20px; - position: absolute; - top: 20px -} - -.closeButtonIcon__52c9d { - color: var(--interactive-text-default) -} - -.description__52c9d,.header__52c9d { - text-align: center -} - -.upsellImage__52c9d { - width: 124px -} - -.container__3782a { - align-items: center; - background-color: var(--background-base-low); - border-radius: var(--radius-sm); - bottom: 0; - display: flex; - flex-direction: column; - position: absolute; - z-index: 1001 -} - -.container__3782a,.content__3782a { - box-sizing: border-box; - width: 100% -} - -.content__3782a { - padding: var(--space-32) var(--space-32) var(--space-12) -} - -.closeButton__3782a { - color: var(--interactive-text-active); - inset-inline-end: 0; - margin: var(--space-16); - position: absolute; - top: 0 -} - -.contentContainer__3782a { - align-items: center; - display: flex; - flex-direction: column; - flex-grow: 2; - text-align: center -} - -.image__3782a { - width: 250px -} - -.title__3782a { - margin-bottom: var(--space-16); - margin-top: var(--space-32); - max-width: 250px -} - -.title__3782a.withBadge__3782a { - margin-top: var(--space-16) -} - -.body__3782a { - max-width: 380px -} - -.footer__3782a { - background-color: var(--background-base-low); - border-end-end-radius: inherit; - border-end-start-radius: inherit; - box-sizing: border-box; - margin-top: auto; - padding-bottom: var(--space-32); - padding-top: var(--space-8); - width: 100% -} - -.buttonContainer__3782a { - align-items: center; - display: flex; - flex-wrap: wrap; - gap: var(--space-8); - justify-content: space-between; - margin: 0 auto; - padding: 0 var(--space-32) -} - -.noParentContainer_d829e7 { - bottom: 0; - inset-inline-start: 0; - top: 0 -} - -.hasParentContainer_d829e7 { - bottom: 0; - inset-inline-start: 0; - top: -16px -} - -.hasTabParentContainer_d829e7 { - bottom: 0; - inset-inline-start: 0; - top: -62px -} - -.container_d829e7 { - align-items: center; - background-color: var(--background-base-lower); - border-radius: var(--radius-sm); - box-sizing: border-box; - display: flex; - flex-direction: column; - justify-content: center; - position: absolute; - z-index: 1001 -} - -.closeButton_d829e7 { - color: var(--interactive-text-active); - inset-inline-end: 0; - margin: 16px; - position: absolute; - top: 0 -} - -.contentContainer_d829e7 { - align-items: center; - display: flex; - flex-direction: column; - flex-grow: 2; - justify-content: center -} - -.image_d829e7 { - margin: 0 0 32px -} - -.title_d829e7 { - padding: 0 32px -} - -.titleNoSocialProof_d829e7 { - margin-bottom: 24px -} - -.body_d829e7 { - padding: 0 32px; - text-align: center -} - -.ctaContainer_d829e7 { - align-items: center; - background: var(--background-base-lowest); - border-radius: 0 0 var(--radius-sm) var(--radius-sm); - bottom: 0; - box-sizing: border-box; - display: flex; - inset-inline-start: 0; - justify-content: space-between; - padding: 16px; - width: 100% -} - -.secondaryCTA_d829e7 { - color: var(--premium-nitro-pink-text); - margin-inline-end:8px} - -.secondaryCTA_d829e7:hover { - text-decoration: underline -} - -.container_d829e7,.ctaContainer_d829e7 { - background-color: var(--modal-background) -} - -.banner__97f37 { - border-radius: 8px; - max-width: 304px; - width: 100% -} - -.banner__97f37.hasTrialOffer__97f37 { - margin-bottom: 16px; - margin-top: 16px; - max-height: 172px -} - -.wrapper__97f37 { - align-items: center; - background-color: var(--modal-background); - border-radius: 8px; - display: flex; - height: 100%; - inset-inline-start: 0; - justify-content: center; - position: absolute; - top: 0; - width: 100%; - z-index: 101 -} - -.closeButton__97f37 { - color: var(--interactive-text-default); - cursor: pointer; - inset-inline-end: 16px; - position: absolute; - top: 16px -} - -.contentFill__97f37 { - background: var( --gradients-nitro-classic-diagonal,linear-gradient(45deg,var(--premium-tier-1-blue) 0,var(--premium-tier-1-purple) 75.25%) ); - border-radius: 8px; - margin-top: 24px; - padding: 36px 84px -} - -.nitroWheel__97f37 { - color: var(--premium-tier-1-purple); - padding-inline-end:4px} - -.content__97f37 { - box-sizing: border-box; - color: var(--text-default); - flex-direction: column; - text-align: center -} - -.content__97f37,.header__97f37 { - align-items: center; - display: flex -} - -.header__97f37 { - justify-content: center; - margin: 16px 0 8px -} - -.headerText__97f37 { - margin-inline-start:4px} - -.subheaderText__97f37 { - color: var(--text-default); - max-width: 350px; - padding: 0 32px; - white-space: pre-line -} - -.subheaderText__97f37 a { - color: var(--text-default); - text-decoration: underline -} - -.ctaActionWrapper__97f37 { - align-items: center; - box-sizing: border-box; - display: flex; - justify-content: center; - margin-top: 24px; - padding: 0 48px; - width: 100% -} - -.listWrapper_affa7e { - overflow-x: hidden; - overflow-y: scroll; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.listHeight_affa7e { - visibility: hidden -} - -.listItems_affa7e { - contain: layout; - position: absolute -} - -.scroller_affa7e { - height: 100% -} - -.wrapper__4e6ce { - background-color: var(--background-base-lower); - border-radius: 0 0 0 8px; - display: grid; - grid-template-rows: 1fr auto; - inset-inline: 0; - bottom: 0; - overflow: hidden; - position: absolute; - width: 48px -} - -.high-contrast-mode .wrapper__4e6ce { - border-inline-end:1px solid var(--border-subtle);box-sizing: border-box -} - -.enable-forced-colors .wrapper__4e6ce { - border-inline-end:1px solid CanvasText} - -.guildIcon__0fa6d { - align-items: center; - background-color: var(--background-base-low); - color: var(--text-default); - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.mask__0fa6d { - display: block -} - -.guildAcronym__0fa6d { - font-size: 12px; - overflow: hidden; - text-overflow: ellipsis -} - -.categoryItemLockIconContainer__0fa6d,.guildIconWithoutImage__0fa6d { - background-color: var(--background-base-low) -} - -.categoryItemLockIconContainer__0fa6d { - border: 1px solid var(--background-base-lowest); - border-radius: 50%; - height: 16px; - inset-block: auto -1px; - inset-inline: auto -1px; - position: absolute; - width: 16px -} - -.categoryItemLockIcon__0fa6d { - color: var(--icon-strong); - inset: 0; - margin: auto; - position: absolute -} - -.categoryItem_b9ee0c { - cursor: pointer -} - -.categoryItemGuildCategory_b9ee0c,.categoryItemPackCategory_b9ee0c { - height: var(--custom-emoji-picker-constants-guild-category-icon-size); - margin-bottom: var(--custom-emoji-picker-constants-guild-category-icon-margin-verical); - position: relative; - width: var(--custom-emoji-picker-constants-guild-category-icon-size) -} - -.categoryItemGuildCategory_b9ee0c,.categoryItemPackCategory_b9ee0c { -} - -.categoryItemDefaultCategory_b9ee0c { - border-radius: 4px; - height: var(--custom-emoji-picker-constants-unicode-category-icon-size); - margin-bottom: var(--custom-emoji-picker-constants-unicode-category-icon-margin-vertical); - padding: var(--custom-emoji-picker-constants-unicode-category-icon-padding); - transition: background-color .1s ease-in-out; - width: var(--custom-emoji-picker-constants-unicode-category-icon-size) -} - -.categoryItemDefaultCategory_b9ee0c:hover { - background-color: var(--interactive-background-hover) -} - -.categoryItemDefaultCategory_b9ee0c:hover .categoryIcon_b9ee0c { - color: var(--interactive-text-hover) -} - -.categoryItemRecentEmoji_b9ee0c { - margin-bottom: var(--custom-emoji-picker-constants-guild-category-icon-margin-verical) -} - -.categoryItemDefaultCategorySelected_b9ee0c,.categoryItemDefaultCategorySelected_b9ee0c:hover { - background-color: var(--background-base-low) -} - -.categoryItemDefaultCategorySelected_b9ee0c .categoryIcon_b9ee0c,.categoryItemDefaultCategorySelected_b9ee0c:hover .categoryIcon_b9ee0c { - color: var(--interactive-text-active) -} - -.categoryIcon_b9ee0c { - color: var(--interactive-text-default) -} - -.guildCategorySeparator_b9ee0c { - border: none; - border-bottom: var(--custom-emoji-picker-constants-category-separator-size) solid var(--border-subtle); - margin: var(--custom-emoji-picker-constants-category-separator-margin-vertical) 0 -} - -.theme-light .categoryItemDefaultCategorySelected_b9ee0c .categoryIcon_b9ee0c,.theme-light .categoryItemDefaultCategorySelected_b9ee0c:hover .categoryIcon_b9ee0c { - color: var(--interactive-text-active) -} - -.unicodeShortcut_b9ee0c { - align-items: center; - background: inherit; - border-top: 1px solid var(--border-normal); - bottom: 0; - box-sizing: border-box; - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - height: var(--custom-emoji-picker-constants-unicode-category-shortcut-height); - inset-inline-start: 0; - justify-content: center; - opacity: 1; - position: absolute; - width: 100% -} - -.full-motion .unicodeShortcut_b9ee0c { - transition: opacity .1s,transform .1s -} - -.unicodeShortcut_b9ee0c:hover { - color: var(--interactive-text-hover) -} - -.unicodeShortcutInvisible_b9ee0c { - opacity: 0; - transform: translateY(var(--custom-emoji-picker-constants-unicode-category-shortcut-height)) -} - -.hideUnicodeShortcut_b9ee0c { - display: none -} - -.enable-forced-colors .unicodeShortcut_b9ee0c { - background-color: ButtonFace; - border-top: 1px solid CanvasText; - color: ButtonText; - height: calc(var(--custom-emoji-picker-constants-unicode-category-shortcut-height) - 1px) -} - -.enable-forced-colors .categoryItemDefaultCategory_b9ee0c,.enable-forced-colors .categoryItemGuildCategory_b9ee0c { - border: 1px solid Canvas; - border-radius: 4px; - transition: none -} - -.enable-forced-colors .categoryItemDefaultCategory_b9ee0c .categoryIcon_b9ee0c,.enable-forced-colors .categoryItemGuildCategory_b9ee0c .categoryIcon_b9ee0c { - color: ButtonText -} - -.enable-forced-colors .categoryItemDefaultCategory_b9ee0c:hover,.enable-forced-colors .categoryItemGuildCategory_b9ee0c:hover { - background: ButtonFace; - border-color: ButtonText -} - -.enable-forced-colors .categoryItemDefaultCategorySelected_b9ee0c,.enable-forced-colors .categoryItemDefaultCategorySelected_b9ee0c:hover { - background-color: HighlightText; - border-color: Highlight -} - -.enable-forced-colors .categoryItemDefaultCategorySelected_b9ee0c .categoryIcon_b9ee0c,.enable-forced-colors .categoryItemDefaultCategorySelected_b9ee0c:hover .categoryIcon_b9ee0c { - color: Highlight -} - -.guildEmojiSectionItems_b9ee0c { - position: relative -} - -.guildEmojiSectionHighlighted_b9ee0c:before { - content: ""; - position: absolute; - top: -4px; - inset-inline: -8px; - bottom: 0 -} - -.theme-dark .guildEmojiSectionHighlighted_b9ee0c:before { - background: linear-gradient(90deg,#503572,#673471 50%,#623e55) -} - -.theme-light .guildEmojiSectionHighlighted_b9ee0c:before { - background: linear-gradient(90deg,#d7c6ec,#e7c5ea 50%,#e3ccd9) -} - -.wrapper_ac2cfb { - background-color: var(--background-base-lowest); - border-radius: 4px; - color: var(--text-default); - display: flex; - font-size: 12px; - font-weight: var(--font-weight-medium); - margin: 8px 8px 0; - padding: 10px 8px 8px -} - -.icon_ac2cfb { - color: var(--icon-feedback-critical); - height: 16px; - min-width: var(--custom-emoji-picker-constants-emoji-size) -} - -.close_ac2cfb { - color: var(--interactive-text-default); - cursor: pointer; - height: 14px; - width: 14px -} - -.close_ac2cfb:hover { - color: var(--interactive-text-hover) -} - -.content_ac2cfb { - flex: 1 1 auto -} - -@value iconSize 20px;.visuallyHidden__0d242 { - border: 0; - clip: rect(1px,1px,1px,1px); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px -} - -.tooltip__0d242 { - max-width: 275px -} - -.tooltipContainer__0d242 { - justify-content: center -} - -.tooltipContainer__0d242,.tooltipHeadingContainer__0d242 { - align-items: center; - display: flex -} - -.nitroWheel__0d242 { - padding-inline-end:4px} - -input[type=checkbox]:focus-visible+.label__0d242 { - box-shadow: 0 0 8px var(--brand-500) -} - -.label__0d242 { - background: var(--background-base-lowest); - border-radius: var(--radius-lg); - color: var(--text-default); - cursor: pointer; - display: flex; - height: 20px; - overflow: hidden; - padding: 6px; - position: relative; - transition: background .25s ease-out; - width: 20px -} - -.labelChecked__0d242 { - background: var(--brand-500); - box-shadow: unset -} - -.icon__0d242 { - opacity: 1; - position: absolute; - transition: opacity .3s -} - -.emojiErrorItem__67954 { - align-items: center; - background-color: var(--background-base-lower); - border-radius: var(--radius-xs); - display: flex; - justify-content: center; - padding: 16px -} - -.fileIcon__67954 { - margin-inline-end:18px} - -.fileName__67954 { - color: var(--text-default); - font-size: 16px; - margin-bottom: 4px; - max-width: 256px; - overflow: hidden; - text-overflow: ellipsis; - text-transform: lowercase; - white-space: nowrap -} - -.fileInfo__67954 { - flex: 1 -} - -.fileInput__46231 { - display: none -} - -.diversitySelector_a45a2a { - position: relative -} - -.diversitySelectorButton_a45a2a { - cursor: pointer; - height: var(--custom-emoji-picker-constants-diversity-emoji-size); - width: var(--custom-emoji-picker-constants-diversity-emoji-size) -} - -.diversitySelectorOptions_a45a2a { - background-color: var(--background-mod-normal); - border: 1px solid var(--background-base-lowest); - border-radius: 4px; - display: flex; - flex-direction: column; - inset-inline-end: -5px; - position: absolute; - top: -5px -} - -.diversityEmojiItem_a45a2a { - background-position: 50%; - background-repeat: no-repeat; - background-size: contain; - cursor: pointer; - height: var(--custom-emoji-picker-constants-diversity-emoji-size); - padding: var(--custom-emoji-picker-constants-emoji-container-padding-vertical) var(--custom-emoji-picker-constants-emoji-container-padding-horizontal); - transition: background-color .1s ease-in-out; - width: var(--custom-emoji-picker-constants-diversity-emoji-size) -} - -.diversityEmojiItem_a45a2a:hover { - background-color: var(--interactive-background-hover) -} - -.diversityEmojiItemImage_a45a2a { - height: var(--custom-emoji-picker-constants-diversity-emoji-size); - width: var(--custom-emoji-picker-constants-diversity-emoji-size) -} - -.enable-forced-colors .diversitySelectorButton_a45a2a { - background-color: HighlightText; - border: 1px solid Highlight; - border-radius: 4px; - padding: 2px -} - -.enable-forced-colors .diversityEmojiItem_a45a2a { - background-color: ButtonFace; - border: 1px solid ButtonFace; - border-radius: 4px -} - -.enable-forced-colors .diversityEmojiItem_a45a2a:hover { - border-color: ButtonText -} - -.wrapper_c0e32c { - position: relative; - transition: box-shadow .2s ease-out -} - -.wrapper_c0e32c,.wrapper_c0e32c.isBurstReactionPicker_c0e32c { - border-radius: var(--custom-emoji-picker-border-radius) -} - -.wrapper_c0e32c.isBurstReactionPicker_c0e32c { - box-shadow: 0 0 0 2px var(--background-brand) -} - -.wrapper_c0e32c.isBurstReactionPicker_c0e32c:before { - box-shadow: 0 0 100px var(--background-brand); - content: ""; - height: 100%; - opacity: .5; - pointer-events: none; - position: absolute; - width: 100% -} - -.high-contrast-mode .wrapper_c0e32c:not(.emojiPickerHasTabWrapper_c0e32c) { - border: 1px solid var(--border-normal); - contain: content -} - -.emojiPicker_c0e32c { - background-color: var(--background-surface-high); - border-end-end-radius: var(--custom-emoji-picker-border-radius); - border-end-start-radius: var(--custom-emoji-picker-border-radius); - box-shadow: var(--shadow-border),var(--shadow-high); - display: grid; - grid-template-columns: 48px auto; - grid-template-rows: auto 1fr auto; - height: var(--custom-emoji-picker-constants-emoji-picker-height); - overflow: hidden; - width: var(--custom-emoji-picker-constants-min-emoji-picker-width) -} - -.emojiPickerHasTabWrapper_c0e32c .emojiPicker_c0e32c { - background-color: transparent; - border-end-end-radius: 0; - border-end-start-radius: 0; - box-shadow: none; - flex: 1 1 auto; - grid-template-rows: auto 1fr auto; - height: 100%; - width: auto -} - -.header_c0e32c { - align-items: center; - background-color: var(--background-surface-high); - border-bottom: 1px solid var(--border-subtle); - border-start-end-radius: var(--custom-emoji-picker-border-radius); - border-start-start-radius: var(--custom-emoji-picker-border-radius); - box-shadow: none; - display: flex; - gap: var(--space-12); - grid-column: 1/3; - margin: 0 -1px; - padding: var(--custom-gif-picker-gutter-size); - padding-top: var(--space-16); - position: relative; - z-index: 100 -} - -.emojiPickerHasTabWrapper_c0e32c .header_c0e32c { - background-color: transparent; - border-end-end-radius: 0; - border-end-start-radius: 0; - margin: 0; - padding: 0 16px 16px -} - -.isBurstReactionPicker_c0e32c .header_c0e32c { - margin: 0 -} - -.emojiPickerHasTabWrapper_c0e32c .header_c0e32c { - padding: var(--custom-gif-picker-gutter-size) -} - -.diversitySelector_c0e32c { - grid-column: 2/3 -} - -.categoryList_c0e32c { - border-end-start-radius: var(--custom-emoji-picker-border-radius); - bottom: 0; - inset-inline-start: 0; - overflow: hidden; - position: absolute; - top: 69px; - width: 48px -} - -.emojiPickerHasTabWrapper_c0e32c .categoryList_c0e32c { - top: 53px -} - -.bodyWrapper_c0e32c { - display: grid; - grid-column: 2/3; - grid-row: 2/3; - grid-template-rows: auto 1fr; - position: relative -} - -.emojiPickerListWrapper_c0e32c { - grid-row: 2/2; - overflow: hidden; - position: relative -} - -.inspector_c0e32c { - grid-column: 2/3; - grid-row: 3/4 -} - -.enable-forced-colors .header_c0e32c { - border-bottom: 1px solid CanvasText -} - -@media (max-width: 485px) { - .emojiPicker_c0e32c { - width:350px - } -} - -.inspector_aeaaeb { - align-items: center; - background-color: var(--background-base-lower); - border-top: 1px solid var(--border-normal); - box-sizing: border-box; - display: flex; - flex: 0 0 auto; - flex-direction: row; - height: var(--custom-expression-picker-constants-expression-picker-inspector-bar-height); - overflow: hidden; - padding: 0 16px; - width: 100% -} - -.graphicPrimary_aeaaeb { - height: var(--custom-expression-picker-constants-expression-picker-inspector-bar-graphic-primary-dimensions); - width: var(--custom-expression-picker-constants-expression-picker-inspector-bar-graphic-primary-dimensions) -} - -.graphicSecondary_aeaaeb { - height: var(--custom-expression-picker-constants-expression-picker-inspector-bar-graphic-secondary-dimensions); - margin-inline-start:8px;width: var(--custom-expression-picker-constants-expression-picker-inspector-bar-graphic-secondary-dimensions) -} - -.textWrapper_aeaaeb { - flex: 1; - margin-inline-start:8px;overflow: hidden -} - -.titlePrimary_aeaaeb,.titleSecondary_aeaaeb { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.titleSecondary_aeaaeb { - color: var(--interactive-text-default) -} - -.favorite_aeaaeb { - margin-inline-end:3px;margin-bottom: -1px -} - -.badgeLabel_aeaaeb { - border-radius: 4px; - display: flex; - flex-direction: row; - gap: 4px; - padding: 4px 8px; - text-transform: uppercase -} - -.topGuildEmojiBadge_aeaaeb { - background: linear-gradient(268.26deg,#bf5151,#db803f 102.45%) -} - -.newlyAddedBadge_aeaaeb { - background: linear-gradient(268.26deg,#097d8d,#60a654 102.45%) -} - -.newlyAddedBadgeIcon_aeaaeb { - color: #fff; - height: 14px; - margin-block:2px 0;margin-inline:0 2px;width: 14px -} - -.enable-forced-colors .inspector_aeaaeb { - border-top: 1px solid CanvasText -} - -.emoji__045fa { - height: 100%; - -o-object-fit: contain; - object-fit: contain; - width: 100% -} - -.glyphEmoji__045fa { - align-items: center; - display: flex; - justify-content: center; - overflow: hidden; - white-space: nowrap -} - -.icon__045fa { - height: 100%; - transform-origin: 50% 50%; - width: 100%; - fill: var(--interactive-text-default) -} - -.wrapper__29ebd { - align-items: center; - display: flex; - flex-direction: column; - font-size: 14px; - font-weight: var(--font-weight-medium); - height: 100%; - justify-content: center; - text-align: center -} - -.sadImage__29ebd { - background-repeat: no-repeat; - background-size: 90px; - height: 90px; - margin-bottom: 20px; - width: 90px -} - -.theme-dark .wrapper__29ebd { - color: var(--primary-400) -} - -.theme-dark .sadImage__29ebd { - background-image: url(/assets/2a563d3b06091b16.svg) -} - -.theme-dark .forceLightTheme__29ebd .wrapper__29ebd,.theme-light .wrapper__29ebd { - color: #99aab5 -} - -.theme-dark .forceLightTheme__29ebd .sadImage__29ebd,.theme-light .sadImage__29ebd { - background-image: url(/assets/ed7b8a00de132314.svg) -} - -.wrapper__14245 { - align-items: center; - background-color: var(--background-surface-high); - box-sizing: border-box; - display: flex; - height: var(--custom-expression-picker-constants-expression-picker-list-section-heading-height); - padding-block:0;padding-inline:8px 4px;z-index: 1 -} - -.wrapper__14245:after { - background-color: inherit; - content: ""; - height: 3px; - inset-inline-start: 0; - position: absolute; - top: -2px; - width: 100% -} - -.header__14245 { - align-items: center; - color: var(--text-default); - display: flex; - font-size: 14px; - font-weight: var(--font-weight-semibold); - justify-content: flex-start; - min-width: 0; - transition: color .125s -} - -.interactive__14245:hover { - cursor: pointer -} - -.interactive__14245:hover:hover { - color: var(--interactive-text-active) -} - -.headerIcon__14245 { - display: contents; - height: 100%; - margin-inline-end:8px} - -.headerLabel__14245 { - margin-inline-end:4px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.headerIcon__14245+.headerLabel__14245 { - margin-inline-start:4px} - -.headerCollapseIcon__14245 { - flex-shrink: 0 -} - -.full-motion .headerCollapseIcon__14245 { - transition: transform .1s -} - -.headerCollapseIconCollapsed__14245 { - transform: rotate(-90deg) -} - -.enable-forced-colors .wrapper__14245 { - border-bottom: 1px solid CanvasText -} - -.enable-forced-colors .interactive__14245 { - background-color: ButtonFace; - color: ButtonText; - forced-color-adjust: none -} - -.enable-forced-colors .interactive__14245:hover { - color: ButtonText -} - -.upsellContainer__0b69f { - align-items: center; - background: linear-gradient(var(--background-base-lower),var(--background-base-lower)) padding-box,var(--custom-premium-colors-premium-gradient-tier-2-tri-color) border-box; - border-radius: 12px; - display: flex; - padding: 12px -} - -.upsellContainer__0b69f.withGradientBg__0b69f { - background: var(--background-surface-high); - border: 1px solid var(--border-subtle); - overflow: hidden; - padding: 0 -} - -.upsellContainer__0b69f.updatedStyling__0b69f { - background: var(--background-surface-highest); - border: 1px solid var(--border-subtle) -} - -.expressiveGradient__0b69f { - align-items: center; - display: flex; - flex: 1 1; - min-height: var(--space-32); - padding: 12px -} - -.expressiveGradient__0b69f.radialMask__0b69f { - background: radial-gradient(100% 100% at 50% 100%,#000 60%) -} - -.expressiveGradient__0b69f.hasPreviewSound__0b69f { - padding: 8px 12px -} - -.upsellContainerFloating__0b69f { - inset-block: auto var(--space-8); - inset-inline: var(--space-8) var(--space-16); - position: absolute -} - -.upsellContainerInline__0b69f { - flex: 1; - margin: var(--space-8) var(--space-16) -} - -.upsellContainerBottom__0b69f { - border-radius: 0; - padding-bottom: var(--space-12); - width: 100% -} - -.soundPreview__0b69f { - flex-grow: 1 -} - -.soundPreviewName__0b69f { - align-items: center; - display: flex; - gap: 6px; - height: var(--space-24) -} - -.soundPreviewText__0b69f { - flex: 1; - margin-inline-end:16px} - -.upsellText__0b69f { - flex: 1; - margin-block:auto;margin-inline:8px 16px} - -.lockIcon__0b69f { - color: var(--text-strong) -} - -.nitroTopDividerContainer_b3fb5f { - display: flex; - flex-direction: column; - padding-top: var(--custom-emoji-picker-constants-emoji-premium-upsell-margin-top) -} - -.nitroTopDividerUpper_b3fb5f { - height: 12px -} - -.nitroTopDividerShadow_b3fb5f { - box-shadow: 0 -5px 30px 4px var(--premium-tier-2-purple) -} - -.glow_b3fb5f.reducedMotion_b3fb5f { - opacity: 0 -} - -.glow_b3fb5f:not(.reducedMotion_b3fb5f) { - animation: glowAnimation_b3fb5f 5s forwards -} - -@keyframes glowAnimation_b3fb5f { - 0% { - opacity: 0 - } - - 60% { - opacity: 0 - } - - 80% { - opacity: 1 - } - - to { - opacity: 0 - } -} - -.nitroTopDividerLockContainer_b3fb5f { - align-items: center; - display: flex -} - -.nitroTopDividerLockBorder_b3fb5f { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color); - height: 1px; - width: 100% -} - -.nitroTopDividerLockBorder_b3fb5f.brandRefresh_b3fb5f { - background: var(--border-normal) -} - -.nitroTopDividerLockCircle_b3fb5f { - align-items: center; - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color); - border-radius: 50%; - display: flex; - height: 28px; - inset-inline-start: 50%; - justify-content: center; - position: absolute; - transform: translateX(-50%); - width: 28px; - z-index: 9999 -} - -.nitroTopDividerLockCircle_b3fb5f.brandRefresh_b3fb5f { - background: var(--background-surface-highest); - border: 1px solid var(--border-normal) -} - -.nitroTopDividerLockBorderReversed_b3fb5f { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color-reverse); - height: 1px; - width: 100% -} - -.nitroTopDividerLockBorderReversed_b3fb5f.brandRefresh_b3fb5f { - background: var(--border-normal) -} - -.nitroTopDividerLower_b3fb5f { - background-color: var(--premium-tier-2-purple); - height: 12px -} - -.nitroTopDividerLower_b3fb5f.brandRefresh_b3fb5f { - background: linear-gradient(90deg,var(--expressive-gradient-nitro-pink-start) 0,var(--expressive-gradient-nitro-pink-end) 100%) -} - -.theme-light .nitroTopDividerLower_b3fb5f.brandRefresh_b3fb5f { - background: linear-gradient(90deg,color-mix(in srgb,var(--expressive-gradient-nitro-pink-start) 40%,transparent),color-mix(in srgb,var(--expressive-gradient-nitro-pink-end) 40%,transparent)) -} - -.reverseTrialTopDividerLower_b3fb5f { - background-color: var(--premium-tier-2-purple); - height: 25px; - opacity: .2 -} - -.premiumUnlockAnimation_b3fb5f { - animation: shrink_b3fb5f 2s forwards; - inset-inline-start: 50%; - position: absolute; - transform: translateX(-50%); - z-index: 9999 -} - -@keyframes shrink_b3fb5f { - 0% { - height: 67px; - width: 67px - } - - 75% { - height: 66px; - width: 66px - } - - 90% { - height: 32px; - width: 32px - } - - to { - display: none; - height: 32px; - width: 32px - } -} - -.premiumUnlockedWithNitroPillContainer_b3fb5f { - align-items: center; - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color); - border-radius: 14px; - box-shadow: inset 0 0 4px 0 rgba(194,105,195,.6); - display: flex; - gap: 4px; - height: 24px; - inset-inline-start: 50%; - opacity: 0; - overflow: hidden; - padding: 4px 16px; - position: absolute; - transform: translateX(-50%); - z-index: 9999 -} - -.premiumUnlockedWithNitroPillContainer_b3fb5f.reducedMotion_b3fb5f { - opacity: 1 -} - -.premiumUnlockedWithNitroPillContainer_b3fb5f:not(.reducedMotion_b3fb5f) { - animation: expand_b3fb5f 2s 2s forwards -} - -@keyframes expand_b3fb5f { - 0% { - max-width: 0; - opacity: 1 - } - - to { - max-width: 200px; - opacity: 1 - } -} - -.rowContainer__1e702 { - display: flex; - flex-direction: row; - gap: 8px; - padding-top: 8px -} - -.image__1859b,.imageLoading__1859b { - color: transparent; - -o-object-fit: contain; - object-fit: contain; - pointer-events: none -} - -.imageLoading__1859b { - background-repeat: no-repeat; - -o-object-position: 100px; - object-position: 100px -} - -.images-light .imageLoading__1859b { - background-image: url(/assets/4b212c35992a1e02.png) -} - -.images-dark .imageLoading__1859b { - background-image: url(/assets/2dfc4736f9e305f0.png) -} - -.emojiSpriteImage_d982c2 { - -o-object-fit: contain; - object-fit: contain; - pointer-events: none -} - -.emojiLockIconContainer_d982c2 { - background: var(--opacity-black-60); - border-radius: 2px; - height: 40px; - inset: 0; - margin: auto; - pointer-events: none; - position: absolute; - width: 40px -} - -.emojiLockIcon_d982c2 { - color: var(--icon-strong); - inset: 0; - margin: auto; - position: absolute -} - -.lockedEmoji_d982c2 { - border-radius: 2px -} - -.theme-light .emojiLockIconContainer_d982c2 { - background: hsl(var(--primary-200-hsl)/.6) -} - -@keyframes ripple_fc7141 { - 0% { - opacity: 1; - transform: scale(.8) - } - - to { - opacity: 0; - transform: scale(1.6) - } -} - -.emojiListRow_fc7141 { - display: grid; - grid-auto-flow: column; - padding-inline-start:8px} - -.newlyAddedHighlightContainer_fc7141 .emojiListRow_fc7141 { - padding-inline-start:0} - -.emojiListRow_fc7141 li { - overflow: hidden -} - -.topEmojiSectionContainer_fc7141 { - display: flex; - flex-direction: row; - margin-bottom: 20px -} - -.topEmojiContainer_fc7141 { - display: grid; - grid-auto-flow: column -} - -.topEmojiContainer_fc7141.noEmojis_fc7141 { - display: none -} - -.newlyAddedHighlightContainer_fc7141 { - display: flex; - flex-direction: column; - position: relative -} - -.newlyAddedBadge_fc7141 { - align-items: center; - background-color: #2d7d46; - border-radius: 0 0 4px 4px; - bottom: calc(var(--custom-emoji-picker-constants-newly-added-emoji-badge-height)*-1); - display: flex; - flex-direction: row; - justify-content: center; - margin-inline-start:auto;position: absolute; - text-transform: uppercase; - z-index: 1 -} - -.newlyAddedBadge_fc7141.alignRight_fc7141 { - inset-inline-end: 0 -} - -.newlyAddedBadgeMedium_fc7141 { - width: calc(var(--custom-emoji-picker-constants-emoji-size-medium) + var(--custom-emoji-picker-constants-emoji-container-padding-horizontal)*2 + 2px) -} - -.newlyAddedBadgeLarge_fc7141 { - width: calc(var(--custom-emoji-picker-constants-emoji-size-large) + var(--custom-emoji-picker-constants-emoji-container-padding-horizontal)*2 + 2px) -} - -.newlyAddedBadgeStar_fc7141 { - color: #fff; - height: 12px; - margin-inline-end:2px;width: 12px -} - -.newlyAddedHighlight_fc7141 { - border: 1px solid #2d7d46; - border-radius: 4px 4px 4px 0; - z-index: 1 -} - -.newlyAddedHighlight_fc7141.alignRight_fc7141 { - border-radius: 4px 4px 0 -} - -.newlyAddedHighlight_fc7141.oneItem_fc7141 { - border-radius: 4px 4px 0 0 -} - -.newlyAddedHighlightContainer_fc7141:hover .newlyAddedHighlight_fc7141 { - border: 1px solid rgba(45,125,70,.5) -} - -.newlyAddedHighlightContainer_fc7141:hover .newlyAddedBadge_fc7141 { - background-color: rgba(45,125,70,.5) -} - -.emojiListRowLargeSize_fc7141 { - grid-template-columns: repeat(auto-fill,calc(var(--custom-emoji-picker-constants-emoji-size-large) + var(--custom-emoji-picker-constants-emoji-container-padding-vertical)*2)); - height: calc(var(--custom-emoji-picker-constants-emoji-size-large) + var(--custom-emoji-picker-constants-emoji-container-padding-vertical)*2) -} - -.emojiListRowMediumSize_fc7141 { - grid-template-columns: repeat(auto-fill,calc(var(--custom-emoji-picker-constants-emoji-size-medium) + var(--custom-emoji-picker-constants-emoji-container-padding-vertical)*2)); - height: calc(var(--custom-emoji-picker-constants-emoji-size-medium) + var(--custom-emoji-picker-constants-emoji-container-padding-vertical)*2) -} - -.emojiItem_fc7141 { - background: none; - border-radius: 4px; - cursor: pointer; - display: block; - padding: var(--custom-emoji-picker-constants-emoji-container-padding-vertical) var(--custom-emoji-picker-constants-emoji-container-padding-horizontal); - position: relative -} - -.emojiItem_fc7141,.emojiItem_fc7141:focus { - outline: none -} - -.emojiItem_fc7141.emojiItemSelected_fc7141,.emojiItem_fc7141.expandCollapseButtonSelected_fc7141 { - background-color: var(--interactive-background-selected) -} - -.emojiItem_fc7141.expandCollapseButtonSelected_fc7141 { - border-radius: var(--radius-round) -} - -.emojiItem_fc7141:after { - border: 3px solid var(--yellow-300); - border-radius: 100%; - box-sizing: border-box; - content: ""; - height: 100%; - inset-inline-start: 0; - opacity: 0; - pointer-events: none; - position: absolute; - top: 0; - transform-origin: center; - width: 100%; - z-index: 5 -} - -.full-motion .emojiItem_fc7141:after { - transition: all .15s ease-in-out -} - -.emojiItem_fc7141.showPulse_fc7141:after { - animation: ripple_fc7141 .25s ease-out 1 -} - -.emojiItemLarge_fc7141 { - height: calc(var(--custom-emoji-picker-constants-emoji-size-large) + var(--custom-emoji-picker-constants-emoji-container-padding-vertical)*2); - width: calc(var(--custom-emoji-picker-constants-emoji-size-large) + var(--custom-emoji-picker-constants-emoji-container-padding-horizontal)*2) -} - -.emojiItemMedium_fc7141 { - height: calc(var(--custom-emoji-picker-constants-emoji-size-medium) + var(--custom-emoji-picker-constants-emoji-container-padding-vertical)*2); - width: calc(var(--custom-emoji-picker-constants-emoji-size-medium) + var(--custom-emoji-picker-constants-emoji-container-padding-horizontal)*2) -} - -.icon_fc7141 { - height: 100%; - transform-origin: 50% 50%; - width: 100%; - fill: var(--interactive-text-default) -} - -.icon_fc7141:hover .icon_fc7141 { - fill: var(--interactive-text-hover) -} - -.keyboard-mode .emojiItem_fc7141.emojiItemSelected_fc7141 { - position: relative -} - -.keyboard-mode .emojiItem_fc7141.emojiItemSelected_fc7141:before { - bottom: 0; - content: ""; - display: block; - inset-inline: 0; - pointer-events: none; - position: absolute; - top: 0; - --__adaptive-radius: 4px; - border-radius: var(--__adaptive-radius); - --__adaptive-focus-color: var(--interactive-text-hover); - border: 2px solid var(--__adaptive-focus-color); - z-index: 1 -} - -.enable-forced-colors .emojiItem_fc7141.emojiItemSelected_fc7141 { - background-color: HighlightText; - outline: 1px solid Highlight; - outline-offset: -1px -} - -.enable-forced-colors .emojiItem_fc7141.emojiItemSelected_fc7141:before { - border-color: Highlight -} - -.theme-light .emojiItem_fc7141.emojiItemSelected_fc7141,.theme-light .emojiItem_fc7141.expandCollapseButtonSelected_fc7141 { - background-color: var(--background-base-low); - box-shadow: inset 0 0 0 1px var(--border-subtle) -} - -.listWrapper_c656ac { - inset: 0; - position: absolute -} - -.list_c656ac { - height: 100% -} - -.noSearchResultsContainer_c656ac { - inset: 0; - position: absolute -} - -.categorySection_c656ac { - padding-bottom: var(--custom-emoji-picker-constants-emoji-section-margin-bottom) -} - -.categorySectionCollapsed_c656ac,.categorySectionLast_c656ac,.categorySectionNitroDivider_c656ac { - padding-bottom: 0 -} - -.categorySectionNitroLocked_c656ac { - background-color: hsl(var(--premium-tier-2-purple-hsl)/.2) -} - -.categorySectionNitroLocked_c656ac.brandRefresh_c656ac { - background: linear-gradient(90deg,var(--expressive-gradient-nitro-pink-start) 0,var(--expressive-gradient-nitro-pink-end) 100%) -} - -.theme-light .categorySectionNitroLocked_c656ac.brandRefresh_c656ac { - background: linear-gradient(90deg,color-mix(in srgb,var(--expressive-gradient-nitro-pink-start) 40%,transparent),color-mix(in srgb,var(--expressive-gradient-nitro-pink-end) 40%,transparent)) -} - -.header_c656ac { - position: sticky; - top: 0 -} - -.inactiveNitroHeader_c656ac { - background-color: transparent!important -} - -.activeNitroHeader_c656ac { - background-color: var(--background-base-lower) -} - -.premiumUpsell_c656ac { - box-sizing: border-box; - height: var(--custom-emoji-picker-constants-emoji-premium-upsell-height); - margin-top: var(--custom-emoji-picker-constants-emoji-premium-upsell-margin-top); - margin-inline-end:4px;overflow: hidden -} - -.sectionPremiumUpsellTopOfList_c656ac { - margin-top: 8px -} - -.premiumUpsellTopOfList_c656ac { - height: 50px; - margin-top: 0 -} - -.nitroBottomDivider_c656ac { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color); - height: 1px; - margin: var(--custom-emoji-picker-constants-emoji-premium-upsell-margin-top) 0; - width: 100% -} - -.nitroBottomDivider_c656ac.brandRefresh_c656ac { - background: var(--border-normal) -} - -.upsellContainer_c656ac { - align-items: center; - background: linear-gradient(var(--background-base-lower),var(--background-base-lower)) padding-box,var(--custom-premium-colors-premium-gradient-tier-2-tri-color) border-box; - border-radius: 12px; - display: flex; - filter: drop-shadow(0 0 10px #b845c180); - height: 52px; - inset-block: auto 8px; - inset-inline: 8px 16px; - padding: 0 12px; - position: absolute -} - -.upsellText_c656ac { - margin-block:auto;margin-inline:4px 16px} - -.upsellLock_c656ac { - color: var(--icon-strong); - padding-bottom: 2px -} - -.upsellButton_c656ac { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color); - border-radius: 3px; - flex-shrink: 0; - margin-inline-start:auto} - -.soundmojiViewMore_c656ac { - align-self: center; - color: var(--text-default); - cursor: pointer; - font-size: 12px; - font-weight: var(--font-weight-semibold); - margin-inline-start:auto;transition: color .125s -} - -.soundmojiViewMore_c656ac:hover { - color: var(--interactive-text-active) -} - -.premiumRetentionNotice_a606ef { - background-color: var(--background-base-lowest); - border-radius: 4px; - color: var(--text-default); - display: flex; - font-size: 12px; - font-weight: var(--font-weight-medium); - line-height: 16px; - margin: 8px 8px 0; - padding: 8px -} - -.premiumRetentionNoticeIcon_a606ef { - height: 24px; - margin-inline-end:8px;margin-top: 2px; - min-width: var(--custom-emoji-picker-constants-emoji-size) -} - -.premiumRetentionNoticeClose_a606ef { - color: var(--interactive-text-default); - cursor: pointer; - height: 16px; - width: 16px -} - -.premiumRetentionNoticeClose_a606ef:hover { - color: var(--interactive-text-hover) -} - -.premiumRetentionNoticeContent_a606ef { - flex: 1 1 auto -} - -.noBoxShadowMargin_d0aeea { - margin: 0 -} - -html:not(.chat-input) .inputInner_b1f01d { - background: var(--input-background-default); - border-radius: var(--radius-sm); - --custom-slate-textarea-padding: calc((var(--custom-channel-textarea-text-area-height) - var(--chat-markup-line-height))/2); - --custom-slate-textarea-right-offset: 10px; - padding: 0 calc(var(--custom-slate-textarea-padding) - var(--custom-slate-textarea-right-offset)) 0 var(--custom-slate-textarea-padding) -} - -.chat-input .input_b1f01d,.custom-theme-background .input_b1f01d { - background: var(--input-background-default) -} - -.input_b1f01d { - border: none!important -} - -.editor_b1f01d.editor_b1f01d { - --channel-text-area-placeholder: var(--text-muted); - font-size: 14px; - min-height: 88px -} - -.container_b1f01d { - background: var(--background-base-low); - border-radius: 8px; - box-shadow: var(--shadow-border),var(--shadow-high); - padding: 16px; - width: 300px -} - -.user-profile-modal .container_b1f01d.activity_b1f01d { - width: 400px -} - -.user-profile-sidebar .container_b1f01d { - width: 276px -} - -.user-profile-sidebar .container_b1f01d.status_b1f01d { - margin-inline-start:-90px} - -.user-profile-popout .container_b1f01d.status_b1f01d { - margin-inline-start:-122px} - -.user-profile-popout .container_b1f01d.avatar_b1f01d { - margin-inline-start:-32px} - -.user-profile-popout .container_b1f01d.activity_b1f01d { - margin-inline-start:-32px} - -.user-profile-popout .container_b1f01d.customProfileTheme_b1f01d.status_b1f01d { - margin-inline-start:-118px} - -.user-profile-popout .container_b1f01d.customProfileTheme_b1f01d.avatar_b1f01d { - margin-inline-start:-28px} - -.popover_c97e55 { - background-color: var(--user-profile-toolbar-background); - border: 1px solid var(--user-profile-toolbar-border); - border-radius: 100px; - box-shadow: var(--shadow-low); - display: inline-flex; - gap: 2px; - height: unset; - inset-inline-end: min(8px,calc(50% - 29px)); - opacity: 0; - padding: 2px; - pointer-events: none; - position: absolute; - top: -16px -} - -.custom-user-profile-theme.theme-dark .popover_c97e55 { - -webkit-backdrop-filter: blur(36px); - backdrop-filter: blur(36px) -} - -.custom-user-profile-theme.theme-light .popover_c97e55 { - -webkit-backdrop-filter: blur(24px); - backdrop-filter: blur(24px) -} - -.popover_c97e55.visible_c97e55,.popover_c97e55:focus-within { - opacity: 1; - pointer-events: all -} - -:not(.keyboard-mode).full-motion .popover_c97e55.visible_c97e55,:not(.keyboard-mode).full-motion .popover_c97e55:focus-within { - animation: hoverIn_c97e55 .15s ease forwards; - opacity: 0 -} - -:not(.keyboard-mode).full-motion .popover_c97e55.visible_c97e55.expandable_c97e55,:not(.keyboard-mode).full-motion .popover_c97e55:focus-within.expandable_c97e55 { - animation-delay: .3s -} - -@keyframes hoverIn_c97e55 { - 0% { - opacity: 0; - top: -14px - } - - to { - opacity: 1; - top: -16px - } -} - -.button_c97e55 { - height: unset; - min-width: unset; - padding: 4px -} - -.button_c97e55.left_c97e55 { - border-radius: 100px 6px 6px 100px; - padding-inline-start:6px} - -.button_c97e55.right_c97e55 { - border-radius: 6px 100px 100px 6px; - padding-inline-end:6px} - -.button_c97e55:hover { - background: var(--background-mod-normal) -} - -.button_c97e55:hover .icon_c97e55 { - fill: var(--interactive-text-hover) -} - -.button_c97e55:active { - background: var(--background-mod-strong) -} - -.button_c97e55:active .icon_c97e55 { - fill: var(--interactive-text-active) -} - -.container_ab8609 { - --custom-status-bubble-background-color: var(--background-surface-highest); - --custom-status-bubble-background: var(--custom-status-bubble-background-color); - height: -moz-fit-content; - height: fit-content; - position: absolute; - width: -moz-fit-content; - width: fit-content; - z-index: var(--custom-user-profile-middle-layer-z-index) -} - -.theme-dark .container_ab8609 { - --custom-status-bubble-overlay-color: 255 255 255 -} - -.theme-light .container_ab8609 { - --custom-status-bubble-overlay-color: 0 0 0 -} - -.container_ab8609.editable_ab8609:hover { - --custom-status-bubble-background: linear-gradient(rgb(var(--custom-status-bubble-overlay-color)/0.04),rgb(var(--custom-status-bubble-overlay-color)/0.04)),var(--custom-status-bubble-background-color) -} - -.container_ab8609.hoisted_ab8609 { - z-index: var(--custom-user-profile-hoist-z-index) -} - -.user-profile-popout .container_ab8609 { - inset-inline-start: 105px; - top: calc(var(--custom-user-profile-banner-height) - 7px) -} - -.user-profile-modal .container_ab8609 { - inset-inline-start: 161px; - top: -10px -} - -.user-profile-modal-v2 .container_ab8609 { - inset-inline-start: 172px; - top: calc(var(--custom-user-profile-banner-height) - 8px) -} - -.user-profile-sidebar .container_ab8609 { - inset-inline-start: 109px; - top: calc(var(--custom-user-profile-banner-height) - 7px) -} - -.referenceContainer_ab8609 { - pointer-events: none; - position: relative; - visibility: hidden -} - -.referenceContainer_ab8609.withLabel_ab8609 { - margin-top: -24px -} - -.user-profile-popout .referenceContainer_ab8609 { - margin-top: -10px; - margin-inline:109px 12px} - -.user-profile-popout .referenceContainer_ab8609.withLabel_ab8609 { - margin-top: -24px -} - -.custom-user-profile-theme .user-profile-popout .referenceContainer_ab8609 { - margin-inline:105px 8px} - -.user-profile-modal .referenceContainer_ab8609 { - margin-top: -10px; - margin-inline:161px 16px} - -.user-profile-modal .referenceContainer_ab8609.withLabel_ab8609 { - margin-top: -24px -} - -.user-profile-modal-v2 .referenceContainer_ab8609 { - margin-top: -10px; - margin-inline:144px 12px} - -.user-profile-modal-v2 .referenceContainer_ab8609.withLabel_ab8609 { - margin-top: -24px -} - -.user-profile-sidebar .referenceContainer_ab8609 { - margin-top: -10px; - margin-inline:109px 8px} - -.user-profile-sidebar .referenceContainer_ab8609.withLabel_ab8609 { - margin-top: -24px -} - -.referenceContainer_ab8609>.outer_ab8609>.inner_ab8609>.content_ab8609 { - max-width: 155px -} - -.referenceContainer_ab8609>.outer_ab8609>.inner_ab8609>.content_ab8609.unclamped_ab8609 { - position: absolute; - top: 0 -} - -.referenceContainer_ab8609>.outer_ab8609 { - max-width: none; - width: -moz-max-content; - width: max-content -} - -.outer_ab8609 { - background: var(--custom-status-bubble-background); - border: 1px solid var(--border-muted); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-low); - box-sizing: border-box; - max-width: 181px; - min-width: 42px; - overflow: hidden -} - -.outer_ab8609:before { - height: 20px; - inset-inline-start: 10px; - top: -8px; - width: 20px -} - -.outer_ab8609:after,.outer_ab8609:before { - background: inherit; - border: inherit; - border-radius: 50%; - box-shadow: inherit; - box-sizing: border-box; - content: ""; - position: absolute -} - -.outer_ab8609:after { - height: 10px; - inset-inline-start: -3px; - top: -15px; - width: 10px -} - -.theme-dark .outer_ab8609 { - border: 1px solid var(--border-muted) -} - -.custom-user-profile-theme.theme-dark .outer_ab8609 { - border: 1px solid var(--border-strong) -} - -.inner_ab8609 { - background: var(--custom-status-bubble-background); - cursor: text; - display: block; - font-size: medium; - margin: 0 auto; - padding: 8px 12px; - position: relative; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text; - width: -moz-fit-content; - width: fit-content; - word-break: break-word -} - -.inner_ab8609.clickable_ab8609 { - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.content_ab8609 { - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - color: var(--text-default); - overflow: hidden -} - -.content_ab8609.unclamped_ab8609 { - -webkit-line-clamp: 9 -} - -.singleLineTextClamp_ab8609 { - -webkit-line-clamp: 1 -} - -.ring_ab8609 { - border-radius: var(--radius-lg) -} - -.statusText_ab8609 { - color: var(--text-default) -} - -.statusEmoji_ab8609:not(:has(~.statusText_ab8609)) { - max-height: 32px; - max-width: 32px; - min-height: 32px; - min-width: 32px -} - -.statusEmoji_ab8609:has(~.statusText_ab8609) { - height: 16px; - margin-inline-end:4px;width: 16px -} - -.statusEmoji_ab8609~.statusText_ab8609 { - display: inline -} - -.labelRow_ab8609~.statusEmoji_ab8609:not(:has(~.statusText_ab8609)) { - display: block; - margin-inline:auto} - -.addStatusPrompt_ab8609 { - color: var(--text-muted); - display: inline; - vertical-align: middle -} - -.italicPrompt_ab8609 { - font-style: italic; - padding-inline-end:1px;padding-bottom: 1px -} - -.addStatusIcon_ab8609 { - display: inline; - margin-inline-end:4px;fill: var(--text-muted); - vertical-align: middle -} - -.outer_ab8609:hover .addStatusPrompt_ab8609 { - color: var(--text-default) -} - -.outer_ab8609:hover .addStatusIconColor_ab8609 { - fill: var(--text-default) -} - -.labelRow_ab8609 { - margin-bottom: 2px -} - -:where(.section_bf424d) { - display: flex; - flex-direction: column; - gap: 8px -} - -:where(.section_bf424d):empty,:where(.section_bf424d):has(>.headings_bf424d:only-child) { - display: none -} - -.header_bf424d { - align-items: center; - display: flex; - gap: 4px -} - -.list__20827 { - display: inline-flex; - flex-wrap: wrap; - gap: 6px -} - -.popover__2ee1b { -} - -.popoverGradientWrapper__2ee1b { -} - -.popoverContentWithGradient__2ee1b { -} - -.closeButton__2ee1b { -} - -.header__2ee1b { - gap: 2px; - margin-bottom: 0; - margin-top: var(--space-20) -} - -.headerWithBadge__2ee1b { -} - -.badgeContainer__2ee1b { -} - -.content__2ee1b { -} - -.footer__2ee1b { -} - -.footerText__2ee1b { -} - -.footerLink__2ee1b { -} - -.actionBar__2ee1b { - margin-top: var(--space-16) -} - -.actions__2ee1b { -} - -.multistepIndicator__2ee1b { -} - -.multistepActionLayout__2ee1b { -} - -.caretIcon__2ee1b { -} - -.graphic__2ee1b { - margin-bottom: calc(var(--space-6)*-1); - margin-top: 0 -} - -.graphic--sm__2ee1b { -} - -.graphic--md__2ee1b { -} - -.graphic--lg__2ee1b { -} - -.graphic--video__2ee1b { -} - -.title__2ee1b { - color: var(--text-strong); - font-family: var(--font-display-marketing-header); - font-size: 21px; - font-style: italic; - font-weight: 900; - line-height: 20px; - text-align: center; - text-transform: uppercase; - word-wrap: break-word; - width: 100% -} - -.headerBody__2ee1b { - width: 90% -} - -.caretHoverable__2ee1b { - pointer-events: auto -} - -.tooltipContainer__7bebc { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - padding: 16px 8px 4px -} - -.tooltipWordmarkContainer__7bebc { - margin-top: 8px -} - -.tooltipDescription_cd30d9 { - margin-top: var(--space-4) -} - -.tooltipWordmark_cd30d9 { - color: var(--text-muted) -} - -.tooltipWordmark_d4ff11 { - color: var(--text-muted); - margin-bottom: 4px -} - -.orbBadgeAsset_d4ff11 { - width: 64px -} - -.orbHeaderWithSpacing_d4ff11 { - margin-bottom: 12px -} - -.orbSubtext_d4ff11 { - color: var(--text-muted); - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: 16px; - margin-bottom: 4px; - margin-top: 10px -} - -.container__8061a { - align-items: center; - box-sizing: border-box; - display: flex; - flex-wrap: wrap; - gap: 2px; - margin: 1px 0; - width: -moz-fit-content; - width: fit-content -} - -.container__8061a:empty { - visibility: hidden -} - -.badge__8061a { - background-position: 50%; - background-repeat: no-repeat; - background-size: contain; - display: block; - filter: saturate(var(--saturation-factor,1)); - height: 20px; - -o-object-fit: cover; - object-fit: cover; - overflow: hidden; - pointer-events: none; - width: 20px -} - -.badge__8061a:active { - opacity: .8 -} - -.badge__8061a:before { - background-color: var(--user-profile-border); - border-radius: 50%; - content: ""; - display: block; - height: 100%; - width: 100% -} - -.container__9d597 { - --custom-lift-hover: 2px; - --custom-peek-base: 8px; - --custom-peek-hover: 2px; - --custom-expanded-total: calc(var(--custom-lift-hover) + var(--custom-peek-base) + var(--custom-peek-hover)); - --custom-transition-duration: 200ms -} - -.container__9d597:has(.hasMultipleCards__9d597) { - padding-bottom: var(--custom-peek-base) -} - -.cardsList__9d597,.firstCardContainer__9d597 { - position: relative -} - -.hasMultipleCards__9d597 .firstCardContainer__9d597 .card__9d597 { - box-shadow: var(--shadow-low); - z-index: var(--custom-user-profile-middle-layer-z-index) -} - -.full-motion .hasMultipleCards__9d597 .firstCardContainer__9d597 .card__9d597 { - transition: transform var(--custom-transition-duration) ease-in-out -} - -.hasMultipleCards__9d597:focus-within .firstCardContainer__9d597 .card__9d597,.hasMultipleCards__9d597:hover .firstCardContainer__9d597 .card__9d597 { - transform: translateY(calc(var(--custom-lift-hover)*-1)) -} - -.backgroundCardContainer__9d597 { - box-sizing: border-box; - cursor: pointer; - height: var(--custom-peek-base); - overflow: hidden; - padding-inline:8px;position: absolute; - top: 100%; - width: 100% -} - -.full-motion .backgroundCardContainer__9d597 { - --custom-transition-height-delay: var(--custom-transition-duration); - transition: transform var(--custom-transition-duration) ease-in-out,height 0s var(--custom-transition-height-delay) linear -} - -.cardsList__9d597:focus-within .backgroundCardContainer__9d597,.cardsList__9d597:hover .backgroundCardContainer__9d597 { - --custom-transition-height-delay: 0ms; - height: var(--custom-expanded-total); - transform: translateY(calc(var(--custom-lift-hover)*-1)) -} - -.backgroundCardContainer__9d597 .backgroundCard__9d597 { - height: calc(var(--custom-expanded-total)*2); - transform: translateY(calc(-100% + var(--custom-peek-base))); - z-index: var(--custom-user-profile-bottom-layer-z-index) -} - -.full-motion .backgroundCardContainer__9d597 .backgroundCard__9d597 { - transition: transform var(--custom-transition-duration) ease-in-out -} - -.backgroundCardContainer__9d597 .backgroundCard__9d597:focus-within,.backgroundCardContainer__9d597 .backgroundCard__9d597:hover { - background: var(--user-profile-overlay-background-hover) -} - -.cardsList__9d597:focus-within .backgroundCardContainer__9d597 .backgroundCard__9d597,.cardsList__9d597:hover .backgroundCardContainer__9d597 .backgroundCard__9d597 { - transform: translateY(calc(-100% + var(--custom-expanded-total))) -} - -.link_d9ba3e { - border-radius: inherit -} - -.link_d9ba3e:hover { - text-decoration-color: var(--text-default) -} - -.clickable_ef9ae7 { - border-radius: inherit; - cursor: pointer; - max-width: 100%; - width: -moz-max-content; - width: max-content -} - -.clickable_ef9ae7:hover { - text-decoration: underline; - text-decoration-color: var(--text-default) -} - -.imagePosition_ef9ae7 { - align-items: center; - border-radius: var(--radius-xs); - display: flex; - height: -moz-max-content; - height: max-content; - overflow: visible; - position: relative -} - -.aspectRatio_ef9ae7 { - aspect-ratio: 1/1 -} - -.aspectRatio_ef9ae7.crunchyroll_ef9ae7 { - aspect-ratio: 2/3 -} - -.imageContainer_ef9ae7 { - border-radius: inherit; - display: flex; - justify-content: center -} - -.imageContainer_ef9ae7 .contentImage_ef9ae7 { - min-width: 60px -} - -.smallImageContainer_ef9ae7 { - align-items: center; - border-radius: var(--radius-round); - bottom: -4px; - display: flex; - flex-shrink: 0; - inset-inline-end: -4px; - justify-content: center; - position: absolute -} - -.contentImage_ef9ae7 { - border-radius: inherit; - -o-object-fit: cover; - object-fit: cover -} - -.container__39f84 { - background: var(--background-mod-subtle); - border-end-end-radius: inherit; - border-end-start-radius: inherit; - display: flex; - justify-content: space-between -} - -.custom-user-profile-theme .container__39f84 { - background: var(--user-profile-overlay-background) -} - -.content__39f84 { - min-width: 0; - padding: 8px -} - -.icon__39f84 { - display: inline-block; - height: 1lh; - vertical-align: bottom; - width: 1lh -} - -.viewMoreButton__39f84 { - border-end-end-radius: inherit; - padding: 8px -} - -.viewMoreButton__39f84:focus-visible,.viewMoreButton__39f84:hover { - background-color: var(--background-mod-subtle); - cursor: pointer -} - -.card__39ec2 { - --custom-activity-card-padding: 8px; - display: flex; - flex-direction: column; - gap: 8px; - overflow: visible; - padding: var(--custom-activity-card-padding); - position: relative -} - -.user-profile-sidebar .card__39ec2 { - --custom-activity-card-padding: 12px -} - -.user-profile-modal-v2 .card__39ec2 { - --custom-activity-card-padding: 12px; - border-radius: var(--radius-md) -} - -.card__39ec2.hoisted__39ec2 { - background: linear-gradient(rgb(var(--background-gradient-color)/.4),rgb(var(--background-gradient-color)/.4)),linear-gradient(var(--profile-gradient-end),var(--profile-gradient-end))!important; - pointer-events: none; - z-index: var(--custom-user-profile-hoist-z-index) -} - -.header__39ec2 { - justify-content: space-between -} - -.header__39ec2,.headerText__39ec2 { - display: flex; - gap: 4px -} - -.headerText__39ec2 { - align-items: center; - overflow: hidden -} - -.headerContextMenu__39ec2:empty { - display: none -} - -.headerContextMenu__39ec2.absolute__39ec2 { - align-self: flex-end; - display: inline-flex; - position: absolute -} - -.headerContextMenu__39ec2.absolute__39ec2~.body__39ec2>.content__39ec2>.details__39ec2 { - margin-inline-end:24px} - -.platformIcon__39ec2 { - background-color: var(--icon-muted); - height: 12px; - -webkit-mask-size: cover; - mask-size: cover; - width: 12px -} - -.body__39ec2 { - gap: 8px -} - -.body__39ec2,.toolbarContainer__39ec2 { - display: flex; - flex-direction: column -} - -.toolbarContainer__39ec2 { - border: none; - overflow: visible; - position: relative -} - -.content__39ec2 { - display: flex; - flex: 1 1 auto; - flex-direction: row; - gap: 8px -} - -.user-profile-modal .content__39ec2,.user-profile-modal-v2 .content__39ec2 { - gap: 12px -} - -.details__39ec2 { - align-self: center; - display: flex; - flex-direction: column; - gap: 4px; - overflow: hidden; - width: 100% -} - -.actions__39ec2 { - align-items: center; - display: flex; - gap: 8px; - margin: 4px 0 0 -} - -.user-profile-modal .actions__39ec2 { - flex-wrap: nowrap; - margin-block:0;margin-inline:20px 0} - -.user-profile-modal-v2 .actions__39ec2 { - flex-wrap: wrap -} - -.actions__39ec2 .primaryButton__39ec2 { - flex: 1 -} - -.actions__39ec2 .customButtons__39ec2 { - align-items: center; - display: flex; - flex: 1; - flex-wrap: wrap; - gap: 8px -} - -.actions__39ec2 .customButtons__39ec2>button { - flex: 1 1 auto; - min-width: calc(50% - 8px) -} - -.actions__39ec2:empty { - display: none -} - -.badges__39ec2 { - -moz-column-gap: 8px; - column-gap: 8px; - flex-wrap: wrap; - row-gap: 0 -} - -.clickableContainer__39ec2 { - cursor: pointer -} - -.clickableContainer__39ec2:hover>.card__39ec2 { - background: var(--user-profile-overlay-background-hover) -} - -.image__39ec2 { - border-radius: var(--radius-sm) -} - -.hangStatusIcon__39ec2 { - height: 100px; - width: 100px -} - -.hangStatusIcon__39ec2.small__39ec2 { - height: 60px; - width: 60px -} - -.clickableImage__39ec2,.clickableImage__39ec2:after { - border-radius: var(--radius-sm) -} - -.clickableImage__39ec2:after { - background-color: var(--black); - content: ""; - height: 100%; - inset-inline-end: 0; - opacity: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.full-motion .clickableImage__39ec2:after { - transition: opacity var(--custom-button-transition-duration) ease -} - -.clickableImage__39ec2:hover:after { - opacity: .2 -} - -.clickableText__39ec2 { - cursor: pointer; - max-width: 100%; - width: -moz-max-content; - width: max-content -} - -.clickableText__39ec2:hover { - text-decoration: underline; - text-decoration-color: var(--text-default) -} - -.inline__39ec2 { - display: inline -} - -.gameState__39ec2 { - display: flex; - gap: 4px -} - -.guildIcon__39ec2 { - border-radius: var(--radius-xs) -} - -.voiceChannel__39ec2 { - align-items: center; - display: flex; - gap: 4px -} - -.voiceChannelText__39ec2 { - align-items: center; - display: flex; - flex: 1; - gap: 2px; - overflow: hidden -} - -.voiceChannelText__39ec2 .clickableText__39ec2 { - overflow: hidden -} - -.voiceChannelText__39ec2 .voiceIcon__39ec2 { - flex-shrink: 0 -} - -.voiceChannelHeading__39ec2 { - overflow: hidden; - word-break: break-all -} - -.voiceChannelHeading__39ec2 .voiceIcon__39ec2 { - margin-inline-end:2px;position: relative; - top: 1px -} - -.voiceChannelDivider__39ec2 { - width: 100% -} - -.voiceChannelOverflowCount__39ec2 { - background: var(--background-mod-strong) -} - -.contextMenu__39ec2 { - cursor: pointer; - display: flex; - margin-inline-end:4px} - -.appWidgetPreview__39ec2 { - margin-block-end:calc(var(--custom-activity-card-padding)*-1);margin-inline: calc(var(--custom-activity-card-padding)*-1) -} - -.divider__23eb0 { - background-color: var(--user-profile-border); - height: 1px -} - -.cloudPlaySection__33ecd { - align-items: center; - display: flex; - gap: 16px; - justify-content: space-between -} - -.cloudPlaySectionTextContainer__33ecd { - align-items: center; - display: flex; - gap: 8px -} - -.cloudPlayDivider__33ecd { - margin: 4px 0; - width: 100% -} - -.container__8e51c { - align-items: center; - display: flex; - gap: 8px -} - -.text__8e51c { - font-family: var(--font-code) -} - -.bar__8e51c { - background-color: var(--background-mod-muted); - border-radius: 2px; - flex: 1; - height: 2px -} - -.progress__8e51c { - background-color: var(--interactive-text-active); - border-radius: inherit; - height: inherit; - min-width: 4px -} - -.heading-sm\/normal_a7acae { - font-family: var(--font-display); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.heading-sm\/normal_a7acae.fontScaling_a7acae { - font-size: .875rem -} - -.heading-sm\/medium_a7acae { - font-family: var(--font-display); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.heading-sm\/medium_a7acae.fontScaling_a7acae { - font-size: .875rem -} - -.heading-sm\/semibold_a7acae { - font-family: var(--font-display); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.heading-sm\/semibold_a7acae.fontScaling_a7acae { - font-size: .875rem -} - -.heading-sm\/bold_a7acae { - font-family: var(--font-display); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.heading-sm\/bold_a7acae.fontScaling_a7acae { - font-size: .875rem -} - -.heading-sm\/extrabold_a7acae { - font-family: var(--font-display); - font-size: 14px; - font-weight: 800; - line-height: 1.2857142857142858 -} - -.heading-sm\/extrabold_a7acae.fontScaling_a7acae { - font-size: .875rem -} - -.heading-md\/normal_a7acae { - font-family: var(--font-display); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.heading-md\/normal_a7acae.fontScaling_a7acae { - font-size: 1rem -} - -.heading-md\/medium_a7acae { - font-family: var(--font-display); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.heading-md\/medium_a7acae.fontScaling_a7acae { - font-size: 1rem -} - -.heading-md\/semibold_a7acae { - font-family: var(--font-display); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.heading-md\/semibold_a7acae.fontScaling_a7acae { - font-size: 1rem -} - -.heading-md\/bold_a7acae { - font-family: var(--font-display); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.heading-md\/bold_a7acae.fontScaling_a7acae { - font-size: 1rem -} - -.heading-md\/extrabold_a7acae { - font-family: var(--font-display); - font-size: 16px; - font-weight: 800; - line-height: 1.25 -} - -.heading-md\/extrabold_a7acae.fontScaling_a7acae { - font-size: 1rem -} - -.heading-lg\/normal_a7acae { - font-family: var(--font-display); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.heading-lg\/normal_a7acae.fontScaling_a7acae { - font-size: 1.25rem -} - -.heading-lg\/medium_a7acae { - font-family: var(--font-display); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.heading-lg\/medium_a7acae.fontScaling_a7acae { - font-size: 1.25rem -} - -.heading-lg\/semibold_a7acae { - font-family: var(--font-display); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.heading-lg\/semibold_a7acae.fontScaling_a7acae { - font-size: 1.25rem -} - -.heading-lg\/bold_a7acae { - font-family: var(--font-display); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.heading-lg\/bold_a7acae.fontScaling_a7acae { - font-size: 1.25rem -} - -.heading-lg\/extrabold_a7acae { - font-family: var(--font-display); - font-size: 20px; - font-weight: 800; - line-height: 1.2 -} - -.heading-lg\/extrabold_a7acae.fontScaling_a7acae { - font-size: 1.25rem -} - -.heading-xl\/normal_a7acae { - font-family: var(--font-display); - font-size: 24px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xl\/normal_a7acae.fontScaling_a7acae { - font-size: 1.5rem -} - -.heading-xl\/medium_a7acae { - font-family: var(--font-display); - font-size: 24px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xl\/medium_a7acae.fontScaling_a7acae { - font-size: 1.5rem -} - -.heading-xl\/semibold_a7acae { - font-family: var(--font-display); - font-size: 24px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xl\/semibold_a7acae.fontScaling_a7acae { - font-size: 1.5rem -} - -.heading-xl\/bold_a7acae { - font-family: var(--font-display); - font-size: 24px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xl\/bold_a7acae.fontScaling_a7acae { - font-size: 1.5rem -} - -.heading-xl\/extrabold_a7acae { - font-family: var(--font-display); - font-size: 24px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xl\/extrabold_a7acae.fontScaling_a7acae { - font-size: 1.5rem -} - -.heading-xxl\/normal_a7acae { - font-family: var(--font-display); - font-size: 32px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xxl\/normal_a7acae.fontScaling_a7acae { - font-size: 2rem -} - -.heading-xxl\/medium_a7acae { - font-family: var(--font-display); - font-size: 32px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xxl\/medium_a7acae.fontScaling_a7acae { - font-size: 2rem -} - -.heading-xxl\/semibold_a7acae { - font-family: var(--font-display); - font-size: 32px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xxl\/semibold_a7acae.fontScaling_a7acae { - font-size: 2rem -} - -.heading-xxl\/bold_a7acae { - font-family: var(--font-display); - font-size: 32px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xxl\/bold_a7acae.fontScaling_a7acae { - font-size: 2rem -} - -.heading-xxl\/extrabold_a7acae { - font-family: var(--font-display); - font-size: 32px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xxl\/extrabold_a7acae.fontScaling_a7acae { - font-size: 2rem -} - -.eyebrow_a7acae { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - letter-spacing: .02em; - line-height: 1.3333333333333333; - text-transform: uppercase -} - -.eyebrow_a7acae.fontScaling_a7acae { - font-size: .75rem -} - -.heading-deprecated-12\/normal_a7acae { - font-family: var(--font-display); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/normal_a7acae.fontScaling_a7acae { - font-size: .75rem -} - -.heading-deprecated-12\/medium_a7acae { - font-family: var(--font-display); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/medium_a7acae.fontScaling_a7acae { - font-size: .75rem -} - -.heading-deprecated-12\/semibold_a7acae { - font-family: var(--font-display); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/semibold_a7acae.fontScaling_a7acae { - font-size: .75rem -} - -.heading-deprecated-12\/bold_a7acae { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/bold_a7acae.fontScaling_a7acae { - font-size: .75rem -} - -.heading-deprecated-12\/extrabold_a7acae { - font-family: var(--font-display); - font-size: 12px; - font-weight: 800; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/extrabold_a7acae.fontScaling_a7acae { - font-size: .75rem -} - -.redesign\/heading-18\/bold_a7acae { - font-family: var(--font-display); - font-size: 18px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/heading-18\/bold_a7acae.fontScaling_a7acae { - font-size: 1.125rem -} - -.text-xxs\/normal_a7acae { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 400; - line-height: 1.2 -} - -.text-xxs\/normal_a7acae.fontScaling_a7acae { - font-size: .625rem -} - -.text-xxs\/medium_a7acae { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 500; - line-height: 1.2 -} - -.text-xxs\/medium_a7acae.fontScaling_a7acae { - font-size: .625rem -} - -.text-xxs\/semibold_a7acae { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 600; - line-height: 1.2 -} - -.text-xxs\/semibold_a7acae.fontScaling_a7acae { - font-size: .625rem -} - -.text-xxs\/bold_a7acae { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 700; - line-height: 1.2 -} - -.text-xxs\/bold_a7acae.fontScaling_a7acae { - font-size: .625rem -} - -.text-xs\/normal_a7acae { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.text-xs\/normal_a7acae.fontScaling_a7acae { - font-size: .75rem -} - -.text-xs\/medium_a7acae { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.text-xs\/medium_a7acae.fontScaling_a7acae { - font-size: .75rem -} - -.text-xs\/semibold_a7acae { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.text-xs\/semibold_a7acae.fontScaling_a7acae { - font-size: .75rem -} - -.text-xs\/bold_a7acae { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.text-xs\/bold_a7acae.fontScaling_a7acae { - font-size: .75rem -} - -.text-sm\/normal_a7acae { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.text-sm\/normal_a7acae.fontScaling_a7acae { - font-size: .875rem -} - -.text-sm\/medium_a7acae { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.text-sm\/medium_a7acae.fontScaling_a7acae { - font-size: .875rem -} - -.text-sm\/semibold_a7acae { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.text-sm\/semibold_a7acae.fontScaling_a7acae { - font-size: .875rem -} - -.text-sm\/bold_a7acae { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.text-sm\/bold_a7acae.fontScaling_a7acae { - font-size: .875rem -} - -.text-md\/normal_a7acae { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.text-md\/normal_a7acae.fontScaling_a7acae { - font-size: 1rem -} - -.text-md\/medium_a7acae { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.text-md\/medium_a7acae.fontScaling_a7acae { - font-size: 1rem -} - -.text-md\/semibold_a7acae { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.text-md\/semibold_a7acae.fontScaling_a7acae { - font-size: 1rem -} - -.text-md\/bold_a7acae { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.text-md\/bold_a7acae.fontScaling_a7acae { - font-size: 1rem -} - -.text-lg\/normal_a7acae { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.text-lg\/normal_a7acae.fontScaling_a7acae { - font-size: 1.25rem -} - -.text-lg\/medium_a7acae { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.text-lg\/medium_a7acae.fontScaling_a7acae { - font-size: 1.25rem -} - -.text-lg\/semibold_a7acae { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.text-lg\/semibold_a7acae.fontScaling_a7acae { - font-size: 1.25rem -} - -.text-lg\/bold_a7acae { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.text-lg\/bold_a7acae.fontScaling_a7acae { - font-size: 1.25rem -} - -.redesign\/message-preview\/normal_a7acae { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/normal_a7acae.fontScaling_a7acae { - font-size: .9375rem -} - -.redesign\/message-preview\/medium_a7acae { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/medium_a7acae.fontScaling_a7acae { - font-size: .9375rem -} - -.redesign\/message-preview\/semibold_a7acae { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/semibold_a7acae.fontScaling_a7acae { - font-size: .9375rem -} - -.redesign\/message-preview\/bold_a7acae { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/bold_a7acae.fontScaling_a7acae { - font-size: .9375rem -} - -.redesign\/channel-title\/normal_a7acae { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.375 -} - -.redesign\/channel-title\/normal_a7acae.fontScaling_a7acae { - font-size: 1rem -} - -.redesign\/channel-title\/medium_a7acae { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.375 -} - -.redesign\/channel-title\/medium_a7acae.fontScaling_a7acae { - font-size: 1rem -} - -.redesign\/channel-title\/semibold_a7acae { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.375 -} - -.redesign\/channel-title\/semibold_a7acae.fontScaling_a7acae { - font-size: 1rem -} - -.redesign\/channel-title\/bold_a7acae { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.375 -} - -.redesign\/channel-title\/bold_a7acae.fontScaling_a7acae { - font-size: 1rem -} - -.display-sm_a7acae { - font-family: var(--font-headline); - font-size: 20px; - font-weight: 800; - line-height: 1 -} - -.display-sm_a7acae.fontScaling_a7acae { - font-size: 1.25rem -} - -.display-md_a7acae { - font-family: var(--font-headline); - font-size: 34px; - font-weight: 800; - line-height: 1.0588235294117647 -} - -.display-md_a7acae.fontScaling_a7acae { - font-size: 2.125rem -} - -.display-lg_a7acae { - font-family: var(--font-headline); - font-size: 44px; - font-weight: 800; - line-height: .9545454545454546 -} - -.display-lg_a7acae.fontScaling_a7acae { - font-size: 2.75rem -} - -.code_a7acae { - font-family: var(--font-code); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.code_a7acae.fontScaling_a7acae { - font-size: .75rem -} - -.live_a7acae { - padding: 0 6px -} - -.liveShapeRound_a7acae { - border-radius: var(--custom-live-indicator-border-radius) -} - -.liveShapeRoundLeft_a7acae { - border-radius: var(--custom-live-indicator-border-radius) 0 0 var(--custom-live-indicator-border-radius) -} - -.liveShapeRoundRight_a7acae { - border-radius: 0 var(--custom-live-indicator-border-radius) var(--custom-live-indicator-border-radius) 0 -} - -.grey_a7acae { - background-color: var(--background-mod-muted); - color: var(--interactive-text-default) -} - -.liveLarge_a7acae { - font-size: 14px; - line-height: 15px -} - -.liveSmall_a7acae { -} - -.heading__3299f { - display: flex; - justify-content: space-between -} - -.preview__3299f { - align-items: center; - aspect-ratio: 16/9; - display: flex; - flex-direction: column; - gap: 8px; - justify-content: center; - width: 100% -} - -.user-profile-modal .preview__3299f { - max-height: 60px; - max-width: 60px; - min-height: 60px; - min-width: 60px -} - -.user-profile-modal-v2 .preview__3299f { - max-height: 100px; - max-width: 100px; - min-height: 100px; - min-width: 100px -} - -.image__3299f { - background-color: var(--background-base-lowest); - border-radius: var(--radius-sm); - height: 100%; - -o-object-fit: contain; - object-fit: contain; - width: 100% -} - -.clickable__3299f { - cursor: pointer -} - -.disabled__3299f { - cursor: not-allowed -} - -.overlay__3299f { - position: relative -} - -.overlay__3299f:after { - border-radius: var(--radius-sm); - content: ""; - height: 100%; - inset-inline-end: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 0 -} - -.overlay__3299f:hover .overlayText__3299f { - opacity: 1 -} - -.full-motion .overlay__3299f:hover .clyde__3299f { - display: initial -} - -.overlayText__3299f { - background: var(--opacity-black-60); - border-radius: var(--radius-sm); - cursor: inherit; - opacity: 0; - padding: 7px 16px; - position: absolute; - z-index: 1 -} - -.full-motion .overlayText__3299f { - transition: opacity var(--custom-button-transition-duration) ease -} - -.clyde__3299f { - animation: fadeIn__3299f 7.6s linear 19s forwards,x__3299f 3.8s linear 19s infinite alternate,y__3299f 2.3s linear 19s infinite alternate,colorX__3299f 19s step-start 19s infinite,colorY__3299f 11.5s step-start 19s infinite; - color: hsl(calc((var(--custom-color-y)*5 + var(--custom-color-x))*14.4) 100% 50%); - display: none; - inset-inline-start: 0; - opacity: 0; - position: absolute; - top: 0 -} - -@keyframes fadeIn__3299f { - 0% { - opacity: 0 - } - - to { - opacity: 1 - } -} - -@keyframes x__3299f { - 0% { - inset-inline-start: 0 - } - - to { - inset-inline-start: calc(100% - 16px) - } -} - -@keyframes y__3299f { - 0% { - top: 0 - } - - to { - top: calc(100% - 16px) - } -} - -@keyframes colorX__3299f { - 0% { - --custom-color-x: 0 - } - - 20% { - --custom-color-x: 2 - } - - 40% { - --custom-color-x: 4 - } - - 60% { - --custom-color-x: 1 - } - - 80% { - --custom-color-x: 3 - } - - to { - --custom-color-x: 0 - } -} - -@keyframes colorY__3299f { - 0% { - --custom-color-y: 0 - } - - 20% { - --custom-color-y: 2 - } - - 40% { - --custom-color-y: 4 - } - - 60% { - --custom-color-y: 1 - } - - 80% { - --custom-color-y: 3 - } - - to { - --custom-color-y: 0 - } -} - -.container_e928f4 { - background: var(--card-background-default); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-sizing: border-box; - flex-shrink: 0; - height: 60px; - position: relative; - width: 60px -} - -.user-profile-modal-v2 .container_e928f4 { - height: 100px; - width: 100px -} - -.container_e928f4:before { - background: var(--background-mod-subtle); - border-radius: inherit; - content: ""; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.theme-dark.custom-user-profile-theme .container_e928f4 { - background: var(--background-mod-strong) -} - -.theme-dark.custom-user-profile-theme .container_e928f4:before { - content: none -} - -.theme-light.custom-user-profile-theme .container_e928f4 { - background: var(--opacity-white-52) -} - -.theme-light.custom-user-profile-theme .container_e928f4:before { - content: none -} - -.circle_e928f4 { - border-radius: var(--radius-round); - box-shadow: var(--shadow-high); - position: absolute -} - -.avatar_e928f4 { - border-radius: inherit; - height: 100%; - outline: 2px solid var(--background-surface-highest); - width: 100% -} - -.theme-dark.custom-user-profile-theme .avatar_e928f4 { - outline-color: var(--opacity-white-24) -} - -.theme-light.custom-user-profile-theme .avatar_e928f4 { - outline-color: var(--opacity-white-72) -} - -.user-profile-modal-v2 .avatar_e928f4 { - outline-width: 3px -} - -.overflowCount_e928f4 { - align-items: center; - background: var(--background-surface-highest); - border-radius: inherit; - display: flex; - height: 100%; - justify-content: center; - padding: 1px; - position: relative; - width: 100% -} - -.theme-dark.custom-user-profile-theme .overflowCount_e928f4 { - background: var(--opacity-white-24) -} - -.theme-light.custom-user-profile-theme .overflowCount_e928f4 { - background: var(--opacity-white-72) -} - -.form__08bd2 { - display: contents -} - -.modalHeader__08bd2 { - gap: 4px; - overflow: visible; - padding: 16px 0 0; - text-align: center -} - -.closeButton__08bd2 { - color: var(--interactive-text-default); - cursor: pointer; - inset-inline-end: -4px; - position: absolute; - top: 8px -} - -.headerImage__08bd2 { - background-image: url(/assets/4ed48b8404cc8dd4.svg); - background-repeat: no-repeat; - height: 150px; - margin: 12px auto; - width: 250px -} - -.titleRow__08bd2 { - align-items: center; - justify-content: flex-end; - margin-bottom: 8px -} - -.title__08bd2,.titleRow__08bd2 { - display: inline-flex -} - -.title__08bd2 { - margin-inline-end:4px} - -.textArea__08bd2 { - background: var(--input-background-default); - max-height: 80px -} - -.error__08bd2 { - margin-top: 8px -} - -.button__08bd2 { - text-transform: capitalize -} - -.modal__08bd2.gradientBorder__08bd2 { - border: none; - box-sizing: border-box -} - -.container__08bd2 { - background: radial-gradient(100% 100% at 50% 100%,var(--background-base-low) 60%,var(--transparent) 100%),linear-gradient(270deg,var(--expressive-gradient-purple-end) 0,var(--expressive-gradient-purple-start) 100%); - border-radius: inherit; - border-end-end-radius: 0; - border-end-start-radius: 0; - box-shadow: inset 0 1px 0 1px var(--border-subtle) -} - -.cancelButton__08bd2 { - margin-inline-end:8px} - -.modalFooter__08bd2 { - background-color: var(--background-base-low); - border-bottom: 1px solid var(--border-subtle); - border-inline-end:1px solid var(--border-subtle);border-inline-start: 1px solid var(--border-subtle); - padding-bottom: var(--space-24); - padding-top: var(--space-8) -} - -.popoutCard__411a9 { - padding: 12px; - width: 260px -} - -.tooltipContainer__411a9 { - max-width: 260px; - padding: 8px -} - -.content__411a9 { - flex-direction: column -} - -.content__411a9,.headerRow__411a9 { - display: flex; - gap: 8px -} - -.headerRow__411a9 { - align-items: center -} - -.titleRow__411a9 { - flex: 1; - gap: 6px; - min-width: 0 -} - -.spicinessRow__411a9,.titleRow__411a9 { - align-items: center; - display: flex -} - -.spicinessRow__411a9 { - gap: 4px -} - -.kindIcon__411a9 { - color: var(--white) -} - -.channelIcon__411a9,.spicinessIconMild__411a9 { - color: var(--status-positive) -} - -.spicinessIconSpicy__411a9 { - color: var(--status-warning) -} - -.spicinessIconUnhinged__411a9 { - color: var(--status-danger) -} - -.guildPart__411a9,.locationRow__411a9 { - align-items: center; - display: flex; - gap: 6px; - min-width: 0 -} - -.guildPart__411a9 { - flex: 0 1 auto; - max-width: 45% -} - -.guildIcon__411a9 { - flex: 0 0 auto -} - -.guildNameTruncated__411a9 { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.separatorIcon__411a9 { - color: var(--text-muted); - flex: 0 0 auto -} - -.channelPart__411a9 { - align-items: center; - display: flex; - flex: 1 1 auto; - gap: 6px; - min-width: 0 -} - -.channelNameTruncated__411a9 { - flex: 1; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.footer__411a9 { - padding-top: 4px -} - -@keyframes completedShine__411a9 { - 0% { - background-position: 0 50% - } - - to { - background-position: 200% 50% - } -} - -.completedShinyText__411a9 { - background: linear-gradient(90deg,var(--status-positive) 0,var(--white) 25%,var(--status-positive) 50%,var(--white) 75%,var(--status-positive) 100%); - background-clip: text; - -webkit-background-clip: text; - background-position: 0 50%; - background-size: 200% 100%; - color: transparent; - display: inline-block; - -webkit-text-fill-color: transparent; - animation: completedShine__411a9 1.8s linear infinite -} - -@media (prefers-reduced-motion:reduce) { - .completedShinyText__411a9 { - animation: none; - background-position: 100% 50% - } -} - -@keyframes placeholderPulse__27cc6 { - 0% { - opacity: .5 - } - - 50% { - opacity: 1 - } - - to { - opacity: .5 - } -} - -.multiplePlaceholder__27cc6,.placeholder__27cc6 { - box-sizing: border-box; - cursor: pointer; - overflow: hidden -} - -.multiplePlaceholderAnimated__27cc6,.placeholderAnimated__27cc6 { - animation: placeholderPulse__27cc6 3s ease-in-out infinite -} - -.placeholder__27cc6 { - align-items: center; - display: flex; - height: 44px -} - -.mulitplePlaceholderUsername__27cc6,.placeholderUsername__27cc6 { - border-radius: 8px; - height: 14px -} - -.mulitplePlaceholderUsername__27cc6 { - margin-top: 12px -} - -.avatarSmall__27cc6 { - height: 32px; - width: 32px -} - -.avatarLarge__27cc6 { - height: 40px; - width: 40px -} - -.placeholderAvatar__27cc6 { - border-radius: 50% -} - -.mulitplePlaceholderUsername__27cc6,.placeholderAvatar__27cc6,.placeholderUsername__27cc6 { - background-color: var(--background-base-low) -} - -.avatarMasked__27cc6 { - margin-inline-end:-6px;-webkit-mask-image: url(/assets/1f9b76e0279b91a4.svg); - mask-image: url(/assets/1f9b76e0279b91a4.svg); - -webkit-mask-position: 0 0; - mask-position: 0 0; - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-size: 100% 100%; - mask-size: 100% 100% -} - -.container__91a9d { - border-radius: var(--radius-sm); - box-sizing: border-box; - color: var(--channels-default); - display: block; - padding: 1px 0; - transition: none -} - -.container__91a9d .muted__91a9d { - color: var(--text-muted) -} - -.childContainer__91a9d { - border-radius: inherit; - padding-block:0;padding-inline:var(--space-xs) 8px} - -.childContainer__91a9d.nameplated__91a9d { - padding-inline-end: 32px; - position: relative -} - -.muted__91a9d .avatar__91a9d { - opacity: .3 -} - -.clickable__91a9d.container__91a9d:hover,.clickable__91a9d.container__91a9d:hover .muted__91a9d { - color: var(--interactive-text-hover) -} - -.clickable__91a9d.container__91a9d:hover .avatar__91a9d { - opacity: 1 -} - -.clickable__91a9d.container__91a9d:active,.highlighted__91a9d.container__91a9d,.selected__91a9d.container__91a9d { - color: var(--interactive-text-active) -} - -.layout__91a9d { - align-items: center; - border-radius: inherit; - display: flex; - flex: 0 1 auto; - height: 42px; - max-width: 100%; - min-width: 0; - position: relative; - width: -moz-fit-content; - width: fit-content; - z-index: 1 -} - -.wrappedLayout__91a9d { - height: auto; - padding: 5px 8px -} - -.clickable__91a9d:hover .childContainer__91a9d { - background-color: var(--interactive-background-hover); - cursor: pointer -} - -.clickable__91a9d:active .childContainer__91a9d { - background-color: var(--interactive-background-active) -} - -.selected__91a9d .childContainer__91a9d { - background-color: var(--interactive-background-selected) -} - -.avatar__91a9d { - align-items: center; - display: flex; - flex: 0 0 auto; - height: 32px; - justify-content: center; - margin-inline-end:12px;width: 32px -} - -.content__91a9d { - flex: 1 1 auto; - min-width: 0; - text-overflow: ellipsis; - white-space: nowrap -} - -.name__91a9d { - display: flex; - flex: 0 1 auto; - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px; - min-width: 0 -} - -.wrappedName__91a9d { - overflow: none; - white-space: normal -} - -.nameAndDecorators__91a9d { - align-items: center; - display: flex; - justify-content: flex-start -} - -.subText__91a9d { - margin-top: -2px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.subText__91a9d:empty { - display: none -} - -.children__91a9d { - flex: 0 0 auto; - margin-inline-start:8px} - -.fallback_b789ab { - align-items: center; - background-color: #423d80; - box-sizing: border-box; - display: flex; - flex-direction: column; - height: 100%; - padding: 12px; - text-align: center -} - -.spacer_b789ab { - flex: 1 -} - -.container_e0f8ae { - background-color: var(--background-base-low); - border: 1px solid var(--border-subtle); - border-radius: 12px; - display: flex; - flex-direction: column; - gap: 16px; - padding: 16px -} - -.headerContainer_e0f8ae { - border-bottom: 1px solid var(--border-subtle); - display: flex; - justify-content: space-between; - padding-bottom: 12px -} - -.headerContent_e0f8ae { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px -} - -.headerTextContainer_e0f8ae { - display: flex; - flex-direction: column; - gap: 4px -} - -.gameGrid_e0f8ae { - display: grid; - gap: 16px; - grid-template-columns: repeat(4,1fr) -} - -.gameGrid_e0f8ae:has(.gameClickable_e0f8ae:nth-of-type(5):last-of-type),.gameGrid_e0f8ae:has(.placeholderArt_e0f8ae:nth-of-type(5):last-of-type) { - grid-template-columns: repeat(5,1fr) -} - -.gameGrid_e0f8ae:has(.gameClickable_e0f8ae:nth-of-type(9)),.gameGrid_e0f8ae:has(.placeholderArt_e0f8ae:nth-of-type(9)) { - grid-template-columns: repeat(5,1fr) -} - -.gameClickable_e0f8ae { - cursor: pointer; - opacity: 1; - perspective: 600px; - transition: opacity .2s ease -} - -.gameGrid_e0f8ae:hover .gameClickable_e0f8ae { - opacity: .5 -} - -.gameGrid_e0f8ae:hover .gameClickable_e0f8ae:hover { - opacity: 1 -} - -.coverArtContainer_e0f8ae { - border-radius: 8px; - position: relative; - transition: box-shadow .2s ease -} - -.full-motion .coverArtContainer_e0f8ae { - transition: box-shadow .2s ease,transform .1s ease-out -} - -.gameGrid_e0f8ae:hover .gameClickable_e0f8ae:hover .coverArtContainer_e0f8ae { - box-shadow: 0 0 25px 0 rgba(145,101,255,.25) -} - -.coverArtContainer_e0f8ae:after { - border-radius: 8px; - content: ""; - inset: 0; - pointer-events: none; - position: absolute; - transition: box-shadow .2s ease -} - -.gameGrid_e0f8ae:hover .gameClickable_e0f8ae:hover .coverArtContainer_e0f8ae:after { - box-shadow: inset 0 0 67px 0 rgba(145,101,255,.25) -} - -.coverArt_e0f8ae { - border: 1px solid var(--border-subtle); - border-radius: 8px; - display: block; - height: 94px; - width: 73px -} - -.closeButton_e0f8ae { - color: var(--interactive-text-default); - cursor: pointer -} - -.placeholderArt_e0f8ae { - background-color: var(--background-surface-high) -} - -.closeButtonContainer_e0f8ae { - display: flex; - justify-content: flex-end -} - -.errorContainer_e0f8ae { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - min-width: 364px -} - -.errorImage_e0f8ae { - margin-bottom: 16px -} - -.instructionsLink_e0f8ae { - display: inline-block; - font-size: inherit -} - -.instructionsLink_e0f8ae:hover { - text-decoration: underline -} - -.gameTile_e0f8ae { - height: 36px; - width: 36px -} - -.helper_fd2e49 { - align-items: center; - background: var(--background-base-lowest); - border: 1px solid hsla(0,0%,100%,.04); - border-radius: 8px; - box-shadow: var(--shadow-low); - display: flex; - flex-direction: row; - padding: 12px -} - -.infoFilledIcon_fd2e49 { - color: var(--text-default); - margin-inline-end:8px} - -@value transitionDuration 200ms;div.modalRoot { - border-radius: 12px; - position: relative -} - -div.modalHeader_c28af8 { - overflow-x: visible -} - -.closeBtn_c28af8 { - inset-inline-end: 12px; - position: absolute; - top: 12px -} - -.asset_c28af8 { - height: auto; - margin-bottom: 24px; - width: 205px -} - -.modalContent_c28af8 { - display: flex; - flex-direction: column; - gap: 6px; - padding: 0 16px 24px -} - -.completedModalContent_c28af8 { - align-items: center; - display: flex; - flex-direction: column; - gap: 4px; - padding: 16px 24px -} - -.choiceContainer_c28af8 { - align-items: center; - background-color: var(--background-base-lower); - border: 1px solid var(--border-muted); - border-radius: 8px; - box-sizing: border-box; - cursor: pointer; - display: flex; - justify-content: space-between; - padding: 12px -} - -.full-motion .choiceContainer_c28af8 { - transition: background-color .2s ease-in-out -} - -.choiceContainer_c28af8:hover { - background-color: var(--message-background-hover) -} - -.closeButtonContainer_c28af8 { - border-radius: 3px; - overflow: hidden; - position: relative -} - -.progressOverlay_c28af8 { - background-color: var(--background-base-lower); - inset: 0; - opacity: .2; - pointer-events: none; - position: absolute; - z-index: 10 -} - -.modalHeader_c28af8 { - padding-bottom: 12px -} - -.image__7d7b6 { - border-radius: inherit; - height: 100%; - inset-inline-start: 0; - -o-object-fit: cover; - object-fit: cover; - position: absolute; - top: 0; - width: 100% -} - -.nitroRewardTileAssetContainer_a92e89 { - background-image: linear-gradient(135deg,var(--premium-tier-1-dark-blue-for-gradients) 20%,var(--premium-perk-pink)); - display: flex; - place-content: center; - text-align: center -} - -.nitroRewardTileAsset_a92e89 { - filter: drop-shadow(0 1px 2px rgb(0 0 0/.4)); - height: auto; - margin-top: 2px; - margin-inline-end:10%;width: 70% -} - -.questRewardTile__28141 { - border-radius: 4px; - box-shadow: 6px 12px 32px rgba(0,0,0,.24); - height: 64px; - position: relative; - width: 64px -} - -.rewardHighlight__28141 { - border-radius: 8px; - overflow: hidden -} - -.rewardHighlight__28141:after { - border: 1px solid var(--opacity-white-16); - border-radius: inherit -} - -.borderOverlay__28141:before,.rewardHighlight__28141:after { - box-sizing: border-box; - content: ""; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.borderOverlay__28141:before { - border: 2px solid var(--white); - border-radius: inherit; - -webkit-mask-image: linear-gradient(-45deg,transparent 50%,#000); - mask-image: linear-gradient(-45deg,transparent 50%,#000); - z-index: 1 -} - -.questRewardTileInteractive__28141 { - cursor: pointer -} - -.questRewardTileAsset__28141 { - border-radius: inherit; - display: block; - height: 100%; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.questRewardTileAssetLazyVideo__28141 { - position: absolute -} - -.questRewardTileAssetStatic__28141 { - pointer-events: none -} - -.questRewardTileDetailsLearnMore__28141 { - align-items: center; - background: rgba(0,0,0,.5); - border-radius: inherit; - bottom: 0; - display: flex; - flex-direction: column; - inset-inline: 0; - justify-content: center; - opacity: 0; - position: absolute; - text-align: center; - top: 0; - transition: opacity .125s; - z-index: 0 -} - -.questRewardTileDetailsLearnMore__28141 p { - flex: 0 0 auto; - margin: 0; - width: 100% -} - -.questRewardTile__28141:focus .questRewardTileDetailsLearnMore__28141,.questRewardTile__28141:hover .questRewardTileDetailsLearnMore__28141 { - opacity: 1 -} - -.hideLearnMore__28141 { - cursor: default -} - -@keyframes Shine__28141 { - 0% { - transform: translate3d(-100%,0,0) - } - - to { - transform: translate3d(210%,0,0) - } -} - -.shine__28141 { - border-radius: inherit; - color: var(--opacity-white-8); - height: 100%; - inset-inline-start: 0; - pointer-events: none; - top: 0; - width: 100% -} - -.reduce-motion .shine__28141 { - opacity: 0 -} - -.full-motion .shine__28141 { - animation-duration: 2s; - animation-iteration-count: infinite; - animation-name: Shine__28141; - animation-timing-function: ease-in-out -} - -.imageVideoOverlay__28141 { - border-radius: inherit; - height: 100%; - inset-inline-start: 0; - -o-object-fit: cover; - object-fit: cover; - position: absolute; - top: 0; - width: 100% -} - -.wrapper__0bd12 { - background: var(--background-gradient-chat,var(--background-base-lower)) -} - -.container__0bd12 { - background-color: var(--interactive-background-selected); - display: flex; - flex-direction: column; - gap: 12px; - padding: 10px -} - -.container__0bd12 .top__0bd12 { - display: flex; - flex-direction: row; - justify-content: space-between -} - -.container__0bd12 .left__0bd12 { - display: flex; - flex-direction: column; - gap: 4px -} - -.container__0bd12 .left__0bd12 .help__0bd12 { - margin-inline-end:8px} - -.container__0bd12 .left__0bd12 .help__0bd12:hover { - cursor: pointer -} - -.container__0bd12 .left__0bd12 .help__0bd12 .helpText__0bd12 { - display: inline; - margin-inline-end:3px} - -.container__0bd12 .left__0bd12 .help__0bd12 .helpIcon__0bd12 { - vertical-align: middle -} - -.container__0bd12 .right__0bd12 .imgWrapper__0bd12 { - height: 38px; - position: relative; - width: 38px -} - -.container__0bd12 .right__0bd12 .imgWrapper__0bd12 .imgUnderlay__0bd12 { - background: linear-gradient(155deg,var(--teal-230) 11.08%,var(--yellow-260) 88.92%); - border-radius: 50%; - filter: blur(20px); - inset: 2.5px; - position: absolute -} - -.assetTile__0bd12 { - border-radius: 8px; - height: 100%; - position: relative; - width: 100%; - z-index: 2 -} - -.member__5d473 { - margin-inline-start:8px;max-width: calc(var(--custom-member-list-width) - 16px); - position: relative -} - -.memberInner__5d473 { - box-sizing: border-box; - height: auto; - padding: var(--space-xxs) 0 var(--space-xxs) -} - -.offline__5d473 { - opacity: .3 -} - -.offline__5d473:hover { - opacity: 1 -} - -.icon__5d473 { - flex: 0 0 auto; - flex-shrink: 0; - height: 14px; - position: relative; - width: 14px -} - -.ownerIcon__5d473 { - color: var(--text-feedback-warning); - margin-inline-start:4px} - -.lostPermission__5d473 { - text-decoration: line-through -} - -.premiumIcon__5d473 { - margin-inline-start:3px;top: 1px -} - -.placeholder__5d473 { - padding: 4px 8px -} - -.botTag__5d473 { - flex: 0 0 auto; - margin-inline-start:4px} - -.username__5d473 { - align-items: center; - display: flex; - flex: 0 1 auto; - margin-inline-end:auto;max-width: 100%; - min-width: 0 -} - -.name__5d473 { - flex: 1 1 auto; - white-space: nowrap -} - -span.clanTag__5d473 { - margin-top: 1px; - margin-inline-start:4px;margin-bottom: 1px -} - -.flatBottom__5d473 { - border-end-end-radius: 0; - border-end-start-radius: 0 -} - -.heading-sm\/normal__13cf1 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.heading-sm\/normal__13cf1.fontScaling__13cf1 { - font-size: .875rem -} - -.heading-sm\/medium__13cf1 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.heading-sm\/medium__13cf1.fontScaling__13cf1 { - font-size: .875rem -} - -.heading-sm\/semibold__13cf1 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.heading-sm\/semibold__13cf1.fontScaling__13cf1 { - font-size: .875rem -} - -.heading-sm\/bold__13cf1 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.heading-sm\/bold__13cf1.fontScaling__13cf1 { - font-size: .875rem -} - -.heading-sm\/extrabold__13cf1 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 800; - line-height: 1.2857142857142858 -} - -.heading-sm\/extrabold__13cf1.fontScaling__13cf1 { - font-size: .875rem -} - -.heading-md\/normal__13cf1 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.heading-md\/normal__13cf1.fontScaling__13cf1 { - font-size: 1rem -} - -.heading-md\/medium__13cf1 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.heading-md\/medium__13cf1.fontScaling__13cf1 { - font-size: 1rem -} - -.heading-md\/semibold__13cf1 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.heading-md\/semibold__13cf1.fontScaling__13cf1 { - font-size: 1rem -} - -.heading-md\/bold__13cf1 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.heading-md\/bold__13cf1.fontScaling__13cf1 { - font-size: 1rem -} - -.heading-md\/extrabold__13cf1 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 800; - line-height: 1.25 -} - -.heading-md\/extrabold__13cf1.fontScaling__13cf1 { - font-size: 1rem -} - -.heading-lg\/normal__13cf1 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.heading-lg\/normal__13cf1.fontScaling__13cf1 { - font-size: 1.25rem -} - -.heading-lg\/medium__13cf1 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.heading-lg\/medium__13cf1.fontScaling__13cf1 { - font-size: 1.25rem -} - -.heading-lg\/semibold__13cf1 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.heading-lg\/semibold__13cf1.fontScaling__13cf1 { - font-size: 1.25rem -} - -.heading-lg\/bold__13cf1 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.heading-lg\/bold__13cf1.fontScaling__13cf1 { - font-size: 1.25rem -} - -.heading-lg\/extrabold__13cf1 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 800; - line-height: 1.2 -} - -.heading-lg\/extrabold__13cf1.fontScaling__13cf1 { - font-size: 1.25rem -} - -.heading-xl\/normal__13cf1 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xl\/normal__13cf1.fontScaling__13cf1 { - font-size: 1.5rem -} - -.heading-xl\/medium__13cf1 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xl\/medium__13cf1.fontScaling__13cf1 { - font-size: 1.5rem -} - -.heading-xl\/semibold__13cf1 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xl\/semibold__13cf1.fontScaling__13cf1 { - font-size: 1.5rem -} - -.heading-xl\/bold__13cf1 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xl\/bold__13cf1.fontScaling__13cf1 { - font-size: 1.5rem -} - -.heading-xl\/extrabold__13cf1 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xl\/extrabold__13cf1.fontScaling__13cf1 { - font-size: 1.5rem -} - -.heading-xxl\/normal__13cf1 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xxl\/normal__13cf1.fontScaling__13cf1 { - font-size: 2rem -} - -.heading-xxl\/medium__13cf1 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xxl\/medium__13cf1.fontScaling__13cf1 { - font-size: 2rem -} - -.heading-xxl\/semibold__13cf1 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xxl\/semibold__13cf1.fontScaling__13cf1 { - font-size: 2rem -} - -.heading-xxl\/bold__13cf1 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xxl\/bold__13cf1.fontScaling__13cf1 { - font-size: 2rem -} - -.heading-xxl\/extrabold__13cf1 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xxl\/extrabold__13cf1.fontScaling__13cf1 { - font-size: 2rem -} - -.eyebrow__13cf1 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - letter-spacing: .02em; - line-height: 1.3333333333333333; - text-transform: uppercase -} - -.eyebrow__13cf1.fontScaling__13cf1 { - font-size: .75rem -} - -.heading-deprecated-12\/normal__13cf1 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/normal__13cf1.fontScaling__13cf1 { - font-size: .75rem -} - -.heading-deprecated-12\/medium__13cf1 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/medium__13cf1.fontScaling__13cf1 { - font-size: .75rem -} - -.heading-deprecated-12\/semibold__13cf1 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/semibold__13cf1.fontScaling__13cf1 { - font-size: .75rem -} - -.heading-deprecated-12\/bold__13cf1 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/bold__13cf1.fontScaling__13cf1 { - font-size: .75rem -} - -.heading-deprecated-12\/extrabold__13cf1 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 800; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/extrabold__13cf1.fontScaling__13cf1 { - font-size: .75rem -} - -.redesign\/heading-18\/bold__13cf1 { - font-family: var(--font-display); - font-size: 18px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/heading-18\/bold__13cf1.fontScaling__13cf1 { - font-size: 1.125rem -} - -.text-xxs\/normal__13cf1 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 400; - line-height: 1.2 -} - -.text-xxs\/normal__13cf1.fontScaling__13cf1 { - font-size: .625rem -} - -.text-xxs\/medium__13cf1 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 500; - line-height: 1.2 -} - -.text-xxs\/medium__13cf1.fontScaling__13cf1 { - font-size: .625rem -} - -.text-xxs\/semibold__13cf1 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 600; - line-height: 1.2 -} - -.text-xxs\/semibold__13cf1.fontScaling__13cf1 { - font-size: .625rem -} - -.text-xxs\/bold__13cf1 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 700; - line-height: 1.2 -} - -.text-xxs\/bold__13cf1.fontScaling__13cf1 { - font-size: .625rem -} - -.text-xs\/normal__13cf1 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.text-xs\/normal__13cf1.fontScaling__13cf1 { - font-size: .75rem -} - -.text-xs\/medium__13cf1 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.text-xs\/medium__13cf1.fontScaling__13cf1 { - font-size: .75rem -} - -.text-xs\/semibold__13cf1 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.text-xs\/semibold__13cf1.fontScaling__13cf1 { - font-size: .75rem -} - -.text-xs\/bold__13cf1 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.text-xs\/bold__13cf1.fontScaling__13cf1 { - font-size: .75rem -} - -.text-sm\/normal__13cf1 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.text-sm\/normal__13cf1.fontScaling__13cf1 { - font-size: .875rem -} - -.text-sm\/medium__13cf1 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.text-sm\/medium__13cf1.fontScaling__13cf1 { - font-size: .875rem -} - -.text-sm\/semibold__13cf1 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.text-sm\/semibold__13cf1.fontScaling__13cf1 { - font-size: .875rem -} - -.text-sm\/bold__13cf1 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.text-sm\/bold__13cf1.fontScaling__13cf1 { - font-size: .875rem -} - -.text-md\/normal__13cf1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.text-md\/normal__13cf1.fontScaling__13cf1 { - font-size: 1rem -} - -.text-md\/medium__13cf1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.text-md\/medium__13cf1.fontScaling__13cf1 { - font-size: 1rem -} - -.text-md\/semibold__13cf1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.text-md\/semibold__13cf1.fontScaling__13cf1 { - font-size: 1rem -} - -.text-md\/bold__13cf1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.text-md\/bold__13cf1.fontScaling__13cf1 { - font-size: 1rem -} - -.text-lg\/normal__13cf1 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.text-lg\/normal__13cf1.fontScaling__13cf1 { - font-size: 1.25rem -} - -.text-lg\/medium__13cf1 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.text-lg\/medium__13cf1.fontScaling__13cf1 { - font-size: 1.25rem -} - -.text-lg\/semibold__13cf1 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.text-lg\/semibold__13cf1.fontScaling__13cf1 { - font-size: 1.25rem -} - -.text-lg\/bold__13cf1 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.text-lg\/bold__13cf1.fontScaling__13cf1 { - font-size: 1.25rem -} - -.redesign\/message-preview\/normal__13cf1 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/normal__13cf1.fontScaling__13cf1 { - font-size: .9375rem -} - -.redesign\/message-preview\/medium__13cf1 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/medium__13cf1.fontScaling__13cf1 { - font-size: .9375rem -} - -.redesign\/message-preview\/semibold__13cf1 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/semibold__13cf1.fontScaling__13cf1 { - font-size: .9375rem -} - -.redesign\/message-preview\/bold__13cf1 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/bold__13cf1.fontScaling__13cf1 { - font-size: .9375rem -} - -.redesign\/channel-title\/normal__13cf1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.375 -} - -.redesign\/channel-title\/normal__13cf1.fontScaling__13cf1 { - font-size: 1rem -} - -.redesign\/channel-title\/medium__13cf1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.375 -} - -.redesign\/channel-title\/medium__13cf1.fontScaling__13cf1 { - font-size: 1rem -} - -.redesign\/channel-title\/semibold__13cf1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.375 -} - -.redesign\/channel-title\/semibold__13cf1.fontScaling__13cf1 { - font-size: 1rem -} - -.redesign\/channel-title\/bold__13cf1 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.375 -} - -.redesign\/channel-title\/bold__13cf1.fontScaling__13cf1 { - font-size: 1rem -} - -.display-sm__13cf1 { - font-family: var(--font-headline); - font-size: 20px; - font-weight: 800; - line-height: 1 -} - -.display-sm__13cf1.fontScaling__13cf1 { - font-size: 1.25rem -} - -.display-md__13cf1 { - font-family: var(--font-headline); - font-size: 34px; - font-weight: 800; - line-height: 1.0588235294117647 -} - -.display-md__13cf1.fontScaling__13cf1 { - font-size: 2.125rem -} - -.display-lg__13cf1 { - font-family: var(--font-headline); - font-size: 44px; - font-weight: 800; - line-height: .9545454545454546 -} - -.display-lg__13cf1.fontScaling__13cf1 { - font-size: 2.75rem -} - -.code__13cf1 { - font-family: var(--font-code); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.code__13cf1.fontScaling__13cf1 { - font-size: .75rem -} - -.container__13cf1 { - box-sizing: border-box; - color: var(--channels-default); - flex: 1 1 auto; - text-overflow: ellipsis; - white-space: nowrap -} - -.header__13cf1 { -} - -.text__13cf1 { - font-family: var(--font-display); - font-size: 12px; - font-weight: var(--font-weight-semibold); - letter-spacing: .02em; - line-height: 16px; - text-transform: uppercase -} - -.headerContainer__095fe { - cursor: pointer; - justify-content: space-between -} - -.header__095fe,.headerContainer__095fe { - align-items: center; - display: flex -} - -.header__095fe { - gap: 4px -} - -.toggleExpandIcon__095fe { - height: 1lh -} - -@keyframes placeholderPulse_c8ffbb { - 0% { - opacity: .5 - } - - 50% { - opacity: 1 - } - - to { - opacity: .5 - } -} - -:root { - --custom-channel-members-bg: var(--background-base-lower) -} - -.container_c8ffbb { - background: var(--background-gradient-chat,var(--custom-channel-members-bg)); - border-inline-start:1px solid var(--border-subtle);display: flex; - flex-direction: column; - height: 100% -} - -.refresh-fast-follow-distinct-borders .container_c8ffbb { - border-inline-start-color:var(--app-frame-border)} - -.membersWrap_c8ffbb { - display: flex; - height: 100%; - max-height: 100%; - min-width: var(--custom-member-list-width); - position: relative -} - -.members_c8ffbb { - background: var(--background-gradient-chat,var(--custom-channel-members-bg)); - flex: 0 0 auto; - height: auto; - padding: 0 0 10px -} - -.members_c8ffbb,.membersListNotices_c8ffbb { - width: var(--custom-member-list-width) -} - -.member_c8ffbb { - background: var(--background-gradient-chat,var(--custom-channel-members-bg)) -} - -.membersGroup_c8ffbb { - height: auto; - padding-block:var(--space-lg) var(--space-xxs);padding-inline: var(--space-md) var(--space-xxs) -} - -.membersGroupHeader_c8ffbb { - display: flex; - flex-direction: row -} - -.membersGroupName_c8ffbb { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.hiddenMembers_c8ffbb { - justify-content: center -} - -.hiddenText_c8ffbb { - align-self: center -} - -.memberGroupsPlaceholder_c8ffbb { - animation: placeholderPulse_c8ffbb 3s ease-in-out infinite; - background-color: var(--background-base-low); - border-radius: 8px; - height: 12px; - width: 40% -} - -.addMembersButton_c8ffbb { - padding-top: 8px; - padding-inline-start:8px} - -.addMembersIcon_c8ffbb { - align-items: center; - background-color: var(--background-mod-normal); - border-radius: 50%; - display: flex; - height: 32px; - justify-content: center; - width: 32px -} - -.roleIcon_c8ffbb { - margin-inline-end:4px;vertical-align: top -} - -.fullWidth_c8ffbb { - width: 100vw -} - -.appIconWrapper_c8ffbb { - align-items: center; - background-color: var(--background-mod-normal); - border-radius: 16px; - display: flex; - height: 32px; - justify-content: center; - width: 32px -} - -.enable-forced-colors .container_c8ffbb,.enable-forced-colors .membersWrap_c8ffbb { - border-inline-start:2px solid CanvasText} - -.enable-forced-colors .container_c8ffbb .membersWrap_c8ffbb { - border-inline-start:none} - -@media (max-width: 485px) { - .membersWrap_c8ffbb { - display:flex; - height: 100%; - position: relative - } - - .members_c8ffbb,.membersListNotices_c8ffbb,.membersWrap_c8ffbb { - min-width: 100% - } -} - -.roleIcon_ee71ee { - display: inline-block; - -o-object-fit: contain; - object-fit: contain; - overflow: hidden -} - -.roleIcon_ee71ee:before { - background-color: var(--background-mod-subtle); - border-radius: 50%; - content: ""; - display: block; - height: 100%; - width: 100% -} - -.clickable_ee71ee { - cursor: pointer -} - -.content__22234 { - border-radius: 8px; - box-sizing: border-box; - padding: var(--custom-tutorial-popout-padding-content) -} - -.contentNoMedia__22234,.contentWithMedia__22234 { -} - -.contentNarrowWithMedia__22234 { - width: 280px -} - -.contentNarrowNoMedia__22234 { - width: 280px -} - -.contentWideWithMedia__22234 { - width: 300px -} - -.contentWideNoMedia__22234 { - width: 300px -} - -&.content__22234,.content__22234 { - background-color: var(--background-surface-highest); - box-shadow: var(--shadow-high) -} - -&.contentWithMedia__22234,.contentWithMedia__22234 { - border: 1px solid var(--border-subtle) -} - -&.leftArrow__22234:before,.leftArrow__22234:before { - border-inline-end-color:var(--background-surface-highest)} - -&.rightArrow__22234: before,.rightArrow__22234:before { - border-inline-start-color:var(--background-surface-highest) -} - -&.contentNarrowMedia__22234.topArrow__22234:before,&.contentWideNoMedia__22234.topArrow__22234:before,&.topArrow__22234:before,.contentNarrowNoMedia__22234.topArrow__22234:before,.contentWideNoMedia__22234.topArrow__22234:before,.topArrow__22234:before { - border-bottom-color: var(--background-surface-highest) -} - -.mediaContainer__22234 { - position: absolute; - top: calc(var(--custom-tutorial-popout-height-media-approx)/2*-1); - width: calc(100% - var(--custom-tutorial-popout-padding-content)*2) -} - -.background__22234 { - position: absolute; - top: 0; - inset-inline: 0 -} - -.title__22234 { - font-size: 16px; - font-weight: var(--font-weight-semibold); - line-height: 20px; - margin-bottom: 4px -} - -&.title__22234,.title__22234 { - color: var(--text-strong) -} - -.titleCenter__22234 { - text-align: center -} - -.titleLeft__22234 { - text-align: start -} - -.body__22234 p,div.body__22234,p.body__22234 { - color: var(--text-default); - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 18px; - margin-bottom: 16px; - margin-top: 0 -} - -strong { - font-weight: var(--font-weight-semibold) -} - -.bodyCenter__22234 { - text-align: center -} - -.bodyLeft__22234 { - text-align: start -} - -.popoutRoot__22234 { - position: relative -} - -.popoutRoot__22234.arrowAlignmentMiddle__22234:before,.popoutRoot__22234.arrowAlignmentTop__22234:before { - border: 8px solid transparent; - content: " "; - height: 0; - pointer-events: none; - position: absolute; - width: 0 -} - -.popoutRoot__22234.top__22234.arrowAlignmentMiddle__22234:before,.popoutRoot__22234.top__22234.arrowAlignmentTop__22234:before { - border-top-color: var(--background-surface-highest); - inset-inline-start: 50%; - top: 100%; - transform: translateX(-50%) -} - -.popoutRoot__22234.right__22234.arrowAlignmentTop__22234:before { - border-inline-end-color:var(--background-surface-highest);inset-inline-end: 100%; - top: 10px -} - -.popoutRoot__22234.right__22234.arrowAlignmentMiddle__22234:before { - border-inline-end-color:var(--background-surface-highest);inset-inline-end: 100%; - margin-top: -16px; - top: 50% -} - -.popoutRoot__22234.left__22234.arrowAlignmentTop__22234:before { - border-inline-start-color:var(--background-surface-highest);inset-inline-start: 100%; - top: 10px -} - -.popoutRoot__22234.left__22234.arrowAlignmentMiddle__22234:before { - border-inline-start-color:var(--background-surface-highest);inset-inline-start: 100%; - margin-top: -16px; - top: 50% -} - -.popoutRoot__22234.bottom__22234.arrowAlignmentMiddle__22234:before,.popoutRoot__22234.bottom__22234.arrowAlignmentTop__22234:before { - border-bottom-color: var(--background-surface-highest); - bottom: 100%; - inset-inline-start: 50%; - transform: translateX(-50%) -} - -.container__5662e { - position: relative; - top: -10px -} - -.container__3f123 { - position: relative; - top: 10px -} - -.container_ee658d { - position: relative -} - -.personAvatar_ee658d { - margin-top: 20px -} - -.guildAvatar_ee658d { - margin-top: -38px; - margin-inline-start:193px} - -.illustration_ee658d { - inset-inline-start: 50px; - position: absolute; - top: 0 -} - -.avatarSizeOverride_ee658d { - height: 60px!important; - width: 60px!important -} - -.icon_dbd216 { - margin: 0 2px 4px; - vertical-align: middle -} - -.voiceConversationsTitle_dbd216 { - margin-top: 32px -} - -.writingMessagesTitle_dbd216 { - margin-top: 44px -} - -.organizeByTopicTitle_dbd216 { - margin-top: 40px -} - -.voiceChannelsTitle_dbd216 { - margin-top: 24px -} - -.channelsTitle_dbd216 { - margin-top: 90px -} - -.indicator_ffc7aa { - display: flex; - pointer-events: auto; - position: absolute; - z-index: 999 -} - -.animationContainer_ffc7aa { - height: 36px; - position: relative; - transition: opacity .2s ease-out; - width: 36px -} - -.animationContainer_ffc7aa.animating_ffc7aa { - opacity: 1 -} - -.animationContainer_ffc7aa.notAnimating_ffc7aa { - opacity: 0 -} - -.top_ffc7aa { - background: url(/assets/c200a7504d42173d.svg) 50% no-repeat; - background-size: contain; - height: 22px; - inset-inline-start: 18.5px; - position: absolute; - top: -10px; - width: 9px -} - -.top_ffc7aa.animating_ffc7aa { - animation: exclaim-loop_ffc7aa 1.5s ease-in-out infinite forwards -} - -.top_ffc7aa.notAnimating_ffc7aa { - animation: quick-fade-out_ffc7aa .2s forwards -} - -.bottom_ffc7aa { - background: url(/assets/5c37dde2c8764c10.svg) 50% no-repeat; - background-size: contain; - height: 8px; - inset-inline-start: 14px; - position: absolute; - top: 14px; - width: 8px -} - -.bottom_ffc7aa.animating_ffc7aa { - animation: dot-loop_ffc7aa 1.5s ease-in-out infinite forwards -} - -.bottom_ffc7aa.notAnimating_ffc7aa { - animation: quick-fade-out_ffc7aa .2s forwards -} - -.innerCircle_ffc7aa { - background: url(/assets/dabf5fea0bb87bf8.svg) 50% no-repeat; - background-size: contain; - height: 30px; - inset-inline-start: 3px; - opacity: .1; - position: absolute; - top: 3px; - width: 30px -} - -.full-motion .innerCircle_ffc7aa.animating_ffc7aa { - animation: inner-circle-loop_ffc7aa 1.5s infinite; - opacity: .1 -} - -.full-motion .innerCircle_ffc7aa.animating_ffc7aa.highPriority_ffc7aa { - animation: inner-circle-loop-high-priority_ffc7aa 1.5s infinite -} - -.innerCircle_ffc7aa.notAnimating_ffc7aa { - animation: quick-fade-out_ffc7aa .2s forwards -} - -.outerCircle_ffc7aa { - background: url(/assets/fd5f7ebed980d3e1.svg) 50% no-repeat; - background-size: contain; - height: 36px; - opacity: .1; - position: absolute; - width: 36px -} - -.full-motion .outerCircle_ffc7aa.animating_ffc7aa { - animation: outer-circle-loop_ffc7aa 1.5s infinite; - opacity: 0 -} - -.full-motion .outerCircle_ffc7aa.animating_ffc7aa.highPriority_ffc7aa { - animation: outer-circle-loop-high-priority_ffc7aa 1.5s infinite -} - -.outerCircle_ffc7aa.notAnimating_ffc7aa { - animation: quick-fade-out_ffc7aa .2s forwards -} - -@keyframes exclaim-loop_ffc7aa { - 0% { - top: -10px - } - - 21% { - top: -14px - } - - 46%,51% { - top: -10px - } - - 72% { - top: -14px - } - - to { - top: -10px - } -} - -@keyframes dot-loop_ffc7aa { - 0% { - top: 14px - } - - 19% { - top: 9px - } - - 44%,49% { - top: 14px - } - - 70% { - top: 9px - } - - 95%,to { - top: 14px - } -} - -@keyframes inner-circle-loop_ffc7aa { - 0% { - opacity: .1; - transform: scale(1.1) - } - - 42% { - opacity: .25; - transform: scale(.9) - } - - to { - opacity: .1; - transform: scale(1.1) - } -} - -@keyframes outer-circle-loop_ffc7aa { - 0% { - opacity: 0; - transform: scale(1.1) - } - - 51% { - opacity: .2; - transform: scale(.9) - } - - to { - opacity: 0; - transform: scale(1.1) - } -} - -@keyframes inner-circle-loop-high-priority_ffc7aa { - 0% { - opacity: .1; - transform: scale(1.1) - } - - 42% { - opacity: .5; - transform: scale(.9) - } - - to { - opacity: .1; - transform: scale(1.1) - } -} - -@keyframes outer-circle-loop-high-priority_ffc7aa { - 0% { - opacity: 0; - transform: scale(1.1) - } - - 51% { - opacity: .4; - transform: scale(.9) - } - - to { - opacity: 0; - transform: scale(1.1) - } -} - -@keyframes quick-fade-out_ffc7aa { - to { - opacity: 0 - } -} - -.roleDot__57082 { - margin-inline:-2px 2px} - -.clickable_b66158 { - align-items: center; - cursor: pointer; - display: flex; - flex-direction: row; - gap: 4px; - max-width: 100%; - width: -moz-max-content; - width: max-content -} - -.clickable_b66158:hover { - text-decoration: underline; - text-decoration-color: var(--text-default) -} - -.clickable_b66158:focus-within .text_b66158,.clickable_b66158:hover .text_b66158 { - max-width: calc(100% - 16px) -} - -.clickable_b66158:focus-within .pencilIcon_b66158,.clickable_b66158:hover .pencilIcon_b66158 { - visibility: visible -} - -.text_b66158 { - max-width: 100%; - width: -moz-max-content; - width: max-content -} - -.pencilIcon_b66158 { - color: var(--text-subtle); - flex-shrink: 0 -} - -.pencilIcon_b66158.hidden_b66158 { - visibility: hidden -} - -.container__63ed3 { - --custom-nickname-line-height: 24px; - --custom-nickname-icon-size: 16px; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.user-profile-modal .container__63ed3,.user-profile-modal-v2 .container__63ed3 { - --custom-nickname-line-height: 30px; - --custom-nickname-icon-size: 18px -} - -.container__63ed3.bot__63ed3 { - display: flex; - flex-direction: column; - gap: 8px -} - -.clickableUsername__63ed3 { - cursor: pointer -} - -.clickableUsername__63ed3:focus-visible .nickname__63ed3,.clickableUsername__63ed3:focus-visible .userTagUsername__63ed3,.clickableUsername__63ed3:hover .nickname__63ed3,.clickableUsername__63ed3:hover .userTagUsername__63ed3 { - text-decoration: underline -} - -.nickname__63ed3 { - overflow: hidden -} - -.nickname__63ed3,.nicknameWithDisplayNameStyles__63ed3 { - max-height: calc(var(--custom-nickname-line-height)*3); - word-break: break-word -} - -.nicknameIcons__63ed3 { - align-self: flex-start; - display: flex; - gap: 2px; - padding-top: calc((var(--custom-nickname-line-height) - var(--custom-nickname-icon-size))/2) -} - -.usernameRow__63ed3 { - gap: 8px -} - -.tags__63ed3,.usernameRow__63ed3 { - align-items: center; - display: flex; - flex-direction: row -} - -.tags__63ed3 { - -moz-column-gap: 9.33px; - column-gap: 9.33px; - flex-wrap: wrap -} - -.user-profile-popout .tags__63ed3,.user-profile-sidebar .tags__63ed3 { - row-gap: 2px -} - -.user-profile-modal-v2 .tags__63ed3 { - row-gap: 4px -} - -.user-profile-modal .tags__63ed3 { - row-gap: 8px -} - -.tags__63ed3.bot__63ed3,.tags__63ed3.pronouns__63ed3 { - -moz-column-gap: 6px; - column-gap: 6px -} - -.userTag__63ed3 { - font-family: var(--text-strong); - font-size: 14px; - font-weight: var(--font-weight-normal); - line-height: 18px -} - -.pronounsTooltip__63ed3 { - max-width: 100% -} - -.pronounsText__63ed3 { - margin-inline-end:2px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.userTagUsername__63ed3 { - display: inline; - text-overflow: unset; - white-space: normal; - word-break: break-all -} - -.userTagDiscriminator__63ed3,.userTagUsername__63ed3 { - color: var(--text-strong); - vertical-align: top -} - -.dotSpacer__63ed3 { - background-color: var(--text-default); - border-radius: 50%; - height: 4px; - width: 4px -} - -.guildTagContainer__63ed3 { - border: 1px solid var(--user-profile-border); - border-radius: var(--radius-sm); - box-sizing: border-box; - height: 20px -} - -.guildTagContainer__63ed3:hover { - background-color: var(--user-profile-background-hover) -} - -.guildTag__63ed3 { - background: unset; - margin: 0 auto -} - -.discordLogo__921c5 { - background: url(/assets/bbbc3d376d38e7bc.svg) no-repeat; - display: none; - flex-shrink: 0; - height: 36px; - margin-bottom: 16px; - width: 112px -} - -.authBox__921c5 { - background-color: var(--modal-background); - border-radius: var(--radius-sm); - box-shadow: var(--legacy-elevation-high); - box-sizing: border-box; - color: var(--text-muted); - font-size: 18px; - padding: var(--custom-auth-box-auth-box-padding); - width: 480px -} - -.authBox__921c5 a { - color: var(--text-link) -} - -.authBox__921c5 a:hover { - text-decoration: underline -} - -.authBoxExpanded__921c5 { - width: 784px -} - -.centeringWrapper__921c5 { - text-align: center; - width: 100% -} - -.avatar__921c5 { - margin-bottom: 24px -} - -@media (max-width: 485px) { - .authBox__921c5 { - align-items:center; - background: linear-gradient(to left bottom,#3d4046,#1e1e23); - border-radius: 0; - display: flex; - flex-direction: column; - height: 100%; - inset: 0; - overflow: scroll; - padding: 20px 16px; - position: absolute; - width: 100% - } - - .authBox__921c5 .discordLogo__921c5 { - display: block - } - - @media (max-width: 830px) { - .authBox__921c5.authBoxExpanded__921c5 { - max-width:unset - } - } - - .authBox__921c5 .centeringWrapper__921c5 { - min-height: 540px; - position: relative - } -} - -@media (max-width: 830px) { - .authBoxExpanded__921c5 { - max-width:480px - } -} - -.is-mobile .authBox__921c5 { - align-items: center; - background: linear-gradient(to left bottom,#3d4046,#1e1e23); - border-radius: 0; - display: flex; - flex-direction: column; - height: 100%; - inset: 0; - overflow: scroll; - padding: 20px 16px; - position: absolute; - width: 100% -} - -.is-mobile .authBox__921c5 .discordLogo__921c5 { - display: block; - top: 16px -} - -@media (max-width: 830px) { - .is-mobile .authBox__921c5.authBoxExpanded__921c5 { - max-width:unset - } -} - -.is-mobile .authBox__921c5 .centeringWrapper__921c5 { - min-height: 540px; - position: relative -} - -.authBox__921c5[data-theme=light] .discordLogo__921c5 { - background: url(/assets/34120ace1e4e4d29.svg) no-repeat; - width: 130px -} - -.authBox__921c5[data-theme=dark] .discordLogo__921c5 { - background: url(/assets/bbbc3d376d38e7bc.svg) no-repeat; - width: 130px -} - -.title__921c5 { - font-weight: var(--font-weight-semibold) -} - -.subText__921c5 { - color: var(--text-muted) -} - -.subText__921c5 strong { - color: hsl(var(--primary-300-hsl)/.9); - font-weight: var(--font-weight-semibold) -} - -.pill__921c5 { - align-items: center; - display: flex -} - -.pillOnline__921c5 { - margin-inline-end:16px} - -.pillMessage__921c5 { - color: var(--text-default); - font-size: 14px; - white-space: nowrap -} - -@media (min-height: 640px) { - .pillMessage__921c5 { - font-size:16px - } -} - -.pillIcon__921c5 { - border-radius: 50%; - display: inline-block; - height: 10px; - margin-inline-end:4px;width: 10px -} - -.pillIconTotal__921c5 { - background-color: var(--text-default) -} - -.pillIconOnline__921c5 { - background-color: var(--icon-feedback-positive) -} - -.pillFlat__921c5 { - background: transparent -} - -.pillFlat__921c5 .pillIconTotal__921c5 { - background-color: var(--text-muted) -} - -.joiningAs__921c5 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - margin-top: 20px -} - -.joiningAsAvatar__921c5 { - margin-inline-start:14px} - -.joiningAsUsername__921c5 { - margin-inline-start:5px} - -.spinnerVideo__921c5 { - height: 200px; - width: 200px -} - -.image__921c5 { - height: auto; - max-height: 120px; - max-width: 186px; - pointer-events: none; - width: 100% -} - -.block__921c5 { - text-align: start; - width: 100% -} - -.button__921c5 { - font-size: 16px; - line-height: 24px -} - -.linkButton__921c5 { - display: block; - padding-inline:0} - -.inviteIcon__921c5 { - align-items: center; - display: inline-flex; - justify-content: center -} - -.inviteLargeIcon__921c5 { - margin-inline-end:0} - -.inviteSmallIcon__921c5 { - flex-shrink: 0; - margin-inline-end:8px;margin-top: 0 -} - -.downloadButtonSubtext__921c5 { - margin-top: 8px; - text-align: center -} - -.inputError__921c5,.inputError__921c5:focus,.inputError__921c5:hover { - border-color: var(--border-feedback-critical) -} - -.description__921c5 { - font-size: 12px; - margin-top: 8px -} - -.applicationDetails__94ab2 { - display: flex; - flex-direction: column; - gap: 16px; - padding-top: 24px -} - -.applicationDetails__94ab2.noPadding__94ab2 { - padding: 0 -} - -.entry__94ab2 { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px -} - -.entryIcon__94ab2 { - background-position: 50%; - background-repeat: no-repeat; - background-size: 16px 16px; - box-sizing: border-box; - flex-shrink: 0; - height: 16px; - width: 16px -} - -.entryIcon__94ab2,.entryInner__94ab2 { - color: var(--text-muted) -} - -.applicationEducation__526cc { - display: flex; - flex-direction: column; - gap: 16px -} - -.applicationEducation__526cc:not(:first-child) { - padding-top: 24px -} - -.applicationEducation__526cc:not(:last-child) { - border-bottom: 1px solid var(--border-subtle); - padding-bottom: 24px -} - -.sectionLabel__526cc { - color: var(--text-default) -} - -.entry__526cc { - align-items: center; - display: flex; - flex-direction: row; - gap: 12px -} - -.entryIcon__526cc { - color: var(--text-muted); - height: 20px; - width: 20px -} - -.entryText__526cc { - flex: 1 -} - -.scopes__29337:not(:first-child) { - padding-top: 24px -} - -.scopes__29337:not(:last-child) { - border-bottom: 1px solid var(--border-subtle); - padding-bottom: 24px -} - -.scopes__29337.noDivider__29337:not(:last-child) { - border-bottom: none; - padding-bottom: 0 -} - -.scope__29337 { - display: flex; - margin-top: 16px -} - -.scopeInner__29337 { - box-sizing: border-box; - display: flex; - flex: 1; - flex-direction: column; - justify-content: center -} - -.sectionLabel__29337 { - color: var(--text-default); - margin-bottom: 16px -} - -.fakeScopeIcon__29337,.scopeIcon__29337 { - color: var(--text-muted) -} - -.fakeScopeIcon__29337 { - opacity: .6 -} - -.icon__29337 { - height: 20px; - margin-inline-end:12px;width: 20px -} - -.botPermissions__41924 { - display: flex; - flex-direction: column; - gap: 24px -} - -.botPermissions__41924:not(:first-child) { - padding-top: 24px -} - -.botPermissions__41924:not(:last-child) { - border-bottom: 1px solid var(--border-subtle); - padding-bottom: 24px -} - -.permissionsList__41924 { - display: flex; - flex-direction: column; - gap: 12px; - justify-content: space-between -} - -.permission__41924 { - align-items: center; - display: flex; - flex: 2; - font-weight: var(--font-weight-medium); - margin: 16px 0 0 -} - -.disabledPermissionIcon__41924 { - background-color: var(--background-feedback-critical); - border-radius: 10%; - box-sizing: border-box; - height: 18px; - margin-inline-end:8px;width: 18px -} - -.icon__41924 { - color: var(--white); - height: 18px; - width: 18px -} - -.selector_c248b6 { - margin-top: var(--space-16) -} - -.header__03630 { - align-items: center; - display: flex; - flex-direction: column; - gap: 8px; - justify-content: center -} - -.currentUser__03630 { - font-weight: var(--font-weight-medium) -} - -.currentUserTag__03630 { - display: inline -} - -.currentUserDiscriminator__03630 { - color: var(--interactive-text-default); - display: inline -} - -.logoutLink__03630 { - color: var(--brand-500); - margin-inline-start:8px} - -.headerIcons__03630 { - align-items: center; - margin-bottom: 16px -} - -.ellipseGroup__03630,.headerIcons__03630 { - display: flex; - justify-content: space-between -} - -.ellipseGroup__03630 { - margin: 0 24px -} - -.ellipse__03630 { - background-color: var(--channel-icon); - border-radius: 50%; - height: 4px; - margin: 0 2px; - opacity: .1; - width: 4px -} - -.botTag__03630 { - margin-top: 4px!important; - margin-inline-start:8px} - -.avatar__03630 { - border: 1px solid var(--border-subtle); - height: 64px; - width: 64px -} - -.icon_bd6d20 { - border: 1px solid var(--border-subtle); - border-radius: var(--radius-md); - box-sizing: border-box; - flex-shrink: 0 -} - -.placeholder_bd6d20 { - background-color: var(--background-mod-subtle) -} - -.header_b1c9f2 { - align-items: center; - gap: 16px; - justify-content: center -} - -.container_b1c9f2,.header_b1c9f2 { - display: flex; - flex-direction: column -} - -.container_b1c9f2 { - gap: var(--space-24); - max-width: 400px -} - -.rows_b1c9f2 { - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - display: grid; - grid-auto-rows: 1fr -} - -.appIcon_b1c9f2 { - border-radius: var(--radius-xl); - height: 80px; - width: 80px -} - -.row_b1c9f2 { - align-items: center; - background: var(--background-base-lower); - background-color: var(--background-surface-higher); - cursor: pointer; - display: flex; - gap: 12px; - padding-inline-start:16px} - -.row_b1c9f2:hover { - background: var(--interactive-background-hover); - background-color: var(--background-surface-high) -} - -.row_b1c9f2:first-child { - border-start-end-radius: 8px; - border-start-start-radius: 8px -} - -.row_b1c9f2:last-child { - border-end-end-radius: 8px; - border-end-start-radius: 8px -} - -.row_b1c9f2:not(:last-child) .rowInner_b1c9f2 { - border-bottom: 1px solid var(--interactive-background-hover); - border-color: var(--border-subtle) -} - -.rowInner_b1c9f2 { - align-items: center; - display: flex; - flex: 1; - gap: 8px; - padding-block:8px;padding-inline:0 16px} - -.rowDetails_b1c9f2 { - display: flex; - flex: 1; - flex-direction: column; - gap: 2px -} - -.leftIcon_b1c9f2 { - padding: 4px -} - -.leftIcon_b1c9f2,.rightIcon_b1c9f2 { - color: var(--interactive-text-default) -} - -.learnMore_b1c9f2 { - margin: 16px 0; - text-align: center -} - -.detailsContainer_b1c9f2 { - display: flex; - flex-direction: column; - gap: var(--custom-disclosure-spacing) -} - -.overviewContainerNoVideo_b1c9f2 { - background-color: unset; - border-radius: var(--radius-sm); - display: flex; - flex-direction: column; - gap: var(--space-8); - padding: var(--space-12) -} - -.titleContainer_b1c9f2 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-8) -} - -.partnerLabelContainer_b1c9f2 { - background-color: var(--interactive-background-active); - border-radius: var(--radius-lg); - justify-content: center; - padding: 2px var(--space-8) -} - -.descriptionContainer_b1c9f2 { - display: flex; - flex-direction: column; - gap: var(--space-4) -} - -.expandableDescriptionClickable_b1c9f2 { - cursor: pointer; - display: flex; - flex-direction: row; - gap: var(--space-4) -} - -.overflowHidden_b1c9f2 { - overflow: hidden -} - -.oauth2PageWrapper__647f0 { - align-items: center; - bottom: 0; - display: flex; - flex-direction: column; - position: absolute; - top: 0; - inset-inline: 0; - justify-content: center; - min-height: 0; - padding-bottom: 40px; - padding-top: 40px -} - -.oauth2PageContent__647f0 { - align-items: center; - background: var(--background-base-low); - border-radius: 12px; - box-shadow: var(--legacy-elevation-high); - display: flex; - height: auto; - max-height: calc(100% - 80px); - max-width: 480px; - min-width: 280px; - overflow: hidden; - position: relative; - width: 100% -} - -@media (max-width: 485px) { - .oauth2PageWrapper__647f0 { - align-items:flex-start; - padding-bottom: 0; - padding-top: 0 - } - - .oauth2PageContent__647f0 { - border-radius: 0; - box-shadow: none; - height: 100%; - max-height: 100%; - overflow: auto - } -} - -.wrapper_b76614 { - align-items: center; - display: flex; - flex: 1; - flex-direction: column; - justify-content: center -} - -.header_b76614 { - background-position: 50%; - background-repeat: no-repeat; - display: inline-block -} - -.text_b76614 { - margin-top: 24px; - text-align: center -} - -.authorizedSuccessSubtext_b76614 { - margin-top: 16px; - max-width: 274px; - text-align: center -} - -.cta_b76614 { - margin-top: 12px -} - -.headerSuccess_b76614 { - background: url(/assets/6410595f71273140.svg) no-repeat; - height: 130px; - width: 158px -} - -.headerFailure_b76614 { - height: 92px; - width: 92px -} - -.closeButton_b76614 { - inset-inline-end: 12px; - opacity: .8; - position: absolute; - top: 12px -} - -.buttonsContainer_b76614 { - align-items: center; - display: flex; - flex-direction: row; - justify-content: space-between; - padding: 16px; - width: 100% -} - -.theme-dark .headerFailure_b76614 { - background: url(/assets/8fb262894795f907.svg) no-repeat -} - -.theme-light .headerFailure_b76614 { - background: url(/assets/b02f7cfc8ed3b51a.svg) no-repeat -} - -.authorizedSuccessExternal_b76614 { - align-items: center; - display: flex; - flex-direction: column; - gap: 24px; - text-align: center -} - -.authorizedSuccessExternalCopy_b76614 { - align-items: center; - display: flex; - flex-direction: column; - gap: 8px -} - -.authorizedSuccessExternalIcon_b76614 { - height: 86px; - width: 86px -} - -.authorizedSuccessExternalIcon_b76614 img { - height: 100%; - width: 100% -} - -.authorizedExternalCta_b76614 { - width: 100% -} - -.authorize__3d3b0 { - align-items: center; - background: unset; - display: flex; - flex: 1; - flex-direction: column; - gap: var(--space-24); - height: auto; - justify-content: center; - margin: 0; - width: 100% -} - -.loadingContainer__3d3b0 { - min-height: 270px -} - -.spinner__3d3b0 { - height: 240px; - inset-inline-start: 50%; - position: fixed; - top: 50%; - transform: translate(-50%,-50%); - width: 240px -} - -.contentWrapper__3d3b0 { - display: flex; - flex-direction: column; - gap: 16px -} - -.content__3d3b0 { - border-radius: 8px; - margin: 0; - padding: var(--space-32); - position: relative -} - -.content__3d3b0.noPadding__3d3b0 { - padding: 0 -} - -.contentBackground__3d3b0 { - background: var(--background-mod-subtle); - border: 1px solid var(--border-muted) -} - -@media (max-width: 568px) { - .content__3d3b0 { - padding:16px - } - - .content__3d3b0.noPadding__3d3b0 { - padding: 0 - } -} - -.intObserver__3d3b0 { - bottom: 24px; - height: 1px; - pointer-events: none; - position: absolute -} - -.deepLinkContainer__3d3b0 { - align-items: center; - background: var(--background-base-low); - border-radius: 12px; - display: flex; - flex-direction: column; - gap: 16px; - padding: 32px; - text-align: center; - width: 480px -} - -div.mobilePushContainer__3d3b0 { - margin: 0; - padding-top: 16px -} - -.button__0f074 { - color: var(--icon-subtle); - cursor: pointer; - height: 16px; - opacity: 0; - transition: color 50ms ease-in; - width: 16px -} - -.button__0f074:focus-visible,.button__0f074:hover { - color: var(--icon-strong); - transition: color .15s ease-out -} - -.button__0f074.visible__0f074,.button__0f074:focus-visible { - opacity: 1 -} - -.viewFullBio_f5f93a { - margin-top: 8px -} - -.descriptionClamp_f5f93a { - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 3; - overflow: hidden -} - -.maxBioHeight_f5f93a { - max-height: 55px -} - -.container__50e22 { - background-color: var(--modal-background); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - padding: 8px; - width: 250px -} - -.popoutRoleDot__50e22 { - margin-inline-end:4px} - -.popoutRoleCircle__50e22 { - margin-inline-end:8px} - -.role_af3987 { - align-items: center; - border: 1px solid var(--border-subtle); - border-radius: var(--radius-round); - box-sizing: border-box; - display: flex; - min-height: 24px; - padding-block:0;padding-inline-end:8px;padding-inline-start:2px} - -.custom-user-profile-theme .role_af3987 { - border-color: var(--user-profile-border) -} - -.roleDot_af3987 { - font-size: 1.34em -} - -.connectionRoleIcon_af3987 { - color: var(--icon-default); - margin-inline-end:4px} - -.roleIcon_af3987 { - margin-inline-start:4px;vertical-align: middle -} - -.roleTag_af3987 { - border-radius: var(--radius-round); - max-width: 100%; - outline: none -} - -.hasRemoveButton_af3987 { - padding-inline-end:0} - -.removeButton_af3987 { - align-items: center; - background: transparent; - border-radius: var(--radius-xs); - box-sizing: border-box; - color: var(--icon-subtle); - cursor: pointer; - display: flex; - flex-shrink: 0; - height: 24px; - justify-content: center; - margin-block:-1px;margin-inline-end:-1px;padding: 0; - transition: color 50ms ease-in; - width: 24px -} - -.removeButton_af3987:hover { - color: var(--icon-strong); - transition: color .15s ease-out -} - -.roleListContainer_af3987 { - display: flex; - flex-wrap: wrap; - gap: 4px; - margin-block-start:2px;position: relative -} - -.listContentsInteractive_af3987 { - display: flex; - flex-wrap: wrap; - gap: 4px; - max-width: 100% -} - -.listContentsStatic_af3987 { - display: contents -} - -.roleName_af3987,.roleNameOverflow_af3987 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.pillButton_af3987 { - background: transparent; - color: var(--interactive-text-default); - cursor: pointer; - min-width: 24px; - padding-inline:4px;transition: background 50ms ease-in,color 50ms ease-in -} - -.pillButton_af3987:hover { - background: var(--background-mod-subtle); - color: var(--interactive-text-hover); - transition: background .15s ease-out,color .15s ease-out -} - -.expandButton_af3987 { - padding-inline-end:8px;padding-inline-start:6px} - -.addButton_af3987 { - align-items: center; - border-radius: var(--radius-round); - box-sizing: border-box; - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - gap: 2px; - height: 24px; - justify-content: center; - min-width: 24px; - transition: color 50ms ease-in -} - -.addButton_af3987:hover { - color: var(--interactive-text-hover); - transition: color .15s ease-out -} - -.addButton_af3987.showLabel_af3987 { - padding-inline-end:4px} - -.enable-forced-colors .role_af3987 { - border: 1px solid CanvasText!important -} - -.enable-forced-colors .removeButton_af3987 { - background-color: ButtonFace; - border: 1px solid CanvasText; - border-inline-start:none;border-radius: 0 var(--radius-round) var(--radius-round) 0; - color: ButtonText; - margin-inline-start:4px} - -.enable-forced-colors .removeButton_af3987:hover { - color: ButtonText -} - -.enable-forced-colors .addButton_af3987,.enable-forced-colors .addButton_af3987:hover,.enable-forced-colors .pillButton_af3987,.enable-forced-colors .pillButton_af3987:hover { - background-color: ButtonFace; - border: 1px solid CanvasText; - color: ButtonText -} - -.popoutWrapper__9ac28 { - display: flex; - inset-inline-start: -30px; - max-height: 40vh; - position: relative; - width: 200px -} - -.scroller__9ac28 { - background-color: var(--background-surface-high); - border-radius: 4px; - padding: 8px 16px -} - -.avatars__9ac28 { - align-items: center; - display: flex; - flex-grow: 0; - position: relative -} - -.avatar__9ac28 { - -webkit-mask: url(/assets/38fe464a6fea7d0e.svg); - mask: url(/assets/38fe464a6fea7d0e.svg); - -webkit-mask-size: 100%; - mask-size: 100%; - mask-type: alpha -} - -.avatar__9ac28.size16__9ac28 { - margin-inline-end:-3px} - -.avatar__9ac28.size20__9ac28 { - margin-inline-end:-5px} - -.avatar__9ac28.size24__9ac28 { - margin-inline-end:-6px} - -.avatar__9ac28.isLast__9ac28 { - margin-inline-end:0;-webkit-mask: none; - mask: none -} - -.overflow__9ac28 { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: 16px; - box-sizing: border-box; - display: flex; - justify-content: center; - width: auto -} - -.overflow__9ac28.size16__9ac28 { - height: 16px; - min-width: 16px; - padding: 0 4px -} - -.overflow__9ac28.size20__9ac28 { - height: 20px; - min-width: 20px; - padding: 0 6px -} - -.overflow__9ac28.size24__9ac28 { - height: 24px; - min-width: 24px; - padding: 0 8px -} - -.button__9ac28 { - cursor: pointer -} - -.container__530ce { - align-items: center; - display: flex -} - -.mutuals__530ce { - -moz-column-gap: 6px; - column-gap: 6px; - flex-wrap: wrap; - row-gap: 2px -} - -.mutuals__530ce,.section__530ce { - align-items: center; - display: flex; - flex-direction: row -} - -.section__530ce { - cursor: pointer; - gap: 4px -} - -.section__530ce:focus-visible .text__530ce,.section__530ce:hover .text__530ce { - text-decoration: underline -} - -.spacer__530ce { - background-color: var(--interactive-text-default); - border-radius: var(--radius-round); - height: 4px; - width: 4px -} - -.buttons_bc38cd { - display: flex; - flex-wrap: wrap; - gap: 8px -} - -.buttons_bc38cd:empty { - display: none -} - -.header__5be3e { - flex-shrink: 0; - min-height: calc(var(--custom-user-profile-banner-height) + 35px); - position: relative -} - -.custom-user-profile-theme .header__5be3e { - min-height: calc(var(--custom-user-profile-banner-height) + 31px) -} - -.headerTag__5be3e { - border-radius: var(--radius-xs); - bottom: 8px; - inset-inline-end: 16px; - position: absolute -} - -.body__5be3e { - display: flex; - flex-direction: column; - gap: 12px; - padding: 4px 16px 8px -} - -.card__5be3e { - padding: 12px -} - -.footer__5be3e { - display: flex; - flex-direction: column; - padding: 0 16px 12px -} - -.footer__5be3e:empty { - padding: 0; - visibility: hidden -} - -.backdrop__5be3e { - background: var(--background-scrim); - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - z-index: var(--custom-user-profile-hoist-z-index) -} - -.toast__5be3e { - top: calc((var(--custom-user-profile-banner-height) - 56px)/2); - z-index: var(--custom-user-profile-toast-z-index) -} - -.container__10737 { - background-color: var(--background-mod-normal); - border-start-end-radius: var(--radius-sm); - border-start-start-radius: var(--radius-sm); - box-shadow: var(--shadow-low); - display: flex; - flex-direction: column; - height: 60px; - justify-content: center; - overflow: hidden; - padding-block-end:6px;padding-block-start:6px;padding-inline-end:0;padding-inline-start:16px;position: relative; - width: 252px -} - -.container__10737:before { - background: #c6cdf4; - content: ""; - filter: blur(40px); - height: 170px; - inset-block-start: -190px; - inset-inline-start: 50%; - opacity: .5; - pointer-events: none; - position: absolute; - transform: translateX(-50%); - width: 221px -} - -.closeButton__10737 { - inset-inline-end: 6px; - position: absolute; - top: 6px -} - -.subtitleContainer__10737 { - align-items: center; - display: flex; - flex-direction: row; - padding-top: 4px -} - -.currentUserAvatar__10737 { - border: 2px solid var(--background-mod-normal); - margin-inline:-4px;margin-inline-end:4px} - -.lottieIconColors__5eb9b :not(defs *)[fill][fill-opacity] { - fill: var(--__lottieIconColor,var(--interactive-text-default)) -} - -.lottieIconColors__5eb9b :not(defs *)[stroke][stroke-opacity] { - stroke: var(--__lottieIconColor,var(--interactive-text-default)) -} - -.lottieIcon__5eb9b svg { - transform: none!important -} - -.enable-forced-colors .lottieIcon__5eb9b :not(defs *)[fill][fill-opacity] { - fill: currentColor -} - -.enable-forced-colors .lottieIcon__5eb9b :not(defs *)[stroke][stroke-opacity] { - stroke: currentColor -} - -.custom-user-profile-theme .container_a99829 { - border: none -} - -.theme-light.custom-user-profile-theme .container_a99829 { - background: var(--opacity-black-8) -} - -.theme-dark.custom-user-profile-theme .container_a99829 { - background: var(--opacity-white-12) -} - -.editor_a99829.editor_a99829 { - --channel-text-area-placeholder: var(--text-muted); - font-size: 14px -} - -.ctaRow_a99829 { - display: flex; - flex-direction: row; - gap: 10px -} - -.menuItemLabel_a99829 { - display: flex; - gap: 6px -} - -.container_a99829 { - border-radius: var(--radius-sm) -} - -.upsell_a99829 { - border-radius: 0 0 var(--radius-sm) var(--radius-sm) -} - -.buttons_a99829 { - align-items: flex-start; - align-self: flex-start; - display: flex; - flex-direction: row; - padding-block-start:var(--space-6)} - -.container__7970d { - position: relative -} - -.avatar__7970d,.avatarIconContainer__7970d { - align-items: center; - display: flex; - justify-content: center -} - -.avatarIconContainer__7970d { - background-color: var(--background-mod-normal); - border-radius: var(--radius-round); - bottom: -8px; - inset-inline-end: -8px; - overflow: hidden; - padding: var(--space-4); - position: absolute -} - -.preview__207b4 { - height: 100%; - width: 100%; - filter: blur(5px); - -o-object-fit: contain; - object-fit: contain; - -o-object-position: top; - object-position: top; - opacity: .3 -} - -.container__207b4,.preview__207b4 { - position: relative -} - -.headerContainer__207b4 { - gap: var(--space-8) -} - -.body__207b4,.headerContainer__207b4 { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center -} - -.body__207b4 { - gap: var(--space-16); - inset-inline-start: 50%; - position: absolute; - text-align: center; - top: 50%; - transform: translate(-50%,-50%); - width: 80% -} - -.toastContainer_a35754 { - border-radius: 32px; - box-shadow: var(--shadow-high); - height: 40px; - inset-inline: 0; - margin: 0 auto; - position: absolute; - width: -moz-fit-content; - width: fit-content -} - -.toastPadding_a35754 { - padding: 10px 16px -} - -.toastIcon_a35754 { - height: 20px; - margin-inline-end:8px;width: 20px -} - -.successToast_a35754 { - display: flex; - gap: 40px -} - -.toast__3fde7 { - align-items: center; - background-color: var(--background-surface-highest); - border: 1px solid var(--border-normal); - border-radius: var(--radius-xl); - box-shadow: var(--shadow-high); - color: var(--text-strong); - display: flex; - line-height: 24px; - margin-bottom: 12px; - padding: var(--space-8) var(--space-12) -} - -.toast__3fde7[data-type=success] { - background-color: var(--notice-background-positive); - color: var(--notice-text-positive) -} - -.toast__3fde7[data-type=failure] { - background-color: var(--notice-background-critical); - color: var(--notice-text-critical) -} - -.content__3fde7 { - color: currentColor -} - -.icon__3fde7 { - height: 16px; - margin-inline-end:8px;margin-top: -2px; - width: 16px -} - -.clipIcon__3fde7 { - color: var(--text-strong) -} - -.gameIcon_b52e4f { - filter: saturate(var(--saturation-factor,1)); - -webkit-user-drag: none; - background-size: 100%; - border-radius: var(--radius-sm); - color: var(--text-strong) -} - -.intersectionContainer_b52e4f,.intersectionRef_b52e4f { - position: absolute -} - -.intersectionRef_b52e4f { - background: transparent; - height: 1px; - inset-inline-start: 0; - top: 0; - width: 1px -} - -.large_b52e4f,.medium_b52e4f,.small_b52e4f { - flex-shrink: 0 -} - -.xxsmall_b52e4f { - border-radius: var(--radius-xs); - height: 16px; - width: 16px -} - -.xsmall_b52e4f { - height: 24px; - width: 24px -} - -.small_b52e4f,.xsmall_b52e4f { - border-radius: var(--radius-sm) -} - -.small_b52e4f { - height: 30px; - width: 30px -} - -.medium_b52e4f { - height: 40px; - width: 40px -} - -.medium_b52e4f,.mediumLarge_b52e4f { - border-radius: var(--radius-md) -} - -.mediumLarge_b52e4f { - height: 48px; - width: 48px -} - -.large_b52e4f { - height: 60px; - width: 60px -} - -.large_b52e4f,.xlarge_b52e4f { - border-radius: var(--radius-lg) -} - -.xlarge_b52e4f { - height: 80px; - width: 80px -} - -@keyframes placeholderPulse_b52e4f { - 0% { - opacity: .8 - } - - 50% { - opacity: 1 - } - - to { - opacity: .8 - } -} - -.gameIconLoading_b52e4f { - animation: placeholderPulse_b52e4f 1.3s ease-in-out infinite; - background-color: var(--background-mod-normal) -} - -.subtext_beaa93 { - align-items: center; - display: flex; - gap: 4px -} - -.header_bfd183 { - margin-bottom: 4px -} - -.list_bfd183 { - gap: 2px -} - -.list_bfd183,.row_bfd183 { - display: flex -} - -.row_bfd183 { - align-items: center; - gap: 4px -} - -.applicationIcon__2c2b3 { - height: 1.33em; - margin-inline:2px 4px;vertical-align: text-bottom; - width: auto -} - -.applicationNameWrapper__2c2b3 { - display: inline-block -} - -.channelHeader__9f71b { - background-color: var(--background-mod-muted); - border-radius: var(--radius-md); - gap: var(--space-12); - margin-top: 12px; - padding: 12px 16px -} - -.header__9f71b { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px -} - -.profile__9f71b { - gap: var(--space-8); - padding: 12px -} - -.icon__9f71b { - color: var(--text-default); - flex-shrink: 0 -} - -.upsellContainer__5997d { - border-radius: var(--radius-sm) -} - -.upsellContent__5997d { - background: var(--user-profile-overlay-background); - border-radius: inherit; - position: relative -} - -.upsellContent__5997d:after,.upsellContent__5997d:before { - border: 2px solid transparent; - border-radius: inherit; - box-sizing: border-box; - content: ""; - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.upsellContent__5997d:before { - border-color: var(--guild-boosting-purple) -} - -.upsellCloseIconWrapper__5997d { - display: flex; - justify-content: center -} - -.upsellDefaultContent__5997d { - padding: 12px -} - -.upsellHeader__5997d { - align-items: center; - justify-content: space-between; - margin-bottom: 12px -} - -.upsellButtons__5997d,.upsellHeader__5997d { - display: flex; - flex-direction: row -} - -.upsellButtons__5997d { - flex-wrap: wrap; - gap: 8px -} - -.upsellButtonWrapper__5997d { - flex: 1 -} - -.user-profile-modal-v2 .upsellButtonWrapper__5997d { - flex: unset -} - -.upsellButtons__5997d .matchManaColors__5997d { - background: var(--control-secondary-background-default) -} - -.upsellButtons__5997d .matchManaColors__5997d:hover { - background: var(--control-secondary-background-hover) -} - -.upsellButtons__5997d .matchManaColors__5997d:active { - background: var(--control-secondary-background-active) -} - -.upsellRowContent__5997d { - align-items: center; - border-radius: inherit; - display: flex; - flex-direction: row; - justify-content: space-between; - padding-block:12px;padding-inline:16px 11px;position: relative -} - -.upsellRowContent__5997d:after,.upsellRowContent__5997d:before { - border: 2px solid transparent; - border-radius: inherit; - box-sizing: border-box; - content: ""; - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.upsellRowContent__5997d:before { - border-color: var(--guild-boosting-purple) -} - -.upsellContent__5997d:after,.upsellRowContent__5997d:after { - border-color: var(--guild-boosting-blue); - -webkit-mask: linear-gradient(0deg,#000,transparent); - mask: linear-gradient(0deg,#000,transparent) -} - -.upsellRowRight__5997d { - align-items: center; - display: flex; - gap: 8px -} - -.upsellCloseIcon__5997d { - color: var(--interactive-text-default); - cursor: pointer -} - -.upsellCloseIcon__5997d:hover { - color: var(--interactive-text-hover) -} - -.upsellCloseIcon__5997d:active { - color: var(--interactive-text-active) -} - -.container__43a4b { - border-radius: var(--radius-sm); - box-sizing: border-box; - display: flex; - flex-direction: column; - gap: 8px; - overflow: hidden; - padding: 10px 12px -} - -.buttonContainer__43a4b { - align-items: center; - display: flex; - gap: 8px -} - -.gameIcon__43a4b { - vertical-align: bottom -} - -.container__581dd { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-8); - padding: var(--space-16) -} - -.container__82fb2 { - cursor: pointer; - display: grid; - gap: var(--space-4); - grid-template-columns: 7fr 3fr -} - -.container__82fb2:hover { - background: var(--user-profile-overlay-background-hover) -} - -.content__82fb2 { - box-sizing: border-box; - min-width: 0 -} - -.applicationIcon__82fb2 { - border-radius: 2px -} - -.previewIcon__82fb2 { - display: inline-block; - height: 1lh; - vertical-align: bottom; - width: 1lh -} - -.textContent__82fb2 { - padding-block:2px} - -.previewImageContainer__82fb2 { - -webkit-mask: linear-gradient(90deg,transparent 0,rgba(0,0,0,.013) 4.7%,rgba(0,0,0,.049) 9.2%,rgba(0,0,0,.104) 13.6%,rgba(0,0,0,.175) 18%,rgba(0,0,0,.259) 22.6%,rgba(0,0,0,.352) 27.4%,rgba(0,0,0,.45) 32.6%,rgba(0,0,0,.55) 38.3%,rgba(0,0,0,.648) 44.5%,rgba(0,0,0,.741) 51.4%,rgba(0,0,0,.825) 59.1%,rgba(0,0,0,.896) 67.7%,rgba(0,0,0,.951) 77.3%,rgba(0,0,0,.987) 88%,#000); - mask: linear-gradient(90deg,transparent 0,rgba(0,0,0,.013) 4.7%,rgba(0,0,0,.049) 9.2%,rgba(0,0,0,.104) 13.6%,rgba(0,0,0,.175) 18%,rgba(0,0,0,.259) 22.6%,rgba(0,0,0,.352) 27.4%,rgba(0,0,0,.45) 32.6%,rgba(0,0,0,.55) 38.3%,rgba(0,0,0,.648) 44.5%,rgba(0,0,0,.741) 51.4%,rgba(0,0,0,.825) 59.1%,rgba(0,0,0,.896) 67.7%,rgba(0,0,0,.951) 77.3%,rgba(0,0,0,.987) 88%,#000); - position: relative -} - -.previewImage__82fb2 { - position: absolute -} - -.breadcrumb_c2a763 { - cursor: pointer -} - -.breadcrumb_c2a763:hover .innerContainer_c2a763 { - background: var(--user-profile-overlay-background-hover) -} - -.innerContainer_c2a763 { - align-items: center; - -webkit-backdrop-filter: blur(16px); - backdrop-filter: blur(16px); - display: flex; - flex-direction: row; - height: 24px; - justify-content: space-between; - padding: 8px -} - -.user-profile-sidebar .innerContainer_c2a763 { - padding: 12px -} - -.icons_c2a763 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px -} - -.icon_c2a763 { - height: 24px; - position: relative; - width: 24px -} - -.icon_c2a763,.icon_c2a763 img { - border-radius: var(--radius-xs) -} - -.icon_c2a763 img { - display: block; - height: auto; - max-height: 100%; - max-width: 100%; - width: auto -} - -.displayCount_c2a763 { - opacity: .5 -} - -.displayCountText_c2a763 { - align-items: center; - background: rgba(0,0,0,.6); - border-radius: var(--radius-xs); - display: flex; - height: 24px; - inset-inline-start: 50%; - justify-content: center; - position: absolute; - top: 50%; - transform: translate(-50%,-50%); - width: 24px -} - -.displayCountTextColor_c2a763 { - color: var(--neutral-9) -} - -.baseAvatar__6738d { - display: inline-block; - vertical-align: top -} - -.largeAvatar__6738d { - line-height: 24px; - margin-inline-end:8px} - -.miniAvatar__6738d { - line-height: 18px; - margin-inline-end:4px;margin-top: 1px -} - -.userListItem_ec58fe { - align-items: center; - border-radius: var(--radius-xs); - display: flex; - margin: 8px -8px; - padding: 4px 8px -} - -.userListItem_ec58fe:last-child { - margin-bottom: 4px -} - -.userListItem_ec58fe:not(.popoutDisabled_ec58fe):hover { - background-color: var(--background-base-lowest); - cursor: pointer -} - -.userListItemTag_ec58fe { - margin-inline-start:8px;overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.avatar_ec58fe { - flex-shrink: 0 -} - -.username_ec58fe { - font-weight: var(--font-weight-semibold) -} - -.emojiButton__04eed { - align-items: center; - cursor: pointer; - display: flex; - font-size: 14px; - justify-content: center; - max-height: 50px -} - -.emojiButtonHovered__04eed,.emojiButtonNormal__04eed { -} - -.spriteContainer__04eed { - position: relative; - --custom-emoji-sprite-bg-position: calc(var(--custom-emoji-sprite-col)*-1*var(--custom-emoji-sprite-size)) calc(var(--custom-emoji-sprite-row)*-1*var(--custom-emoji-sprite-size)); - --custom-emoji-sprite-bg-size: calc(var(--custom-emoji-sprite-size)*20) calc(var(--custom-emoji-sprite-size)*4) -} - -.sprite__04eed,.spriteContainer__04eed { - display: block; - height: var(--custom-emoji-sprite-size); - width: var(--custom-emoji-sprite-size) -} - -.sprite__04eed { - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.spriteColored__04eed { - background-image: url(/assets/1b05b930a77fbff9.png); - background-position: var(--custom-emoji-sprite-bg-position); - background-size: var(--custom-emoji-sprite-bg-size) -} - -.spriteGreyscale__04eed { - background-color: var(--interactive-text-default) -} - -.spriteGreyscale__04eed,.spritePremiumColored__04eed { - -webkit-mask-image: url(/assets/decf85f79e02d276.png); - mask-image: url(/assets/decf85f79e02d276.png); - -webkit-mask-position: var(--custom-emoji-sprite-bg-position); - mask-position: var(--custom-emoji-sprite-bg-position); - -webkit-mask-size: var(--custom-emoji-sprite-bg-size); - mask-size: var(--custom-emoji-sprite-bg-size) -} - -.spritePremiumColored__04eed.reducedMotion__04eed { - background-color: var(--premium-tier-2-purple-for-gradients-2) -} - -.spritePremiumColored__04eed:not(.reducedMotion__04eed) { - animation: spritePremiumPulsing__04eed 2s infinite -} - -@keyframes spritePremiumPulsing__04eed { - 0% { - background-color: var(--premium-tier-2-purple) - } - - 50% { - background-color: var(--premium-tier-2-purple-for-gradients-2) - } - - to { - background-color: var(--premium-tier-2-purple) - } -} - -.inactive__04eed { - opacity: 0 -} - -.active__04eed { - opacity: 1 -} - -.premiumUnlockAnimation__04eed { - position: absolute; - z-index: 100 -} - -.premiumUnlockAnimation__04eed.reducedMotion__04eed { - height: 48px; - width: 48px -} - -.premiumUnlockAnimation__04eed:not(.reducedMotion__04eed) { - animation: shrink__04eed 2s forwards -} - -@keyframes shrink__04eed { - 0% { - height: 60px; - width: 60px - } - - 75% { - height: 60px; - width: 60px - } - - to { - height: 48px; - width: 48px - } -} - -.enable-forced-colors .spriteGreyscale__04eed { - background-color: ButtonText -} - -.reactions__5ba62 { - flex-direction: row; - flex-wrap: wrap; - height: 32px; - justify-content: flex-end; - width: 100px -} - -.reactions__5ba62,.reply__5ba62 { - border-radius: 8px; - display: flex -} - -.reply__5ba62 { - background: var(--background-base-low); - box-shadow: var(--shadow-border),var(--shadow-high); - flex-direction: column; - font-size: 16px; - padding: 16px; - text-indent: 0; - width: 300px -} - -.replyHeader__5ba62 { - margin-bottom: 8px; - text-transform: uppercase -} - -.replyInput__5ba62 { - background: var(--input-background-default) -} - -.emojiButton__5ba62 { - background-color: transparent; - margin-inline-start:8px} - -.reaction__5ba62 { - align-self: center; - display: flex; - opacity: .8; - transition: opacity .2s linear -} - -.reaction__5ba62:hover { - opacity: 1 -} - -.closeIcon__5ba62 { - color: var(--interactive-text-default); - cursor: pointer -} - -.image__04666 { - height: 100%; - -o-object-fit: contain; - object-fit: contain; - width: 100% -} - -.emptyPreviewContainer__04666 { - align-items: center; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - display: flex; - flex: 1 1 auto; - flex-direction: column; - justify-content: center -} - -.emptyPreviewImage__04666 { - background-position: 50%; - background-repeat: no-repeat; - height: 60%; - margin-bottom: 10px; - width: 80% -} - -.emptyPreviewText__04666 { - color: var(--text-default) -} - -.noImage__04666 { - background-image: none -} - -.images-light .emptyPreviewImage__04666 { - background-image: url(/assets/bc689a4cf705a445.svg) -} - -.images-light .emptyPreviewImage__04666.noImage__04666 { - background-image: none -} - -.images-dark .emptyPreviewImage__04666 { - background-image: url(/assets/6b1a461f35c05c7a.svg) -} - -.images-dark .emptyPreviewImage__04666.noImage__04666 { - background-image: none -} - -.clickable_c906e8 { - cursor: pointer -} - -.full-motion .clickable_c906e8 .icon_c906e8 { - transition: transform .15s ease-in-out -} - -.full-motion .clickable_c906e8:hover .icon_c906e8 { - transform: scale(1.125) -} - -.full-motion .clickable_c906e8:active .icon_c906e8 { - transform: scale(.875) -} - -.card__39b32,.container__39b32 { - position: relative -} - -.card__39b32 { - background-color: var(--background-surface-highest); - border-radius: var(--radius-md); - box-shadow: inset 0 0 0 1px var(--border-muted),0 1px 4px 0 hsl(none 0% 0%/.04); - box-sizing: border-box; - cursor: pointer; - display: flex; - flex-direction: column; - height: 173px; - overflow: hidden; - top: 0; - width: 133px -} - -.custom-user-profile-theme .card__39b32 { - background-color: var(--user-profile-overlay-background) -} - -.full-motion .card__39b32 { - transition: transform .2s ease,box-shadow .2s ease -} - -.smallCard__39b32 { - height: 136px; - width: 106px -} - -.smallSquareCard__39b32 { - height: 136px; - width: 136px -} - -.largeCard__39b32 { - height: 173px; - width: 133px -} - -.flexCard__39b32 { - display: flex; - height: 100%; - width: 100% -} - -.card__39b32:focus-within,.card__39b32:hover { - transform: scale(1.025) -} - -.card__39b32.isOwned__39b32 { - cursor: default; - opacity: .8 -} - -.card__39b32.isOwned__39b32:focus-within,.card__39b32.isOwned__39b32:hover { - cursor: pointer; - opacity: 1 -} - -.cardPreview__39b32 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - overflow: hidden; - width: 100% -} - -.card__39b32.isOwned__39b32 .cardPreview__39b32 { - opacity: .5 -} - -.card__39b32.isOwned__39b32:focus-within .cardPreview__39b32,.card__39b32.isOwned__39b32:hover .cardPreview__39b32 { - opacity: 1 -} - -.cardStateIconWrapper__39b32 { - align-items: center; - display: flex; - inset-inline: 0; - justify-content: center; - pointer-events: none; - position: absolute; - top: 50%; - transform: translateY(-50%); - z-index: 2 -} - -.cardStateIcon__39b32 { - color: var(--status-positive); - filter: drop-shadow(0 0 4px rgba(0,0,0,.3)); - height: 38px; - transition: opacity .3s ease; - width: 38px -} - -.card__39b32:focus-within .checkmark__39b32,.card__39b32:hover .checkmark__39b32 { - opacity: 0 -} - -.profileEffectPreview__39b32 { - align-items: flex-start -} - -.bundlePreview__39b32,.profileEffectPreview__39b32 { - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.bundlePreview__39b32 { - align-items: center; - transform: scale(.65); - transform-origin: center -} - -.full-motion .bundlePreview__39b32 { - transition: transform .2s ease -} - -.card__39b32:focus-within .bundlePreview__39b32,.card__39b32:hover .bundlePreview__39b32 { - transform: scale(.65) translateY(-18px) -} - -.smallCard__39b32 .bundlePreview__39b32,.smallSquareCard__39b32 .bundlePreview__39b32 { - height: 175px; - transform: scale(.5); - width: 300px -} - -.card__39b32.smallCard__39b32:focus-within .bundlePreview__39b32,.card__39b32.smallCard__39b32:hover .bundlePreview__39b32 { - transform: scale(.5) -} - -.card__39b32.smallSquareCard__39b32:focus-within .bundlePreview__39b32,.card__39b32.smallSquareCard__39b32:hover .bundlePreview__39b32 { - transform: scale(.5) -} - -.avatarDecorationPreview__39b32 { - align-items: center; - display: flex; - justify-content: center -} - -.full-motion .avatarDecorationPreview__39b32 { - transition: transform .2s ease -} - -.card__39b32:focus-within .avatarDecorationPreview__39b32,.card__39b32:hover .avatarDecorationPreview__39b32 { - transform: translateY(-18px) -} - -.card__39b32.smallCard__39b32:focus-within .avatarDecorationPreview__39b32,.card__39b32.smallCard__39b32:hover .avatarDecorationPreview__39b32 { - transform: none -} - -.card__39b32.smallSquareCard__39b32:focus-within .avatarDecorationPreview__39b32,.card__39b32.smallSquareCard__39b32:hover .avatarDecorationPreview__39b32 { - transform: none -} - -.nameplatePreview__39b32 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - position: relative; - width: 100% -} - -.full-motion .nameplatePreview__39b32 { - transition: transform .2s ease -} - -.card__39b32:focus-within .nameplatePreview__39b32,.card__39b32:hover .nameplatePreview__39b32 { - transform: translateY(-18px) -} - -.card__39b32.smallCard__39b32:focus-within .nameplatePreview__39b32,.card__39b32.smallCard__39b32:hover .nameplatePreview__39b32 { - transform: none -} - -.card__39b32.smallSquareCard__39b32:focus-within .nameplatePreview__39b32,.card__39b32.smallSquareCard__39b32:hover .nameplatePreview__39b32 { - transform: none -} - -.nameplateTopLeft__39b32 { - inset-inline-start: 33px; - position: absolute; - top: 37px -} - -.nameplateBottomRight__39b32 { - bottom: 33px; - inset-inline-end: 33px; - position: absolute -} - -.smallCard__39b32 .nameplateTopLeft__39b32 { - inset-inline-start: 22px; - top: 22px -} - -.smallCard__39b32 .nameplateBottomRight__39b32 { - bottom: 22px; - inset-inline-end: 22px -} - -.smallSquareCard__39b32 .nameplateTopLeft__39b32 { - inset-inline-start: 22px; - top: 22px -} - -.smallSquareCard__39b32 .nameplateBottomRight__39b32 { - bottom: 22px; - inset-inline-end: 22px -} - -.removeItemButton__39b32 { - align-items: center; - background: var(--background-surface-high); - border: 1px solid var(--border-muted); - border-radius: 100px; - box-shadow: var(--shadow-low); - display: flex; - inset-inline-end: -6px; - justify-content: center; - opacity: 0; - padding: 4px; - position: absolute; - top: -6px; - transition: opacity 50ms ease-in; - z-index: 10 -} - -.removeItemButton__39b32:after { - background-color: transparent; - border-radius: var(--radius-round); - content: ""; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - transition: background-color 50ms ease-in; - width: 100% -} - -.removeItemButton__39b32:hover:after { - background-color: var(--opacity-red-8); - transition: background-color .15s ease-out -} - -.removeItemButton__39b32:focus { - transition: none -} - -.container__39b32:hover .removeItemButton__39b32,.removeItemButton__39b32:focus-visible,.keyboard-mode .container__39b32:focus-within .removeItemButton__39b32 { - opacity: 1; - transition: opacity .15s ease-out -} - -.overlay__39b32 { - align-items: flex-end; - bottom: 0; - display: flex; - inset-inline: 0; - justify-content: center; - opacity: 0; - padding: 12px; - position: absolute; - transition: opacity 50ms ease-in; - z-index: 5 -} - -.card__39b32:focus-within .overlay__39b32,.card__39b32:hover .overlay__39b32 { - opacity: 1; - transition: opacity .15s ease-out -} - -.itemIcon__39b32 { - align-items: center; - display: flex; - inset-inline-end: 8px; - justify-content: center; - pointer-events: none; - position: absolute; - top: 8px -} - -.itemIconHeart__39b32 { - fill: var(--control-critical-primary-background-default) -} - -.itemIconShop__39b32 { - fill: var(--icon-strong) -} - -@keyframes placeholderPulse__39b32 { - 0% { - opacity: .5 - } - - 50% { - opacity: 1 - } - - to { - opacity: .5 - } -} - -.placeholderCard__39b32 { - animation: placeholderPulse__39b32 1.3s ease-in-out infinite -} - -.placeholderAvatar__39b32 { - opacity: .5 -} - -.seeMoreCard__39b32 { - height: 173px; - width: 133px -} - -.seeMoreCard__39b32.smallCard__39b32 { - height: 136px; - width: 105px -} - -.seeMoreCard__39b32.smallSquareCard__39b32 { - height: 136px; - width: 136px -} - -.seeMoreCard__39b32:focus-within,.seeMoreCard__39b32:hover { - transform: scale(1.025) -} - -.seeMoreCardPreview__39b32 { - opacity: .5 -} - -.seeMoreOverlay__39b32 { - align-items: center; - background-color: var(--background-scrim); - border-radius: inherit; - display: flex; - inset: 0; - justify-content: center; - pointer-events: none; - position: absolute; - z-index: 2 -} - -.cardContainer__47729 { - align-items: center; - background-position: 50%; - background-size: cover; - box-sizing: border-box; - display: flex; - justify-content: center; - overflow: hidden; - position: relative -} - -.cardContainer__47729.square__47729 { - aspect-ratio: 1; - border-radius: var(--radius-sm) -} - -.cardBackgroundImage__47729 { - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - inset-inline: 0; - bottom: 0; - position: absolute; - top: 0 -} - -.skuImage__47729 { - border-radius: var(--radius-xs); - max-width: 100%; - -o-object-fit: cover; - object-fit: cover; - width: 100%; - z-index: 1 -} - -.skuImage__47729.square__47729 { - aspect-ratio: 1 -} - -.socialLayerCardHover__517e1 .socialLayerCardBackgroundImage__517e1 { - transform: scale(100%) -} - -.socialLayerCardHover__517e1 .socialLayerCardImage__517e1 { - transform: scale(105%) -} - -.socialLayerCard__517e1 { - border-radius: none; - display: flex; - height: 100%; - width: 100% -} - -.socialLayerCardBackgroundImage__517e1 { - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - inset-inline: 0; - bottom: 0; - height: 100%; - overflow: hidden; - position: absolute; - top: 0; - transform: scale(105%); - width: 100% -} - -.full-motion .socialLayerCardBackgroundImage__517e1 { - transition: transform .5s -} - -.socialLayerCardImage__517e1 { - inset-inline: 0; - bottom: 0; - height: 100%; - -o-object-fit: cover; - object-fit: cover; - -o-object-position: center; - object-position: center; - position: absolute; - top: 4px; - transform: scale(100%); - width: 100% -} - -.full-motion .socialLayerCardImage__517e1 { - transition: transform .5s -} - -.profileEffectPreview__517e1 { - align-items: flex-start; - display: flex; - height: 100% -} - -.nameplatePreview__517e1 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - position: relative; - width: 100% -} - -.full-motion .nameplatePreview__517e1 { - transition: transform .2s ease -} - -.nameplateTopLeft__517e1 { - inset-inline-start: 33px; - position: absolute; - top: 37px -} - -.nameplateBottomRight__517e1 { - bottom: 33px; - inset-inline-end: 33px; - position: absolute -} - -.bundlePreview__517e1 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - transform: scale(.65); - transform-origin: center; - width: 100% -} - -.card__9ed47 { - background-color: var(--background-surface-highest); - border-radius: var(--radius-md); - box-shadow: inset 0 0 0 1px var(--border-muted),0 1px 4px 0 hsl(none 0% 0%/.04); - box-sizing: border-box; - cursor: pointer; - display: flex; - flex-direction: column; - overflow: hidden; - position: relative; - top: 0 -} - -.custom-user-profile-theme .card__9ed47 { - background-color: var(--user-profile-overlay-background) -} - -.full-motion .card__9ed47 { - transition: transform .2s ease,box-shadow .2s ease -} - -.skuPreview__9ed47 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.overlay_f5504d { - align-items: flex-end; - bottom: 0; - display: flex; - inset-inline: 0; - justify-content: center; - opacity: 0; - padding: 12px; - position: absolute; - transition: opacity 50ms ease-in; - z-index: 5 -} - -.overlayHoverOrFocus_f5504d { - opacity: 1; - transition: opacity .15s ease-out -} - -.container__96168 { - height: 136px; - margin-top: 8px; - position: relative; - width: 106px -} - -.full-motion .container__96168 { - transition: transform .2s ease -} - -.container__96168:focus-within,.container__96168:hover { - transform: scale(1.025) -} - -.cardContainer__96168 { - height: 100%; - width: 100% -} - -.contextContainer__96168 { - background-color: var(--background-surface-high); - border-radius: 12px; - color: var(--interactive-text-active); - inset-inline-end: -4px; - padding: 2px; - position: absolute; - top: -8px; - z-index: 2 -} - -.contextContainer__96168,.contextIcon__96168 { - align-items: center; - display: flex; - justify-content: center -} - -.contextIcon__96168 { - height: 20px; - margin-inline-start:-1px;width: 20px -} - -.contextIcon__96168,.fireIcon__96168 { - margin-top: -1px -} - -.card__20585:focus-within .cardBackgroundImage__20585,.card__20585:hover .cardBackgroundImage__20585 { - transform: scale(100%) -} - -.card__20585:focus-within .cardImage__20585,.card__20585:hover .cardImage__20585 { - transform: scale(105%) -} - -.card__20585 { - border-radius: none; - display: flex; - height: 100%; - width: 100% -} - -.cardBackgroundImage__20585 { - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - inset-inline: 0; - bottom: 0; - height: 100%; - overflow: hidden; - position: absolute; - top: 0; - transform: scale(105%); - width: 100% -} - -.full-motion .cardBackgroundImage__20585 { - transition: transform .5s -} - -.cardImage__20585 { - inset-inline: 0; - bottom: 0; - height: 100%; - -o-object-fit: cover; - object-fit: cover; - -o-object-position: center; - object-position: center; - position: absolute; - top: 4px; - transform: scale(100%); - width: 100% -} - -.full-motion .cardImage__20585 { - transition: transform .5s -} - -.itemIcon__20585 { - align-items: center; - display: flex; - inset-inline-end: 8px; - justify-content: center; - pointer-events: none; - position: absolute; - top: 8px; - z-index: 1 -} - -.itemIconHeart__20585 { - fill: var(--control-critical-primary-background-default) -} - -.itemIconShop__20585 { - fill: var(--icon-strong) -} - -.container__5291b { - margin-top: 8px; - position: relative -} - -.contextContainer__5291b { - background-color: var(--background-surface-high); - border-radius: 12px; - color: var(--interactive-text-active); - inset-inline-end: -4px; - padding: 2px; - position: absolute; - top: -8px; - z-index: 2 -} - -.contextContainer__5291b,.contextIcon__5291b { - align-items: center; - display: flex; - justify-content: center -} - -.contextIcon__5291b { - height: 20px; - margin-inline-start:-1px;width: 20px -} - -.contextIcon__5291b,.fireIcon__5291b { - margin-top: -1px -} - -.container__3a590 { - background: var(--background-surface-highest); - border-radius: 12px; - display: flex; - flex-direction: column; - gap: 8px; - padding: 12px -} - -.header__3a590 { - align-items: center; - display: flex; - flex-direction: row; - justify-content: space-between -} - -.applicationIcon__3a590 { - border-radius: 4px; - height: 16px; - width: 16px -} - -.items__3a590 { - display: flex; - flex-direction: row; - justify-content: space-between -} - -.spinner__3a590 { - align-items: center; - display: flex; - height: 144px; - justify-content: center; - width: 100% -} - -.contextContainer__3a590 { - background-color: var(--background-surface-highest) -} - -.smallSquareCard__3a590 { - height: 136px; - width: 136px -} - -@value maxWidth: 498px;.container__0800c { - background-color: var(--background-surface-high); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-border),var(--shadow-high); - isolation: isolate; - overflow: hidden -} - -.slotsContainer__0800c { - align-items: center; - display: flex; - justify-content: center; - padding: var(--custom-voice-channel-effects-bar-effect-bar-padding-y) var(--custom-voice-channel-effects-bar-effect-bar-padding-x); - width: calc(var(--custom-voice-channel-effects-bar-max-width) - var(--custom-voice-channel-effects-bar-effect-bar-padding-x)*2) -} - -.slots__0800c { - display: flex; - gap: 24px; - min-width: 272px -} - -.slotsWide__0800c { - gap: 30px -} - -.slot__0800c { - cursor: pointer -} - -.emoji__0800c { - height: 32px; - width: 32px -} - -.emojiItemDisabled__0800c { - filter: grayscale(100%) -} - -.animatedPicker__0800c { - border-radius: 0; - box-shadow: none; - height: 0; - overflow: hidden; - top: 0; - transition: height .4s cubic-bezier(.16,1,.3,1) -} - -.animatedPickerTall__0800c { - height: 440px; - top: 498px -} - -.emojiPickerHeader__0800c { - background: var(--background-surface-high); - box-shadow: none; - padding-bottom: 4px; - padding-top: var(--space-16) -} - -.emojiPickerHeaderExpanded__0800c { - padding-bottom: 12px -} - -.dropDownContainer__0800c { - margin-inline-start:8px} - -.dropDown__0800c { - color: var(--interactive-text-default); - cursor: pointer -} - -.full-motion .dropDown__0800c { - transition: transform .4s cubic-bezier(.16,1,.3,1) -} - -.dropDown__0800c:hover { - color: var(--interactive-text-hover) -} - -.dropDownOpen__0800c { - transform: rotate(180deg) -} - -.container_b2d72f { - align-items: center; - display: flex; - position: relative -} - -.iconContainer_b2d72f { - flex: 0 0 auto; - height: 16px; - margin-inline-end:4px;position: relative; - width: 16px -} - -.icon_b2d72f { - color: var(--primary-300); - position: relative; - width: 100% -} - -.foreground_b2d72f,.icon_b2d72f { - opacity: .6 -} - -.avatar_b2d72f { - margin-inline-end:-4px} - -.emptyUser_b2d72f { - background: var(--primary-500); - border-radius: 50% -} - -.avatarMasked_b2d72f { - -webkit-mask-image: url(/assets/1f9b76e0279b91a4.svg); - mask-image: url(/assets/1f9b76e0279b91a4.svg); - -webkit-mask-position: 0 0; - mask-position: 0 0; - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-size: 100% 100%; - mask-size: 100% 100% -} - -.moreUsers_b2d72f { - background-color: var(--primary-500); - border-radius: 10px; - color: var(--primary-300); - font-size: 12px; - font-weight: var(--font-weight-bold); - height: 20px; - line-height: 20px; - padding-block:0;padding-inline:6px 8px;position: relative -} - -.noUserDrag_c8743f { - -webkit-user-drag: none -} - -.userSelectText_c8743f { - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.userSelectNone_c8743f { - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.cursorDefault_c8743f { - cursor: default -} - -.cursorPointer_c8743f { - cursor: pointer -} - -.clickable__39b46 { - cursor: pointer -} - -.clickable__39b46:hover>* { - text-decoration: underline -} - -.popout__91e7a { - background-color: var(--background-surface-high); - border-radius: 8px; - box-shadow: var(--shadow-border),var(--shadow-high); - padding: 0 16px -} - -.loadingSpinner__5b1b2 { - height: 24px; - margin-inline-end:8px;width: 24px -} - -.popout_af3b89 { - filter: drop-shadow(0 8px 8px var(--opacity-black-32)); - width: 348px -} - -.hero_af3b89,.popout_af3b89 { - display: flex; - flex-direction: column -} - -.hero_af3b89 { - background-color: var(--background-base-lower); - border-radius: var(--radius-sm); - gap: 8px; - padding: 8px -} - -.interactionsContainer_af3b89:before { - background-color: var(--border-subtle); - content: ""; - height: 1px; - position: absolute; - top: 0; - inset-inline: 16px -} - -.interactionsContainer_af3b89 { - background-color: var(--background-surface-high); - border-end-end-radius: var(--radius-md); - border-end-start-radius: var(--radius-md); - display: flex; - flex-direction: column; - gap: 12px; - padding: 16px; - position: relative -} - -.replyInput_af3b89 { - --form-input-height: 36px -} - -.joinPromptContainer_af3b89 { - display: flex; - flex-direction: row -} - -.emojiHotrail_af3b89 { - align-items: center; - display: flex; - justify-content: space-between; - margin-bottom: 12px; - padding: 0 12px -} - -.emojiSuggestionButton_af3b89:hover { - cursor: pointer -} - -.emoji_af3b89 { - width: 24px -} - -.contentImage_af3b89 { - border-radius: var(--radius-xs); - display: block -} - -.heroDetails_af3b89 { - display: flex; - gap: 8px; - justify-content: space-between; - min-width: 0 -} - -.toastContainer_af3b89 { - align-items: center; - inset-inline: 0; - bottom: -80px; - display: flex; - justify-content: center; - position: absolute -} - -.emojiHotrailShareToChannel_af3b89 { - align-items: center; - display: flex; - justify-content: space-between; - padding: 0 12px 16px -} - -.inputContainerShareToChannel_af3b89 { - align-items: center; - display: flex; - gap: 16px -} - -.primaryActionPopoutMessageCloseIcon_af3b89 { - align-items: center; - display: flex; - justify-content: center -} - -.primaryActionPopoutMessageCloseIcon_af3b89:hover { - cursor: pointer -} - -.shareToChannelButton_af3b89 { - align-items: center; - border-inline-end:none;cursor: pointer; - display: flex; - margin-inline-end:var(--space-xs)} - -.attachment_af3b89 { - width: 100% -} - -.attachmentContainer_af3b89 { - border-start-end-radius: var(--radius-md); - border-start-start-radius: var(--radius-md); - box-sizing: border-box; - margin-bottom: 1px; - padding: 16px; - width: 100% -} - -.attachmentContainer_af3b89,.shareToChannelInput_af3b89 { - background: var(--input-background-default) -} - -.shareToChannelInput_af3b89 { - border-radius: var(--radius-md); - display: flex -} - -.shareToChannelInputHasAttachments_af3b89 { - border-start-end-radius: 0; - border-start-start-radius: 0 -} - -.popoutContentHeader_af3b89 { - align-items: center; - display: flex; - gap: 16px; - justify-content: space-between; - margin-bottom: 16px; - overflow: hidden -} - -.popoutContentWrapper_af3b89 { - background: var(--background-surface-high); - border-start-end-radius: var(--radius-md); - border-start-start-radius: var(--radius-md); - padding: 16px -} - -.popoutHeroInner_af3b89 { - align-items: center; - display: flex; - gap: 16px; - position: relative -} - -.popoutThumbnailContainer_af3b89 { - min-height: 72px; - text-align: center; - width: 72px -} - -.popoutHeroBody_af3b89 { - display: flex; - flex-direction: column; - overflow: hidden -} - -.popoutHeroTextPrimary_af3b89 { - color: var(--content-inventory-overlay-text-primary) -} - -.popoutHeroTextPrimary_af3b89.popoutHeroTextPrimaryShort_af3b89 { - max-width: 156px -} - -.popoutHeroTextSecondary_af3b89 { - color: var(--content-inventory-overlay-text-secondary) -} - -.popoutHeaderIcons_af3b89 { - inset-inline-end: 0; - position: absolute; - top: 0 -} - -.popoutUserContainer_af3b89 { - align-items: center; - display: flex; - flex-grow: 1; - overflow: hidden -} - -.popoutUsernames_af3b89 { - overflow: hidden; - text-overflow: ellipsis -} - -.popoutUsername_af3b89 { - display: inline -} - -.streamingPopoutHeader_af3b89 { - align-items: center; - display: flex; - gap: 16px; - padding: 8px -} - -.streamingPopoutHeaderText_af3b89 { - display: flex; - flex-direction: column -} - -.streamingPopoutPreviewContainer_af3b89 { - min-height: 170px; - position: relative -} - -.streamingPopoutImg_af3b89 img { - border-start-end-radius: var(--radius-sm); - border-start-start-radius: var(--radius-sm); - display: block -} - -.voiceChannelPopoutReactorHeader_af3b89 { - display: flex; - justify-content: space-between -} - -.voiceChannelPopoutReactorChannel_af3b89 { - align-items: center; - cursor: pointer; - display: flex; - gap: 4px; - justify-content: center -} - -.voiceChannelPopoutReactorChannel_af3b89:hover .voiceChannelName_af3b89 { - text-decoration: underline -} - -.voiceChannelGuildIcon_af3b89 { - border-radius: 6px -} - -.voiceChannelAdditionalParticipants_af3b89 { - align-items: center; - background-color: var(--background-mod-strong); - border-radius: 8px; - display: flex; - height: 16px; - justify-content: center; - padding: 0 4px -} - -.primaryActionPopoutDivider_af3b89 { - border-bottom: 1px solid var(--border-subtle); - margin: 16px 0; - width: 100% -} - -.primaryActionPopoutActionButtons_af3b89 { - display: flex; - gap: 8px -} - -.hiddenButRenderedInputField_af3b89 { - display: none -} - -.popoutBlockedWarningIcon_af3b89 { - margin-inline-end:3px;margin-bottom: -2px -} - -.popoutTextPrimary_af3b89 { - color: var(--interactive-text-active) -} - -.popoutTextSecondary_af3b89 { - color: var(--interactive-text-default) -} - -.streamingPopoutHero_af3b89 { - border-radius: var(--radius-sm); - display: flex; - flex-direction: column; - gap: 0 -} - -.streamCTA_af3b89 { - align-items: center; - background: var(--background-scrim); - border-radius: 8px; - bottom: 0; - cursor: pointer; - display: flex; - position: absolute; - top: 0; - inset-inline: 0; - justify-content: center; - opacity: 0; - transition: opacity .1s linear -} - -.streamCTA_af3b89:hover { - opacity: 1 -} - -.maybeClickable_af3b89[role=button] { - cursor: pointer -} - -.maybeClickable_af3b89[role=button]:focus-visible .popoutHeroTextPrimary_af3b89,.maybeClickable_af3b89[role=button]:focus-visible .popoutHeroTextSecondary_af3b89,.maybeClickable_af3b89[role=button]:focus-visible .popoutTextPrimary_af3b89,.maybeClickable_af3b89[role=button]:focus-visible .popoutTextSecondary_af3b89,.maybeClickable_af3b89[role=button]:hover .popoutHeroTextPrimary_af3b89,.maybeClickable_af3b89[role=button]:hover .popoutHeroTextSecondary_af3b89,.maybeClickable_af3b89[role=button]:hover .popoutTextPrimary_af3b89,.maybeClickable_af3b89[role=button]:hover .popoutTextSecondary_af3b89 { - text-decoration: underline -} - -.popoutHeaderIcons_af3b89 { - align-items: center; - align-self: flex-start; - display: flex -} - -.popoutGiftingBreadcrumb_af3b89 { - margin-top: 16px; - padding: 16px -} - -.cloudPlaySection__1692d { - justify-content: space-between -} - -.cloudPlaySectionTextContainer__1692d { - align-items: center; - display: flex; - gap: 4px -} - -.appWidgetPreview__1692d { - margin-block-end:calc(var(--custom-content-card-padding)*-1);margin-inline: calc(var(--custom-content-card-padding)*-1); - width: calc(100% + var(--custom-content-card-padding)*2) -} - -.container__264ae { - align-items: center; - border-radius: 32px; - display: flex; - filter: brightness(.7); - flex-direction: column -} - -.container__264ae[role=button] { - cursor: pointer -} - -.container__264ae[role=button]:hover { - filter: brightness(1) -} - -.listeningTimeline__66799 { - align-items: center; - display: flex; - flex-direction: row; - gap: 9px; - justify-content: space-between -} - -.seekBarContainer__66799 { - background-color: var(--content-inventory-media-seekbar-container); - border-radius: 2px; - height: 2px; - position: relative; - width: 100% -} - -.seekBarFill__66799 { - background-color: var(--text-default); - border-radius: 2px; - height: 2px -} - -.timestamp__66799 { - font-family: var(--font-code) -} - -.menuIcon__62974 { - color: var(--interactive-text-default); - cursor: pointer -} - -.menuIcon__62974:hover { - color: var(--interactive-text-active) -} - -.container__5f4c1 { - background-color: var(--background-base-low); - border-radius: var(--radius-sm); - box-shadow: var(--elevation-medium); - width: 180px -} - -.clearText__5f4c1 { - color: var(--text-link) -} - -.rowDivider_ccbbbb,.rowIconPlaceholder_ccbbbb { - --custom-row-icon-size: 40px -} - -.appDetailsRowContainer_ccbbbb,.rowDivider_ccbbbb { - --custom-row-padding: var(--space-12) -} - -.loadingAnimation_ccbbbb { - overflow: hidden; - position: relative -} - -.loadingAnimation_ccbbbb:after { - background: linear-gradient(-45deg,transparent,#fff,transparent); - content: ""; - height: 100%; - inset-inline-start: 0; - opacity: .05; - position: absolute; - top: 0; - transform: skewX(-20deg) translateX(-100%); - width: 100% -} - -.full-motion .loadingAnimation_ccbbbb:after { - animation: shimmer_ccbbbb 1s ease-in-out infinite -} - -.loadingAnimation_ccbbbb.noAnimation_ccbbbb:after { - display: none -} - -.loadingAnimation_ccbbbb:nth-child(2):after { - animation-delay: 50ms -} - -.loadingAnimation_ccbbbb:nth-child(3):after { - animation-delay: .1s -} - -.loadingAnimation_ccbbbb:nth-child(4):after { - animation-delay: .15s -} - -.loadingAnimation_ccbbbb:nth-child(5):after { - animation-delay: .2s -} - -.loadingAnimation_ccbbbb:nth-child(6):after { - animation-delay: .25s -} - -.loadingAnimation_ccbbbb:nth-child(7):after { - animation-delay: .3s -} - -.loadingAnimation_ccbbbb:nth-child(8):after { - animation-delay: .35s -} - -.container_ccbbbb { - background-color: var(--background-surface-high) -} - -.containerBorderRadius_ccbbbb { - border-radius: var(--radius-md) -} - -.rowContainer_ccbbbb:first-child { - border-start-end-radius: var(--radius-md); - border-start-start-radius: var(--radius-md) -} - -.rowContainer_ccbbbb:last-child { - border-end-end-radius: var(--radius-md); - border-end-start-radius: var(--radius-md) -} - -.rowContainer_ccbbbb:last-child .rowDivider_ccbbbb { - display: none -} - -.rowDivider_ccbbbb { - background-color: var(--background-mod-subtle); - bottom: 0; - height: 1px; - inset-inline: calc(var(--custom-row-padding) + var(--custom-row-icon-size) + var(--space-12)) 0; - position: absolute -} - -.theme-light .loadingAnimation_ccbbbb:after { - opacity: .5 -} - -@keyframes shimmer_ccbbbb { - to { - transform: skewX(-20deg) translateX(100%) - } -} - -.bannerImage_ccbbbb { - background-color: var(--background-mod-subtle); - display: none; - overflow: hidden; - width: 100% -} - -.bannerImage_ccbbbb.mediumBanner_ccbbbb { - aspect-ratio: 3/1; - display: block -} - -.bannerImage_ccbbbb.largeBanner_ccbbbb { - aspect-ratio: 2/1; - display: block -} - -.iconPlaceholder_ccbbbb { - --custom-app-icon-size: 38px; - background-color: var(--background-mod-subtle); - border-radius: 25%; - height: var(--custom-app-icon-size); - width: var(--custom-app-icon-size) -} - -.rowIconPlaceholder_ccbbbb { - height: var(--custom-row-icon-size); - width: var(--custom-row-icon-size) -} - -.appDetailsContainer_ccbbbb { - align-items: center; - display: flex; - flex-direction: row; - padding: var(--space-8) -} - -.appDetailsRowContainer_ccbbbb { - padding: var(--custom-row-padding) -} - -.textContainer_ccbbbb { - display: flex; - flex: 1; - flex-direction: column; - margin-inline-start:var(--space-12)} - -.textPlaceholder_ccbbbb { - align-self: flex-start; - background-color: var(--background-mod-subtle); - border-radius: var(--radius-round) -} - -.textPlaceholder_ccbbbb:not(:last-child) { - margin-bottom: 4px -} - -.hidden_ccbbbb { - line-height: 1; - opacity: 0; - touch-action: none; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.contentContainer_c94584 { - padding: 0 var(--space-16) var(--space-16) -} - -.commandListHeader_c94584 { - align-items: center; - display: flex; - height: 32px; - justify-content: space-between; - margin-bottom: var(--space-8); - margin-top: var(--space-16) -} - -.commandContainer_c94584 { - background-color: var(--background-base-lower); - border-radius: var(--radius-sm); - display: flex; - flex-direction: column -} - -.commandContainer_c94584 .command_c94584:first-child { - border-start-end-radius: var(--radius-sm); - border-start-start-radius: var(--radius-sm) -} - -.commandContainer_c94584 .command_c94584:not(:last-child) { - border-bottom: 1px solid var(--border-subtle) -} - -.commandContainer_c94584 .command_c94584:last-child { - border-end-end-radius: var(--radius-sm); - border-end-start-radius: var(--radius-sm) -} - -.commandFocusBlock_c94584 { - width: 100% -} - -.command_c94584,.commandFocusBlock_c94584 { - align-items: center; - display: flex; - flex-direction: row; - justify-content: space-between -} - -.command_c94584 { - background-color: var(--background-base-low); - padding: var(--space-8) var(--space-12) -} - -.command_c94584:hover,.keyboard-mode .command_c94584:focus { - background-color: var(--interactive-background-hover); - cursor: pointer -} - -.command_c94584:active { - background-color: var(--interactive-background-active) -} - -.commandTextContainer_c94584 { - display: flex; - flex-direction: column; - flex-grow: 1; - gap: var(--space-4); - margin-inline-end:var(--space-8);width: 1px -} - -.commandTextContainerPlaceholder_c94584 { - display: flex; - flex-direction: column; - gap: var(--space-4); - padding: 2px 0; - width: 350px -} - -.clickable__8c853 { - border-radius: var(--radius-round); - padding: var(--space-8) -} - -.root_e3f97f { - align-items: center; - border-radius: var(--radius-xs); - display: flex; - padding: 8px -} - -.icon_e3f97f { - flex-shrink: 0; - height: 24px; - margin-inline-end:10px;width: 24px -} - -.text_e3f97f { - flex: 1; - text-align: start -} - -.info_e3f97f { - background-color: var(--background-feedback-info); - border: 1px solid var(--icon-feedback-info) -} - -.info_e3f97f .icon_e3f97f { - color: var(--icon-feedback-info) -} - -.warning_e3f97f { - background-color: var(--background-feedback-warning); - border: 1px solid var(--icon-feedback-warning) -} - -.warning_e3f97f .icon_e3f97f { - box-sizing: border-box; - color: var(--icon-feedback-warning); - height: 20px; - width: 20px -} - -.actionMenuButton_d84e0f { - background-color: var(--background-base-lower); - border-radius: var(--radius-xs); - cursor: pointer; - display: flex; - padding: var(--space-8) -} - -.actionMenuButton_d84e0f:hover { - background-color: var(--interactive-background-hover) -} - -.authedApp__50a54 { - padding: 20px -} - -.authedAppV2__50a54 { - background-color: var(--background-mod-subtle); - border-radius: var(--radius-sm); - margin-bottom: 16px; - overflow: hidden -} - -.header__50a54,.headerV2__50a54 { - align-items: center; - display: grid; - grid-template-columns: auto minmax(0,1fr) auto auto -} - -.headerV2__50a54 { - background-color: var(--background-mod-subtle); - padding: 16px -} - -.headerText__50a54 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.headerTextContainer__50a54 { - margin: 0 8px; - overflow: hidden -} - -.headerTextContainerV2__50a54 { - margin: 0 12px; - overflow: hidden -} - -.appDetailsContainer__50a54 { - padding: 4px 16px -} - -.appDetailsContainer__50a54 .appDetailsSection__50a54 { - background-color: inherit; - border-bottom: 1px solid var(--border-subtle); - border-radius: 0; - margin-bottom: 0; - padding: 16px 0 -} - -.appDetailsContainer__50a54 .appDetailsSection__50a54:active { - background-color: inherit -} - -.appDetailsContainer__50a54 .appDetailsSection__50a54:before { - border-radius: 0; - box-shadow: none -} - -.appDetailsContainer__50a54 .directMessagesSection__50a54 { - background-color: inherit; - border-radius: 0; - margin-bottom: 0; - padding-top: 16px -} - -.appDetailsContainer__50a54 .directMessagesSection__50a54:active { - background-color: inherit -} - -.appDetailsContainer__50a54 .directMessagesSection__50a54:before { - border-radius: 0; - box-shadow: none -} - -.appDetailsSectionHeader__50a54 { - align-items: center; - cursor: pointer; - display: flex; - justify-content: space-between -} - -.appDetailsContent__50a54 { - padding-top: 4px -} - -.appAvatar__50a54 { - border-radius: 50%; - height: 32px; - width: 32px; - -webkit-user-drag: none -} - -.appAvatarV2__50a54 { - border-radius: var(--radius-sm); - height: 40px; - width: 40px; - -webkit-user-drag: none; - border: 1px solid var(--border-subtle); - box-sizing: border-box -} - -.reportButton__50a54 { - margin-inline-end:8px} - -.botTag__50a54 { - margin-inline-start:4px} - -.permissionsDescription__50a54 { - cursor: default -} - -.permission__50a54 { - align-items: center; - display: flex; - margin-top: 8px -} - -.permission__50a54:first-child { - margin-top: 0 -} - -.permissionCheckmark__50a54 { - background: url(/assets/72e4439bf318b5c5.svg) no-repeat; - filter: saturate(var(--saturation-factor,1)); - width: 23px -} - -.disclosureIcon__50a54,.permissionCheckmark__50a54 { - background-size: 18px 18px; - display: inline-block; - height: 18px -} - -.disclosureIcon__50a54 { - color: var(--text-muted); - margin-inline-end:5px;width: 18px -} - -.deleteModalBody__50a54 { - display: flex; - flex-direction: column; - gap: 16px -} - -.warningContainer__50a54 { - flex-grow: 1; - margin-inline-end:4px} - -.warningOuterContainer__50a54 { - align-items: center; - display: flex -} - -.warningIcon__50a54 { - color: var(--text-muted); - height: 24px; - margin-inline-end:8px;width: 24px -} - -.searchContainer__50a54 { - margin-bottom: 16px -} - -.tosPrivacy__50a54 { - display: flex; - flex-direction: row; - margin-top: 8px -} - -.tos__50a54 { - margin-inline-end:12px} - -.privacy__50a54,.tos__50a54 { - display: flex -} - -.externalLinkIcon__50a54 { - height: 18px -} - -.dmSettingsHeader__50a54,.dmSettingsSwitch__50a54 { - margin-bottom: 16px -} - -.dmSettingsSwitch__50a54 { - border-bottom: 1px solid var(--border-subtle); - padding-bottom: 16px -} - -.dmSettingsMute__50a54 { - align-items: center; - display: flex; - justify-content: space-between -} - -.divider__50a54 { - background-color: var(--border-subtle); - display: flex; - height: 100%; - margin-inline-end:12px;width: 1px -} - -.link__50a54 { - color: var(--text-link) -} - -.footer__50a54 { - align-items: center; - display: flex; - justify-content: flex-end; - margin: 16px 0 -} - -.deauthorizeButton__50a54 { - flex-shrink: 0 -} - -.headingCard__50a54 { - display: flex; - flex-direction: column; - gap: var(--space-4); - margin-bottom: var(--space-40); - padding: var(--space-20) -} - -.marginReset_fd297e { - margin-bottom: 0; - margin-top: 0 -} - -.marginTop4_fd297e { - margin-top: var(--custom-margin-margin-x-small) -} - -.marginBottom4_fd297e { - margin-bottom: var(--custom-margin-margin-x-small) -} - -.marginTop8_fd297e { - margin-top: var(--custom-margin-margin-small) -} - -.marginBottom8_fd297e { - margin-bottom: var(--custom-margin-margin-small) -} - -.marginTop20_fd297e { - margin-top: var(--custom-margin-margin-medium) -} - -.marginBottom20_fd297e { - margin-bottom: var(--custom-margin-margin-medium) -} - -.marginBottom24_fd297e { - margin-bottom: var(--space-24) -} - -.marginTop40_fd297e { - margin-top: var(--custom-margin-margin-large) -} - -.marginBottom40_fd297e { - margin-bottom: var(--custom-margin-margin-large) -} - -.marginTop60_fd297e { - margin-top: var(--custom-margin-margin-x-large) -} - -.marginBottom60_fd297e { - margin-bottom: var(--custom-margin-margin-x-large) -} - -.marginCenterHorz_fd297e { - margin-inline:auto} - -.marginLeft8_fd297e { - margin-inline-start:var(--custom-margin-margin-small)} - -.clickable__997f6 { - border-radius: var(--radius-round); - padding: var(--space-8) -} - -.container__997f6 { - display: flex; - flex-direction: row; - gap: var(--space-8) -} - -:root { - --custom-app-launcher-sticky-header-height: 66px; - --custom-app-launcher-container-border-radius: var(--radius-sm) -} - -.stickyContainer__57d3f { - position: sticky; - top: 0; - z-index: 1 -} - -.stickyBannerContainer__57d3f { - height: 0 -} - -.stickyBanner__57d3f { - flex-shrink: 0; - height: var(--custom-app-launcher-sticky-header-height) -} - -.bannerBackground__57d3f { - flex-shrink: 0; - height: 106px -} - -.backButtonContainer__57d3f { - inset-inline-start: var(--space-16) -} - -.backButtonContainer__57d3f,.moreMenuButtonContainer__57d3f { - cursor: pointer; - position: absolute; - top: var(--space-16); - z-index: 1 -} - -.moreMenuButtonContainer__57d3f { - inset-inline-end: var(--space-16) -} - -.headerButton__57d3f { - align-items: center; - background-color: color-mix(in oklab,var(--background-surface-highest) 50%,transparent); - display: flex; - justify-content: center -} - -.headerButton__57d3f:hover,.keyboard-mode .headerButton__57d3f:focus { - background-color: color-mix(in oklab,var(--background-mod-muted) 40%,color-mix(in oklab,var(--background-surface-highest) 50%,transparent)) -} - -.headerButton__57d3f:active { - background-color: color-mix(in oklab,var(--background-mod-muted) 60%,color-mix(in oklab,var(--background-surface-highest) 50%,transparent)) -} - -.nameContainer__57d3f { - align-content: center; - height: var(--custom-app-launcher-sticky-header-height); - inset-inline-start: 0; - justify-content: center; - position: absolute; - top: 0; - width: 100% -} - -.textApplicationName__57d3f { - opacity: 0; - text-align: center -} - -.container_d806b9 { - display: grid; - grid-template-columns: 1fr; - padding: 0 16px 16px; - grid-gap: 16px -} - -.activityUrlOverride_d806b9 { - margin-top: 16px -} - -.searchBar_d806b9 { - padding: 8px -} - -.container__95856,.monetizationDisclosureContainerStyle__95856 { - --custom-disclosure-spacing: var(--space-12) -} - -.container__95856 { - display: flex; - flex-direction: column; - gap: var(--custom-disclosure-spacing); - padding-inline:var(--space-16)} - -.videoContainer__95856 { - position: relative -} - -.videoCover__95856 { - aspect-ratio: 16/9; - border-start-end-radius: var(--radius-sm); - border-start-start-radius: var(--radius-sm); - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.video__95856 { - animation: fadeIn__95856 .5s; - position: absolute -} - -@keyframes fadeIn__95856 { - 0% { - opacity: 0 - } - - to { - opacity: 1 - } -} - -.overviewContainerWithVideo__95856 { - background-color: var(--background-base-low); - border-end-end-radius: var(--radius-sm); - border-end-start-radius: var(--radius-sm); - display: flex; - flex-direction: column; - gap: var(--space-8); - padding: var(--space-12) -} - -.tabBar__95856 { - height: 29px -} - -.tabBar__95856 .tabItem__95856 { - font-weight: 500; - line-height: 18px -} - -.overviewContainerNoVideo__95856 { - background-color: var(--background-base-low); - border-radius: var(--radius-sm); - display: flex; - flex-direction: column; - gap: var(--space-8); - padding: var(--space-12) -} - -.titleContainer__95856 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-8) -} - -.partnerLabelContainer__95856 { - background-color: var(--interactive-background-active); - border-radius: var(--radius-lg); - justify-content: center; - padding: 2px var(--space-8) -} - -.tagsContainer__95856 { - flex-wrap: wrap; - max-height: 24px; - overflow: hidden -} - -.tagContainer__95856,.tagsContainer__95856 { - display: flex; - gap: var(--space-4) -} - -.tagContainer__95856 { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: var(--space-4); - flex-direction: row; - padding: 2px var(--space-4) -} - -.descriptionContainer__95856 { - display: flex; - flex-direction: column; - gap: var(--space-4) -} - -.primaryEntryPointWarningMessage__95856 { - margin-top: 4px -} - -.entrypointContainer__95856 { - display: flex; - gap: 8px; - height: 48px; - margin-top: var(--space-12) -} - -.developerShelfControlsContainer__95856+.entrypointContainer__95856 { - margin-top: var(--space-4) -} - -.experimentTitleContainer__95856 { - display: flex; - flex-direction: column; - gap: 4px -} - -.expandableDescriptionClickable__95856 { - cursor: pointer; - display: flex; - flex-direction: row; - gap: var(--space-4) -} - -.overflowHidden__95856 { - overflow: hidden -} - -.developerShelfControlsContainer__95856 { - border-top: 1px solid var(--border-subtle); - padding-top: 12px -} - -.developerShelfControlsContainer__95856:not(:last-child) { - border-bottom: 1px solid var(--border-subtle); - padding-bottom: 12px -} - -.developerShelfControls__95856 { - padding: 0 -} - -.monetizationDisclosureContainerStyle__95856 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--custom-disclosure-spacing); - margin-bottom: 16.75px -} - -.monetizationDisclosureStyle__95856 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-4) -} - -.container__95856.fixedHeight__95856 { - height: 100% -} - -:root { - --custom-app-launcher-sticky-header-height: 66px; - --custom-app-launcher-container-border-radius: var(--radius-sm) -} - -.container__7bdb0 { - display: flex; - flex-direction: column; - overflow-y: auto -} - -.appIcon__7bdb0,.container__7bdb0 { - background-color: var(--background-base-lower) -} - -.appIcon__7bdb0 { - border: var(--space-4) solid var(--background-base-lower); - border-radius: var(--radius-xl); - height: 80px; - inset-inline-start: var(--space-12); - position: absolute; - top: 0; - transform: translateY(var(--custom-app-launcher-sticky-header-height)); - width: 80px -} - -.brokenImageIcon_f68fcb { - color: var(--background-mod-muted); - height: auto; - -o-object-fit: contain; - object-fit: contain; - width: 48px -} - -.activityBadge__44107 { - background-color: var(--status-positive-background) -} - -.activityBadge__44107,.activityNewBadge__44107 { - align-items: center; - box-shadow: var(--elevation-low); - display: flex -} - -.activityNewBadge__44107 { - background-color: var(--background-feedback-notification) -} - -.activityUpdatedBadge__44107 { - align-items: center; - background-color: var(--badge-background-brand); - box-shadow: var(--elevation-low); - display: flex -} - -.activityBadgeIcon__44107 { - margin-inline:-2px 2px} - -.tooltip__44107 { - text-align: center -} - -.rowIcon_cb32c7 { - --custom-row-icon-size: 40px -} - -.appDetailsRowContainer_cb32c7,.rowDivider_cb32c7 { - --custom-row-padding: var(--space-12) -} - -.container_cb32c7 { - background-color: var(--background-surface-high); - cursor: pointer; - overflow: hidden -} - -.container_cb32c7:hover,.keyboard-mode .container_cb32c7:focus { - background-color: var(--interactive-background-hover) -} - -.container_cb32c7:active { - background-color: var(--interactive-background-active) -} - -.containerBorderRadius_cb32c7 { - border-radius: var(--radius-md) -} - -.rowContainer_cb32c7:first-child { - border-start-end-radius: var(--radius-md); - border-start-start-radius: var(--radius-md) -} - -.rowContainer_cb32c7:last-child { - border-end-end-radius: var(--radius-md); - border-end-start-radius: var(--radius-md) -} - -.rowContainer_cb32c7:last-child .rowDivider_cb32c7 { - display: none -} - -.rowDivider_cb32c7 { - background-color: var(--background-mod-subtle); - bottom: 0; - height: 1px; - inset-inline: 0; - position: absolute -} - -.containerDisabled_cb32c7 { - background-color: var(--background-surface-high); - cursor: not-allowed; - overflow: hidden -} - -.icon_cb32c7 { - --custom-icon-size: 38px; - height: var(--custom-icon-size); - width: var(--custom-icon-size) -} - -.rowIcon_cb32c7 { - height: var(--custom-row-icon-size); - width: var(--custom-row-icon-size) -} - -.iconContainer_cb32c7 { - position: relative -} - -.iconCard_cb32c7 { - --custom-icon-card-size: 48px; - height: var(--custom-icon-card-size); - width: var(--custom-icon-card-size) -} - -.darkenImage_cb32c7 { - filter: brightness(.4) -} - -.bannerImageContainer_cb32c7 { - display: none; - overflow: hidden; - position: relative; - width: 100% -} - -.bannerImageContainer_cb32c7.mediumBanner_cb32c7 { - aspect-ratio: 3/1; - display: block -} - -.bannerImageContainer_cb32c7.largeBanner_cb32c7 { - aspect-ratio: 16/9; - display: block -} - -.bannerImage_cb32c7 { - align-items: center; - animation: fadeIn_cb32c7 .7s; - background-color: var(--background-surface-highest); - display: flex; - height: 100%; - justify-content: center; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.bannerImage_cb32c7.disableFadeIn_cb32c7 { - animation: none -} - -.bannerUpperRightContainer_cb32c7 { - display: flex; - flex-direction: row; - gap: var(--space-4); - inset-inline-end: var(--space-8); - position: absolute; - top: var(--space-8) -} - -.promotedLabelWrapperBanner_cb32c7 { - background-color: color-mix(in oklab,var(--control-secondary-background-default) 80%,transparent 20%); - border-radius: var(--radius-lg); - justify-content: center; - padding: var(--space-4) var(--space-8) -} - -.promotedLabelWrapperNonBanner_cb32c7 { - align-items: center; - background-color: var(--interactive-background-active); - border-radius: var(--radius-lg); - height: 100%; - justify-content: center; - padding: 2px var(--space-4) -} - -.appDetailsContainer_cb32c7 { - align-items: center; - display: flex; - flex-direction: row; - padding: var(--space-8); - position: relative -} - -.appDetailsRowContainer_cb32c7 { - padding: var(--custom-row-padding) -} - -.appDetails_cb32c7 { - display: flex; - flex-direction: column; - flex-shrink: 1; - margin-inline-start:var(--space-12);overflow: hidden -} - -.appDetailsHeaderContainer_cb32c7 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-4) -} - -.bannerImageChildContainer_cb32c7 { - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.activityVideoContainer_cb32c7,.voiceLauncherAppCardContainer_cb32c7 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.activityVideoContainer_cb32c7 { - position: absolute -} - -.activityVideo_cb32c7 { - animation: fadeIn_cb32c7 .5s; - height: 100%; - width: 100% -} - -.activityVideo_cb32c7.videoFadeOut_cb32c7 { - animation: fadeOut_cb32c7 .5s; - animation-fill-mode: forwards -} - -.activityVideo_cb32c7>video { - -o-object-fit: cover!important; - object-fit: cover!important -} - -@keyframes fadeIn_cb32c7 { - 0% { - opacity: 0 - } - - to { - opacity: 1 - } -} - -@keyframes fadeOut_cb32c7 { - 0% { - opacity: 1 - } - - to { - opacity: 0 - } -} - -.tooltipContent_cb32c7 { - text-align: center -} - -.spinner_cb32c7 { - background-color: var(--background-scrim); - height: 100%; - inset-inline-start: 0; - margin: 0; - position: absolute; - top: 0; - width: 100% -} - -.devShelfBadge_cb32c7,.spinner_cb32c7 { - align-items: center; - display: flex; - justify-content: center -} - -.devShelfBadge_cb32c7 { - background-color: var(--background-mod-strong); - border-radius: 50%; - color: var(--white); - height: 22px; - width: 22px; - z-index: 1 -} - -.devShelfIcon_cb32c7 { - height: 18px; - width: 18px -} - -.staffBadge_cb32c7 { - height: 20px; - width: 20px -} - -.emptyStateContainer_cce1d3 { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - margin-top: var(--space-8); - text-align: center -} - -.emptyStateImage_cce1d3 { - height: 180; - width: 180 -} - -.header__32c75 { - align-items: center; - display: flex; - justify-content: space-between -} - -.viewMore__32c75 { - cursor: pointer -} - -.loadingHeader__32c75 { - background-color: var(--background-mod-subtle); - border-radius: var(--radius-round); - display: flex -} - -.hidden__32c75 { - opacity: 0; - touch-action: none; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.container__19cf2 { - align-items: center; - background-color: var(--background-mod-muted); - border-radius: var(--radius-lg); - display: flex; - flex-direction: row; - padding: var(--space-16) -} - -.containerPadding__19cf2 { - padding: 0 32px 32px -} - -.image__19cf2 { - height: 80px; - margin-inline-end:var(--space-8);width: 80px -} - -.body__19cf2 { - flex: 1; - flex-direction: column; - margin-inline-end:var(--space-32)} - -.icon__761e5 { - --custom-app-icon-size: 40px -} - -.focusBlock__761e5 { - --custom-container-padding: var(--space-12) -} - -.clickable__761e5 { - background-color: var(--background-surface-high); - cursor: pointer; - position: relative -} - -.clickable__761e5:hover,.keyboard-mode .clickable__761e5:focus { - background-color: var(--interactive-background-hover) -} - -.clickable__761e5:active { - background-color: var(--interactive-background-active) -} - -.clickable__761e5:first-child { - border-start-end-radius: var(--radius-md); - border-start-start-radius: var(--radius-md) -} - -.clickable__761e5:last-child { - border-end-end-radius: var(--radius-md); - border-end-start-radius: var(--radius-md) -} - -.clickable__761e5:last-child .underline__761e5 { - display: none -} - -.focusBlock__761e5 { - align-items: center; - display: flex; - flex-direction: row; - overflow: hidden; - padding: var(--custom-container-padding) -} - -.icon__761e5 { - height: var(--custom-app-icon-size); - width: var(--custom-app-icon-size) -} - -.cmdDetails__761e5 { - display: flex; - flex-direction: column; - flex-shrink: 1; - margin-inline:var(--space-12) auto;overflow: hidden -} - -.cmdAppName__761e5 { - margin-inline-start:var(--space-8);white-space: nowrap -} - -.underline__761e5 { - background-color: var(--background-mod-subtle); - bottom: 0; - height: 1px; - inset-inline: 0; - position: absolute -} - -.iconPlaceholder__1729d,.underline__1729d { - --custom-app-icon-size: 40px -} - -.container__1729d,.underline__1729d { - --custom-container-padding: var(--space-12) -} - -.container__1729d { - align-items: center; - background-color: var(--background-surface-high); - display: flex; - flex-direction: row; - overflow: hidden; - padding: var(--custom-container-padding); - position: relative -} - -.container__1729d:after { - background: linear-gradient(-45deg,transparent,#fff,transparent); - content: ""; - height: 100%; - inset-inline-start: 0; - opacity: .05; - position: absolute; - top: 0; - transform: skewX(-20deg) translateX(-100%); - width: 100% -} - -.full-motion .container__1729d:after { - animation: shimmer__1729d 1s ease-in-out infinite -} - -.container__1729d.noAnimation__1729d:after { - display: none -} - -.container__1729d:nth-child(2):after { - animation-delay: 50ms -} - -.container__1729d:nth-child(3):after { - animation-delay: .1s -} - -.container__1729d:nth-child(4):after { - animation-delay: .15s -} - -.container__1729d:nth-child(5):after { - animation-delay: .2s -} - -.container__1729d:nth-child(6):after { - animation-delay: .25s -} - -.container__1729d:first-child { - border-start-end-radius: var(--radius-md); - border-start-start-radius: var(--radius-md) -} - -.container__1729d:last-child { - border-end-end-radius: var(--radius-md); - border-end-start-radius: var(--radius-md) -} - -.container__1729d:last-child .underline__1729d { - display: none -} - -.theme-light .container__1729d:after { - opacity: .5 -} - -@keyframes shimmer__1729d { - to { - transform: skewX(-20deg) translateX(100%) - } -} - -.iconPlaceholder__1729d { - background-color: var(--background-mod-subtle); - border-radius: 25%; - height: var(--custom-app-icon-size); - width: var(--custom-app-icon-size) -} - -.textContainer__1729d { - display: flex; - flex: 1; - flex-direction: column; - margin-inline-start:var(--space-12)} - -.textPlaceholder__1729d { - align-self: flex-start; - background-color: var(--background-mod-subtle); - border-radius: var(--radius-round) -} - -.textPlaceholder__1729d:not(:last-child) { - margin-bottom: 4px -} - -.hidden__1729d { - line-height: 1; - opacity: 0; - touch-action: none; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.underline__1729d { - background-color: var(--background-mod-subtle); - bottom: 0; - height: 1px; - inset-inline: calc(var(--custom-container-padding) + var(--custom-app-icon-size) + var(--space-12)) 0; - position: absolute -} - -.sectionContentContainer_b18295 { - margin-bottom: var(--space-24); - margin-top: var(--space-12); - overflow: hidden -} - -.sectionActivitiesContentContainer_b18295 { - display: grid; - gap: var(--space-12); - grid-template-columns: repeat(2,1fr); - margin-bottom: var(--space-24); - margin-top: var(--space-12) -} - -.searchOpenAppDetailAppCard_b18295 { - overflow: hidden -} - -.container__927fc { - display: flex; - flex-direction: column; - height: 100%; - width: 100% -} - -.developerShelfControlsContainer__927fc>:last-child { - padding-bottom: 0 -} - -.developerShelfControlsLabel__927fc,.searchBarContainer__927fc { - padding: var(--space-16) -} - -.searchBarContainer__927fc { - flex-grow: 0 -} - -.scrollableContent__927fc { - display: flex; - flex: 1; - flex-direction: column; - overflow-y: auto; - padding: 0 var(--space-16) var(--space-16); - row-gap: var(--space-16) -} - -.sectionContentContainer__927fc { - margin-bottom: var(--space-32); - margin-top: var(--space-12) -} - -.sectionTwoColumnContentContainer__927fc { - display: grid; - gap: var(--space-12); - grid-template-columns: repeat(2,1fr) -} - -.sectionRowsContentContainer__927fc { - display: flex; - flex-direction: column -} - -.frecentList__927fc { - -moz-column-gap: var(--space-12); - column-gap: var(--space-12); - display: flex; - flex-direction: row -} - -.container_c3787a { - display: flex; - flex-direction: column -} - -.header_c3787a { - align-items: center; - display: grid; - grid-template-columns: 1fr auto 1fr; - justify-content: center; - margin: var(--space-16); - margin-bottom: var(--space-8); - position: relative -} - -.backButton_c3787a { - background-color: var(--background-base-low); - margin-inline-end:auto} - -.backButton_c3787a:hover,.keyboard-mode .backButton_c3787a:focus { - background-color: var(--interactive-background-hover); - cursor: pointer -} - -.backButton_c3787a:active { - background-color: var(--interactive-background-active) -} - -.appGrid_c3787a { - gap: var(--space-12); - grid-template-columns: repeat(2,1fr) -} - -.appGrid_c3787a,.rows_c3787a { - display: grid; - margin: var(--space-8) var(--space-16) -} - -.rows_c3787a { - grid-template-columns: repeat(1,1fr) -} - -.drawerSizingWrapper__9c62c { - flex-grow: 1; - height: 100%; - max-width: 100%; - pointer-events: all; - position: relative; - z-index: 1 -} - -.contentWrapper__9c62c { - background-color: var(--background-base-lower); - border-radius: var(--custom-app-launcher-container-border-radius); - box-shadow: var(--shadow-border),var(--shadow-high); - box-sizing: border-box; - display: flex; - height: 100%; - overflow: hidden; - z-index: 1 -} - -.slideContent__9c62c { - display: flex; - overflow: hidden; - width: 100% -} - -.enable-forced-colors .resizeHandle__9c62c { - background-color: ButtonText -} - -.enable-forced-colors .contentWrapper__9c62c { - border: 3px solid CanvasText -} - -.positionContainer__31a96 { - display: flex; - height: var(--custom-app-launcher-height); - pointer-events: none; - width: var(--custom-app-launcher-width) -} - -.positionLayer__31a96 { - pointer-events: none; - z-index: 0 -} - -.heading-sm\/normal__13533 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.heading-sm\/normal__13533.fontScaling__13533 { - font-size: .875rem -} - -.heading-sm\/medium__13533 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.heading-sm\/medium__13533.fontScaling__13533 { - font-size: .875rem -} - -.heading-sm\/semibold__13533 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.heading-sm\/semibold__13533.fontScaling__13533 { - font-size: .875rem -} - -.heading-sm\/bold__13533 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.heading-sm\/bold__13533.fontScaling__13533 { - font-size: .875rem -} - -.heading-sm\/extrabold__13533 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 800; - line-height: 1.2857142857142858 -} - -.heading-sm\/extrabold__13533.fontScaling__13533 { - font-size: .875rem -} - -.heading-md\/normal__13533 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.heading-md\/normal__13533.fontScaling__13533 { - font-size: 1rem -} - -.heading-md\/medium__13533 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.heading-md\/medium__13533.fontScaling__13533 { - font-size: 1rem -} - -.heading-md\/semibold__13533 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.heading-md\/semibold__13533.fontScaling__13533 { - font-size: 1rem -} - -.heading-md\/bold__13533 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.heading-md\/bold__13533.fontScaling__13533 { - font-size: 1rem -} - -.heading-md\/extrabold__13533 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 800; - line-height: 1.25 -} - -.heading-md\/extrabold__13533.fontScaling__13533 { - font-size: 1rem -} - -.heading-lg\/normal__13533 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.heading-lg\/normal__13533.fontScaling__13533 { - font-size: 1.25rem -} - -.heading-lg\/medium__13533 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.heading-lg\/medium__13533.fontScaling__13533 { - font-size: 1.25rem -} - -.heading-lg\/semibold__13533 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.heading-lg\/semibold__13533.fontScaling__13533 { - font-size: 1.25rem -} - -.heading-lg\/bold__13533 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.heading-lg\/bold__13533.fontScaling__13533 { - font-size: 1.25rem -} - -.heading-lg\/extrabold__13533 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 800; - line-height: 1.2 -} - -.heading-lg\/extrabold__13533.fontScaling__13533 { - font-size: 1.25rem -} - -.heading-xl\/normal__13533 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xl\/normal__13533.fontScaling__13533 { - font-size: 1.5rem -} - -.heading-xl\/medium__13533 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xl\/medium__13533.fontScaling__13533 { - font-size: 1.5rem -} - -.heading-xl\/semibold__13533 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xl\/semibold__13533.fontScaling__13533 { - font-size: 1.5rem -} - -.heading-xl\/bold__13533 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xl\/bold__13533.fontScaling__13533 { - font-size: 1.5rem -} - -.heading-xl\/extrabold__13533 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xl\/extrabold__13533.fontScaling__13533 { - font-size: 1.5rem -} - -.heading-xxl\/normal__13533 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xxl\/normal__13533.fontScaling__13533 { - font-size: 2rem -} - -.heading-xxl\/medium__13533 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xxl\/medium__13533.fontScaling__13533 { - font-size: 2rem -} - -.heading-xxl\/semibold__13533 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xxl\/semibold__13533.fontScaling__13533 { - font-size: 2rem -} - -.heading-xxl\/bold__13533 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xxl\/bold__13533.fontScaling__13533 { - font-size: 2rem -} - -.heading-xxl\/extrabold__13533 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xxl\/extrabold__13533.fontScaling__13533 { - font-size: 2rem -} - -.eyebrow__13533 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - letter-spacing: .02em; - line-height: 1.3333333333333333; - text-transform: uppercase -} - -.eyebrow__13533.fontScaling__13533 { - font-size: .75rem -} - -.heading-deprecated-12\/normal__13533 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/normal__13533.fontScaling__13533 { - font-size: .75rem -} - -.heading-deprecated-12\/medium__13533 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/medium__13533.fontScaling__13533 { - font-size: .75rem -} - -.heading-deprecated-12\/semibold__13533 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/semibold__13533.fontScaling__13533 { - font-size: .75rem -} - -.heading-deprecated-12\/bold__13533 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/bold__13533.fontScaling__13533 { - font-size: .75rem -} - -.heading-deprecated-12\/extrabold__13533 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 800; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/extrabold__13533.fontScaling__13533 { - font-size: .75rem -} - -.redesign\/heading-18\/bold__13533 { - font-family: var(--font-display); - font-size: 18px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/heading-18\/bold__13533.fontScaling__13533 { - font-size: 1.125rem -} - -.text-xxs\/normal__13533 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 400; - line-height: 1.2 -} - -.text-xxs\/normal__13533.fontScaling__13533 { - font-size: .625rem -} - -.text-xxs\/medium__13533 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 500; - line-height: 1.2 -} - -.text-xxs\/medium__13533.fontScaling__13533 { - font-size: .625rem -} - -.text-xxs\/semibold__13533 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 600; - line-height: 1.2 -} - -.text-xxs\/semibold__13533.fontScaling__13533 { - font-size: .625rem -} - -.text-xxs\/bold__13533 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 700; - line-height: 1.2 -} - -.text-xxs\/bold__13533.fontScaling__13533 { - font-size: .625rem -} - -.text-xs\/normal__13533 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.text-xs\/normal__13533.fontScaling__13533 { - font-size: .75rem -} - -.text-xs\/medium__13533 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.text-xs\/medium__13533.fontScaling__13533 { - font-size: .75rem -} - -.text-xs\/semibold__13533 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.text-xs\/semibold__13533.fontScaling__13533 { - font-size: .75rem -} - -.text-xs\/bold__13533 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.text-xs\/bold__13533.fontScaling__13533 { - font-size: .75rem -} - -.text-sm\/normal__13533 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.text-sm\/normal__13533.fontScaling__13533 { - font-size: .875rem -} - -.text-sm\/medium__13533 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.text-sm\/medium__13533.fontScaling__13533 { - font-size: .875rem -} - -.text-sm\/semibold__13533 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.text-sm\/semibold__13533.fontScaling__13533 { - font-size: .875rem -} - -.text-sm\/bold__13533 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.text-sm\/bold__13533.fontScaling__13533 { - font-size: .875rem -} - -.text-md\/normal__13533 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.text-md\/normal__13533.fontScaling__13533 { - font-size: 1rem -} - -.text-md\/medium__13533 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.text-md\/medium__13533.fontScaling__13533 { - font-size: 1rem -} - -.text-md\/semibold__13533 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.text-md\/semibold__13533.fontScaling__13533 { - font-size: 1rem -} - -.text-md\/bold__13533 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.text-md\/bold__13533.fontScaling__13533 { - font-size: 1rem -} - -.text-lg\/normal__13533 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.text-lg\/normal__13533.fontScaling__13533 { - font-size: 1.25rem -} - -.text-lg\/medium__13533 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.text-lg\/medium__13533.fontScaling__13533 { - font-size: 1.25rem -} - -.text-lg\/semibold__13533 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.text-lg\/semibold__13533.fontScaling__13533 { - font-size: 1.25rem -} - -.text-lg\/bold__13533 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.text-lg\/bold__13533.fontScaling__13533 { - font-size: 1.25rem -} - -.redesign\/message-preview\/normal__13533 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/normal__13533.fontScaling__13533 { - font-size: .9375rem -} - -.redesign\/message-preview\/medium__13533 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/medium__13533.fontScaling__13533 { - font-size: .9375rem -} - -.redesign\/message-preview\/semibold__13533 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/semibold__13533.fontScaling__13533 { - font-size: .9375rem -} - -.redesign\/message-preview\/bold__13533 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/bold__13533.fontScaling__13533 { - font-size: .9375rem -} - -.redesign\/channel-title\/normal__13533 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.375 -} - -.redesign\/channel-title\/normal__13533.fontScaling__13533 { - font-size: 1rem -} - -.redesign\/channel-title\/medium__13533 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.375 -} - -.redesign\/channel-title\/medium__13533.fontScaling__13533 { - font-size: 1rem -} - -.redesign\/channel-title\/semibold__13533 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.375 -} - -.redesign\/channel-title\/semibold__13533.fontScaling__13533 { - font-size: 1rem -} - -.redesign\/channel-title\/bold__13533 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.375 -} - -.redesign\/channel-title\/bold__13533.fontScaling__13533 { - font-size: 1rem -} - -.display-sm__13533 { - font-family: var(--font-headline); - font-size: 20px; - font-weight: 800; - line-height: 1 -} - -.display-sm__13533.fontScaling__13533 { - font-size: 1.25rem -} - -.display-md__13533 { - font-family: var(--font-headline); - font-size: 34px; - font-weight: 800; - line-height: 1.0588235294117647 -} - -.display-md__13533.fontScaling__13533 { - font-size: 2.125rem -} - -.display-lg__13533 { - font-family: var(--font-headline); - font-size: 44px; - font-weight: 800; - line-height: .9545454545454546 -} - -.display-lg__13533.fontScaling__13533 { - font-size: 2.75rem -} - -.code__13533 { - font-family: var(--font-code); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.code__13533.fontScaling__13533 { - font-size: .75rem -} - -.autocomplete__13533 { - background-color: var(--background-surface-high); - border-radius: 5px 5px 0 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - z-index: 3 -} - -.autocompleteInner__13533 { - padding-bottom: 8px -} - -.autocompleteRow__13533 { - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 16px; - padding: 0 8px -} - -.autocompleteRowVertical__13533 { -} - -.autocompleteRowHorizontal__13533 { - padding: 0 -} - -.autocompleteRowContent__13533 { - align-items: center; - color: var(--interactive-text-default); - display: flex; - min-height: 16px -} - -.autocompleteRowContentPrimary__13533 { - flex-grow: 0; - flex-shrink: 1; - min-width: 10ch; - overflow: hidden -} - -.autocompleteRowIcon__13533 { - flex: 0 0 auto; - margin-inline-end:8px} - -.autocompleteRowHeading__13533 { - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.autocompleteRowSubheading__13533 { - margin-top: 2px -} - -.autocompleteRowContentSecondary__13533 { - flex-basis: 10ch; - flex-grow: 1; - flex-shrink: 0; - margin-inline-start:var(--space-16);min-width: 10ch; - overflow: hidden; - text-align: end; - text-overflow: ellipsis; - white-space: nowrap -} - -.base__13533 { - border-radius: 3px; - padding: 8px -} - -.clickable__13533[aria-disabled=false]>.base__13533 { - cursor: pointer -} - -.clickable__13533[aria-disabled=false]>.base__13533:hover,.clickable__13533[aria-selected=true]>.base__13533 { - background-color: var(--interactive-background-hover) -} - -.dividerContainer__13533 .base__13533 { - padding-bottom: 0; - padding-top: 0 -} - -.divider__13533 { - padding: 8px -} - -.divider__13533:after { - background-color: var(--border-subtle); - content: ""; - display: block; - height: 1px; - width: 100% -} - -.contentTitle__13533 { - color: var(--interactive-text-default); - padding: 4px 0; - text-transform: uppercase -} - -.contentTitle__13533 strong { - color: var(--text-subtle); - text-transform: none -} - -.icon__13533 { - color: var(--interactive-text-default); - height: 16px; - -o-object-fit: contain; - object-fit: contain; - width: 16px -} - -.iconForeground__13533 { - fill: var(--interactive-text-default) -} - -.emojiImage__13533 { - height: 20px; - -o-object-fit: contain; - object-fit: contain; - width: 20px -} - -.emojiRaw__13533 { - display: block; - font-size: 20px -} - -.descriptionDiscriminator__13533 { - opacity: .6 -} - -.autocompleteRowVerticalSmall__13533 .base__13533 { - padding: 4px 8px -} - -.roleDot__13533 { - margin-inline-end:4px} - -.autocompletePlaceholder__13533 { - background: var(--background-base-low); - border-radius: 16px; - height: 16px; - margin-top: 8px -} - -.autocompleteContentWrapper__13533 { - display: flex; - flex-grow: 1; - gap: var(--space-16) -} - -.autoCompleteRowSuggestion__13533 { -} - -.autoCompleteRowSuggestion__13533 .base__13533 { - border-radius: 0; - padding: var(--space-12) -} - -.autoCompleteRowSuggestion__13533 .autocompleteRowContentPrimary__13533 { - min-width: 12ch -} - -.autoCompleteRowSuggestion__13533 .autocompleteContentWrapper__13533 { - align-items: baseline; - gap: var(--space-8); - margin-inline-end:var(--space-16)} - -.autoCompleteRowSuggestion__13533 .autocompleteRowContentSecondary__13533 { - flex-basis: auto; - margin-inline-start:0;min-width: 0; - text-align: start -} - -.autoCompleteRowSuggestion__13533 .keyComboContainer__13533 { - display: flex; - justify-content: flex-end; - min-width: 3em -} - -.autoCompleteRowSuggestion__13533 .verticalLayout__13533 .autocompleteContentWrapper__13533 { - flex-direction: column; - gap: 0 -} - -.autoCompleteRowSuggestion__13533 .verticalLayout__13533 .autocompleteRowContentPrimary__13533 { - composes: text-xs\/medium -} - -.autoCompleteRowSuggestion__13533 .verticalLayout__13533 .autocompleteRowContentSecondary__13533 { - composes: text-xxs\/medium; - flex-basis: auto -} - -.enable-forced-colors .autocomplete__13533 { - border: 2px solid CanvasText -} - -.enable-forced-colors .clickable__13533[aria-selected=true]>.base__13533 { - outline: 2px solid Highlight; - outline-offset: Highlight -} - -.enable-forced-colors .descriptionDiscriminator__13533 { - opacity: 1 -} - -.option_a19535 { - border-radius: 4px; - display: block; - font-weight: var(--font-weight-normal); - padding: 0 4px -} - -.theme-light .option_a19535 { - background-color: var(--primary-300) -} - -.theme-dark .option_a19535 { - background-color: var(--primary-800) -} - -.inline_a19535 { - margin-inline-end:4px;padding-bottom: 1px -} - -.set_a19535 { - opacity: .5 -} - -.error_a19535 { - color: var(--text-feedback-critical) -} - -.active_a19535 { - background-color: var(--brand-500)!important; - color: var(--white) -} - -.clickable_a19535 { - cursor: pointer -} - -.mask_ca5f52 { - display: block -} - -.icon_ca5f52 { - height: 32px; - width: 32px -} - -.wrapper_ca5f52 { - border-radius: 8px; - transition: background-color .1s ease-in-out -} - -.selectable_ca5f52:hover { - background-color: var(--interactive-background-hover) -} - -.selectable_ca5f52:hover .icon_ca5f52 { - color: var(--interactive-text-hover) -} - -.selected_ca5f52,.selected_ca5f52:hover { - background-color: var(--background-base-low) -} - -.selected_ca5f52 .icon_ca5f52,.selected_ca5f52:hover .icon_ca5f52 { - color: var(--interactive-text-active) -} - -.theme-light .selected_ca5f52 .icon_ca5f52,.theme-light .selected_ca5f52:hover .icon_ca5f52 { - color: var(--primary-500) -} - -.icon__1a58a { - color: var(--interactive-text-default) -} - -.wrapper__1a58a { - border-radius: 8px; - transition: background-color .1s ease-in-out -} - -.selectable__1a58a:hover { - background-color: var(--interactive-background-hover) -} - -.selectable__1a58a:hover .icon__1a58a { - color: var(--interactive-text-hover) -} - -.selected__1a58a,.selected__1a58a:hover { - background-color: var(--background-base-low) -} - -.selected__1a58a .icon__1a58a,.selected__1a58a:hover .icon__1a58a { - color: var(--interactive-text-active) -} - -.theme-light .selected__1a58a .icon__1a58a,.theme-light .selected__1a58a:hover .icon__1a58a { - color: var(--primary-500) -} - -.wrapper__920ab { - align-items: center; - display: flex; - flex-direction: row; - height: 40px -} - -.image__920ab { - align-self: flex-start; - flex-grow: 0; - flex-shrink: 0; - justify-self: flex-start; - margin-top: 4px; - margin-inline-end:16px} - -.infoWrapper__920ab { - display: flex; - flex-direction: column; - flex-grow: 1; - overflow: hidden -} - -.source__920ab { - flex-grow: 0; - flex-shrink: 0; - margin-inline-start:16px;max-width: 33%; - overflow: hidden; - text-overflow: ellipsis -} - -.usageWrapper__920ab { - align-items: center; - display: flex; - flex-direction: row -} - -.description__920ab { - margin-top: 4px; - white-space: nowrap -} - -.description__920ab,.title__920ab { - overflow: hidden; - text-overflow: ellipsis -} - -.title__920ab { - flex-shrink: 0; - font-weight: var(--font-weight-medium) -} - -.option__920ab { - margin-inline-start:8px} - -.error__920ab { - color: var(--text-feedback-critical) -} - -.optionalHeader__920ab { - color: var(--text-muted); - text-transform: uppercase -} - -.optionalCount__920ab,.optionalHeader__920ab { - padding-inline-start:8px} - -.disabled__920ab { - opacity: .3 -} - -.optionals__920ab { - align-items: center; - border-inline-start:1px solid var(--border-subtle);display: flex; - flex-direction: row; - margin-inline-start:8px} - -.optionalNames__920ab { - display: flex; - flex-direction: column -} - -.wrapper__78aa3 { - align-items: center; - display: flex; - flex-direction: row; - height: 40px; - justify-content: space-between; - padding: 8px -} - -.infoWrapper__78aa3 { - display: flex; - flex-direction: column; - flex-grow: 1; - overflow: hidden -} - -.usageWrapper__78aa3 { - align-items: center; - background: var(--background-mod-strong); - border-radius: 16px; - display: flex; - flex-direction: row; - flex-grow: 1; - height: 16px -} - -.image__78aa3 { - align-self: flex-start; - background: var(--background-base-low); - border-radius: 50%; - flex-grow: 0; - flex-shrink: 0; - height: 32px; - justify-self: flex-start; - margin-top: 4px; - margin-inline-end:16px;width: 32px -} - -.description__78aa3 { - margin-top: 8px -} - -.description__78aa3,.source__78aa3 { - background: var(--background-base-low); - border-radius: 16px; - height: 16px -} - -.source__78aa3 { - flex-shrink: 0; - margin-inline-start:16px} - -.upsell__98044 { - margin-bottom: -4px; - margin-top: -4px -} - -.emojis__98044 { - display: flex -} - -.emojiMask__98044 { - position: relative -} - -.emojiBackground__98044 { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: 50%; - display: flex; - height: 24px; - justify-content: center; - width: 24px -} - -.emoji__98044 { - height: 16px; - width: 16px -} - -.wrapper_b1e4f3 { - background-color: var(--background-base-lowest); - margin-bottom: -16px; - padding-bottom: 8px; - width: 48px -} - -.list_b1e4f3 { - background: none; - height: 100% -} - -.section_b1e4f3 { - border-radius: 4px; - cursor: pointer; - margin-bottom: 8px -} - -.section_b1e4f3:last-child { - margin-bottom: 0 -} - -.builtInSeparator_b1e4f3 { - border: none; - border-bottom: 1px solid var(--border-subtle); - border-color: var(--border-subtle); - margin: 8px 0 -} - -.outerWrapper_d1405b { - border-radius: 5px; - box-shadow: var(--shadow-border),var(--shadow-high); - inset-inline: 0 var(--space-8); - bottom: calc(100% + 8px); - overflow: hidden; - position: absolute -} - -.wrapper_d1405b { - display: flex; - flex-direction: row; - height: 420px; - padding-bottom: 0 -} - -.noSearchResults_d1405b { - height: 340px -} - -.list_d1405b { - height: 100% -} - -.rail_d1405b { - align-self: stretch; - flex-grow: 0; - flex-shrink: 0 -} - -.content_d1405b { - display: flex; - flex-direction: column; - flex-grow: 1 -} - -.categoryHeader_d1405b { - background-color: var(--background-surface-high); - padding: 0 8px; - position: sticky; - top: 0 -} - -.categorySection_d1405b { - margin-bottom: 16px -} - -.categorySectionLast_d1405b { - margin-bottom: 0 -} - -.itemWrapper_d1405b { - padding: 0 -} - -.inlineElement__1464f { - display: inline-block -} - -.inlineVoid__1464f { - cursor: default -} - -.inlineVoid__1464f .mention { - padding-bottom: 1px -} - -.applicationCommand__1464f { - display: flex; - flex-wrap: wrap; - margin-inline-start:-4px;margin-bottom: -4px; - margin-top: -8px -} - -.applicationCommand__1464f>[data-slate-node=text]>* { - display: inline-block; - padding-top: 8px -} - -.applicationCommand__1464f>[data-slate-node=text]:not(:first-child)>:first-child { - margin-inline-start:4px} - -.applicationCommand__1464f>[data-slate-node=text]:not(:first-child)>:first-child.emptyText__1464f { - margin-inline-start:2px} - -.applicationCommand__1464f>[data-slate-node=text]:not(:last-child)>:last-child { - margin-inline-end:4px} - -.applicationCommand__1464f>[data-slate-node=text]:not(:last-child)>:last-child.emptyText__1464f { - margin-inline-end:2px} - -.applicationCommand__1464f:after { - color: var(--text-muted); - content: attr(data-trailing-placeholder); - flex: 1; - padding-inline-start:4px;padding-top: 8px; - pointer-events: none; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - white-space: nowrap -} - -.applicationCommand__1464f>[data-slate-node=text]>*,.optionPillValue__1464f>[data-slate-node=text]>* { - min-width: 4px -} - -.commandName__1464f { - font-weight: var(--font-weight-semibold); - margin-inline-start:4px} - -.optionPill__1464f { - align-self: flex-start; - border: 1px solid transparent; - border-radius: 4px; - display: flex; - justify-content: flex-start; - margin-top: 6px; - max-width: calc(100% - 30px); - vertical-align: top -} - -.optionPill__1464f .optionPill__1464f { - margin-block:0;max-width: none -} - -.optionPillKey__1464f { - border-end-start-radius: 4px; - border-start-start-radius: 4px; - display: inline-block; - flex-shrink: 0; - padding: 1px 8px; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.optionPillValue__1464f { - display: block; - flex: 1; - padding: 1px 8px; - vertical-align: top; - white-space: pre-wrap -} - -.readonlyPillValue__1464f { - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.readonlyPillValue__1464f ::-moz-selection { - background: transparent -} - -.readonlyPillValue__1464f ::selection { - background: transparent -} - -.attachmentFilename__1464f { - color: var(--brand-500) -} - -.theme-dark .optionPill__1464f { - background-color: var(--background-mod-normal); - border-color: var(--background-base-lowest) -} - -.theme-dark .optionPillKey__1464f { - background-color: var(--background-surface-high) -} - -.theme-light .optionPill__1464f { - background-color: var(--background-base-lower); - border-color: var(--interactive-background-selected) -} - -.theme-light .optionPillKey__1464f { - background-color: var(--background-base-lowest) -} - -.selectedPill__1464f { - border-color: Highlight!important -} - -.erroredPill__1464f { - border-color: var(--red-400)!important -} - -.newLine__1464f { - display: inline-block -} - -.gameMention__1464f,.timestampMention__1464f { - display: inline-flex; - margin: -4px 2px -8px -} - -.inlineFlex__1464f { - display: inline-flex; - margin: 0 -} - -.mentionSuggestion__1464f { - background-color: var(--mention-background); - border-radius: var(--radius-xs); - color: var(--mention-foreground); - font-weight: var(--font-weight-medium) -} - -.mentionSuggestion__1464f.mentionSuggestionSimpleColorVariant__1464f { - background-color: transparent; - color: var(--text-brand) -} - -.assetWrapper__31fc2 { - position: relative; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.assetWrapperMasked__31fc2 { - -webkit-mask: url(/assets/34378a8df8efb149.svg) 0 0/100% 100%; - mask: url(/assets/34378a8df8efb149.svg) 0 0/100% 100% -} - -.stickerAsset__31fc2 { - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.lottieCanvas__31fc2,.pngImage__31fc2 { -} - -.pngImage__31fc2 { - display: block; - -o-object-fit: contain; - object-fit: contain -} - -.roundedMask__31fc2 { - display: block; - height: 100%; - inset-inline-start: 0; - position: absolute; - top: 0; - width: 100% -} - -.errorContent__31fc2 { - align-items: center; - display: flex; - justify-content: center -} - -.loadingIndicator__31fc2 { - box-sizing: border-box; - cursor: default; - display: block; - -webkit-mask: url(/assets/34378a8df8efb149.svg) 0 0/100% 100%; - mask: url(/assets/34378a8df8efb149.svg) 0 0/100% 100% -} - -.error__31fc2,.loadingIndicator__31fc2 { - background-color: var(--interactive-background-active); - height: 100%; - width: 100% -} - -.error__31fc2 { - align-items: center; - display: flex; - flex-direction: column; - font-weight: var(--font-weight-semibold); - justify-content: center -} - -.errorIcon__31fc2 { - color: var(--text-default) -} - -.errorText__31fc2 { - margin-top: 4px -} - -.overlayWrapper__31fc2 { - pointer-events: none; - position: relative; - z-index: 1 -} - -.overlayStickerWrapper__31fc2 { - position: relative; - z-index: 2 -} - -.overlayLabelWrapper__31fc2 { - position: relative; - z-index: 1 -} - -.overlayLabel__31fc2 { - background-color: var(--background-surface-high); - border-radius: 20px; - inset-inline-start: 50%; - margin-top: 2px; - padding: 2px 8px; - position: absolute; - top: 100%; - transform: translateX(-50%); - white-space: nowrap -} - -.positionedLayer__31fc2 { - pointer-events: none -} - -.legacyInputCommandHeader__38272 { - display: flex; - justify-content: space-between -} - -.noAutocompleteResults__3b122 { - height: 200px -} - -.secondarySection_ed47e5 { - padding-top: 0 -} - -.divider_ed47e5 { - padding-bottom: 0!important -} - -.spinner__13d18 { - padding-top: 8px -} - -.horizontalAutocompletes__13d18 { - margin-bottom: -4px; - overflow-x: auto; - padding: 0 8px -} - -.no-webkit-scrollbar .horizontalAutocompletes__13d18 { - scrollbar-width: thin -} - -.horizontalAutocomplete__13d18 { - margin-bottom: 4px -} - -@use postcss-pxtorem;.autocomplete__6b0e0 { - border-radius: 5px; - box-shadow: var(--shadow-border),var(--shadow-high); - overflow: hidden -} - -.autocompleteAttached__6b0e0 { - bottom: calc(100% + 8px); - inset-inline: var(--space-8) var(--space-16); - position: absolute -} - -.autocompleteAttached__6b0e0.bottom__6b0e0 { - inset-inline: 0; - bottom: inherit; - top: 100% -} - -.autocompletePopout__6b0e0 { - max-width: 800px; - min-width: 430px; - pointer-events: all -} - -.autocompleteInner__6b0e0 { - padding-bottom: 0 -} - -.autocompleteTop__6b0e0 { - z-index: 1001 -} - -.scroller__6b0e0 { - display: flex; - flex-direction: column; - padding-bottom: 8px -} - -.stickerAutoComplete__6b0e0 { - display: grid; - grid-template-columns: repeat(auto-fill,116px); - grid-template-rows: repeat(auto-fill,116px); - grid-gap: 8px; - padding: 8px 16px -} - -.sticker__6b0e0 { - margin-top: 2px; - margin-inline-start:2px} - -.minimal__6b0e0 { - border-radius: 8px; - min-width: 200px -} - -.minimal__6b0e0 .scroller__6b0e0 { - margin-inline:-8px;padding-bottom: 0 -} - -.container__871cd { - align-items: flex-start; - display: flex; - flex-direction: row; - height: 100%; - margin-inline-end:12px;overflow: hidden; - position: relative -} - -.column__871cd { - height: auto; - opacity: .1; - padding-top: 12px; - transform-origin: 0 0 -} - -.gif__871cd { - background-color: var(--text-link) -} - -.categoryLoader__871cd,.gif__871cd { - border-radius: 5px; - margin-inline-start:12px;margin-bottom: 12px -} - -.categoryLoader__871cd { - background-color: var(--black); - height: 110px -} - -@keyframes ripple__43deb { - 0% { - opacity: 1; - transform: scale(.4) - } - - 90% { - opacity: 0; - transform: scale(1.4) - } - - to { - opacity: 0; - transform: scale(1.6) - } -} - -@keyframes bounce__43deb { - 25% { - transform: scale(.6) - } - - 50% { - transform: scale(1.2) - } - - to { - transform: scale(1) - } -} - -.gifFavoriteButton__43deb { - background-color: var(--background-base-low); - border-radius: 5px; - color: var(--icon-strong); - opacity: 1; - padding: 6px -} - -.gifFavoriteButton__43deb:after { - border: 2px solid var(--yellow-300); - border-radius: 50%; - box-sizing: border-box; - content: ""; - height: 100%; - inset-inline-start: 0; - opacity: 0; - pointer-events: none; - position: absolute; - top: 0; - transform-origin: center; - width: 100%; - z-index: 5 -} - -.full-motion .gifFavoriteButton__43deb:after { - transition: all .15s ease-in-out -} - -.gifFavoriteButton__43deb:focus,.gifFavoriteButton__43deb:hover { - color: var(--yellow-300); - transform: none -} - -.gifFavoriteButton__43deb.selected__43deb { - color: var(--yellow-300) -} - -.gifFavoriteButton__43deb.selected__43deb.showPulse__43deb:after { - animation: ripple__43deb .45s ease -} - -.gifFavoriteButton__43deb.selected__43deb.showPulse__43deb .icon__43deb { - animation: bounce__43deb .4s linear -} - -.icon__43deb { - display: block; - height: 100%; - width: 100% -} - -.enable-forced-colors .gifFavoriteButton__43deb { - background-color: ButtonFace; - border: 1px solid ButtonFace; - border-radius: 4px; - color: ButtonText -} - -.enable-forced-colors .gifFavoriteButton__43deb:focus,.enable-forced-colors .gifFavoriteButton__43deb:hover { - border-color: ButtonText -} - -.enable-forced-colors .gifFavoriteButton__43deb.selected__43deb { - background-color: HighlightText; - border-color: Highlight; - color: Highlight -} - -.results__2dc39 { - height: 100% -} - -.result__2dc39 { - border-radius: 5px; - cursor: pointer; - position: relative; - transition: box-shadow .15s ease-out -} - -.result__2dc39:after { - border-radius: 7px; - content: ""; - display: block; - position: absolute; - top: -1px; - inset-inline: -1px; - bottom: -1px; - pointer-events: none; - transition: background .15s ease-out; - transition-property: box-shadow,background -} - -.result__2dc39:hover .favButton__2dc39 { - opacity: 1; - transform: none -} - -.result__2dc39:hover,.result__2dc39[data-focused=true] { - box-shadow: var(--shadow-border),var(--shadow-low) -} - -.result__2dc39:hover:after,.result__2dc39[data-focused=true]:after { - background: linear-gradient(to bottom,var(--background-mod-strong),var(--background-mod-subtle)) -} - -.result__2dc39[data-focused=true] { - box-shadow: 0 0 0 2px var(--border-focus) -} - -.result__2dc39[data-selected=true] { - box-shadow: 0 0 0 3px var(--border-focus) -} - -.high-contrast-mode .result__2dc39 { - border: 1px solid var(--border-strong) -} - -.gif__2dc39 { - background-color: transparent; - border-radius: 5px; - display: block; - -o-object-fit: cover; - object-fit: cover; - position: relative; - width: 100% -} - -.endContainer__2dc39 { - padding-bottom: 32px; - padding-top: 32px; - text-align: center -} - -.endContainer__2dc39:after { - background-position: bottom; - background-repeat: no-repeat; - background-size: contain; - content: ""; - display: block; - height: 220px; - margin: 0 auto; - width: 100% -} - -.endText__2dc39 { - color: var(--text-feedback-warning); - font-size: 16px; - font-weight: var(--font-weight-medium); - margin-bottom: 8px -} - -.searchSuggestions__2dc39 { - margin-bottom: 32px -} - -.noResults__2dc39 { - height: 100% -} - -.spinnerContainer__2dc39 { - align-items: center; - display: flex; - height: 100%; - justify-content: center -} - -.favButton__2dc39 { - box-sizing: border-box; - inset-inline-end: 4px; - opacity: 0; - position: absolute; - top: 4px; - transform: translateY(-10px); - z-index: 4 -} - -.full-motion .favButton__2dc39 { - transition: transform .2s ease,opacity .1s ease -} - -.emptyHints__2dc39 { - align-items: center; - display: flex; - flex-wrap: wrap; - justify-content: space-around; - margin-block:6px;margin-inline:6px 0} - -.emptyHint__2dc39 { - flex: 1 1 33.33% -} - -.emptyHintCard__2dc39 { - align-items: center; - background-color: var(--background-mod-muted); - border-radius: 5px; - box-sizing: border-box; - color: var(--text-default); - display: flex; - height: 160px; - justify-content: center; - margin: 6px; - padding: 20px; - position: relative -} - -.emptyHintSpacer__2dc39 { - height: 160px -} - -.emptyHintText__2dc39 { - font-size: 15px; - line-height: 1.6; - max-width: 180px; - text-align: center -} - -.emptyHintFavorite__2dc39 { - color: var(--text-feedback-warning); - height: 29px; - inset-inline-end: 6px; - position: absolute; - top: 6px; - width: 29px -} - -@keyframes loadIN__2dc39 { - 0% { - opacity: 0; - transform: translate3d(0,24px,0) - } - - to { - opacity: 1; - transform: translateZ(0) - } -} - -.placeholder__2dc39 { - background: var(--background-mod-muted); - border-radius: 5px; - height: 100%; - width: 100% -} - -.full-motion .placeholder__2dc39 { - animation: loadIN__2dc39 .6s cubic-bezier(.17,.67,.16,.99); - animation-fill-mode: forwards; - opacity: 0; - transform: translate3d(0,48px,0); - will-change: transform3d,opacity -} - -.enable-forced-colors .result__2dc39[data-focused=true] { - border: 1px solid Canvas; - outline: 2px solid Highlight -} - -.images-light .endContainer__2dc39:after { - background-image: url(/assets/9423b541a1c3dfef.svg) -} - -.images-dark .endContainer__2dc39:after { - background-image: url(/assets/00f1d64f0e8c77b3.svg) -} - -.container_d02962 { - height: 100% -} - -.categoryFade_d02962,.categoryFadeBlurple_d02962 { - border: 1px solid transparent; - border-radius: 4px; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - transition: -webkit-backdrop-filter .15s ease-out; - transition: backdrop-filter .15s ease-out; - transition: backdrop-filter .15s ease-out,-webkit-backdrop-filter .15s ease-out; - z-index: 1 -} - -.categoryText_d02962 { - align-items: center; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - display: flex; - font-weight: var(--font-weight-semibold); - justify-content: center; - pointer-events: none; - z-index: 3 -} - -.categoryIcon_d02962 { - height: 20px; - margin-inline-end:4px;width: 20px -} - -.categoryName_d02962 { - font-size: 16px; - font-weight: var(--font-weight-semibold); - line-height: 16px -} - -.spinnerContainer_d02962 { - height: 100% -} - -.formatSelectors_d02962,.spinnerContainer_d02962 { - align-items: center; - display: flex; - justify-content: center -} - -.formatSelectors_d02962 { - flex-direction: row -} - -.formatSelectors_d02962 select { - display: block; - margin: 0 4px -} - -.categoryFade_d02962 { - background-color: var(--background-scrim) -} - -.categoryFadeBlurple_d02962 { - background-color: color-mix(in srgb,var(--background-brand),transparent 20%) -} - -.categoryFade_d02962:hover,.categoryFadeBlurple_d02962:hover,[data-focused=true] .categoryFade_d02962,[data-focused=true] .categoryFadeBlurple_d02962 { - -webkit-backdrop-filter: brightness(60%) blur(4px); - backdrop-filter: brightness(60%) blur(4px); - border: 1px solid var(--border-strong); - border-radius: 5px -} - -.categoryText_d02962 { - color: var(--white); - text-shadow: 0 1px 1px rgba(0,0,0,.6) -} - -.container_fed6d3 { - background-color: var(--background-base-lower); - display: flex; - flex: 1 1 auto; - flex-direction: column; - overflow: hidden; - position: relative; - white-space: normal; - width: 100% -} - -.container_fed6d3:after { - bottom: 0; - content: ""; - height: 8px; - inset-inline: 0; - pointer-events: none; - position: absolute -} - -.content_fed6d3 { - z-index: 0 -} - -.content_fed6d3,.header_fed6d3 { - position: relative -} - -.header_fed6d3 { - background-color: var(--background-surface-high); - border-bottom: 1px solid var(--border-subtle); - box-shadow: none; - flex: 0 0 auto; - padding: var(--custom-gif-picker-gutter-size); - z-index: 1 -} - -.searchHeader_fed6d3 { - margin-bottom: 0; - margin-inline-start:8px} - -.backButton_fed6d3 { - align-items: center; - color: var(--text-muted); - cursor: pointer; - display: flex; - height: 28px; - margin-inline-end:8px} - -.backButton_fed6d3:hover { - color: var(--text-default) -} - -.content_fed6d3 { - flex: 1 1 auto; - overflow: hidden -} - -.wrapper__0c74f { - display: grid; - grid-template-rows: 1fr auto; - height: 100%; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.wrapper__0d1ef { - flex: 1 -} - -.wrapper__0856d { - display: grid; - grid-template-columns: 48px auto; - grid-template-rows: auto 1fr auto; - position: relative -} - -.header__0856d { - align-items: center; - box-shadow: var(--elevation-low); - display: flex; - grid-column: 1/3; - grid-row: 1/2; - padding: 12px; - z-index: 1 -} - -.loadingIndicator__0856d { - grid-column: 1/-1; - grid-row: 1/-1 -} - -.gridNoticeWrapper__0856d,.listWrapper__0856d { - flex: 1; - inset-inline-start: 48px; - position: relative; - width: calc(100% - 48px) -} - -.gridNoticeWrapper__0856d { - flex: none -} - -.emptyState__0856d { - grid-column: 1/3; - grid-row: 2/2; - margin: 0 20px; - overflow: hidden; - position: relative -} - -.enable-forced-colors .header__0856d { - border-bottom: 1px solid CanvasText -} - -.categoryList_a1e0e0 { - top: 65px -} - -.expressionPickerCategoryList_a1e0e0 { - top: 53px -} - -.hasBottomBarUpsell_a1e0e0 { - bottom: 50px -} - -.category_a1e0e0 { - cursor: pointer; - margin-bottom: 8px; - position: relative -} - -.categoryIcon_a1e0e0 { - align-items: center; - border-radius: var(--radius-xs); - display: flex; - justify-content: center; - padding: 4px; - transition: background-color .2s ease -} - -.categoryIcon_a1e0e0.selected_a1e0e0 { - background-color: var(--background-base-low) -} - -.categoryIcon_a1e0e0:hover { - background-color: var(--interactive-background-hover) -} - -.categoryIconIcon_a1e0e0 { - height: 24px; - width: 24px -} - -.categoryIconIcon_a1e0e0,.defaultsShortcut_a1e0e0 { - color: var(--interactive-text-default) -} - -.defaultsShortcut_a1e0e0 { - align-items: center; - background-color: var(--background-base-lower); - border-top: 1px solid var(--border-normal); - bottom: 0; - box-sizing: border-box; - cursor: pointer; - display: flex; - height: var(--custom-expression-picker-constants-expression-picker-inspector-bar-height); - inset-inline-start: 0; - justify-content: center; - position: absolute; - width: 100% -} - -.defaultsShortcut_a1e0e0:hover { - color: var(--interactive-text-hover) -} - -.keybindHint__46002 { - align-items: center; - background-color: var(--background-base-lower); - border-radius: var(--radius-sm); - display: flex; - margin: 16px; - padding: 8px; - position: relative -} - -.keybindHintText__46002 { - display: inline -} - -.warningIcon__46002 { - color: var(--yellow-300); - margin-inline-end:8px;min-width: 20px -} - -.emoji__46002 { - height: 100%; - width: 100% -} - -.closeButton__46002,.emoji__46002 { - color: var(--interactive-text-default) -} - -.closeButton__46002 { - cursor: pointer; - margin-top: -16px -} - -.soundAddButton_da9eb2 { - background: initial; - border: 1px dashed var(--border-subtle); - color: var(--text-muted); - cursor: pointer -} - -.soundAddButton_da9eb2:hover { - border-color: var(--border-normal); - color: var(--text-default) -} - -.soundAddButton_da9eb2.focused_da9eb2 { - border-color: var(--border-strong); - color: var(--text-default) -} - -.soundAddButton_da9eb2 svg { - margin-inline-end:3px} - -.disabled_da9eb2 { - pointer-events: none -} - -.sectionContainer__61424 { - align-items: center; - background-color: var(--background-base-low); - display: flex; - height: var(--custom-expression-picker-constants-expression-picker-list-section-heading-height); - padding-block:0;padding-inline:8px 4px;position: sticky; - top: 0; - z-index: 10000 -} - -.sectionContainerNitroLocked__61424 { - position: relative -} - -.sectionContainerNitroLockedBackground__61424 { - background-color: hsl(var(--premium-tier-2-purple-hsl)/.35) -} - -.sectionContainerNitroLockedBackground__61424.brandRefresh__61424 { - background: linear-gradient(90deg,var(--expressive-gradient-nitro-pink-start) 0,var(--expressive-gradient-nitro-pink-end) 100%) -} - -.theme-light .sectionContainerNitroLockedBackground__61424.brandRefresh__61424 { - background: linear-gradient(90deg,color-mix(in srgb,var(--expressive-gradient-nitro-pink-start) 40%,transparent),color-mix(in srgb,var(--expressive-gradient-nitro-pink-end) 40%,transparent)) -} - -.sectionHeader__61424 { - color: var(--interactive-text-default); - cursor: pointer; - display: inline-block -} - -.sectionHeader__61424:hover,.sectionHeader__61424:hover .sectionTitle__61424 { - color: var(--interactive-text-active) -} - -.sectionHeader__61424 .sectionTitle__61424 { - color: var(--interactive-text-default) -} - -.sectionHeaderContent__61424 { - align-items: center; - display: flex -} - -.smallPaddingFooter__61424 { - height: 20px -} - -.lastSectionFooter__61424 { - height: 70px -} - -.nitroLocked__61424.lastSectionFooter__61424,.nitroLocked__61424.smallPaddingFooter__61424 { - background-color: hsl(var(--premium-tier-2-purple-hsl)/.35) -} - -.nitroLocked__61424.lastSectionFooter__61424.brandRefresh__61424,.nitroLocked__61424.smallPaddingFooter__61424.brandRefresh__61424 { - background: linear-gradient(90deg,var(--expressive-gradient-nitro-pink-start) 0,var(--expressive-gradient-nitro-pink-end) 100%) -} - -.theme-light .nitroLocked__61424.lastSectionFooter__61424.brandRefresh__61424,.theme-light .nitroLocked__61424.smallPaddingFooter__61424.brandRefresh__61424 { - background: linear-gradient(90deg,color-mix(in srgb,var(--expressive-gradient-nitro-pink-start) 40%,transparent),color-mix(in srgb,var(--expressive-gradient-nitro-pink-end) 40%,transparent)) -} - -.nitroLocked__61424:not(.lastSectionFooter__61424) { - margin-bottom: var(--space-4) -} - -.sectionTitle__61424 { - margin: 0 4px -} - -.headerIcon__61424 { - height: 16px; - width: 16px -} - -.soundRow__61424 { - display: flex; - gap: 8px; - padding-bottom: 8px; - padding-inline-start:8px} - -.soundRowNitroLocked__61424 { - background-color: hsl(var(--premium-tier-2-purple-hsl)/.35) -} - -.soundRowNitroLocked__61424.brandRefresh__61424 { - background: linear-gradient(90deg,var(--expressive-gradient-nitro-pink-start) 0,var(--expressive-gradient-nitro-pink-end) 100%) -} - -.theme-light .soundRowNitroLocked__61424.brandRefresh__61424 { - background: linear-gradient(90deg,color-mix(in srgb,var(--expressive-gradient-nitro-pink-start) 40%,transparent),color-mix(in srgb,var(--expressive-gradient-nitro-pink-end) 40%,transparent)) -} - -.settingsClickArea__61424 { - color: var(--interactive-text-default); - cursor: pointer; - margin-inline-start:var(--space-12);transition: color .2s ease -} - -.settingsClickArea__61424:focus,.settingsClickArea__61424:hover { - color: var(--interactive-text-hover) -} - -.settingsSoundmojiClickArea__61424 { - padding: 0 -} - -.settingsSoundmojiClickArea__61424:last-child { - padding-inline-end:0} - -.premiumSectionDivider__61424 { - padding-top: 8px -} - -.settingsIcon__61424 { - height: 24px; - width: 24px -} - -.picker__09f65 { - background-color: var(--background-base-low); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-border),var(--shadow-high); - display: flex; - flex-direction: column; - height: 420px; - overflow: hidden; - width: 531px -} - -.picker__09f65.inExpressionPicker__09f65 { - box-shadow: none; - height: 100%; - width: auto -} - -.fetching__09f65 { - justify-content: center -} - -.categoryList__3ad28 { - top: 53px -} - -.stickerCategory__3ad28 { - border-radius: 4px; - color: var(--interactive-text-default); - cursor: pointer; - height: var(--custom-stickers-constants-sticker-category-icon-size); - margin-bottom: var(--custom-stickers-constants-sticker-category-icon-margin); - width: var(--custom-stickers-constants-sticker-category-icon-size) -} - -.firstPartyCategory__3ad28:hover { - background-color: var(--interactive-background-hover); - color: var(--interactive-text-hover) -} - -.firstPartyCategorySelected__3ad28,.firstPartyCategorySelected__3ad28:hover,.stickerCategoryGenericSelected__3ad28,.stickerCategoryGenericSelected__3ad28:hover { - background-color: var(--background-base-low) -} - -.stickerCategoryGenericSelected__3ad28 .stickerCategoryGenericIcon__3ad28 { - color: var(--interactive-text-active) -} - -.stickerCategoryGeneric__3ad28 { - box-sizing: border-box; - margin-bottom: 2px; - padding: 4px -} - -.stickerCategoryGeneric__3ad28.stickerCategoryGenericLast__3ad28 { - margin-bottom: var(--custom-stickers-constants-sticker-category-icon-margin) -} - -.stickerCategoryGeneric__3ad28:hover { - background-color: var(--background-base-low) -} - -.stickerCategoryGenericDisabled__3ad28 { - color: var(--text-muted); - cursor: default -} - -.stickerCategoryGenericDisabled__3ad28 .stickerCategoryGenericIcon__3ad28 { - opacity: .6 -} - -.stickerCategoryGenericDisabled__3ad28.stickerCategory__3ad28:hover { - background-color: transparent; - color: var(--text-muted) -} - -.stickerPackThumbnail__3ad28 { - display: block; - height: 100%; - width: 100% -} - -.guildCategorySeparator__3ad28 { - border: none; - border-bottom: var(--custom-stickers-constants-category-separator-size) solid var(--border-subtle); - margin: var(--custom-stickers-constants-category-separator-margin-vertical) 0 -} - -.standardStickerShortcut__3ad28 { - align-items: center; - background: inherit; - border-top: 1px solid var(--border-normal); - bottom: 0; - box-sizing: border-box; - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - height: 48px; - inset-inline-start: 0; - justify-content: center; - opacity: 1; - position: absolute; - width: 100% -} - -.full-motion .standardStickerShortcut__3ad28 { - transition: opacity .1s,transform .1s -} - -.standardStickerShortcut__3ad28:hover { - color: var(--interactive-text-hover) -} - -.invisibleShortcut__3ad28 { - display: none; - opacity: 0 -} - -.enable-forced-colors .standardStickerShortcut__3ad28 { - background-color: ButtonFace; - border-top: 1px solid CanvasText; - color: ButtonText; - height: 47px -} - -.enable-forced-colors .stickerCategory__3ad28 { - border: 1px solid Canvas; - border-radius: 4px; - transition: none -} - -.enable-forced-colors .stickerCategory__3ad28 .stickerCategoryGenericIcon__3ad28 { - color: ButtonText -} - -.enable-forced-colors .stickerCategory__3ad28:hover { - background: ButtonFace; - border-color: ButtonText -} - -.enable-forced-colors .stickerCategoryGenericDisabled__3ad28 { - color: GrayText -} - -.enable-forced-colors .stickerCategoryGenericDisabled__3ad28.stickerCategory__3ad28:hover { - color: GrayText -} - -.enable-forced-colors .firstPartyCategorySelected__3ad28,.enable-forced-colors .firstPartyCategorySelected__3ad28:hover,.enable-forced-colors .stickerCategoryGenericSelected__3ad28,.enable-forced-colors .stickerCategoryGenericSelected__3ad28:hover { - background-color: HighlightText; - border-color: Highlight -} - -.enable-forced-colors .firstPartyCategorySelected__3ad28 .stickerCategoryGenericIcon__3ad28,.enable-forced-colors .firstPartyCategorySelected__3ad28:hover .stickerCategoryGenericIcon__3ad28,.enable-forced-colors .stickerCategoryGenericSelected__3ad28 .stickerCategoryGenericIcon__3ad28,.enable-forced-colors .stickerCategoryGenericSelected__3ad28:hover .stickerCategoryGenericIcon__3ad28 { - color: Highlight -} - -.emptyState__70126 { - align-items: center; - display: flex; - flex-direction: column; - height: 100%; - justify-content: center; - text-align: center -} - -.emptyState__70126.unifyTrialUpsell__70126 { - margin: 0 -} - -.header__70126 { - margin-bottom: 8px; - margin-top: 16px -} - -.subtitle__70126 { - margin-bottom: 12px -} - -.stickersRow__70126 { - display: flex; - margin-bottom: 20px -} - -.stickersRow__70126 .sticker__70126 { - margin: auto 2px -} - -.upsell_ac65bb { - align-items: center; - background-color: var(--background-base-lowest); - border-radius: 4px; - display: flex; - padding-block:8px;padding-inline:8px 12px} - -.icon_ac65bb { - color: var(--interactive-text-default); - margin-inline-end:8px;width: 24px -} - -.body_ac65bb { - flex: 1 -} - -.iconWrapper_d13236 { - align-items: center; - background-image: linear-gradient(90deg,var(--guild-boosting-blue),var(--guild-boosting-purple)); - border-radius: 24px; - color: var(--white); - display: flex; - height: 24px; - justify-content: center; - width: 24px -} - -.icon_d13236 { - height: 12px; - width: 12px -} - -.unownedStickerLockContainer_ced283 { - align-items: center; - background-color: var(--background-mod-muted); - border: 1px solid var(--border-subtle); - border-radius: 100%; - bottom: 0; - display: flex; - inset-inline-end: 0; - justify-content: center; - position: absolute -} - -.unownedStickerLockIcon_ced283 { - color: var(--interactive-text-default) -} - -@keyframes ripple_c6367b { - 0% { - opacity: 1; - transform: scale(.8) - } - - to { - opacity: 0; - transform: scale(1.6) - } -} - -.row_c6367b { - display: grid; - margin-bottom: var(--custom-stickers-constants-sticker-picker-preview-margin); - overflow: hidden -} - -.row_c6367b,.sticker_c6367b { - position: relative -} - -.sticker_c6367b { - border-radius: var(--custom-stickers-constants-sticker-picker-preview-border-radius); - padding: var(--custom-stickers-constants-sticker-picker-preview-padding) -} - -.sticker_c6367b:after { - border: 3px solid var(--yellow-300); - border-radius: 100%; - box-sizing: border-box; - content: ""; - height: 100%; - inset-inline-start: 0; - opacity: 0; - pointer-events: none; - position: absolute; - top: 0; - transform-origin: center; - width: 100%; - z-index: 5 -} - -.full-motion .sticker_c6367b:after { - transition: all .15s ease-in-out -} - -.sticker_c6367b.showPulse_c6367b:after { - animation: ripple_c6367b .25s ease-out 1 -} - -.createSticker_c6367b { - align-items: center; - background-color: var(--background-base-low); - border-radius: 8px; - display: flex; - flex-direction: column; - font-weight: var(--font-weight-medium); - justify-content: space-evenly; - position: relative -} - -.createInspected_c6367b { - background-color: var(--background-mod-normal) -} - -.uploadCard_c6367b { - cursor: pointer -} - -.iconWrapper_c6367b { - align-items: center; - background-image: linear-gradient(90deg,var(--guild-boosting-blue),var(--guild-boosting-purple)); - border-radius: 36px; - color: var(--white); - display: flex; - height: 36px; - justify-content: center; - width: 36px -} - -.icon_c6367b { - height: 18px; - width: 18px -} - -.inspectedIndicator_c6367b { - height: 100%; - inset-inline-start: 0; - -webkit-mask: url(/assets/34378a8df8efb149.svg) 0 0/100% 100%; - mask: url(/assets/34378a8df8efb149.svg) 0 0/100% 100%; - position: absolute; - top: 0; - transition: background-color .08s ease-out,color .08s ease-out; - width: 100%; - z-index: -1 -} - -.stickerInspected_c6367b .inspectedIndicator_c6367b { - background-color: var(--interactive-background-selected) -} - -.stickerPlaceholder_c6367b { - cursor: default -} - -.viewAll_c6367b { - inset-inline-end: 0; - position: absolute; - top: 0 -} - -.viewAll_c6367b:hover .viewAllButton_c6367b,.viewAllInspected_c6367b .viewAllButton_c6367b { - background-color: var(--background-base-lowest); - color: var(--interactive-text-hover) -} - -.viewAllButton_c6367b { - align-items: center; - background-color: var(--background-mod-normal); - box-sizing: border-box; - color: var(--interactive-text-default); - display: grid; - font-size: 16px; - font-weight: var(--font-weight-semibold); - height: 100%; - line-height: 20px; - text-align: center; - white-space: pre-line; - width: 100% -} - -.stickerNode_c6367b { - transition: opacity .25s -} - -.stickerNodeDimmed_c6367b { - opacity: .3 -} - -.stickerNodeHidden_c6367b { - visibility: hidden -} - -.stickerUnsendable_c6367b { - filter: grayscale(100%) -} - -.keyboard-mode .stickerInspected_c6367b { - position: relative -} - -.keyboard-mode .stickerInspected_c6367b:before { - border-radius: 12px; - bottom: 0; - content: ""; - display: block; - inset-inline: 0; - pointer-events: none; - position: absolute; - top: 0; - --__adaptive-focus-color: var(--interactive-text-hover); - border: 2px solid var(--__adaptive-focus-color); - z-index: 1 -} - -.enable-forced-colors .stickerInspected_c6367b { - background-color: HighlightText; - outline: 1px solid Highlight; - outline-offset: -1px -} - -.enable-forced-colors .stickerInspected_c6367b:before { - border-color: Highlight -} - -.wrapper_e94b8c { - display: grid; - grid-template-rows: 1fr auto; - height: 100%; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.footerActions_e94b8c { - margin-bottom: var(--custom-stickers-constants-sticker-picker-preview-margin) -} - -.packHeader_e94b8c { - padding-inline-start:0;position: sticky; - top: 0 -} - -.packHeaderShopWrapper_e94b8c { - -moz-column-gap: 12px; - column-gap: 12px; - display: flex -} - -.packHeaderShop_e94b8c { - flex: 1 1 auto; - overflow: hidden -} - -.packHeaderShopActions_e94b8c { - flex: 0 0 auto -} - -.sticker_e94b8c { - cursor: pointer -} - -.divider_e94b8c { - align-items: center; - display: flex; - height: var(--custom-stickers-constants-stickers-list-divider-height) -} - -.shopDivider_e94b8c { - border-top-width: var(--custom-stickers-constants-stickers-shop-divider-border-top-width); - height: var(--custom-stickers-constants-stickers-shop-divider-height) -} - -.tipWithSearch_e94b8c { - margin-top: var(--custom-stickers-constants-sticker-picker-tip-margin-search-results) -} - -.searchSuggestions_e94b8c { - max-width: 250px; - padding-top: 16px -} - -.searchSuggestion_e94b8c { - background: var(--background-surface-high); - border-radius: 8px; - cursor: pointer; - display: inline-block; - font-weight: var(--font-weight-normal); - margin: 0 4px 8px; - padding: 4px 8px -} - -.emptyGuildUpsell_e94b8c { - box-sizing: border-box; - height: var(--custom-stickers-constants-stickers-list-empty-guild-upsell-height); - margin-inline:4px;overflow: hidden -} - -.wrapper_cdf8a9 { - flex: 1 -} - -.upsellWrapper__8fe94 { - align-items: center; - background: var(--background-base-lower); - bottom: 0; - display: flex; - flex-direction: column; - height: 100%; - position: absolute; - top: 0; - inset-inline: 0; - justify-content: center; - opacity: .98; - padding: 0 20px; - text-align: center; - z-index: 2 -} - -.upsellWrapper__8fe94.unifyTrialUpsell__8fe94 { - justify-content: flex-end; - opacity: 1; - padding: 0 -} - -.upsellImage__8fe94 { - height: 80px; - margin-bottom: 16px; - width: -moz-fit-content; - width: fit-content -} - -.upsellTitle__8fe94 { - margin-bottom: 8px -} - -.upsellDescription__8fe94 { - margin-bottom: 44px; - max-width: 400px -} - -.upsellClose__8fe94 { - color: var(--interactive-text-default); - cursor: pointer; - inset-inline-end: 20px; - position: absolute; - top: 20px -} - -.wrapper__8ef02 { - display: grid; - grid-template-columns: 48px auto; - grid-template-rows: auto 1fr auto; - position: relative -} - -.header__8ef02 { - align-items: center; - background-color: var(--background-surface-high); - border-bottom: 1px solid var(--border-subtle); - box-shadow: none; - display: flex; - grid-column: 1/3; - grid-row: 1/2; - min-height: 1px; - padding: var(--custom-gif-picker-gutter-size); - z-index: 1 -} - -.loadingIndicator__8ef02 { - grid-column: 1/-1; - grid-row: 1/-1 -} - -.listWrapper__8ef02 { - grid-column: 2/2 -} - -.emptyState__8ef02,.listWrapper__8ef02 { - grid-row: 2/2; - overflow: hidden; - position: relative -} - -.emptyState__8ef02 { - grid-column: 1/3; - margin: 0 20px -} - -.enable-forced-colors .header__8ef02 { - border-bottom: 1px solid CanvasText -} - -.positionLayer__08434 { - pointer-events: none; - z-index: 0 -} - -.positionLayerDefaultAlignLeft__08434 { - inset-inline-end: var(--space-16) -} - -.positionLayerDefaultAlignRight__08434 { - inset-inline-start: var(--space-16) -} - -.positionContainer__08434 { - height: 486px; - pointer-events: none; - width: 100% -} - -.positionContainerOnlyEmoji__08434 { - height: 396px -} - -.drawerSizingWrapper__08434 { - height: 100%; - inset-inline-end: var(--space-24)!important; - max-width: 100%; - pointer-events: all; - position: absolute; - width: 100%; - z-index: 1 -} - -.contentWrapper__08434 { - box-sizing: border-box; - display: grid; - flex: 1 1 auto; - grid-template-rows: 30px auto; - grid-row-gap: 12px; - background-color: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-high); - height: 100%; - padding-top: 16px; - position: relative; - z-index: 1 -} - -.positionContainerOnlyEmoji__08434 .contentWrapper__08434 { - grid-template-rows: auto -} - -.nav__08434 { - padding: 0 16px -} - -.navList__08434 { - display: flex -} - -.navItem__08434 { - flex: 0 0 auto -} - -.navItem__08434+.navItem__08434 { - margin-inline-start:8px} - -.navButton__08434 { - border-radius: var(--radius-sm); - color: var(--text-default); - cursor: pointer; - font-size: 14px; - font-weight: 600; - line-height: 14px; - padding: 8px 12px; - transition: background-color .1s ease-in-out,color .1s ease-in-out -} - -.high-contrast-mode .navButton__08434 { - border: 1px solid var(--border-subtle); - box-sizing: border-box -} - -.navButton__08434:hover { - background-color: var(--control-secondary-background-hover); - color: var(--interactive-text-hover) -} - -.navButton__08434:active { - background-color: var(--control-secondary-background-active); - color: var(--interactive-text-active) -} - -.navButtonActive__08434 { - background-color: var(--background-mod-normal); - color: var(--text-strong) -} - -.navButtonActive__08434:active,.navButtonActive__08434:hover { - background-color: var(--background-mod-strong); - color: var(--interactive-text-active) -} - -.high-contrast-mode .navButtonActive__08434 { - border: 1px solid var(--border-normal); - box-sizing: border-box -} - -.resizeHandle__08434 { - cursor: ew-resize; - height: 100%; - position: absolute; - top: 0; - width: 4px; - z-index: 2 -} - -.introductionWrapper__08434 { - text-align: center -} - -.introductionHeader__08434 { - font-weight: var(--font-weight-semibold); - margin-bottom: 4px -} - -.introductionSubheader__08434 { - margin-bottom: 16px -} - -.introductionAction__08434 { - color: var(--brand-500); - font-weight: var(--font-weight-semibold) -} - -.introductionTooltip__08434 { - max-width: none; - width: 280px -} - -.introductionTooltipContent__08434 { - padding: 16px -} - -.stickersNavItem__08434 { - align-items: center; - display: flex -} - -.stickersUnseenBadge__08434 { - align-items: center; - border-radius: 8px; - box-shadow: none; - display: inline-flex; - height: 16px; - justify-content: center; - margin-inline-start:4px;min-width: 16px; - padding: 0; - text-shadow: none -} - -.soundboardContainer__08434 { - position: relative -} - -.soundboardHeader__08434 { - align-items: center; - background-color: var(--background-surface-high); - border-bottom: 1px solid var(--border-subtle); - box-shadow: none; - display: flex; - padding: var(--custom-gif-picker-gutter-size) -} - -.enable-forced-colors .resizeHandle__08434 { - background-color: ButtonText -} - -.enable-forced-colors .contentWrapper__08434 { - border: 3px solid CanvasText -} - -.enable-forced-colors .navButton__08434 { - border: 1px solid ButtonFace -} - -.enable-forced-colors .navButton__08434:hover { - border-color: ButtonText -} - -.enable-forced-colors .navButtonActive__08434 { - border-color: Highlight -} - -.soundmojiLabelContainer__08434 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-4) -} - -.userTooltip__6b453 { - align-items: center; - display: flex; - font-size: 16px -} - -.avatar__6b453 { - margin-inline-end:8px} - -.discriminator__6b453 { - opacity: .5 -} - -.icon__6b453 { - height: 1em; - margin-bottom: .2rem; - margin-inline-end:3px;vertical-align: middle; - width: 1em -} - -.tabular__6b453 { - font-variant-numeric: tabular-nums -} - -.syntaxAfter_ada32f,.syntaxBefore_ada32f { - font-weight: var(--font-weight-semibold) -} - -.codeBlockSyntax_ada32f,.syntaxAfter_ada32f,.syntaxBefore_ada32f { - color: var(--textbox-markdown-syntax) -} - -.codeBlockLang_ada32f { - color: var(--text-feedback-positive) -} - -.after_s_ada32f,.after_spoiler_ada32f,.after_u_ada32f,.before_s_ada32f,.before_spoiler_ada32f,.before_u_ada32f,.syntaxOverride_ada32f { - font-weight: var(--font-weight-normal) -} - -.before_em_ada32f { - margin-inline-end:-1px} - -.after_em_ada32f { - margin-inline-start:1px} - -.before_s_ada32f,.before_u_ada32f { - margin-inline-end:1px} - -.after_s_ada32f,.after_u_ada32f { - margin-inline-start:1px} - -.after_u_ada32f,.before_u_ada32f { - text-decoration: underline -} - -.before_inlineCode_ada32f { - border-inline-end:none;border-radius: 3px 0 0 3px; - border-end-start-radius: var(--radius-xs); - border-start-start-radius: var(--radius-xs) -} - -.after_inlineCode_ada32f { - border-inline-start:none;border-radius: 0 3px 3px 0; - border-end-end-radius: var(--radius-xs); - border-start-end-radius: var(--radius-xs) -} - -.bold_ada32f { - font-weight: var(--font-weight-bold) -} - -.italics_ada32f { - font-style: italic -} - -.underline_ada32f { - text-decoration: underline -} - -.strikethrough_ada32f { - text-decoration: line-through -} - -.fakeLink_ada32f { - color: var(--text-link); - -webkit-text-decoration: var(--link-decoration); - text-decoration: var(--link-decoration) -} - -.after_inlineCode_ada32f,.before_inlineCode_ada32f,.inlineCode_ada32f { - background: var(--background-code); - border: 1px solid var(--border-muted); - box-sizing: border-box; - font-family: var(--font-code); - font-size: 85%; - height: auto; - line-height: 18px; - margin: 0; - padding: 0; - text-indent: 0; - white-space: pre-wrap; - width: auto -} - -.inlineCode_ada32f { - border-inline-end:none;border-inline-start:none} - -.spoiler_ada32f { - padding: 0 .2em -} - -.codeBlockText_ada32f,.codeLine_ada32f { - color: var(--text-default); - font-family: var(--font-code); - font-size: 85%; - -webkit-text-size-adjust: none; - -moz-text-size-adjust: none; - text-size-adjust: none -} - -.codeLine_ada32f .hljs-comment,.codeLine_ada32f .hljs-quote { - color: var(--text-muted) -} - -.codeLine_ada32f .hljs-ansi-control-sequence { - background-color: inherit; - color: var(--textbox-markdown-syntax); - display: inherit; - font-size: 70%; - font-style: inherit; - font-weight: inherit; - padding-inline-end:.1em;text-decoration: none; - vertical-align: middle -} - -.before_subtext_ada32f { - font-weight: 700 -} - -.before_subtext_ada32f,.subtext_ada32f { - color: var(--text-muted); - font-size: 13px; - line-height: 17.875px -} - -.editor__1b31f { - caret-color: var(--text-default); - text-align: start; - white-space: break-spaces!important; - word-break: break-word -} - -.editor__1b31f pre { - max-width: none -} - -.placeholder__1b31f { - color: var(--channel-text-area-placeholder); - inset-inline: 0; - overflow: hidden; - pointer-events: none; - position: absolute; - text-overflow: ellipsis; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - white-space: nowrap -} - -.enable-forced-colors .placeholder__1b31f { - color: GrayText -} - -.theme-dark.custom-theme-background.custom-client-theme .placeholder__1b31f { - color: color-mix(in oklab,var(--text-default) 40%,transparent) -} - -.slateContainer_ec4baf { - position: relative -} - -.slateTextArea_ec4baf { - box-sizing: border-box; - inset-inline: 0 10px; - min-height: var(--custom-channel-textarea-text-area-height); - padding: calc((var(--custom-channel-textarea-text-area-height) - var(--chat-markup-line-height))/2) 0; - position: absolute -} - -.enable-forced-colors .placeholder_ec4baf { - color: GrayText -} - -@use postcss-pxtorem;.channelTextArea__74017 { - border: 1px solid var(--border-muted); - border-radius: var(--radius-sm); - position: relative; - text-indent: 0; - transition: border-color .2s ease; - width: 100% -} - -.channelTextArea__74017:not(.highlighted__74017) { - transition: box-shadow .2s ease -} - -.channelTextArea__74017.error__74017:not(:hover):not(:focus-within) { - box-shadow: 0 0 0 2px var(--text-feedback-critical) -} - -.channelTextArea__74017.highlighted__74017 { - border: 1px solid var(--border-muted); - box-shadow: 0 0 0 4px var(--blue-345) -} - -.channelTextArea__74017:focus-within { - border-color: var(--border-subtle); - box-shadow: none -} - -.focusRing__74017 { - border-radius: 8px -} - -.scrollableContainer__74017 { - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - border-radius: 8px; - max-height: var(--custom-channel-textarea-text-area-max-height); - overflow-x: hidden; - overflow-y: scroll -} - -.themedBackground__74017 { - background: var(--background-gradient-highest,var(--chat-background-default)) -} - -.no-webkit-scrollbar .scrollableContainer__74017 { - scrollbar-width: none -} - -:where(.custom-theme-background) .channelTextArea__74017 { - background: var(--background-gradient-highest,var(--input-background-default)); - border-color: var(--app-frame-border) -} - -.refresh-fast-follow-distinct-borders .channelTextArea__74017 { - border: 1px solid var(--border-subtle) -} - -.refresh-fast-follow-distinct-borders .channelTextArea__74017:focus-within { - border-color: var(--app-frame-border) -} - -.sticker__74017 { - display: block -} - -.disabledButtonWrapper__74017 { - display: inline-flex -} - -.background-opacity-low .scrollableContainer__74017 { - background-color: hsl(var(--primary-500-hsl)/.5) -} - -.background-opacity-low .scrollableContainer__74017 .textArea__74017 { - color: var(--white) -} - -.background-opacity-medium .scrollableContainer__74017 { - background-color: hsl(var(--primary-500-hsl)/.7) -} - -.background-opacity-medium .scrollableContainer__74017 .textArea__74017 { - color: var(--white) -} - -.background-opacity-high .scrollableContainer__74017 { - background-color: hsl(var(--primary-500-hsl)/.9) -} - -.scrollableContainer__74017::-webkit-scrollbar { - height: 12px; - width: 12px -} - -.scrollableContainer__74017::-webkit-scrollbar-thumb,.scrollableContainer__74017::-webkit-scrollbar-track { - background-clip: padding-box; - border: 4px solid transparent -} - -.scrollableContainer__74017::-webkit-scrollbar-track { - border-width: initial -} - -.scrollableContainer__74017::-webkit-scrollbar-thumb { - background-color: hsl(var(--primary-800-hsl)/.6); - border-radius: 8px -} - -.channelTextAreaDisabled__74017 .scrollableContainer__74017 { - opacity: 1 -} - -.hasStackedBar__74017 { - border-start-end-radius: 0; - border-start-start-radius: 0 -} - -.inlineContainer__74017 { - box-shadow: none; - --custom-channel-textarea-text-area-height: var(--form-input-height); - background: var(--input-background-default); - border-color: var(--input-border-default); - box-sizing: border-box -} - -.inlineContainer__74017 .themedBackground__74017 { - background: none; - border: none; - height: auto -} - -.inlineContainer__74017 .accessoryBar__74017 { - align-items: center; - border-top: none; - box-sizing: border-box; - height: var(--custom-channel-textarea-text-area-height) -} - -.flushContainer__74017 { - background: unset; - border: none -} - -.flushContainer__74017 .inner__74017 { - padding-inline-start:0} - -.flushContainer__74017 .accessoryBar__74017 { - align-items: flex-start; - border-top: none -} - -.inner__74017 { - display: flex; - padding-inline-start:calc(var(--space-16) - 1px);position: relative -} - -.innerDisabled__74017 { - cursor: not-allowed -} - -.sansAttachButton__74017 { - padding-inline-start:calc(var(--space-16) - 1px)} - -.sansAttachButtonCreateThread__74017 { - padding-inline-start: 10px -} - -.sansAttachButtonCreatePost__74017,.sansAttachButtonUserProfileReply__74017 { - padding-inline-start:0} - -.attachButton__74017 { - margin-inline:calc((var(--space-xs) + var(--space-6))*-1) 10px} - -.textArea__74017 { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: transparent; - border: none; - box-sizing: border-box; - color: var(--text-default); - font-size: 1rem; - font-weight: var(--font-weight-normal); - height: var(--custom-channel-textarea-text-area-height); - line-height: 1.375rem; - min-height: var(--custom-channel-textarea-text-area-height); - padding-inline:0 10px;resize: none; - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; - width: 100% -} - -.textArea__74017::-moz-placeholder { - color: var(--text-muted); - overflow: hidden; - text-overflow: ellipsis; - -moz-user-select: none; - user-select: none; - white-space: nowrap -} - -.textArea__74017::placeholder { - color: var(--text-muted); - overflow: hidden; - text-overflow: ellipsis; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - white-space: nowrap -} - -.textAreaSlate__74017 { - box-sizing: border-box; - padding: 0 -} - -.textAreaThreadCreation__74017 { - min-height: 66px -} - -.profileBioInput__74017 { - min-height: 100% -} - -.profileBioInput__74017>div { - height: 100% -} - -.hiddenAppLauncherAnchor__74017 { - align-self: center; - height: 0; - visibility: hidden; - width: 0 -} - -.overlayInlineReply__74017 { - height: 100%; - min-height: 100%; - padding-inline-end:0!important} - -.textAreaForPostCreation__74017 { - margin-inline-start:0!important;padding: 0!important -} - -.textAreaForOverlayInlineReply__74017,.textAreaForUserProfile__74017 { - font-size: .875rem -} - -.textAreaCustomGift__74017[role=textbox] { - box-sizing: border-box; - height: 100%; - position: absolute!important -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .textArea__74017 { - font-weight:var(--font-weight-medium) - } -} - -.textAreaDisabled__74017 { - pointer-events: none -} - -.buttons__74017 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-8); - height: var(--custom-channel-textarea-text-area-height); - margin-inline-end:6px;position: sticky; - top: 0 -} - -.has-webkit-scrollbar .buttons__74017 { - margin-inline-end:0} - -.buttonContainer__74017 { - align-items: center; - display: flex -} - -.button__74017 { - box-sizing: border-box; - margin-inline:0;min-height: var(--space-32); - min-width: var(--space-32); - padding: 0 -} - -.emojiButton__74017 { - border-radius: 8px; - transition-duration: .2s -} - -.emojiButton__74017:hover { - background-color: var(--interactive-background-selected) -} - -.stickerIcon__74017 { - height: 20px!important; - width: 20px!important -} - -.stickerButton__74017 { - transition: background-color .2s,transform .2s -} - -.stickerButtonWithNotification__74017 { - margin-top: 1px; - padding-inline:2px} - -@keyframes wiggle__74017 { - 0% { - transform: rotate(0) - } - - 30% { - transform: rotate(8deg) - } - - 40% { - transform: rotate(8deg) - } - - 80% { - transform: rotate(-6deg) - } - - to { - transform: rotate(0deg) - } -} - -.floatingBars__74017 { - margin-bottom: 0; - min-height: 0 -} - -.floatingBars__74017 .newMemberActionBar__74017 { - margin-inline:0} - -.stackedBars__74017 { - background: var(--background-surface-higher) -} - -.stackedBars__74017>:not(:last-child) { - border-bottom: 1px solid var(--border-subtle) -} - -.floatingBars__74017:empty+.stackedBars__74017:nth-child(2),.stackedBars__74017:first-child { - border-start-end-radius: var(--radius-sm); - border-start-start-radius: var(--radius-sm) -} - -.stackedBars__74017:not(:last-child) { - border-bottom: 1px solid var(--border-subtle) -} - -.sparkles__74017 { - height: 150%; - inset-inline-start: -10px; - position: absolute; - top: -10px; - width: 150% -} - -.expressionPickerPositionLayer__74017 { - inset-inline-start: 86px -} - -.theme-dark.custom-theme-background .stackedBars__74017 { - background: var(--background-gradient-lowest,var(--background-base-lower)) -} - -.theme-dark.custom-theme-background .textAreaMobileThemed__74017 { - margin-top: 16px -} - -.theme-light.custom-theme-background .stackedBars__74017 { - background: var(--background-gradient-lower,var(--background-base-lower)) -} - -.theme-light.custom-theme-background .textAreaMobileThemed__74017 { - margin-top: 16px -} - -.theme-brand .inner__74017 { - background-color: var(--brand-400) -} - -.theme-brand .textArea__74017 { - color: var(--opacity-white-68) -} - -.theme-brand .textArea__74017::-moz-placeholder { - color: var(--opacity-white-28) -} - -.theme-brand .textArea__74017::placeholder { - color: var(--opacity-white-28) -} - -.textArea__74017:not(.textAreaSlate__74017) { - box-sizing: border-box; - min-height: var(--custom-channel-textarea-text-area-height); - padding: calc((var(--custom-channel-textarea-text-area-height) - var(--chat-markup-line-height))/2) 0 -} - -.enable-forced-colors .scrollableContainer__74017 { - overflow-y: auto; - padding-inline-end:12px} - -.enable-forced-colors .scrollableContainer__74017::-webkit-scrollbar { - width: 8px -} - -.enable-forced-colors .scrollableContainer__74017::-webkit-scrollbar-track { - border-radius: 0 8px 8px 0 -} - -.enable-forced-colors .scrollableContainer__74017::-webkit-scrollbar-thumb,.enable-forced-colors .scrollableContainer__74017::-webkit-scrollbar-track { - border-width: 1px -} - -.enable-forced-colors .scrollableContainer__74017::-webkit-scrollbar-thumb { - background-color: CanvasText -} - -.announcementScrollableContainer__74017 { - max-height: calc(var(--custom-channel-textarea-text-area-max-height) + 80px) -} - -.divider__908e2 { - align-items: center; - border-top: thin solid var(--border-subtle); - box-sizing: border-box; - display: flex; - flex: 0 0 auto; - height: 0; - justify-content: center; - pointer-events: none; - position: relative; - z-index: 1; - --divider-color: var(--background-feedback-notification) -} - -.custom-theme-background .divider__908e2 { - border-top-color: var(--border-strong) -} - -.low-saturation .divider__908e2 { - --divider-color: var(--red-430) -} - -.background-opacity-low .divider__908e2,.background-opacity-medium .divider__908e2 { - opacity: .5 -} - -.background-opacity-high .divider__908e2 { - opacity: .8 -} - -.isUnread__908e2,.custom-theme-background .isUnread__908e2 { - border-color: var(--divider-color) -} - -.hasContent__908e2 { - inset-inline: auto; - position: relative -} - -.endCap__908e2 { - align-items: center; - border-inline-start:none;border-radius: 0 4px 4px 0; - box-sizing: border-box; - color: var(--white); - display: flex; - font-size: 10px; - font-weight: var(--font-weight-bold); - height: 13px; - inset-inline-end: 0; - justify-content: center; - line-height: 9px; - padding-block:0;padding-inline:1px 4px;position: absolute; - text-transform: uppercase; - top: -7px -} - -.unreadPill__908e2 { - background-color: var(--divider-color) -} - -.unreadPillCap__908e2 { - display: block; - height: 13px; - inset-inline-start: -8px; - position: absolute; - top: 0 -} - -.unreadPillCapStroke__908e2 { - color: var(--divider-color); - fill: var(--divider-color) -} - -.content__908e2 { - background: var(--background-gradient-chat,var(--background-base-lower)); - border-radius: 8px; - color: var(--text-muted); - display: block; - flex: 0 0 auto; - font-size: 12px; - font-weight: var(--font-weight-semibold); - line-height: 13px; - margin-top: -1px; - padding: 2px 4px; - padding-inline-start:var(--space-4)} - -.isUnread__908e2 .content__908e2 { - color: var(--divider-color) -} - -.stickerPreviews_a4cf0b { - padding-block:10px;padding-inline:16px 0} - -.stickerPreviewContainer_a4cf0b { - display: inline-block; - position: relative -} - -.stickerPreview_a4cf0b { - margin-inline-end:4px} - -.stickerPreviewDivider_a4cf0b { - margin-inline-start:16px} - -.closeButton_a4cf0b { - color: var(--interactive-text-default); - cursor: pointer; - inset-inline-end: 0; - line-height: 0; - position: absolute; - top: 0; - z-index: 1 -} - -.closeButton_a4cf0b:hover { - color: var(--interactive-text-hover) -} - -.closeIconContainer_a4cf0b { - background: var(--channeltextarea-background); - border-radius: 100% -} - -.closeIcon_a4cf0b { - height: 16px; - width: 16px -} - -.closeButton_e876a8 { - color: var(--interactive-text-default); - cursor: pointer; - flex: 0 0 auto; - line-height: 0; - padding-block:8px;padding-inline:var(--space-12)} - -.closeButton_e876a8: hover { - color:var(--interactive-text-hover) -} - -.closeIcon_e876a8 { - height: 16px; - width: 16px -} - -.heading-sm\/normal__84522 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.heading-sm\/normal__84522.fontScaling__84522 { - font-size: .875rem -} - -.heading-sm\/medium__84522 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.heading-sm\/medium__84522.fontScaling__84522 { - font-size: .875rem -} - -.heading-sm\/semibold__84522 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.heading-sm\/semibold__84522.fontScaling__84522 { - font-size: .875rem -} - -.heading-sm\/bold__84522 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.heading-sm\/bold__84522.fontScaling__84522 { - font-size: .875rem -} - -.heading-sm\/extrabold__84522 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 800; - line-height: 1.2857142857142858 -} - -.heading-sm\/extrabold__84522.fontScaling__84522 { - font-size: .875rem -} - -.heading-md\/normal__84522 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.heading-md\/normal__84522.fontScaling__84522 { - font-size: 1rem -} - -.heading-md\/medium__84522 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.heading-md\/medium__84522.fontScaling__84522 { - font-size: 1rem -} - -.heading-md\/semibold__84522 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.heading-md\/semibold__84522.fontScaling__84522 { - font-size: 1rem -} - -.heading-md\/bold__84522 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.heading-md\/bold__84522.fontScaling__84522 { - font-size: 1rem -} - -.heading-md\/extrabold__84522 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 800; - line-height: 1.25 -} - -.heading-md\/extrabold__84522.fontScaling__84522 { - font-size: 1rem -} - -.heading-lg\/normal__84522 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.heading-lg\/normal__84522.fontScaling__84522 { - font-size: 1.25rem -} - -.heading-lg\/medium__84522 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.heading-lg\/medium__84522.fontScaling__84522 { - font-size: 1.25rem -} - -.heading-lg\/semibold__84522 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.heading-lg\/semibold__84522.fontScaling__84522 { - font-size: 1.25rem -} - -.heading-lg\/bold__84522 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.heading-lg\/bold__84522.fontScaling__84522 { - font-size: 1.25rem -} - -.heading-lg\/extrabold__84522 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 800; - line-height: 1.2 -} - -.heading-lg\/extrabold__84522.fontScaling__84522 { - font-size: 1.25rem -} - -.heading-xl\/normal__84522 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xl\/normal__84522.fontScaling__84522 { - font-size: 1.5rem -} - -.heading-xl\/medium__84522 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xl\/medium__84522.fontScaling__84522 { - font-size: 1.5rem -} - -.heading-xl\/semibold__84522 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xl\/semibold__84522.fontScaling__84522 { - font-size: 1.5rem -} - -.heading-xl\/bold__84522 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xl\/bold__84522.fontScaling__84522 { - font-size: 1.5rem -} - -.heading-xl\/extrabold__84522 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xl\/extrabold__84522.fontScaling__84522 { - font-size: 1.5rem -} - -.heading-xxl\/normal__84522 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xxl\/normal__84522.fontScaling__84522 { - font-size: 2rem -} - -.heading-xxl\/medium__84522 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xxl\/medium__84522.fontScaling__84522 { - font-size: 2rem -} - -.heading-xxl\/semibold__84522 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xxl\/semibold__84522.fontScaling__84522 { - font-size: 2rem -} - -.heading-xxl\/bold__84522 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xxl\/bold__84522.fontScaling__84522 { - font-size: 2rem -} - -.heading-xxl\/extrabold__84522 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xxl\/extrabold__84522.fontScaling__84522 { - font-size: 2rem -} - -.eyebrow__84522 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - letter-spacing: .02em; - line-height: 1.3333333333333333; - text-transform: uppercase -} - -.eyebrow__84522.fontScaling__84522 { - font-size: .75rem -} - -.heading-deprecated-12\/normal__84522 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/normal__84522.fontScaling__84522 { - font-size: .75rem -} - -.heading-deprecated-12\/medium__84522 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/medium__84522.fontScaling__84522 { - font-size: .75rem -} - -.heading-deprecated-12\/semibold__84522 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/semibold__84522.fontScaling__84522 { - font-size: .75rem -} - -.heading-deprecated-12\/bold__84522 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/bold__84522.fontScaling__84522 { - font-size: .75rem -} - -.heading-deprecated-12\/extrabold__84522 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 800; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/extrabold__84522.fontScaling__84522 { - font-size: .75rem -} - -.redesign\/heading-18\/bold__84522 { - font-family: var(--font-display); - font-size: 18px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/heading-18\/bold__84522.fontScaling__84522 { - font-size: 1.125rem -} - -.text-xxs\/normal__84522 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 400; - line-height: 1.2 -} - -.text-xxs\/normal__84522.fontScaling__84522 { - font-size: .625rem -} - -.text-xxs\/medium__84522 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 500; - line-height: 1.2 -} - -.text-xxs\/medium__84522.fontScaling__84522 { - font-size: .625rem -} - -.text-xxs\/semibold__84522 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 600; - line-height: 1.2 -} - -.text-xxs\/semibold__84522.fontScaling__84522 { - font-size: .625rem -} - -.text-xxs\/bold__84522 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 700; - line-height: 1.2 -} - -.text-xxs\/bold__84522.fontScaling__84522 { - font-size: .625rem -} - -.text-xs\/normal__84522 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.text-xs\/normal__84522.fontScaling__84522 { - font-size: .75rem -} - -.text-xs\/medium__84522 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.text-xs\/medium__84522.fontScaling__84522 { - font-size: .75rem -} - -.text-xs\/semibold__84522 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.text-xs\/semibold__84522.fontScaling__84522 { - font-size: .75rem -} - -.text-xs\/bold__84522 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.text-xs\/bold__84522.fontScaling__84522 { - font-size: .75rem -} - -.text-sm\/normal__84522 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.text-sm\/normal__84522.fontScaling__84522 { - font-size: .875rem -} - -.text-sm\/medium__84522 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.text-sm\/medium__84522.fontScaling__84522 { - font-size: .875rem -} - -.text-sm\/semibold__84522 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.text-sm\/semibold__84522.fontScaling__84522 { - font-size: .875rem -} - -.text-sm\/bold__84522 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.text-sm\/bold__84522.fontScaling__84522 { - font-size: .875rem -} - -.text-md\/normal__84522 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.text-md\/normal__84522.fontScaling__84522 { - font-size: 1rem -} - -.text-md\/medium__84522 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.text-md\/medium__84522.fontScaling__84522 { - font-size: 1rem -} - -.text-md\/semibold__84522 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.text-md\/semibold__84522.fontScaling__84522 { - font-size: 1rem -} - -.text-md\/bold__84522 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.text-md\/bold__84522.fontScaling__84522 { - font-size: 1rem -} - -.text-lg\/normal__84522 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.text-lg\/normal__84522.fontScaling__84522 { - font-size: 1.25rem -} - -.text-lg\/medium__84522 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.text-lg\/medium__84522.fontScaling__84522 { - font-size: 1.25rem -} - -.text-lg\/semibold__84522 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.text-lg\/semibold__84522.fontScaling__84522 { - font-size: 1.25rem -} - -.text-lg\/bold__84522 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.text-lg\/bold__84522.fontScaling__84522 { - font-size: 1.25rem -} - -.redesign\/message-preview\/normal__84522 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/normal__84522.fontScaling__84522 { - font-size: .9375rem -} - -.redesign\/message-preview\/medium__84522 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/medium__84522.fontScaling__84522 { - font-size: .9375rem -} - -.redesign\/message-preview\/semibold__84522 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/semibold__84522.fontScaling__84522 { - font-size: .9375rem -} - -.redesign\/message-preview\/bold__84522 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/bold__84522.fontScaling__84522 { - font-size: .9375rem -} - -.redesign\/channel-title\/normal__84522 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.375 -} - -.redesign\/channel-title\/normal__84522.fontScaling__84522 { - font-size: 1rem -} - -.redesign\/channel-title\/medium__84522 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.375 -} - -.redesign\/channel-title\/medium__84522.fontScaling__84522 { - font-size: 1rem -} - -.redesign\/channel-title\/semibold__84522 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.375 -} - -.redesign\/channel-title\/semibold__84522.fontScaling__84522 { - font-size: 1rem -} - -.redesign\/channel-title\/bold__84522 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.375 -} - -.redesign\/channel-title\/bold__84522.fontScaling__84522 { - font-size: 1rem -} - -.display-sm__84522 { - font-family: var(--font-headline); - font-size: 20px; - font-weight: 800; - line-height: 1 -} - -.display-sm__84522.fontScaling__84522 { - font-size: 1.25rem -} - -.display-md__84522 { - font-family: var(--font-headline); - font-size: 34px; - font-weight: 800; - line-height: 1.0588235294117647 -} - -.display-md__84522.fontScaling__84522 { - font-size: 2.125rem -} - -.display-lg__84522 { - font-family: var(--font-headline); - font-size: 44px; - font-weight: 800; - line-height: .9545454545454546 -} - -.display-lg__84522.fontScaling__84522 { - font-size: 2.75rem -} - -.code__84522 { - font-family: var(--font-code); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.code__84522.fontScaling__84522 { - font-size: .75rem -} - -.bar__84522 { - align-items: center; - display: flex; - flex-direction: row; - padding-bottom: 2px; - padding-inline-start:16px;padding-top: 2px -} - -.commandInfo__84522 { - align-items: baseline; - display: flex; - flex: 1 1 auto; - font-size: 14px; - font-weight: var(--font-weight-medium); - gap: 8px; - line-height: 16px -} - -.name__84522 { - color: var(--interactive-text-active); - font-size: 16px; - font-weight: var(--font-weight-semibold); - line-height: 20px -} - -.description__84522 { - color: var(--interactive-text-default) -} - -.description__84522,.error__84522 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.error__84522 { - color: var(--red-400) -} - -.actions__84522 { - align-items: center; - display: flex; - flex: 0 0 auto -} - -.bar__4930d { - align-items: center; - background: var(--background-surface-higher); - border-radius: 8px 8px 0 0; - display: flex; - flex-direction: row; - gap: 8px; - justify-content: start; - padding: var(--space-8) var(--space-16) -} - -.clipContainer__841c8 { - margin-top: 0; - overflow: hidden; - padding-top: 3px -} - -.container__841c8 { - display: flex; - flex-direction: column -} - -.replyBar__841c8,.threadSuggestionBar__841c8 { - background: var(--background-base-lower); - background-color: unset; - border-bottom: none; - cursor: pointer -} - -.theme-dark.custom-theme-background .replyBar__841c8,.theme-dark.custom-theme-background .threadSuggestionBar__841c8 { - background: var(--background-gradient-lowest,var(--background-base-lower)) -} - -.theme-light.custom-theme-background .replyBar__841c8,.theme-light.custom-theme-background .threadSuggestionBar__841c8 { - background: var(--background-gradient-lower,var(--background-base-lower)) -} - -.replyBar__841c8 { - align-items: center; - border-start-end-radius: 8px; - border-start-start-radius: 8px; - display: grid; - grid-template-columns: 1fr auto -} - -.threadSuggestionBar__841c8 { - border-top: 1px solid var(--border-subtle); - box-shadow: 0 3px 0 var(--background-base-lower); - display: flex; - flex-direction: row; - padding-bottom: 7px; - padding-top: 7px -} - -.text__841c8 { - margin-inline-start:var(--space-16);overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.replyLabel__841c8 { - flex: 1 1 auto -} - -.name__841c8 { - font-weight: var(--font-weight-semibold) -} - -.actions__841c8 { - flex: 0 0 auto; - margin-inline-end:var(--space-4)} - -.actions__841c8,.mentionButton__841c8 { - align-items: center; - display: flex -} - -.mentionButton__841c8 { - cursor: pointer; - padding: 8px 12px; - text-transform: uppercase -} - -.mentionIcon__841c8 { - height: 16px; - margin-inline-end:4px;width: 16px -} - -.separator__841c8 { - background-color: var(--border-subtle); - height: 20px; - width: 1px -} - -.createThreadButton__841c8 { - margin-inline:8px;white-space: nowrap -} - -.bar_d90114 { - align-items: center; - background: var(--background-surface-higher); - border-radius: 8px 8px 0 0; - display: flex; - flex-direction: row; - gap: 8px; - justify-content: start; - padding: var(--space-8) var(--space-16) -} - -.error_d90114 { - color: var(--red-400); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.small__258ba { - height: 16px; - width: 16px -} - -.medium__258ba { - height: 24px; - width: 24px -} - -.large__258ba { - height: 40px; - width: 40px -} - -.container__183e8 { - display: flex; - flex-direction: column; - overflow: hidden -} - -.banner__183e8 { - align-items: center; - background-color: var(--background-base-lowest); - border-start-end-radius: var(--radius-sm); - border-start-start-radius: var(--radius-sm); - display: flex; - flex-grow: 1; - padding: var(--space-md) var(--space-md); - padding-bottom: var(--space-md) -} - -.bannerForumChannel__183e8 { - border-end-end-radius: var(--radius-sm); - border-end-start-radius: var(--radius-sm) -} - -.text__183e8 { - flex-grow: 1; - margin-inline-start:8px} - -.clickable__183e8 { - cursor: pointer -} - -.channelIcon__183e8 { - color: var(--channel-icon); - height: 20px; - padding: 4px; - width: 20px -} - -.iconCircle__183e8 { - align-items: center; - background-color: var(--brand-500); - border-radius: 10px; - display: flex; - height: 20px; - justify-content: center; - width: 20px -} - -.nextIcon__183e8 { - color: var(--interactive-text-active) -} - -.completed__183e8 { - color: var(--status-positive) -} - -.clipContainer_ba21b4 { - margin-top: 0; - overflow: hidden; - padding-top: 3px -} - -.container_ba21b4 { - display: flex; - flex-direction: column -} - -.scheduledMessageBar_ba21b4 { - align-items: center; - background: var(--background-base-lower); - background-color: unset; - border-bottom: none; - cursor: pointer -} - -.theme-dark.custom-theme-background .scheduledMessageBar_ba21b4 { - background: var(--background-gradient-lowest,var(--background-base-lower)) -} - -.theme-light.custom-theme-background .scheduledMessageBar_ba21b4 { - background: var(--background-gradient-lower,var(--background-base-lower)) -} - -.scheduledMessageBar_ba21b4 { - box-shadow: 0 3px 0 var(--background-base-lower); - display: flex; - flex-direction: row -} - -.text_ba21b4 { - margin-inline-start:var(--space-16);overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.scheduledMessageBarLabel_ba21b4 { - flex: 1 1 auto -} - -.actions_ba21b4 { - align-items: center; - display: flex; - flex: 0 0 auto; - margin-inline-end:var(--space-4)} - -.buttonContainer_e6e74f { - border-radius: 8px; - margin-top: unset; - transition-duration: .2s -} - -.buttonContainer_e6e74f:hover { - background-color: var(--interactive-background-selected) -} - -.button_e6e74f { - align-items: center; - background: unset; - border: unset; - border-radius: 8px; - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - height: auto; - justify-content: center; - min-height: var(--space-32); - min-width: var(--space-32); - width: unset -} - -.button_e6e74f:active,.button_e6e74f:hover,.buttonActive_e6e74f { - color: var(--interactive-text-active) -} - -.channelAppLauncher_e6e74f { - align-content: flex-end; - align-items: center; - display: flex; - justify-content: center; - margin: 0; - padding: 0; - position: relative -} - -.hiddenAppLauncherAnchor_e6e74f { - height: 0; - width: 0 -} - -.entrypointAnimation_e6e74f { - height: 44px; - width: 44px -} - -.entrypointAnimation_e6e74f .buttonContainer_e6e74f:hover { - background-color: transparent -} - -.animationGlow_e6e74f { - bottom: -44px; - height: 132px; - inset-inline-start: -32px; - width: 108px -} - -.animationTrinkets_e6e74f { - bottom: -24px; - height: 100px; - inset-inline-start: -24px; - width: 100px -} - -.channelAppLauncherButtonPopoutIconContainer_e6e74f { - border-radius: 8px; - outline: 2px solid var(--brand-500); - overflow: hidden; - pointer-events: none; - position: absolute; - z-index: 1 -} - -.full-motion .channelAppLauncherButtonPopoutIcon_e6e74f { - animation: channelAppLauncherButtonPopoutIconAnimation_e6e74f 3s; - animation-delay: 10ms; - transform: translateY(-100%) -} - -@keyframes channelAppLauncherButtonPopoutIconAnimation_e6e74f { - 0% { - transform: translateY(-100%) - } - - 6% { - transform: translateY(0) - } - - 94% { - transform: translateY(0) - } - - to { - transform: translateY(100%) - } -} - -.channelAppLauncherButtonPopoutIconShimmerContainer_e6e74f { - inset-inline-end: 0; - position: absolute; - top: 0 -} - -.channelAppLauncherButtonPopoutIconShimmer_e6e74f { - align-items: center; - display: flex; - justify-content: center; - margin-top: auto; - transform: translateX(-100%) -} - -.full-motion .channelAppLauncherButtonPopoutIconShimmer_e6e74f { - animation: channelAppLauncherButtonPopoutIconShimmerAnimation_e6e74f 3s -} - -@keyframes channelAppLauncherButtonPopoutIconShimmerAnimation_e6e74f { - 0% { - transform: translateX(-100%) - } - - 10% { - transform: translateX(-100%) - } - - 22% { - transform: translateX(100%) - } - - to { - transform: translateX(100%) - } -} - -@keyframes pulseIcon__24af7 { - 0%,to { - transform: scale(var(--custom-channel-text-area-button-hover-scale)) - } - - 50% { - transform: none - } -} - -@keyframes pulseButton__24af7 { - 0%,to { - opacity: .3 - } - - 50% { - opacity: 1 - } -} - -@keyframes sparkle__24af7 { - 0% { - opacity: 0; - transform: rotate(-50deg) scale(0) - } - - 20% { - opacity: 1; - transform: rotate(0deg) scale(1) - } - - 40% { - opacity: 0; - transform: rotate(50deg) scale(0) - } - - to { - opacity: 0; - transform: rotate(-50deg) scale(0) - } -} - -.button__24af7 { - align-items: center; - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - justify-content: center -} - -.button__24af7:hover { - color: var(--interactive-text-hover) -} - -.button__24af7:active { - color: var(--interactive-text-active) -} - -.button__24af7.disabled__24af7 { - cursor: not-allowed; - opacity: .5 -} - -.button__24af7:not(.noHover__24af7) { - border-radius: 8px; - transition-duration: .2s -} - -.button__24af7:not(.noHover__24af7):hover { - background-color: var(--interactive-background-selected) -} - -.iconMask__24af7 { - display: block; - height: 20px; - width: 20px -} - -.buttonContent__24af7 { - position: relative -} - -.icon__24af7 { - height: 20px; - width: 20px -} - -.pulseIcon__24af7 { - animation: pulseIcon__24af7 .6s ease -} - -.pulseButton__24af7 .buttonWrapper__24af7 { - animation: pulseButton__24af7 .6s ease -} - -.buttonWrapper__24af7 svg { - display: block -} - -.notificationDot__24af7 { - background-color: var(--background-feedback-notification); - border-radius: 100%; - bottom: 0; - height: 7px; - inset-inline-end: 0; - transform: translateY(-5%); - width: 7px -} - -.notificationDot__24af7,.sparkleContainer__24af7 { - position: absolute -} - -.sparkleStar__24af7 { - color: var(--gold); - inset-inline-start: 20px; - top: -12px -} - -.sparklePlus__24af7,.sparkleStar__24af7 { - animation: sparkle__24af7 2.5s ease-in-out infinite; - position: relative -} - -.sparklePlus__24af7 { - animation-delay: .5s; - color: var(--brand-500); - inset-inline-start: -20px; - opacity: 0; - top: 16px -} - -.active__24af7 .buttonWrapper__24af7,.buttonWrapper__24af7:hover { - color: var(--interactive-text-active) -} - -.container_c0c49a { - display: flex; - position: relative -} - -.container_c0c49a:before { - background: var(--custom-promotion-gradient); - border-radius: 8px; - content: ""; - inset: 0; - opacity: 0; - pointer-events: none; - position: absolute; - transition: opacity .2s ease; - z-index: -1 -} - -.containerHovered_c0c49a:before { - opacity: 1 -} - -.iconContainer_c0c49a { - align-items: center; - display: flex; - height: 20px; - justify-content: center; - pointer-events: none; - position: relative; - width: 20px -} - -.giftBoxIcon_c0c49a { - bottom: -6px; - height: auto; - inset-inline-start: -6px; - position: absolute; - width: 32px -} - -.trinketsIcon_c0c49a { - position: relative; - z-index: 3 -} - -.trinketsDecoration_c0c49a { - height: 140%; - inset-inline-start: -20%; - position: absolute; - top: -20%; - transform: scale(1.4); - width: 140%; - z-index: 4 -} - -.trinketsGlow_c0c49a { - border-radius: 8px; - height: 150%; - inset-inline-start: -25%; - -webkit-mask: radial-gradient(circle at center,transparent 0,transparent 1%,#000 0); - mask: radial-gradient(circle at center,transparent 0,transparent 1%,#000 0); - position: absolute; - top: -25%; - transform: scale(1.1); - width: 150%; - z-index: 1 -} - -.button_aa63ab>.disabled_aa63ab { - color: var(--icon-muted); - cursor: not-allowed -} - -.container_aa63ab { - align-items: center; - display: flex; - margin-inline-start:0} - -.separator_aa63ab { - align-self: center; - background-color: var(--border-muted); - height: var(--chat-markup-line-height); - margin-inline-start:0;width: 1px -} - -.buttonContainer_aa63ab { - display: flex -} - -.sendIcon_aa63ab { - height: 20px!important; - width: 20px!important -} - -.button_aa63ab { - box-sizing: border-box; - margin-inline:0;min-height: var(--space-32); - min-width: var(--space-32); - padding: 4px -} - -.buttonChild_aa63ab { - transition: color .25s ease-in-out -} - -.activeButtonChild_aa63ab { - color: var(--text-brand) -} - -.theme-dark .button_aa63ab:focus .activeButtonChild_aa63ab,.theme-dark .button_aa63ab:focus-within .activeButtonChild_aa63ab,.theme-dark .button_aa63ab:hover .activeButtonChild_aa63ab { - color: var(--brand-430) -} - -.theme-dark .button_aa63ab:active .activeButtonChild_aa63ab { - color: var(--brand-500) -} - -.theme-light .activeInnerButton_aa63ab:focus,.theme-light .activeInnerButton_aa63ab:hover,.theme-light .buttonContainer_aa63ab:focus,.theme-light .buttonContainer_aa63ab:hover { - color: var(--brand-560) -} - -.theme-light .activeInnerButton_aa63ab:active,.theme-light .buttonContainer_aa63ab:active { - color: var(--brand-600) -} - -.reduce-motion .button_aa63ab { - transition: unset -} - -.container_aa63ab:before { - display: none -} - -.container_ddf599 { - display: inline-flex; - padding-top: var(--space-8); - padding-inline:19px;padding-bottom: var(--space-16) -} - -.container_ddf599 label[data-mana-component=checkbox] { - align-items: center; - gap: var(--space-8); - margin: calc(var(--space-4)*-1); - padding: var(--space-4) -} - -.container_ddf599 label[data-mana-component=checkbox]>div:first-of-type { - height: 16px; - width: 16px -} - -.container_ddf599 label[data-mana-component=checkbox] label>div { - font-size: 14px; - line-height: 1.25 -} - -.density-compact .container_ddf599 { - padding-inline:17px} - -.density-cozy .container_ddf599 { - padding-inline:21px} - -.icon_ddf599 { - height: 1em; - margin-bottom: -2px; - margin-top: -2px; - margin-inline:0;margin-inline-end:2px;width: 1em -} - -.icon_ddf599,.label_ddf599 { - display: inline -} - -.upload_aa605f { - background: var(--background-gradient-higher,var(--background-base-lower)); - background-color: var(--background-surface-high); - border: 1px solid var(--border-normal); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-low); - display: inline-flex; - flex-direction: column; - margin: 0; - max-height: 200px; - max-width: 200px; - min-height: 200px; - min-width: 200px; - padding: 8px; - position: relative -} - -.upload_aa605f:focus .smallActionBar_aa605f,.upload_aa605f:focus-within .smallActionBar_aa605f { - opacity: 1 -} - -.upload_aa605f.sizeClip_aa605f { - background-color: var(--background-mod-subtle); - flex: 1; - max-height: 400px; - max-width: 400px; - min-width: 320px; - padding: 0 -} - -.uploadContainer_aa605f { - display: flex; - flex-direction: column; - height: 100%; - position: relative -} - -.uploadContainer_aa605f:hover .smallActionBar_aa605f { - opacity: 1 -} - -.uploadContainer_aa605f:focus-within .smallActionBar_aa605f { - opacity: 1 -} - -.actionBarContainer_aa605f { - top: 0 -} - -.actionBar_aa605f,.actionBarContainer_aa605f { - inset-inline-end: 0; - position: absolute -} - -.actionBar_aa605f { - padding: 0; - transform: translate(25%,-25%); - z-index: 1 -} - -.smallActionBar_aa605f { - opacity: 0 -} - -.miniPopover_aa605f { - align-items: center; - height: 24px -} - -@keyframes uploadIconAnimateInLeft_b78547 { - 0% { - opacity: .6; - transform: translateZ(0) rotate(0deg) - } - - 4.1667% { - opacity: .68; - transform: translate3d(0,-1px,0) rotate(0deg) - } - - 30.8334% { - opacity: 1; - transform: translate3d(-48px,-10px,0) rotate(-36deg) - } - - 58.3334% { - opacity: 1; - transform: translate3d(-41px,-3px,0) rotate(-28deg) - } - - 75% { - opacity: 1; - transform: translate3d(-45px,-8px,0) rotate(-32deg) - } - - 83.3334% { - opacity: 1; - transform: translate3d(-44px,-7px,0) rotate(-30.7deg) - } - - to { - opacity: 1; - transform: translate3d(-44px,-6px,0) rotate(-30deg) - } -} - -@keyframes uploadIconAnimateInMiddle_b78547 { - 0% { - transform: translateZ(0) - } - - 30.8334% { - transform: translate3d(0,-10px,0) - } - - 58.3334% { - transform: translate3d(0,-3px,0) - } - - 75% { - transform: translate3d(0,-8px,0) - } - - 83.3334% { - transform: translate3d(0,-7px,0) - } - - to { - transform: translate3d(0,-6px,0) - } -} - -@keyframes uploadIconAnimateInRight_b78547 { - 0% { - opacity: .6; - transform: translateZ(0) rotate(0deg) - } - - 4.1667% { - opacity: .68; - transform: translate3d(0,-1px,0) rotate(0deg) - } - - 30.8334% { - opacity: 1; - transform: translate3d(48px,-10px,0) rotate(36deg) - } - - 58.3334% { - opacity: 1; - transform: translate3d(41px,-3px,0) rotate(28deg) - } - - 75% { - opacity: 1; - transform: translate3d(45px,-8px,0) rotate(32deg) - } - - 83.3334% { - opacity: 1; - transform: translate3d(44px,-7px,0) rotate(30.7deg) - } - - to { - opacity: 1; - transform: translate3d(44px,-6px,0) rotate(30deg) - } -} - -@keyframes uploadModalShake_b78547 { - 10%,90% { - transform: translate3d(-1px,0,0) - } - - 20%,80% { - transform: translate3d(2px,0,0) - } - - 30%,50%,70% { - transform: translate3d(-4px,0,0) - } - - 40%,60% { - transform: translate3d(4px,0,0) - } -} - -@keyframes uploadTextFadeIn_b78547 { - 0%,8.334% { - opacity: 0; - transform: translate3d(0,-4px,0) - } - - 45.8%,to { - opacity: 1; - transform: translateZ(0) - } -} - -@keyframes uploadModalBounceTransition_b78547 { - 0% { - transform: translateZ(0) scale(.99) - } - - 50% { - transform: translateZ(0) scale(1.005) - } - - to { - transform: translateZ(0) scale(1) - } -} - -@keyframes uploadModalBounce_b78547 { - 0% { - transform: translateZ(0) scale(.99) - } - - 15%,to { - inset: -10px - } - - 33.3334% { - transform: translateZ(0) scale(1.005) - } - - 54.1667% { - transform: translateZ(0) scale(.995) - } - - 70.8334%,to { - transform: translateZ(0) scale(1) - } -} - -.uploadArea_b78547 { - align-items: center; - background: var(--opacity-black-80); - color: var(--white); - display: flex; - font-size: 36px; - height: 100%; - inset-inline-start: 0; - justify-content: center; - opacity: 0; - position: absolute; - top: 0; - visibility: hidden; - width: 100%; - z-index: 2000 -} - -.uploadArea_b78547 strong { - font-weight: var(--font-weight-bold) -} - -.uploadArea_b78547.droppable_b78547 { - visibility: visible -} - -.uploadArea_b78547.uploadModalIn_b78547 { - opacity: 1 -} - -.uploadDropModal_b78547 { - animation: uploadIconAnimateIn_b78547 .25s ease-in-out 0s normal forwards; - display: flex; - height: 170px; - pointer-events: none; - position: relative; - width: 310px -} - -.uploadDropModal_b78547 .instructions_b78547,.uploadDropModal_b78547 .title_b78547 { - animation: uploadTextFadeIn_b78547 1s ease .175s normal forwards; - opacity: 0 -} - -.uploadDropModal_b78547 .instructions_b78547 { - animation-delay: .185s; - animation-duration: 1.25s -} - -.uploadDropModal_b78547 .bgScale_b78547 { - background: var(--brand-500); - border-radius: 10px; - inset: 0; - position: absolute; - z-index: -1 -} - -.full-motion .uploadDropModal_b78547 .bgScale_b78547 { - animation: uploadModalBounce_b78547 .8s ease 0s normal forwards -} - -.uploadDropModal_b78547 .inner_b78547 { - align-items: center; - border: 2px dashed var(--opacity-white-40); - border-radius: 6px; - color: var(--white); - display: flex; - flex: 1; - flex-direction: column; - justify-content: center; - width: 100% -} - -.uploadDropModal_b78547 .inner_b78547 .title_b78547 { - box-sizing: border-box; - color: var(--white); - font-size: 22px; - font-weight: var(--font-weight-bold); - padding: 0 8px; - text-align: center; - width: 100% -} - -.uploadDropModal_b78547 .inner_b78547 .title_b78547 strong { - color: var(--white) -} - -.uploadDropModal_b78547 .inner_b78547 .instructions_b78547 { - color: var(--white); - font-size: 12px; - line-height: 16px; - margin-top: 4px; - margin-inline-start:4px;text-align: center -} - -.icons_b78547 { - height: 60px; - position: relative; - width: 100px -} - -.icon_b78547 { - background-position: 50%; - background-repeat: no-repeat; - flex-shrink: 0; - height: 130px; - transform: translateZ(0); - width: 100px -} - -.icon_b78547.one_b78547,.icon_b78547.three_b78547,.icon_b78547.two_b78547 { - position: absolute -} - -.full-motion .icon_b78547.one_b78547 { - animation: uploadIconAnimateInLeft_b78547 .8s ease 0s normal forwards; - opacity: 0 -} - -.full-motion .icon_b78547.two_b78547 { - animation: uploadIconAnimateInMiddle_b78547 .8s ease 0s normal forwards; - filter: drop-shadow(0 0 48px rgba(88,101,242,.5)) -} - -.full-motion .icon_b78547.three_b78547 { - animation: uploadIconAnimateInRight_b78547 .8s ease 0s normal forwards; - opacity: 0 -} - -.reduce-motion .icon_b78547.one_b78547 { - transform: translate3d(-44px,-6px,0) rotate(-30deg) -} - -.reduce-motion .icon_b78547.two_b78547 { - transform: translate3d(0,-6px,0) -} - -.reduce-motion .icon_b78547.three_b78547 { - transform: translate3d(44px,-6px,0) rotate(30deg) -} - -.wrapOne_b78547 { - transform: translate3d(0,-70px,0) -} - -.wrapTwo_b78547 { - transform: translate3d(0,-80px,0) -} - -.wrapThree_b78547 { - transform: translate3d(0,-70px,0) -} - -.document_b78547 { - background-image: url(/assets/9ebe3ef63aafee84.svg) -} - -.image_b78547 { - background-image: url(/assets/5dea307a4037ec4e.svg) -} - -.code_b78547 { - background-image: url(/assets/f4bad77442422ded.svg) -} - -.crossOne_b78547,.crossTwo_b78547,.lightOne_b78547,.lightTwo_b78547,.popOne_b78547,.sparkleOne_b78547,.sparkleTwo_b78547 { - animation-iteration-count: 1!important; - opacity: .95; - z-index: 1 -} - -.sparkleOne_b78547 { - bottom: -40px; - inset-inline-end: -15px -} - -.sparkleTwo_b78547 { - animation-delay: 1.2s; - inset-inline-end: 12px; - top: -67px -} - -.lightOne_b78547 { - animation-delay: .4s; - inset-inline-end: -35px; - top: 24px -} - -.lightTwo_b78547 { - animation-delay: .61s; - inset-inline-start: -10px; - top: -32px -} - -.crossOne_b78547 { - animation-delay: .56s; - bottom: -35px; - inset-inline-end: 100px -} - -.crossTwo_b78547 { - animation-delay: .8s; - bottom: 50px; - inset-inline-start: -70px -} - -.popOne_b78547 { - animation-delay: .7s; - bottom: -40px; - inset-inline-start: 50px -} - -.fileIcon_b78547 { - background-position: 50%; - background-repeat: no-repeat; - flex-shrink: 0; - height: 130px; - margin-bottom: 16px; - margin-top: -16px; - width: 100px -} - -.fileIcon_b78547.image_b78547 { - background-color: var(--background-base-lower); - border-radius: 8px; - box-shadow: 0 2px 8px var(--opacity-black-40); - box-sizing: border-box; - -o-object-fit: contain; - object-fit: contain -} - -.fileIcon_b78547.video_b78547 { - background-image: url(/assets/98178bd2173d947a.svg) -} - -.fileIcon_b78547.acrobat_b78547 { - background-image: url(/assets/a42338c030af6d9c.svg) -} - -.fileIcon_b78547.ae_b78547 { - background-image: url(/assets/88b289225ff6142a.svg) -} - -.fileIcon_b78547.sketch_b78547 { - background-image: url(/assets/c9f0992f0ab98d49.svg) -} - -.fileIcon_b78547.ai_b78547 { - background-image: url(/assets/93636da24d7ab467.svg) -} - -.fileIcon_b78547.archive_b78547 { - background-image: url(/assets/f89b66e97ea52ba7.svg) -} - -.fileIcon_b78547.code_b78547 { - background-image: url(/assets/f4bad77442422ded.svg) -} - -.fileIcon_b78547.document_b78547 { - background-image: url(/assets/9ebe3ef63aafee84.svg) -} - -.fileIcon_b78547.photoshop_b78547,.fileIcon_b78547.ps_b78547 { - background-image: url(/assets/1084490cf680dd7c.svg) -} - -.fileIcon_b78547.spreadsheet_b78547 { - background-image: url(/assets/b8ca4488a6d443bc.svg) -} - -.fileIcon_b78547.webcode_b78547 { - background-image: url(/assets/6e688b0c124e0ca8.svg) -} - -.fileIcon_b78547.audio_b78547 { - background-image: url(/assets/a6db1d05d35f13d0.svg) -} - -.fileIcon_b78547.unknown_b78547 { - background-image: url(/assets/94660b205108a49f.svg) -} - -.container_b9f75c { - align-items: flex-start; - background: var(--background-surface-high); - box-sizing: border-box; - display: flex; - gap: var(--space-8); - padding: var(--space-12); - width: 100% -} - -.clipIcon_b9f75c { - align-items: center; - background-color: var(--background-mod-subtle); - border-radius: var(--radius-sm); - display: flex; - flex-shrink: 0; - height: 32px; - justify-content: center; - overflow: hidden; - position: relative; - width: 32px -} - -.clipIconImage_b9f75c { - height: 100%; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.clipTextInfo_b9f75c { - display: flex; - flex: 1; - flex-direction: column; - gap: 2px; - justify-content: center; - min-width: 0 -} - -.clipSubtitle_b9f75c,.clipTitle_b9f75c { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.clipAvatars_b9f75c { - align-items: center; - display: flex; - flex-shrink: 0 -} - -.actionBarIcon_a7e304 { - display: block; - height: 20px; - -o-object-fit: contain; - object-fit: contain; - width: 20px -} - -.filenameContainer__41ea0 { - margin-top: auto -} - -.filename__41ea0 { - margin-top: 8px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.mediaContainer__41ea0 { - margin-top: auto; - min-height: 0; - position: relative -} - -.mediaContainer__41ea0>div:not([aria-expanded=false]),.mediaContainer__41ea0>div:not([aria-expanded=false])>div { - height: 100% -} - -.tags__41ea0 { - bottom: 6px; - inset-inline-start: 3px; - position: absolute -} - -.altTag__41ea0 { - background: var(--text-default); - border-radius: 3px; - font-size: 10px; - font-weight: var(--font-weight-semibold); - margin-inline-end:4px;padding: 3px; - text-transform: uppercase; - z-index: 1 -} - -.spoilerContainer__41ea0 { - height: 100% -} - -.spoilerContainer__41ea0.sizeXSmall__41ea0,.spoilerContainer__41ea0.sizeXXSmall__41ea0 { - background: none -} - -.spoilerWrapper__41ea0 { - display: flex; - height: 100%; - justify-content: center -} - -.media__41ea0 { - border-radius: var(--radius-md); - max-width: 100%; - -o-object-fit: contain; - object-fit: contain -} - -.media__41ea0.sizeClip__41ea0 { - aspect-ratio: 16/9; - border-end-end-radius: 0; - border-end-start-radius: 0 -} - -.media__41ea0.sizeXSmall__41ea0,.media__41ea0.sizeXXSmall__41ea0 { - border-radius: var(--radius-xs); - -o-object-fit: cover; - object-fit: cover -} - -.clickableMedia__41ea0 { - cursor: pointer -} - -.spoiler__41ea0 { - filter: blur(var(--custom-channel-attachment-upload-spoiler-blur-radius)); - pointer-events: none -} - -.icon__41ea0 { - background-position: 50%; - background-repeat: no-repeat; - height: 144px -} - -.icon__41ea0.imageSmall__41ea0 { - height: var(--custom-channel-attachment-upload-mini-attachment-size) -} - -.icon__41ea0.imageSmall__41ea0,.icon__41ea0.sizeXSmall__41ea0,.icon__41ea0.sizeXXSmall__41ea0 { - background-size: contain -} - -.icon__41ea0.video__41ea0 { - background-image: url(/assets/98178bd2173d947a.svg) -} - -.icon__41ea0.acrobat__41ea0 { - background-image: url(/assets/a42338c030af6d9c.svg) -} - -.icon__41ea0.ae__41ea0 { - background-image: url(/assets/88b289225ff6142a.svg) -} - -.icon__41ea0.sketch__41ea0 { - background-image: url(/assets/c9f0992f0ab98d49.svg) -} - -.icon__41ea0.ai__41ea0 { - background-image: url(/assets/93636da24d7ab467.svg) -} - -.icon__41ea0.archive__41ea0 { - background-image: url(/assets/f89b66e97ea52ba7.svg) -} - -.icon__41ea0.code__41ea0 { - background-image: url(/assets/f4bad77442422ded.svg) -} - -.icon__41ea0.document__41ea0 { - background-image: url(/assets/9ebe3ef63aafee84.svg) -} - -.icon__41ea0.photoshop__41ea0,.icon__41ea0.ps__41ea0 { - background-image: url(/assets/1084490cf680dd7c.svg) -} - -.icon__41ea0.spreadsheet__41ea0 { - background-image: url(/assets/b8ca4488a6d443bc.svg) -} - -.icon__41ea0.webcode__41ea0 { - background-image: url(/assets/6e688b0c124e0ca8.svg) -} - -.icon__41ea0.audio__41ea0 { - background-image: url(/assets/a6db1d05d35f13d0.svg) -} - -.icon__41ea0.unknown__41ea0 { - background-image: url(/assets/94660b205108a49f.svg) -} - -.attachmentItemSmall__41ea0 { - background-color: unset; - max-height: var(--custom-channel-attachment-upload-mini-attachment-size); - max-width: var(--custom-channel-attachment-upload-mini-attachment-size); - min-height: var(--custom-channel-attachment-upload-mini-attachment-size); - min-width: var(--custom-channel-attachment-upload-mini-attachment-size); - padding: 0 -} - -.imageSmall__41ea0 { - border-radius: 12px; - height: var(--custom-channel-attachment-upload-mini-attachment-size); - -o-object-fit: cover; - object-fit: cover -} - -.sizeXSmall__41ea0 { - height: 48px; - min-width: 48px; - width: 48px -} - -.sizeXXSmall__41ea0 { - height: 32px; - min-width: 32px; - width: 32px -} - -.actionBarIcon__41ea0 { - display: block; - height: 16px; - -o-object-fit: contain; - object-fit: contain; - width: 16px -} - -.action__41ea0 { - height: 16px; - min-width: 18px; - padding: 4px -} - -.clipsBadge__41ea0 { - inset-inline-start: 8px; - position: absolute; - top: 8px -} - -.clipsFooter__41ea0 { - background-color: transparent -} - -.theme-dark .altTag__41ea0 { - color: #000; - mix-blend-mode: screen -} - -.theme-light .altTag__41ea0 { - color: #fff; - mix-blend-mode: multiply -} - -.emptyOption_b088b0 { - border: 2px dashed var(--text-default); - border-radius: 8px -} - -.emptyOptionActive_b088b0 { - border-color: var(--text-link) -} - -.clickContainer_b088b0 { - cursor: pointer; - display: flex; - height: 100%; - width: 100% -} - -.commandOptionContainer_b088b0 { - display: flex; - flex-direction: column; - margin: auto; - width: 80% -} - -.optionName_b088b0 { - background-color: var(--background-base-lowest); - border-radius: 4px; - color: var(--text-default); - inset-inline-end: 8px; - padding: 2px 6px; - position: absolute -} - -.optionNameActive_b088b0 { - color: var(--text-link) -} - -.optionIcon_b088b0 { - height: 100px -} - -.optionHelp_b088b0 { - text-align: center -} - -.fileInput_b088b0 { - display: none -} - -.channelAttachmentArea_b77158 { - display: flex; - gap: 24px; - margin: 0; - overflow-x: auto; - padding: 20px 10px 10px -} - -.channelAttachmentArea_b77158:last-child { - padding-inline-end:30px} - -.divider_b77158 { - margin-inline-start:16px} - -.root__6c5a1 { - background: var(--background-base-lower); - background-color: var(--background-surface-highest); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - display: flex; - max-width: 100%; - padding: var(--space-4) var(--space-8) -} - -.text__6c5a1 { - margin-inline-start:4px} - -.premium__6c5a1 { - flex-shrink: 0; - height: 18px; - width: 18px -} - -.iconOnly__6c5a1 { - color: var(--text-default); - cursor: pointer -} - -.characterCount__795fb { - align-items: flex-end; - bottom: 10px; - color: var(--text-default); - display: flex; - flex-direction: column; - inset-inline-end: 14px; - position: absolute -} - -.premiumFlair__795fb { - color: var(--interactive-text-default) -} - -.premiumFlair__795fb:not(:last-child) { - margin-inline-end:4px} - -.flairContainer__795fb { - align-items: center; - display: flex; - height: 24px -} - -.upsell__795fb { - margin-top: 8px -} - -.container_be506c { - margin: 8px 8px 0 -} - -.toolbar_bba883 { - align-items: center; - background-color: var(--primary-800); - border-radius: 4px; - box-shadow: var(--shadow-border),var(--shadow-high); - color: var(--primary-300); - display: flex; - flex-direction: column; - pointer-events: all; - position: absolute -} - -.toolbar_bba883:before { - border-inline-end:8px solid transparent;border-inline-start:8px solid transparent;border-top: 8px solid var(--primary-800); - bottom: -8px; - content: ""; - height: 0; - inset-inline-start: calc(50% - 8px); - position: absolute; - width: 0 -} - -.buttons_bba883 { - border-radius: 4px; - display: flex; - flex-direction: row; - height: 32px; - overflow: hidden -} - -.staticToolbar_bba883 { - border-radius: 4px 4px 0 0; - display: flex; - justify-content: space-between; - margin-bottom: calc(var(--space-4)*-1) -} - -.staticToolbar_bba883 .button_bba883:first-of-type { - border-start-start-radius: 4px -} - -.theme-dark .staticToolbar_bba883 { - background-color: var(--background-mod-normal) -} - -.theme-light .staticToolbar_bba883 { - background-color: var(--background-base-lowest) -} - -.staticButtons_bba883 { - border-radius: 4px 4px 0 0; - display: flex -} - -.emojiButton_bba883 { - padding-inline-end:8px} - -.divider_bba883 { - border-inline-start:1px solid var(--opacity-white-4);display: inline-block; - height: 20px; - margin: 6px -} - -.button_bba883 { - align-items: center; - background-color: transparent; - border-radius: 0; - color: var(--primary-300); - display: flex; - justify-content: center; - padding: 0; - width: 32px -} - -.button_bba883:hover { - background-color: var(--interactive-background-hover); - color: var(--primary-100) -} - -.button_bba883[aria-pressed=true] { - background-color: var(--interactive-background-selected); - color: var(--white) -} - -.buttonInner_bba883,.icon_bba883 { - height: 20px -} - -.icon_bba883 { - color: currentColor; - width: 20px -} - -.staticIcon_bba883 { - color: var(--interactive-text-default) -} - -.staticDivider_bba883 { - border-inline-start:1px solid var(--border-subtle)} - -.body_c93be2 { - margin-bottom: 40px; - margin-top: 40px -} - -.textArea_c93be2 { - --channel-text-area-placeholder: var(--input-placeholder-text-default); - height: 136px; - transition: border-color .2s ease-in-out -} - -.textArea_c93be2:focus-within { - border-color: var(--text-link) -} - -.textArea_c93be2>div { - height: 100% -} - -.editorTextArea_c93be2 { - height: 136px -} - -.theme-dark .textArea_c93be2 *>span { - color: var(--white) -} - -.theme-light .textArea_c93be2 *>span { - color: var(--text-default) -} - -.container__8564e { - align-items: center; - background-color: var(--primary-860); - border-radius: var(--radius-sm); - cursor: pointer; - display: flex; - gap: var(--space-6); - height: var(--space-32); - justify-content: center; - opacity: .7; - padding: 0 var(--space-16); - width: 140px -} - -.container__8564e.refresh__8564e { - background-color: var(--background-mod-subtle); - border-color: var(--border-muted); - opacity: 1 -} - -.contentContainer__8564e { - align-items: center; - display: flex; - gap: var(--space-6); - min-width: 0 -} - -.soundIcon__8564e { - min-height: 14px; - min-width: 14px -} - -.text__8564e { - color: var(--white) -} - -.text__8564e.refresh__8564e { - color: var(--text-strong) -} - -.customGiftHeader__8564e { - align-items: center; - background-color: var(--background-surface-high); - box-shadow: var(--elevation-low); - display: block; - grid-column: 1/3; - grid-row: 1/2; - padding: 12px; - z-index: 1 -} - -.searchAndSound__8564e { - align-items: center; - display: flex; - margin-top: 10px -} - -.container__231eb { - align-items: center; - background-color: var(--primary-860); - border-radius: var(--radius-sm); - cursor: pointer; - display: flex; - gap: var(--space-6); - height: var(--space-32); - justify-content: center; - opacity: .7; - padding: 0 var(--space-16); - width: 140px -} - -.container__231eb.refresh__231eb { - background-color: var(--background-mod-subtle); - border-color: var(--border-muted); - opacity: 1 -} - -.contentContainer__231eb { - align-items: center; - display: flex; - gap: var(--space-6); - min-width: 0 -} - -.emojiIcon__231eb { - min-height: 14px; - min-width: 14px -} - -.text__231eb { - color: var(--white) -} - -.text__231eb.refresh__231eb { - color: var(--text-strong) -} - -.emojiList__231eb { - background-color: var(--background-surface-high) -} - -.emojiHeader__231eb { - background-color: var(--background-base-lower)!important -} - -.customGiftContent__231eb { - display: flex; - flex-direction: column; - width: 100% -} - -.customGiftHeader__231eb { - margin-bottom: 10px -} - -.categoryList__231eb { - top: 120px -} - -.customGiftBox_d50aac,.customGiftBoxHighlighted_d50aac { - background-color: var(--transparent); - border: 3px solid var(--transparent); - border-radius: 8px; - cursor: pointer -} - -.customGiftBoxHighlighted_d50aac { - border: 2px solid var(--brand-500); - padding: 2px -} - -.button_d50aac { - width: 100% -} - -.giftMainAnimation_d54fab { - align-items: flex-end; - display: flex; - height: 275px; - justify-content: center; - position: relative -} - -.adjustedGiftMainAnimation_d54fab { - height: 230px; - margin-inline-start:-20px} - -.soundEmojiContainer_d54fab { - bottom: 16px; - display: flex; - gap: var(--space-6); - justify-content: center; - position: absolute; - width: 100% -} - -.soundEmojiContainer_d54fab.refresh_d54fab { - bottom: unset; - gap: var(--space-12); - height: -moz-fit-content; - height: fit-content; - margin-top: 12px; - top: 100% -} - -.animation_d54fab { - transform: scale(1) -} - -.spinner_d54fab { - height: 100%; - transform: scale(.75); - width: 100% -} - -.giftBoxOptionContainer_d54fab { - align-items: center; - display: flex; - flex-direction: row; - gap: 9px; - margin: 25px auto auto; - max-width: 85% -} - -.adjustedGiftBoxOptionContainer_d54fab { - margin-inline-start:15px} - -.planOption_b13085 { - align-items: center; - color: var(--interactive-text-default); - display: flex; - justify-content: space-between; - padding: 8px 0 -} - -.planOneTimeCost_b13085 { - margin-top: 8px -} - -.selectionBox_b13085 { - border-radius: var(--radius-lg); - margin-bottom: 12px; - outline: 1px solid var(--border-normal); - padding: var(--space-12) -} - -.selectedPlan_b13085 { - background-color: var(--background-surface-high); - outline: 2px solid var(--brand-500); - outline-color: var(--checkbox-border-default) -} - -.planOptionDisabled_b13085 { - opacity: .6 -} - -.planOptionClickable_b13085 { - align-items: center; - display: flex -} - -.planOptionDisabled_b13085 .planOptionClickable_b13085 { - cursor: not-allowed -} - -.planOptionCheckbox_b13085 { - margin-inline-end:10px} - -.planOptionInterval_b13085 { - color: var(--interactive-text-default); - font-weight: var(--font-weight-medium) -} - -.optionSelected_b13085 { - color: var(--interactive-text-active) -} - -.planOptionCurrentPlan_b13085,.planOptionMonthsFree_b13085 { - margin-inline-start:4px} - -.planOptionDiscount_b13085 { - border-radius: 16px; - margin-inline-start:8px;padding: 2px 8px -} - -.annualPlanOptionDiscount_b13085,.planOptionDiscount_b13085 { - background-color: var(--green-360); - font-weight: var(--font-weight-semibold) -} - -.annualPlanOptionDiscount_b13085 { - align-content: center; - border-radius: 8px; - margin-top: 3px; - margin-inline-end:8px;padding: 0 6px; - position: relative -} - -.planOptionSubtextContainer_b13085 { - display: flex; - justify-content: flex-end; - width: 100% -} - -.planOptionSubtext_b13085 { - max-width: 176px; - padding-bottom: 12px -} - -.planWithOfferOptionSubtext_b13085 { - max-width: 300px; - text-align: end -} - -.planWithOfferOptionSubtext_b13085 p { - margin: 0 -} - -.planOptionClickableContainer_b13085 { - cursor: pointer -} - -.optionPriceSelected_b13085,.updatedOptionSelected_b13085 { - font-weight: var(--font-weight-semibold) -} - -.optionPriceSelected_b13085 { - color: var(--interactive-text-active) -} - -.giftRecipientInfo__57118 { - align-items: center; - display: flex -} - -.giftRecipientInfo__57118>* { - margin-inline-end:8px} - -.content__57118 { - margin: 24px 0 -} - -.giftRecipientName__57118,.giftRecipientTag__57118 { - max-width: 200px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.giftRecipientTag__57118 { - color: GrayText -} - -.stepBody_d947e6 { - padding-top: 12px -} - -.giftOptions_d947e6 { - justify-content: center; - max-width: 75% -} - -.stepBodyCustomGift_d947e6 { - display: flex; - flex-direction: row; - width: 820px -} - -.bodyColumnMiddle_d947e6 { - margin-inline-end:30px} - -.bodyColumnMiddleCentered_d947e6 { - align-items: center; - display: flex; - justify-content: center; - margin-inline-end:30px} - -.bodyColumnLeft_d947e6 { - margin-inline:var(--space-24) 60px} - -.bodyColumnRight_d947e6 { - margin-inline-end: 10px -} - -.stepBodyCustomGift_d947e6>* { - width: 50% -} - -.bodyText_d947e6 { - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px; - margin-bottom: 16px -} - -.bodyText_d947e6,.interactiveColor_d947e6 { - color: var(--interactive-text-default) -} - -.clickable_d947e6 { - cursor: pointer -} - -.selectPlanDivider_d947e6 { - background-color: var(--border-subtle); - height: 1px; - margin: 8px 0 16px -} - -.giftRecipientName_d947e6 { - color: GrayText -} - -.giftRecipientInfo_d947e6 { - align-items: center; - display: flex -} - -.giftRecipientInfo_d947e6>* { - margin-inline-end:8px} - -.giftNitroInfo_d947e6 { - margin-bottom: 20px; - margin-top: 15px -} - -.giftNitroInfo_d947e6 hr { - background-color: var(--border-subtle); - border: none; - height: 1px -} - -.selectPlanChooseSubtitle_d947e6,.selectPlanChooseTitle_d947e6 { - color: var(--interactive-text-active); - font-size: 16px; - line-height: 20px; - margin-bottom: 8px -} - -.selectPlanChooseTitle_d947e6 { - font-weight: var(--font-weight-semibold) -} - -.selectPlanChooseSubtitle_d947e6 { - margin-bottom: 18px -} - -.selectPlanTotalRow_d947e6 { - margin-top: 8px -} - -.seasonalGiftBoxHeaderIcon_d947e6 { - align-self: center; - position: absolute; - top: -131px; - width: 260px -} - -.planSelectSeparator_d947e6 { - background: var(--border-subtle); - border: 0; - height: 1px; - margin: 0 0 4px; - width: 100% -} - -.trialPlanSelectHeader_d947e6 { - padding-bottom: 12px -} - -.legacyPricingNotice_d947e6 { - margin-bottom: 16px -} - -.customGiftMessageWrapper_d947e6 { - margin-bottom: 16px; - margin-top: 16px -} - -.customGiftMessage_d947e6.customGiftMessage_d947e6 { - background-color: var(--background-base-lowest) -} - -.selectGiftTitle_d947e6 { - margin-top: 16px -} - -.compactSendGiftToUser_d947e6 { - margin: 16px 0 -} - -.compactSelectGiftTitle_d947e6 { - margin-top: 16px -} - -.subscriptionCostRowAmount__3d62f { - font-weight: var(--font-weight-medium) -} - -.subscriptionCostRow__3d62f { - color: var(--text-strong) -} - -.invoiceItemLabelWithIcon__3d62f { - align-items: center; - display: flex -} - -.invoiceItemLabelIcon__3d62f { - color: var(--interactive-text-default); - height: 14px; - margin-inline-start:8px;width: 14px -} - -.invoiceItemTooltip__3d62f { - max-width: 240px -} - -.invoiceItemTooltip__3d62f p { - margin: 0 -} - -.invoiceItemTooltip__3d62f strong { - font-weight: var(--font-weight-bold) -} - -.subscriptionDetailsToggle__3d62f { - align-items: center; - color: var(--text-default); - cursor: pointer; - display: flex; - font-size: 12px; - font-weight: var(--font-weight-medium); - justify-content: center; - line-height: 16px; - margin: -16px 0; - padding: 16px 0 -} - -.subscriptionDetailsToggleCaret__3d62f { - height: 16px; - margin-inline-start:6px;width: 16px -} - -.subscriptionPeriodResetNotice__3d62f { - color: var(--text-default); - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px; - margin-bottom: 16px -} - -.subscriptionAddedInvoiceItem__3d62f { - color: var(--text-feedback-positive) -} - -.purchaseDetailsHeaderText__3d62f { - margin-bottom: 8px -} - -.root_e4d803 { - background-color: transparent -} - -.root_e4d803>:last-child { - border-end-end-radius: 4px; - border-end-start-radius: 4px -} - -.root_e4d803.withHeader_e4d803>:first-child { - background-color: transparent -} - -.shaker_e4d803 { - align-items: center; - display: flex; - flex-direction: column; - flex-grow: 1; - height: 100%; - justify-content: center; - max-height: 660px -} - -.modalHeader_e4d803 { - margin-bottom: -20px; - padding-bottom: 0 -} - -.header_e4d803 { - margin-bottom: 12px -} - -.headerAnimation_e4d803 { - bottom: -20px; - inset-inline-start: calc(50% - 220px); - position: absolute; - top: 0; - width: 440px -} - -.stepBody_e4d803 { - padding-top: 16px -} - -.bodyText_e4d803 { - color: var(--interactive-text-default); - font-size: 16px; - font-weight: var(--font-weight-medium); - line-height: 20px -} - -.invoice_e4d803,.taxInclusiveNote_e4d803 { - margin-bottom: 16px -} - -.premiumBrandRefreshInputBackground_e4d803 { - background-color: var(--background-mod-subtle)!important; - border: 0!important -} - -.paymentSourceWrapper_e4d803 { - margin: 12px 0 -} - -.paymentSourceOptionalWarning_e4d803 { - color: var(--text-default); - margin-top: 16px -} - -.currencyWrapper_e4d803 { - margin-bottom: 16px; - margin-top: 16px -} - -@media (max-width: 485px) { - .shaker_e4d803 { - position:absolute; - top: 0; - inset-inline: 0; - bottom: 0; - max-height: none - } - - .root_e4d803,.shaker_e4d803 { - justify-content: center - } - - .root_e4d803 { - border-radius: 0; - width: 100vw - } -} - -.trialCheckbox_e4d803 { - align-items: flex-start -} - -.trialCheckboxLabel_e4d803 { - font-size: 14px; - line-height: 18px -} - -.loader_e4d803 { - margin-top: 64px -} - -.contentWrapper_e4d803 { - display: flex; - flex: 1 1 auto; - flex-direction: column; - min-height: 0 -} - -.reviewWarningMessageContainer_e4d803 { - background-color: var(--background-feedback-warning); - border: 1px solid var(--status-warning-background); - border-radius: 4px; - display: flex; - flex-direction: row; - margin-bottom: 16px; - padding: 10px -} - -.reviewWarningMessage_e4d803 { - color: var(--interactive-text-active); - margin-inline-start:10px} - -.trialPriceLine_e4d803 { - align-items: center; - display: flex; - justify-content: space-between; - padding-bottom: 4px -} - -.afterTrialPriceLine_e4d803 { - align-items: center; - display: flex; - justify-content: flex-end -} - -.discountSubtext_e4d803 { - text-align: end -} - -.discountSubtext_e4d803 p { - margin: 0 -} - -.formTitle_e4d803 { - margin-bottom: 12px -} - -.spinnerWrapper_e4d803 { - align-items: center; - display: flex; - justify-content: center; - min-height: 280px; - min-width: 402px -} - -.trialHeader_e4d803 { - margin-bottom: 18px -} - -.giftMainAnimation_e4d803 { - margin-bottom: var(--space-8); - margin-top: -32px; - position: relative -} - -.selectFreeSku_e4d803 { - display: none -} - -.premiumGroupNotice_e4d803 { - margin: 12px 0 -} - -.renewalInvoiceDate_d313e6 { - color: var(--text-default); - margin-bottom: 16px -} - -.paymentNote__56a21 { - background-color: var(--modal-background); - margin: 0; - padding: 16px 16px 0 -} - -.warningContainer__98bed { - align-items: center; - background-color: var(--background-feedback-warning); - border-radius: var(--radius-sm); - display: flex; - flex-direction: row; - gap: var(--space-10); - padding: var(--space-8) -} - -.checkoutModalFooter_e3bd55 { - align-items: center -} - -.stepBodyV1_bb5449 { - display: flex; - flex-direction: column; - gap: var(--space-8); - padding-top: var(--space-16) -} - -.topSpacer_bb5449 { - height: var(--space-8) -} - -.subscriptionDetailsContainer_bb5449 { - margin-top: var(--space-12) -} - -.invoiceSummaryContainer_bb5449,.paymentMethodContainer_bb5449 { - margin-top: var(--space-24) -} - -.invoiceSummaryContainer_bb5449 { - margin-bottom: var(--space-12) -} - -.reviewStepBody_bb5449 { - min-height: 440px -} - -.checkoutInlineNoticeContainer_bb5449 { - margin-bottom: var(--space-24) -} - -.loadingContainer_bb5449 { - display: flex; - flex-direction: column; - justify-content: center; - min-height: 440px -} - -.profileEffectContainer_a84142 { - border-radius: var(--radius-sm); - height: 296px; - margin: 16px auto 0; - overflow: hidden; - position: relative; - width: 200px -} - -.profileEffectBackground_a84142 { - position: absolute; - width: 100% -} - -.nameplateContainer_a84142 { - margin: var(--space-40) auto var(--space-48); - position: relative; - width: 324px -} - -.nameplate_a84142 { - width: 100% -} - -.giftMainAnimationWrapper_a84142 { - align-items: center; - display: flex; - justify-content: center -} - -.avatar_a84142 { - margin-top: 24px -} - -.bundlePreviewWrapper_a84142 { - margin: 0 auto; - width: 224px -} - -.slayerStorefrontReviewImageWrapper_a84142 { - display: flex; - justify-content: center; - margin-bottom: 8px; - width: 100% -} - -.slayerStorefrontReviewImage_a84142 { - max-width: 180px; - width: 100% -} - -.profileEffectContainer__0b5ea { - height: 52px; - position: relative; - width: 42px -} - -.profileEffect__0b5ea,.profileEffectBackground__0b5ea { - border-radius: var(--radius-xs); - height: 100%; - position: absolute; - width: 100% -} - -.profileEffect__0b5ea { - -o-object-fit: cover; - object-fit: cover; - -o-object-position: top; - object-position: top -} - -.avatarDecoration__0b5ea,.nameplateContainer__0b5ea { - height: 42px; - width: 42px -} - -.nameplateContainer__0b5ea { - background-color: var(--modal-background); - border-radius: var(--radius-xs); - overflow: hidden; - position: relative -} - -.externalProductAsset__0b5ea { - height: 42px -} - -.bundleMiniPreview__0b5ea { - align-items: center; - display: flex; - height: 52px; - justify-content: center; - position: relative; - width: 52px -} - -.bundleMiniProfileEffectContainer__0b5ea { - height: 44px; - inset-inline-start: 6px; - position: absolute; - top: 2px; - transform: rotate(-8deg); - width: 36px -} - -.bundleMiniProfileEffectContainer__0b5ea .profileEffectContainer__0b5ea { - height: 100%; - width: 100% -} - -.bundleMiniAvatarDecorationContainer__0b5ea { - height: 26px; - inset-inline-end: 0; - position: absolute; - top: 5px; - transform: rotate(8deg); - width: 26px -} - -.bundleMiniAvatarDecorationContainer__0b5ea .avatarDecoration__0b5ea { - height: 100%; - width: 100% -} - -.bundleMiniNameplateContainer__0b5ea { - border-radius: var(--radius-xs); - bottom: 10px; - height: 12px; - inset-inline-start: 0; - overflow: hidden; - position: absolute; - width: 100% -} - -.bundleMiniNameplateContainer__0b5ea .nameplateContainer__0b5ea { - height: 100%; - width: 100% -} - -.applicationIcon__6dec9 { - border-radius: 6px -} - -.container__42d0d { - align-items: center; - display: flex; - gap: 8px; - margin-bottom: 8px -} - -.invoiceTable__25766 { - margin-bottom: 16px -} - -.invoiceRow__25766 { - align-items: center; - display: flex; - flex-direction: row; - font-size: 16px; - font-weight: var(--font-weight-medium); - gap: 10px; - line-height: 20px -} - -.invoiceRow__25766:not(:last-child) { - margin-bottom: 8px -} - -.invoiceTagColumn__25766 { - flex: 1 -} - -.invoiceDescriptionColumn__25766 { - flex: 5 -} - -.invoiceRegularText__25766 { - color: var(--text-strong) -} - -.invoiceDiscountText__25766 { - color: var(--text-feedback-positive) -} - -.invoiceDiscountTag__25766 { - background-color: var(--green-360); - border-radius: 12px; - display: inline-block; - padding: 2px 6px; - position: relative; - text-align: center -} - -.totalRow__25766 { - font-weight: 700 -} - -.slayerStorefrontProductPreview__25766 { - box-sizing: border-box; - height: 48px; - width: 48px -} - -.rentalDescription__25766 { - color: var(--text-muted); - display: block; - margin-top: 4px -} - -.stepBody_b44961 { - padding-top: var(--space-8) -} - -.invoiceSpinner_b44961 { - height: 23px -} - -.errorBlock_b44961 { - margin: 5px 15px 15px; - position: relative -} - -.fineprint_b44961 { - margin-bottom: 0; - margin-top: 16px -} - -.skuHeading_b44961 { - display: flex; - gap: 25px; - margin-bottom: 20px -} - -.skuHeadingText_b44961 { - align-self: center -} - -.socialLayerGameItemDisclaimer_b44961 { - margin-top: 16px -} - -.back_e125f1 { - margin-inline-end:auto;min-width: auto; - padding: 2px 4px; - width: auto -} - -.modalFooter_e125f1 { - padding-top: var(--space-24)!important -} - -.shopPageContainer__4485d { - margin-top: 80px; - min-height: 300px -} - -.giftModalContainer__4485d,.shopPageContainer__4485d { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center -} - -.giftModalContainer__4485d { - background-color: var(--background-base-low); - padding: var(--space-24) -} - -.heading1__4485d { - margin-bottom: 24px; - margin-top: 24px -} - -.description__4485d { - text-align: center -} - -.reload__4485d { - margin-top: 24px -} - -.customConfetti__69c6c { - inset-inline-end: -75px; - pointer-events: none; - top: -120px -} - -.headerContainerGift__57a87 { - align-items: center; - background-image: linear-gradient(var(--modal-background),var(--modal-background)); - background-size: 100% calc(100% - 20px); - border-radius: var(--radius-md); - display: flex; - justify-content: space-between; - padding: 8px 16px -} - -.closeButtonGift__57a87 { - margin-inline-start:auto} - -.headerContainer__9abb9 { - background-color: var(--background-base-low); - border-start-end-radius: 4px; - border-start-start-radius: 4px; - box-sizing: border-box; - height: 146px; - margin-inline-start:-1px;margin-top: -2px; - min-height: 146px; - position: relative; - width: calc(100% + 2px) -} - -.closeButton__9abb9 { - inset-inline-end: 16px; - position: absolute; - top: 16px -} - -.headerImage__9abb9,.headerImageContainer__9abb9 { - height: 100%; - width: 100% -} - -.headerImage__9abb9 { - border-start-end-radius: var(--radius-md); - border-start-start-radius: var(--radius-md); - -o-object-fit: cover; - object-fit: cover -} - -.orbCheckoutHeaderVideo__9abb9 { - -o-object-fit: cover; - object-fit: cover; - -o-object-position: left top; - object-position: left top -} - -div.modalOverrideBody__34eac { - background-color: transparent; - border-radius: 8px; - overflow: visible; - padding: 0 -} - -.modalOverrideBody__34eac::-webkit-scrollbar { - height: 0; - width: 0 -} - -.modalOverrideSliderBody__34eac { - padding: 0; - width: 440px -} - -.confettiCanvas__34eac { - height: 100%; - position: absolute; - width: 100% -} - -.customConfetti__34eac { - opacity: 1; - pointer-events: none; - transition: opacity .5s ease-in-out -} - -.customConfetti__34eac.hidden__34eac { - opacity: 0 -} - -.modalOverrideBody__34eac { - margin-inline-start:-1px;margin-top: -2px; - padding: 0; - width: calc(100% + 2px) -} - -.blurb_cd2ff7 { - color: var(--text-default); - margin-top: 10px -} - -.blurb_cd2ff7,.giftSentMessage_cd2ff7 { - font-size: 16px; - line-height: 1.44; - max-width: 360px; - text-align: center -} - -.giftSentMessage_cd2ff7 { - color: var(--text-strong); - margin-bottom: 8px; - margin-top: 16px -} - -.giftRecipientTag_cd2ff7 { - color: var(--text-default); - font-size: 13px -} - -.giftRecipientContainer_cd2ff7 { - align-items: flex-start; - background-color: var(--background-mod-muted); - border-color: var(--border-subtle); - border-radius: var(--radius-sm); - border-style: solid; - border-width: 1px; - display: flex; - gap: 12px; - margin-top: 16px; - padding: var(--space-12) -} - -.giftRecipientTextContainer_cd2ff7 { - display: flex; - flex-direction: column; - gap: 2px -} - -.divider_cd2ff7 { - background-color: var(--border-subtle); - height: 1px; - margin: 20px 0; - width: 100% -} - -.subtext_cd2ff7 { - color: var(--channels-default); - font-size: 12px; - line-height: 14px; - margin-top: 10px -} - -.subtextError_cd2ff7 { - color: var(--text-feedback-critical)!important -} - -.header_cd2ff7 { - margin-top: 20px -} - -.headerCustomGifting_cd2ff7 { - margin-top: 24px -} - -.giftRecipientSection_cd2ff7 { - margin-top: 40px; - width: 100% -} - -.giftRecipient_cd2ff7 { - display: flex; - gap: var(--space-8); - width: 100% -} - -.giftRecipientButton_cd2ff7 { - align-self: flex-end; - margin-bottom: 2px -} - -.giftRecipientInputWrapper_cd2ff7 { - margin-inline-end:8px;width: 100% -} - -.giftRecipientInputError_cd2ff7 { - border-color: var(--text-feedback-critical)!important -} - -.giftRecipientRowAvatar_cd2ff7 { - margin-inline-end:8px} - -.sendToRecipientButton_cd2ff7 { - height: 42px; - margin-inline-start:8px} - -.giftBox_cd2ff7 { - position: absolute; - top: -131px; - width: 260px -} - -.confirmation_cd2ff7 { - align-items: center; - border-radius: 4px; - display: flex; - flex-direction: column; - padding: 0 16px 24px -} - -.seasonalConfirmationPadding_cd2ff7 { - padding: 0 16px -} - -.giftCodeSection_cd2ff7 { - flex-direction: column -} - -.avatar_d28e10 { - align-items: center; - display: flex; - inset-inline-start: 16px; - justify-content: center; - position: absolute; - top: 61px; - z-index: 1 -} - -.overlay_d28e10:after { - background-color: var(--opacity-black-40); - border-radius: var(--radius-round); - content: ""; - height: 100%; - inset-inline-end: 0; - top: 0; - width: 100%; - z-index: 0 -} - -.overlay_d28e10:after,.overlayIcon_d28e10 { - opacity: 0; - position: absolute; - transition: opacity .2s ease -} - -.clickable_d28e10.avatar_d28e10 { - cursor: pointer -} - -.clickable_d28e10:focus-within .overlay_d28e10:after,.clickable_d28e10:focus-within .overlayIcon_d28e10,.clickable_d28e10:hover .overlay_d28e10:after,.clickable_d28e10:hover .overlayIcon_d28e10 { - opacity: 1 -} - -.menu_d28e10 { - margin-top: 30px -} - -.clickable_faf576 { - align-items: center; - background-color: var(--opacity-black-40); - cursor: pointer; - display: flex; - gap: 4px; - height: 100%; - inset-inline-start: 0; - justify-content: center; - opacity: 0; - position: absolute; - top: 0; - transition: opacity .2s ease; - width: 100% -} - -.clickable_faf576:hover { - opacity: 1 -} - -.container__5a2c6 { - box-sizing: content-box; - position: relative -} - -.header__5a2c6 { - min-height: calc(var(--custom-user-profile-banner-height) + 45px); - z-index: 2 -} - -.custom-user-profile-theme .header__5a2c6 { - min-height: calc(var(--custom-user-profile-banner-height) + 35px) -} - -.previewContainer__50f14 { - border: 1px solid var(--border-normal); - border-radius: var(--radius-md); - display: flex; - flex-direction: column; - overflow: hidden -} - -.giftInfoContainer__50f14 { - align-items: center; - display: flex; - padding: 12px 16px -} - -.previewContainerSelected__50f14 { - background-color: var(--opacity-blurple-8); - border-color: var(--checkbox-border-default) -} - -.previewContainerError__50f14 { - border-color: var(--background-feedback-critical) -} - -.previewTextContainer__50f14 { - display: flex; - flex: 1; - flex-direction: column; - gap: 4px; - padding-inline-start:10px} - -.previewTitleContainer__50f14 { - align-items: center; - display: flex; - justify-content: space-between; - margin-bottom: 8px -} - -.previewTitle__50f14 { - margin-bottom: 0 -} - -.recipientError__50f14 { - background-color: var(--background-base-lowest); - border-top: 1px solid var(--border-normal); - padding: 8px 12px -} - -.previewLink__50f14 { - cursor: pointer -} - -.previewLink__50f14:hover { - text-decoration: underline; - text-decoration-color: var(--text-link) -} - -.stepBody_bf1b4a { - display: flex; - flex-direction: row; - padding-top: 12px -} - -.bodyColumnMiddle_bf1b4a { - margin-inline-end:30px} - -.bodyColumnLeft_bf1b4a { - margin-inline:24px 60px} - -.bodyColumnRight_bf1b4a { - margin-inline-end:10px} - -.stepBody_bf1b4a>* { - width: 50% -} - -.sendTo_bf1b4a { - margin-top: 24px; - margin-inline-end:16px} - -.customGiftMessageWrapper_bf1b4a { - margin-bottom: 24px; - margin-top: 24px; - margin-inline-end:16px} - -.customGiftMessage_bf1b4a { - background-color: var(--background-base-lowest) -} - -.giftPreview_bf1b4a { - margin-bottom: 24px; - margin-inline-end:16px} - -.wishlistGiftPreview_bf1b4a { - margin-bottom: 12px; - margin-inline-end:16px} - -.wishlistGiftPreviewHeader_bf1b4a { - margin-bottom: 0 -} - -.wishlistGiftPreviewFooter_bf1b4a { - margin-bottom: 4px -} - -.selectGiftTitle_bf1b4a { - margin-bottom: 16px; - margin-top: 24px -} - -.recommendedGiftPreview_bf1b4a { - cursor: pointer; - margin-bottom: 12px -} - -.navigateToShopButton_bf1b4a { - align-items: center; - background: var(--background-base-low); - border: 1px solid var(--border-muted); - border-radius: var(--radius-xs); - cursor: pointer; - display: flex; - gap: 10px; - justify-content: space-between; - padding: 12px 16px -} - -.navigateToShopBody_bf1b4a { - align-items: center; - display: flex; - flex-direction: row; - gap: 10px -} - -.navigateToShopTextWrapper_bf1b4a { - display: flex; - flex-direction: column -} - -.shopIcon_bf1b4a { - align-items: center; - background-color: var(--interactive-background-hover); - border-radius: var(--radius-round); - display: flex; - flex-shrink: 0; - height: 42px; - justify-content: center; - width: 42px -} - -.confirmation__9baa5 { - align-items: center; - align-self: stretch; - display: flex; - flex-direction: column; - padding-bottom: 16px; - text-align: center; - width: 394px -} - -.confirmationHeader__9baa5 { - margin-bottom: 8px -} - -.confirmationDescription__9baa5 { - margin-bottom: 64px -} - -.skuImage__9baa5 { - aspect-ratio: 1; - margin-bottom: 24px; - width: 150px -} - -.headerContainerGift_eb2ea2 { - align-items: center; - background-color: var(--background-base-low); - background-size: 100% calc(100% - 20px); - display: flex; - justify-content: space-between; - padding: 16px 24px -} - -.closeButtonGift_eb2ea2 { - margin-inline-end:-8px;margin-inline-start:auto} - -.stepBody__7b8d0 { - display: flex; - flex-direction: row; - gap: var(--size-32); - margin: var(--size-32) var(--size-16); - min-height: 400px -} - -.bodyColumnLeft__7b8d0 { - align-items: center -} - -.bodyColumnLeft__7b8d0,.bodyColumnRight__7b8d0 { - display: flex; - flex: 1; - flex-direction: column; - gap: var(--size-16) -} - -.bodyColumnRight__7b8d0 { - justify-content: center; - min-width: 0 -} - -.skuCardImage__7b8d0 { - aspect-ratio: 1; - max-width: 352px; - width: 100% -} - -.customGiftMessageWrapper__7b8d0 { - margin: 0 -} - -.customGiftMessage__7b8d0 { - background-color: var(--background-base-lower); - border-radius: var(--size-8); - padding: var(--size-12) -} - -.giftEffectsContainer__7b8d0,.giftPreviewContainer__7b8d0 { - display: flex; - gap: var(--size-8) -} - -.giftPreviewContainer__7b8d0 { - flex-direction: column -} - -.giftPreviewTitle__7b8d0 { - margin: 0!important -} - -.giftPreviewContent__7b8d0 { - align-items: center; - background-color: color-mix(in srgb,var(--background-brand) 10%,transparent); - border: 1px solid var(--background-brand); - border-radius: var(--radius-md); - display: flex; - gap: 16px; - padding: 12px 16px -} - -.giftPreviewImageContainer__7b8d0 { - flex-shrink: 0 -} - -.giftPreviewCardContainer__7b8d0 { - aspect-ratio: 1; - width: 48px -} - -.giftPreviewImage__7b8d0 { - max-height: 100%; - max-width: 100%; - -o-object-fit: contain; - object-fit: contain -} - -.giftPreviewTextContainer__7b8d0 { - display: flex; - flex: 1; - flex-direction: column; - min-width: 0 -} - -.guildNameContainer__7b8d0 { - align-items: center; - display: flex; - gap: 6px -} - -.recipientPickerSelectWrapper__7b8d0 { - background-color: var(--background-base-lower) -} - -.footer__7b8d0 { - background-color: var(--background-base-low) -} - -.viewItemButton__7b8d0 { - max-width: 100%; - width: 100% -} - -.orbIconSVG__85200 { - height: 18px -} - -.full-motion .loading__85200 { - animation-duration: 1.5s; - animation-iteration-count: infinite; - animation-name: spin__85200; - animation-timing-function: linear -} - -@keyframes spin__85200 { - 0% { - transform: rotate(0deg) - } - - to { - transform: rotate(1turn) - } -} - -.stepBody__83734 { - display: flex; - flex-direction: column; - gap: var(--space-8); - padding-top: var(--space-16) -} - -.warningContainer__83734 { - align-items: center; - background-color: var(--background-feedback-warning); - border-radius: var(--radius-sm); - display: flex; - flex-direction: row; - gap: var(--space-10); - padding: var(--space-8) -} - -.paymentSourceWrapper__83734 { - margin: 16px 0 -} - -.paymentSourceItem__83734 { - align-items: center; - cursor: default; - display: flex; - flex-direction: row; - justify-content: space-between; - padding: 14px 16px -} - -.purchaseDetailsSpinner__83734 { - height: 20px -} - -.orbAmountTag__83734 { - align-items: center; - display: flex; - flex-direction: row; - gap: 4px -} - -.displayFlex__83734 { - display: flex -} - -.stepBody_a3d32e { - display: flex; - flex-direction: row; - padding-top: 12px -} - -.stepBody_a3d32e>* { - width: 50% -} - -.orbIconAligned__18bf6 { - margin-top: 1px; - vertical-align: top -} - -.productVariantsContainer__2c397 { - align-items: center; - display: flex; - flex-direction: row -} - -.productVariantsContainer__2c397.wrap__2c397 { - flex-wrap: wrap -} - -.full-motion .productVariantsContainer__2c397 { - transition: gap .25s ease -} - -.productVariant__2c397 { - align-items: center; - border-radius: var(--radius-round); - box-shadow: inset 0 0 0 0 transparent,0 0 0 1px var(--background-base-low),0 0 0 2px var(--background-base-low); - cursor: pointer; - display: flex; - justify-content: center -} - -.productVariant__2c397.productVariantOverflow__2c397 { - background-color: var(--control-overlay-secondary-background-default); - box-shadow: inset 0 0 0 0 transparent,0 0 0 1px var(--control-overlay-secondary-border-hover),0 0 0 2px var(--background-base-low) -} - -.full-motion .productVariant__2c397 { - transition: box-shadow .25s ease,height .25s ease,margin-inline-start .25s ease,width .25s ease -} - -.full-motion .productVariantCheckmark__2c397,.full-motion .productVariantOverflowIcon__2c397 { - transition: height .25s ease,width .25s ease -} - -.expanded__2c397.productVariantsContainer__2c397 { - gap: var(--space-6); - padding: 2px -} - -.expanded__2c397 .productVariant__2c397 { - height: 20px; - width: 20px -} - -.expanded__2c397 .productVariant__2c397.selected__2c397,.expanded__2c397 .productVariant__2c397:focus-within,.expanded__2c397 .productVariant__2c397:hover { - box-shadow: inset 0 0 0 2px var(--background-base-low),0 0 0 1px var(--button-outline-primary-text),0 0 0 2px var(--background-base-low) -} - -.expanded__2c397 .productVariantCheckmark__2c397 { - height: 16px; - width: 16px -} - -.expanded__2c397 .productVariantOverflowIcon__2c397 { - height: 18px; - width: 18px -} - -.collapsed__2c397.productVariantsContainer__2c397 { - gap: 0 -} - -.collapsed__2c397 .productVariant__2c397 { - height: 16px; - width: 16px -} - -.collapsed__2c397 .productVariant__2c397:not(:first-child) { - margin-inline-start:-2px} - -.collapsed__2c397 .productVariant__2c397.selected__2c397,.collapsed__2c397 .productVariant__2c397:focus-within,.collapsed__2c397 .productVariant__2c397:hover { - box-shadow: inset 0 0 0 1px var(--button-outline-primary-text),inset 0 0 0 3px var(--background-base-low),0 0 0 2px var(--background-base-low); - height: 20px; - width: 20px -} - -.collapsed__2c397 .productVariantCheckmark__2c397 { - height: 12px; - width: 12px -} - -.collapsed__2c397 .productVariantOverflowIcon__2c397 { - height: 14px; - width: 14px -} - -.wishlistButton__979b1 { - align-items: center; - -webkit-backdrop-filter: blur(10px); - backdrop-filter: blur(10px); - background: rgba(0,0,0,.52); - border: 1px solid var(--border-muted); - border-radius: var(--radius-sm); - cursor: pointer; - display: flex; - flex-shrink: 0; - gap: 4px; - justify-content: center; - padding: 9px -} - -.disabledButton__979b1 { - cursor: not-allowed; - opacity: .5 -} - -.withHover__979b1 { - transition: background-color 50ms ease-in,color 50ms ease-in,border-color 50ms ease-in,opacity 50ms ease-in -} - -.withHover__979b1:hover { - background: var(--control-overlay-secondary-background-hover); - transition: background-color .15s ease-out,color .15s ease-out,border-color .15s ease-out,opacity .15s ease-out -} - -.withHover__979b1:active { - background: var(--control-overlay-secondary-background-active) -} - -.normalIconColor__979b1 { - fill: var(--white) -} - -.wishlistedOrHoveredIconColor__979b1 { - fill: #ec1060 -} - -.disabledIconColor__979b1 { - fill: var(--icon-strong) -} - -.toastIcon__979b1 { - flex-shrink: 0; - margin-inline-end:4px} - -.iconContainer__979b1,.toastIcon__979b1 { - align-items: center; - display: flex -} - -.iconContainer__979b1 { - justify-content: center; - position: relative -} - -.iconWrapper__979b1 { - display: flex -} - -.animationOverlay__979b1 { - align-items: center; - display: flex; - inset: 0; - justify-content: center; - pointer-events: none; - position: absolute; - transform: scale(0); - transform-origin: center -} - -.animationOverlay__979b1 path { - fill: var(--white) -} - -.full-motion .wishlistButton__979b1:active .canAnimate__979b1 .animationOverlay__979b1 { - animation: holdScale__979b1 .1s cubic-bezier(.67,0,.26,1) forwards; - animation-delay: 50ms -} - -.full-motion .clickAnimation__979b1 { - animation: clickScale__979b1 .4s cubic-bezier(.67,0,.26,1) forwards -} - -.full-motion .clickAnimation__979b1 path { - animation: clickColor__979b1 .4s cubic-bezier(.67,0,.26,1) forwards -} - -.full-motion .wishlistButton__979b1:focus-visible .canHover__979b1,.full-motion .wishlistButton__979b1:hover .canHover__979b1 { - animation: hoverPulse__979b1 1.25s cubic-bezier(.23,0,.58,1) infinite -} - -@keyframes holdScale__979b1 { - 0% { - transform: scale(0) - } - - to { - transform: scale(.25) - } -} - -@keyframes clickScale__979b1 { - 0% { - transform: scale(0) - } - - 62.5% { - transform: scale(1.35) - } - - to { - transform: scale(1) - } -} - -@keyframes clickColor__979b1 { - 0%,20% { - fill: var(--white) - } - - 70%,to { - fill: #ec1060 - } -} - -@keyframes hoverPulse__979b1 { - 0% { - transform: scale(1) - } - - 20% { - transform: scale(1.1) - } - - 60% { - transform: scale(.9) - } - - 80%,to { - transform: scale(1) - } -} - -.wishlistButton__7b466 { - margin-inline-start:auto} - -.productCardContainer_fcbddd { - background: var(--background-base-low); - border: 2px solid var(--border-muted); - border-radius: 8px; - box-sizing: border-box; - cursor: pointer; - display: flex; - flex: 1; - height: 100%; - margin: 0 auto; - min-height: 246px; - min-width: 246px; - overflow: hidden; - overflow-anchor: none; - width: 100% -} - -.full-motion .productCardContainer_fcbddd { - transition: border-color .2s -} - -.productCardContentContainer_fcbddd { - box-sizing: border-box; - display: flex; - flex: 1; - flex-direction: column; - -o-object-fit: contain; - object-fit: contain; - position: relative; - width: 100% -} - -.productPreviewContainer_fcbddd { - align-items: center; - display: flex; - inset: 0; - justify-content: center; - padding: 0 var(--space-8); - position: absolute -} - -.productPreviewContainer_fcbddd.hasBottomContent_fcbddd { - padding-bottom: 56px; - transform: translateY(0) -} - -.full-motion .productPreviewContainer_fcbddd.hasBottomContent_fcbddd { - transition: transform .25s ease -} - -.headerContainer_fcbddd { - display: flex; - gap: var(--space-8); - justify-content: space-between; - padding: var(--space-12); - z-index: 1 -} - -.footerContainer_fcbddd { - box-sizing: border-box; - margin-top: auto; - padding: var(--space-12); - position: relative; - width: 100% -} - -.footerInfoContainer_fcbddd { - align-items: flex-end; - display: flex; - flex: 1; - flex-direction: row; - gap: var(--space-12); - justify-content: space-between -} - -.footerInfoLeft_fcbddd { - display: flex; - flex-direction: column; - min-width: 0 -} - -.footerInfoShrinkOnHover_fcbddd { - align-content: end; - display: grid; - grid-template-rows: 1fr; - opacity: 1; - padding-top: var(--space-4); - width: 100% -} - -.full-motion .footerInfoShrinkOnHover_fcbddd { - transition: grid-template-rows .25s ease,padding-top .25s ease,opacity .25s ease -} - -.footerInfoShrinkOnHover_fcbddd>* { - min-height: 0 -} - -.footerButtonContainer_fcbddd { - align-content: end; - display: grid; - grid-template-rows: 0fr; - opacity: 0; - padding-top: 0; - transform: translateY(20%); - width: 100% -} - -.full-motion .footerButtonContainer_fcbddd { - transition: grid-template-rows .25s ease,padding-top .25s ease,opacity .25s ease-out,transform .25s ease-out -} - -.footerButtonGroup_fcbddd { - height: -moz-fit-content; - height: fit-content; - min-height: 0; - min-width: 0 -} - -.productCardContainer_fcbddd.hovered_fcbddd { - border-color: var(--border-subtle) -} - -.productCardContainer_fcbddd.hovered_fcbddd .footerInfoShrinkOnHover_fcbddd { - grid-template-rows: 0fr; - opacity: 0; - padding-top: 0 -} - -.productCardContainer_fcbddd.hovered_fcbddd .footerButtonContainer_fcbddd { - grid-template-rows: 1fr; - opacity: 1; - padding-top: var(--space-6); - transform: translateY(0) -} - -.productCardContainer_fcbddd.hovered_fcbddd .productPreviewContainer_fcbddd { - transform: translateY(-6px) -} - -.footerGradient_fcbddd { - bottom: 0; - height: 146px; - inset-inline-end: 0; - inset-inline-start: 0; - position: absolute -} - -.theme-dark .footerGradient_fcbddd { - background: linear-gradient(to top,var(--background-base-lowest),color-mix(in oklab,var(--background-base-lowest) 80%,transparent) 30%,color-mix(in oklab,var(--background-base-lowest) 0%,transparent) 100%) -} - -.theme-light .footerGradient_fcbddd { - background: linear-gradient(to top,hsl(var(--white-hsl)/1) 0,hsl(var(--white-hsl)/.84) 40%,hsl(var(--white-hsl)/0) 100%) -} - -.theme-light .productName_fcbddd { - text-shadow: 0 0 4px var(--background-base-low) -} - -.container_b4f99f { - background: var(--background-surface-higher); - box-sizing: border-box; - flex-direction: column; - padding: 16px; - width: 254px -} - -.assetContainer_b4f99f,.container_b4f99f { - align-items: center; - display: flex; - justify-content: center -} - -.assetContainer_b4f99f { - flex-shrink: 0; - height: 86px; - margin-bottom: 12px -} - -.orbAsset_b4f99f { - height: 86px -} - -.sectionDivider_b4f99f { - background-color: var(--border-subtle); - border: 0; - height: 1px; - margin: 0 -12px; - width: 100% -} - -.linkContainer_b4f99f { - align-items: center; - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: center; - margin-top: 16px -} - -.linkPreText_b4f99f { - color: var(--text-default) -} - -.learnMoreLink_b4f99f { - color: var(--border-focus); - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: 16px -} - -.baseCardOutline__1ab14 { - -webkit-backdrop-filter: blur(18px); - backdrop-filter: blur(18px); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-md); - box-shadow: 0 12px 64px 0 rgba(0,0,0,.32) -} - -.theme-light .progressContainer__9258b.blue__9258b { - --custom-gradient-start: var(--blurple-80); - --custom-gradient-end: var(--blurple-38); - --custom-gradient-glow: var(--blurple-38) -} - -.theme-light .progressContainer__9258b.orange__9258b { - --custom-gradient-start: var(--orange-new-80); - --custom-gradient-end: var(--orange-new-24); - --custom-gradient-glow: var(--orange-new-24) -} - -.theme-dark .progressContainer__9258b.blue__9258b { - --custom-gradient-start: var(--blurple-44); - --custom-gradient-end: var(--blurple-24); - --custom-gradient-glow: var(--blurple-24) -} - -.theme-dark .progressContainer__9258b.orange__9258b { - --custom-gradient-start: var(--orange-new-40); - --custom-gradient-end: var(--orange-new-10); - --custom-gradient-glow: var(--orange-new-10) -} - -.theme-darker .progressContainer__9258b.blue__9258b { - --custom-gradient-start: var(--blurple-54); - --custom-gradient-end: var(--blurple-24); - --custom-gradient-glow: var(--blurple-24) -} - -.theme-darker .progressContainer__9258b.orange__9258b { - --custom-gradient-start: var(--orange-new-48); - --custom-gradient-end: var(--orange-new-10); - --custom-gradient-glow: var(--orange-new-10) -} - -.theme-midnight .progressContainer__9258b.blue__9258b { - --custom-gradient-start: var(--blurple-59); - --custom-gradient-end: var(--blurple-24); - --custom-gradient-glow: var(--blurple-24) -} - -.theme-midnight .progressContainer__9258b.orange__9258b { - --custom-gradient-start: var(--orange-new-48); - --custom-gradient-end: var(--orange-new-10); - --custom-gradient-glow: var(--orange-new-10) -} - -.progressContainer__9258b { - --custom-background: var(--background-mod-normal); - background: var(--custom-background); - border-radius: 5px; - height: var(--custom-expressive-progress-height); - overflow: visible; - pointer-events: none; - position: relative; - width: 100% -} - -.progressContainer__9258b.light__9258b { - --custom-expressive-progress-height: 4px; - --custom-expressive-progress-glow-offset: -6px -} - -.progressContainer__9258b.medium__9258b { - --custom-expressive-progress-height: 6px; - --custom-expressive-progress-glow-offset: -5px -} - -.progress__9258b { - --custom-min-width: 10px; - border-radius: 5px; - height: 100%; - min-width: var(--custom-min-width); - overflow: visible; - position: relative; - transition: width .15s cubic-bezier(.16,1,.3,1); - width: 0 -} - -.glow__9258b { - background: linear-gradient(90deg,transparent,color-mix(in srgb,var(--custom-gradient-glow) 70%,transparent)); - border-radius: 20px; - filter: blur(3px); - height: 16px; - inset-inline-end: -3px; - max-width: min(100%,120px); - position: absolute; - top: var(--custom-expressive-progress-glow-offset); - transform: perspective(400px) rotateY(-30deg); - transform-origin: left center; - width: 100% -} - -.glow__9258b.empty__9258b { - max-width: calc(var(--custom-min-width)*2); - width: calc(var(--custom-min-width)*2) -} - -.bar__9258b { - background: linear-gradient(90deg,var(--custom-gradient-start) 0,var(--custom-gradient-end) 100%); - border-radius: 5px; - height: 100%; - width: 100% -} - -.bar__9258b.empty__9258b { - background: var(--custom-gradient-end) -} - -.container_e93594 { - align-items: center; - display: flex; - padding-top: var(--space-12); - width: 100% -} - -.icon_e93594 { - color: var(--text-default); - margin-inline-start:-2px;z-index: 10 -} - -@container premium-marketing-page-container (min-width: 720px) { - .icon_e93594 { - color: #fff - } -} - -.container__58471 { - align-items: center; - background: var(--background-surface-higher); - box-sizing: border-box; - justify-content: center; - padding: var(--space-16); - width: 254px -} - -.container__58471,.contentContainer__58471 { - display: flex; - flex-direction: column -} - -.contentContainer__58471 { - gap: var(--space-8) -} - -.assetContainer__58471 { - align-items: center; - display: flex; - flex-shrink: 0; - height: 53px; - justify-content: center; - padding-top: var(--space-16) -} - -.orbAsset__58471 { - height: 53px -} - -.linkContainer__58471 { - align-items: center; - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: center; - margin-top: var(--space-16) -} - -.linkPreText__58471 { - color: var(--text-default) -} - -.learnMoreLink__58471 { - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: 16px -} - -.balanceText__58471 { - color: var(--text-default) -} - -.rewardsContainer__58471 { - display: flex; - flex-direction: column; - gap: var(--space-12); - padding-bottom: var(--space-12) -} - -.balanceContainer__58471 { - display: flex; - flex-direction: column; - gap: var(--space-4) -} - -.videoBackground__58471,.videoContainer__58471 { - border-radius: inherit -} - -.videoBackground__58471 { - border: 1px solid var(--border-subtle); - height: 100%; - inset: 0; - -o-object-fit: cover; - object-fit: cover; - position: absolute; - width: 100%; - z-index: 0 -} - -.content__58471 { - position: relative; - z-index: 1 -} - -.balanceCounterMargin__98f64 { - margin-inline-start:2px} - -.balanceCounterText__98f64 { - color: var(--text-strong); - display: flex; - flex-direction: row; - justify-content: flex-end; - text-align: end -} - -.full-motion .balanceCounterText__98f64 { - transition: all .4s ease-out,opacity .4s ease-in -} - -.clickable_a3e8db { - box-sizing: border-box; - cursor: pointer; - height: 32px; - pointer-events: all -} - -.disabled_a3e8db { - cursor: none; - pointer-events: none -} - -.container_a3e8db { - align-items: center; - border: 1px solid var(--border-muted); - border-radius: var(--radius-sm); - box-sizing: border-box; - cursor: pointer; - display: inline-flex; - flex-shrink: 0; - height: 32px; - justify-content: flex-end; - opacity: 1!important; - padding: 8px 12px; - pointer-events: all; - position: relative -} - -.container_a3e8db.disabled_a3e8db { - cursor: none; - pointer-events: none -} - -.orbsLottieContainer_a3e8db { - height: 60px; - overflow: visible; - position: relative; - width: 20px -} - -.orbsLottie_a3e8db { - inset-inline-end: -20px; - position: absolute -} - -@keyframes spin_a3e8db { - 0% { - transform: rotate(0deg) - } - - to { - transform: rotate(1turn) - } -} - -.full-motion .orbIconloading_a3e8db { - animation-duration: 1s; - animation-iteration-count: infinite; - animation-name: spin_a3e8db; - animation-timing-function: linear -} - -.containerLoading_a3e8db { - gap: 0 -} - -.counterLoading_a3e8db { - visibility: hidden -} - -.container_a3e8db.default_a3e8db { - background-color: var(--background-mod-subtle) -} - -.container_a3e8db.highlighted_a3e8db,.container_a3e8db.selected_a3e8db { - background-color: var(--background-mod-normal) -} - -.inModalOverlay_a3e8db.container_a3e8db.default_a3e8db,.inModalOverlay_a3e8db.container_a3e8db.highlighted_a3e8db,.inModalOverlay_a3e8db.container_a3e8db.selected_a3e8db { - background-color: var(--modal-background) -} - -.notificationBadge_a3e8db { - inset-inline-end: -4.5px; - position: absolute -} - -.container__44ee9 { - position: relative -} - -.cardContainer__44ee9 { - margin-top: 8px; - position: absolute; - z-index: 1000 -} - -.alignLeft__44ee9 { - inset-inline-start: 0 -} - -.alignRight__44ee9 { - inset-inline-end: 0 -} - -.coachmarkContainer__44ee9 { - align-items: flex-end; - display: flex; - flex-direction: column; - gap: 20px -} - -.hidden__44ee9 { - visibility: hidden -} - -.visible__44ee9 { - visibility: visible -} - -.hidden__5ed15 { - height: 0; - visibility: hidden -} - -.verticalContainer__5ed15 { - align-items: flex-start -} - -.verticalContainer__5ed15,.verticalContainerCentered__5ed15 { - display: flex; - flex-direction: column; - row-gap: 8px -} - -.verticalContainerCentered__5ed15 { - align-items: center -} - -.balanceWidgetMenu__5ed15 { - width: -moz-max-content!important; - width: max-content!important -} - -.balanceWidgetMenuContainer__5ed15 { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - width: auto -} - -.scroller__5ed15 { - height: 100% -} - -.loader__5ed15 { - align-items: center; - display: flex; - justify-content: center -} - -.balanceWidgetPillContainer__5ed15 { - align-items: center; - display: flex; - gap: 8px -} - -.tabularNums__5ed15 { - font-variant-numeric: tabular-nums -} - -.headerWithSpacing__5ed15 { - margin-bottom: 8px; - margin-top: 4px -} - -.labelValueRow__5ed15 { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px -} - -.orderSummaryContainer__5ed15 { - box-sizing: border-box; - display: flex; - flex-direction: column; - gap: 8px; - padding: 20px; - width: 100% -} - -@use postcss-pxtorem;.wrapper_c19a55 { - position: relative; - word-wrap: break-word; - flex: 0 0 auto; - min-height: 1.375rem; - padding-inline-end:var(--custom-message-margin-horizontal);-webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.compact_c19a55.wrapper_c19a55 { - padding-bottom: var(--custom-message-padding-vertical-container-compact); - padding-top: var(--custom-message-padding-vertical-container-compact); - padding-inline-start:var(--custom-message-margin-compact-indent)} - -.cozy_c19a55.wrapper_c19a55 { - padding-bottom: var(--custom-message-spacing-vertical-container-cozy); - padding-top: var(--custom-message-spacing-vertical-container-cozy); - padding-inline-start:var(--custom-message-margin-left-content-cozy)} - -.compact_c19a55.wrapper_c19a55.contentOnly_c19a55,.cozy_c19a55.wrapper_c19a55.contentOnly_c19a55,.preview_c19a55.compact_c19a55.wrapper_c19a55,.preview_c19a55.cozy_c19a55.wrapper_c19a55 { - padding-inline-start: 16px -} - -.repliedMessage_c19a55 { - --reply-spacing: 4px; - align-items: center; - color: var(--text-default); - display: flex; - font-size: .875rem; - line-height: var(--custom-message-reply-message-preview-line-height); - position: relative; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - white-space: pre -} - -.executedCommand_c19a55,.threadMessageAccessory_c19a55 { - align-items: center; - display: flex; - font-size: .875rem; - height: 18px; - line-height: 1.125rem; - min-width: 0 -} - -.repliedMessage_c19a55.latin12CompactTimeStamp_c19a55 { - --timestamp-width: 2.25rem -} - -.repliedMessage_c19a55.latin24CompactTimeStamp_c19a55 { - --timestamp-width: 3.1rem -} - -.repliedMessage_c19a55.asianCompactTimeStamp_c19a55 { - --timestamp-width: 4.25rem -} - -.compact_c19a55 .repliedMessage_c19a55 { - --text-indent: calc((var(--custom-message-margin-compact-indent) - 1rem)*-1); - margin-inline-start:calc(var(--timestamp-width) + var(--custom-message-meta-space) + var(--text-indent));padding-inline-start: var(--custom-message-reply-indent) -} - -.compact_c19a55 .repliedMessage_c19a55 .contextCommandMessage_c19a55 { - --text-indent: 0 -} - -.cozy_c19a55 .repliedMessage_c19a55 { - margin-bottom: var(--reply-spacing) -} - -.cozy_c19a55 .messageSpine_c19a55:before,.cozy_c19a55 .repliedMessageClickableSpine_c19a55 { - --gutter: var(--custom-message-margin-horizontal) -} - -.a11y-font-scaled-down .cozy_c19a55 .messageSpine_c19a55:before,.a11y-font-scaled-down .cozy_c19a55 .repliedMessageClickableSpine_c19a55 { - --chat-avatar-size: 2.5rem; - --wrapper-padding-left: 4.5rem; - --avatar-position-left: 1rem; - --gutter: calc(var(--wrapper-padding-left) - var(--avatar-position-left) - var(--chat-avatar-size)) -} - -.compact_c19a55 .messageSpine_c19a55:before,.compact_c19a55 .repliedMessageClickableSpine_c19a55 { - --chat-avatar-size: var(--timestamp-width); - --gutter: var(--custom-message-meta-space) -} - -.messageSpine_c19a55:before,.repliedMessageClickableSpine_c19a55 { - --spine-width: 2px; - border-block-width:var(--spine-width) 0;border-color: var(--spine-default); - border-inline-width:var(--spine-width) 0;border-start-start-radius: 6px; - border-style: solid; - box-sizing: border-box; - content: ""; - display: block; - position: absolute; - top: 50%; - inset-inline: calc((var(--chat-avatar-size)/2 + var(--gutter))*-1) 100%; - bottom: 0; - margin-inline:calc(var(--spine-width)*-1/2) var(--reply-spacing);margin-bottom: calc(var(--custom-message-spacing-vertical-container-cozy) - 4px); - margin-top: calc(var(--spine-width)*-1/2) -} - -.compact_c19a55 .messageSpine_c19a55:before,.compact_c19a55 .repliedMessageClickableSpine_c19a55 { - inset-inline-end: calc(100% - var(--custom-message-reply-indent)) -} - -.repliedMessageClickableSpine_c19a55 { - cursor: pointer; - transition: border 50ms -} - -.repliedMessageClickableSpine_c19a55:hover { - border-color: var(--interactive-text-default) -} - -.repliedMessageClickableSpine_c19a55:before { - bottom: -5px; - content: ""; - cursor: pointer; - position: absolute; - top: -10px; - inset-inline: -5px -} - -.repliedMessageClickableSpine_c19a55.repliedMessageContentHovered_c19a55 { - border-color: var(--interactive-text-default) -} - -.executedCommandAvatar_c19a55,.replyAvatar_c19a55,.replyBadge_c19a55,.replyChatIconContainer_c19a55,.threadMessageAccessoryAvatar_c19a55 { - border-radius: 50%; - flex: 0 0 auto; - height: 16px; - margin-inline-end:var(--custom-message-meta-space);-webkit-user-select: none; - -moz-user-select: none; - user-select: none; - width: 16px -} - -.threadMessageAccessoryAvatar_c19a55 { - margin-inline-end:8px} - -.replyBadge_c19a55 { - align-items: center; - background: var(--background-base-lowest); - color: var(--text-default); - display: flex; - justify-content: center -} - -.replyIcon_c19a55 { - height: 7.2px; - width: 10.8px -} - -.clanTagChiplet_c19a55 { - margin-inline:4px 0} - -.compact_c19a55 .clanTagChiplet_c19a55 { - background: transparent; - display: inline; - padding: 0; - vertical-align: baseline -} - -.compact_c19a55 .clanTagChiplet_c19a55>span { - background: var(--background-mod-strong); - border-radius: 4px; - padding: 1px 4px 0 -} - -.compact_c19a55 .clanTagChiplet_c19a55:hover { - background: transparent -} - -.compact_c19a55 .clanTagChiplet_c19a55:hover>span { - background: var(--background-mod-muted) -} - -.ticketIcon_c19a55,.userJoinSystemMessageIcon_c19a55 { - height: 12px; - margin-inline-end:var(--custom-message-meta-space);width: 12px -} - -.commandIcon_c19a55 { - height: 9px; - width: 5px -} - -.executedCommand_c19a55 .applicationName_c19a55,.executedCommand_c19a55 .clanTagChiplet_c19a55,.executedCommand_c19a55 .commandName_c19a55,.executedCommand_c19a55 .username_c19a55,.repliedMessage_c19a55 .clanTagChiplet_c19a55,.repliedMessage_c19a55 .roleDot_c19a55,.repliedMessage_c19a55 .username_c19a55,.threadMessageAccessory_c19a55 .clanTagChiplet_c19a55,.threadMessageAccessory_c19a55 .username_c19a55 { - flex-shrink: 0; - font-size: inherit; - line-height: inherit; - margin-inline-end:var(--custom-message-meta-space);opacity: .64 -} - -.executedCommand_c19a55 .clanTagChiplet_c19a55,.repliedMessage_c19a55 .clanTagChiplet_c19a55,.threadMessageAccessory_c19a55 .clanTagChiplet_c19a55 { - margin-inline-start:0} - -.executedCommand_c19a55 .applicationName_c19a55,.executedCommand_c19a55 .commandName_c19a55,.executedCommand_c19a55 .username_c19a55 { - margin-inline-end:0} - -.executedCommand_c19a55 .botTag_c19a55,.repliedMessage_c19a55 .botTag_c19a55,.threadMessageAccessory_c19a55 .botTag_c19a55 { - top: 0 -} - -.executedCommand_c19a55 .commandName_c19a55 { - color: var(--text-link); - font-weight: var(--font-weight-medium) -} - -.executedCommand_c19a55 .appsIcon_c19a55 { - margin-inline-end:3px} - -.executedCommand_c19a55 .appLauncherOnboardingCommandName_c19a55 { - background-color: var(--mention-background); - border-radius: 3px; - color: var(--text-link); - font-weight: var(--font-weight-medium); - padding: 0 2px -} - -.executedCommand_c19a55 .appLauncherOnboardingCommandName_c19a55:hover { - background-color: var(--brand-500); - color: var(--white) -} - -.executedCommand_c19a55 .applicationName_c19a55 { - color: var(--text-strong); - font-weight: var(--font-weight-medium) -} - -.executedCommand_c19a55 .repliedMessage_c19a55 .username_c19a55,.executedCommand_c19a55 .targetUsername_c19a55 { - flex-shrink: 3; - text-overflow: ellipsis -} - -.executedCommand_c19a55 .repliedMessage_c19a55 { - margin-bottom: 0; - min-width: 40px; - padding-inline-start:0} - -.executedCommand_c19a55 .repliedMessage_c19a55 .username_c19a55 { - margin-inline-end:4px} - -.executedCommandSeparator_c19a55 { - flex-shrink: 0; - margin-inline-end:2px} - -.repliedTextPreview_c19a55,.threadMessageAccessoryPreview_c19a55 { - flex: 0 1 auto; - overflow: hidden -} - -.repliedTextPreview_c19a55 .repliedTextContent_c19a55 { - display: -webkit-inline-box!important; - -webkit-line-clamp: 1; - -webkit-box-orient: vertical; - width: 100%; - word-break: break-all -} - -.repliedTextPreview_c19a55 .repliedTextContent_c19a55 a { - word-break: break-all -} - -.repliedTextPreview_c19a55 { - display: flex; - max-height: 2em -} - -.repliedTextPreview_c19a55 .emoji { - height: 1.25em; - width: 1.25em -} - -.threadMessageAccessoryPreview_c19a55 { - align-items: center; - display: flex -} - -.repliedTextPreview_c19a55.clickable_c19a55 { - cursor: pointer -} - -.repliedTextPreview_c19a55.clickable_c19a55:hover,.repliedTextPreview_c19a55.repliedMessageClickableSpineHovered_c19a55 { - color: var(--text-strong) -} - -.repliedMessage_c19a55 .repliedTextPreview_c19a55 .repliedTextContent_c19a55,.threadMessageAccessory_c19a55 .threadMessageAccessoryPreview_c19a55 .threadMessageAccessoryContent_c19a55 { - color: inherit; - font-size: inherit; - line-height: inherit; - pointer-events: none; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.threadMessageAccessory_c19a55 .threadMessageAccessoryPreview_c19a55 .threadMessageAccessoryContent_c19a55 { - text-overflow: ellipsis; - white-space: nowrap -} - -.repliedMessage_c19a55 .repliedTextPreview_c19a55 .repliedTextContent_c19a55 code,.threadMessageAccessory_c19a55 .threadMessageAccessoryPreview_c19a55 .threadMessageAccessoryContent_c19a55 code { - white-space: pre -} - -.repliedTextContent_c19a55 { - inline-size: -moz-max-content; - inline-size: max-content -} - -.repliedTextPlaceholder_c19a55,.threadMessageAccessoryPlaceholder_c19a55 { - font-style: italic; - padding-inline-end:2px} - -.repliedTextContentTrailingIcon_c19a55,.threadMessageAccessoryContentTrailingIcon_c19a55 { - flex: 0 0 auto; - margin-inline-start:4px} - -.repliedTextContentLeadingIcon_c19a55,.threadMessageAccessoryContentLeadingIcon_c19a55 { - flex: 0 0 auto; - margin-inline-end:4px} - -.compact_c19a55 .contents_c19a55 { - margin-inline-start:calc(var(--custom-message-margin-compact-indent)*-1);padding-inline-start: var(--custom-message-margin-compact-indent); - text-indent: calc((var(--custom-message-margin-compact-indent) - 1rem)*-1) -} - -.cozy_c19a55 .contents_c19a55 { - margin-inline-start:0;padding-inline-start:0;position: static; - text-indent: 0 -} - -.zalgo_c19a55.compact_c19a55 .messageContent_c19a55 { - overflow: hidden -} - -.gradient_c19a55.compact_c19a55 .header_c19a55,.gradient_c19a55.cozy_c19a55 .header_c19a55,.withDisplayNameStyles_c19a55 .repliedMessage_c19a55 .username_c19a55,.withDisplayNameStyles_c19a55.withDisplayNameStyles_c19a55.withDisplayNameStyles_c19a55 .header_c19a55,.zalgo_c19a55.gradient_c19a55.compact_c19a55 .header_c19a55,.zalgo_c19a55.gradient_c19a55.cozy_c19a55 .header_c19a55 { - overflow: visible; - position: relative -} - -.compact_c19a55 .header_c19a55 { - display: inline -} - -.cozy_c19a55 .header_c19a55 { - color: var(--text-muted); - display: block; - line-height: 1.375rem; - min-height: 1.375rem; - position: relative; - white-space: break-spaces; - word-wrap: pre-wrap -} - -.zalgo_c19a55.cozy_c19a55 .header_c19a55 { - overflow: hidden -} - -.a11y-font-scaled-down .cozy_c19a55.wrapper_c19a55,.a11y-font-scaled-down .cozy_c19a55.wrapper_c19a55 .header_c19a55 { - padding-inline-start:4.5rem} - -.a11y-font-scaled-down .cozy_c19a55 .header_c19a55 { - margin-inline-start:-4.5rem} - -.buttonContainer_c19a55 { - inset-inline-end: 0; - position: absolute; - top: 0 -} - -.systemMessageAccessories_c19a55 { - margin-inline-start:2.5rem} - -.avatar_c19a55 { - border-radius: 50%; - cursor: pointer; - flex: 0 0 auto; - height: var(--chat-avatar-size); - inset-inline-start: var(--custom-message-margin-horizontal); - margin-top: calc(4px - var(--custom-message-spacing-vertical-container-cozy)); - overflow: hidden; - pointer-events: none; - position: absolute; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - width: var(--chat-avatar-size); - z-index: 1 -} - -.compact_c19a55 .avatar_c19a55 { - height: 1em; - inset-inline-start: 0; - margin-top: 0; - margin-inline:.1em .25em;position: relative; - top: .18em; - width: 1em -} - -.preview_c19a55 .avatar_c19a55 { - inset-inline-start: 8px -} - -.avatar_c19a55:before { - background-color: var(--background-mod-subtle); - content: ""; - display: block; - height: 100%; - width: 100% -} - -.avatarDecoration_c19a55 { - height: var(--custom-message-avatar-decoration-size); - inset-inline-start: calc(var(--custom-message-margin-horizontal) - (var(--custom-message-avatar-decoration-size) - var(--custom-message-avatar-size))/2); - margin-top: calc(4px - var(--custom-message-spacing-vertical-container-cozy) - (var(--custom-message-avatar-decoration-size) - var(--custom-message-avatar-size))/2); - pointer-events: none; - position: absolute; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - width: var(--custom-message-avatar-decoration-size); - z-index: 1 -} - -.a11y-font-scaled-down .avatar_c19a55:not(.compact_c19a55) { - height: 2.5rem; - inset-inline-start: 1rem; - margin-top: calc(.25rem - var(--custom-message-spacing-vertical-container-cozy)); - width: 2.5rem -} - -.a11y-font-scaled-down .avatarDecoration_c19a55:not(.compact_c19a55) { - height: calc(2.5rem*var(--decoration-to-avatar-ratio)); - inset-inline-start: .8rem; - width: calc(2.5rem*var(--decoration-to-avatar-ratio)) -} - -.avatar_c19a55.clickable_c19a55 { - pointer-events: auto -} - -.full-motion .avatar_c19a55.clickable_c19a55:hover { - filter: drop-shadow(var(--elevation-medium)) -} - -.avatar_c19a55.clickable_c19a55:active,.avatar_c19a55.clickable_c19a55:active+.avatarDecoration_c19a55 { - transform: translateY(1px) -} - -.roleIcon_c19a55 { - height: calc(1rem + 4px); - margin-inline-start:.25rem;position: relative; - top: 1px; - vertical-align: top; - width: calc(1rem + 4px) -} - -.timestamp_c19a55 { - cursor: default; - display: inline-block; - font-weight: var(--font-weight-medium); - height: 1.25rem; - pointer-events: none -} - -.timestampInline_c19a55 { - margin-inline-start:.25rem} - -.timestamp_c19a55.latin12CompactTimeStamp_c19a55 { - width: 2.25rem -} - -.timestamp_c19a55.latin24CompactTimeStamp_c19a55 { - width: 3.1rem -} - -.timestamp_c19a55.asianCompactTimeStamp_c19a55 { - width: 4.25rem -} - -.mouse-mode .timestamp_c19a55 { - pointer-events: auto -} - -.cozy_c19a55 .timestamp_c19a55 { - color: var(--chat-text-muted); - font-size: .75rem; - line-height: 1.375rem; - vertical-align: baseline -} - -.background-opacity-high .cozy_c19a55 .timestamp_c19a55,.background-opacity-low .cozy_c19a55 .timestamp_c19a55,.background-opacity-medium .cozy_c19a55 .timestamp_c19a55 { - display: none -} - -.compact_c19a55 .timestamp_c19a55,.cozy_c19a55 .timestamp_c19a55.alt_c19a55 { - color: var(--text-muted); - font-size: .6875rem; - line-height: 1.375rem; - margin-inline-end:var(--custom-message-meta-space);text-align: end; - text-indent: 0 -} - -.cozy_c19a55 .timestamp_c19a55.alt_c19a55 { - height: 1.375rem; - inset-inline-start: 0; - line-height: 1.375rem; - position: absolute; - text-align: end; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - width: 56px; - z-index: 1 -} - -.a11y-font-scaled-down .cozy_c19a55 .timestamp_c19a55.alt_c19a55 { - width: 3.5rem -} - -.a11y-font-scaled-up .cozy_c19a55 .timestamp_c19a55.alt_c19a55 { - font-size: 12Px -} - -.background-opacity-high .compact_c19a55 .timestamp_c19a55,.background-opacity-low .compact_c19a55 .timestamp_c19a55,.background-opacity-medium .compact_c19a55 .timestamp_c19a55 { - text-shadow: none -} - -.background-opacity-high .compact_c19a55 .timestamp_c19a55,.background-opacity-low .compact_c19a55 .timestamp_c19a55,.background-opacity-medium .compact_c19a55 .timestamp_c19a55 { - color: var(--primary-100) -} - -.timestampTooltip_c19a55 { - max-width: none!important; - white-space: nowrap -} - -.timestampVisibleOnHover_c19a55 { - opacity: 0 -} - -.nitroAuthorBadgeTootip_c19a55 { - max-width: 30vw!important; - white-space: nowrap -} - -.mouse-mode .wrapper_c19a55:hover .timestampVisibleOnHover_c19a55 { - opacity: 1 -} - -.keyboard-mode .wrapper_c19a55:focus .timestampVisibleOnHover_c19a55 { - opacity: 1 -} - -.username_c19a55 { - color: var(--text-strong); - display: inline; - flex-shrink: 0; - font-size: 1rem; - font-weight: var(--font-weight-medium); - line-height: 1.375rem; - overflow: hidden; - position: relative; - vertical-align: baseline -} - -.usernameColorOnName_c19a55 { -} - -.compact_c19a55 .headerText_c19a55,.cozy_c19a55 .headerText_c19a55,.roleDot_c19a55 { - margin-inline-end:.25rem} - -.compact_c19a55 .headerText_c19a55.hasRoleIcon_c19a55,.cozy_c19a55 .headerText_c19a55.hasRoleIcon_c19a55 { - margin-inline-end:0} - -.compact_c19a55 .headerText_c19a55.hasRoleIcon_c19a55.hasBadges_c19a55,.cozy_c19a55 .headerText_c19a55.hasRoleIcon_c19a55.hasBadges_c19a55 { - margin-inline-end:.25rem} - -.applicationName_c19a55.clickable_c19a55:hover,.commandName_c19a55.clickable_c19a55:hover,.username_c19a55.clickable_c19a55:hover { - cursor: pointer; - text-decoration: underline -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light .username_c19a55 { - font-weight:var(--font-weight-semibold) - } -} - -.background-opacity-high .username_c19a55,.background-opacity-low .username_c19a55,.background-opacity-medium .username_c19a55 { - text-shadow: 0 0 1px var(--primary-700),1px 1px 0 var(--primary-700) -} - -.background-opacity-low .username_c19a55,.background-opacity-medium .username_c19a55 { - font-weight: var(--font-weight-semibold) -} - -.botTag_c19a55 { - position: relative; - top: .1rem -} - -.botTagCompact_c19a55 { - margin-inline-end:.25rem} - -.botTagCozy_c19a55 { - margin-inline-start:.25rem} - -.nitroBadgeSvg_c19a55 { - height: 14px; - margin-inline-start:.2rem;position: relative; - width: 24px -} - -.nitroBadgeSvgRepliedMessage_c19a55 { - display: flex; - justify-content: center; - margin-inline-end:4px} - -.nitroAuthorBadgeContainer_c19a55 { - display: inline-block -} - -.replyLink_c19a55 { - color: var(--text-default) -} - -.replyLink_c19a55.clickable_c19a55 { - cursor: pointer -} - -.replyLink_c19a55.clickable_c19a55:hover { - text-decoration: underline -} - -.separator_c19a55 { - display: inline-block; - font-style: normal; - opacity: 0; - position: absolute; - z-index: -1 -} - -.messageEditorCompact_c19a55 { - margin-inline:var(--custom-message-margin-compact-indent) var(--custom-message-margin-horizontal)} - -.messageContent_c19a55 { - text-indent: 0 -} - -.cozy_c19a55 .messageContent_c19a55 { - position: relative -} - -.cozy_c19a55.hasThread_c19a55:after { - border-bottom: 2px solid var(--spine-default); - border-end-start-radius: 8px; - border-inline-start:2px solid var(--spine-default);bottom: 29px; - content: ""; - inset-inline-start: calc(var(--custom-message-margin-horizontal) + var(--chat-avatar-size)/2 - 1px); - position: absolute; - width: calc(var(--custom-message-margin-left-content-cozy) - var(--custom-message-margin-horizontal) - var(--chat-avatar-size)/2 - var(--space-4)) -} - -.font-size-24 .cozy_c19a55.hasThread_c19a55:after { - top: 2rem -} - -.font-size-20 .cozy_c19a55.hasThread_c19a55:after { - top: 2.375rem -} - -.font-size-18 .cozy_c19a55.hasThread_c19a55:after { - top: 2.75rem -} - -.font-size-12 .cozy_c19a55.hasThread_c19a55:after,.font-size-14 .cozy_c19a55.hasThread_c19a55:after,.font-size-15 .cozy_c19a55.hasThread_c19a55:after,.font-size-16 .cozy_c19a55.hasThread_c19a55:after { - top: 3rem -} - -.font-size-12 .cozy_c19a55.hasThread_c19a55.isSystemMessage_c19a55:after,.font-size-14 .cozy_c19a55.hasThread_c19a55.isSystemMessage_c19a55:after,.font-size-15 .cozy_c19a55.hasThread_c19a55.isSystemMessage_c19a55:after,.font-size-16 .cozy_c19a55.hasThread_c19a55.isSystemMessage_c19a55:after,.font-size-18 .cozy_c19a55.hasThread_c19a55.isSystemMessage_c19a55:after,.font-size-20 .cozy_c19a55.hasThread_c19a55.isSystemMessage_c19a55:after,.font-size-24 .cozy_c19a55.hasThread_c19a55.isSystemMessage_c19a55:after { - top: 1.75rem -} - -.font-size-24 .cozy_c19a55.hasThread_c19a55.hasReply_c19a55:after { - top: 3.25rem -} - -.font-size-20 .cozy_c19a55.hasThread_c19a55.hasReply_c19a55:after { - top: 3.75rem -} - -.font-size-18 .cozy_c19a55.hasThread_c19a55.hasReply_c19a55:after { - top: 4rem -} - -.font-size-15 .cozy_c19a55.hasThread_c19a55.hasReply_c19a55:after,.font-size-16 .cozy_c19a55.hasThread_c19a55.hasReply_c19a55:after { - top: 4.375rem -} - -.font-size-14 .cozy_c19a55.hasThread_c19a55.hasReply_c19a55:after { - top: 4.5rem -} - -.font-size-12 .cozy_c19a55.hasThread_c19a55.hasReply_c19a55:after { - top: 4.75rem -} - -.compact_c19a55 .contents_c19a55 { - position: relative -} - -.compact_c19a55.hasThread_c19a55 .contents_c19a55:before { - background-color: var(--spine-default); - bottom: 0; - content: ""; - inset-inline-start: 2.5rem; - position: absolute; - top: 1.5rem; - width: 2px -} - -.compact_c19a55.hasThread_c19a55.isSystemMessage_c19a55 .contents_c19a55:before { - inset-inline-start: 3rem; - top: 1.75rem -} - -.compact_c19a55.hasThread_c19a55 .contents_c19a55:before { - background-color: var(--background-mod-subtle) -} - -.zalgo_c19a55 .messageContent_c19a55 { - overflow: hidden -} - -.messageContent_c19a55:empty { - display: none -} - -.compact_c19a55 .messageContent_c19a55 { - display: inline; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text; - word-wrap: break-word -} - -.compact_c19a55 .messageContent_c19a55 .emoji { - position: relative -} - -.compact_c19a55 .messageContent_c19a55 .jumboable { - height: var(--custom-emoji-size-emoji); - min-height: var(--custom-emoji-size-emoji); - width: var(--custom-emoji-size-emoji) -} - -.compact_c19a55 .messageContent_c19a55.markupRtl_c19a55 { - display: block; - text-indent: 0 -} - -.cozy_c19a55 .messageContent_c19a55 { - margin-inline-start:calc(var(--custom-message-margin-left-content-cozy)*-1);padding-inline-start: var(--custom-message-margin-left-content-cozy); - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.isSending_c19a55 { - opacity: .5 -} - -.isFailed_c19a55,.isFailed_c19a55 .hljs,.isFailed_c19a55 a,.isFailed_c19a55 code.inline,.theme-dark .isFailed_c19a55,.theme-light .isFailed_c19a55 { - color: var(--text-feedback-critical) -} - -.theme-dark .isUnsupported_c19a55 { - color: var(--text-muted); - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.cozy_c19a55 .markupRtl_c19a55 { - text-align: left; - text-indent: 0; - unicode-bidi: plaintext -} - -.edited_c19a55 { - font-size: .625rem; - font-weight: var(--font-weight-normal); - line-height: 1; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -.communicationDisabled_c19a55 { - margin-inline-end:8px} - -.communicationDisabled_c19a55,.compactCommunicationDisabled_c19a55 { - color: var(--icon-feedback-critical); - vertical-align: -1px -} - -.compactCommunicationDisabled_c19a55 { - margin: 0 4px -} - -.communicationDisabledOpacity_c19a55 { - opacity: .5 -} - -.mention { - border-radius: 3px; - padding: 0 2px -} - -.background-opacity-low .edited_c19a55 { - color: var(--opacity-white-60) -} - -.background-opacity-medium .edited_c19a55 { - color: var(--opacity-white-48) -} - -.background-opacity-high .edited_c19a55 { - color: var(--opacity-white-40) -} - -.badgesContainer_c19a55 { - align-items: baseline; - display: inline-flex; - margin-inline-end:.25rem;row-gap: .25rem; - text-indent: 0 -} - -.compact_c19a55 .badgesContainer_c19a55 { - margin-inline-start:-.25rem} - -.cozy_c19a55 .badgesContainer_c19a55 { - margin-inline-start:.25rem} - -.cozy_c19a55 .pollBadgeDefault_c19a55,.pollBadgeReplied_c19a55 { - margin-block:0;margin-inline:.25rem 0} - -.enable-forced-colors .headerText_c19a55 .username_c19a55 { - color: ButtonText; - forced-color-adjust: none -} - -.wrapper__74bfa { - align-items: center; - display: flex; - margin-top: 4px -} - -.icon__74bfa { - display: block; - margin-inline-end:8px} - -.errorIcon__74bfa { - color: var(--icon-feedback-critical) -} - -@value maxModalWidth from "./MarkdownModal.module.css";.video__1689b { - border-radius: 3px -} - -@media (max-width: 480px) { - .video__1689b { - width:auto!important - } -} - -.container__1689b { - padding-bottom: 20px -} - -.container__1689b p>img,.image__1689b { - border-radius: 5px -} - -.title__1689b { - font-size: 16px; - font-weight: 700; - line-height: 20px; - text-transform: uppercase -} - -.lead__1689b { - border-radius: 5px; - margin-bottom: 20px -} - -.added__1689b,.fixed__1689b,.improved__1689b,.progress__1689b { - align-items: center; - display: flex; - margin-top: 40px -} - -.added__1689b.marginTop__1689b,.fixed__1689b.marginTop__1689b,.improved__1689b.marginTop__1689b,.progress__1689b.marginTop__1689b { - margin-top: 20px -} - -.added__1689b:after,.fixed__1689b:after,.improved__1689b:after,.progress__1689b:after { - content: ""; - flex: 1 1 auto; - height: 1px; - margin-left: 4px; - opacity: .6 -} - -.added__1689b,h3.added__1689b { - color: var(--text-feedback-positive) -} - -.added__1689b:after,h3.added__1689b:after { - background-color: var(--text-feedback-positive) -} - -.fixed__1689b,h3.fixed__1689b { - color: var(--text-feedback-critical) -} - -.fixed__1689b:after,h3.fixed__1689b:after { - background-color: var(--text-feedback-critical) -} - -.progress__1689b,h3.progress__1689b { - color: var(--text-feedback-warning) -} - -.progress__1689b:after,h3.progress__1689b:after { - background-color: var(--text-feedback-warning) -} - -.improved__1689b,h3.improved__1689b { - color: var(--text-brand) -} - -.improved__1689b:after,h3.improved__1689b:after { - background-color: var(--text-brand) -} - -.footer__1689b { - display: flex -} - -.socialLink__1689b { - margin-right: 16px -} - -.premiumBanner__1689b { - align-items: center; - background-image: linear-gradient(to left,var(--premium-tier-1-purple),var(--premium-tier-1-blue)); - border-radius: 5px; - color: var(--white); - display: flex; - font-size: 16px; - line-height: 18px; - padding: 16px -} - -.premiumBanner__1689b .premiumIcon__1689b { - flex-shrink: 0; - height: 36px; - margin-right: 16px; - width: 36px -} - -.theme-dark .date__1689b { - color: var(--primary-300) -} - -.theme-dark .video__1689b { - box-shadow: 0 2px 10px 0 hsl(var(--black-hsl)/.2) -} - -.theme-dark .socialLink__1689b { - color: var(--primary-300) -} - -.theme-light .date__1689b { - color: var(--primary-400) -} - -.theme-light .video__1689b { - box-shadow: 0 2px 10px 0 hsl(var(--black-hsl)/.1) -} - -.theme-light .socialLink__1689b { - color: var(--primary-500) -} - -@use postcss-pxtorem;.gameIcon__5a366 { - height: 1rem; - margin-inline-end:4px;vertical-align: text-top -} - -.gameMessageTooltip__5a366 { - max-width: unset -} - -.gameMessageTooltipContent__5a366 { - align-items: center; - display: flex; - padding: 16px -} - -.textContainer__5a366 { - align-items: flex-start; - display: flex; - flex-direction: column; - margin-inline-start:8px;width: 240px -} - -.timestamp__5a366 { - margin-bottom: 4px -} - -.linkIcon_ebf183 { - display: inline-block -} - -.badge__86331 { - align-items: center; - background-color: var(--background-mod-normal); - border-radius: 12px; - color: var(--interactive-text-active); - cursor: pointer; - display: inline-flex; - flex-direction: row; - margin-inline-start:.25rem;-o-object-fit: contain; - object-fit: contain; - overflow: hidden; - padding-block:2px;padding-inline:4px 8px;position: relative; - text-indent: 0; - top: 1px; - vertical-align: top -} - -.badge__86331:hover { - color: var(--interactive-text-hover) -} - -.badgeVerifiedIcon__86331 { - height: 16px; - margin-inline-end:4px;width: 16px -} - -.roleName__86331 { - max-width: 150px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.theme-light .badge__86331 { - color: var(--channels-default) -} - -.theme-light .badge__86331:hover { - color: var(--interactive-text-hover) -} - -.popout__86331 { - background-color: var(--background-surface-high); - border-radius: 4px; - box-shadow: var(--shadow-border),var(--shadow-high); - overflow: hidden -} - -.popoutChecks__86331 { - padding: 12px -} - -.popoutChecksGroup__86331:not(:last-of-type) { - margin-bottom: 12px; - padding-bottom: 12px -} - -.popoutCheckGroupName__86331 { - align-items: center; - display: flex -} - -.popoutCheckGroupPlatformIcon__86331 { - height: 20px; - margin-inline-end:8px;width: 20px -} - -.popoutCheck__86331 { - align-items: center; - display: flex; - margin-top: 8px; - margin-inline-start:28px;padding-inline-end:20px} - -.popoutCheckIcon__86331 { - color: var(--text-feedback-positive); - height: 18px; - margin-inline-end:8px;width: 18px -} - -.popoutHeaderContainer__86331 { - background-color: var(--background-base-lowest); - padding: 10px -} - -.popoutHeaderText__86331 strong { - color: var(--text-strong) -} - -.buttonContainer__86331 { - padding: 8px -} - -.getRolesButton__86331 { - margin-bottom: 8px; - width: 100% -} - -.viewConnectionsButton__86331 { - width: 100% -} - -.botTag__86331 { - margin-inline-start:8px;margin-top: -1px -} - -.messagePopoutContent_c00127 { - max-width: calc(var(--custom-message-helpers-popout-container-width)*1.25); - min-width: var(--custom-message-helpers-popout-container-width); - width: auto -} - -.popoutContainer_c00127 { - padding: var(--custom-message-helpers-popout-padding-width) -} - -.mainContent_c00127 { - align-items: start; - display: flex; - flex-direction: row -} - -.iconContainer_c00127 { - color: var(--text-feedback-positive); - margin-inline-end:var(--custom-message-helpers-popout-padding-width);width: 40px -} - -.header_c00127 { - margin-bottom: 4px -} - -.ctaButton_c00127 { - margin: 16px auto 0 -} - -.boosterBadge_c00127 { - margin-inline-end:3px} - -.newMemberBadge_f80704 { - color: var(--text-feedback-positive); - cursor: pointer; - display: inline-block; - height: calc(1rem + 4px); - margin-inline-start:.25rem;-o-object-fit: contain; - object-fit: contain; - overflow: hidden; - position: relative; - text-indent: 0; - top: 1px; - vertical-align: top; - width: calc(1rem + 4px) -} - -.badge__6ba43 { - color: var(--interactive-text-default); - cursor: pointer; - display: inline-block; - height: 1rem; - -o-object-fit: contain; - object-fit: contain; - overflow: hidden; - position: relative; - text-indent: 0; - top: .1875rem; - vertical-align: top; - width: 1rem -} - -.popoutContainer__8641f { - padding: var(--custom-message-helpers-popout-padding-width) -} - -.mainContent__8641f { - align-items: start; - display: flex; - flex-direction: row; - margin-bottom: 16px -} - -.roleIconContainer__8641f { - margin-inline-end:var(--custom-message-helpers-popout-padding-width);width: 40px -} - -.roleName__8641f { - margin-bottom: 4px -} - -.truncatingText__8641f { - min-width: 0 -} - -.ctaButtonContent__8641f { - align-items: center; - display: flex; - justify-content: center -} - -.boosterBadge__8641f { - height: 14px; - margin-inline-end:3px;width: 14px -} - -.ephemeral__5126c { - background: var(--brand-05a); - position: relative -} - -.nitroMessage__5126c.nitroMessage__5126c { - background: linear-gradient(to right,hsl(var(--premium-tier-2-purple-for-gradients-hsl)/.08),hsl(var(--premium-tier-2-pink-for-gradients-hsl)/.08)) -} - -.nitroMessage__5126c.nitroMessage__5126c a { - color: var(--text-link) -} - -.automodMessage__5126c { - background: var(--message-automod-background-default) -} - -.mentioned__5126c { - background: var(--message-mentioned-background-default); - position: relative -} - -.highlighted__5126c,.replying__5126c { - background: var(--message-highlight-background-default); - position: relative -} - -.automodMessage__5126c:before,.ephemeral__5126c:before,.highlighted__5126c:before,.mentioned__5126c:before,.nitroMessage__5126c:before,.replying__5126c:before { - bottom: 0; - content: ""; - display: block; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 2px -} - -.ephemeral__5126c:before { - background: var(--brand-500) -} - -.nitroMessage__5126c:before { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color-vertical) -} - -.automodMessage__5126c:before { - background: var(--text-feedback-critical) -} - -.mentioned__5126c:before { - background: var(--icon-feedback-warning) -} - -.highlighted__5126c:before { - background: var(--text-brand) -} - -.replying__5126c:before { - background: var(--brand-500) -} - -.messageListItem__5126c { - outline: none; - position: relative -} - -.message__5126c { - border-end-end-radius: var(--radius-xs); - border-start-end-radius: var(--radius-xs); - padding-inline-end:var(--space-xl)!important} - -.theme-dark .message__5126c.selected__5126c,.theme-dark.mouse-mode.full-motion .message__5126c: hover { - background:var(--message-background-hover) -} - -.theme-light .message__5126c.selected__5126c,.theme-light.mouse-mode.full-motion .message__5126c:hover { - background: var(--message-background-hover) -} - -.message__5126c.mentioned__5126c.selected__5126c,.mouse-mode.full-motion .message__5126c.mentioned__5126c:hover { - background: var(--message-mentioned-background-hover) -} - -.message__5126c.highlighted__5126c.selected__5126c,.mouse-mode.full-motion .message__5126c.highlighted__5126c:hover { - background: var(--message-highlight-background-hover) -} - -.message__5126c.ephemeral__5126c.selected__5126c,.mouse-mode.full-motion .message__5126c.ephemeral__5126c:hover { - background: var(--brand-10a) -} - -.message__5126c.nitroMessage__5126c.selected__5126c,.mouse-mode.full-motion .message__5126c.nitroMessage__5126c:hover { - background: linear-gradient(to right,hsl(var(--premium-tier-2-purple-for-gradients-hsl)/.2),hsl(var(--premium-tier-2-pink-for-gradients-hsl)/.2)) -} - -.message__5126c.automodMessage__5126c.selected__5126c,.mouse-mode.full-motion .message__5126c.automodMessage__5126c:hover { - background: var(--message-automod-background-hover) -} - -.message__5126c.replying__5126c.selected__5126c,.mouse-mode.full-motion .message__5126c.replying__5126c:hover { - background: var(--brand-10a) -} - -.quotedChatMessage__5126c { - position: relative -} - -.highlightContainer__5126c { - align-items: center; - display: flex; - margin-bottom: 4px -} - -.highlightIcon__5126c { - margin-inline-end:4px} - -.jump__5126c { - background-color: var(--background-base-lowest); - border-radius: 3px; - box-sizing: border-box; - color: var(--text-default); - cursor: pointer; - display: none; - font-size: 12px; - font-weight: var(--font-weight-medium); - height: 24px; - inset-inline-end: 16px; - line-height: 16px; - margin-inline-start:6px;padding: 4px; - position: absolute; - text-align: center; - top: 0; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - z-index: 1 -} - -.quotedChatMessage__5126c:hover .jump__5126c { - display: block -} - -.jump__5126c:hover { - color: var(--interactive-text-hover) -} - -.jump__5126c:active { - color: var(--interactive-text-active); - transform: translate3d(0,1px,0) -} - -.buttons__5126c { - opacity: 0; - pointer-events: none -} - -.message__5126c.selected__5126c .buttons__5126c,.mouse-mode .message__5126c:hover .buttons__5126c { - opacity: 1 -} - -.message__5126c.selected__5126c .buttonsInner__5126c,.mouse-mode .message__5126c:hover .buttonsInner__5126c { - pointer-events: auto -} - -.hideIfMessageNotFocused__5126c { - opacity: 0 -} - -.message__5126c.selected__5126c .hideIfMessageNotFocused__5126c,.mouse-mode .message__5126c:hover .hideIfMessageNotFocused__5126c { - opacity: 1 -} - -.cozyMessage__5126c.groupStart__5126c { - min-height: 2.75rem -} - -.systemMessage__5126c.groupStart__5126c { - min-height: 1.375rem -} - -.backgroundFlash__5126c { - flex: 0 0 auto -} - -.channelTextArea__5126c { - margin-top: 8px -} - -.divider__5126c { - margin-inline:1rem .875rem} - -.disableInteraction__5126c:after { - content: ""; - display: block; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.groupStart__5126c { - margin-top: var(--custom-group-spacing-start) -} - -.group-spacing-0 .groupStart__5126c { - --custom-group-spacing-start: 0.0625rem -} - -.group-spacing-0 .divider__5126c { - margin-bottom: -1px; - margin-top: 0 -} - -.group-spacing-0 .divider__5126c.hasContent__5126c { - margin-bottom: 1.5rem; - margin-top: 1.5rem -} - -.group-spacing-4 .groupStart__5126c { - --custom-group-spacing-start: 0.3125rem -} - -.group-spacing-4 .divider__5126c { - margin-bottom: 0; - margin-top: -1px -} - -.group-spacing-4 .divider__5126c.beforeGroup__5126c { - margin-bottom: -1px; - margin-top: 0; - top: .125rem -} - -.group-spacing-4 .divider__5126c.hasContent__5126c { - margin-bottom: 1.25rem; - margin-top: 1.5rem -} - -.group-spacing-8 .groupStart__5126c { - --custom-group-spacing-start: 0.5625rem -} - -.group-spacing-8 .divider__5126c { - margin-bottom: 2px; - margin-top: 2px -} - -.group-spacing-8 .divider__5126c.beforeGroup__5126c { - margin-bottom: -1px; - margin-top: 0; - top: .25rem -} - -.group-spacing-8 .divider__5126c.hasContent__5126c { - margin-bottom: 1rem; - margin-top: 1.5rem -} - -.group-spacing-16 .groupStart__5126c { - --custom-group-spacing-start: 1.0625rem -} - -.group-spacing-16 .divider__5126c { - margin-bottom: 4px; - margin-top: 4px -} - -.group-spacing-16 .divider__5126c.beforeGroup__5126c { - margin-bottom: -1px; - margin-top: 0; - top: .5rem -} - -.group-spacing-16 .divider__5126c.hasContent__5126c { - margin-bottom: .5rem; - margin-top: 1.5rem -} - -.group-spacing-24 .groupStart__5126c { - --custom-group-spacing-start: 1.5625rem -} - -.group-spacing-24 .divider__5126c { - margin-bottom: 4px; - margin-top: 4px -} - -.group-spacing-24 .divider__5126c.beforeGroup__5126c { - margin-bottom: -1px; - margin-top: 0; - top: .75rem -} - -.group-spacing-24 .divider__5126c.hasContent__5126c { - margin-bottom: 0; - margin-top: 1.5rem -} - -.backgroundFlash__5126c .groupStart__5126c { - margin-top: 0!important -} - -.interactionSending__5126c { - opacity: .5 -} - -.enable-forced-colors .automodMessage__5126c:before,.enable-forced-colors .ephemeral__5126c:before,.enable-forced-colors .highlighted__5126c:before,.enable-forced-colors .mentioned__5126c:before,.enable-forced-colors .replying__5126c:before { - forced-color-adjust: none; - width: 4px -} - -.mentioned__5126c .mention.interactive:hover { - text-decoration: underline -} - -.container_de50c1 { - position: relative -} - -.blockUserInteraction_de50c1 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - cursor: default -} - -.emojiIcon_ff168a { - height: 20px; - width: 20px -} - -.icon__0e5a2 { - display: inline-block; - height: 16px; - margin-inline-end:4px;margin-bottom: 2px; - vertical-align: bottom; - width: 16px -} - -.container__6b8b8 { - display: flex; - flex-direction: row-reverse -} - -.emojiContainer__6b8b8 { - margin-inline-end:16px} - -.name__6b8b8 { - margin-bottom: 2px -} - -.linkedName__6b8b8:hover { - cursor: pointer; - text-decoration: underline -} - -.channelNameContainer__6b8b8 { - align-items: center; - display: flex -} - -.infoContainer__6b8b8 { - flex: 1 -} - -.draftBadge__53d72 { - color: var(--primary-860); - letter-spacing: .02em -} - -.draftBadgeContainer_ea81a9 { - background-color: var(--background-base-lowest); - border-radius: 8px; - display: flex; - justify-content: center; - margin-top: -20px; - overflow: hidden; - padding: 2px; - position: relative; - z-index: 1 -} - -.tierName_ea81a9 { - font-family: var(--font-display); - margin-bottom: 16px; - text-align: start -} - -.tierImage_ea81a9 { - border-radius: 50%; - height: var(--custom-guild-role-subscription-card-basic-info-tier-image-size); - -o-object-fit: cover; - object-fit: cover; - overflow: hidden; - width: var(--custom-guild-role-subscription-card-basic-info-tier-image-size) -} - -.tierPrice_ea81a9 { - color: var(--interactive-text-active); - font-size: 24px; - font-weight: var(--font-weight-semibold); - margin-bottom: 2px; - margin-top: 16px -} - -.tierPeriod_ea81a9,.tierPrice_ea81a9 { - font-family: var(--font-display) -} - -.tierPeriod_ea81a9 { - letter-spacing: .02em; - margin-bottom: 16px; - text-transform: uppercase -} - -@media (max-width: 485px) { - .tierName_ea81a9 { - margin-bottom:4px - } - - .tierImage_ea81a9 { - height: var(--custom-guild-role-subscription-card-basic-info-tier-image-size) Mobile; - width: var(--custom-guild-role-subscription-card-basic-info-tier-image-size) Mobile - } -} - -.container_b27fdc { - align-items: center; - align-self: stretch; - display: flex; - flex-direction: row -} - -.tierTextInfoContainer_b27fdc { - align-items: flex-start; - flex: 1; - flex-direction: column; - margin-inline-start:16px} - -.tierPriceContainer_b27fdc { - align-items: center; - display: flex; - flex-direction: row -} - -.tierPrice_b27fdc { - color: var(--text-default); - font-size: 16px; - height: 20px; - text-align: start -} - -.toggleButton_b27fdc { - color: var(--interactive-text-default); - height: 24px; - justify-content: flex-end; - width: 24px -} - -.confirmationContainer_e64651 { - margin-top: 56px -} - -.confirmationHeader_e64651 { - color: var(--text-default); - margin-bottom: 8px; - text-align: center -} - -.confirmationHeader_e64651 strong { - color: var(--text-strong) -} - -.confirmationSubtitle_e64651 { - text-align: center -} - -.actions_e64651 { - display: flex; - flex-direction: column; - margin-top: 24px; - row-gap: 8px -} - -.container__24a95 { - background-color: var(--background-base-lower); - border-radius: 8px; - display: flex; - padding: 16px -} - -.container__24a95:hover { - background-color: var(--background-base-lowest); - cursor: pointer -} - -.emojiContainer__24a95 { - margin-inline-end:16px} - -.name__24a95 { - margin-bottom: 2px -} - -.channelNameContainer__24a95 { - align-items: center; - display: flex -} - -.infoContainer__24a95 { - flex: 1 -} - -.caret__24a95 { - color: var(--interactive-text-default); - flex: 0 0 auto; - margin-inline-start:16px} - -.headerContainer__4dd8e { - height: 146px; - position: relative; - width: 100% -} - -.headerEmpty__4dd8e { - height: 10px; - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.headerEmpty__4dd8e+.breadcrumb,.headerEmpty__4dd8e+.paymentModalContent { - background-color: var(--modal-background); - border-start-end-radius: 4px; - border-start-start-radius: 4px -} - -.headerEmpty__4dd8e .closeContainer__4dd8e { - top: 13px -} - -.headerImage__4dd8e,.headerImageEmpty__4dd8e { - border-start-end-radius: var(--radius-md); - border-start-start-radius: var(--radius-md) -} - -.headerImage__4dd8e { - height: 100%; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.headerImageEmpty__4dd8e { - background-color: var(--background-base-lower); - height: 100%; - width: 100% -} - -.closeContainer__4dd8e { - align-items: center; - background-color: var(--opacity-black-84); - border-radius: 50%; - cursor: pointer; - display: flex; - height: 24px; - inset-inline-end: 16px; - justify-content: center; - position: absolute; - top: 16px; - width: 24px -} - -.closeIcon__4dd8e { - color: var(--interactive-text-default); - height: 16px; - width: 16px -} - -.tierImageContainer__4dd8e { - background-color: var(--background-base-low); - border-radius: 50%; - bottom: -48px; - height: 80px; - inset-inline-start: calc(50% - 48px); - padding: 8px; - position: absolute; - width: 80px; - z-index: 1 -} - -.tierImage__4dd8e { - border-radius: 50%; - height: 100%; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.confirmationContainer__4dd8e { - margin-top: 56px -} - -.confirmationHeader__4dd8e { - margin-bottom: 8px; - text-align: center -} - -.confirmationHeader__4dd8e strong { - color: var(--text-strong) -} - -.confirmationSubtitle__4dd8e { - margin-bottom: 32px; - text-align: center -} - -.confirmationSectionLabel__4dd8e { - font-family: var(--font-display); - letter-spacing: .02em; - margin-bottom: 8px; - text-transform: uppercase -} - -.confirmationBenefits__4dd8e { - display: flex; - flex-direction: column; - gap: 8px; - margin-bottom: 20px -} - -.confirmationButton__4dd8e { - margin: 32px auto 0 -} - -.emojiBenefitsRow__4dd8e { - background-color: var(--background-base-lower); - border-radius: 8px; - display: flex; - padding: 16px -} - -.emojiImage__4dd8e { - margin-inline-end:16px} - -.emojiName__4dd8e { - margin-bottom: 2px -} - -.container__3efc4 { - border-radius: 8px; - flex: 1; - max-width: 320px; - overflow: hidden; - position: relative -} - -.cardContainerWithoutTopIndicator__3efc4 { - border-radius: 8px; - margin-top: 24px; - overflow: hidden -} - -.tierTrialIndicator__3efc4 { - background-color: var(--brand-500); - color: var(--white); - font-weight: var(--font-weight-medium); - padding-bottom: 4px; - padding-top: 4px -} - -.tierTrialIndicatorIcon__3efc4 { - margin-inline-start:4px} - -.tierTopIndicator__3efc4 { - display: flex; - font-family: var(--font-display); - font-size: 12px; - justify-content: center; - line-height: 16px; - text-transform: uppercase -} - -.tierInfoContainer__3efc4 { - align-items: center; - display: flex; - flex-direction: column; - padding: 32px 16px 16px; - text-align: center -} - -.theme-light .tierInfoContainer__3efc4 { - background-color: hsla(0,0%,100%,.5) -} - -.theme-dark .tierInfoContainer__3efc4 { - background-color: var(--interactive-background-hover) -} - -.tierDescriptionTruncate__3efc4 { - display: -webkit-box; - -webkit-line-clamp: 4; - -webkit-box-orient: vertical; - overflow: hidden -} - -.subscriptionPerks__3efc4 { - background-color: var(--background-base-lower); - padding: 16px -} - -.roleMessagePreview__3efc4 { - background-color: var(--background-base-low); - border: 1px solid var(--border-subtle); - border-radius: 4px; - padding: 12px 0 -} - -.roleMessagePreview__3efc4:not(:last-child) { - margin-bottom: 24px -} - -.sectionHeader__3efc4 { - font-family: var(--font-display); - letter-spacing: .02em; - text-transform: uppercase -} - -.benefitsSection__3efc4:not(:last-child) { - border-bottom: 1px solid var(--opacity-white-4); - margin-bottom: 16px; - padding-bottom: 16px -} - -.benefitsList__3efc4 { - display: flex; - flex-direction: column; - gap: 16px -} - -.emojiList__3efc4 { - display: grid; - gap: 9px 8px; - grid-template-columns: repeat(9,1fr) -} - -.emojiListEmoji__3efc4 { - -o-object-fit: contain; - object-fit: contain -} - -.divider__3efc4 { - background-color: hsla(0,0%,100%,.06); - height: 1px; - margin: 16px 0; - width: 100% -} - -@media (max-width: 485px) { - .container__3efc4 { - width:auto - } - - .cardContainerWithoutTopIndicator__3efc4 { - margin-top: 0 - } - - .tierInfoContainer__3efc4 { - padding-top: 16px - } - - .subscriptionPerks__3efc4 { - background-color: var(--background-mod-normal) - } -} - -.helpMessage__3efc4 { - text-align: start -} - -.giftButton__82222 { - --button-outline-brand-text: var(--brand-500) -} - -.innerGiftButton__82222 { - align-items: center; - display: flex -} - -.buttonText__82222 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.giftIcon__82222 { - height: 16px; - margin-inline-end:4px;min-height: 16px; - min-width: 16px; - width: 16px -} - -.discountOfferContainer__5521c { - align-content: center; - align-items: flex-start; - display: flex; - flex-direction: row; - gap: 16px -} - -.discountOfferContent__5521c { - display: flex; - flex-direction: column; - gap: var(--space-4) -} - -.nitroIcon__5521c { - border-radius: var(--radius-sm); - filter: saturate(var(--saturation-factor,1)) -} - -.offerDetailContainer__11ee8 { - background: var(--background-mod-subtle); - border-radius: var(--radius-md); - padding: var(--space-16) -} - -.legalContainer__11ee8 { - margin: var(--space-16) 0 -} - -.spinnerContainer__11ee8 { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.churnFooterContainer__64988 { - overflow-x: clip; - position: relative -} - -.churnFooterGlow__64988 { - background: linear-gradient(90deg,rgba(125,74,191,.5),rgba(171,77,187,.5) 50.5%,rgba(161,97,136,.5)); - filter: blur(20px); - height: 42px; - inset-inline-start: 15px; - position: absolute; - top: -4px; - width: 410px -} - -.churnFooterTopBorder__64988 { - background: linear-gradient(90deg,#8947c6,#b246c2 50%,#ac5c8d); - height: 1px; - width: 100% -} - -.churnFooterContent__64988 { - background: var(--background-mod-subtle); - min-height: 132px; - position: relative; - width: 100%; - z-index: 1 -} - -.churnFooterContentBody__64988 { - display: flex -} - -.churnFooterIcon__64988 { - align-self: flex-start; - padding-top: 20px; - padding-inline-start:16px} - -.churnFooterContentBodyInner__64988 { - align-items: flex-start; - display: flex; - flex-direction: column; - padding-inline:20px 22px;padding-bottom: 24px; - padding-top: 22px -} - -.churnFooterCopy__64988 { - color: var(--text-strong); - margin-bottom: 14px -} - -.churnFooterCTAs__64988 { - display: flex -} - -.churnFooterClaimCTA__64988 { - border-color: #764db9; - margin-inline-end:8px;min-width: 153px -} - -.churnFooterClaimCTAInner__64988 { - align-items: center; - display: flex -} - -.churnFooterClaimIcon__64988 { - padding-inline-end:4px} - -.theme-dark .churnFooterClaimCTA__64988,.theme-dark .churnFooterClaimCopy__64988 { - color: var(--white) -} - -.theme-light .churnFooterClaimCTA__64988,.theme-light .churnFooterClaimCopy__64988 { - color: #b449b6 -} - -.body__89307 { - background-color: var(--modal-background); - color: var(--text-default); - font-size: 16px; - line-height: 20px; - padding-bottom: 16px -} - -.body__89307 .title__89307 { - margin-top: 16px -} - -.clickable__89307 { - cursor: pointer -} - -.whatYouLoseItemContainer__89307 { - display: flex; - flex-direction: column; - margin-bottom: 16px; - margin-top: 16px; - row-gap: 8px -} - -.whatYouLoseItemContainer__89307.premiumRebrand__89307 { - filter: saturate(var(--saturation-factor,1)); - margin-bottom: 12px; - margin-top: 12px -} - -.whatYouLoseItemContainer__89307.premiumRebrand__89307.noMargin__89307 { - margin-bottom: 0; - margin-top: 0 -} - -.whatYouLoseItem__89307 { - background: var(--background-base-lowest); - border-radius: 8px; - box-sizing: border-box; - display: flex; - min-height: 140px; - padding: 16px -} - -.whatYouLoseItem__89307.premiumRebrand__89307 { - background: var(--background-mod-subtle); - border: 1px solid var(--border-muted); - padding: 11px -} - -.whatYouLoseItemLeftColumn__89307 { - display: flex; - flex-direction: column; - justify-content: space-between; - padding-inline-end:16px;width: 100% -} - -.whatYouLoseItemLeftColumn__89307.premiumRebrand__89307 { - padding-inline-end:var(--space-40)} - -.whatYouLoseItemLeftColumn__89307.premiumRebrand__89307 strong { - font-weight: 400 -} - -.whatYouLoseItemLearnMore__89307 { - padding: 0; - width: -moz-min-content; - width: min-content -} - -.whatYouLoseItemRightColumn__89307 { - display: flex; - max-width: 180px; - min-width: 180px -} - -.whatYouLoseItemRightColumn__89307.premiumRebrand__89307 { - border-radius: var(--radius-sm); - overflow: hidden -} - -.textSingleLineEllipsis__89307 { - display: block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.profileCard__89307 { - min-height: 96px; - overflow: hidden; - padding: 4px; - position: relative; - width: 100% -} - -.profileCardTier1__89307 { - background-color: var(--background-base-low); - border-radius: 8px -} - -.profileAvatar__89307.profileAvatar__89307 { - inset-inline-start: 12px; - position: absolute; - top: 30px -} - -.profileAvatarTier1__89307 { - inset-inline-start: 60px; - position: relative; - top: 16px -} - -.profileInner__89307 { - background: transparent; - margin-bottom: 8px; - margin-top: 30px; - margin-inline-start:0} - -.profileUserInfo__89307 { - align-items: center; - display: flex; - margin: 30px 0 8px; - padding: 0 16px -} - -.profileUserInfoTier1__89307 { - display: flex; - justify-content: center; - padding-top: 16px; - position: relative -} - -.profileNameTag__89307 { - color: var(--interactive-text-active); - font-weight: var(--font-weight-semibold); - line-height: 20px; - max-width: 118px -} - -.profileNameTagUsername__89307 { - line-height: 20px; - text-overflow: ellipsis -} - -.profilePremiumIcon__89307 { - margin-inline-start:6px} - -.emojiStickersPersonalizedContainer__89307 { - align-items: center; - display: flex; - justify-content: space-between; - width: 100% -} - -.emojiStickersPersonalizedContainer__89307 :not(:last-child) { - margin-inline-end:2px} - -.personalizedEmoji__89307 { - height: 58px; - width: 58px -} - -.boostCardContainer__89307 { - overflow: hidden; - width: 100% -} - -.boostCard__89307 { - background-color: var(--background-base-low); - border-radius: 8px; - box-sizing: border-box; - flex-grow: 1; - height: 100%; - overflow: hidden; - padding: 10px -} - -.boostCardIcon__89307 { - flex-shrink: 0; - margin-bottom: 4px -} - -.boostCardInfo__89307 { - overflow: hidden -} - -.boostCardTitle__89307 { - color: var(--text-strong) -} - -.boostCardSubtitle__89307 { - align-items: center; - display: flex; - padding-top: 2px -} - -.boostCardGem__89307 { - color: var(--guild-boosting-pink); - height: 16px; - margin-inline-end:4px;width: auto -} - -.boostCardCount__89307 { - color: var(--text-strong); - opacity: .5 -} - -.nonPersonalizedGraphic__89307 { - max-width: 168px; - width: 100% -} - -.spinner__89307 { - padding-bottom: 44px; - padding-top: 60px -} - -.badgeGradientWrapper__89307 { - align-items: center; - background-color: var(--background-surface-higher); - border-radius: 8px; - display: flex; - flex-shrink: 0; - height: 100%; - justify-content: center; - min-height: 108px; - position: relative; - width: 100% -} - -.badgeImage__89307 { - display: block; - height: 66px; - width: auto -} - -.previewContainer__70c78 { - padding-block:0 var(--space-6);padding-inline: 0 -} - -.title_feb59c { - margin-bottom: 8px -} - -h1.title_feb59c { - font-size: 20px; - font-weight: var(--font-weight-medium); - line-height: 1.2 -} - -h2.title_feb59c { - font-size: 16px; - font-weight: var(--font-weight-semibold); - line-height: 1.25 -} - -h3.title_feb59c { - font-size: 14px; - line-height: 1.14 -} - -h3.title_feb59c,h4.title_feb59c,h5.title_feb59c,h6.title_feb59c { - font-weight: var(--font-weight-semibold); - text-transform: uppercase -} - -h4.title_feb59c,h5.title_feb59c,h6.title_feb59c { - font-size: 12px; - line-height: 1.33 -} - -.theme-dark h3.title_feb59c { - color: var(--primary-200) -} - -.theme-dark h4.title_feb59c,.theme-dark h5.title_feb59c,.theme-dark h6.title_feb59c { - color: var(--primary-300) -} - -.theme-light h3.title_feb59c,.theme-light h4.title_feb59c,.theme-light h5.title_feb59c,.theme-light h6.title_feb59c { - color: var(--primary-400) -} - -h1.title_feb59c,h2.title_feb59c { - color: var(--text-default) -} - -.hljs { - border-radius: 4px; - display: block; - overflow-x: auto; - padding: .5em; - -webkit-text-size-adjust: none; - -moz-text-size-adjust: none; - text-size-adjust: none; - background: var(--background-base-lower); - color: var(--text-default) -} - -.theme-dark .hljs-doctag,.theme-dark .hljs-keyword,.theme-dark .hljs-meta .hljs-keyword,.theme-dark .hljs-template-tag,.theme-dark .hljs-template-variable,.theme-dark .hljs-type,.theme-dark .hljs-variable.language_ { - color: #ff7b72 -} - -.theme-dark .hljs-title,.theme-dark .hljs-title.class_,.theme-dark .hljs-title.class_.inherited__,.theme-dark .hljs-title.function_ { - color: #d2a8ff -} - -.theme-dark .hljs-attr,.theme-dark .hljs-attribute,.theme-dark .hljs-literal,.theme-dark .hljs-meta,.theme-dark .hljs-number,.theme-dark .hljs-operator,.theme-dark .hljs-selector-attr,.theme-dark .hljs-selector-class,.theme-dark .hljs-selector-id,.theme-dark .hljs-variable { - color: #79c0ff -} - -.theme-dark .hljs-meta .hljs-string,.theme-dark .hljs-regexp,.theme-dark .hljs-string { - color: #a5d6ff -} - -.theme-dark .hljs-built_in,.theme-dark .hljs-symbol { - color: #ffa657 -} - -.theme-dark .hljs-code,.theme-dark .hljs-comment,.theme-dark .hljs-formula { - color: #8b949e -} - -.theme-dark .hljs-name,.theme-dark .hljs-quote,.theme-dark .hljs-selector-pseudo,.theme-dark .hljs-selector-tag { - color: #7ee787 -} - -.theme-dark .hljs-subst { - color: #c9d1d9 -} - -.theme-dark .hljs-section { - color: #1f6feb; - font-weight: 700 -} - -.theme-dark .hljs-bullet { - color: #f2cc60 -} - -.theme-dark .hljs-emphasis { - color: #c9d1d9; - font-style: italic -} - -.theme-dark .hljs-strong { - color: #c9d1d9; - font-weight: 700 -} - -.theme-dark .hljs-addition { - background-color: #033a16; - color: #aff5b4 -} - -.theme-dark .hljs-deletion { - background-color: #67060c; - color: #ffdcd7 -} - -.theme-light .hljs-doctag,.theme-light .hljs-keyword,.theme-light .hljs-meta .hljs-keyword,.theme-light .hljs-template-tag,.theme-light .hljs-template-variable,.theme-light .hljs-type,.theme-light .hljs-variable.language_ { - color: #d73a49 -} - -.theme-light .hljs-title,.theme-light .hljs-title.class_,.theme-light .hljs-title.class_.inherited__,.theme-light .hljs-title.function_ { - color: #6f42c1 -} - -.theme-light .hljs-attr,.theme-light .hljs-attribute,.theme-light .hljs-literal,.theme-light .hljs-meta,.theme-light .hljs-number,.theme-light .hljs-operator,.theme-light .hljs-selector-attr,.theme-light .hljs-selector-class,.theme-light .hljs-selector-id,.theme-light .hljs-variable { - color: #005cc5 -} - -.theme-light .hljs-meta .hljs-string,.theme-light .hljs-regexp,.theme-light .hljs-string { - color: #032f62 -} - -.theme-light .hljs-built_in,.theme-light .hljs-symbol { - color: #e36209 -} - -.theme-light .hljs-code,.theme-light .hljs-comment,.theme-light .hljs-formula { - color: #6a737d -} - -.theme-light .hljs-name,.theme-light .hljs-quote,.theme-light .hljs-selector-pseudo,.theme-light .hljs-selector-tag { - color: #22863a -} - -.theme-light .hljs-subst { - color: #24292e -} - -.theme-light .hljs-section { - color: #005cc5; - font-weight: 700 -} - -.theme-light .hljs-bullet { - color: #735c0f -} - -.theme-light .hljs-emphasis { - color: #24292e; - font-style: italic -} - -.theme-light .hljs-strong { - color: #24292e; - font-weight: 700 -} - -.theme-light .hljs-addition { - background-color: #f0fff4; - color: #22863a -} - -.theme-light .hljs-deletion { - background-color: #ffeef0; - color: #b31d28 -} - -.hljs_d285a6 { - border-radius: 4px; - display: block; - overflow-x: auto; - padding: .5em; - -webkit-text-size-adjust: none; - -moz-text-size-adjust: none; - text-size-adjust: none; - color: var(--text-default) -} - -.hljs-doctag_d285a6,.hljs-keyword_d285a6,.hljs-meta_d285a6 .hljs-keyword_d285a6,.hljs-template-tag_d285a6,.hljs-template-variable_d285a6,.hljs-type_d285a6,.hljs-variable_d285a6.language__d285a6 { - color: var(--text-code-keyword) -} - -.hljs-title_d285a6,.hljs-title_d285a6.class__d285a6,.hljs-title_d285a6.class__d285a6.inherited___d285a6,.hljs-title_d285a6.function__d285a6 { - color: var(--text-code-title) -} - -.hljs-attr_d285a6,.hljs-attribute_d285a6,.hljs-literal_d285a6,.hljs-meta_d285a6,.hljs-number_d285a6,.hljs-operator_d285a6,.hljs-selector-attr_d285a6,.hljs-selector-class_d285a6,.hljs-selector-id_d285a6,.hljs-variable_d285a6 { - color: var(--text-code-variable) -} - -.hljs-meta_d285a6 .hljs-string_d285a6,.hljs-regexp_d285a6,.hljs-string_d285a6 { - color: var(--text-code-string) -} - -.hljs-built_in_d285a6,.hljs-symbol_d285a6 { - color: var(--text-code-builtin) -} - -.hljs-code_d285a6,.hljs-comment_d285a6,.hljs-formula_d285a6 { - color: var(--text-code-comment) -} - -.hljs-name_d285a6,.hljs-quote_d285a6,.hljs-selector-pseudo_d285a6,.hljs-selector-tag_d285a6 { - color: var(--text-code-tag) -} - -.hljs-subst_d285a6 { - color: var(--text-code) -} - -.hljs-section_d285a6 { - color: var(--text-code-section); - font-weight: 700 -} - -.hljs-bullet_d285a6 { - color: var(--text-code-bullet) -} - -.hljs-emphasis_d285a6 { - color: var(--text-code); - font-style: italic -} - -.hljs-strong_d285a6 { - color: var(--text-code); - font-weight: 700 -} - -.hljs-addition_d285a6 { - background-color: var(--background-code-addition); - color: var(--text-code-addition) -} - -.hljs-deletion_d285a6 { - background-color: var(--background-code-deletion); - color: var(--text-code-deletion) -} - -.markdown_d285a6 { - font-size: 16px; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.markdown_d285a6 h1,.markdown_d285a6 h2,.markdown_d285a6 h3,.markdown_d285a6 h4,.markdown_d285a6 h5,.markdown_d285a6 h6,.markdown_d285a6 hr { - margin: 20px 0 8px -} - -.markdown_d285a6 .blockquote_d285a6,.markdown_d285a6 ol,.markdown_d285a6 p,.markdown_d285a6 pre,.markdown_d285a6 table,.markdown_d285a6 ul { - margin-top: 8px -} - -.markdown_d285a6 :first-child { - margin-top: 0 -} - -.markdown_d285a6 hr,.markdown_d285a6 p { - margin-bottom: 0 -} - -.markdown_d285a6 strong { - font-weight: var(--font-weight-semibold) -} - -.markdown_d285a6 em { - font-style: italic -} - -.markdown_d285a6 ol li { - list-style-type: decimal; - margin-inline-start:20px} - -.markdown_d285a6 ul { - margin: 0 0 12px -} - -.markdown_d285a6 ul li { - list-style-type: disc; - margin-block:8px 0;margin-inline:20px 0} - -.markdown_d285a6 ul li:first-child { - margin-top: 0 -} - -.markdown_d285a6 table { - border-collapse: collapse -} - -.markdown_d285a6 th { - font-weight: var(--font-weight-semibold) -} - -.markdown_d285a6 td,.markdown_d285a6 th { - border-inline-end:1px solid transparent;border-inline-start:1px solid transparent;padding: 8px; - text-align: start -} - -.markdown_d285a6 tr { - border: 1px solid transparent -} - -.markdown_d285a6 img { - border-radius: 3px -} - -.markdown_d285a6 .blockquote_d285a6 { - border-inline-start:4px solid transparent;display: block; - padding-inline-start:8px} - -.markdown_d285a6 .blockquote_d285a6 p { - margin: 0 -} - -.markdown_d285a6 .codeInline_d285a6 { - padding: .2em -} - -.markdown_d285a6 .paragraph_d285a6 { - margin-bottom: 8px -} - -.theme-dark .markdown_d285a6 { - color: var(--primary-300) -} - -.theme-dark .markdown_d285a6 th { - background-color: var(--primary-700); - border-color: var(--primary-500); - color: var(--white) -} - -.theme-dark .markdown_d285a6 td { - border-color: var(--primary-500) -} - -.theme-dark .markdown_d285a6 tr { - border-color: var(--primary-500); - color: var(--primary-300) -} - -.theme-dark .markdown_d285a6 tr:nth-child(2n) { - background-color: var(--primary-630) -} - -.theme-dark .markdown_d285a6 .blockquote_d285a6 { - border-inline-start-color:var(--primary-500)} - -.theme-dark .markdown_d285a6 code { - background-color: var(--primary-630) -} - -.theme-dark .markdown_d285a6 .codeInline_d285a6 { - color: var(--primary-200) -} - -.labelValueRow__2e5ea { - align-items: center; - display: flex; - flex-direction: row; - gap: 8px -} - -.paymentElementStoryContainer__1f918 { - display: flex; - flex-direction: row; - gap: 12px -} - -.paymentElementStepModalBody__1f918 { - max-width: 456px; - width: 456px -} - -.fullLengthPaymentElementStepModalBody__1f918 { - max-width: 456px; - min-height: 900px; - width: 456px -} - -.paymentElementContainer__1f918 { - margin: 8px -} - -.fullLengthPaymentElementContainer__1f918 { - min-height: 900px -} - -.wrapper_cc5dd2 { - position: relative -} - -.svg_cc5dd2,.wrapper_cc5dd2 { - box-sizing: border-box -} - -.svg_cc5dd2 { - contain: paint; - position: absolute -} - -.shiftSVG_cc5dd2 { - inset-inline-start: -4px; - top: -4px -} - -.noContain_cc5dd2 { - contain: style -} - -.lowerBadge_cc5dd2 { - bottom: 0; - inset-inline-end: 0; - pointer-events: none; - position: absolute; - z-index: 2 -} - -.isHighlighted_cc5dd2 { - stroke: #000; - stroke-width: 8px -} - -.upperBadge_cc5dd2 { - inset-inline-end: 0; - pointer-events: none; - position: absolute; - top: 0 -} - -.focusStroke_cc5dd2 { - stroke: transparent; - stroke-width: 0 -} - -.focusFill_cc5dd2,.focusStroke_cc5dd2,.highlight_cc5dd2 { - fill: transparent -} - -.keyboard-mode .focusStroke_cc5dd2 { - fill: transparent; - stroke: var(--blue-345); - stroke-width: 8px -} - -.keyboard-mode .focusFill_cc5dd2 { - fill: var(--background-base-lowest) -} - -.keyboard-mode .simpleFocused_cc5dd2 { - box-shadow: 0 0 0 4px var(--blue-345) -} - -.menu_c1e9c4 { - --custom-menu-viewport-padding: 16px; - --custom-menu-separator-margin: 8px; - --custom-menu-flexible-min-width: 188px; - background: var(--background-surface-high); - background-color: var(--background-surface-higher); - border: 1px solid var(--border-subtle); - border-radius: 8px; - box-shadow: var(--shadow-high); - box-sizing: border-box; - cursor: default; - display: flex; - height: auto; - max-height: var(--reference-position-layer-max-height,calc(100vh - var(--custom-menu-viewport-padding)*2)); - z-index: 1 -} - -.scroller_c1e9c4 { - padding: 8px -} - -.fixed_c1e9c4 { - width: 220px -} - -.flexible_c1e9c4 { - max-width: 320px; - min-width: var(--custom-menu-flexible-min-width) -} - -.item_c1e9c4 { - border-radius: 2px; - box-sizing: border-box; - cursor: pointer; - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 18px; - margin: 0 -} - -.item_c1e9c4.hideInteraction_c1e9c4 { - cursor: default -} - -.item_c1e9c4:hover { - background-color: var(--background-mod-subtle) -} - -.customItem_c1e9c4 { - border-color: var(--interactive-text-default); - color: var(--interactive-text-default); - font-size: 14px; - font-weight: var(--font-weight-medium); - line-height: 18px -} - -.labelContainer_c1e9c4 { - align-items: center; - box-sizing: border-box; - display: flex; - justify-content: space-between; - min-height: 32px; - padding: 8px -} - -.label_c1e9c4 { - flex: 1 1 auto; - text-overflow: ellipsis; - white-space: nowrap -} - -.label_c1e9c4,.marqueeContainer_c1e9c4 { - overflow: hidden -} - -.labelContainer_c1e9c4.focused_c1e9c4 .marqueeContainer_c1e9c4[data-overflow],.labelContainer_c1e9c4:hover .marqueeContainer_c1e9c4[data-overflow] { - -webkit-mask-image: linear-gradient(90deg,transparent,#000 8px,#000 calc(100% - 8px),transparent); - mask-image: linear-gradient(90deg,transparent,#000 8px,#000 calc(100% - 8px),transparent) -} - -.marqueeText_c1e9c4 { - display: inline-block; - inset-inline-start: 0; - max-width: 100%; - overflow: hidden; - position: relative; - text-overflow: ellipsis; - vertical-align: top; - white-space: nowrap -} - -.labelContainer_c1e9c4.focused_c1e9c4 .marqueeContainer_c1e9c4[data-overflow] .marqueeText_c1e9c4,.labelContainer_c1e9c4:hover .marqueeContainer_c1e9c4[data-overflow] .marqueeText_c1e9c4 { - inset-inline-start: calc(var(--custom-marquee-overflow, 0px)*-1); - max-width: none; - overflow: visible; - transition: inset-inline-start 2s linear -} - -.switchItem_c1e9c4 { - align-items: center; - display: flex; - gap: var(--space-16); - justify-content: space-between; - min-height: var(--space-32); - padding: var(--space-8) -} - -.switchItem_c1e9c4 .labelContainer_c1e9c4 { - min-height: 0; - padding: 0 -} - -.subtext_c1e9c4,.switchItem_c1e9c4 .label_c1e9c4 { - white-space: normal -} - -.subtext_c1e9c4 { - margin-top: 2px -} - -.subtextLineClamp_c1e9c4 { - white-space: pre -} - -.iconContainer_c1e9c4 { - align-items: center; - display: flex; - flex: 0 0 auto; - height: 20px; - margin-inline-start:8px;width: 20px -} - -.iconContainerLeft_c1e9c4 { - margin-inline:0 8px} - -.iconContainerLarge_c1e9c4 { - height: 32px; - width: 32px -} - -.badgeContainer_c1e9c4 { - align-items: center; - display: flex; - flex: 0 0 auto; - height: 18px; - margin-inline-start:8px} - -.switchContainer_c1e9c4 { - flex-shrink: 0 -} - -.icon_c1e9c4 { - height: 100%; - width: 100% -} - -.avatarAccessory_c1e9c4,.imageAccessory_c1e9c4 { - flex: 0 0 auto; - height: 18px; - margin-inline-end:8px;-o-object-fit: contain; - object-fit: contain; - width: 18px -} - -.roleDotAccessory_c1e9c4 { - align-items: center; - display: flex; - flex: 0 0 auto; - height: 20px; - justify-content: center; - margin-inline-end:8px;width: 20px -} - -.statusAccessory_c1e9c4 { -} - -.guildTagAccessory_c1e9c4 { - align-items: center; - display: flex; - flex: 0 0 auto; - margin-inline-end:8px} - -.avatarAccessory_c1e9c4 { - border-radius: var(--radius-round); - -o-object-fit: cover; - object-fit: cover -} - -.shortcutContainer_c1e9c4 { - flex: 0 0 auto; - margin-inline-start:8px;max-height: 18px -} - -.caret_c1e9c4 { - color: var(--text-muted); - height: 20px; - width: 20px -} - -.disabled_c1e9c4 { - cursor: pointer; - opacity: .5; - pointer-events: none -} - -.separator_c1e9c4 { - border-bottom-color: var(--border-subtle); - border-bottom: 1px solid var(--border-subtle); - box-sizing: border-box; - margin: var(--custom-menu-separator-margin,8px) -} - -.submenuPaddingContainer_c1e9c4 { - padding: 0 8px -} - -.submenu_c1e9c4 { - max-height: var(--custom-floating-layer-max-height,var(--reference-position-layer-max-height)); - max-width: 320px; - min-width: 188px -} - -.colorDefault_c1e9c4 { - border-color: var(--interactive-text-default); - color: var(--interactive-text-default) -} - -.colorDefault_c1e9c4 .subtext_c1e9c4 { - color: var(--text-muted) -} - -.colorDefault_c1e9c4 .checkbox_c1e9c4,.colorDefault_c1e9c4 .radioSelection_c1e9c4 { - color: var(--control-brand-foreground-new) -} - -.colorDefault_c1e9c4 .check_c1e9c4 { - color: var(--white) -} - -.colorDefault_c1e9c4.focused_c1e9c4 { - background-color: var(--interactive-background-hover); - border-radius: 4px; - color: var(--text-strong) -} - -.keyboard-mode .colorDefault_c1e9c4.focused_c1e9c4 { - outline: 2px solid var(--border-focus); - outline-offset: -2px -} - -.colorDefault_c1e9c4.focused_c1e9c4 .caret_c1e9c4,.colorDefault_c1e9c4.focused_c1e9c4 .checkbox_c1e9c4,.colorDefault_c1e9c4.focused_c1e9c4 .radioSelection_c1e9c4,.colorDefault_c1e9c4.focused_c1e9c4 .subtext_c1e9c4 { - color: var(--white) -} - -.colorDefault_c1e9c4.focused_c1e9c4 .check_c1e9c4 { - color: var(--text-brand); - fill: var(--text-brand) -} - -.colorDefault_c1e9c4.focused_c1e9c4:not(.checkboxContainer_c1e9c4) path { - fill: var(--interactive-text-active) -} - -.colorDefault_c1e9c4.focused_c1e9c4 .subtext_c1e9c4 { - color: var(--text-subtle) -} - -.colorDefault_c1e9c4:active:not(.hideInteraction_c1e9c4) { - background-color: var(--background-mod-subtle); - color: var(--white) -} - -.colorDefault_c1e9c4 .label_c1e9c4 { - color: var(--text-strong) -} - -.colorBrand_c1e9c4,.colorDanger_c1e9c4 { -} - -.colorDanger_c1e9c4 { - color: var(--text-feedback-critical) -} - -.colorDanger_c1e9c4 .checkbox_c1e9c4,.colorDanger_c1e9c4 .radioSelection_c1e9c4 { - color: var(--icon-feedback-critical) -} - -.colorDanger_c1e9c4.focused_c1e9c4 { - background-color: var(--background-feedback-critical) -} - -.colorDanger_c1e9c4.focused_c1e9c4,.colorDanger_c1e9c4.focused_c1e9c4 .check_c1e9c4,.colorDanger_c1e9c4.focused_c1e9c4 .label_c1e9c4 { - color: var(--text-feedback-critical) -} - -.colorDanger_c1e9c4.focused_c1e9c4:not(.checkboxContainer_c1e9c4) path { - fill: var(--text-feedback-critical) -} - -.colorDanger_c1e9c4:active:not(.hideInteraction_c1e9c4) { - background-color: var(--background-feedback-critical); - color: var(--text-feedback-critical) -} - -.colorDanger_c1e9c4 .label_c1e9c4 { - color: var(--text-feedback-critical) -} - -.colorPremium_c1e9c4 { -} - -.colorPremium_c1e9c4 .icon_c1e9c4 { - color: var(--guild-boosting-pink) -} - -.colorPremium_c1e9c4.focused_c1e9c4 .icon_c1e9c4,.colorPremium_c1e9c4:active:not(.hideInteraction_c1e9c4) .icon_c1e9c4 { - color: var(--white) -} - -.colorPremiumGradient_c1e9c4 { -} - -.colorPremiumGradient_c1e9c4.focused_c1e9c4,.colorPremiumGradient_c1e9c4:active:not(.hideInteraction_c1e9c4) { - background: linear-gradient(270deg,var(--premium-tier-2-pink-for-gradients) 0,var(--premium-tier-2-pink-for-gradients-2) 33.63%,var(--premium-tier-2-purple-for-gradients) 100%) -} - -.colorPremiumGradient_c1e9c4 .check_c1e9c4,.colorPremiumGradient_c1e9c4 .radioSelection_c1e9c4 { - color: var(--white) -} - -.colorPremiumGradient_c1e9c4 .checkbox_c1e9c4,.colorPremiumGradient_c1e9c4.focused_c1e9c4 .check_c1e9c4 { - color: var(--premium-tier-2-pink-for-gradients) -} - -.colorSuccess_c1e9c4 { -} - -.colorSuccess_c1e9c4,.colorSuccess_c1e9c4 .checkbox_c1e9c4,.colorSuccess_c1e9c4 .radioSelection_c1e9c4 { - color: var(--green-230) -} - -.colorSuccess_c1e9c4.focused_c1e9c4 { - background-color: var(--green-230); - color: var(--white) -} - -.colorSuccess_c1e9c4.focused_c1e9c4 .check_c1e9c4 { - color: var(--green-230) -} - -.colorSuccess_c1e9c4:active:not(.hideInteraction_c1e9c4) { - background-color: var(--green-360); - color: var(--white) -} - -.groupLabel_c1e9c4 { - color: var(--text-muted); - font-family: var(--font-display); - font-size: 14px; - font-weight: 500; - line-height: 20px; - text-transform: capitalize -} - -.loader_c1e9c4 { - align-items: center; - justify-content: center; - padding: 16px -} - -.custom-theme-background .menu_c1e9c4 { - border: 1px solid var(--border-strong) -} - -[data-popout-animating=true] .item_c1e9c4[aria-haspopup=true] { - pointer-events: none -} - -svg.radioIcon_c1e9c4 { - height: 20px; - width: 20px -} - -:where(.density-compact) .labelContainer_c1e9c4 { - padding: 4px 8px -} - -.refresh-fast-follow-distinct-borders .menu_c1e9c4 { - border-color: var(--app-frame-border) -} - -.refresh-fast-follow-distinct-borders .separator_c1e9c4 { - border-bottom-color: var(--app-frame-border) -} - -.enable-forced-colors .menu_c1e9c4 { - background-color: ButtonFace; - border: 2px solid CanvasText -} - -.enable-forced-colors .colorDefault_c1e9c4 { - background-color: ButtonFace; - border: 1px solid ButtonFace; - color: ButtonText; - forced-color-adjust: none -} - -.enable-forced-colors .colorDefault_c1e9c4 .caret_c1e9c4,.enable-forced-colors .colorDefault_c1e9c4 .label_c1e9c4,.enable-forced-colors .colorDefault_c1e9c4 .subtext_c1e9c4 { - color: inherit -} - -.enable-forced-colors .colorDefault_c1e9c4 .checkbox_c1e9c4,.enable-forced-colors .colorDefault_c1e9c4 .radioSelection_c1e9c4 { - color: Highlight -} - -.enable-forced-colors .colorDefault_c1e9c4 .check_c1e9c4 { - color: HighlightText -} - -.enable-forced-colors .colorDefault_c1e9c4.focused_c1e9c4,.enable-forced-colors .colorDefault_c1e9c4:hover { - border-color: ButtonText -} - -.enable-forced-colors .colorDefault_c1e9c4[aria-checked=true] { - background-color: Highlight; - color: HighlightText -} - -.enable-forced-colors .colorDefault_c1e9c4[aria-checked=true].focused_c1e9c4,.enable-forced-colors .colorDefault_c1e9c4[aria-checked=true]:hover { - border-color: HighlightText -} - -.enable-forced-colors .colorDefault_c1e9c4[aria-checked=true] .radioSelection_c1e9c4 { - color: HighlightText -} - -.enable-forced-colors .colorDefault_c1e9c4:active:not(.hideInteraction_c1e9c4) { - background-color: Highlight; - color: HighlightText -} - -.enable-forced-colors .groupLabel_c1e9c4,.enable-forced-colors .groupLabel_c1e9c4:hover,.enable-forced-colors .hideInteraction_c1e9c4,.enable-forced-colors .hideInteraction_c1e9c4:hover { - background-color: ButtonFace; - border-color: ButtonFace; - color: ButtonText -} - -.enable-forced-colors .disabled_c1e9c4,.enable-forced-colors .disabled_c1e9c4[aria-checked=true] { - background-color: Canvas; - color: GrayText; - cursor: not-allowed; - opacity: 1 -} - -.enable-forced-colors .disabled_c1e9c4 .checkbox_c1e9c4,.enable-forced-colors .disabled_c1e9c4 .radioSelection_c1e9c4,.enable-forced-colors .disabled_c1e9c4[aria-checked=true] .checkbox_c1e9c4,.enable-forced-colors .disabled_c1e9c4[aria-checked=true] .radioSelection_c1e9c4 { - color: GrayText -} - -.svg__4f569 { - display: inline-block; - flex-shrink: 0; - forced-color-adjust: none; - height: 1.25em; - vertical-align: text-bottom; - width: 1.25em -} - -.background__4f569 { - fill: var(--background-mod-normal) -} - -.dot__4f569,.dotBorderColor__4f569 { -} - -.dotBorderColor__4f569 { - opacity: .4 -} - -.roleCircle__4f569 { - align-items: center; - border-radius: 50%; - display: flex; - flex-shrink: 0; - forced-color-adjust: none; - height: 12px; - justify-content: center; - margin: 0 4px; - padding: 0; - width: 12px -} - -.dotBorderBase__4f569 { - fill: var(--text-strong) -} - -.mask_a423bd { - display: block -} - -.status_a423bd { - height: 100%; - width: 100% -} - -.enable-forced-colors .status_a423bd { - forced-color-adjust: none!important -} - -.wrapper__6e9f8 { - cursor: pointer; - transition: background-color .1s ease-out,color .1s ease-out -} - -.childWrapper__6e9f8,.wrapper__6e9f8 { - align-items: center; - display: flex; - height: var(--guildbar-avatar-size); - justify-content: center; - width: var(--guildbar-avatar-size) -} - -.childWrapper__6e9f8 { - color: var(--text-default); - transition: scale .15s ease-out,background-color .15s ease-out,color .15s ease-out -} - -.childWrapperNoHoverBg__6e9f8 { - background-color: var(--background-mod-subtle) -} - -.custom-theme-background .childWrapperNoHoverBg__6e9f8 { - background-color: var(--background-mod-strong) -} - -.wrapper__6e9f8.selected__6e9f8 .childWrapperHoverScale__6e9f8,.wrapper__6e9f8:hover .childWrapperHoverScale__6e9f8 { - scale: 1.1 -} - -.wrapper__6e9f8.selected__6e9f8 .childWrapper__6e9f8,.wrapper__6e9f8:hover .childWrapper__6e9f8 { - background-color: var(--background-brand); - color: var(--white) -} - -.acronym__6e9f8 { - font-weight: var(--font-weight-medium); - line-height: 1.2em; - white-space: nowrap -} - -.icon__6e9f8 { - display: block; - height: var(--guildbar-avatar-size); - -o-object-fit: cover; - object-fit: cover; - pointer-events: none; - width: var(--guildbar-avatar-size) -} - -.icon__6e9f8:before { - background-color: var(--background-mod-subtle); - content: ""; - display: block; - height: 100%; - width: 100% -} - -.btnHamburger__006d6 { - background: transparent; - cursor: pointer; - height: 45px; - margin-right: -20px; - padding: 8px; - position: relative; - transform: scale(.3); - width: 60px -} - -.btnHamburger__006d6 span { - background: var(--white); - display: block; - height: 9px; - opacity: 1; - position: absolute; - transform: rotate(0deg); - transition: .25s ease-in-out; - width: 50% -} - -.btnHamburger__006d6 span:nth-child(2n) { - border-radius: 0 9px 9px 0; - left: 50% -} - -.btnHamburger__006d6 span:nth-child(odd) { - border-radius: 9px 0 0 9px; - left: 0 -} - -.btnHamburger__006d6 span:first-child,.btnHamburger__006d6 span:nth-child(2) { - top: 0 -} - -.btnHamburger__006d6 span:nth-child(3),.btnHamburger__006d6 span:nth-child(4) { - top: 18px -} - -.btnHamburger__006d6 span:nth-child(5),.btnHamburger__006d6 span:nth-child(6) { - top: 36px -} - -.theme-dark .btnHamburger__006d6 span { - background: var(--white) -} - -.theme-light .btnHamburger__006d6 span { - background: var(--black) -} - -.btnHamburgerOpen__006d6 span:first-child,.btnHamburgerOpen__006d6 span:nth-child(6) { - transform: rotate(45deg) -} - -.btnHamburgerOpen__006d6 span:nth-child(2),.btnHamburgerOpen__006d6 span:nth-child(5) { - transform: rotate(-45deg) -} - -.btnHamburgerOpen__006d6 span:first-child { - left: 5px; - top: 7px -} - -.btnHamburgerOpen__006d6 span:nth-child(2) { - left: calc(50% - 5px); - top: 7px -} - -.btnHamburgerOpen__006d6 span:nth-child(3) { - left: -50%; - opacity: 0 -} - -.btnHamburgerOpen__006d6 span:nth-child(4) { - left: 100%; - opacity: 0 -} - -.btnHamburgerOpen__006d6 span:nth-child(5) { - left: 5px; - top: 29px -} - -.btnHamburgerOpen__006d6 span:nth-child(6) { - left: calc(50% - 5px); - top: 29px -} - -.hamburger__9293f { - flex: 0 0 auto; - margin-inline-end:-4px} - -.container__9293f { - color: var(--text-default); - cursor: default; - display: flex; - flex: 0 0 auto; - flex-direction: column; - font-size: 16px; - justify-content: center; - line-height: 20px; - min-height: var(--custom-channel-header-height); - min-width: 0; - padding: 8px; - position: relative; - width: 100%; - z-index: 2; - --__header-bar-background: var(--background-base-lower); - border-bottom: 1px solid var(--border-subtle); - height: var(--custom-channel-header-height); - padding-inline-start:calc(var(--custom-message-margin-horizontal))} - -.container__9293f,.container__9293f * { - box-sizing: border-box -} - -.container__9293f.transparent__9293f { - background: transparent -} - -.upperContainer__9293f { - display: flex; - flex: 1 -} - -.children__9293f { - align-items: center; - display: flex; - flex: 1 1 auto; - min-width: 0; - overflow: hidden; - position: relative -} - -.children__9293f:after { - content: ""; - height: 100%; - inset-inline-end: 0; - position: absolute; - top: 0; - width: 8px -} - -.custom-theme-background .children__9293f:after { - content: unset -} - -.children__9293f.scrollable__9293f:after { - display: none -} - -.theme-dark .children__9293f:after { - background: linear-gradient(to right,rgba(54,57,63,0) 0,var(--__header-bar-background) 100%) -} - -.theme-light .children__9293f:after { - background: linear-gradient(to right,hsla(0,0%,100%,0) 0,var(--__header-bar-background) 100%) -} - -.transparent__9293f .children__9293f:after { - display: none -} - -.container__9293f.hidden__9293f { - opacity: 0; - pointer-events: none -} - -.toolbar__9293f { - align-items: center; - display: flex; - flex: 0 0 auto; - gap: var(--space-xs); - min-width: 0; - padding-inline-start:var(--space-xs)} - -.toolbar__9293f .iconWrapper__9293f { - margin: 0 -} - -.title__9293f { - align-items: center; - display: flex; - justify-content: flex-start; - overflow: hidden; - white-space: nowrap -} - -.titleWrapper__9293f { - flex: 0 0 auto; - margin-block:0;margin-inline:0 8px;min-width: auto -} - -.clickable__9293f { - cursor: pointer -} - -.iconWrapper__9293f { - align-items: center; - display: flex; - flex: 0 0 auto; - height: var(--space-32); - justify-content: center; - margin: 0; - position: relative; - width: var(--space-32) -} - -.icon__9293f { - display: block; - -webkit-app-region: no-drag; - height: var(--chat-input-icon-size); - width: var(--chat-input-icon-size) -} - -.channelIcon__9293f { - margin-inline:-1px calc(var(--custom-message-margin-horizontal)/2 + 1px);width: auto -} - -.icon__9293f { - color: var(--channel-icon) -} - -.clickable__9293f .icon__9293f { - color: var(--icon-muted) -} - -.clickable__9293f:hover .icon__9293f { - color: var(--icon-subtle) -} - -.selected__9293f .icon__9293f { - color: var(--icon-strong) -} - -.iconDisabled__9293f { - opacity: .6 -} - -.iconBadge__9293f { - background-color: var(--background-feedback-notification); - border-radius: 8px; - height: 8px; - position: absolute; - width: 8px -} - -.iconBadgeTop__9293f { - inset-inline-end: 2px; - top: 2px -} - -.iconBadgeBottom__9293f { - --custom-icon-offset: calc((var(--chat-input-icon-size) - 8px)/2); - bottom: var(--custom-icon-offset); - inset-inline-end: var(--custom-icon-offset) -} - -.divider__9293f { - background: var(--border-subtle); - flex: 0 0 auto; - height: 24px; - margin: 0 4px; - width: 1px -} - -.dot__9293f { - align-items: center; - color: var(--background-mod-strong); - height: 4px; - margin: 0 4px; - width: 4px -} - -.caret__9293f { - color: var(--icon-muted); - flex-shrink: 0; - margin: 0 2px; - width: 10px -} - -.scrollable__9293f { - overflow: auto; - -ms-overflow-style: none -} - -.refresh-fast-follow-distinct-borders .container__9293f { - border-bottom-color: var(--app-frame-border) -} - -.refresh-fast-follow-distinct-borders .divider__9293f { - background: var(--app-frame-border) -} - -.no-webkit-scrollbar .scrollable__9293f { - scrollbar-width: none -} - -.scrollable__9293f::-webkit-scrollbar { - display: none -} - -.high-contrast-mode .container__9293f,.theme-midnight .container__9293f { - border-bottom: 1px solid var(--border-subtle) -} - -.high-contrast-mode .container__9293f.transparent__9293f,.theme-midnight .container__9293f.transparent__9293f { - border-bottom: none -} - -.theme-dark .themed__9293f { - background: var(--background-gradient-lower,var(--__header-bar-background)) -} - -.theme-dark .themedMobile__9293f { - background: var(--background-gradient-high,var(--__header-bar-background)) -} - -.theme-light .themed__9293f { - background: var(--background-gradient-lowest,var(--__header-bar-background)) -} - -.theme-light .themedMobile__9293f { - background: var(--background-gradient-low,var(--__header-bar-background)) -} - -.enable-forced-colors .container__9293f { - border-bottom: 2px solid CanvasText -} - -.enable-forced-colors .icon__9293f { - color: CanvasText -} - -.enable-forced-colors .clickable__9293f .icon__9293f,.enable-forced-colors .clickable__9293f:hover .icon__9293f { - color: ButtonText -} - -.enable-forced-colors .clickable__9293f { - background-color: ButtonFace; - border-radius: 4px; - color: ButtonText -} - -.enable-forced-colors .iconBadge__9293f { - background-color: Highlight -} - -.titleClickable__9293f { - cursor: pointer -} - -.titleClickable__9293f:hover { - color: var(--text-strong) -} - -.nitroWheel_c5f0dc { - height: 16px; - margin-inline:2px 4px;vertical-align: sub; - width: 16px -} - -.themeSelectionContainer__36dee { - box-sizing: border-box; - height: var(--custom-theme-selection-selection-size); - position: relative; - width: var(--custom-theme-selection-selection-size) -} - -.themeSelection__36dee { - border-radius: 8px; - cursor: pointer; - height: 100%; - width: 100% -} - -.themeSelection__36dee.selected__36dee { - cursor: default -} - -.defaultThemeSelection__36dee { - box-shadow: inset 0 0 0 1px var(--interactive-text-default) -} - -.selectionCircle__36dee { - border-radius: 8px; - box-shadow: inset 0 0 0 4px var(--brand-500); - cursor: default; - height: calc(100% + 4px); - inset-inline-start: -2px; - pointer-events: none; - position: absolute; - top: -2px; - width: calc(100% + 4px) -} - -.checkmarkCircle__36dee { - inset-inline-end: -6px; - position: absolute; - top: -6px -} - -.checkmark__36dee { - color: var(--brand-500) -} - -.redCircle__36dee { - background-color: var(--red-430); - border-radius: 50%; - inset-inline-end: -6px; - top: -6px -} - -.lockedBadgeContainer__36dee,.redCircle__36dee { - background-clip: content-box; - box-shadow: inset 0 0 0 2px var(--background-base-low); - cursor: pointer; - height: 18px; - padding: 1px; - position: absolute; - width: 18px; - z-index: 2 -} - -.lockedBadgeContainer__36dee { - align-items: center; - background-color: var(--background-base-low); - border-radius: var(--radius-round); - bottom: 0; - display: flex; - inset-inline-end: 0; - justify-content: center -} - -.lockedBadge__36dee { - color: var(--icon-strong); - height: 10px; - width: 10px -} - -.disabled__36dee { - opacity: .2; - pointer-events: none -} - -.iconWrapper__36dee { - height: var(--custom-theme-selection-selection-size); - margin-top: 18px; - position: absolute; - text-align: center; - width: var(--custom-theme-selection-selection-size) -} - -.darkOverlay__36dee { - --background-gradient: linear-gradient(var(--opacity-black-20),var(--opacity-black-20)) -} - -.lightOverlay__36dee { - --background-gradient: linear-gradient(var(--opacity-white-48),var(--opacity-white-48)) -} - -.darkIcon__36dee { - background: var(--primary-600) -} - -.darkerIcon__36dee { - background: var(--plum-20) -} - -.midnightIcon__36dee { - background: var(--black) -} - -.lightIcon__36dee { - background: var(--white) -} - -.badgeContainer__8fea4 { - position: relative -} - -.badgeContainer__8fea4,.container__8fea4 { - display: flex; - justify-content: center -} - -.container__8fea4 { - align-items: center -} - -.paletteIcon__8fea4 { - filter: drop-shadow(0 1px 4px rgba(0,0,0,.14)) -} - -.newBadge__8fea4 { - position: absolute; - top: -8px; - z-index: 1 -} - -.newRing__8fea4 { - border-radius: 8px; - filter: drop-shadow(0 0 4px rgba(180,115,245,.44)); - height: calc(100% + 8px); - inset-inline-start: -4px; - position: absolute; - top: -4px; - width: calc(100% + 8px) -} - -.darkOverlay__8fea4 { - --background-gradient: linear-gradient(var(--opacity-black-40),var(--opacity-black-40)) -} - -.lightOverlay__8fea4 { - --background-gradient: linear-gradient(var(--background-mod-subtle),var(--background-mod-subtle)) -} - -.borderOverlay__8fea4 { - border-radius: 8px; - box-shadow: inset 0 0 0 1px var(--border-strong); - inset: 0; - pointer-events: none; - position: absolute -} - -.disabled__8fea4 { - opacity: .5; - pointer-events: none -} - -.header__67a11 { - align-items: center; - display: flex; - justify-content: space-between -} - -.headings__67a11 { - display: flex; - flex-direction: column; - gap: var(--space-8) -} - -.upsellText__67a11 { - color: var(--premium-nitro-pink-text) -} - -.title__67a11 { - align-items: center; - display: flex; - flex: 1 -} - -.betaTag__67a11 { - background: var(--custom-premium-colors-premium-gradient-tier-2); - display: inline -} - -.betaTagContainer__67a11 { - border-radius: 8px; - display: inline-flex; - margin-inline-start:2px} - -.premiumIcon__67a11 { - color: var(--icon-strong) -} - -.easterEggSelection__67a11 { - position: relative; - transform: rotate(0deg),scale(0) -} - -.full-motion .easterEggSelection__67a11 { - animation: scale-bounce-wiggle__67a11 .5s 0s forwards -} - -.sparkles__67a11 { - animation: fadeOut__67a11 2s 3s forwards; - inset-inline-start: 0; - opacity: 1; - pointer-events: none; - position: absolute; - top: 0 -} - -@keyframes fadeOut__67a11 { - 0% { - opacity: 1 - } - - to { - opacity: 0 - } -} - -@keyframes scale-bounce-wiggle__67a11 { - 0% { - transform: scale(0) rotate(10deg) - } - - 30% { - transform: scale(1.3) rotate(10deg) - } - - 55% { - transform: scale(.7) rotate(-10deg) - } - - 75% { - transform: scale(1.2) rotate(20deg) - } - - 85% { - transform: scale(.98) rotate(-20deg) - } - - 92% { - transform: scale(1.1) rotate(-5deg) - } - - 97% { - transform: scale(.99) rotate(-5deg) - } - - to { - transform: scale(1) rotate(0deg) - } -} - -.subtext__67a11 { - color: var(--text-subtle) -} - -.titleText__67a11 { - color: var(--text-strong) -} - -.root_df4c28 { - --custom-playground-offset-top: calc(var(--custom-app-top-bar-height) + var(--space-4)); - background: var(--background-base-lowest); - display: grid; - grid-template-columns: auto 1fr; - height: 100vh; - max-height: 100vh; - overflow: hidden; - padding-top: var(--custom-playground-offset-top) -} - -.group_df4c28 { - align-items: center; - display: flex; - flex-direction: column; - gap: var(--space-12); - width: var(--custom-guild-list-width) -} - -.header_df4c28 { - border-inline-start:1px solid var(--app-frame-border);border-start-start-radius: var(--radius-md); - border-top: 1px solid var(--app-frame-border) -} - -.headerDivider_df4c28 { - margin-inline-end:var(--space-12)} - -.headerRight_df4c28 { - margin-inline-start: auto; - padding-inline-end:var(--space-8)} - -.sidebar_df4c28 { - background: var(--background-base-lowest); - border-inline-end:1px solid var(--border-subtle);border-inline-start: 1px solid var(--border-subtle); - overflow-y: auto; - padding: var(--space-16); - padding-bottom: var(--space-24); - width: 280px -} - -.page_df4c28 { - box-sizing: border-box; - display: grid; - grid-template-columns: auto 1fr; - grid-template-rows: 1fr; - height: 100%; - min-height: 0; - padding-bottom: var(--custom-playground-offset-top) -} - -.content_df4c28,.main_df4c28 { - flex: 1; - height: 100%; - min-height: 0 -} - -.main_df4c28 { - display: flex -} - -.mainRightLayout_df4c28 { - flex-direction: row -} - -.mainBottomLayout_df4c28 { - flex-direction: column -} - -.canvas_df4c28 { - background-color: var(--background-base-lower); - flex: 1; - overflow: auto; - padding: var(--space-24); - padding-bottom: var(--space-32) -} - -.controls_df4c28 { - background: var(--background-base-lowest); - overflow-y: auto; - padding: var(--space-16); - padding-bottom: var(--space-24) -} - -.controlsRight_df4c28 { - border-inline-start:1px solid var(--border-subtle);width: 300px -} - -.controlsBottom_df4c28 { - border-block-start:1px solid var(--border-subtle);max-height: 300px -} - -.controlsSection_df4c28 { - margin-bottom: 24px -} - -.storyContent_df4c28 { - background: var(--background-surface-high); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - contain: layout; - container-type: inline-size; - padding: var(--space-24) -} - -.errorCodeMessage__85414 { - margin-top: 16px -} - -.dots__5ad89 { - color: var(--white); - pointer-events: none; - transform: translateZ(0) -} - -.dots__5ad89.themed__5ad89 { - color: var(--text-default) -} - -.wrapper__44b0c { - border-radius: 50%; - position: relative -} - -.pointer__44b0c { - cursor: pointer -} - -.mask__44b0c { - display: block; - pointer-events: none; - position: relative; - width: auto -} - -.svg__44b0c { - contain: paint -} - -.status__44b0c,.svg__44b0c { - position: absolute -} - -.status__44b0c { - bottom: 0; - height: auto; - inset-inline-end: 0 -} - -.cursorDefault__44b0c { - cursor: default -} - -.avatarStack__44b0c { - display: grid; - height: 100%; - width: 100% -} - -.avatar__44b0c,.avatarBorder__44b0c { - grid-area: 1/1; - height: 100%; - width: 100% -} - -.avatar__44b0c,.avatarBorder__44b0c { - border-radius: 50% -} - -.avatar__44b0c { - display: block; - -o-object-fit: cover; - object-fit: cover; - pointer-events: none -} - -.avatar__44b0c:before { - background-color: var(--background-mod-subtle); - content: ""; - display: block; - height: 100%; - width: 100% -} - -.pointerEvents__44b0c { - pointer-events: auto -} - -.avatarDecorationContainer__44b0c { - contain: paint; - inset-inline-start: var(--custom-avatar-avatar-decoration-border-position); - pointer-events: none; - position: absolute; - top: var(--custom-avatar-avatar-decoration-border-position) -} - -.avatarDecoration__44b0c { - display: block; - height: 100%; - -o-object-fit: cover; - object-fit: cover; - pointer-events: none; - width: 100% -} - -.cutoutIcon__44b0c { - color: var(--icon-muted); - height: 100%; - width: 100% -} - -.cutoutIcon__44b0c,.largeEmoji__44b0c { - align-items: center; - display: flex -} - -.largeEmoji__44b0c { - font-size: 48px; - justify-content: center -} - -.button__06eda { - align-items: center; - border-radius: 50%; - box-sizing: border-box; - cursor: pointer; - display: flex; - flex-shrink: 0; - justify-content: center -} - -.full-motion .button__06eda { - transition: color .2s ease,background-color .2s ease -} - -.primary__06eda { - background-color: var(--background-base-low); - color: var(--text-strong) -} - -.primary__06eda:hover:not(.disabled__06eda) { - color: var(--interactive-text-active) -} - -.primary__06eda:hover:not(.disabled__06eda),.secondary__06eda { - background-color: var(--background-base-lower) -} - -.secondary__06eda { - color: var(--text-default) -} - -.secondary__06eda:hover:not(.disabled__06eda) { - background-color: var(--background-base-low); - color: var(--interactive-text-active) -} - -.tertiary__06eda { - background-color: var(--background-base-lowest); - color: var(--interactive-text-default) -} - -.tertiary__06eda:hover:not(.disabled__06eda) { - background-color: var(--background-surface-high); - color: var(--interactive-text-active) -} - -.primary__06eda,.secondary__06eda,.tertiary__06eda { - background-color: var(--control-secondary-background-default); - color: var(--control-secondary-text-default) -} - -.primary__06eda:hover,.secondary__06eda:hover,.tertiary__06eda:hover { - background-color: var(--control-secondary-background-hover) -} - -.size24__06eda { - height: 24px; - min-height: 24px; - min-width: 24px; - padding: 4px; - width: 24px -} - -.size32__06eda { - height: 32px; - min-height: 32px; - min-width: 32px; - width: 32px -} - -.size36__06eda { - height: 36px; - min-height: 36px; - min-width: 36px; - width: 36px -} - -.disabled__06eda { - opacity: .3; - pointer-events: none -} - -.collapseable__217b7 { - background-color: var(--background-base-low); - border-radius: var(--radius-sm); - position: relative; - transition: background-color .1s ease -} - -.collapseable__217b7:active { - background-color: var(--background-mod-subtle) -} - -.collapseable__217b7:before { - border-radius: 8px; - box-shadow: var(--elevation-stroke),var(--elevation-low); - content: ""; - display: none; - inset: 0; - opacity: 0; - overflow: visible; - position: absolute; - transition: opacity .2s ease-in-out; - z-index: 0 -} - -.collapseable__217b7.toggled__217b7 { - cursor: default -} - -.collapseable__217b7.toggled__217b7:active { - background-color: var(--background-base-lower) -} - -.collapseable__217b7.toggled__217b7:before { - opacity: 1 -} - -.collapseable__217b7:not(:last-child) { - margin-bottom: 16px -} - -.header__217b7 { - cursor: pointer; - position: relative -} - -.header__217b7.toggled__217b7 { - cursor: default -} - -.contentExpandContainer__217b7 { - flex: 1; - overflow: hidden -} - -.showOverflow__217b7 { - overflow: visible -} - -.combobox__97e86 { - height: 100%; - overflow: hidden; - padding: var(--space-8) -} - -.header__97e86 { - padding-bottom: var(--space-8) -} - -.list__97e86 { - height: 100%; - list-style: none; - margin-top: 8px -} - -.list__97e86.scroller__97e86 { - margin-block:0 8px;margin-inline:8px 0} - -.item__97e86 { - align-items: center; - border-radius: 4px; - color: var(--interactive-text-default); - cursor: pointer; - display: flex; - font-weight: var(--font-weight-medium); - margin-bottom: 4px; - outline: none; - padding: 10px 8px -} - -.focused__97e86,.item__97e86:hover:not(.disabled__97e86) { - background-color: var(--interactive-background-hover); - color: var(--interactive-text-hover) -} - -.itemLabel__97e86 { - width: 100% -} - -.itemCheckbox__97e86~.itemLabel__97e86 { - margin-inline-start:8px} - -.empty__97e86 { - display: grid; - padding: 20px; - text-align: center; - grid-gap: 12px -} - -.disabled__97e86 { - cursor: not-allowed; - opacity: .3 -} - -.selected__97e86 { - background-color: var(--interactive-background-selected) -} - -.selectedBrand__97e86 { - background: var(--brand-500); - color: var(--white) -} - -.selectedBrand__97e86.focused__97e86,.selectedBrand__97e86:hover { - background: var(--brand-600); - color: var(--white) -} - -.searchWithScrollbar__97e86 { - margin: 8px -} - -.enable-forced-colors .item__97e86 { - border: 1px solid Canvas -} - -.enable-forced-colors .disabled__97e86 { - color: GrayText; - opacity: 1 -} - -.enable-forced-colors .disabled__97e86.focused__97e86 { - border-color: GrayText -} - -.enable-forced-colors .focused__97e86:not(.disabled__97e86),.enable-forced-colors .item__97e86:hover:not(.disabled__97e86) { - background-color: ButtonFace; - border-color: ButtonText; - color: ButtonText -} - -.enable-forced-colors .selected__97e86:not(.disabled__97e86) { - background-color: HighlightText; - color: Highlight -} - -.enable-forced-colors .selected__97e86:not(.disabled__97e86).focused__97e86,.enable-forced-colors .selected__97e86:not(.disabled__97e86):hover { - border-color: Highlight -} - -.container__3dde2 { - background-color: var(--modal-background); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-sizing: border-box; - overflow: hidden; - padding: 8px; - width: 250px -} - -.scroller__3dde2 { - padding: 0 -} - -.list__3dde2 { - height: 280px; - padding: 8px 0 -} - -.refresh-fast-follow-distinct-borders .container__3dde2 { - border-color: var(--app-frame-border) -} - -.positioner_a516b9 { - bottom: 0; - inset-inline: 0; - position: fixed; - text-align: center; - z-index: 1000 -} - -.container_a516b9 { - align-items: center; - background-color: var(--background-surface-high); - border-radius: 8px; - box-shadow: var(--shadow-border),var(--shadow-high); - display: inline-flex; - justify-content: center; - margin: auto auto 16px; - max-width: 90vw; - padding: 16px -} - -.bindName_a516b9 { - flex: 0 0 auto; - margin-inline-end:8px} - -.shortcut_a516b9 { - flex: 0 0 auto; - margin: 0 2px -} - -.separator_a516b9 { - color: var(--text-default); - flex: 0 0 auto; - font-weight: var(--font-weight-semibold); - margin: 0 12px -} - -.separator_a516b9:after { - content: "•" -} - -.focus-rings-ring { - background: none; - border-radius: var(--__adaptive-focus-ring-radius,4px); - box-shadow: 0 0 0 4px var(--__adaptive-focus-ring-color,var(--focus-primary,#00b0f4)); - display: block; - margin: 0; - padding: 0; - pointer-events: none; - position: absolute -} - -.enable-forced-colors .focus-rings-ring { - forced-color-adjust: none; - --__adaptive-focus-ring-color: CanvasText!important; - border: 2px solid Canvas -} - -.fieldWrapper_ce6bbd { - padding-top: 4px; - position: relative -} - -.interactive_bf202d { - border-radius: inherit; - color: var(--interactive-text-default); - cursor: pointer -} - -.interactive_bf202d:focus-within,.interactive_bf202d:hover { - background: var(--interactive-background-hover); - color: var(--interactive-text-hover) -} - -.interactive_bf202d:active { - background: var(--interactive-background-active); - color: var(--interactive-text-active) -} - -.muted_bf202d { - color: var(--text-muted) -} - -@media (min-width: 486px) { - .selected_bf202d { - background:var(--interactive-background-selected); - color: var(--interactive-text-active) - } -} - -.enable-forced-colors .interactive_bf202d { - background-color: ButtonFace; - border: 1px solid ButtonFace; - color: ButtonText; - forced-color-adjust: none -} - -.enable-forced-colors .interactive_bf202d:focus-within,.enable-forced-colors .interactive_bf202d:hover { - background-color: ButtonFace; - border-color: ButtonText; - color: ButtonText -} - -.enable-forced-colors .interactive_bf202d.selected_bf202d,.enable-forced-colors .interactive_bf202d.selected_bf202d:focus-within,.enable-forced-colors .interactive_bf202d.selected_bf202d:hover { - background-color: Highlight; - border-color: HighlightText; - color: HighlightText -} - -.container__3ff92 { - position: relative; - width: 100% -} - -.items__3ff92 { - flex-direction: row; - flex-wrap: wrap -} - -.itemMeasurements__3ff92,.items__3ff92 { - display: flex -} - -.overflowMeasurement__3ff92 { - display: inline-block -} - -.measurements__3ff92 { - inset: 0; - pointer-events: none; - position: absolute; - visibility: hidden; - z-index: -1 -} - -.tooltip__3ff92 { - display: flex; - flex-wrap: wrap; - gap: 4px -} - -.scrollerBase__99f8c { - box-sizing: border-box; - flex: 1 1 auto; - min-height: 0; - position: relative -} - -.auto__99f8c,.none__99f8c,.thin__99f8c { -} - -.thin__99f8c::-webkit-scrollbar { - height: 8px; - width: 8px -} - -.thin__99f8c::-webkit-scrollbar-track { - background-color: var(--scrollbar-thin-track); - border: 2px solid var(--scrollbar-thin-track); - border-color: var(--scrollbar-thin-track) -} - -.thin__99f8c::-webkit-scrollbar-thumb { - background-clip: padding-box; - background-color: var(--scrollbar-thin-thumb); - border: 2px solid transparent; - border-radius: 4px; - min-height: 40px -} - -.thin__99f8c::-webkit-scrollbar-corner { - background-color: transparent -} - -.auto__99f8c::-webkit-scrollbar { - height: 16px; - width: 16px -} - -.auto__99f8c::-webkit-scrollbar-track { - background-color: var(--scrollbar-auto-track) -} - -.auto__99f8c::-webkit-scrollbar-thumb,.auto__99f8c::-webkit-scrollbar-track { - background-clip: padding-box; - border: 4px solid transparent; - border-radius: 8px -} - -.auto__99f8c::-webkit-scrollbar-thumb { - background-color: var(--scrollbar-auto-thumb); - min-height: 40px -} - -.auto__99f8c::-webkit-scrollbar-corner { - background-color: transparent -} - -.none__99f8c::-webkit-scrollbar { - height: 0; - width: 0 -} - -.fade__99f8c::-webkit-scrollbar-thumb,.fade__99f8c::-webkit-scrollbar-track { - visibility: hidden -} - -.fade__99f8c:hover::-webkit-scrollbar-thumb,.fade__99f8c:hover::-webkit-scrollbar-track { - visibility: visible -} - -.scrolling__99f8c.fade__99f8c:focus-within::-webkit-scrollbar-thumb,.scrolling__99f8c.fade__99f8c:focus-within::-webkit-scrollbar-track { - visibility: visible -} - -.no-webkit-scrollbar .thin__99f8c { - scrollbar-color: var(--scrollbar-thin-thumb) var(--scrollbar-thin-track); - scrollbar-width: thin -} - -.no-webkit-scrollbar .thin__99f8c.fade__99f8c.scrolling__99f8c,.no-webkit-scrollbar .thin__99f8c.fade__99f8c:hover { - scrollbar-color: var(--scrollbar-thin-thumb) var(--scrollbar-thin-track) -} - -.no-webkit-scrollbar .auto__99f8c { - scrollbar-color: var(--scrollbar-auto-scrollbar-color-thumb) var(--scrollbar-auto-scrollbar-color-track); - scrollbar-width: auto -} - -.no-webkit-scrollbar .auto__99f8c.fade__99f8c.scrolling__99f8c,.no-webkit-scrollbar .auto__99f8c.fade__99f8c:hover { - scrollbar-color: var(--scrollbar-auto-scrollbar-color-thumb) var(--scrollbar-auto-scrollbar-color-track) -} - -.no-webkit-scrollbar .none__99f8c { - scrollbar-width: none -} - -.no-webkit-scrollbar .fade__99f8c { - scrollbar-color: transparent transparent -} - -.content__99f8c { - position: relative -} - -.disableScrollAnchor__99f8c { - overflow-anchor: none -} - -.managedReactiveScroller__99f8c { - overflow-x: hidden; - overflow-y: scroll -} - -.pointerCover__99f8c { - inset: 0; - position: absolute; - z-index: 9999 -} - -.enable-forced-colors ::-webkit-scrollbar-track { - border-radius: 0; - border-width: 1px -} - -.enable-forced-colors ::-webkit-scrollbar-thumb { - background-color: CanvasText; - border-width: 1px -} - -.enable-forced-colors ::-webkit-scrollbar-thumb:horizontal:active,.enable-forced-colors ::-webkit-scrollbar-thumb:horizontal:hover,.enable-forced-colors ::-webkit-scrollbar-thumb:vertical:active,.enable-forced-colors ::-webkit-scrollbar-thumb:vertical:hover { - background-color: Highlight -} - -.enable-forced-colors .auto__99f8c::-webkit-scrollbar { - height: 8px; - width: 8px -} - -.enable-forced-colors .auto__99f8c::-webkit-scrollbar-track { - border-radius: 0; - border-width: 1px -} - -.sliderContainer__65039 { - box-sizing: border-box; - overflow: visible; - padding: 0 8px -} - -.slider__65039 { - position: relative; - top: -4px -} - -.backdrop__78332 { - position: fixed; - top: 0; - inset-inline: 0 var(--devtools-sidebar-width,0); - bottom: 0; - transform: translateZ(0) -} - -.backdrop__78332.withLayer__78332 { - pointer-events: all -} - -.backdrop_bc663c { - background-color: var(--black) -} - -.backdrop_bc663c,.layer_bc663c { - inset: 0; - position: absolute -} - -.layer_bc663c { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - min-height: 0; - padding-bottom: var(--space-24); - padding-top: var(--space-24) -} - -.layer_bc663c:has([data-modal-align=top]) { - bottom: auto; - padding-top: 0 -} - -.inactive_bc663c>* { - pointer-events: none!important; - z-index: -1 -} - -.content__6e5a0 { - padding-bottom: 20px -} - -.cancelButton__6e5a0 { - margin-inline-end:var(--space-12)} - -: root { - --custom-paginator-round-button-size:28px -} - -.pageControlContainer_c15210 { - bottom: 0; - margin-top: 16px; - width: 100% -} - -.pageControl_c15210 { - margin: auto; - padding: 4px 0 -} - -.pageButton_c15210,.pageControl_c15210 { - align-items: center; - display: flex; - width: -moz-min-content; - width: min-content -} - -.pageButton_c15210 { - box-sizing: border-box; - color: var(--text-strong); - cursor: pointer; - font-weight: var(--font-weight-semibold); - height: var(--custom-paginator-round-button-size); - justify-content: center; - margin: 4px; - min-width: var(--custom-paginator-round-button-size); - padding: 6px -} - -.pageButton_c15210:hover { - background-color: var(--background-mod-normal); - color: var(--interactive-text-hover) -} - -.roundButton_c15210 { - border-radius: 14px; - min-width: var(--custom-paginator-round-button-size) -} - -.endButton_c15210 { - padding: 0 8px -} - -.endButton_c15210:first-child { - padding-inline-end:12px} - -.endButton_c15210:last-child { - padding-inline-start:12px} - -.endButtonInner_c15210 { - align-items: center; - display: flex -} - -.gap_c15210 { - color: var(--text-default); - margin: 8px 4px; - text-align: center; - width: var(--custom-paginator-round-button-size) -} - -.iconCaret_c15210 { - display: inline-block; - height: 1em; - width: 1em -} - -.iconCaret_c15210:first-child { - margin-inline-end:4px} - -.iconCaret_c15210:last-child { - margin-inline-start:4px} - -.jumpToPageInlineInput_c15210 { - width: 50px -} - -.jumpToPageInlineInput_c15210+.activeButton_c15210 { - margin-inline-start:10px} - -.activeButton_c15210+.jumpToPageInlineInput_c15210 { - margin-inline-start:6px} - -.enable-forced-colors .pageButton_c15210 { - background-color: ButtonFace; - color: ButtonText; - forced-color-adjust: none -} - -.enable-forced-colors .pageButton_c15210:hover { - text-decoration: underline -} - -.enable-forced-colors .activeButton_c15210,.enable-forced-colors .activeButton_c15210:hover { - background-color: HighlightText; - color: Highlight -} - -.activeButton_c15210,.activeButton_c15210:hover { - background-color: var(--brand-500); - color: var(--white) -} - -.selectableItem_eb626b { - border-radius: 3px; - box-sizing: border-box; - color: var(--text-strong); - cursor: pointer; - height: 34px; - margin: 2px 0; - margin-inline-end:4px;padding: 8px 10px -} - -.selectableItem_eb626b.selected_eb626b { - cursor: default -} - -.selectableItemLabel_eb626b { - font-size: 16px; - overflow: hidden; - position: relative; - text-overflow: ellipsis; - top: 1px; - white-space: nowrap -} - -.theme-light .selectableItem_eb626b:focus,.theme-light .selectableItem_eb626b:hover { - background-color: var(--primary-200) -} - -.theme-dark .selectableItem_eb626b:focus,.theme-dark .selectableItem_eb626b:hover { - background-color: hsl(var(--primary-700-hsl)/.6) -} - -.enable-forced-colors .selectableItem_eb626b:focus,.enable-forced-colors .selectableItem_eb626b:hover { - outline: 1px solid Highlight; - outline-offset: -1px -} - -.popoutList__92efc { - background: var(--background-mod-normal); - border-radius: 5px; - padding: 10px 10px 0 -} - -.popoutListEmpty__92efc { - box-sizing: border-box; - color: var(--text-strong); - cursor: default; - margin-block:2px;margin-inline:0 4px;padding: 8px 10px 0 -} - -.divider__92efc { - margin: 8px 0 -} - -.enable-forced-colors .popoutList__92efc { - border: 2px solid CanvasText -} - -.roleDotRight__703b9 { - margin-inline-start:.25rem} - -.roleDotLeft__703b9 { - margin-inline-end:.25rem} - -.username__703b9 { -} - -.container__703b9 { - text-decoration: inherit -} - -.container__703b9,.nameContainer__703b9 { - display: inline-flex; - max-width: 100%; - min-width: 0 -} - -.nameContainer__703b9 { - position: relative; - z-index: 0 -} - -.name__703b9 { - position: relative -} - -.name__703b9,.nameGlow__703b9 { - overflow: hidden; - text-overflow: ellipsis -} - -.nameGlow__703b9 { - height: 100%; - inset: 0; - position: absolute; - width: 100% -} - -.tabItem__9e06a { - cursor: pointer; - font-size: 13px; - margin-inline-end:20px;padding-bottom: 20px; - padding-top: 20px -} - -.tabContainer__9e06a { - color: var(--text-muted) -} - -.pillContainer__9e06a,.tabContainer__9e06a { - display: flex; - flex-direction: row -} - -.pillContainer__9e06a { - background-color: var(--background-base-lowest); - border-radius: var(--radius-md); - gap: var(--space-4); - justify-content: stretch; - padding: var(--space-4) -} - -.pillItem__9e06a { - border-radius: var(--radius-sm); - color: var(--text-subtle); - cursor: pointer; - display: flex; - flex: 1 1 auto; - justify-content: center; - padding: 8px 16px -} - -.pillItem__9e06a:not(.pillItemSelected__9e06a):hover { - background-color: var(--background-mod-subtle) -} - -.pillItemSelected__9e06a { - background-color: var(--background-surface-high); - color: var(--interactive-text-active) -} - -.theme-dark .tabItemSelected__9e06a { - border-bottom: 2px solid var(--primary-100) -} - -.theme-light .tabItemSelected__9e06a { - border-bottom: 2px solid var(--primary-500) -} - -.tabItemSelected__9e06a { - color: var(--text-strong) -} - -.icon__9e06a { - height: 16px; - width: 16px -} - -.controlText__9e06a { - align-items: center; - display: flex; - gap: var(--space-8) -} - -.pillItemText__9e06a { - line-height: 1 -} - -.disabled__9e06a { - cursor: not-allowed -} - -.disabledContainer__9e06a { - opacity: .3 -} - -.transitionGroup__6ba63 { - overflow: hidden; - position: relative -} - -.measurement__6ba63 { - overflow: hidden -} - -.measurementFill__6ba63 { - flex: 0 0 auto; - width: 100% -} - -.measurementFillStatic__6ba63 { - height: 100% -} - -.animatedNode__6ba63 { - width: 100% -} - -@media screen and (max-height: 550px),screen and (max-width:485px) { - .outerAnimatedDivDynamicSizing__59d99 { - min-height:100%!important; - min-width: 100%!important - } - - .innerAnimatedDivDynamicSizing__59d99 { - width: 100%!important - } -} - -.containerTop__45edc { - top: calc(var(--custom-app-top-bar-height) + var(--space-8)) -} - -.containerBottom__45edc,.containerTop__45edc { - display: flex; - flex-direction: column; - inset-inline-start: 50%; - position: fixed; - transform: translateX(-50%); - z-index: 4999 -} - -.containerBottom__45edc { - bottom: 140px -} - -.breadcrumbs__75013 { - overflow: hidden -} - -.breadcrumbWrapper__75013 { - align-items: center; - color: var(--text-muted); - display: flex; - flex-grow: 0; - font-weight: var(--font-weight-semibold) -} - -.activeBreadcrumb__75013 { - color: var(--interactive-text-active) -} - -.breadcrumbClickWrapper__75013,.breadcrumbFinalWrapper__75013 { - overflow: hidden -} - -.breadcrumb__75013 { - font-size: 14px -} - -.breadcrumbArrow__75013 { - height: 18px; - width: 18px -} - -.interactiveBreadcrumb__75013 { - cursor: pointer -} - -.container__459fb { - display: flex; - flex-wrap: wrap; - margin-top: -10px; - margin-inline-end:-10px} - -.container__459fb.gradient__459fb { - flex-wrap: nowrap -} - -.customContainer__459fb,.defaultContainer__459fb { - flex: 1; - margin-top: 10px; - margin-inline-end:10px;max-width: 70px; - min-width: 60px -} - -.custom__459fb .preset__459fb { - display: flex; - flex: 1 1 auto; - flex-direction: vertical -} - -.customColorPicker__459fb { - background-color: var(--background-base-low); - border: 1px solid var(--border-subtle); - border-radius: 4px; - box-shadow: var(--shadow-border),var(--shadow-high); - display: flex; - flex-direction: column; - gap: 16px; - padding: 16px; - width: 220px -} - -.customColorPickerInputContainer__459fb { - align-items: center; - display: flex; - gap: 12px -} - -.customColorPickerEyeDropper__459fb { - align-items: center; - cursor: pointer; - display: flex; - height: 16px; - justify-content: center; - margin: 0; - width: 16px -} - -.customColorPickerInput__459fb { - flex: 1 -} - -.customColorPickerInput__459fb .input__459fb { - height: 24px; - text-transform: uppercase -} - -.colorPickerRow__459fb { - display: flex; - flex-wrap: wrap; - height: 20px; - margin-top: 10px; - overflow: hidden -} - -.colorPickerSwatch__459fb { - align-items: center; - background-color: transparent; - border: 1px solid transparent; - border-radius: 3px; - box-sizing: border-box; - cursor: pointer; - display: flex; - height: 20px; - justify-content: center; - margin-inline-end:10px;padding: 0; - position: relative; - width: 20px -} - -.colorPickerSwatch__459fb.custom__459fb,.colorPickerSwatch__459fb.default__459fb { - border-radius: 4px; - height: 50px; - margin-inline-end:0;width: 100% -} - -.colorPickerSwatch__459fb.gradientPreset__459fb { - border: none; - height: 20px; - margin-inline-end:var(--space-8);width: 40px -} - -.colorPickerSwatch__459fb.disabled__459fb { - cursor: not-allowed; - opacity: .3; - pointer-events: none -} - -.colorPickerSwatch__459fb .colorPickerDropper__459fb { - inset-inline-end: 4px; - position: absolute; - top: 4px -} - -.suggestedColors__459fb { - display: flex; - flex-wrap: wrap; - gap: 12px; - justify-content: center -} - -.suggestedColor__459fb { - border: 1px solid var(--primary-400); - border-radius: 4px; - cursor: pointer; - height: 32px; - width: 32px -} - -.theme-dark .colorPickerSwatch__459fb.noColor__459fb { - border-color: var(--opacity-white-8) -} - -.theme-light .colorPickerSwatch__459fb.noColor__459fb { - border-color: hsl(var(--primary-500-hsl)/.1) -} - -.enable-forced-colors .colorPickerSwatch__459fb { - border: 1px solid ButtonText; - forced-color-adjust: none -} - -.enable-forced-colors .colorPickerSwatch__459fb.disabled__459fb { - border-color: GrayText -} - -.copyInput_fffc15 { - background-color: var(--input-background-default); - border: 1px solid var(--input-border-default); - border-radius: var(--radius-sm); - box-sizing: border-box; - cursor: pointer; - height: 40px; - overflow: hidden; - position: relative; - transition: border-color .2s ease-in-out -} - -.copyInputDefault_fffc15,.copyInputError_fffc15 { -} - -.copyInputError_fffc15 { - background-color: var(--background-feedback-critical); - border-color: var(--text-feedback-critical); - color: var(--text-feedback-critical) -} - -.copyInputSuccess_fffc15 { - background-color: var(--background-feedback-positive); - border-color: var(--text-feedback-positive); - color: var(--text-feedback-positive) -} - -.layout_fffc15 { - inset: 0; - position: absolute -} - -.inputWrapper_fffc15 { - position: relative -} - -.button_fffc15 { - border-radius: var(--radius-xs); - margin-block:4px;margin-inline:0 4px;padding: 2px 20px!important; - transition: background-color .2s ease-in-out,color .2s ease-in-out -} - -.hiddenMessage_fffc15,.input_fffc15 { - background-color: transparent; - font-size: 16px; - line-height: 20px; - overflow: hidden; - padding-block:10px;padding-inline:10px 0;position: relative; - text-overflow: ellipsis; - white-space: nowrap -} - -.input_fffc15 { - border: none; - box-sizing: border-box; - cursor: text; - flex: 1 1 auto; - min-width: 0; - transition: color .15s ease; - width: 100% -} - -.inputDefault_fffc15 { -} - -.inputError_fffc15,.inputSuccess_fffc15 { - color: var(--text-default) -} - -.hiddenMessage_fffc15 { - inset: 0; - position: absolute; - z-index: 0 -} - -.inputHidden_fffc15 { - visibility: hidden -} - -.hiddenMessage_fffc15,.inputDefault_fffc15 { - color: var(--text-default) -} - -.hiddenMessage_fffc15::-moz-placeholder,.inputDefault_fffc15::-moz-placeholder { - color: var(--input-placeholder-text-default) -} - -.hiddenMessage_fffc15::placeholder,.inputDefault_fffc15::placeholder { - color: var(--input-placeholder-text-default) -} - -.wrapper__33d12 { - height: 100%; - margin-inline:auto;max-width: 440px; - width: 100% -} - -.image__33d12 { - background-size: 100% 100% -} - -.title__33d12 { - font-size: 17px; - font-weight: var(--font-weight-semibold); - line-height: 22px; - text-transform: uppercase -} - -.text__33d12,.title__33d12 { - color: var(--text-muted); - text-align: center -} - -.text__33d12 { - font-size: 16px; - line-height: 20px -} - -.slider__4e371 { - inset-inline: 0; - bottom: 0; - position: absolute -} - -.shinyButton__6a443 { - overflow: hidden -} - -.buttonShine__6a443,.shinyButton__6a443>* { - pointer-events: none -} - -.buttonShine__6a443 { - color: var(--opacity-white-8) -} - -.full-motion .buttonShine__6a443 { - animation-delay: .75s; - animation-duration: 2s; - animation-iteration-count: infinite; - animation-name: Shine__6a443; - animation-timing-function: ease-in-out -} - -.reduce-motion .buttonShine__6a443 { - transform: translate3d(calc(100% - 32px),0,0) -} - -.full-motion .onlyShineOnHover__6a443 { - animation-delay: 0s; - animation-name: InstantShine__6a443 -} - -.shineContainer__6a443 { - bottom: 0; - position: absolute; - top: -50%; - inset-inline: -50% 0 -} - -.shine__6a443 { - background-color: currentColor; - height: 300%; - position: relative; - top: -100%; - transform: rotate(30deg) -} - -.shineDefault__6a443 { - width: 56px -} - -.shineSmall__6a443 { - width: 30px -} - -.shinePaused__6a443 { - animation-play-state: paused -} - -.shineInner__6a443 { - background-color: currentColor; - height: 100% -} - -.shineInnerDefault__6a443 { - width: 16px -} - -.shineInnerSmall__6a443 { - width: 10px -} - -@keyframes Shine__6a443 { - 0% { - transform: translate3d(-50%,0,0) - } - - to { - transform: translate3d(200%,0,0) - } -} - -@keyframes InstantShine__6a443 { - 0% { - transform: translateZ(0) - } - - to { - transform: translate3d(200%,0,0) - } -} - -.iconLayout__0c4c4 { - align-items: center; - box-sizing: border-box; - cursor: text; - display: flex; - justify-content: center -} - -.iconLayout__0c4c4[data-size=sm],.iconLayout__0c4c4[data-size=sm] .iconContainer__0c4c4 { - height: var(--icon-size-xs); - width: var(--icon-size-xs) -} - -.iconLayout__0c4c4[data-size=md],.iconLayout__0c4c4[data-size=md] .iconContainer__0c4c4 { - height: var(--icon-size-sm); - width: var(--icon-size-sm) -} - -.pointer__0c4c4 { - cursor: pointer -} - -.iconContainer__0c4c4 { - position: relative -} - -.icon__0c4c4 { - box-sizing: border-box; - color: var(--interactive-text-default); - height: 100%; - inset-inline-start: 0; - opacity: 0; - position: absolute; - top: 0; - transform: rotate(90deg); - width: 100% -} - -.full-motion .icon__0c4c4 { - transition: transform .1s ease-out,opacity .1s ease-out -} - -.icon__0c4c4.visible__0c4c4 { - opacity: 1; - transform: rotate(0deg) -} - -.clear__0c4c4 { -} - -.iconLayout__0c4c4:hover .clear__0c4c4 { - color: var(--interactive-text-hover) -} - -.iconLayout__0c4c4:active .clear__0c4c4 { - color: var(--interactive-text-active) -} - -.enable-forced-colors .icon__0c4c4 { - background-color: Canvas; - border: 1px solid Canvas; - color: GrayText -} - -.enable-forced-colors .clear__0c4c4 { - background-color: ButtonFace; - border-color: CanvasText; - color: ButtonText -} - -.enable-forced-colors .iconLayout__0c4c4:active .clear__0c4c4,.enable-forced-colors .iconLayout__0c4c4:hover .clear__0c4c4 { - border-color: ButtonText; - color: ButtonText -} - -.heading-sm\/normal__2b1f5 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.heading-sm\/normal__2b1f5.fontScaling__2b1f5 { - font-size: .875rem -} - -.heading-sm\/medium__2b1f5 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.heading-sm\/medium__2b1f5.fontScaling__2b1f5 { - font-size: .875rem -} - -.heading-sm\/semibold__2b1f5 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.heading-sm\/semibold__2b1f5.fontScaling__2b1f5 { - font-size: .875rem -} - -.heading-sm\/bold__2b1f5 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.heading-sm\/bold__2b1f5.fontScaling__2b1f5 { - font-size: .875rem -} - -.heading-sm\/extrabold__2b1f5 { - font-family: var(--font-display); - font-size: 14px; - font-weight: 800; - line-height: 1.2857142857142858 -} - -.heading-sm\/extrabold__2b1f5.fontScaling__2b1f5 { - font-size: .875rem -} - -.heading-md\/normal__2b1f5 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.heading-md\/normal__2b1f5.fontScaling__2b1f5 { - font-size: 1rem -} - -.heading-md\/medium__2b1f5 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.heading-md\/medium__2b1f5.fontScaling__2b1f5 { - font-size: 1rem -} - -.heading-md\/semibold__2b1f5 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.heading-md\/semibold__2b1f5.fontScaling__2b1f5 { - font-size: 1rem -} - -.heading-md\/bold__2b1f5 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.heading-md\/bold__2b1f5.fontScaling__2b1f5 { - font-size: 1rem -} - -.heading-md\/extrabold__2b1f5 { - font-family: var(--font-display); - font-size: 16px; - font-weight: 800; - line-height: 1.25 -} - -.heading-md\/extrabold__2b1f5.fontScaling__2b1f5 { - font-size: 1rem -} - -.heading-lg\/normal__2b1f5 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.heading-lg\/normal__2b1f5.fontScaling__2b1f5 { - font-size: 1.25rem -} - -.heading-lg\/medium__2b1f5 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.heading-lg\/medium__2b1f5.fontScaling__2b1f5 { - font-size: 1.25rem -} - -.heading-lg\/semibold__2b1f5 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.heading-lg\/semibold__2b1f5.fontScaling__2b1f5 { - font-size: 1.25rem -} - -.heading-lg\/bold__2b1f5 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.heading-lg\/bold__2b1f5.fontScaling__2b1f5 { - font-size: 1.25rem -} - -.heading-lg\/extrabold__2b1f5 { - font-family: var(--font-display); - font-size: 20px; - font-weight: 800; - line-height: 1.2 -} - -.heading-lg\/extrabold__2b1f5.fontScaling__2b1f5 { - font-size: 1.25rem -} - -.heading-xl\/normal__2b1f5 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xl\/normal__2b1f5.fontScaling__2b1f5 { - font-size: 1.5rem -} - -.heading-xl\/medium__2b1f5 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xl\/medium__2b1f5.fontScaling__2b1f5 { - font-size: 1.5rem -} - -.heading-xl\/semibold__2b1f5 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xl\/semibold__2b1f5.fontScaling__2b1f5 { - font-size: 1.5rem -} - -.heading-xl\/bold__2b1f5 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xl\/bold__2b1f5.fontScaling__2b1f5 { - font-size: 1.5rem -} - -.heading-xl\/extrabold__2b1f5 { - font-family: var(--font-display); - font-size: 24px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xl\/extrabold__2b1f5.fontScaling__2b1f5 { - font-size: 1.5rem -} - -.heading-xxl\/normal__2b1f5 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 400; - line-height: 1.25 -} - -.heading-xxl\/normal__2b1f5.fontScaling__2b1f5 { - font-size: 2rem -} - -.heading-xxl\/medium__2b1f5 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 500; - line-height: 1.25 -} - -.heading-xxl\/medium__2b1f5.fontScaling__2b1f5 { - font-size: 2rem -} - -.heading-xxl\/semibold__2b1f5 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 600; - line-height: 1.25 -} - -.heading-xxl\/semibold__2b1f5.fontScaling__2b1f5 { - font-size: 2rem -} - -.heading-xxl\/bold__2b1f5 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 700; - line-height: 1.25 -} - -.heading-xxl\/bold__2b1f5.fontScaling__2b1f5 { - font-size: 2rem -} - -.heading-xxl\/extrabold__2b1f5 { - font-family: var(--font-display); - font-size: 32px; - font-weight: 800; - line-height: 1.25 -} - -.heading-xxl\/extrabold__2b1f5.fontScaling__2b1f5 { - font-size: 2rem -} - -.eyebrow__2b1f5 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - letter-spacing: .02em; - line-height: 1.3333333333333333; - text-transform: uppercase -} - -.eyebrow__2b1f5.fontScaling__2b1f5 { - font-size: .75rem -} - -.heading-deprecated-12\/normal__2b1f5 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/normal__2b1f5.fontScaling__2b1f5 { - font-size: .75rem -} - -.heading-deprecated-12\/medium__2b1f5 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/medium__2b1f5.fontScaling__2b1f5 { - font-size: .75rem -} - -.heading-deprecated-12\/semibold__2b1f5 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/semibold__2b1f5.fontScaling__2b1f5 { - font-size: .75rem -} - -.heading-deprecated-12\/bold__2b1f5 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/bold__2b1f5.fontScaling__2b1f5 { - font-size: .75rem -} - -.heading-deprecated-12\/extrabold__2b1f5 { - font-family: var(--font-display); - font-size: 12px; - font-weight: 800; - line-height: 1.3333333333333333 -} - -.heading-deprecated-12\/extrabold__2b1f5.fontScaling__2b1f5 { - font-size: .75rem -} - -.redesign\/heading-18\/bold__2b1f5 { - font-family: var(--font-display); - font-size: 18px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/heading-18\/bold__2b1f5.fontScaling__2b1f5 { - font-size: 1.125rem -} - -.text-xxs\/normal__2b1f5 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 400; - line-height: 1.2 -} - -.text-xxs\/normal__2b1f5.fontScaling__2b1f5 { - font-size: .625rem -} - -.text-xxs\/medium__2b1f5 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 500; - line-height: 1.2 -} - -.text-xxs\/medium__2b1f5.fontScaling__2b1f5 { - font-size: .625rem -} - -.text-xxs\/semibold__2b1f5 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 600; - line-height: 1.2 -} - -.text-xxs\/semibold__2b1f5.fontScaling__2b1f5 { - font-size: .625rem -} - -.text-xxs\/bold__2b1f5 { - font-family: var(--font-primary); - font-size: 10px; - font-weight: 700; - line-height: 1.2 -} - -.text-xxs\/bold__2b1f5.fontScaling__2b1f5 { - font-size: .625rem -} - -.text-xs\/normal__2b1f5 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.text-xs\/normal__2b1f5.fontScaling__2b1f5 { - font-size: .75rem -} - -.text-xs\/medium__2b1f5 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.text-xs\/medium__2b1f5.fontScaling__2b1f5 { - font-size: .75rem -} - -.text-xs\/semibold__2b1f5 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.text-xs\/semibold__2b1f5.fontScaling__2b1f5 { - font-size: .75rem -} - -.text-xs\/bold__2b1f5 { - font-family: var(--font-primary); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.text-xs\/bold__2b1f5.fontScaling__2b1f5 { - font-size: .75rem -} - -.text-sm\/normal__2b1f5 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 400; - line-height: 1.2857142857142858 -} - -.text-sm\/normal__2b1f5.fontScaling__2b1f5 { - font-size: .875rem -} - -.text-sm\/medium__2b1f5 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 500; - line-height: 1.2857142857142858 -} - -.text-sm\/medium__2b1f5.fontScaling__2b1f5 { - font-size: .875rem -} - -.text-sm\/semibold__2b1f5 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 600; - line-height: 1.2857142857142858 -} - -.text-sm\/semibold__2b1f5.fontScaling__2b1f5 { - font-size: .875rem -} - -.text-sm\/bold__2b1f5 { - font-family: var(--font-primary); - font-size: 14px; - font-weight: 700; - line-height: 1.2857142857142858 -} - -.text-sm\/bold__2b1f5.fontScaling__2b1f5 { - font-size: .875rem -} - -.text-md\/normal__2b1f5 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.25 -} - -.text-md\/normal__2b1f5.fontScaling__2b1f5 { - font-size: 1rem -} - -.text-md\/medium__2b1f5 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.25 -} - -.text-md\/medium__2b1f5.fontScaling__2b1f5 { - font-size: 1rem -} - -.text-md\/semibold__2b1f5 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.25 -} - -.text-md\/semibold__2b1f5.fontScaling__2b1f5 { - font-size: 1rem -} - -.text-md\/bold__2b1f5 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.25 -} - -.text-md\/bold__2b1f5.fontScaling__2b1f5 { - font-size: 1rem -} - -.text-lg\/normal__2b1f5 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 400; - line-height: 1.2 -} - -.text-lg\/normal__2b1f5.fontScaling__2b1f5 { - font-size: 1.25rem -} - -.text-lg\/medium__2b1f5 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 500; - line-height: 1.2 -} - -.text-lg\/medium__2b1f5.fontScaling__2b1f5 { - font-size: 1.25rem -} - -.text-lg\/semibold__2b1f5 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 600; - line-height: 1.2 -} - -.text-lg\/semibold__2b1f5.fontScaling__2b1f5 { - font-size: 1.25rem -} - -.text-lg\/bold__2b1f5 { - font-family: var(--font-primary); - font-size: 20px; - font-weight: 700; - line-height: 1.2 -} - -.text-lg\/bold__2b1f5.fontScaling__2b1f5 { - font-size: 1.25rem -} - -.redesign\/message-preview\/normal__2b1f5 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 400; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/normal__2b1f5.fontScaling__2b1f5 { - font-size: .9375rem -} - -.redesign\/message-preview\/medium__2b1f5 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 500; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/medium__2b1f5.fontScaling__2b1f5 { - font-size: .9375rem -} - -.redesign\/message-preview\/semibold__2b1f5 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 600; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/semibold__2b1f5.fontScaling__2b1f5 { - font-size: .9375rem -} - -.redesign\/message-preview\/bold__2b1f5 { - font-family: var(--font-primary); - font-size: 15px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.redesign\/message-preview\/bold__2b1f5.fontScaling__2b1f5 { - font-size: .9375rem -} - -.redesign\/channel-title\/normal__2b1f5 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - line-height: 1.375 -} - -.redesign\/channel-title\/normal__2b1f5.fontScaling__2b1f5 { - font-size: 1rem -} - -.redesign\/channel-title\/medium__2b1f5 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 500; - line-height: 1.375 -} - -.redesign\/channel-title\/medium__2b1f5.fontScaling__2b1f5 { - font-size: 1rem -} - -.redesign\/channel-title\/semibold__2b1f5 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 600; - line-height: 1.375 -} - -.redesign\/channel-title\/semibold__2b1f5.fontScaling__2b1f5 { - font-size: 1rem -} - -.redesign\/channel-title\/bold__2b1f5 { - font-family: var(--font-primary); - font-size: 16px; - font-weight: 700; - line-height: 1.375 -} - -.redesign\/channel-title\/bold__2b1f5.fontScaling__2b1f5 { - font-size: 1rem -} - -.display-sm__2b1f5 { - font-family: var(--font-headline); - font-size: 20px; - font-weight: 800; - line-height: 1 -} - -.display-sm__2b1f5.fontScaling__2b1f5 { - font-size: 1.25rem -} - -.display-md__2b1f5 { - font-family: var(--font-headline); - font-size: 34px; - font-weight: 800; - line-height: 1.0588235294117647 -} - -.display-md__2b1f5.fontScaling__2b1f5 { - font-size: 2.125rem -} - -.display-lg__2b1f5 { - font-family: var(--font-headline); - font-size: 44px; - font-weight: 800; - line-height: .9545454545454546 -} - -.display-lg__2b1f5.fontScaling__2b1f5 { - font-size: 2.75rem -} - -.code__2b1f5 { - font-family: var(--font-code); - font-size: 12px; - font-weight: 700; - line-height: 1.3333333333333333 -} - -.code__2b1f5.fontScaling__2b1f5 { - font-size: .75rem -} - -.base__2b1f5 { - box-sizing: border-box; - color: var(--white); - flex: 0 0 auto; - height: 16px; - min-height: 16px; - min-width: 16px; - text-align: center; -} - -.textBadge__2b1f5 { - border-radius: var(--radius-sm); - overflow: hidden; - padding: 0 6px; - text-overflow: ellipsis; - white-space: nowrap -} - -.baseShapeRound__2b1f5 { - border-radius: var(--radius-round) -} - -.baseShapeRoundLeft__2b1f5 { - border-radius: var(--radius-xs) 0 0 var(--radius-xs) -} - -.baseShapeRoundRight__2b1f5 { - border-radius: 0 var(--radius-xs) var(--radius-xs) 0 -} - -.iconBadge__2b1f5,.numberBadge__2b1f5 { - align-items: center; - display: flex; - justify-content: center -} - -.iconBadge__2b1f5 { - width: 16px -} - -.icon__2b1f5 { - height: 100%; - padding: 2px; - width: 100% -} - -.circleBadge__2b1f5 { - height: 8px; - width: 8px -} - -.premiumBadge__2b1f5 { - background: var(--custom-premium-colors-premium-gradient-tier-2-tri-color) -} - -.limitedTimeText__2b1f5 { - text-transform: uppercase -} - -.enable-forced-colors .base__2b1f5 { - background-color: Highlight!important; - color: HighlightText; - forced-color-adjust: none; - outline: 2px solid Canvas -} - -.quickSelect_ebaca5 { - color: var(--interactive-text-default); - cursor: default; - font-size: 13px -} - -.quickSelectClick_ebaca5 { - cursor: pointer -} - -.quickSelectValue_ebaca5 { - margin-inline-start:4px} - -.quickSelectLabel_ebaca5 { - color: var(--text-muted); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.quickSelectArrow_ebaca5 { - height: 7px; - margin-inline-start:5px;width: 10px -} - -.quickSelectPopout_ebaca5 { - border-radius: 3px; - color: var(--text-strong); - overflow: hidden -} - -.quickSelectPopoutScroll_ebaca5 { - max-height: 384px -} - -.quickSelectScroller_ebaca5 { - overflow-y: auto -} - -.quickSelectPopoutOption_ebaca5 { - cursor: pointer; - padding: 8px -} - -.quickSelectPopoutOption_ebaca5.selected_ebaca5 { - background-color: inherit; - cursor: default -} - -.quickSelectPopoutOption_ebaca5:focus { - background-color: var(--interactive-background-hover); - color: var(--interactive-text-hover) -} - -.theme-light .quickSelectArrow_ebaca5 { - background: url(/assets/0bf989849f27d0a7.svg) 50% no-repeat -} - -.theme-light .quickSelectPopout_ebaca5 { - background-color: var(--white) -} - -.theme-light .quickSelectPopoutOption_ebaca5:hover { - background-color: var(--primary-100) -} - -.theme-dark .quickSelectArrow_ebaca5 { - background: url(/assets/77e059c86b79040e.svg) 50% no-repeat -} - -.theme-dark .quickSelectPopout_ebaca5 { - background: var(--primary-630) -} - -.theme-dark .quickSelectPopoutOption_ebaca5:hover { - background-color: hsl(var(--primary-500-hsl)/.3) -} - -.value__96f95 { - width: 48px -} - -.actions__96f95 { - align-items: center; - display: flex; - gap: var(--space-4); - justify-content: center -} - -.wrapper__00943 { - background-color: none; - border: 1px solid var(--border-subtle); - padding-inline:calc(var(--space-16) - 1px) calc(var(--space-16) - 2px)} - -.overlay__00943 { - align-items: center; - bottom: -1px; - box-sizing: border-box; - display: none; - inset-inline-end: 0; - justify-content: flex-end; - max-width: 100%; - padding-inline-start:50px;position: absolute; - top: -1px -} - -.overlay__00943:empty { - padding-inline-start:0} - -.headerOverlay__00943,.sectionOverlay__00943,.twitchOverlay__00943 { -} - -.headerOverlay__00943 { - inset-inline-end: 32px -} - -.section__00943 { - background-color: var(--background-mod-muted); - border: 1px solid var(--border-subtle); - padding: 12px; - position: relative -} - -.section__00943:not(:last-child) { - border-bottom: none -} - -.section__00943:first-child { - border-radius: 8px 8px 0 0 -} - -.section__00943:last-child { - border-radius: 0 0 8px 8px -} - -.section__00943:only-child { - border-radius: 8px -} - -.clickable__00943 { - cursor: pointer -} - -.clickable__00943:hover .textContent__00943 { - text-decoration: underline -} - -.textContent__00943 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.separator__00943 { - background-color: var(--border-subtle); - height: 1px -} - -.separator__00943.inset__00943 { - margin-inline:12px} - -.header__00943 { - align-items: center; - display: grid; - height: 36px; - position: relative; - grid-gap: 12px -} - -.headerFull__00943 { - grid-template-columns: 32px minmax(20px,auto) 24px -} - -.headerSimple__00943 { - grid-template-columns: 32px minmax(20px,auto) -} - -.headerAvatar__00943 { - cursor: pointer -} - -.headerIcon__00943,.multipleIconWrapper__00943 { - border-radius: 4px; - display: block; - height: 24px; - justify-self: end; - width: 24px -} - -.multipleIconWrapper__00943 { - align-items: center; - background-color: var(--background-mod-normal); - border-radius: 50%; - display: flex; - justify-content: center -} - -.multipleIcon__00943 { - height: 18px; - width: 18px -} - -.body__00943 { - border-radius: 8px -} - -.body__00943:not(:empty) { - margin-top: 12px -} - -.body__00943 .separator__00943 { - margin-bottom: 0; - margin-top: 0 -} - -.wrapper__00943 .body__00943 { - background: none -} - -.voiceSection__00943 { - display: grid; - grid-template-columns: 32px minmax(20px,auto) max-content; - grid-gap: 12px; - align-items: center -} - -.voiceSectionAssets__00943 { - align-items: center; - border-radius: 50%; - display: flex; - height: 32px; - justify-content: center; - position: relative; - width: 32px -} - -.voiceSectionDetails__00943 { - cursor: pointer -} - -.voiceSectionDetails__00943:hover .voiceSectionText__00943 { - text-decoration: underline -} - -.voiceSectionGuildImage__00943,.voiceSectionNoGuildImageWrapper__00943 { - border-radius: 50%; - cursor: pointer; - height: 32px; - -webkit-mask: url(/assets/a90b040155ee449f.svg); - mask: url(/assets/a90b040155ee449f.svg); - -webkit-mask-size: 100%; - mask-size: 100%; - mask-type: luminance; - width: 32px -} - -.voiceSectionNoGuildImageWrapper__00943 { - align-items: center; - background: var(--background-surface-high); - color: var(--white); - display: flex; - justify-content: center -} - -.voiceSectionNoGuildImage__00943 { - display: inline-block; - overflow: hidden -} - -.voiceSectionIconWrapper__00943 { - align-items: center; - border-radius: 50%; - bottom: -2px; - display: flex; - height: 16px; - inset-inline-end: -2px; - justify-content: center; - position: absolute; - width: 16px -} - -.voiceSectionIcon__00943 { - color: var(--text-default); - height: 10px; - width: 10px -} - -.applicationStreamingSection__00943 { - display: grid; - grid-template-columns: 32px minmax(20px,auto) max-content; - grid-gap: 12px; - align-items: center -} - -.applicationStreamingAvatar__00943 { - cursor: pointer -} - -.applicationStreamingPreviewWrapper__00943 { - align-items: center; - aspect-ratio: 16/9; - background-color: var(--background-mod-normal); - border-radius: 4px; - cursor: pointer; - display: flex; - justify-content: center; - margin-top: 12px; - overflow: hidden; - position: relative -} - -.applicationStreamingPreviewWrapper__00943:hover .applicationStreamingHoverText__00943 { - opacity: 1 -} - -.applicationStreamingPreviewSize__00943 { - height: 100%; - width: 100% -} - -.applicationStreamingHoverText__00943 { - background: var(--opacity-black-60); - border-radius: var(--radius-xl); - cursor: pointer; - opacity: 0; - padding: 7px 16px; - position: absolute -} - -.full-motion .applicationStreamingHoverText__00943 { - transition: opacity var(--custom-button-transition-duration) ease -} - -.xboxSection__00943 { - display: grid; - grid-template-columns: 32px minmax(20px,auto); - grid-gap: 12px; - align-items: center -} - -.xboxSectionIcon__00943 { - color: var(--primary-300); - height: 28px; - padding: 2px; - width: 28px -} - -.twitchSection__00943 { - min-height: 32px -} - -.twitchSectionHeader__00943 { - display: grid; - position: relative; - grid-gap: 12px; - align-items: center -} - -.twitchSectionSimple__00943 { - grid-template-columns: 32px minmax(20px,auto) -} - -.twitchSectionFull__00943 { - grid-template-columns: 32px minmax(20px,auto) 24px -} - -.twitchSectionIcon__00943 { - height: 32px; - width: 32px -} - -.twitchSectionPlayButton__00943 { - cursor: pointer; - inset-inline-start: calc(50% - 16px); - opacity: .75; - position: absolute; - top: calc(50% - 16px) -} - -.twitchSectionAvatar__00943 { - justify-self: end -} - -.twitchSectionPreviewWrapper__00943 { - cursor: pointer; - margin-top: 12px -} - -.twitchSectionPreviewWrapper__00943:hover .twitchSectionPlayButton__00943 { - opacity: 1 -} - -.twitchSectionPreview__00943 { - border-radius: 4px; - height: 100%; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.gameSection__00943 { - display: grid; - grid-template-columns: 32px minmax(20px,auto) max-content; - grid-gap: 12px; - align-items: center -} - -.gameSectionIcon__00943 { - cursor: pointer; - height: 32px; - width: 32px -} - -.activitySection__00943 { - display: grid; - grid-template-columns: 32px minmax(20px,auto); - grid-gap: 12px; - align-items: start -} - -.activitySectionWithButtons__00943 { - grid-template-columns: 32px minmax(20px,auto) minmax(0,max-content) -} - -.activitySectionAssets__00943 { - align-items: center; - display: grid; - justify-content: start; - position: relative -} - -.activitySectionAssets__00943 .largeImageMask__00943 { - -webkit-mask: url(/assets/ed4c6d51411aad5e.svg); - mask: url(/assets/ed4c6d51411aad5e.svg); - -webkit-mask-size: 100%; - mask-size: 100%; - mask-type: luminance -} - -.activitySectionAssets__00943 .largeImage__00943 { - border-radius: 4px; - height: 32px; - -o-object-fit: cover; - object-fit: cover; - width: 32px -} - -.activitySectionAssets__00943 .borderRadius0__00943 { - border-radius: 0 -} - -.activitySectionAssets__00943 .smallImage__00943 { - border-radius: 50%; - bottom: -4px; - height: 16px; - inset-inline-end: -4px; - position: absolute; - width: 16px -} - -.activitySectionDetails__00943 { - display: flex; - flex-direction: column; - height: 100%; - justify-content: center -} - -.spotifySection__00943 { - grid-template-columns: 32px minmax(20px,auto) max-content -} - -.theme-dark .voiceSectionIconWrapper__00943 { - background-color: var(--primary-800) -} - -.theme-light .voiceSectionIconWrapper__00943 { - background: var(--primary-300) -} - -.embeddedActivityTopRow__00943 { - align-items: center; - display: flex; - margin-bottom: 8px -} - -.embeddedActivityName__00943 { - flex: 1 -} - -.embeddedActivityTimeElapsed__00943 { - flex: 0 0 auto -} - -.embeddedActivityPlayerContainer__00943 { - align-items: center; - aspect-ratio: 16/9; - background: var(--black); - border-radius: 12px; - display: flex; - justify-content: center; - position: relative -} - -.embeddedActivityImage__00943 { - border-radius: 12px; - height: auto; - max-height: 100%; - -o-object-fit: cover; - object-fit: cover; - width: 100% -} - -.embeddedActivityImageOverlay__00943 { - align-items: center; - background: var(--opacity-black-60); - border-radius: 12px; - display: flex; - flex-direction: column; - height: 100%; - justify-content: center; - position: absolute; - width: 100% -} - -.embeddedActivityJoinWrapper__00943 { - margin-top: 8px -} - -.embeddedActivityIcon__00943 { - margin-inline-end:8px} - -.cloudPlaySectionSeparator__00943 { - margin: 12px 0!important -} - -.cloudPlaySection__00943 { - align-items: center; - display: flex; - gap: 12px; - justify-content: space-between -} - -.cloudPlaySectionTextContainer__00943 { - align-items: center; - display: flex; - gap: 8px -} - -.refresh-active-now .wrapper__00943 { - background: var(--background-base-low); - border-radius: 16px; - padding: 0 -} - -.refresh-active-now .body__00943 { - border-radius: 0 -} - -.refresh-active-now .body__00943:not(:empty) { - margin-top: 0 -} - -.refresh-active-now .header__00943 { - background-color: var(--background-mod-muted); - padding: 16px -} - -.refresh-active-now .section__00943 { - background: none; - border: none; - border-radius: 0; - padding: 16px -} - -.refresh-active-now .section__00943:not(:last-child) { - border-bottom: 1px solid var(--border-subtle) -} - -.refresh-active-now .applicationStreamingPreviewWrapper__00943 { - border-radius: 8px -} - -.titleBar__421ed { - z-index: 3001; - -webkit-app-region: drag; - flex-shrink: 0 -} - -.withFrame__421ed { - height: 18px; - margin-top: 4px -} - -.typeMacOS__421ed { - position: absolute; - width: 72px -} - -.typeMacOS__421ed .macDragRegion__421ed { - padding-bottom: 32px; - width: 70px -} - -.typeMacOSWithFrame__421ed { - align-items: center; - display: flex; - flex-direction: row; - height: 24px; - justify-content: space-between; - margin-top: 0 -} - -.typeMacOSWithFrame__421ed .macButtons__421ed { - margin-top: -2px; - margin-inline-end:-70px} - -.typeWindows__421ed { - align-items: stretch; - display: flex; - flex-direction: row-reverse; - justify-content: flex-start -} - -.wordmark__421ed { - font-size: 0; - pointer-events: none -} - -.wordmarkWindows__421ed { - color: var(--text-muted); - inset-inline-start: 0; - padding: 4px 9px 3px; - position: absolute; - top: 0 -} - -.wordmarkMacOS__421ed { - margin: 0 auto -} - -.winButton__421ed { - align-items: center; - cursor: pointer; - display: flex; - height: 22px; - justify-content: center; - pointer-events: auto; - position: relative; - top: -4px; - width: 28px; - -webkit-app-region: no-drag -} - -.winButtonMinMax__421ed { -} - -.winButtonMinMax__421ed:hover { - background-color: var(--interactive-background-hover); - color: var(--interactive-text-hover) -} - -.winButtonMinMax__421ed:active { - background-color: var(--interactive-background-active); - color: var(--interactive-text-active) -} - -.winButtonClose__421ed { -} - -.winButtonClose__421ed:hover { - background-color: var(--background-feedback-critical); - color: var(--white) -} - -.winButton__421ed { - color: var(--interactive-text-default) -} - -.macButtons__421ed { - align-items: center; - box-sizing: border-box; - display: flex; - flex-direction: row; - justify-content: space-between; - padding: 10px; - width: 70px -} - -.macButtons__421ed svg { - display: block; - visibility: hidden -} - -.focused__421ed .macButtons__421ed:hover svg { - visibility: visible -} - -.macButton__421ed { - border-radius: 50%; - box-sizing: border-box; - height: 12px; - width: 12px; - -webkit-app-region: no-drag -} - -.macButtonClose__421ed,.macButtonMaximize__421ed,.macButtonMinimize__421ed { -} - -.macButtonMaximize__421ed { - background-color: #34c749 -} - -.macButtonMinimize__421ed { - background-color: #fdbc40 -} - -.macButtonClose__421ed { - background-color: #fc615d -} - -.unfocused__421ed .macButtonClose__421ed,.unfocused__421ed .macButtonMaximize__421ed,.unfocused__421ed .macButtonMinimize__421ed { - background-color: var(--background-mod-subtle) -} - -.custom-theme-background .withBackgroundOverride__421ed { - background: var(--background-gradient-app-frame,var(--background-base-lowest)) -} - -.custom-theme-background .withBackgroundOverride__421ed.typeWindows__421ed { - margin-top: 0; - padding-top: 4px -} - -.wrapper__61a6b { - background-color: var(--background-base-lower); - background-position: 50% 0; - background-size: cover; - color: var(--text-default); - min-height: 100vh; - overflow: hidden -} - -.flexWrapper__61a6b { - height: 100vh -} - -.image__61a6b { - background-size: 100% 100%; - height: 154px; - margin-bottom: 40px; - width: 254px -} - -.text__61a6b { - margin-bottom: 20px; - width: 440px -} - -.note__61a6b { - color: var(--text-muted); - font-size: 16px -} - -.note__61a6b,.title__61a6b { - text-align: center -} - -.title__61a6b { - color: var(--text-strong); - font-size: 24px; - font-weight: var(--font-weight-semibold); - line-height: 8px; - margin-bottom: 24px -} - -.images-light .wrapper__61a6b { - background-image: url(/assets/069714e321aa543f.svg) -} - -.images-light .image__61a6b { - background-image: url(/assets/f9b5479cf5959262.svg) -} - -.images-dark .wrapper__61a6b { - background-image: url(/assets/069714e321aa543f.svg) -} - -.images-dark .image__61a6b { - background-image: url(/assets/4ced53d842c0ca67.svg) -} - -.tile__90dc5 { - background-color: var(--primary-700); - border-radius: var(--custom-base-tile-border-radius); - box-sizing: border-box; - overflow: hidden; - position: relative -} - -.tile__90dc5.noBorder__90dc5 { - border-radius: 0 -} - -.fillParent__078ba { - display: block; - height: 100%; - overflow: clip; - width: 100% -} - -.hiddenIframeContainer__078ba { - opacity: 0 -} - -.iframePlaceholder__078ba { - background-color: #000 -} - -.popoutWrapper_d6b206 { - display: flex; - margin-bottom: 10px; - max-height: 40vh; - position: relative; - width: 200px -} - -.scroller_d6b206 { - background-color: var(--background-surface-high); - border-radius: 4px; - padding: 8px 16px -} - -.viewers_d6b206 { - align-items: center; - display: flex; - flex-grow: 0; - position: relative -} - -.viewers_d6b206:hover .popoutWrapper_d6b206 { - opacity: 1; - transform: translateX(0); - visibility: visible -} - -.viewer_d6b206 { - margin-inline-start:-2px;-webkit-mask: url(/assets/eae6388e2d5a721a.svg); - mask: url(/assets/eae6388e2d5a721a.svg); - -webkit-mask-size: 100%; - mask-size: 100%; - mask-type: luminance -} - -.viewer_d6b206:last-child { - -webkit-mask: none; - mask: none -} - -.overflow_d6b206 { - align-items: center; - background-color: var(--primary-600); - border-radius: 15px; - box-shadow: -1px 0 0 1px var(--black); - color: var(--primary-300); - display: flex; - font-size: 12px; - font-weight: var(--font-weight-bold); - height: 25px; - justify-content: center; - letter-spacing: .5px; - margin-inline-start:-4px;width: 25px; - z-index: 2 -} - -.memberListHeader_d6b206 { - font-weight: var(--font-weight-semibold); - margin-bottom: 12px; - margin-top: 8px; - text-transform: uppercase -} - -.memberListHeader_d6b206,.memberListItemText_d6b206 { - color: var(--text-default) -} - -.viewersIcon_d6b206 { - color: var(--primary-300); - display: flex; - height: 24px; - margin-inline-end:4px;width: 24px -} - -.viewersIcon_d6b206.activeButton_d6b206,.viewersIcon_d6b206:hover { - color: var(--white) -} - -.viewersTooltipItem_d6b206 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.memberListItem_d6b206:not(.popoutDisabled_d6b206):hover { - background-color: var(--background-base-lowest) -} - -.videoHeight__94748 { - flex: 0 0 auto; - position: relative -} - -.videoHeight__94748.animated__94748 { - transition: height .35s ease -} - -.videoHeight__94748.normal__94748 { - height: 50vh -} - -.videoHeight__94748.minimum__94748 { - height: 275px -} - -.videoHeight__94748.haven__94748,.videoHeight__94748.noChat__94748 { - height: 100vh -} - -.videoHeight__94748.fullScreen__94748 { - height: 100vh; - position: fixed; - top: 0; - inset-inline: 0; - bottom: 0; - transition: none; - z-index: 100 -} - -.platform-win .videoHeight__94748.normal__94748 { - height: calc(50vh - 22px) -} - -.platform-win .videoHeight__94748.noChat__94748 { - height: calc(100vh - 22px) -} - -.video__94748 { - background-color: var(--primary-800); - background-position: 50%; - background-size: 100%; - cursor: default; - height: 100%; - min-height: 275px; - overflow: hidden; - position: relative; - width: 100% -} - -.video__94748.idle__94748 { - cursor: none -} - -.video__94748.fullScreen__94748 .videoCenter__94748,.video__94748.noChat__94748 .videoCenter__94748,.video__94748.normal__94748 .videoCenter__94748 { - align-self: flex-end -} - -.video__94748.fullScreen__94748 .videoBottom__94748,.video__94748.noChat__94748 .videoBottom__94748,.video__94748.normal__94748 .videoBottom__94748 { - opacity: 0; - transform: translate3d(0,8px,0) -} - -.full-motion .video__94748.fullScreen__94748 .videoBottom__94748,.full-motion .video__94748.noChat__94748 .videoBottom__94748,.full-motion .video__94748.normal__94748 .videoBottom__94748 { - transition: transform .2s ease-in-out,opacity .2s ease-in-out -} - -.video__94748.fullScreen__94748:not(.idle__94748) .videoWrapper__94748.focused__94748 .videoBottom__94748,.video__94748.fullScreen__94748:not(.idle__94748) .videoWrapper__94748:hover .videoBottom__94748,.video__94748.noChat__94748:not(.idle__94748) .videoWrapper__94748.focused__94748 .videoBottom__94748,.video__94748.noChat__94748:not(.idle__94748) .videoWrapper__94748:hover .videoBottom__94748,.video__94748.normal__94748:not(.idle__94748) .videoWrapper__94748.focused__94748 .videoBottom__94748,.video__94748.normal__94748:not(.idle__94748) .videoWrapper__94748:hover .videoBottom__94748 { - opacity: 1; - transform: translateZ(0) -} - -.video__94748 .videoBackgroundTransition__94748 { - box-shadow: var(--legacy-elevation-high) -} - -.videoWrapper__94748 { - border-radius: 3px; - position: relative -} - -.videoInner__94748 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - overflow: hidden; - pointer-events: none; - z-index: 1 -} - -.videoBottom__94748,.videoCenter__94748,.videoTop__94748 { - pointer-events: all -} - -.videoBackground__94748,.videoBackgroundTransition__94748 { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - height: 100%; - width: 100% -} - -.videoBackgroundTransition__94748 { - align-items: center; - display: flex; - overflow: hidden -} - -.theme-light .video__94748.minimum__94748 { - background-color: var(--white); - box-shadow: inset 0 -1px 0 var(--primary-230) -} - -.images-light .video__94748.noChat__94748 .videoTop__94748,.images-light .video__94748.normal__94748 .videoTop__94748 { - background-image: linear-gradient(0deg,hsl(var(--black-hsl)/0) 0,var(--opacity-black-88) 100%) -} - -.images-dark .video__94748.noChat__94748 .videoTop__94748,.images-dark .video__94748.normal__94748 .videoTop__94748 { - background-image: linear-gradient(0deg,hsl(var(--black-hsl)/0) 0,var(--opacity-black-88) 100%) -} - -.pictureInPictureVideo_e4cb9a { - aspect-ratio: 16/9; - background: var(--primary-800); - border-radius: 8px; - overflow: hidden; - transform: translateZ(0) -} - -.videoControls_e4cb9a { - background: linear-gradient(var(--black) 0,transparent 30%,transparent 60%,var(--black) 100%); - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - display: flex; - flex-direction: column; - justify-content: space-between; - padding: 8px; - pointer-events: none; - transition: opacity .2s ease-in-out -} - -.videoControlsTextActivity_e4cb9a { - pointer-events: all; - z-index: 2 -} - -.topControls_e4cb9a { - flex: 0 1 auto; - pointer-events: all; - transform: translateZ(0); - width: 100% -} - -.full-motion .topControls_e4cb9a { - transition: transform .2s ease-in-out -} - -.topControlsTextActivity_e4cb9a { - align-items: flex-start; - flex-direction: row; - height: 100% -} - -.bottomControls_e4cb9a,.topControlsTextActivity_e4cb9a { - display: flex; - justify-content: space-between -} - -.bottomControls_e4cb9a { - align-items: center; - color: var(--white); - flex: 0 1 auto; - pointer-events: all; - transform: translateZ(0); - width: 100% -} - -.full-motion .bottomControls_e4cb9a { - transition: transform .2s ease-in-out -} - -.bottomLeftControls_e4cb9a { - min-width: 0 -} - -.idle_e4cb9a .videoControls_e4cb9a { - opacity: 0 -} - -.idle_e4cb9a .topControls_e4cb9a { - transform: translate3d(0,-8px,0) -} - -.idle_e4cb9a .bottomControls_e4cb9a { - transform: translate3d(0,8px,0) -} - -.backButton_e4cb9a { - color: var(--primary-300); - flex-shrink: 0; - margin-inline-end:2px} - -.headerBar_e4cb9a { - height: auto; - padding: 4px 0 0 -} - -.topControls_e4cb9a .headerBar_e4cb9a { - -webkit-app-region: no-drag -} - -.headerIdleContainer_e4cb9a { - justify-content: space-between -} - -.headerIdleContainer_e4cb9a,.headerTitle_e4cb9a { - display: flex; - flex-shrink: 1; - overflow: hidden -} - -.headerTitle_e4cb9a { - align-items: center; - border-bottom: 1px solid transparent; - color: var(--white); - justify-content: flex-start; - line-height: 20px; - margin: 0; - width: -moz-min-content; - width: min-content -} - -.headerTitle_e4cb9a:hover { - border-bottom: 1px solid var(--white); - cursor: pointer -} - -.headerTitle_e4cb9a:hover .backButton_e4cb9a { - color: var(--white) -} - -.headerText_e4cb9a { - flex-shrink: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.menuIcon_e4cb9a { - cursor: pointer -} - -.keyboard-mode .videoControls_e4cb9a { - opacity: 1 -} - -.art_c6e099 { - background-position: 50%; - background-repeat: no-repeat; - background-size: contain; - margin-bottom: 16px; - width: 100% -} - -.art_c6e099.small_c6e099 { - display: none -} - -.art_c6e099.medium_c6e099 { - height: 88px -} - -.art_c6e099.large_c6e099 { - height: 134px -} - -.header_c6e099 { - color: var(--white); - margin-bottom: 4px; - text-align: center -} - -.errorCodeMessage_c6e099 { - position: relative; - z-index: 1 -} - -.description_c6e099 { - color: var(--primary-200); - text-align: center -} - -.description_c6e099 a { - position: relative; - z-index: 1 -} - -.description_c6e099 p { - margin: 0 -} - -.outerButton_c6e099 { - margin-top: 16px; - z-index: 1 -} - -.root_e605a1 { - background-color: var(--opacity-black-60); - bottom: 0; - position: absolute; - top: 0; - inset-inline: 0; - pointer-events: none -} - -.popIn_c7da74 { - transform: rotate(-180deg) -} - -.participantsButton__211d1 { - background: var(--primary-630); - border-radius: 48px; - box-shadow: var(--shadow-border),var(--shadow-high); - padding: 4px -} - -.participantsButton__211d1:hover { - background: var(--primary-700) -} - -.rightCaret__211d1 { - transform: rotate(-90deg) -} - -.leftCaret__211d1 { - transform: rotate(90deg) -} - -.upCaret__211d1 { - transform: rotate(180deg) -} - -.downCaret__211d1 { - transform: rotate(0deg) -} - -.members__211d1 { - margin-inline-end:4px} - -.iconContainer__211d1 { - display: flex -} - -.iconContainer__211d1>svg { - color: var(--white) -} - -.fauxDisabled__666fa { - opacity: .5 -} - -.enable-forced-colors .fauxDisabled__666fa { - opacity: 1 -} - -.container__183c2 { - align-items: center; - display: flex -} - -.icon__183c2 { - color: var(--text-muted); - flex: 0 0 auto; - height: 20px; - margin-inline:-2px calc(var(--space-8) - 2px);width: 20px -} - -.size56__183c2 .avatarContainer__183c2 { - height: 56px; - width: 56px -} - -.size56__183c2 .avatarContainerMasked__183c2 { - height: 56px; - margin-inline-end:-14px;width: 56px -} - -.size56__183c2 .clickableAvatar__183c2,.size56__183c2 .emptyUser__183c2 { - height: 56px; - width: 56px -} - -.size32__183c2 .avatarContainer__183c2 { - height: 32px; - width: 32px -} - -.size32__183c2 .avatarContainerMasked__183c2 { - height: 32px; - margin-inline-end:-8px;width: 32px -} - -.size32__183c2 .clickableAvatar__183c2,.size32__183c2 .emptyUser__183c2 { - height: 32px; - width: 32px -} - -.size24__183c2 .avatarContainer__183c2 { - height: 24px; - width: 24px -} - -.size24__183c2 .avatarContainerMasked__183c2 { - height: 24px; - margin-inline-end:-6px;width: 24px -} - -.size24__183c2 .clickableAvatar__183c2,.size24__183c2 .emptyUser__183c2 { - height: 24px; - width: 24px -} - -.size16__183c2 .avatarContainer__183c2 { - height: 16px; - width: 16px -} - -.size16__183c2 .avatarContainerMasked__183c2 { - height: 16px; - margin-inline-end:-3px;width: 16px -} - -.size16__183c2 .clickableAvatar__183c2,.size16__183c2 .emptyUser__183c2 { - height: 16px; - width: 16px -} - -.clickableAvatar__183c2 { - border-radius: 50%; - cursor: pointer -} - -.emptyUser__183c2 { - background: var(--background-mod-muted); - border-radius: 50% -} - -.emptyUserDimmed__183c2 { - background: var(--background-scrim) -} - -.avatarWithBackground__183c2 { - align-items: center; - background-color: var(--opacity-black-52); - display: flex; - justify-content: center; - position: relative -} - -.avatar__183c2,.avatarWithBackground__183c2 { - border-radius: 50%; - height: 100%; - width: 100% -} - -.moreUsers__183c2 { - align-items: center; - background-color: var(--background-base-lowest); - box-sizing: border-box; - color: var(--text-default); - display: flex; - font-weight: var(--font-weight-semibold); - justify-content: center; - line-height: 1; - text-align: center -} - -.moreUsers__183c2.moreUsersDimmed__183c2 { - background-color: var(--background-scrim) -} - -.size56__183c2 .moreUsers__183c2 { - border-radius: 28px; - font-size: 24px; - height: 56px; - min-width: 56px; - padding: 0 8px -} - -.size32__183c2 .moreUsers__183c2 { - border-radius: 16px; - font-size: 12px; - height: 32px; - min-width: 32px; - padding: 0 8px -} - -.size24__183c2 .moreUsers__183c2 { - border-radius: 12px; - font-size: 12px; - height: 24px; - min-width: 24px; - padding: 0 8px -} - -.size16__183c2 .moreUsers__183c2 { - border-radius: 8px; - font-size: 10px; - height: 16px; - min-width: 16px; - padding: 0 4px -} - -.arrowIcon_f41a99 { - height: 24px; - transform: rotate(270deg); - width: 24px -} - -.arrowIconExpanded_f41a99 { - transform: rotate(1turn) -} - -.rightPipIcon_f41a99 { - margin-inline-start:16px} - -.leftPipIcon_f41a99 { - margin-inline-end:16px} - -.participantsButton_f41a99 { - inset-inline-end: 16px; - position: absolute; - top: 50%; - transform: translateY(-50%) -} - -.pipHeader_f41a99 { - height: 12px; - position: relative; - width: 100%; - z-index: 2 -} - -.pipHeaderContent_f41a99 { - align-items: center; - background-color: var(--black); - box-sizing: border-box; - display: flex; - height: 12px; - justify-content: space-between; - max-width: 100%; - padding: 0 8px; - position: absolute -} - -.full-motion .pipHeaderContent_f41a99 { - transition: all .2s ease-in-out -} - -.pipHeaderContentOpen_f41a99 { - background: linear-gradient(var(--black) 0,var(--black) 30%,transparent 100%); - cursor: pointer; - height: 40px; - padding: 8px -} - -.pipHeaderButtonsRight_f41a99 { - display: flex -} - -.menuIcon_f41a99 { - color: #fff -} - -.leaveActivityIcon_f41a99 { - height: 24px; - margin-inline-start:6px;width: 24px -} - -.root_c5bcbe { - height: 100%; - width: 100% -} - -.root_c5bcbe.pipMode_c5bcbe { - transition: height .2s ease-in-out; - width: 320px -} - -.root_c5bcbe.pipModeShort_c5bcbe { - height: 180px -} - -.root_c5bcbe.pipModeTall_c5bcbe { - height: 410px -} - -.root_c5bcbe.hidden_c5bcbe { - max-height: 0; - transition: none; - width: 0 -} - -.iframe_c5bcbe { - height: 100%; - min-height: 2px; - min-width: 2px; - width: 100% -} - -.iframe_c5bcbe.pipModeShort_c5bcbe { - height: 180px -} - -.iframe_c5bcbe.pipModeTall_c5bcbe { - height: 410px -} - -.pipNonInteractive_c5bcbe { - pointer-events: none -} - -.clickShield_c5bcbe { - inset: 0; - position: absolute -} - -.multiPIPMode_c5bcbe { - margin-top: 8px -} - -:root { - --legacy-elevation-low: 0 1px 5px var(--opacity-black-20); - --legacy-elevation-high: 0 2px 10px 0 var(--opacity-black-8); - --legacy-elevation-border: 0 0 0 1px hsl(var(--primary-300-hsl)/0.3) -} - -.theme-dark { - --legacy-elevation-low: 0 1px 5px 0 var(--opacity-black-28); - --legacy-elevation-high: 0 2px 10px 0 var(--opacity-black-20); - --legacy-elevation-border: 0 0 0 1px hsl(var(--primary-700-hsl)/0.6) -} - -.elevationLow__2b2f1 { - box-shadow: var(--legacy-elevation-low) -} - -.elevationHigh__2b2f1 { - box-shadow: var(--legacy-elevation-high) -} - -.elevationBorderLow__2b2f1 { - box-shadow: var(--legacy-elevation-border),var(--legacy-elevation-low) -} - -.darkElevationBorderHigh__2b2f1 { - box-shadow: var(--legacy-elevation-border),var(--legacy-elevation-high) -} - -.lightElevationLow__2b2f1 { - box-shadow: var(--legacy-elevation-low) -} - -.lightElevationHigh__2b2f1 { - box-shadow: var(--legacy-elevation-high) -} - -.lightElevationBorderLow__2b2f1 { - box-shadow: var(--legacy-elevation-border),var(--legacy-elevation-low) -} - -.elevationBorderHigh__2b2f1,.elevationBorderLow__2b2f1,.elevationHigh__2b2f1,.elevationLow__2b2f1,.lightElevationBorderHigh__2b2f1 { - box-shadow: var(--legacy-elevation-border),var(--legacy-elevation-high) -} - -.closeButton__47998 { - inset-inline-end: 14px; - position: absolute; - top: 14px -} - -.art__47998 { - background-position: 50%; - background-repeat: no-repeat; - background-size: contain -} - -.description__47998 { - line-height: 1.44; - margin-bottom: 20px -} - -.conflictButton__47998 { - background-color: var(--background-base-lower); - border: 1px solid; - height: 100px; - justify-content: flex-start; - padding: 20px; - width: 100% -} - -.linkButtonSize__47998 { - height: 38px; - width: auto -} - -.linkButton__47998 { - padding: 0 -} - -.linkButton__47998:hover { - text-decoration: underline -} - -.retryButton__47998 { - margin-inline-end:8px} - -.conflictButtonInner__47998 { - align-items: center; - display: flex; - width: 100% -} - -.title__47998 { - color: var(--primary-300); - font-size: 12px; - font-weight: var(--font-weight-bold); - letter-spacing: .5px; - text-transform: uppercase -} - -.buttonBody__47998 { - color: var(--text-default); - font-size: 16px; - line-height: 1.25; - text-align: start -} - -.timestamp__47998 { - font-weight: var(--font-weight-bold) -} - -.choiceWrapper__47998 { - margin-bottom: 20px; - margin-top: 20px -} - -.choiceLine__47998 { - height: 1px; - width: 100% -} - -.choiceTitle__47998 { - margin: 0 8px -} - -.conflictTitle__47998 { - margin-bottom: 4px -} - -.conflictArt__47998 { - height: 60px; - margin-inline-end:20px;width: 60px -} - -.conflictDownloadArt__47998,.conflictUploadArt__47998 { -} - -.errorArt__47998 { - height: 123px; - margin-bottom: 40px; - margin-top: 20px; - width: 220px -} - -.theme-dark .conflictButton__47998 { - border-color: var(--primary-700) -} - -.theme-dark .conflictButton__47998:hover { - background-color: var(--primary-500) -} - -.theme-dark .choiceLine__47998 { - background-color: hsl(var(--primary-500-hsl)/.6) -} - -.theme-light .conflictButton__47998 { - border-color: hsl(var(--primary-200-hsl)/.6) -} - -.theme-light .conflictButton__47998:hover { - background-color: hsl(var(--primary-200-hsl)/.3); - border-color: hsl(var(--primary-300-hsl)/.6) -} - -.theme-light .choiceLine__47998 { - background-color: hsl(var(--primary-200-hsl)/.6) -} - -.modal__47998 { - color: var(--text-default) -} - -.images-light .conflictUploadArt__47998 { - background-image: url(/assets/c48922675fa3fdb4.svg) -} - -.images-light .conflictDownloadArt__47998 { - background-image: url(/assets/7eb5ad9165537ef6.svg) -} - -.images-light .errorArt__47998 { - background-image: url(/assets/eaf420114113c0bc.svg) -} - -.images-dark .conflictUploadArt__47998 { - background-image: url(/assets/c9d06b948b249c3a.svg) -} - -.images-dark .conflictDownloadArt__47998 { - background-image: url(/assets/cc1962559d37a6d1.svg) -} - -.images-dark .errorArt__47998 { - background-image: url(/assets/49e200b52c0a66ab.svg) -} - -@supports (grid-template-columns: subgrid) and (white-space-collapse:collapse) { - .bar_c38106 { - grid-area:titleBar - } -} - -.bar_c38106 { - align-content: center; - align-items: center; - display: flex; - gap: var(--space-8); - justify-content: space-between; - min-height: var(--custom-app-top-bar-height); - padding-inline-end:var(--space-12);position: relative; - -webkit-app-region: drag -} - -.platform-osx .bar_c38106 { - padding-inline-start:calc(100/var(--custom-zoom, 100)*(var(--custom-guild-list-width) + var(--space-md)))} - -.platform-win .bar_c38106 { - padding-inline-end: 0 -} - -.systemBar_c38106 { - display: none; - justify-content: flex-end; - z-index: 999999 -} - -.show_c38106 { - display: flex -} - -.fixed_c38106 { - position: fixed; - top: 0; - inset-inline: 0 var(--devtools-sidebar-width) -} - -.title_c38106 { - justify-content: center; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.leading_c38106,.title_c38106 { - align-items: center; - display: flex -} - -.leading_c38106 { - box-sizing: border-box; - justify-content: flex-start -} - -.leading_c38106,.trailing_c38106 { - gap: var(--space-12); - position: relative; - z-index: 1; - -webkit-app-region: no-drag -} - -.trailing_c38106,.winButtons_c38106 { - align-items: center; - display: flex; - justify-content: flex-end -} - -.winButtons_c38106 { - color: var(--interactive-text-default); - gap: var(--space-4) -} - -.winButtonsWithDivider_c38106:before { - border-inline-start:1px solid var(--border-subtle);content: ""; - display: block; - height: calc(var(--custom-app-top-bar-height) - var(--space-12)); - margin-inline:var(--space-4)} - -.winButton_c38106 { - align-items: center; - cursor: pointer; - display: flex; - height: var(--custom-app-top-bar-height); - justify-content: center; - width: var(--custom-app-top-bar-height) -} - -.winButtonMinMax_c38106:hover { - background-color: var(--interactive-background-hover); - color: var(--interactive-text-hover) -} - -.winButtonMinMax_c38106:active { - background-color: var(--interactive-background-active); - color: var(--interactive-text-active) -} - -.winButtonClose_c38106 { -} - -.winButtonClose_c38106:hover { - background-color: var(--control-critical-primary-background-default); - color: var(--control-critical-primary-icon-default) -} - -.clouds__51c01 { - bottom: 0; - top: 0; - inset-inline: 0 -} - -.cloud__51c01,.clouds__51c01 { - position: absolute -} - -.cloud__51c01 { - opacity: 0; - transform: scale(.5) -} - -.full-motion .cloud__51c01 { - animation: cloud__51c01 210s linear infinite -} - -@keyframes cloud__51c01 { - 0% { - inset-inline-start: 480px; - opacity: 0; - transform: translate(0) scale(.85) - } - - 15% { - opacity: 1 - } - - 35% { - opacity: .65 - } - - 50% { - opacity: .85; - transform: translateY(32px) - } - - 70% { - opacity: .75 - } - - 85% { - opacity: 1 - } - - to { - inset-inline-start: 1220px; - opacity: 0; - transform: translateY(24px) scale(.75) - } -} - -.wrapper__49d3a { - position: absolute -} - -.wrapper__49d3a:hover .vingette__49d3a { - opacity: 50% -} - -.character__49d3a { - --custom-haven-participant-actions-offset-top: 80px; - --custom-haven-avatar-offset: 48px; - cursor: pointer; - pointer-events: all; - position: absolute -} - -.character__49d3a.flip__49d3a { - transform: scaleX(-1) -} - -.vingette__49d3a { - background: radial-gradient(#fff,transparent); - background-repeat: no-repeat; - border-radius: 30%; - filter: blur(8px); - inset-inline-start: -15%; - opacity: 0; - pointer-events: none; - position: absolute; - top: -15%; - transition: opacity .15s linear -} - -.participant__0e7cc { - pointer-events: none -} - -.participant__0e7cc.hovering__0e7cc .actions__0e7cc,.participant__0e7cc:hover .actions__0e7cc { - opacity: 1; - pointer-events: all -} - -.avatar__0e7cc { - --custom-haven-participant-actions-offset-top: 80px; - --custom-haven-avatar-offset: 48px; - cursor: pointer; - pointer-events: all; - position: absolute -} - -.seat__68e91 { - --custom-haven-seat-width: 180px; - position: absolute; - width: var(--custom-haven-seat-width) -} - -.seat__68e91:not(.claimed__68e91) { - cursor: pointer -} - -.seatWrapper__68e91 { - inset-inline-start: 15%; - position: absolute -} - -.image__68e91 { - --custom-outline-width: 2px; - --custom-negative-outline-width: calc(var(--custom-outline-width)*-1); - filter: drop-shadow(var(--custom-outline-width) 0 0 transparent) drop-shadow(var(--custom-negative-outline-width) 0 0 transparent) drop-shadow(0 var(--custom-outline-width) 0 transparent) drop-shadow(0 var(--custom-negative-outline-width) 0 transparent); - inset-inline-start: 0; - position: absolute; - will-change: filter -} - -.image__68e91:not(.shadow__68e91) { - animation: nux__68e91 1.5s ease -} - -.image__68e91.noAnimation__68e91 { - animation: none -} - -.image__68e91.shadow__68e91 { - bottom: 0; - pointer-events: none -} - -.image__68e91:hover { - filter: drop-shadow(var(--custom-outline-width) 0 0 white) drop-shadow(var(--custom-negative-outline-width) 0 0 white) drop-shadow(0 var(--custom-outline-width) 0 white) drop-shadow(0 var(--custom-negative-outline-width) 0 white) -} - -.image__68e91.claimed__68e91:hover,.image__68e91.shadow__68e91:hover { - filter: drop-shadow(var(--custom-outline-width) 0 0 transparent) drop-shadow(var(--custom-negative-outline-width) 0 0 transparent) drop-shadow(0 var(--custom-outline-width) 0 transparent) drop-shadow(0 var(--custom-negative-outline-width) 0 transparent) -} - -.pulse__68e91 { - opacity: 1; - transition: opacity .2s ease; - width: var(--custom-haven-seat-width) -} - -.pulse__68e91.idle__68e91,.pulse__68e91.occupied__68e91 { - opacity: 0 -} - -@keyframes nux__68e91 { - 0%,to { - filter: drop-shadow(var(--custom-outline-width) 0 0 transparent) drop-shadow(var(--custom-negative-outline-width) 0 0 transparent) drop-shadow(0 var(--custom-outline-width) 0 transparent) drop-shadow(0 var(--custom-negative-outline-width) 0 transparent) - } - - 50% { - filter: drop-shadow(var(--custom-outline-width) 0 0 white) drop-shadow(var(--custom-negative-outline-width) 0 0 white) drop-shadow(0 var(--custom-outline-width) 0 white) drop-shadow(0 var(--custom-negative-outline-width) 0 white) - } -} - -.star_a54554 { - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.row__5adc3 { - display: flex; - flex-direction: row; - gap: 8px -} - -.havenWrapper__5adc3 { - background: #0c1f26; - border-radius: 12px; - height: calc(100vh - var(--custom-app-top-bar-height) - 128px); - margin: 48px 48px 0; - opacity: 1; - overflow: hidden; - position: relative; - transform: scale(1); - width: 100%; - z-index: 0 -} - -.haven__5adc3 { - --custom-haven-seat-width: 120px; - --custom-haven-seat-height: 40px; - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.full-motion .haven__5adc3 { - transition: transform .15s linear -} - -.haven__5adc3.noTransition__5adc3 { - transition: none -} - -.overlay__5adc3 { - -webkit-backdrop-filter: blur(15px); - backdrop-filter: blur(15px); - height: 100%; - inset: 0; - pointer-events: all; - position: absolute; - width: 100% -} - -.full-motion .overlay__5adc3 { - transition: all 2s ease -} - -.overlay__5adc3.out__5adc3 { - -webkit-backdrop-filter: blur(0); - backdrop-filter: blur(0); - pointer-events: none -} - -.overlay__5adc3.out__5adc3 .welcome__5adc3 { - opacity: 0; - transform: scale(.33) -} - -.environment__5adc3 { - height: 1536px; - position: absolute; - top: 0; - width: 2180px -} - -.background__5adc3 { - bottom: 0; - -o-object-fit: cover; - object-fit: cover; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - -webkit-user-drag: none -} - -.background__5adc3,.video__5adc3 { - inset-inline-start: 0; - position: absolute -} - -.video__5adc3 { - top: 0 -} - -.noInteract__5adc3 { - pointer-events: none -} - -.bottomActions__5adc3 { - align-items: flex-end; - bottom: 20px; - color: #fff; - height: 20px; - inset-inline: 0; - justify-content: space-between; - padding: 0 20px; - position: absolute -} - -.welcome__5adc3 { - height: 400px; - inset: 0; - margin: auto; - max-width: 380px; - opacity: 1; - padding: 24px; - pointer-events: none; - position: absolute; - text-align: center; - transform: scale(1); - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - width: 100%; - -webkit-user-drag: none -} - -.full-motion .welcome__5adc3 { - transition: all 2s ease -} - -.hidden__5adc3 { - opacity: 0; - pointer-events: none -} - -.interactionOverlay__5adc3 { - inset: 0; - pointer-events: all; - position: absolute -} - -.havenPipWrapper__3bbd0 { - background: var(--black); - overflow: hidden; - pointer-events: none; - position: relative -} - -.havenContainer__3bbd0 { - border-radius: 0; - height: 100%; - margin: 0; - width: 100% -} - -.pictureInPicture__6341f { - inset-inline-start: 0; - position: absolute; - top: 0 -} - -.pictureInPicture__6341f.dragging__6341f { - cursor: grabbing; - height: 100%; - pointer-events: auto; - width: 100% -} - -.pictureInPictureWindow__6341f { - position: absolute; - z-index: 1000 -} - -.borderRadius__6341f { - border-radius: 8px -} - -.hidden__6341f { - display: none -} - -.resizeHandle__6341f { - height: 20px; - position: absolute; - width: 20px -} - -.resizeHandleTopRight__6341f { - bottom: -7px; - cursor: nesw-resize; - inset-inline-start: -7px -} - -.resizeHandleTopLeft__6341f { - bottom: -7px; - cursor: nwse-resize; - inset-inline-end: -7px -} - -.resizeHandleBottomRight__6341f { - cursor: nwse-resize; - inset-inline-start: -7px; - top: -7px -} - -.resizeHandleBottomLeft__6341f { - cursor: nesw-resize; - inset-inline-end: -7px; - top: -7px -} - -.belowSidebar__6341f { - z-index: 90!important -} - -.canvas_eb6eba { - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100%; - z-index: 10000 -} - -.errorPage__01b8d { - width: 100% -} - -.debugMode_fa952e { - background-color: rgba(255,0,0,.25); - border: 2px solid red -} - -.clickable_fa952e { - pointer-events: auto -} - -.dismissButton_dd40b0 { - align-items: center; - background-color: var(--background-base-low); - border: 1px solid var(--border-subtle); - border-radius: 16px; - box-shadow: var(--shadow-border) var(--shadow-high); - cursor: pointer; - display: flex; - height: 24px; - inset-inline-end: -8px; - justify-content: center; - line-height: 0; - position: absolute; - top: -8px; - visibility: hidden; - width: 24px -} - -.dismissButton_dd40b0 .dismissIcon_dd40b0 { - color: var(--interactive-text-default) -} - -.dismissButton_dd40b0:hover { - background-color: var(--background-surface-high) -} - -.dismissButton_dd40b0:hover .dismissIcon_dd40b0 { - color: var(--interactive-text-active) -} - -.dismissButton_dd40b0:hover,.container:hover .dismissButton_dd40b0 { - visibility: visible -} - -.animationWrapper_e8d31f { - background-color: rgba(19,19,24,.95); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-border) var(--shadow-high); - position: absolute; - transform-origin: 50% 100%; - width: 100% -} - -.clickZone_e8d31f { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0 -} - -.animationWrapper_e8d31f.clickable_e8d31f:hover { - background-color: rgba(28,29,35,.95) -} - -.overflowWrapper_e8d31f { - border-radius: var(--radius-sm); - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - overflow: hidden -} - -.container_e8d31f { - box-sizing: border-box; - padding: 8px; - position: relative; - width: var(--custom-notification-container-width) -} - -.container_e8d31f:after { - bottom: -8px; - content: ""; - display: block; - height: 8px; - inset-inline-start: 0; - position: absolute; - width: 100% -} - -.iconAndDetails_e8d31f { - align-items: top; - display: flex -} - -.details_e8d31f,.footer_e8d31f { - display: flex -} - -.details_e8d31f { - flex: 1 1 auto; - flex-direction: column; - justify-content: center; - overflow: hidden -} - -.hint_e8d31f { - margin-top: var(--space-4) -} - -.clickable_e8d31f { - cursor: pointer -} - -.animationWrapper_e8d31f:hover .dismissButton_e8d31f,.dismissButton_e8d31f:hover { - visibility: visible -} - -.buttonContainer_e8d31f { - display: flex; - margin-top: var(--space-8) -} - -.button_e8d31f { - max-width: calc(50% - var(--space-4)) -} - -.button_e8d31f+.button_e8d31f { - margin-inline-start:var(--space-8)} - -.avatar_e8d31f { - align-items: center; - border-radius: 2px; - display: flex; - flex: 0 0 auto; - justify-content: center; - margin-inline-end:var(--space-12)} - -.avatar_e8d31f,.icon_e8d31f { - height: 40px; - width: 40px -} - -.icon_e8d31f { - border-radius: var(--radius-sm) -} - -.errorNotificationContainer_e13eda { - background-color: rgba(19,19,24,.95); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-border) var(--shadow-high); - position: relative!important -} - -.errorNotificationWrapper_e13eda { - height: auto; - position: relative; - top: auto; - inset-inline: auto; - bottom: auto; - display: block; - margin-top: 24px; - margin-inline-start:24px;width: 300px -} - -.errorClickNotification_e13eda { - width: 300px -} - -.stackTraceCode_e13eda { - font-family: var(--font-code) -} - -.code_e13eda { - padding: 15px; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.notificationIcon_e13eda { - fill: var(--white) -} - -.popout__0bd4a { - display: flex; - flex-direction: column; - height: 100vh -} - -.content__0bd4a { - align-items: center; - display: flex; - flex: 1 1 auto; - flex-direction: column; - height: 100%; - justify-content: center; - min-height: 0; - position: relative; - --custom-chat-input-margin-bottom: var(--space-xs) -} - -.titleBar__0bd4a { - background: var(--background-gradient-app-frame,var(--background-base-lowest)) -} - -@keyframes pulse__7135b { - 0% { - border-color: hsla(0,0%,100%,0) - } - - 25% { - border-color: hsla(0,0%,100%,.16) - } - - 50%,to { - border-color: hsla(0,0%,100%,0) - } -} - -@keyframes darkerPulse__7135b { - 0% { - border-color: hsla(0,0%,100%,0) - } - - 25% { - border-color: hsla(0,0%,100%,.32) - } - - 50%,to { - border-color: hsla(0,0%,100%,0) - } -} - -.innerContainer__7135b { - border: 20px solid hsla(0,0%,100%,0); - border-radius: 50% -} - -.app-focused .innerContainer__7135b { - animation: darkerPulse__7135b 2s linear infinite; - animation-timing-function: cubic-bezier(.4,0,1,1) -} - -.outerContainer__7135b { - border: 20px solid hsla(0,0%,100%,0); - border-radius: 50% -} - -.app-focused .outerContainer__7135b { - animation: pulse__7135b 2s linear infinite; - animation-delay: .2s; - animation-timing-function: cubic-bezier(.4,0,1,1) -} - -.background_fb62e2 { - align-items: center; - display: flex; - flex-wrap: nowrap; - height: 100%; - justify-content: center; - position: relative; - width: 100%; - z-index: 0 -} - -.background_fb62e2:before { - background: inherit; - content: ""; - display: block; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - z-index: -1 -} - -.avatarWrapper_fb62e2 { - border-radius: 50% -} - -.user-profile-video-tile-background.background_fb62e2 { - background: linear-gradient(color-mix(in oklab,var(--profile-gradient-primary-color) 20%,var(--profile-gradient-modal-background-color)),color-mix(in oklab,var(--profile-gradient-secondary-color) 60%,var(--profile-gradient-modal-background-color)) 100%) -} - -.button_fbcaba { - line-height: 0 -} - -.badgeUpgrade_fbcaba { - bottom: 2px; - color: var(--brand-500); - inset-inline-end: 2px; - position: absolute -} - -.mirror__948a4 { - transform: scaleX(-1) -} - -.video__948a4 { - background-color: var(--black); - position: relative -} - -.media__948a4,.video__948a4 { - height: 100%; - width: 100% -} - -.media__948a4 { - -o-object-fit: contain; - object-fit: contain -} - -.container__29209 { - align-items: center; - background: radial-gradient(100% 100% at 50% 100%,var(--modal-background) 60%,var(--transparent) 100%),linear-gradient(270deg,var(--expressive-gradient-purple-start) 0,var(--expressive-gradient-purple-end) 100%); - display: flex; - flex-wrap: wrap; - height: 100%; - justify-content: center; - padding: var(--space-4); - width: 100% -} - -.content__29209,.cta__29209 { - text-align: center; - width: 100% -} - -.cta__29209 { - display: inline-block -} - -.cta__29209>button { - display: block; - margin: 0 auto -} - -.artContainer__29209 { - padding-bottom: var(--space-48); - width: 100% -} - -.artContainer__29209.small__29209 { - padding-bottom: 0 -} - -.art__29209 { - background-image: url(/assets/422703837071ae97.svg); - background-position: 50%; - background-repeat: no-repeat; - background-size: contain; - height: 185px; - width: 100% -} - -.art__29209.small__29209 { - display: none -} - -.art__29209.medium__29209 { - height: 134px -} - -.art__29209.large__29209 { - height: 185px -} - -.header__29209 { - color: var(--text-strong); - margin-bottom: var(--space-24); - text-align: center -} - -.content_c30e20 { - align-items: center; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - display: flex; - justify-content: center; - overflow: hidden -} - -.streamHidden_c30e20 { - background: var(--primary-700); - flex-direction: column -} - -.streamHiddenEmptyState_c30e20 { - margin: 0; - padding: 0 -} - -.streamHiddenCTA_c30e20 { - align-items: center; - display: flex; - justify-content: center -} - -.largePaddingTop_c30e20 { - padding-top: 8px -} - -.cta_c30e20 { - z-index: 1 -} - -.addCta_c30e20 { - margin-inline-start:8px} - -.content__02686 { - align-items: center; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - display: flex; - justify-content: center; - overflow: hidden -} - -.streamHidden__02686 { - background: var(--primary-700); - flex-direction: column -} - -.streamHiddenEmptyState__02686 { - margin: 0; - padding: 0 -} - -.streamHiddenCTA__02686 { - align-items: center; - display: flex; - justify-content: center -} - -.largePaddingTop__02686 { - padding-top: 8px -} - -.addStreamIcon__02686 { - height: 24px; - width: 24px -} - -.addCTA__02686 { - margin-inline-start:8px;padding: 8px -} - -.leftTrayIcon__2cdb8 { - margin-inline-end:16px} - -.rightTrayIcon__2cdb8 { - margin-inline-start:16px} - -.embedTarget__2cdb8 { - height: 100%; - width: 100% -} - -.embedTargetHidden__2cdb8 { - height: 0; - visibility: hidden; - width: 0 -} - -.participantName__2cdb8 { - margin-inline-start:8px} - -.videoControls__2cdb8 { - background: none -} - -.loading__6ef4d { - background: var(--background-base-lower); - color: var(--text-default); - height: 100vh; - position: absolute; - width: 100% -} - -.container__5b927 { - background-color: var(--background-base-lower); - border-radius: 3px; - bottom: 0; - box-shadow: var(--shadow-border),var(--shadow-high); - font-family: var(--font-code); - font-size: 10px; - inset-inline-end: 0; - margin: 8px; - min-height: 5em; - padding: 8px; - pointer-events: all; - position: absolute; - white-space: pre; - width: 320px; - z-index: 1001 -} - -.containerMinimized__5b927 { - min-height: auto; - width: auto -} - -.content__5b927 { - align-items: flex-start; - display: flex; - flex-direction: column; - min-height: 5em -} - -.contentMinimized__5b927 { - display: none -} - -.notTracked__5b927 { - color: var(--text-muted) -} - -.label__5b927 { - display: flex -} - -.baseIcon__5b927 { - height: 12px; - width: 12px -} - -.iconNotTracked__5b927 { - color: var(--primary-300) -} - -.iconTracked__5b927 { - color: var(--status-positive) -} - -.minimizeButton__5b927 { - cursor: pointer; - inset-inline-end: 0; - padding: 8px; - position: absolute; - top: 0 -} - -.jsonSection__5b927 { - margin-inline-start:8px;margin-bottom: 8px; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text -} - -.stack__5b927 { - color: var(--text-muted) -} - -.current__5b927,.stack__5b927 { -} - -.current__5b927 { - color: var(--text-default) -} - -@keyframes outgoing-call-pulse_f910d0 { - 0% { - opacity: 0; - transform: scaleX(1) - } - - 11.56% { - opacity: 1 - } - - 39.02% { - opacity: 0; - transform: scale3d(1.1,1.1,1) - } - - 39.03% { - transform: scaleX(1) - } - - 39.04% { - opacity: 1 - } - - 66.48% { - opacity: 0; - transform: scale3d(1.1,1.1,1) - } - - to { - opacity: 0; - transform: scaleX(1) - } -} - -@keyframes incoming-call-pulse_f910d0 { - 0% { - opacity: 0; - transform: scaleX(1) - } - - 0.7% { - opacity: 1 - } - - 25.54% { - opacity: 0; - transform: scale3d(1.1,1.1,1) - } - - 25.55% { - transform: scaleX(1) - } - - 25.56% { - opacity: 1 - } - - 49.98% { - opacity: 0; - transform: scale3d(1.1,1.1,1) - } - - 49.99% { - transform: scaleX(1) - } - - 50% { - opacity: 1 - } - - 74.69% { - opacity: 0; - transform: scale3d(1.1,1.1,1) - } - - 74.70% { - transform: scaleX(1) - } - - 74.71% { - opacity: 1 - } - - to { - opacity: 0; - transform: scale3d(1.1,1.1,1) - } -} - -.clickable_f910d0 { - cursor: pointer -} - -.wrapper_f910d0 { - position: relative -} - -.wrapper_f910d0.ringingOutgoing_f910d0:after { - animation: outgoing-call-pulse_f910d0 var(--custom-call-avatar-outgoing-duration) infinite ease-out; - background: transparent; - border: 3px solid var(--interactive-text-active); - border-radius: 100%; - content: ""; - height: 100%; - inset-inline-start: -3px; - pointer-events: none; - position: absolute; - top: -3px; - transform-origin: center; - width: 100% -} - -.wrapper_f910d0.small_f910d0.ringingOutgoing_f910d0:after { - border: 1px solid var(--interactive-text-active); - inset-inline-start: -1px; - top: -1px -} - -.wrapper_f910d0.ringingIncoming_f910d0:after { - animation: incoming-call-pulse_f910d0 var(--custom-call-avatar-incoming-duration) infinite ease-out; - background: transparent; - border: 3px solid var(--interactive-text-active); - border-radius: 100%; - content: ""; - height: 100%; - inset-inline-start: -3px; - pointer-events: none; - position: absolute; - top: -3px; - transform-origin: center; - width: 100% -} - -.statusContainer_f910d0 { - background-color: var(--red-400); - border-radius: 50%; - bottom: 0; - inset-inline-end: 0; - padding: 4px; - position: absolute -} - -.status_f910d0,.statusContainer_f910d0 { - height: 16px; - width: 16px -} - -.voiceAvatar_f910d0 { - border-radius: 50%; - height: 100%; - position: inherit; - width: 100% -} - -.voiceAvatar_f910d0.ringingOutgoing_f910d0 { - opacity: .3 -} - -.border_f910d0 { - border-radius: 50%; - inset: 0; - position: absolute -} - -.app-focused .border_f910d0 { - transition: box-shadow .1s ease-out -} - -.overlay_f910d0 { - align-items: center; - background-color: var(--opacity-black-60); - border-radius: 50%; - display: flex; - height: 100%; - inset: 0; - justify-content: center; - position: absolute; - width: 100% -} - -.overlayIcon_f910d0 { - color: var(--white); - height: 32px; - width: 32px -} - -.callAvatarMask_f910d0 { - overflow: visible; - position: relative -} - -.callAvatarMaskContainer_f910d0 { - height: 100%; - position: relative -} - -.root__2dbe1 { - align-items: center; - background: var(--background-surface-high); - border-radius: 8px; - box-shadow: var(--shadow-border),var(--shadow-high); - box-sizing: border-box; - display: flex; - flex-direction: column; - min-width: 232px; - padding: 16px; - position: relative -} - -.root__2dbe1.previewCamera__2dbe1 { - flex-direction: column-reverse; - overflow: hidden -} - -.previewRoot__2dbe1 { - align-items: center; - background: var(--background-mod-normal); - display: flex; - justify-content: center -} - -.preview__2dbe1,.preview__2dbe1:after,.previewRoot__2dbe1 { - inset: 0; - position: absolute -} - -.preview__2dbe1:after { - background: linear-gradient(#18191c,transparent 84px,transparent 183px,#18191c); - content: "" -} - -.mainChannelInfo__2dbe1 { - align-items: center; - display: flex; - flex-direction: column; - max-width: 100% -} - -.mainChannelInfo__2dbe1 .titleGroup__2dbe1 { - max-width: 100% -} - -.mainChannelInfo__2dbe1 .icon__2dbe1 { - margin: 16px 0 -} - -.mainChannelInfo__2dbe1 .subtitle__2dbe1,.mainChannelInfo__2dbe1 .title__2dbe1 { - text-align: center -} - -.mainChannelInfo__2dbe1 .title__2dbe1 { - flex-shrink: 1; - margin-bottom: 2px; - min-width: 0; - overflow-wrap: break-word -} - -.mainChannelInfo__2dbe1 .subtitle__2dbe1 { - margin-bottom: 24px -} - -.previewChannelHeader__2dbe1 { - align-items: center; - box-sizing: border-box; - display: flex; - inset-inline-start: 0; - max-width: 100%; - padding-inline-start:16px;position: absolute; - top: 16px -} - -.previewChannelHeader__2dbe1 .titleGroup__2dbe1 { - flex-shrink: 1; - min-width: 0 -} - -.previewChannelHeader__2dbe1 .icon__2dbe1 { - margin-inline-end:16px} - -.previewChannelHeader__2dbe1 .subtitle__2dbe1,.previewChannelHeader__2dbe1 .title__2dbe1 { - color: var(--text-strong); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.wrapper__2dbe1 { - pointer-events: all; - z-index: 1001 -} - -.actionButton__2dbe1 { - line-height: 0 -} - -.fullRegionDropdownButtonOverride__2dbe1 { - padding: 0 4px -} - -.actions__2dbe1 { - display: flex; - gap: 8px -} - -.previewButton__2dbe1 { - margin-top: 16px -} - -.guildIcon__2dbe1 { - align-items: center; - background-color: var(--background-base-low); - color: var(--text-default); - display: flex; - height: 100%; - justify-content: center; - width: 100% -} - -.mask__2dbe1 { - display: block -} - -.guildAcronym__2dbe1 { - font-size: 24px; - overflow: hidden; - text-overflow: ellipsis -} - -.guildIconWithoutImage__2dbe1 { - background-color: var(--background-base-low) -} - -.container__0bec3 { - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: fixed; - top: 0; - width: 100%; - z-index: 9999 -} - -.gridOverlay__0bec3 { - height: 100%; - width: 100%; - --custom-grid-line-width: 0.3px; - --custom-grid-vertical-lines: linear-gradient(to right,var(--custom-grid-line-color) var(--custom-grid-line-width),transparent var(--custom-grid-line-width)); - --custom-grid-horizontal-lines: linear-gradient(to bottom,var(--custom-grid-line-color) var(--custom-grid-line-width),transparent var(--custom-grid-line-width)); - --custom-grid-line-color: var(--border-normal) -} - -.gridOverlay__0bec3[data-horizontal=true][data-vertical=true] { - background-image: var(--custom-grid-horizontal-lines),var(--custom-grid-vertical-lines); - background-position: 0 var(--custom-grid-vertical-spacing),var(--custom-grid-horizontal-spacing) 0; - background-size: var(--custom-grid-horizontal-spacing) var(--custom-grid-vertical-spacing) -} - -.gridOverlay__0bec3[data-horizontal=true][data-vertical=false] { - background-image: var(--custom-grid-horizontal-lines); - background-position: 0 var(--custom-grid-horizontal-spacing),var(--custom-grid-horizontal-spacing) 0; - background-size: var(--custom-grid-horizontal-spacing) var(--custom-grid-horizontal-spacing) -} - -.gridOverlay__0bec3[data-horizontal=false][data-vertical=true] { - background-image: var(--custom-grid-vertical-lines); - background-position: 0 var(--custom-grid-vertical-spacing),var(--custom-grid-vertical-spacing) 0; - background-size: var(--custom-grid-vertical-spacing) var(--custom-grid-vertical-spacing) -} - -@media (-webkit-max-device-pixel-ratio: 1.5),(max-resolution:1.5x) { - .gridOverlay__0bec3 { - --custom-grid-line-width:1px - } -} - -.container_a7f798 { - background-color: var(--background-base-lower); - border-radius: 3px; - bottom: 0; - box-shadow: var(--shadow-border),var(--shadow-high); - font-family: var(--font-code); - font-size: 10px; - inset-inline-end: 0; - margin: 8px; - min-width: 5rem; - padding: 8px; - pointer-events: all; - position: absolute; - white-space: pre; - z-index: 1001 -} - -.status_a7f798 { - align-items: center; - display: flex; - justify-content: center -} - -.statusIndicator_a7f798 { - padding-inline-end:.5rem} - -.statusText_a7f798 { - padding-inline-start:.5rem} - -[data-popout-root],html { - --brand-05a: hsla(var(--brand-500-hsl)/0.05); - --brand-10a: hsla(var(--brand-500-hsl)/0.1); - --brand-15a: hsla(var(--brand-500-hsl)/0.15); - --brand-20a: hsla(var(--brand-500-hsl)/0.2); - --brand-25a: hsla(var(--brand-500-hsl)/0.25); - --brand-30a: hsla(var(--brand-500-hsl)/0.3); - --brand-35a: hsla(var(--brand-500-hsl)/0.35); - --brand-40a: hsla(var(--brand-500-hsl)/0.4); - --brand-45a: hsla(var(--brand-500-hsl)/0.45); - --brand-50a: hsla(var(--brand-500-hsl)/0.5); - --brand-55a: hsla(var(--brand-500-hsl)/0.55); - --brand-60a: hsla(var(--brand-500-hsl)/0.6); - --brand-65a: hsla(var(--brand-500-hsl)/0.65); - --brand-70a: hsla(var(--brand-500-hsl)/0.7); - --brand-75a: hsla(var(--brand-500-hsl)/0.75); - --brand-80a: hsla(var(--brand-500-hsl)/0.8); - --brand-85a: hsla(var(--brand-500-hsl)/0.85); - --brand-90a: hsla(var(--brand-500-hsl)/0.9); - --brand-95a: hsla(var(--brand-500-hsl)/0.95) -} - -[data-popout-root].low-saturation,html.low-saturation { - --control-brand-foreground: var(--interactive-text-default) -} - -html.disable-forced-colors * { - forced-color-adjust: none -} - -.decorate-links { - --link-decoration: underline -} - -.appIconSelectionContainer__8a3d7 { - box-sizing: border-box; - position: relative -} - -.appIconSelection__8a3d7,.appIconSelectionContainer__8a3d7 { - height: var(--custom-theme-selection-selection-size); - width: var(--custom-theme-selection-selection-size) -} - -.appIconSelection__8a3d7 { - border-radius: 8px; - box-shadow: 0 0 2px var(--interactive-text-default); - cursor: pointer; - overflow: hidden -} - -.appIconSelection__8a3d7.selected__8a3d7 { - cursor: default -} - -.disabled__8a3d7 { - pointer-events: none -} - -.lockedBadgeContainer__8a3d7 { - align-items: center; - background-clip: content-box; - background-color: var(--background-base-low); - border-radius: var(--radius-round); - bottom: 0; - box-shadow: inset 0 0 0 2px var(--background-base-low); - cursor: pointer; - display: flex; - height: 18px; - inset-inline-end: 0; - justify-content: center; - padding: 1px; - position: absolute; - width: 18px; - z-index: 2 -} - -.lockedBadge__8a3d7 { - color: var(--icon-strong); - height: 10px; - width: 10px -} - -.header_a00e6e { - align-items: flex-start; - display: flex; - justify-content: space-between -} - -.headings_a00e6e { - display: flex; - flex-direction: column; - gap: var(--space-8) -} - -.title_a00e6e { - align-items: center; - display: flex; - flex: 1 -} - -.premiumIcon_a00e6e { - color: var(--icon-strong); - height: 20px; - width: 20px -} - -.titleText_a00e6e { - color: var(--text-strong) -} - -.preview__3e443 { - align-items: stretch; - display: flex; - flex-direction: column; - height: 180px; - justify-content: center; - overflow: hidden; - position: relative -} - -.preview__3e443:after { - bottom: 0; - content: ""; - cursor: default; - position: absolute; - top: 0; - inset-inline: 0 -} - -.firstMessage__3e443 { - margin-top: -.8rem!important -} - -.compactPreview__3e443 .firstMessage__3e443 { - margin-top: 0!important -} - -.forcedColorsWarning__3e443 { - margin-bottom: 12px -} - -.themeTitle__3e443 { - align-items: flex-start; - display: flex; - justify-content: space-between -} - -.formControl__3e443 { - position: relative -} - -.previewButton__3e443 { - inset-inline-end: 0; - position: absolute; - top: 0 -} - -.theme-dark.custom-theme-background .preview__3e443 { - background: var(--background-gradient-chat-preview) -} - -.theme-light.custom-theme-background .preview__3e443 { - background: var(--background-gradient-higher) -} - -.messageGroupingSpacing__3e443 { - margin-top: var(--space-24) -} - -.subtext__3e443 { - margin-bottom: var(--space-16); - text-transform: none -} - -.a11yCallout__3e443,.subtext__3e443 { - color: var(--text-subtle) -} - -.a11yCallout__3e443 { - margin-bottom: var(--space-32); - margin-top: var(--space-24) -} - -.divider__3e443 { - margin-bottom: var(--space-xl); - margin-top: var(--space-xxl) -} - -.compactModeToggle__3e443 { - display: flex; - height: 40px; - justify-content: center; - margin-bottom: 0; - margin-top: var(--space-24) -} - -.themeTitleContainer__3e443 { - display: flex; - flex-direction: column; - gap: var(--space-8); - margin-bottom: var(--space-16) -} - -.timeTitle__3e443 { - margin-bottom: var(--space-24) -} - -[role=radiogroup] [role=radio]:last-of-type { - margin-bottom: 0 -} - -.editorAnimate__2a891 { - animation: slidein__2a891 .25s ease-in 1s forwards; - margin-inline-end:calc((var(--custom-client-themes-editor-content-width) + (var(--custom-client-themes-editor-editor-padding)*2))*-1)} - -@keyframes slidein__2a891 { - 0% { - margin-inline-end: calc((var(--custom-client-themes-editor-content-width) + (var(--custom-client-themes-editor-editor-padding)*2))*-1) - } - - to { - margin-inline-end:0} -} - -.title__2a891 { - align-items: center; - display: flex; - flex: 1 -} - -.title__2a891 .premiumIcon__2a891 { - color: var(--text-strong); - height: 18px; - margin-inline-start:8px;width: 18px -} - -.editor__2a891 { - background: var(--background-base-low); - border-inline-start:5px solid var(--background-base-lower);box-sizing: border-box; - display: flex; - flex-direction: column; - flex-grow: 0; - height: 100%; - inset-inline-end: 0; - padding: var(--custom-client-themes-editor-editor-padding) 0; - padding-inline:var(--space-16);position: relative; - z-index: 1 -} - -.editorHeader__2a891 { - width: var(--custom-client-themes-editor-content-width) -} - -.editorBody__2a891,.editorHeader__2a891 { - padding: 0 var(--custom-client-themes-editor-editor-padding) -} - -.selectionGroup__2a891 { - margin-top: 16px; - width: var(--custom-client-themes-editor-content-width) -} - -.editorFooter__2a891 { - display: flex; - flex-direction: column; - gap: 8px; - margin-top: 16px; - padding: 0 var(--custom-client-themes-editor-editor-padding) -} - -.closeCircleButton__2a891 { - cursor: pointer; - height: 20px; - inset-inline-end: 18px; - position: absolute; - top: 18px; - width: 20px; - z-index: 2 -} - -.closeCircle__2a891 { - color: var(--interactive-text-default); - height: 100%; - width: 100% -} - -.closeCircle__2a891:focus,.closeCircle__2a891:hover { - color: var(--interactive-text-hover) -} - -.debugToolsPanel__50163 { - background: var(--background-base-low); - box-sizing: border-box; - display: flex; - flex-direction: column; - flex-grow: 0; - gap: 25px; - height: 100%; - inset-inline-end: 0; - max-width: 350px; - padding: 0; - position: relative; - z-index: 1 -} - -.header__50163 { - align-items: center; - display: flex; - flex-direction: row; - gap: var(--space-16); - justify-content: space-between; - padding-top: var(--space-16); - padding-inline:var(--space-12)} - -.headerTextContainer__50163 { - gap: var(--space-8) -} - -.body__50163,.headerTextContainer__50163 { - display: flex; - flex-direction: column -} - -.body__50163 { - flex-grow: 1; - gap: var(--space-16); - padding-inline:var(--space-12)} - -.footer__50163 { - border-top: 1px solid var(--border-normal) -} - -.categoryContainer__50163,.footer__50163 { - display: flex; - flex-direction: column; - gap: var(--space-8); - padding: var(--space-16) -} - -.categoryContainer__50163 { - background-color: var(--background-mod-subtle); - border-radius: var(--radius-sm) -} - -.categoryHeader__50163 { - align-items: center -} - -.categoryHeader__50163,.statusRow__50163 { - display: flex; - flex-direction: row; - gap: var(--space-4) -} - -.statusRow__50163 { - align-items: start -} - -.statusTextContainer__50163 { - align-items: flex-start; - display: flex; - flex: 1 0 0; - flex-direction: column; - gap: 4px; - justify-content: center -} - -.iconGood__50163 { - fill: var(--icon-feedback-positive) -} - -.iconBad__50163 { - fill: var(--icon-feedback-critical) -} - -.statusIcon__50163 { - flex-shrink: 0 -} - -.calloutBox__50163 { - align-items: center; - align-self: stretch; - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - display: flex; - gap: var(--space-4); - justify-content: center; - padding: var(--space-8) -} - -.tabBar__50163 { - border-bottom: 1px solid var(--border-subtle); - max-width: calc(100% - 60px) -} - -.tabBar__50163.vertical__50163 { - border-bottom: none; - gap: var(--space-16) -} - -.tab__50163 { - align-items: center; - display: flex; - flex-shrink: 1; - gap: var(--space-8); - padding-bottom: var(--space-12) -} - -.tab__50163.vertical__50163 { - border-inline-start:1px solid var(--border-subtle);border-radius: 0 -} - -.tab__50163.vertical__50163.selected__50163 { - border-inline-start:2px solid var(--text-brand);color: var(--text-brand); - margin-inline-start:-1px} - -.spinner__50163 { - margin-inline:var(--space-96)} - -.container_cc53de { - background-color: var(--background-surface-highest); - border-radius: var(--radius-sm); - box-sizing: border-box; - display: flex; - flex-direction: column; - gap: 10px; - margin: 25px auto 0; - padding: 8px; - width: 260px -} - -.bannerImage_cc53de { - height: 33.75px; - width: 57.23px -} - -.topContent_cc53de { - align-items: center; - display: flex; - gap: 12px -} - -.textContent_cc53de { - display: flex; - flex: 1; - flex-direction: column; - gap: 2px -} - -.editorAnimate_cf6da1 { - animation: slidein_cf6da1 .5s ease-in 1s forwards; - margin-inline-end:calc((var(--custom-client-themes-editor-content-width) + (var(--custom-client-themes-editor-editor-padding)*2))*-1)} - -@keyframes slidein_cf6da1 { - 0% { - margin-inline-end: calc((var(--custom-client-themes-editor-content-width) + (var(--custom-client-themes-editor-editor-padding)*2))*-1) - } - - to { - margin-inline-end:0} -} - -.themeEditor_cf6da1 { - background: var(--background-base-low); - border-inline-start:6px solid var(--background-base-lower);box-sizing: border-box; - display: flex; - flex-direction: column; - flex-grow: 0; - height: 100%; - inset-inline-end: 0; - padding: var(--custom-client-themes-editor-editor-padding) 0; - padding-inline:var(--space-12);position: relative; - z-index: 1 -} - -.editorHeader_cf6da1 { - width: var(--custom-client-themes-editor-content-width) -} - -.editorBody_cf6da1,.editorHeader_cf6da1 { - padding: 0 var(--custom-client-themes-editor-editor-padding) -} - -.selectionGroup_cf6da1 { - margin-top: 16px; - width: var(--custom-client-themes-editor-content-width) -} - -.editorFooter_cf6da1 { - box-sizing: border-box; - display: flex; - flex-direction: column; - gap: 12px; - margin-top: 16px; - max-width: 268px; - padding: 0 var(--custom-client-themes-editor-editor-padding) -} - -.closeCircleButton_cf6da1 { - cursor: pointer; - height: 20px; - inset-inline-end: 18px; - position: absolute; - top: 18px; - width: 20px; - z-index: 2 -} - -.closeCircle_cf6da1 { - color: var(--interactive-text-default); - height: 100%; - width: 100% -} - -.closeCircle_cf6da1:focus,.closeCircle_cf6da1:hover { - color: var(--interactive-text-hover) -} - -.bannerUpsell_cf6da1 { - align-items: center; - background-image: var(--custom-premium-colors-premium-gradient-tier-2); - border-radius: 8px; - box-sizing: border-box; - display: flex; - padding: 10px -} - -.bannerUpsell_cf6da1 .premiumIcon_cf6da1 { - color: var(--white); - flex-shrink: 0; - height: 24px; - margin-inline-end:10px;width: 24px -} - -.container__247cf { - flex-direction: column; - gap: 8px -} - -.container__247cf,.loader__247cf { - display: flex; - width: 100% -} - -.loader__247cf { - align-items: center; - justify-content: center -} - -.headerContainer__247cf { - align-items: center; - display: flex; - justify-content: space-between -} - -.addColorButtonText__247cf { - color: var(--text-brand); - text-transform: capitalize -} - -.addColorButtonWrapper__247cf { - position: relative -} - -.disabledButtonOverlay__247cf { - cursor: not-allowed; - inset: 0; - position: absolute; - z-index: 1 -} - -.addColorButton__247cf.disabled__247cf .addColorButtonText__247cf { - color: var(--text-muted) -} - -.gradientBar__247cf { - border-radius: var(--radius-sm); - height: 44px; - position: relative; - width: 100% -} - -.colorSquare__247cf { - align-items: center; - border-radius: var(--radius-xs); - cursor: pointer; - display: flex; - justify-content: center; - position: absolute; - top: 50%; - transform: translate(-50%,-50%) -} - -.full-motion .colorSquare__247cf { - transition: all .2s ease -} - -.full-motion .colorSquare__247cf:hover { - transform: translate(-50%,-50%) scale(1.05) -} - -.colorSquareInner__247cf { - border: 2px solid #fff; - border-radius: var(--radius-xs); - box-shadow: 0 3px 13.2px 0 rgba(0,0,0,.15),0 1px 2px 0 rgba(0,0,0,.05),inset 0 1px 6px 0 rgba(0,0,0,.15); - height: 20px; - width: 20px -} - -.colorSquare__247cf.selected__247cf .colorSquareInner__247cf { - border: 4px solid #fff -} - -.colorSquare__247cf:hover .removeColorButton__247cf { - opacity: 1 -} - -.colorSquareSelectedTriangle__247cf { - border-inline-end:4px solid transparent;border-inline-start:4px solid transparent;border-top: 5px solid #fff; - height: 0; - position: absolute; - top: calc(50% + 13px); - transform: translateX(-50%); - width: 0 -} - -.hexInputContainer__247cf { - align-items: center; - background: var(--background-mod-subtle); - border: 1px solid var(--border-muted); - border-radius: var(--radius-sm); - display: flex; - height: 40px; - padding-block:0;padding-inline:8px 4px;position: relative; - transition: border-color .15s ease -} - -.hexInputContainer__247cf:focus-within { - border-color: var(--control-primary-background-default); - box-shadow: 0 0 0 1px var(--control-primary-background-default) -} - -.hexInput__247cf { - background: none; - border: none; - color: var(--text-default); - flex: 1 1 0; - font-family: var(--font-primary); - font-size: 16px; - font-weight: 400; - height: 100%; - min-width: 0; - outline: none; - padding: 0 -} - -.hexInput__247cf::-moz-placeholder { - color: var(--text-muted) -} - -.hexInput__247cf::placeholder { - color: var(--text-muted) -} - -.trailingIcons__247cf { - align-items: center; - display: flex; - flex-shrink: 0; - margin-inline-start:8px} - -.hexInputInner__247cf { - background: none; - border: none; - font-family: var(--font-primary); - font-size: 14px; - font-weight: 400; - height: 100%; - padding: 0 -} - -.eyeDropperIcon__247cf { - height: 16px; - width: 16px -} - -.colorItem__247cf { - align-items: center; - display: flex; - gap: 8px -} - -.hexColorPreview__247cf { - border: 1px solid var(--border-subtle); - border-radius: var(--radius-xs); - flex-shrink: 0; - height: 24px; - margin-inline-end:8px;width: 24px -} - -.colorPreview__247cf { - border: 1px solid var(--background-base-lowest); - border-radius: var(--radius-xs); - height: 24px; - width: 24px -} - -.enable-forced-colors .container__247cf { - forced-color-adjust: none -} - -.icon__247cf { - display: block -} - -.eyeDropperTooltip__247cf { - max-width: 130px -} - -.themeSelector_c85552 { - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - height: 32px -} - -.themePillItem_c85552 { - border-radius: var(--radius-xs) -} - -.themePillItemSelected_c85552 { - border: 1px solid var(--border-subtle) -} - -.container__5a2df { - border: 1px solid var(--border-muted); - width: 288px -} - -.container__5a2df,.mobileContainer__5a2df { - background: var(--background-base-low); - display: flex; - flex-direction: column -} - -.mobileContainer__5a2df { - height: 100%; - width: 100% -} - -.editorBody__5a2df { - gap: 32px; - margin-top: 16px; - padding-inline:var(--space-12) calc(var(--space-12) - 8px)} - -.editorBody__5a2df,.section__5a2df { - display: flex; - flex-direction: column -} - -.section__5a2df { - gap: 12px -} - -.resetButton__5a2df { - display: flex; - flex-direction: column; - gap: 8px; - margin-top: 19px -} - -.headerContainer__5a2df { - align-items: center; - display: flex; - gap: 4px -} - -.closeButton__5a2df { - margin-inline-start:auto} - -.sliderContainer__5a2df { - display: flex; - flex-direction: column; - gap: 8px -} - -.gradientDirectionSliderContainer__5a2df { - margin-bottom: 4px; - position: relative -} - -.angleIndicatorOverlay__5a2df { - align-items: center; - display: flex; - height: 12px; - position: absolute; - top: 35.5px; - inset-inline: .5px; - justify-content: space-between -} - -.angleIndicatorDot__5a2df { - background-color: var(--background-mod-muted); - border-radius: 50%; - height: 8px; - width: 8px -} - -.angleIndicatorDotHover__5a2df,.angleIndicatorLargeDot__5a2df { - background-color: var(--background-mod-muted); - border-radius: 50%; - height: 10px; - width: 10px -} - -.angleIndicatorLargeDotHover__5a2df { - background-color: var(--background-mod-muted); - border-radius: 50%; - height: 12px; - width: 12px -} - -.sliderWrapper__5a2df:hover .angleIndicatorDot__5a2df { - height: 10px; - width: 10px -} - -.sliderWrapper__5a2df:hover .angleIndicatorLargeDot__5a2df { - height: 12px; - width: 12px -} - -.footerContainer__5a2df { - align-items: center; - border-top: 1px solid var(--border-subtle); - display: flex; - flex-direction: row; - gap: 8px; - padding: 16px 20px -} - -.footerContainer__5a2df>* { - flex: 1 -} - -.footerContainerNonPremium__5a2df { - flex-direction: column; - gap: 12px -} - -.footerRightButtons__5a2df { - align-items: center; - display: flex; - gap: 24px; - justify-content: flex-end; - width: 100% -} - -.subscribeButton__5a2df { - color: var(--white); - flex-shrink: 0; - height: 40px; - min-width: 110px; - width: 100% -} - -.coachmarkHeaderContainer__5a2df { - display: flex; - flex-direction: column -} - -.headerAndCloseRow__5a2df { - align-items: center; - display: flex; - justify-content: space-between; - width: 100% -} - -.subtitle__5a2df { - margin-top: var(--space-4) -} - -.coachmarkFooterContainer__5a2df { - border-top: 1px solid var(--border-subtle); - display: flex; - flex-direction: column; - gap: var(--space-8); - padding: var(--space-16) var(--space-20) -} - -.nitroIcon__5a2df { - fill: var(--icon-strong) -} - -.controlLabelContainer__5a2df { - display: flex; - justify-content: space-between; - white-space: nowrap -} - -.controlLabelInput__5a2df { - background: none; - border: 1px solid transparent; - border-radius: var(--radius-xs); - color: var(--text-default); - flex: 0 0 auto; - font-family: var(--font-primary); - font-size: 16px; - font-weight: var(--font-weight-medium); - height: 20px; - line-height: 20px; - outline: none; - padding-block:0;padding-inline:12px 0;text-align: end; - transition: border-color .15s ease; - width: 40px -} - -.controlLabelInput__5a2df:focus { - background: var(--input-background-default); - border-color: var(--control-primary-background-default); - box-shadow: 0 0 0 1px var(--control-primary-background-default) -} - -.controlLabelInput__5a2df::-moz-placeholder { - color: var(--input-placeholder-text-default) -} - -.controlLabelInput__5a2df::placeholder { - color: var(--input-placeholder-text-default) -} - -.mobileContainer__26f4a { - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - z-index: 100 -} - -.appAsidePanelWrapper_a3002d { - display: flex; - flex: 1 1 auto; - flex-direction: row; - min-height: 0 -} - -.mobileAppAsidePanelWrapper_a3002d { - flex-direction: column; - overflow-y: scroll -} - -.notAppAsidePanel_a3002d { - flex: 1; - min-width: 0 -} - -.app_a3002d { - position: static -} - -.app_a3002d,.mobileApp_a3002d { - height: 100%; - z-index: auto -} - -.allowsScrolling_a3002d,.mobileApp_a3002d { - overflow: auto -} - -.notAppAsidePanel_a3002d:has(~[data-app-right-panel=true]) .app_a3002d { - position: relative -} - -.modal_ab9bdf { - border-radius: 5px; - box-shadow: var(--legacy-elevation-border),var(--legacy-elevation-high); - display: flex; - flex: 1; - flex-direction: column; - min-height: 0; - position: relative -} - -.theme-dark .modal_ab9bdf { - background-color: var(--primary-600) -} - -.theme-light .modal_ab9bdf { - background: var(--white) -} - -.sizeSmall_ab9bdf { - max-height: 660px; - min-height: 200px; - width: 440px -} - -.sizeMedium_ab9bdf { - max-height: 800px; - min-height: 400px; - width: 600px -} - -.sizeLarge_ab9bdf { - max-width: 960px; - min-height: 400px; - min-width: 800px -} - -@media (max-width: 485px) { - .sizeLarge_ab9bdf,.sizeMedium_ab9bdf,.sizeSmall_ab9bdf { - align-self:center; - max-height: 100vh; - min-width: auto; - width: 96% - } - - .fullscreenOnMobile_ab9bdf { - border-radius: 0; - position: absolute; - top: 0; - inset-inline: 0; - bottom: 0; - overflow-y: auto; - width: 100% - } - - .fullscreenOnMobile_ab9bdf .footer_ab9bdf,.fullscreenOnMobile_ab9bdf .header_ab9bdf { - border-radius: 0 - } - - .hideOnFullscreen_ab9bdf { - display: none - } -} - -:root { - --application-subscription-end: hsl(var(--application-subscription-end-hsl)/1); - --application-subscription-end-hsl: 196.564 calc(var(--saturation-factor, 1)*98.788%) 32.353%; - --application-subscription-start: hsl(var(--application-subscription-start-hsl)/1); - --application-subscription-start-hsl: 234.909 calc(var(--saturation-factor, 1)*68.465%) 52.745%; - --battlenet: hsl(var(--battlenet-hsl)/1); - --battlenet-hsl: 199.651 calc(var(--saturation-factor, 1)*100%) 44.902%; - --bg-animated-gradient-background-indigo-1: hsl(var(--bg-animated-gradient-background-indigo-1-hsl)/1); - --bg-animated-gradient-background-indigo-1-hsl: 241.5 calc(var(--saturation-factor, 1)*57.143%) 27.451%; - --bg-animated-gradient-background-indigo-2: hsl(var(--bg-animated-gradient-background-indigo-2-hsl)/1); - --bg-animated-gradient-background-indigo-2-hsl: 257.059 calc(var(--saturation-factor, 1)*100%) 20%; - --bg-animated-gradient-background-not-black: hsl(var(--bg-animated-gradient-background-not-black-hsl)/1); - --bg-animated-gradient-background-not-black-hsl: 240 calc(var(--saturation-factor, 1)*7.143%) 5.49%; - --bg-animated-gradient-background-pink-1: hsl(var(--bg-animated-gradient-background-pink-1-hsl)/1); - --bg-animated-gradient-background-pink-1-hsl: 327.831 calc(var(--saturation-factor, 1)*80.583%) 59.608%; - --bg-gradient-aurora-1: hsl(var(--bg-gradient-aurora-1-hsl)/1); - --bg-gradient-aurora-1-hsl: 219.74 calc(var(--saturation-factor, 1)*86.517%) 17.451%; - --bg-gradient-aurora-2: hsl(var(--bg-gradient-aurora-2-hsl)/1); - --bg-gradient-aurora-2-hsl: 237.778 calc(var(--saturation-factor, 1)*76.415%) 41.569%; - --bg-gradient-aurora-3: hsl(var(--bg-gradient-aurora-3-hsl)/1); - --bg-gradient-aurora-3-hsl: 183.556 calc(var(--saturation-factor, 1)*78.035%) 33.922%; - --bg-gradient-aurora-4: hsl(var(--bg-gradient-aurora-4-hsl)/1); - --bg-gradient-aurora-4-hsl: 169.2 calc(var(--saturation-factor, 1)*60.241%) 32.549%; - --bg-gradient-aurora-5: hsl(var(--bg-gradient-aurora-5-hsl)/1); - --bg-gradient-aurora-5-hsl: 229.839 calc(var(--saturation-factor, 1)*92.537%) 26.275%; - --bg-gradient-blurple-twilight-1: hsl(var(--bg-gradient-blurple-twilight-1-hsl)/1); - --bg-gradient-blurple-twilight-1-hsl: 233.904 calc(var(--saturation-factor, 1)*79.574%) 53.922%; - --bg-gradient-blurple-twilight-2: hsl(var(--bg-gradient-blurple-twilight-2-hsl)/1); - --bg-gradient-blurple-twilight-2-hsl: 245.294 calc(var(--saturation-factor, 1)*63.75%) 31.373%; - --bg-gradient-chroma-glow-1: hsl(var(--bg-gradient-chroma-glow-1-hsl)/1); - --bg-gradient-chroma-glow-1-hsl: 183.39 calc(var(--saturation-factor, 1)*86.341%) 40.196%; - --bg-gradient-chroma-glow-2: hsl(var(--bg-gradient-chroma-glow-2-hsl)/1); - --bg-gradient-chroma-glow-2-hsl: 258.113 calc(var(--saturation-factor, 1)*89.831%) 46.275%; - --bg-gradient-chroma-glow-3: hsl(var(--bg-gradient-chroma-glow-3-hsl)/1); - --bg-gradient-chroma-glow-3-hsl: 298.491 calc(var(--saturation-factor, 1)*90.857%) 34.314%; - --bg-gradient-chroma-glow-4: hsl(var(--bg-gradient-chroma-glow-4-hsl)/1); - --bg-gradient-chroma-glow-4-hsl: 264.767 calc(var(--saturation-factor, 1)*100%) 66.275%; - --bg-gradient-chroma-glow-5: hsl(var(--bg-gradient-chroma-glow-5-hsl)/1); - --bg-gradient-chroma-glow-5-hsl: 206.702 calc(var(--saturation-factor, 1)*75.494%) 50.392%; - --bg-gradient-citrus-sherbert-1: hsl(var(--bg-gradient-citrus-sherbert-1-hsl)/1); - --bg-gradient-citrus-sherbert-1-hsl: 39.683 calc(var(--saturation-factor, 1)*88.732%) 58.235%; - --bg-gradient-citrus-sherbert-2: hsl(var(--bg-gradient-citrus-sherbert-2-hsl)/1); - --bg-gradient-citrus-sherbert-2-hsl: 18 calc(var(--saturation-factor, 1)*81.522%) 63.922%; - --bg-gradient-cotton-candy-1: hsl(var(--bg-gradient-cotton-candy-1-hsl)/1); - --bg-gradient-cotton-candy-1-hsl: 349.315 calc(var(--saturation-factor, 1)*76.842%) 81.373%; - --bg-gradient-cotton-candy-2: hsl(var(--bg-gradient-cotton-candy-2-hsl)/1); - --bg-gradient-cotton-candy-2-hsl: 226.4 calc(var(--saturation-factor, 1)*92.593%) 84.118%; - --bg-gradient-crimson-moon-1: hsl(var(--bg-gradient-crimson-moon-1-hsl)/1); - --bg-gradient-crimson-moon-1-hsl: 0 calc(var(--saturation-factor, 1)*88.608%) 30.98%; - --bg-gradient-crimson-moon-2: hsl(var(--bg-gradient-crimson-moon-2-hsl)/1); - --bg-gradient-crimson-moon-2-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --bg-gradient-desert-khaki-1: hsl(var(--bg-gradient-desert-khaki-1-hsl)/1); - --bg-gradient-desert-khaki-1-hsl: 28.696 calc(var(--saturation-factor, 1)*32.394%) 86.078%; - --bg-gradient-desert-khaki-2: hsl(var(--bg-gradient-desert-khaki-2-hsl)/1); - --bg-gradient-desert-khaki-2-hsl: 40 calc(var(--saturation-factor, 1)*41.284%) 78.627%; - --bg-gradient-desert-khaki-3: hsl(var(--bg-gradient-desert-khaki-3-hsl)/1); - --bg-gradient-desert-khaki-3-hsl: 50.164 calc(var(--saturation-factor, 1)*49.593%) 75.882%; - --bg-gradient-dusk-1: hsl(var(--bg-gradient-dusk-1-hsl)/1); - --bg-gradient-dusk-1-hsl: 292.8 calc(var(--saturation-factor, 1)*13.514%) 36.275%; - --bg-gradient-dusk-2: hsl(var(--bg-gradient-dusk-2-hsl)/1); - --bg-gradient-dusk-2-hsl: 223.125 calc(var(--saturation-factor, 1)*41.026%) 69.412%; - --bg-gradient-easter-egg-1: hsl(var(--bg-gradient-easter-egg-1-hsl)/1); - --bg-gradient-easter-egg-1-hsl: 226.731 calc(var(--saturation-factor, 1)*58.427%) 65.098%; - --bg-gradient-easter-egg-2: hsl(var(--bg-gradient-easter-egg-2-hsl)/1); - --bg-gradient-easter-egg-2-hsl: 227.143 calc(var(--saturation-factor, 1)*30.973%) 44.314%; - --bg-gradient-forest-1: hsl(var(--bg-gradient-forest-1-hsl)/1); - --bg-gradient-forest-1-hsl: 124.286 calc(var(--saturation-factor, 1)*25.926%) 10.588%; - --bg-gradient-forest-2: hsl(var(--bg-gradient-forest-2-hsl)/1); - --bg-gradient-forest-2-hsl: 142.5 calc(var(--saturation-factor, 1)*26.23%) 23.922%; - --bg-gradient-forest-3: hsl(var(--bg-gradient-forest-3-hsl)/1); - --bg-gradient-forest-3-hsl: 76.154 calc(var(--saturation-factor, 1)*20.635%) 24.706%; - --bg-gradient-forest-4: hsl(var(--bg-gradient-forest-4-hsl)/1); - --bg-gradient-forest-4-hsl: 116.667 calc(var(--saturation-factor, 1)*16.981%) 41.569%; - --bg-gradient-forest-5: hsl(var(--bg-gradient-forest-5-hsl)/1); - --bg-gradient-forest-5-hsl: 42.766 calc(var(--saturation-factor, 1)*38.525%) 47.843%; - --bg-gradient-hanami-1: hsl(var(--bg-gradient-hanami-1-hsl)/1); - --bg-gradient-hanami-1-hsl: 352.174 calc(var(--saturation-factor, 1)*68.317%) 80.196%; - --bg-gradient-hanami-2: hsl(var(--bg-gradient-hanami-2-hsl)/1); - --bg-gradient-hanami-2-hsl: 43.146 calc(var(--saturation-factor, 1)*73.554%) 76.275%; - --bg-gradient-hanami-3: hsl(var(--bg-gradient-hanami-3-hsl)/1); - --bg-gradient-hanami-3-hsl: 115.714 calc(var(--saturation-factor, 1)*43.077%) 74.51%; - --bg-gradient-lofi-vibes-1: hsl(var(--bg-gradient-lofi-vibes-1-hsl)/1); - --bg-gradient-lofi-vibes-1-hsl: 219.759 calc(var(--saturation-factor, 1)*83.838%) 80.588%; - --bg-gradient-lofi-vibes-2: hsl(var(--bg-gradient-lofi-vibes-2-hsl)/1); - --bg-gradient-lofi-vibes-2-hsl: 183.81 calc(var(--saturation-factor, 1)*57.798%) 78.627%; - --bg-gradient-lofi-vibes-3: hsl(var(--bg-gradient-lofi-vibes-3-hsl)/1); - --bg-gradient-lofi-vibes-3-hsl: 129.6 calc(var(--saturation-factor, 1)*46.296%) 78.824%; - --bg-gradient-lofi-vibes-4: hsl(var(--bg-gradient-lofi-vibes-4-hsl)/1); - --bg-gradient-lofi-vibes-4-hsl: 75.738 calc(var(--saturation-factor, 1)*48.8%) 75.49%; - --bg-gradient-mars-1: hsl(var(--bg-gradient-mars-1-hsl)/1); - --bg-gradient-mars-1-hsl: 14.795 calc(var(--saturation-factor, 1)*36.318%) 39.412%; - --bg-gradient-mars-2: hsl(var(--bg-gradient-mars-2-hsl)/1); - --bg-gradient-mars-2-hsl: 0 calc(var(--saturation-factor, 1)*36.19%) 41.176%; - --bg-gradient-midnight-blurple-1: hsl(var(--bg-gradient-midnight-blurple-1-hsl)/1); - --bg-gradient-midnight-blurple-1-hsl: 245.077 calc(var(--saturation-factor, 1)*55.085%) 53.725%; - --bg-gradient-midnight-blurple-2: hsl(var(--bg-gradient-midnight-blurple-2-hsl)/1); - --bg-gradient-midnight-blurple-2-hsl: 259.024 calc(var(--saturation-factor, 1)*74.545%) 10.784%; - --bg-gradient-mint-apple-1: hsl(var(--bg-gradient-mint-apple-1-hsl)/1); - --bg-gradient-mint-apple-1-hsl: 165.625 calc(var(--saturation-factor, 1)*39.669%) 52.549%; - --bg-gradient-mint-apple-2: hsl(var(--bg-gradient-mint-apple-2-hsl)/1); - --bg-gradient-mint-apple-2-hsl: 118.681 calc(var(--saturation-factor, 1)*40.444%) 55.882%; - --bg-gradient-mint-apple-3: hsl(var(--bg-gradient-mint-apple-3-hsl)/1); - --bg-gradient-mint-apple-3-hsl: 86.667 calc(var(--saturation-factor, 1)*48.293%) 59.804%; - --bg-gradient-neon-nights-1: hsl(var(--bg-gradient-neon-nights-1-hsl)/1); - --bg-gradient-neon-nights-1-hsl: 176.407 calc(var(--saturation-factor, 1)*98.817%) 33.137%; - --bg-gradient-neon-nights-2: hsl(var(--bg-gradient-neon-nights-2-hsl)/1); - --bg-gradient-neon-nights-2-hsl: 259.333 calc(var(--saturation-factor, 1)*39.474%) 55.294%; - --bg-gradient-neon-nights-3: hsl(var(--bg-gradient-neon-nights-3-hsl)/1); - --bg-gradient-neon-nights-3-hsl: 313.548 calc(var(--saturation-factor, 1)*52.542%) 46.275%; - --bg-gradient-retro-raincloud-1: hsl(var(--bg-gradient-retro-raincloud-1-hsl)/1); - --bg-gradient-retro-raincloud-1-hsl: 201.553 calc(var(--saturation-factor, 1)*47.032%) 42.941%; - --bg-gradient-retro-raincloud-2: hsl(var(--bg-gradient-retro-raincloud-2-hsl)/1); - --bg-gradient-retro-raincloud-2-hsl: 241.017 calc(var(--saturation-factor, 1)*29.648%) 60.98%; - --bg-gradient-retro-storm-1: hsl(var(--bg-gradient-retro-storm-1-hsl)/1); - --bg-gradient-retro-storm-1-hsl: 201.553 calc(var(--saturation-factor, 1)*47.032%) 42.941%; - --bg-gradient-retro-storm-2: hsl(var(--bg-gradient-retro-storm-2-hsl)/1); - --bg-gradient-retro-storm-2-hsl: 240.896 calc(var(--saturation-factor, 1)*27.801%) 47.255%; - --bg-gradient-sepia-1: hsl(var(--bg-gradient-sepia-1-hsl)/1); - --bg-gradient-sepia-1-hsl: 32.727 calc(var(--saturation-factor, 1)*14.163%) 45.686%; - --bg-gradient-sepia-2: hsl(var(--bg-gradient-sepia-2-hsl)/1); - --bg-gradient-sepia-2-hsl: 36.207 calc(var(--saturation-factor, 1)*46.774%) 24.314%; - --bg-gradient-strawberry-lemonade-1: hsl(var(--bg-gradient-strawberry-lemonade-1-hsl)/1); - --bg-gradient-strawberry-lemonade-1-hsl: 326.98 calc(var(--saturation-factor, 1)*74.129%) 39.412%; - --bg-gradient-strawberry-lemonade-2: hsl(var(--bg-gradient-strawberry-lemonade-2-hsl)/1); - --bg-gradient-strawberry-lemonade-2-hsl: 27.778 calc(var(--saturation-factor, 1)*71.681%) 44.314%; - --bg-gradient-strawberry-lemonade-3: hsl(var(--bg-gradient-strawberry-lemonade-3-hsl)/1); - --bg-gradient-strawberry-lemonade-3-hsl: 39.588 calc(var(--saturation-factor, 1)*80.165%) 52.549%; - --bg-gradient-sunrise-1: hsl(var(--bg-gradient-sunrise-1-hsl)/1); - --bg-gradient-sunrise-1-hsl: 326.809 calc(var(--saturation-factor, 1)*41.964%) 43.922%; - --bg-gradient-sunrise-2: hsl(var(--bg-gradient-sunrise-2-hsl)/1); - --bg-gradient-sunrise-2-hsl: 27.5 calc(var(--saturation-factor, 1)*44.86%) 58.039%; - --bg-gradient-sunrise-3: hsl(var(--bg-gradient-sunrise-3-hsl)/1); - --bg-gradient-sunrise-3-hsl: 50.286 calc(var(--saturation-factor, 1)*46.256%) 44.51%; - --bg-gradient-sunset-1: hsl(var(--bg-gradient-sunset-1-hsl)/1); - --bg-gradient-sunset-1-hsl: 259.2 calc(var(--saturation-factor, 1)*55.556%) 35.294%; - --bg-gradient-sunset-2: hsl(var(--bg-gradient-sunset-2-hsl)/1); - --bg-gradient-sunset-2-hsl: 21.667 calc(var(--saturation-factor, 1)*66.667%) 57.647%; - --bg-gradient-under-the-sea-1: hsl(var(--bg-gradient-under-the-sea-1-hsl)/1); - --bg-gradient-under-the-sea-1-hsl: 114.783 calc(var(--saturation-factor, 1)*10.502%) 42.941%; - --bg-gradient-under-the-sea-2: hsl(var(--bg-gradient-under-the-sea-2-hsl)/1); - --bg-gradient-under-the-sea-2-hsl: 158.667 calc(var(--saturation-factor, 1)*20.362%) 43.333%; - --bg-gradient-under-the-sea-3: hsl(var(--bg-gradient-under-the-sea-3-hsl)/1); - --bg-gradient-under-the-sea-3-hsl: 175.385 calc(var(--saturation-factor, 1)*10.924%) 46.667%; - --bungie: hsl(var(--bungie-hsl)/1); - --bungie-hsl: 196.916 calc(var(--saturation-factor, 1)*100%) 44.51%; - --checkpoint-emojis-gradient-end: hsl(var(--checkpoint-emojis-gradient-end-hsl)/1); - --checkpoint-emojis-gradient-end-hsl: 283.2 calc(var(--saturation-factor, 1)*53.191%) 9.216%; - --checkpoint-emojis-gradient-start: hsl(var(--checkpoint-emojis-gradient-start-hsl)/1); - --checkpoint-emojis-gradient-start-hsl: 260.114 calc(var(--saturation-factor, 1)*87.129%) 60.392%; - --checkpoint-emojis-primary: hsl(var(--checkpoint-emojis-primary-hsl)/1); - --checkpoint-emojis-primary-hsl: 269.011 calc(var(--saturation-factor, 1)*88.35%) 79.804%; - --checkpoint-emojis-secondary: hsl(var(--checkpoint-emojis-secondary-hsl)/1); - --checkpoint-emojis-secondary-hsl: 268.837 calc(var(--saturation-factor, 1)*72.067%) 64.902%; - --checkpoint-end-gradient-end: hsl(var(--checkpoint-end-gradient-end-hsl)/1); - --checkpoint-end-gradient-end-hsl: 149.333 calc(var(--saturation-factor, 1)*56.962%) 15.49%; - --checkpoint-end-gradient-start: hsl(var(--checkpoint-end-gradient-start-hsl)/1); - --checkpoint-end-gradient-start-hsl: 149.302 calc(var(--saturation-factor, 1)*50.588%) 50%; - --checkpoint-end-primary: hsl(var(--checkpoint-end-primary-hsl)/1); - --checkpoint-end-primary-hsl: 143.316 calc(var(--saturation-factor, 1)*83.55%) 54.706%; - --checkpoint-end-secondary: hsl(var(--checkpoint-end-secondary-hsl)/1); - --checkpoint-end-secondary-hsl: 143.673 calc(var(--saturation-factor, 1)*78.61%) 36.667%; - --checkpoint-friends-gradient-end: hsl(var(--checkpoint-friends-gradient-end-hsl)/1); - --checkpoint-friends-gradient-end-hsl: 259.481 calc(var(--saturation-factor, 1)*79.381%) 19.02%; - --checkpoint-friends-gradient-start: hsl(var(--checkpoint-friends-gradient-start-hsl)/1); - --checkpoint-friends-gradient-start-hsl: 256.018 calc(var(--saturation-factor, 1)*96.507%) 55.098%; - --checkpoint-friends-primary: hsl(var(--checkpoint-friends-primary-hsl)/1); - --checkpoint-friends-primary-hsl: 234 calc(var(--saturation-factor, 1)*90%) 80.392%; - --checkpoint-friends-secondary: hsl(var(--checkpoint-friends-secondary-hsl)/1); - --checkpoint-friends-secondary-hsl: 234 calc(var(--saturation-factor, 1)*39.823%) 55.686%; - --checkpoint-gaming-gradient-end: hsl(var(--checkpoint-gaming-gradient-end-hsl)/1); - --checkpoint-gaming-gradient-end-hsl: 28.8 calc(var(--saturation-factor, 1)*53.191%) 9.216%; - --checkpoint-gaming-gradient-start: hsl(var(--checkpoint-gaming-gradient-start-hsl)/1); - --checkpoint-gaming-gradient-start-hsl: 27.595 calc(var(--saturation-factor, 1)*100%) 53.529%; - --checkpoint-gaming-primary: hsl(var(--checkpoint-gaming-primary-hsl)/1); - --checkpoint-gaming-primary-hsl: 34.699 calc(var(--saturation-factor, 1)*100%) 51.176%; - --checkpoint-gaming-secondary: hsl(var(--checkpoint-gaming-secondary-hsl)/1); - --checkpoint-gaming-secondary-hsl: 26.809 calc(var(--saturation-factor, 1)*86.239%) 42.745%; - --checkpoint-guilds-gradient-end: hsl(var(--checkpoint-guilds-gradient-end-hsl)/1); - --checkpoint-guilds-gradient-end-hsl: 170.4 calc(var(--saturation-factor, 1)*53.191%) 9.216%; - --checkpoint-guilds-gradient-start: hsl(var(--checkpoint-guilds-gradient-start-hsl)/1); - --checkpoint-guilds-gradient-start-hsl: 169.589 calc(var(--saturation-factor, 1)*90.123%) 47.647%; - --checkpoint-guilds-primary: hsl(var(--checkpoint-guilds-primary-hsl)/1); - --checkpoint-guilds-primary-hsl: 169.811 calc(var(--saturation-factor, 1)*97.546%) 68.039%; - --checkpoint-guilds-secondary: hsl(var(--checkpoint-guilds-secondary-hsl)/1); - --checkpoint-guilds-secondary-hsl: 169.672 calc(var(--saturation-factor, 1)*68.539%) 34.902%; - --checkpoint-messages-gradient-end: hsl(var(--checkpoint-messages-gradient-end-hsl)/1); - --checkpoint-messages-gradient-end-hsl: 7.2 calc(var(--saturation-factor, 1)*53.191%) 9.216%; - --checkpoint-messages-gradient-start: hsl(var(--checkpoint-messages-gradient-start-hsl)/1); - --checkpoint-messages-gradient-start-hsl: 308.75 calc(var(--saturation-factor, 1)*98.969%) 61.961%; - --checkpoint-messages-primary: hsl(var(--checkpoint-messages-primary-hsl)/1); - --checkpoint-messages-primary-hsl: 315.084 calc(var(--saturation-factor, 1)*100%) 64.902%; - --checkpoint-messages-secondary: hsl(var(--checkpoint-messages-secondary-hsl)/1); - --checkpoint-messages-secondary-hsl: 315.319 calc(var(--saturation-factor, 1)*70.854%) 39.02%; - --checkpoint-persona-eight-background-overlay: hsl(var(--checkpoint-persona-eight-background-overlay-hsl)/1); - --checkpoint-persona-eight-background-overlay-hsl: 32 calc(var(--saturation-factor, 1)*60%) 9.804%; - --checkpoint-persona-eight-gradient-end: hsl(var(--checkpoint-persona-eight-gradient-end-hsl)/1); - --checkpoint-persona-eight-gradient-end-hsl: 31.154 calc(var(--saturation-factor, 1)*55.319%) 18.431%; - --checkpoint-persona-eight-gradient-start: hsl(var(--checkpoint-persona-eight-gradient-start-hsl)/1); - --checkpoint-persona-eight-gradient-start-hsl: 39.184 calc(var(--saturation-factor, 1)*56.322%) 65.882%; - --checkpoint-persona-eight-primary: hsl(var(--checkpoint-persona-eight-primary-hsl)/1); - --checkpoint-persona-eight-primary-hsl: 39.184 calc(var(--saturation-factor, 1)*56.322%) 65.882%; - --checkpoint-persona-eight-secondary: hsl(var(--checkpoint-persona-eight-secondary-hsl)/1); - --checkpoint-persona-eight-secondary-hsl: 39.612 calc(var(--saturation-factor, 1)*42.387%) 47.647%; - --checkpoint-persona-five-background-overlay: hsl(var(--checkpoint-persona-five-background-overlay-hsl)/1); - --checkpoint-persona-five-background-overlay-hsl: 339.545 calc(var(--saturation-factor, 1)*66.667%) 12.941%; - --checkpoint-persona-five-gradient-end: hsl(var(--checkpoint-persona-five-gradient-end-hsl)/1); - --checkpoint-persona-five-gradient-end-hsl: 339.231 calc(var(--saturation-factor, 1)*67.241%) 22.745%; - --checkpoint-persona-five-gradient-start: hsl(var(--checkpoint-persona-five-gradient-start-hsl)/1); - --checkpoint-persona-five-gradient-start-hsl: 339.114 calc(var(--saturation-factor, 1)*90.805%) 65.882%; - --checkpoint-persona-five-primary: hsl(var(--checkpoint-persona-five-primary-hsl)/1); - --checkpoint-persona-five-primary-hsl: 339.114 calc(var(--saturation-factor, 1)*90.805%) 65.882%; - --checkpoint-persona-five-secondary: hsl(var(--checkpoint-persona-five-secondary-hsl)/1); - --checkpoint-persona-five-secondary-hsl: 338.983 calc(var(--saturation-factor, 1)*71.66%) 48.431%; - --checkpoint-persona-four-background-overlay: hsl(var(--checkpoint-persona-four-background-overlay-hsl)/1); - --checkpoint-persona-four-background-overlay-hsl: 312.222 calc(var(--saturation-factor, 1)*93.103%) 11.373%; - --checkpoint-persona-four-gradient-end: hsl(var(--checkpoint-persona-four-gradient-end-hsl)/1); - --checkpoint-persona-four-gradient-end-hsl: 7.2 calc(var(--saturation-factor, 1)*53.191%) 9.216%; - --checkpoint-persona-four-gradient-start: hsl(var(--checkpoint-persona-four-gradient-start-hsl)/1); - --checkpoint-persona-four-gradient-start-hsl: 308.75 calc(var(--saturation-factor, 1)*98.969%) 61.961%; - --checkpoint-persona-four-primary: hsl(var(--checkpoint-persona-four-primary-hsl)/1); - --checkpoint-persona-four-primary-hsl: 312.083 calc(var(--saturation-factor, 1)*84.706%) 66.667%; - --checkpoint-persona-four-secondary: hsl(var(--checkpoint-persona-four-secondary-hsl)/1); - --checkpoint-persona-four-secondary-hsl: 315.319 calc(var(--saturation-factor, 1)*70.854%) 39.02%; - --checkpoint-persona-nine-background-overlay: hsl(var(--checkpoint-persona-nine-background-overlay-hsl)/1); - --checkpoint-persona-nine-background-overlay-hsl: 0 calc(var(--saturation-factor, 1)*0%) 14.902%; - --checkpoint-persona-nine-gradient-end: hsl(var(--checkpoint-persona-nine-gradient-end-hsl)/1); - --checkpoint-persona-nine-gradient-end-hsl: 0 calc(var(--saturation-factor, 1)*0%) 24.706%; - --checkpoint-persona-nine-gradient-start: hsl(var(--checkpoint-persona-nine-gradient-start-hsl)/1); - --checkpoint-persona-nine-gradient-start-hsl: 0 calc(var(--saturation-factor, 1)*0%) 93.333%; - --checkpoint-persona-nine-primary: hsl(var(--checkpoint-persona-nine-primary-hsl)/1); - --checkpoint-persona-nine-primary-hsl: 0 calc(var(--saturation-factor, 1)*0%) 93.333%; - --checkpoint-persona-nine-secondary: hsl(var(--checkpoint-persona-nine-secondary-hsl)/1); - --checkpoint-persona-nine-secondary-hsl: 0 calc(var(--saturation-factor, 1)*0%) 66.275%; - --checkpoint-persona-one-background-overlay: hsl(var(--checkpoint-persona-one-background-overlay-hsl)/1); - --checkpoint-persona-one-background-overlay-hsl: 169.5 calc(var(--saturation-factor, 1)*76.923%) 10.196%; - --checkpoint-persona-one-gradient-end: hsl(var(--checkpoint-persona-one-gradient-end-hsl)/1); - --checkpoint-persona-one-gradient-end-hsl: 168.98 calc(var(--saturation-factor, 1)*59.036%) 16.275%; - --checkpoint-persona-one-gradient-start: hsl(var(--checkpoint-persona-one-gradient-start-hsl)/1); - --checkpoint-persona-one-gradient-start-hsl: 169.811 calc(var(--saturation-factor, 1)*97.546%) 68.039%; - --checkpoint-persona-one-primary: hsl(var(--checkpoint-persona-one-primary-hsl)/1); - --checkpoint-persona-one-primary-hsl: 169.811 calc(var(--saturation-factor, 1)*97.546%) 68.039%; - --checkpoint-persona-one-secondary: hsl(var(--checkpoint-persona-one-secondary-hsl)/1); - --checkpoint-persona-one-secondary-hsl: 169.672 calc(var(--saturation-factor, 1)*68.539%) 34.902%; - --checkpoint-persona-seven-background-overlay: hsl(var(--checkpoint-persona-seven-background-overlay-hsl)/1); - --checkpoint-persona-seven-background-overlay-hsl: 44.516 calc(var(--saturation-factor, 1)*100%) 6.078%; - --checkpoint-persona-seven-gradient-end: hsl(var(--checkpoint-persona-seven-gradient-end-hsl)/1); - --checkpoint-persona-seven-gradient-end-hsl: 30.361 calc(var(--saturation-factor, 1)*85.567%) 19.02%; - --checkpoint-persona-seven-gradient-start: hsl(var(--checkpoint-persona-seven-gradient-start-hsl)/1); - --checkpoint-persona-seven-gradient-start-hsl: 49.891 calc(var(--saturation-factor, 1)*100%) 63.922%; - --checkpoint-persona-seven-primary: hsl(var(--checkpoint-persona-seven-primary-hsl)/1); - --checkpoint-persona-seven-primary-hsl: 49.891 calc(var(--saturation-factor, 1)*100%) 63.922%; - --checkpoint-persona-seven-secondary: hsl(var(--checkpoint-persona-seven-secondary-hsl)/1); - --checkpoint-persona-seven-secondary-hsl: 49.935 calc(var(--saturation-factor, 1)*74.163%) 40.98%; - --checkpoint-persona-six-background-overlay: hsl(var(--checkpoint-persona-six-background-overlay-hsl)/1); - --checkpoint-persona-six-background-overlay-hsl: 27 calc(var(--saturation-factor, 1)*80%) 9.804%; - --checkpoint-persona-six-gradient-end: hsl(var(--checkpoint-persona-six-gradient-end-hsl)/1); - --checkpoint-persona-six-gradient-end-hsl: 28.8 calc(var(--saturation-factor, 1)*53.191%) 9.216%; - --checkpoint-persona-six-gradient-start: hsl(var(--checkpoint-persona-six-gradient-start-hsl)/1); - --checkpoint-persona-six-gradient-start-hsl: 27.595 calc(var(--saturation-factor, 1)*100%) 53.529%; - --checkpoint-persona-six-primary: hsl(var(--checkpoint-persona-six-primary-hsl)/1); - --checkpoint-persona-six-primary-hsl: 26.834 calc(var(--saturation-factor, 1)*100%) 60.98%; - --checkpoint-persona-six-secondary: hsl(var(--checkpoint-persona-six-secondary-hsl)/1); - --checkpoint-persona-six-secondary-hsl: 26.809 calc(var(--saturation-factor, 1)*86.239%) 42.745%; - --checkpoint-persona-three-background-overlay: hsl(var(--checkpoint-persona-three-background-overlay-hsl)/1); - --checkpoint-persona-three-background-overlay-hsl: 262.105 calc(var(--saturation-factor, 1)*80.282%) 13.922%; - --checkpoint-persona-three-gradient-end: hsl(var(--checkpoint-persona-three-gradient-end-hsl)/1); - --checkpoint-persona-three-gradient-end-hsl: 283.2 calc(var(--saturation-factor, 1)*53.191%) 9.216%; - --checkpoint-persona-three-gradient-start: hsl(var(--checkpoint-persona-three-gradient-start-hsl)/1); - --checkpoint-persona-three-gradient-start-hsl: 260.114 calc(var(--saturation-factor, 1)*87.129%) 60.392%; - --checkpoint-persona-three-primary: hsl(var(--checkpoint-persona-three-primary-hsl)/1); - --checkpoint-persona-three-primary-hsl: 269.011 calc(var(--saturation-factor, 1)*88.35%) 79.804%; - --checkpoint-persona-three-secondary: hsl(var(--checkpoint-persona-three-secondary-hsl)/1); - --checkpoint-persona-three-secondary-hsl: 268.837 calc(var(--saturation-factor, 1)*72.067%) 64.902%; - --checkpoint-persona-two-background-overlay: hsl(var(--checkpoint-persona-two-background-overlay-hsl)/1); - --checkpoint-persona-two-background-overlay-hsl: 260.426 calc(var(--saturation-factor, 1)*70.149%) 13.137%; - --checkpoint-persona-two-gradient-end: hsl(var(--checkpoint-persona-two-gradient-end-hsl)/1); - --checkpoint-persona-two-gradient-end-hsl: 259.481 calc(var(--saturation-factor, 1)*79.381%) 19.02%; - --checkpoint-persona-two-gradient-start: hsl(var(--checkpoint-persona-two-gradient-start-hsl)/1); - --checkpoint-persona-two-gradient-start-hsl: 256.018 calc(var(--saturation-factor, 1)*96.507%) 55.098%; - --checkpoint-persona-two-primary: hsl(var(--checkpoint-persona-two-primary-hsl)/1); - --checkpoint-persona-two-primary-hsl: 217.982 calc(var(--saturation-factor, 1)*93.162%) 77.059%; - --checkpoint-persona-two-secondary: hsl(var(--checkpoint-persona-two-secondary-hsl)/1); - --checkpoint-persona-two-secondary-hsl: 234 calc(var(--saturation-factor, 1)*39.823%) 55.686%; - --checkpoint-persona-zero-background-overlay: hsl(var(--checkpoint-persona-zero-background-overlay-hsl)/1); - --checkpoint-persona-zero-background-overlay-hsl: 148.8 calc(var(--saturation-factor, 1)*60.976%) 8.039%; - --checkpoint-persona-zero-gradient-end: hsl(var(--checkpoint-persona-zero-gradient-end-hsl)/1); - --checkpoint-persona-zero-gradient-end-hsl: 149.333 calc(var(--saturation-factor, 1)*56.962%) 15.49%; - --checkpoint-persona-zero-gradient-start: hsl(var(--checkpoint-persona-zero-gradient-start-hsl)/1); - --checkpoint-persona-zero-gradient-start-hsl: 149.302 calc(var(--saturation-factor, 1)*50.588%) 50%; - --checkpoint-persona-zero-primary: hsl(var(--checkpoint-persona-zero-primary-hsl)/1); - --checkpoint-persona-zero-primary-hsl: 143.804 calc(var(--saturation-factor, 1)*83.636%) 56.863%; - --checkpoint-persona-zero-secondary: hsl(var(--checkpoint-persona-zero-secondary-hsl)/1); - --checkpoint-persona-zero-secondary-hsl: 143.673 calc(var(--saturation-factor, 1)*78.61%) 36.667%; - --checkpoint-quests-gradient-end: hsl(var(--checkpoint-quests-gradient-end-hsl)/1); - --checkpoint-quests-gradient-end-hsl: 283.2 calc(var(--saturation-factor, 1)*53.191%) 9.216%; - --checkpoint-quests-gradient-start: hsl(var(--checkpoint-quests-gradient-start-hsl)/1); - --checkpoint-quests-gradient-start-hsl: 260.114 calc(var(--saturation-factor, 1)*87.129%) 60.392%; - --checkpoint-quests-primary: hsl(var(--checkpoint-quests-primary-hsl)/1); - --checkpoint-quests-primary-hsl: 269.011 calc(var(--saturation-factor, 1)*88.35%) 79.804%; - --checkpoint-quests-secondary: hsl(var(--checkpoint-quests-secondary-hsl)/1); - --checkpoint-quests-secondary-hsl: 268.837 calc(var(--saturation-factor, 1)*72.067%) 64.902%; - --checkpoint-voice-gradient-end: hsl(var(--checkpoint-voice-gradient-end-hsl)/1); - --checkpoint-voice-gradient-end-hsl: 30.361 calc(var(--saturation-factor, 1)*85.567%) 19.02%; - --checkpoint-voice-gradient-start: hsl(var(--checkpoint-voice-gradient-start-hsl)/1); - --checkpoint-voice-gradient-start-hsl: 49.891 calc(var(--saturation-factor, 1)*100%) 63.922%; - --checkpoint-voice-primary: hsl(var(--checkpoint-voice-primary-hsl)/1); - --checkpoint-voice-primary-hsl: 49.891 calc(var(--saturation-factor, 1)*100%) 63.922%; - --checkpoint-voice-secondary: hsl(var(--checkpoint-voice-secondary-hsl)/1); - --checkpoint-voice-secondary-hsl: 49.935 calc(var(--saturation-factor, 1)*74.163%) 40.98%; - --checkpoint-welcome-gradient-end: hsl(var(--checkpoint-welcome-gradient-end-hsl)/1); - --checkpoint-welcome-gradient-end-hsl: 149.333 calc(var(--saturation-factor, 1)*56.962%) 15.49%; - --checkpoint-welcome-gradient-start: hsl(var(--checkpoint-welcome-gradient-start-hsl)/1); - --checkpoint-welcome-gradient-start-hsl: 149.302 calc(var(--saturation-factor, 1)*50.588%) 50%; - --checkpoint-welcome-primary: hsl(var(--checkpoint-welcome-primary-hsl)/1); - --checkpoint-welcome-primary-hsl: 143.316 calc(var(--saturation-factor, 1)*83.55%) 54.706%; - --checkpoint-welcome-secondary: hsl(var(--checkpoint-welcome-secondary-hsl)/1); - --checkpoint-welcome-secondary-hsl: 143.673 calc(var(--saturation-factor, 1)*78.61%) 36.667%; - --crunchyroll: hsl(var(--crunchyroll-hsl)/1); - --crunchyroll-hsl: 29.289 calc(var(--saturation-factor, 1)*92.952%) 55.49%; - --ebay: hsl(var(--ebay-hsl)/1); - --ebay-hsl: 211.429 calc(var(--saturation-factor, 1)*100%) 41.176%; - --epic-games: hsl(var(--epic-games-hsl)/1); - --epic-games-hsl: 34.286 calc(var(--saturation-factor, 1)*6.195%) 22.157%; - --facebook: hsl(var(--facebook-hsl)/1); - --facebook-hsl: 220.714 calc(var(--saturation-factor, 1)*44.211%) 37.255%; - --github: hsl(var(--github-hsl)/1); - --github-hsl: 0 calc(var(--saturation-factor, 1)*4.167%) 9.412%; - --gold: hsl(var(--gold-hsl)/1); - --gold-hsl: 45.652 calc(var(--saturation-factor, 1)*100%) 54.902%; - --guild-boosting-blue: hsl(var(--guild-boosting-blue-hsl)/1); - --guild-boosting-blue-hsl: 221.132 calc(var(--saturation-factor, 1)*70.044%) 55.49%; - --guild-boosting-blue-for-gradients: hsl(var(--guild-boosting-blue-for-gradients-hsl)/1); - --guild-boosting-blue-for-gradients-hsl: 234.909 calc(var(--saturation-factor, 1)*68.465%) 52.745%; - --guild-boosting-pink: hsl(var(--guild-boosting-pink-hsl)/1); - --guild-boosting-pink-hsl: 302.143 calc(var(--saturation-factor, 1)*100%) 72.549%; - --guild-boosting-pink-refresh: hsl(var(--guild-boosting-pink-refresh-hsl)/1); - --guild-boosting-pink-refresh-hsl: 315.084 calc(var(--saturation-factor, 1)*100%) 64.902%; - --guild-boosting-purple: hsl(var(--guild-boosting-purple-hsl)/1); - --guild-boosting-purple-hsl: 269.032 calc(var(--saturation-factor, 1)*83.784%) 70.98%; - --guild-boosting-purple-for-gradients: hsl(var(--guild-boosting-purple-for-gradients-hsl)/1); - --guild-boosting-purple-for-gradients-hsl: 269.162 calc(var(--saturation-factor, 1)*91.795%) 61.765%; - --hypesquad-house-1: hsl(var(--hypesquad-house-1-hsl)/1); - --hypesquad-house-1-hsl: 253.458 calc(var(--saturation-factor, 1)*76.978%) 72.745%; - --hypesquad-house-2: hsl(var(--hypesquad-house-2-hsl)/1); - --hypesquad-house-2-hsl: 8.511 calc(var(--saturation-factor, 1)*86.503%) 68.039%; - --hypesquad-house-3: hsl(var(--hypesquad-house-3-hsl)/1); - --hypesquad-house-3-hsl: 168.553 calc(var(--saturation-factor, 1)*69.091%) 56.863%; - --illo-blue-10: hsl(var(--illo-blue-10-hsl)/1); - --illo-blue-10-hsl: 235.2 calc(var(--saturation-factor, 1)*100%) 95.098%; - --illo-blue-20: hsl(var(--illo-blue-20-hsl)/1); - --illo-blue-20-hsl: 234.737 calc(var(--saturation-factor, 1)*90.476%) 87.647%; - --illo-blue-30: hsl(var(--illo-blue-30-hsl)/1); - --illo-blue-30-hsl: 234 calc(var(--saturation-factor, 1)*90%) 80.392%; - --illo-blue-40: hsl(var(--illo-blue-40-hsl)/1); - --illo-blue-40-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --illo-blue-50: hsl(var(--illo-blue-50-hsl)/1); - --illo-blue-50-hsl: 236.25 calc(var(--saturation-factor, 1)*58.716%) 42.745%; - --illo-blue-60: hsl(var(--illo-blue-60-hsl)/1); - --illo-blue-60-hsl: 237.931 calc(var(--saturation-factor, 1)*72.5%) 31.373%; - --illo-blue-70: hsl(var(--illo-blue-70-hsl)/1); - --illo-blue-70-hsl: 240 calc(var(--saturation-factor, 1)*100%) 20%; - --illo-green-10: hsl(var(--illo-green-10-hsl)/1); - --illo-green-10-hsl: 145.385 calc(var(--saturation-factor, 1)*100%) 94.902%; - --illo-green-20: hsl(var(--illo-green-20-hsl)/1); - --illo-green-20-hsl: 144.407 calc(var(--saturation-factor, 1)*88.06%) 86.863%; - --illo-green-30: hsl(var(--illo-green-30-hsl)/1); - --illo-green-30-hsl: 143.67 calc(var(--saturation-factor, 1)*87.2%) 75.49%; - --illo-green-40: hsl(var(--illo-green-40-hsl)/1); - --illo-green-40-hsl: 143.804 calc(var(--saturation-factor, 1)*83.636%) 56.863%; - --illo-green-50: hsl(var(--illo-green-50-hsl)/1); - --illo-green-50-hsl: 145.714 calc(var(--saturation-factor, 1)*70%) 43.137%; - --illo-green-60: hsl(var(--illo-green-60-hsl)/1); - --illo-green-60-hsl: 149.048 calc(var(--saturation-factor, 1)*79.747%) 30.98%; - --illo-green-70: hsl(var(--illo-green-70-hsl)/1); - --illo-green-70-hsl: 152.941 calc(var(--saturation-factor, 1)*100%) 20%; - --illo-nitro-blue: hsl(var(--illo-nitro-blue-hsl)/1); - --illo-nitro-blue-hsl: 240 calc(var(--saturation-factor, 1)*82.063%) 43.725%; - --illo-orange-10: hsl(var(--illo-orange-10-hsl)/1); - --illo-orange-10-hsl: 33.571 calc(var(--saturation-factor, 1)*100%) 83.529%; - --illo-orange-20: hsl(var(--illo-orange-20-hsl)/1); - --illo-orange-20-hsl: 28.029 calc(var(--saturation-factor, 1)*100%) 73.137%; - --illo-orange-30: hsl(var(--illo-orange-30-hsl)/1); - --illo-orange-30-hsl: 25.532 calc(var(--saturation-factor, 1)*98.947%) 62.745%; - --illo-orange-40: hsl(var(--illo-orange-40-hsl)/1); - --illo-orange-40-hsl: 24.149 calc(var(--saturation-factor, 1)*99.177%) 52.353%; - --illo-orange-50: hsl(var(--illo-orange-50-hsl)/1); - --illo-orange-50-hsl: 21.391 calc(var(--saturation-factor, 1)*90.551%) 49.804%; - --illo-orange-60: hsl(var(--illo-orange-60-hsl)/1); - --illo-orange-60-hsl: 18.356 calc(var(--saturation-factor, 1)*91.632%) 46.863%; - --illo-orange-70: hsl(var(--illo-orange-70-hsl)/1); - --illo-orange-70-hsl: 15 calc(var(--saturation-factor, 1)*92.035%) 44.314%; - --illo-pink-10: hsl(var(--illo-pink-10-hsl)/1); - --illo-pink-10-hsl: 290.4 calc(var(--saturation-factor, 1)*100%) 95.098%; - --illo-pink-20: hsl(var(--illo-pink-20-hsl)/1); - --illo-pink-20-hsl: 308 calc(var(--saturation-factor, 1)*90.909%) 87.059%; - --illo-pink-30: hsl(var(--illo-pink-30-hsl)/1); - --illo-pink-30-hsl: 312 calc(var(--saturation-factor, 1)*96.154%) 79.608%; - --illo-pink-40: hsl(var(--illo-pink-40-hsl)/1); - --illo-pink-40-hsl: 315.084 calc(var(--saturation-factor, 1)*100%) 64.902%; - --illo-pink-50: hsl(var(--illo-pink-50-hsl)/1); - --illo-pink-50-hsl: 309.787 calc(var(--saturation-factor, 1)*64.977%) 42.549%; - --illo-pink-60: hsl(var(--illo-pink-60-hsl)/1); - --illo-pink-60-hsl: 305.455 calc(var(--saturation-factor, 1)*76.101%) 31.176%; - --illo-pink-70: hsl(var(--illo-pink-70-hsl)/1); - --illo-pink-70-hsl: 300 calc(var(--saturation-factor, 1)*100%) 20%; - --illo-purple-10: hsl(var(--illo-purple-10-hsl)/1); - --illo-purple-10-hsl: 271.2 calc(var(--saturation-factor, 1)*100%) 95.098%; - --illo-purple-20: hsl(var(--illo-purple-20-hsl)/1); - --illo-purple-20-hsl: 268.966 calc(var(--saturation-factor, 1)*90.625%) 87.451%; - --illo-purple-30: hsl(var(--illo-purple-30-hsl)/1); - --illo-purple-30-hsl: 269.011 calc(var(--saturation-factor, 1)*88.35%) 79.804%; - --illo-purple-40: hsl(var(--illo-purple-40-hsl)/1); - --illo-purple-40-hsl: 268.462 calc(var(--saturation-factor, 1)*85.714%) 64.314%; - --illo-purple-50: hsl(var(--illo-purple-50-hsl)/1); - --illo-purple-50-hsl: 264.275 calc(var(--saturation-factor, 1)*59.817%) 42.941%; - --illo-purple-60: hsl(var(--illo-purple-60-hsl)/1); - --illo-purple-60-hsl: 260.87 calc(var(--saturation-factor, 1)*72.327%) 31.176%; - --illo-purple-70: hsl(var(--illo-purple-70-hsl)/1); - --illo-purple-70-hsl: 257.059 calc(var(--saturation-factor, 1)*100%) 20%; - --illo-yellow-10: hsl(var(--illo-yellow-10-hsl)/1); - --illo-yellow-10-hsl: 50 calc(var(--saturation-factor, 1)*100%) 87.059%; - --illo-yellow-20: hsl(var(--illo-yellow-20-hsl)/1); - --illo-yellow-20-hsl: 49.714 calc(var(--saturation-factor, 1)*100%) 79.412%; - --illo-yellow-30: hsl(var(--illo-yellow-30-hsl)/1); - --illo-yellow-30-hsl: 50.069 calc(var(--saturation-factor, 1)*100%) 71.569%; - --illo-yellow-40: hsl(var(--illo-yellow-40-hsl)/1); - --illo-yellow-40-hsl: 49.891 calc(var(--saturation-factor, 1)*100%) 63.922%; - --illo-yellow-50: hsl(var(--illo-yellow-50-hsl)/1); - --illo-yellow-50-hsl: 42.551 calc(var(--saturation-factor, 1)*89.091%) 56.863%; - --illo-yellow-60: hsl(var(--illo-yellow-60-hsl)/1); - --illo-yellow-60-hsl: 35.942 calc(var(--saturation-factor, 1)*81.176%) 50%; - --illo-yellow-70: hsl(var(--illo-yellow-70-hsl)/1); - --illo-yellow-70-hsl: 30.137 calc(var(--saturation-factor, 1)*100%) 42.941%; - --lol: hsl(var(--lol-hsl)/1); - --lol-hsl: 190.286 calc(var(--saturation-factor, 1)*89.744%) 7.647%; - --lol-text-dark: hsl(var(--lol-text-dark-hsl)/1); - --lol-text-dark-hsl: 37.444 calc(var(--saturation-factor, 1)*81.595%) 31.961%; - --lol-text-light: hsl(var(--lol-text-light-hsl)/1); - --lol-text-light-hsl: 41.143 calc(var(--saturation-factor, 1)*50.239%) 59.02%; - --partner: hsl(var(--partner-hsl)/1); - --partner-hsl: 215.376 calc(var(--saturation-factor, 1)*82.775%) 59.02%; - --paypal: hsl(var(--paypal-hsl)/1); - --paypal-hsl: 230.455 calc(var(--saturation-factor, 1)*69.841%) 24.706%; - --playstation: hsl(var(--playstation-hsl)/1); - --playstation-hsl: 220 calc(var(--saturation-factor, 1)*95.652%) 27.059%; - --premium-nitro-pink-dark: hsl(var(--premium-nitro-pink-dark-hsl)/1); - --premium-nitro-pink-dark-hsl: 299.45 calc(var(--saturation-factor, 1)*43.083%) 50.392%; - --premium-nitro-pink-light: hsl(var(--premium-nitro-pink-light-hsl)/1); - --premium-nitro-pink-light-hsl: 299.333 calc(var(--saturation-factor, 1)*42.857%) 58.824%; - --premium-perk-blue: hsl(var(--premium-perk-blue-hsl)/1); - --premium-perk-blue-hsl: 222.047 calc(var(--saturation-factor, 1)*100%) 75.098%; - --premium-perk-blue-alt: hsl(var(--premium-perk-blue-alt-hsl)/1); - --premium-perk-blue-alt-hsl: 223.03 calc(var(--saturation-factor, 1)*100%) 80.588%; - --premium-perk-dark-blue: hsl(var(--premium-perk-dark-blue-hsl)/1); - --premium-perk-dark-blue-hsl: 220.392 calc(var(--saturation-factor, 1)*67.401%) 55.49%; - --premium-perk-gold: hsl(var(--premium-perk-gold-hsl)/1); - --premium-perk-gold-hsl: 37.5 calc(var(--saturation-factor, 1)*95.726%) 54.118%; - --premium-perk-green: hsl(var(--premium-perk-green-hsl)/1); - --premium-perk-green-hsl: 163.953 calc(var(--saturation-factor, 1)*55.128%) 69.412%; - --premium-perk-light-blue: hsl(var(--premium-perk-light-blue-hsl)/1); - --premium-perk-light-blue-hsl: 221.481 calc(var(--saturation-factor, 1)*100%) 84.118%; - --premium-perk-orange: hsl(var(--premium-perk-orange-hsl)/1); - --premium-perk-orange-hsl: 25.424 calc(var(--saturation-factor, 1)*96.721%) 64.118%; - --premium-perk-pink: hsl(var(--premium-perk-pink-hsl)/1); - --premium-perk-pink-hsl: 305.197 calc(var(--saturation-factor, 1)*100%) 75.098%; - --premium-perk-purple: hsl(var(--premium-perk-purple-hsl)/1); - --premium-perk-purple-hsl: 272.079 calc(var(--saturation-factor, 1)*100%) 80.196%; - --premium-perk-yellow: hsl(var(--premium-perk-yellow-hsl)/1); - --premium-perk-yellow-hsl: 46.813 calc(var(--saturation-factor, 1)*98.913%) 63.922%; - --premium-tier-0-blue: hsl(var(--premium-tier-0-blue-hsl)/1); - --premium-tier-0-blue-hsl: 201.649 calc(var(--saturation-factor, 1)*100%) 38.039%; - --premium-tier-0-blue-for-gradients: hsl(var(--premium-tier-0-blue-for-gradients-hsl)/1); - --premium-tier-0-blue-for-gradients-hsl: 201.649 calc(var(--saturation-factor, 1)*100%) 38.039%; - --premium-tier-0-blue-for-gradients-2: hsl(var(--premium-tier-0-blue-for-gradients-2-hsl)/1); - --premium-tier-0-blue-for-gradients-2-hsl: 209.022 calc(var(--saturation-factor, 1)*80%) 45.098%; - --premium-tier-0-header-gradient-1: hsl(var(--premium-tier-0-header-gradient-1-hsl)/1); - --premium-tier-0-header-gradient-1-hsl: 240.451 calc(var(--saturation-factor, 1)*55.187%) 47.255%; - --premium-tier-0-header-gradient-2: hsl(var(--premium-tier-0-header-gradient-2-hsl)/1); - --premium-tier-0-header-gradient-2-hsl: 224.444 calc(var(--saturation-factor, 1)*77.885%) 59.216%; - --premium-tier-0-header-gradient-3: hsl(var(--premium-tier-0-header-gradient-3-hsl)/1); - --premium-tier-0-header-gradient-3-hsl: 246.207 calc(var(--saturation-factor, 1)*74.359%) 69.412%; - --premium-tier-0-header-gradient-4: hsl(var(--premium-tier-0-header-gradient-4-hsl)/1); - --premium-tier-0-header-gradient-4-hsl: 294.595 calc(var(--saturation-factor, 1)*79.856%) 72.745%; - --premium-tier-0-header-gradient-5: hsl(var(--premium-tier-0-header-gradient-5-hsl)/1); - --premium-tier-0-header-gradient-5-hsl: 336.456 calc(var(--saturation-factor, 1)*55.245%) 71.961%; - --premium-tier-0-purple: hsl(var(--premium-tier-0-purple-hsl)/1); - --premium-tier-0-purple-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --premium-tier-0-purple-for-gradients: hsl(var(--premium-tier-0-purple-for-gradients-hsl)/1); - --premium-tier-0-purple-for-gradients-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --premium-tier-1-blue: hsl(var(--premium-tier-1-blue-hsl)/1); - --premium-tier-1-blue-hsl: 227.538 calc(var(--saturation-factor, 1)*86.667%) 70.588%; - --premium-tier-1-blue-for-gradients: hsl(var(--premium-tier-1-blue-for-gradients-hsl)/1); - --premium-tier-1-blue-for-gradients-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --premium-tier-1-dark-blue-for-gradients: hsl(var(--premium-tier-1-dark-blue-for-gradients-hsl)/1); - --premium-tier-1-dark-blue-for-gradients-hsl: 234.909 calc(var(--saturation-factor, 1)*68.465%) 52.745%; - --premium-tier-1-purple: hsl(var(--premium-tier-1-purple-hsl)/1); - --premium-tier-1-purple-hsl: 243.704 calc(var(--saturation-factor, 1)*100%) 84.118%; - --premium-tier-2-pink: hsl(var(--premium-tier-2-pink-hsl)/1); - --premium-tier-2-pink-hsl: 342 calc(var(--saturation-factor, 1)*57.971%) 72.941%; - --premium-tier-2-pink-for-gradients: hsl(var(--premium-tier-2-pink-for-gradients-hsl)/1); - --premium-tier-2-pink-for-gradients-hsl: 325.385 calc(var(--saturation-factor, 1)*31.707%) 51.765%; - --premium-tier-2-pink-for-gradients-2: hsl(var(--premium-tier-2-pink-for-gradients-2-hsl)/1); - --premium-tier-2-pink-for-gradients-2-hsl: 295.42 calc(var(--saturation-factor, 1)*51.373%) 50%; - --premium-tier-2-purple: hsl(var(--premium-tier-2-purple-hsl)/1); - --premium-tier-2-purple-hsl: 270 calc(var(--saturation-factor, 1)*86.667%) 70.588%; - --premium-tier-2-purple-for-gradients: hsl(var(--premium-tier-2-purple-for-gradients-hsl)/1); - --premium-tier-2-purple-for-gradients-hsl: 269.291 calc(var(--saturation-factor, 1)*52.697%) 52.745%; - --premium-tier-2-purple-for-gradients-2: hsl(var(--premium-tier-2-purple-for-gradients-2-hsl)/1); - --premium-tier-2-purple-for-gradients-2-hsl: 295.645 calc(var(--saturation-factor, 1)*50%) 51.373%; - --reddit: hsl(var(--reddit-hsl)/1); - --reddit-hsl: 16.235 calc(var(--saturation-factor, 1)*100%) 50%; - --riot-games: hsl(var(--riot-games-hsl)/1); - --riot-games-hsl: 349.487 calc(var(--saturation-factor, 1)*100%) 45.882%; - --role-blue: hsl(var(--role-blue-hsl)/1); - --role-blue-hsl: 203.793 calc(var(--saturation-factor, 1)*64.444%) 35.294%; - --role-brown: hsl(var(--role-brown-hsl)/1); - --role-brown-hsl: 23.929 calc(var(--saturation-factor, 1)*100%) 32.941%; - --role-burgundy: hsl(var(--role-burgundy-hsl)/1); - --role-burgundy-hsl: 333.725 calc(var(--saturation-factor, 1)*79.275%) 37.843%; - --role-dark-blue: hsl(var(--role-dark-blue-hsl)/1); - --role-dark-blue-hsl: 198.947 calc(var(--saturation-factor, 1)*18.447%) 40.392%; - --role-dark-grey: hsl(var(--role-dark-grey-hsl)/1); - --role-dark-grey-hsl: 199.535 calc(var(--saturation-factor, 1)*18.298%) 46.078%; - --role-dark-purple: hsl(var(--role-dark-purple-hsl)/1); - --role-dark-purple-hsl: 282.143 calc(var(--saturation-factor, 1)*43.75%) 37.647%; - --role-dark-teal: hsl(var(--role-dark-teal-hsl)/1); - --role-dark-teal-hsl: 168.108 calc(var(--saturation-factor, 1)*76.552%) 28.431%; - --role-default: hsl(var(--role-default-hsl)/1); - --role-default-hsl: 203.571 calc(var(--saturation-factor, 1)*15.909%) 65.49%; - --role-green: hsl(var(--role-green-hsl)/1); - --role-green-hsl: 145 calc(var(--saturation-factor, 1)*63.529%) 33.333%; - --role-grey: hsl(var(--role-grey-hsl)/1); - --role-grey-hsl: 202.5 calc(var(--saturation-factor, 1)*4%) 60.784%; - --role-light-blue: hsl(var(--role-light-blue-hsl)/1); - --role-light-blue-hsl: 203.571 calc(var(--saturation-factor, 1)*15.909%) 65.49%; - --role-light-green: hsl(var(--role-light-green-hsl)/1); - --role-light-green-hsl: 145.443 calc(var(--saturation-factor, 1)*63.2%) 49.02%; - --role-light-grey: hsl(var(--role-light-grey-hsl)/1); - --role-light-grey-hsl: 183.529 calc(var(--saturation-factor, 1)*8.718%) 61.765%; - --role-magenta: hsl(var(--role-magenta-hsl)/1); - --role-magenta-hsl: 339.606 calc(var(--saturation-factor, 1)*82.186%) 51.569%; - --role-orange: hsl(var(--role-orange-hsl)/1); - --role-orange-hsl: 28.163 calc(var(--saturation-factor, 1)*79.675%) 51.765%; - --role-purple: hsl(var(--role-purple-hsl)/1); - --role-purple-hsl: 282.581 calc(var(--saturation-factor, 1)*38.912%) 53.137%; - --role-salmon: hsl(var(--role-salmon-hsl)/1); - --role-salmon-hsl: 5.614 calc(var(--saturation-factor, 1)*78.082%) 57.059%; - --role-sky-blue: hsl(var(--role-sky-blue-hsl)/1); - --role-sky-blue-hsl: 204.072 calc(var(--saturation-factor, 1)*69.874%) 53.137%; - --role-tan: hsl(var(--role-tan-hsl)/1); - --role-tan-hsl: 36.667 calc(var(--saturation-factor, 1)*86.538%) 40.784%; - --role-teal: hsl(var(--role-teal-hsl)/1); - --role-teal-hsl: 168.148 calc(var(--saturation-factor, 1)*75.701%) 41.961%; - --role-terracotta: hsl(var(--role-terracotta-hsl)/1); - --role-terracotta-hsl: 5.546 calc(var(--saturation-factor, 1)*63.636%) 36.667%; - --role-yellow: hsl(var(--role-yellow-hsl)/1); - --role-yellow-hsl: 48.053 calc(var(--saturation-factor, 1)*88.976%) 50.196%; - --samsung: hsl(var(--samsung-hsl)/1); - --samsung-hsl: 231 calc(var(--saturation-factor, 1)*77.778%) 35.294%; - --skype: hsl(var(--skype-hsl)/1); - --skype-hsl: 196.186 calc(var(--saturation-factor, 1)*100%) 42.157%; - --spotify: hsl(var(--spotify-hsl)/1); - --spotify-hsl: 141.154 calc(var(--saturation-factor, 1)*72.897%) 41.961%; - --steam: hsl(var(--steam-hsl)/1); - --steam-hsl: 214.615 calc(var(--saturation-factor, 1)*35.135%) 14.51%; - --transparent: hsl(var(--transparent-hsl)/0); - --transparent-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --twitch: hsl(var(--twitch-hsl)/1); - --twitch-hsl: 264.13 calc(var(--saturation-factor, 1)*100%) 63.922%; - --twitch-secondary: hsl(var(--twitch-secondary-hsl)/1); - --twitch-secondary-hsl: 263.936 calc(var(--saturation-factor, 1)*80.342%) 54.118%; - --twitter: hsl(var(--twitter-hsl)/1); - --twitter-hsl: 202.817 calc(var(--saturation-factor, 1)*89.121%) 53.137%; - --xbox: hsl(var(--xbox-hsl)/1); - --xbox-hsl: 120 calc(var(--saturation-factor, 1)*77.143%) 27.451%; - --youtube: hsl(var(--youtube-hsl)/1); - --youtube-hsl: 0.351 calc(var(--saturation-factor, 1)*72.766%) 46.078% -} - -.theme-dark { - --app-frame-background: var(--neutral-78); - --app-frame-border: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --app-message-embed-secondary-text: hsl(var(--white-hsl)/0.7); - --background-accent: var(--primary-530); - --background-base-low: var(--neutral-66); - --background-base-lower: var(--neutral-69); - --background-base-lowest: var(--neutral-73); - --background-brand: var(--blurple-50); - --background-code: hsl(var(--opacity-blurple-8-hsl)/0.0784313725490196); - --background-code-addition: hsl(var(--opacity-green-12-hsl)/0.12156862745098039); - --background-code-deletion: hsl(var(--opacity-red-12-hsl)/0.12156862745098039); - --background-feedback-critical: hsl(var(--opacity-red-8-hsl)/0.0784313725490196); - --background-feedback-info: hsl(var(--opacity-blue-8-hsl)/0.0784313725490196); - --background-feedback-notification: var(--red-new-46); - --background-feedback-positive: hsl(var(--opacity-green-8-hsl)/0.0784313725490196); - --background-feedback-warning: hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196); - --background-mod-muted: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --background-mod-normal: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --background-mod-strong: hsl(var(--opacity-20-hsl)/0.2); - --background-mod-subtle: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --background-scrim: hsl(var(--opacity-black-72-hsl)/0.7215686274509804); - --background-scrim-lightbox: hsl(var(--opacity-black-92-hsl)/0.9215686274509803); - --background-secondary-alt: var(--primary-660); - --background-surface-high: var(--neutral-64); - --background-surface-higher: var(--neutral-62); - --background-surface-highest: var(--neutral-60); - --background-tile-gradient-pink-end: hsl(var(--illo-pink-70-hsl)/0.3); - --background-tile-gradient-pink-start: hsl(var(--illo-pink-50-hsl)/0.3); - --badge-background-brand: var(--blurple-50); - --badge-background-default: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --badge-expressive-background-default: var(--neutral-1); - --badge-expressive-text-default: var(--neutral-71); - --badge-notification-background: var(--red-new-46); - --badge-text-brand: var(--neutral-1); - --badge-text-default: var(--neutral-1); - --bg-surface-raised: var(--primary-560); - --border-feedback-critical: hsl(var(--opacity-red-20-hsl)/0.2); - --border-focus: var(--blue-new-30); - --border-muted: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --border-normal: hsl(var(--opacity-20-hsl)/0.2); - --border-strong: hsl(var(--opacity-44-hsl)/0.4392156862745098); - --border-subtle: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --button-danger-background-disabled: var(--red-new-50); - --button-outline-brand-background-hover: var(--brand-500); - --button-outline-brand-border-active: var(--brand-560); - --button-outline-primary-text: var(--white); - --card-background-default: var(--neutral-64); - --card-primary-pressed-bg: var(--primary-645); - --card-secondary-bg: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --card-secondary-pressed-bg: var(--primary-645); - --channel-icon: var(--neutral-28); - --channel-text-area-placeholder: var(--primary-430); - --channels-default: var(--neutral-28); - --channeltextarea-background: var(--primary-560); - --chat-background: var(--primary-600); - --chat-background-default: var(--neutral-64); - --chat-border: var(--primary-700); - --chat-text-muted: var(--neutral-27); - --checkbox-background-active: var(--blurple-65); - --checkbox-background-default: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --checkbox-background-hover: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --checkbox-background-selected-default: var(--blurple-50); - --checkbox-background-selected-hover: var(--blurple-60); - --checkbox-border-active: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --checkbox-border-default: var(--neutral-34); - --checkbox-border-hover: hsl(var(--opacity-80-hsl)/0.8); - --checkbox-border-selected-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --checkbox-border-selected-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --checkbox-icon-active: var(--neutral-1); - --chip-blurple-dark-background: var(--blurple-70); - --chip-blurple-dark-text: var(--blurple-1); - --chip-blurple-light-background: var(--blurple-20); - --chip-blurple-light-text: var(--blurple-91); - --chip-blurple-medium-background: var(--blurple-40); - --chip-blurple-medium-text: var(--blurple-100); - --chip-gray-dark-background: var(--neutral-75); - --chip-gray-dark-text: var(--neutral-6); - --chip-gray-light-background: var(--neutral-10); - --chip-gray-light-text: var(--neutral-68); - --chip-gray-medium-background: var(--neutral-40); - --chip-gray-medium-text: var(--neutral-1); - --chip-green-dark-background: var(--green-new-70); - --chip-green-dark-text: var(--green-new-1); - --chip-green-light-background: var(--green-new-20); - --chip-green-light-text: var(--green-new-91); - --chip-green-medium-background: var(--green-new-45); - --chip-green-medium-text: var(--green-new-96); - --chip-orange-dark-background: var(--orange-new-70); - --chip-orange-dark-text: var(--orange-new-1); - --chip-orange-light-background: var(--orange-new-20); - --chip-orange-light-text: var(--orange-new-93); - --chip-orange-medium-background: var(--orange-new-40); - --chip-orange-medium-text: var(--orange-new-100); - --chip-pink-dark-background: var(--illo-pink-60); - --chip-pink-dark-text: var(--neutral-1); - --chip-pink-light-background: var(--illo-pink-20); - --chip-pink-light-text: var(--neutral-73); - --chip-pink-medium-background: var(--illo-pink-40); - --chip-pink-medium-text: var(--neutral-100); - --chip-purple-dark-background: var(--illo-purple-60); - --chip-purple-dark-text: var(--neutral-10); - --chip-purple-light-background: var(--illo-purple-20); - --chip-purple-light-text: var(--neutral-75); - --chip-purple-medium-background: var(--illo-purple-40); - --chip-purple-medium-text: var(--neutral-100); - --chip-red-dark-background: var(--red-new-70); - --chip-red-dark-text: var(--red-new-1); - --chip-red-light-background: var(--red-new-20); - --chip-red-light-text: var(--red-new-95); - --chip-red-medium-background: var(--red-new-45); - --chip-red-medium-text: var(--red-new-100); - --chip-yellow-dark-background: var(--yellow-new-70); - --chip-yellow-dark-text: var(--yellow-new-1); - --chip-yellow-light-background: var(--yellow-new-20); - --chip-yellow-light-text: var(--yellow-new-87); - --chip-yellow-medium-background: var(--yellow-new-45); - --chip-yellow-medium-text: var(--yellow-new-80); - --content-inventory-media-seekbar-container: hsl(var(--plum-6-hsl)/0.24); - --content-inventory-overlay-text-primary: hsl(var(--white-hsl)/0.85); - --content-inventory-overlay-text-secondary: hsl(var(--white-hsl)/0.7); - --context-menu-backdrop-background: hsl(var(--opacity-black-72-hsl)/0.7215686274509804); - --control-brand-foreground: var(--brand-360); - --control-brand-foreground-new: var(--brand-360); - --control-connected-background-active: var(--green-new-65); - --control-connected-background-default: var(--green-new-50); - --control-connected-background-hover: var(--green-new-60); - --control-connected-border-active: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-connected-border-default: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-connected-border-hover: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-connected-icon-active: var(--neutral-1); - --control-connected-icon-default: var(--neutral-1); - --control-connected-icon-hover: var(--neutral-1); - --control-connected-text-active: var(--neutral-1); - --control-connected-text-default: var(--neutral-1); - --control-connected-text-hover: var(--neutral-1); - --control-critical-primary-background-active: var(--red-new-65); - --control-critical-primary-background-default: var(--red-new-50); - --control-critical-primary-background-hover: var(--red-new-60); - --control-critical-primary-border-active: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-critical-primary-border-default: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-critical-primary-border-hover: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-critical-primary-icon-active: var(--neutral-1); - --control-critical-primary-icon-default: var(--neutral-1); - --control-critical-primary-icon-hover: var(--neutral-1); - --control-critical-primary-text-active: var(--neutral-1); - --control-critical-primary-text-default: var(--neutral-1); - --control-critical-primary-text-hover: var(--neutral-1); - --control-critical-secondary-background-active: hsl(var(--opacity-20-hsl)/0.2); - --control-critical-secondary-background-default: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --control-critical-secondary-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-critical-secondary-border-active: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-critical-secondary-border-default: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-critical-secondary-border-hover: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-critical-secondary-icon-active: var(--red-new-22); - --control-critical-secondary-icon-default: var(--red-new-22); - --control-critical-secondary-icon-hover: var(--red-new-22); - --control-critical-secondary-text-active: var(--red-new-22); - --control-critical-secondary-text-default: var(--red-new-22); - --control-critical-secondary-text-hover: var(--red-new-22); - --control-expressive-background-active: var(--neutral-5); - --control-expressive-background-default: var(--neutral-1); - --control-expressive-background-hover: var(--neutral-1); - --control-expressive-border-active: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-expressive-border-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-expressive-border-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-expressive-icon-active: var(--neutral-100); - --control-expressive-icon-default: var(--neutral-100); - --control-expressive-icon-hover: var(--neutral-100); - --control-expressive-text-active: var(--neutral-100); - --control-expressive-text-default: var(--neutral-100); - --control-expressive-text-hover: var(--neutral-100); - --control-icon-only-background-active: hsl(var(--opacity-20-hsl)/0.2); - --control-icon-only-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-icon-only-border-active: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-icon-only-border-hover: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-icon-only-icon-active: var(--neutral-1); - --control-icon-only-icon-default: var(--neutral-16); - --control-icon-only-icon-hover: var(--neutral-1); - --control-overlay-primary-background-active: var(--neutral-17); - --control-overlay-primary-background-default: var(--neutral-1); - --control-overlay-primary-background-hover: var(--neutral-9); - --control-overlay-primary-border-active: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-overlay-primary-border-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-overlay-primary-border-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-overlay-primary-icon-active: var(--neutral-100); - --control-overlay-primary-icon-default: var(--neutral-100); - --control-overlay-primary-icon-hover: var(--neutral-100); - --control-overlay-primary-text-active: var(--neutral-100); - --control-overlay-primary-text-default: var(--neutral-100); - --control-overlay-primary-text-hover: var(--neutral-100); - --control-overlay-secondary-background-active: hsl(var(--opacity-black-48-hsl)/0.47843137254901963); - --control-overlay-secondary-background-default: hsl(var(--opacity-black-52-hsl)/0.5215686274509804); - --control-overlay-secondary-background-hover: hsl(var(--opacity-black-64-hsl)/0.6392156862745098); - --control-overlay-secondary-border-active: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-overlay-secondary-border-default: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-overlay-secondary-border-hover: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-overlay-secondary-icon-active: var(--neutral-1); - --control-overlay-secondary-icon-default: var(--neutral-1); - --control-overlay-secondary-icon-hover: var(--neutral-1); - --control-overlay-secondary-text-active: var(--neutral-1); - --control-overlay-secondary-text-default: var(--neutral-1); - --control-overlay-secondary-text-hover: var(--neutral-1); - --control-primary-background-active: var(--blurple-65); - --control-primary-background-default: var(--blurple-50); - --control-primary-background-hover: var(--blurple-60); - --control-primary-border-active: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-primary-border-default: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-primary-border-hover: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-primary-icon-active: var(--neutral-1); - --control-primary-icon-default: var(--neutral-1); - --control-primary-icon-hover: var(--neutral-1); - --control-primary-text-active: var(--neutral-1); - --control-primary-text-default: var(--neutral-1); - --control-primary-text-hover: var(--neutral-1); - --control-secondary-background-active: hsl(var(--opacity-20-hsl)/0.2); - --control-secondary-background-default: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --control-secondary-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-secondary-border-active: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-secondary-border-default: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-secondary-border-hover: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-secondary-icon-active: var(--neutral-1); - --control-secondary-icon-default: var(--neutral-1); - --control-secondary-icon-hover: var(--neutral-1); - --control-secondary-text-active: var(--neutral-1); - --control-secondary-text-default: var(--neutral-1); - --control-secondary-text-hover: var(--neutral-1); - --creator-revenue-icon-gradient-end: var(--teal-430); - --creator-revenue-icon-gradient-start: var(--teal-360); - --creator-revenue-info-box-background: hsl(var(--teal-430-hsl)/0.1); - --creator-revenue-info-box-border: var(--teal-400); - --creator-revenue-locked-channel-icon: var(--teal-345); - --creator-revenue-progress-bar: var(--teal-400); - --embed-background: var(--primary-630); - --embed-background-alternate: var(--primary-600); - --experimental-avatar-embed-bg: hsl(var(--opacity-black-52-hsl)/0.5215686274509804); - --expressive-gradient-blue-end: hsl(var(--illo-blue-60-hsl)/0.45); - --expressive-gradient-blue-start: hsl(var(--illo-blue-40-hsl)/0.45); - --expressive-gradient-green-end: hsl(var(--illo-green-70-hsl)/0.45); - --expressive-gradient-green-start: hsl(var(--illo-green-50-hsl)/0.45); - --expressive-gradient-nitro-green-end: hsl(var(--illo-nitro-blue-hsl)/0.5); - --expressive-gradient-nitro-green-start: hsl(var(--illo-green-50-hsl)/0.5); - --expressive-gradient-nitro-pink-end: hsl(var(--illo-nitro-blue-hsl)/0.5); - --expressive-gradient-nitro-pink-start: hsl(var(--illo-pink-60-hsl)/0.5); - --expressive-gradient-pink-end: hsl(var(--illo-pink-70-hsl)/0.45); - --expressive-gradient-pink-start: hsl(var(--illo-pink-50-hsl)/0.45); - --expressive-gradient-purple-end: hsl(var(--illo-purple-60-hsl)/0.45); - --expressive-gradient-purple-start: hsl(var(--illo-purple-40-hsl)/0.45); - --expressive-gradient-tenure-badge-bronze-end: hsl(var(--illo-orange-70-hsl)/0.3); - --expressive-gradient-tenure-badge-bronze-start: hsl(var(--illo-orange-30-hsl)/0.3); - --expressive-gradient-tenure-badge-diamond-end: hsl(var(--illo-purple-50-hsl)/0.3); - --expressive-gradient-tenure-badge-diamond-start: hsl(var(--illo-purple-40-hsl)/0.3); - --expressive-gradient-tenure-badge-emerald-end: hsl(var(--illo-green-60-hsl)/0.3); - --expressive-gradient-tenure-badge-emerald-start: hsl(var(--illo-green-40-hsl)/0.3); - --expressive-gradient-tenure-badge-gold-end: hsl(var(--yellow-new-41-hsl)/0.3); - --expressive-gradient-tenure-badge-gold-start: hsl(var(--illo-yellow-50-hsl)/0.3); - --expressive-gradient-tenure-badge-opal-end: hsl(var(--blue-new-50-hsl)/0.3); - --expressive-gradient-tenure-badge-opal-start: hsl(var(--teal-new-30-hsl)/0.3); - --expressive-gradient-tenure-badge-platinum-end: hsl(var(--teal-new-60-hsl)/0.3); - --expressive-gradient-tenure-badge-platinum-start: hsl(var(--teal-new-20-hsl)/0.3); - --expressive-gradient-tenure-badge-ruby-end: hsl(var(--red-new-80-hsl)/0.3); - --expressive-gradient-tenure-badge-ruby-start: hsl(var(--red-new-44-hsl)/0.3); - --expressive-gradient-tenure-badge-silver-end: hsl(var(--neutral-58-hsl)/0.3); - --expressive-gradient-tenure-badge-silver-start: hsl(var(--neutral-12-hsl)/0.3); - --gradient-progress-pill-background: var(--neutral-46); - --guild-profile-banner-background-default: var(--neutral-73); - --home-background: var(--primary-645); - --icon-default: var(--neutral-10); - --icon-feedback-critical: var(--red-new-22); - --icon-feedback-info: var(--blue-new-24); - --icon-feedback-notification: var(--red-new-46); - --icon-feedback-positive: var(--green-new-25); - --icon-feedback-warning: var(--yellow-new-46); - --icon-invert: var(--neutral-71); - --icon-link: var(--blue-new-27); - --icon-muted: var(--neutral-23); - --icon-overlay-dark: var(--neutral-71); - --icon-overlay-light: var(--neutral-1); - --icon-status-dnd: var(--red-new-45); - --icon-status-idle: var(--yellow-new-22); - --icon-status-offline: var(--neutral-27); - --icon-status-online: var(--green-new-40); - --icon-strong: var(--neutral-1); - --icon-subtle: var(--neutral-16); - --icon-transparent: hsl(var(--transparent-hsl)/0); - --icon-voice-connected: var(--green-new-25); - --icon-voice-disconnected: var(--red-new-22); - --icon-voice-muted: var(--red-new-46); - --icon-voice-speaking: var(--green-new-40); - --input-background-default: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --input-background-error-default: hsl(var(--opacity-red-4-hsl)/0.0392156862745098); - --input-border-active: var(--blurple-50); - --input-border-default: hsl(var(--opacity-20-hsl)/0.2); - --input-border-error-default: var(--red-new-22); - --input-border-hover: hsl(var(--opacity-20-hsl)/0.2); - --input-border-readonly: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --input-icon-default: var(--neutral-16); - --input-placeholder-text-default: var(--neutral-31); - --input-text-default: var(--neutral-10); - --input-text-error-default: var(--neutral-10); - --interactive-background-active: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --interactive-background-hover: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --interactive-background-selected: hsl(var(--opacity-20-hsl)/0.2); - --interactive-icon-active: var(--neutral-1); - --interactive-icon-default: var(--neutral-16); - --interactive-icon-hover: var(--neutral-1); - --interactive-muted: var(--primary-500); - --interactive-text-active: var(--neutral-1); - --interactive-text-default: var(--neutral-16); - --interactive-text-hover: var(--neutral-1); - --logo-primary: var(--white); - --mention-background: hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981); - --mention-foreground: var(--blurple-8); - --message-automod-background-default: hsl(var(--red-400-hsl)/0.05); - --message-automod-background-hover: hsl(var(--red-400-hsl)/0.1); - --message-background-hover: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --message-highlight-background-default: hsl(var(--opacity-blurple-16-hsl)/0.1607843137254902); - --message-highlight-background-hover: hsl(var(--opacity-blurple-12-hsl)/0.12156862745098039); - --message-mentioned-background-default: hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196); - --message-mentioned-background-hover: hsl(var(--opacity-yellow-4-hsl)/0.0392156862745098); - --message-reacted-background-default: hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981); - --message-reacted-text-default: var(--blurple-3); - --mobile-background-scrim-opaque: var(--black); - --mobile-expression-picker-background-default: var(--neutral-69); - --mobile-text-heading-primary: var(--neutral-1); - --modal-background: var(--neutral-64); - --modal-footer-background: var(--neutral-64); - --navigator-header-tint: var(--white); - --notice-background-critical: var(--red-new-75); - --notice-background-info: var(--blue-new-76); - --notice-background-positive: var(--green-new-77); - --notice-background-warning: var(--yellow-new-84); - --notice-text-critical: var(--red-new-1); - --notice-text-info: var(--blue-new-1); - --notice-text-positive: var(--green-new-3); - --notice-text-warning: var(--yellow-new-1); - --overlay-backdrop-lightbox: hsl(var(--opacity-black-92-hsl)/0.9215686274509803); - --panel-bg: var(--neutral-66); - --polls-normal-image-background: var(--primary-660); - --polls-victor-fill: hsl(var(--green-360-hsl)/0.2); - --polls-voted-fill: hsl(var(--brand-500-hsl)/0.2); - --premium-nitro-pink-text: var(--pink-34); - --profile-gradient-note-background: hsl(var(--black-hsl)/0.3); - --profile-gradient-overlay: hsl(var(--black-hsl)/0.6); - --profile-gradient-overlay-synced-with-user-theme: hsl(var(--black-hsl)/0.8); - --profile-gradient-role-pill-background: hsl(var(--primary-660-hsl)/0.5); - --profile-gradient-role-pill-border: hsl(var(--white-hsl)/0.2); - --profile-gradient-section-box: hsl(var(--black-hsl)/0.45); - --progressbar-indicator-background: var(--blurple-50); - --progressbar-track-background: hsl(var(--opacity-20-hsl)/0.2); - --radio-background-active: var(--blurple-65); - --radio-background-default: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --radio-background-hover: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --radio-background-selected-default: var(--blurple-50); - --radio-background-selected-hover: var(--blurple-60); - --radio-border-active: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --radio-border-default: hsl(var(--opacity-64-hsl)/0.6392156862745098); - --radio-border-hover: hsl(var(--opacity-80-hsl)/0.8); - --radio-border-selected-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --radio-border-selected-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --radio-foreground-active: var(--blurple-50); - --radio-foreground-default: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --radio-foreground-hover: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --radio-thumb-background-active: var(--neutral-1); - --redesign-button-premium-primary-pink-for-gradient: var(--premium-tier-2-pink-for-gradients); - --redesign-button-premium-primary-pressed-background: hsl(var(--black-hsl)/0.1); - --redesign-button-premium-primary-purple-for-gradient: var(--premium-tier-2-purple-for-gradients); - --redesign-button-premium-primary-purple-for-gradient-2: var(--premium-tier-2-purple-for-gradients-2); - --redesign-button-tertiary-background: var(--primary-660); - --redesign-button-tertiary-pressed-background: var(--primary-560); - --redesign-button-tertiary-pressed-text: var(--primary-330); - --redesign-button-tertiary-text: var(--neutral-10); - --scrollbar-auto-scrollbar-color-thumb: var(--neutral-39); - --scrollbar-auto-scrollbar-color-track: var(--neutral-74); - --scrollbar-auto-thumb: var(--neutral-36); - --scrollbar-auto-track: hsl(var(--transparent-hsl)/0); - --scrollbar-thin-thumb: var(--neutral-38); - --scrollbar-thin-track: hsl(var(--black-hsl)/0); - --slider-track-background: var(--neutral-47); - --spine-default: var(--neutral-47); - --spoiler-hidden-background: var(--neutral-36); - --spoiler-hidden-background-hover: var(--neutral-27); - --spoiler-revealed-background: var(--primary-660); - --status-danger: var(--red-400); - --status-online: var(--green-360); - --status-positive: var(--green-360); - --status-positive-background: var(--green-430); - --status-positive-text: var(--white); - --status-speaking: var(--green-360); - --status-warning: var(--yellow-300); - --status-warning-background: var(--yellow-300); - --status-warning-text: var(--black); - --switch-background-active: var(--blurple-65); - --switch-background-default: hsl(var(--opacity-black-12-hsl)/0.12156862745098039); - --switch-background-hover: hsl(var(--opacity-black-12-hsl)/0.12156862745098039); - --switch-background-selected-default: var(--blurple-50); - --switch-background-selected-hover: var(--blurple-60); - --switch-border-default: hsl(var(--opacity-20-hsl)/0.2); - --switch-border-hover: hsl(var(--opacity-40-hsl)/0.4); - --switch-border-selected-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --switch-border-selected-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --switch-thumb-background-default: var(--neutral-1); - --switch-thumb-background-selected-default: var(--neutral-1); - --switch-thumb-icon-active: var(--blurple-50); - --switch-thumb-icon-default: var(--neutral-71); - --text-brand: var(--blurple-26); - --text-code: var(--blue-new-16); - --text-code-addition: var(--green-new-15); - --text-code-builtin: var(--orange-new-14); - --text-code-bullet: var(--yellow-new-32); - --text-code-comment: var(--neutral-26); - --text-code-deletion: var(--red-new-11); - --text-code-keyword: var(--red-new-12); - --text-code-section: var(--blue-new-16); - --text-code-string: var(--teal-new-17); - --text-code-tag: var(--green-new-12); - --text-code-title: var(--blurple-9); - --text-code-variable: var(--blue-new-10); - --text-default: var(--neutral-10); - --text-feedback-critical: var(--red-new-22); - --text-feedback-info: var(--blue-new-24); - --text-feedback-positive: var(--green-new-25); - --text-feedback-warning: var(--yellow-new-46); - --text-invert: var(--neutral-71); - --text-link: var(--blue-new-27); - --text-muted: var(--neutral-23); - --text-overlay-dark: var(--neutral-71); - --text-overlay-light: var(--neutral-1); - --text-status-dnd: var(--red-new-45); - --text-status-idle: var(--yellow-new-22); - --text-status-offline: var(--neutral-27); - --text-status-online: var(--green-new-40); - --text-strong: var(--neutral-1); - --text-subtle: var(--neutral-16); - --text-voice-connected: var(--green-new-25); - --text-voice-disconnected: var(--red-new-22); - --text-voice-speaking: var(--green-new-40); - --textbox-markdown-syntax: var(--primary-360); - --thread-channel-spine: var(--primary-500); - --user-profile-activity-toolbar-background: var(--neutral-60); - --user-profile-background-hover: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --user-profile-border: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --user-profile-note-background-focus: var(--neutral-73); - --user-profile-overlay-background: var(--neutral-60); - --user-profile-overlay-background-hover: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --user-profile-toolbar-background: var(--neutral-60); - --user-profile-toolbar-border: hsl(var(--opacity-4-hsl)/0.0392156862745098) -} - -@supports (color: color-mix(in lch,red,blue)) { - .theme-dark { - --app-frame-background:color-mix(in oklab,var(--neutral-78) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --app-frame-border: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --app-message-embed-secondary-text: color-mix(in oklab,hsl(var(--white-hsl)/0.7) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.7) var(--custom-theme-text-color-amount,0%)); - --background-accent: color-mix(in oklab,var(--primary-530) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-base-low: color-mix(in oklab,var(--neutral-66) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-base-lower: color-mix(in oklab,var(--neutral-69) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-base-lowest: color-mix(in oklab,var(--neutral-73) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-code: color-mix(in oklab,hsl(var(--opacity-blurple-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-code-addition: color-mix(in oklab,hsl(var(--opacity-green-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-code-deletion: color-mix(in oklab,hsl(var(--opacity-red-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-critical: color-mix(in oklab,hsl(var(--opacity-red-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-feedback-info: color-mix(in oklab,hsl(var(--opacity-blue-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-feedback-positive: color-mix(in oklab,hsl(var(--opacity-green-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-feedback-warning: color-mix(in oklab,hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-scrim: color-mix(in oklab,hsl(var(--opacity-black-72-hsl)/0.7215686274509804) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.7215686274509804) var(--custom-theme-base-color-amount,0%)); - --background-scrim-lightbox: color-mix(in oklab,hsl(var(--opacity-black-92-hsl)/0.9215686274509803) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.9215686274509803) var(--custom-theme-base-color-amount,0%)); - --background-secondary-alt: color-mix(in oklab,var(--primary-660) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-surface-high: color-mix(in oklab,var(--neutral-64) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-surface-higher: color-mix(in oklab,var(--neutral-62) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-surface-highest: color-mix(in oklab,var(--neutral-60) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-tile-gradient-pink-end: color-mix(in oklab,hsl(var(--illo-pink-70-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --background-tile-gradient-pink-start: color-mix(in oklab,hsl(var(--illo-pink-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --bg-surface-raised: color-mix(in oklab,var(--primary-560) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --border-feedback-critical: color-mix(in oklab,hsl(var(--opacity-red-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-focus: color-mix(in oklab,var(--blue-new-30) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-muted: color-mix(in oklab,hsl(var(--opacity-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-normal: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-strong: color-mix(in oklab,hsl(var(--opacity-44-hsl)/0.4392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.4392156862745098) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-subtle: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --card-background-default: color-mix(in oklab,var(--neutral-64) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --card-primary-pressed-bg: color-mix(in oklab,var(--primary-645) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --card-secondary-bg: color-mix(in oklab,hsl(var(--opacity-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --card-secondary-pressed-bg: color-mix(in oklab,var(--primary-645) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --channel-icon: color-mix(in oklab,var(--neutral-28) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channel-text-area-placeholder: color-mix(in oklab,var(--primary-430) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channels-default: color-mix(in oklab,var(--neutral-28) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channeltextarea-background: color-mix(in oklab,var(--primary-560) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --chat-background: color-mix(in oklab,var(--primary-600) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --chat-background-default: color-mix(in oklab,var(--neutral-64) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --chat-border: color-mix(in oklab,var(--primary-700) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --chat-text-muted: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --content-inventory-media-seekbar-container: color-mix(in oklab,hsl(var(--plum-6-hsl)/0.24) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.24) var(--custom-theme-base-color-amount,0%)); - --content-inventory-overlay-text-primary: color-mix(in oklab,hsl(var(--white-hsl)/0.85) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.85) var(--custom-theme-text-color-amount,0%)); - --content-inventory-overlay-text-secondary: color-mix(in oklab,hsl(var(--white-hsl)/0.7) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.7) var(--custom-theme-text-color-amount,0%)); - --context-menu-backdrop-background: color-mix(in oklab,hsl(var(--opacity-black-72-hsl)/0.7215686274509804) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.7215686274509804) var(--custom-theme-base-color-amount,0%)); - --control-brand-foreground: color-mix(in oklab,var(--brand-360) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --control-brand-foreground-new: color-mix(in oklab,var(--brand-360) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --control-secondary-border-active: color-mix(in oklab,hsl(var(--opacity-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --control-secondary-border-default: color-mix(in oklab,hsl(var(--opacity-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --creator-revenue-icon-gradient-end: color-mix(in oklab,var(--teal-430) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --creator-revenue-icon-gradient-start: color-mix(in oklab,var(--teal-360) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --creator-revenue-info-box-background: color-mix(in oklab,hsl(var(--teal-430-hsl)/0.1) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1) var(--custom-theme-base-color-amount,0%)); - --creator-revenue-info-box-border: color-mix(in oklab,var(--teal-400) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --creator-revenue-locked-channel-icon: color-mix(in oklab,var(--teal-345) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --creator-revenue-progress-bar: color-mix(in oklab,var(--teal-400) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --embed-background: color-mix(in oklab,var(--primary-630) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --embed-background-alternate: color-mix(in oklab,var(--primary-600) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --experimental-avatar-embed-bg: color-mix(in oklab,hsl(var(--opacity-black-52-hsl)/0.5215686274509804) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.5215686274509804) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-blue-end: color-mix(in oklab,hsl(var(--illo-blue-60-hsl)/0.45) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.45) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-blue-start: color-mix(in oklab,hsl(var(--illo-blue-40-hsl)/0.45) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.45) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-green-end: color-mix(in oklab,hsl(var(--illo-green-70-hsl)/0.45) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.45) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-green-start: color-mix(in oklab,hsl(var(--illo-green-50-hsl)/0.45) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.45) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-green-end: color-mix(in oklab,hsl(var(--illo-nitro-blue-hsl)/0.5) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.5) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-green-start: color-mix(in oklab,hsl(var(--illo-green-50-hsl)/0.5) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.5) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-pink-end: color-mix(in oklab,hsl(var(--illo-nitro-blue-hsl)/0.5) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.5) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-pink-start: color-mix(in oklab,hsl(var(--illo-pink-60-hsl)/0.5) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.5) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-pink-end: color-mix(in oklab,hsl(var(--illo-pink-70-hsl)/0.45) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.45) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-pink-start: color-mix(in oklab,hsl(var(--illo-pink-50-hsl)/0.45) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.45) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-purple-end: color-mix(in oklab,hsl(var(--illo-purple-60-hsl)/0.45) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.45) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-purple-start: color-mix(in oklab,hsl(var(--illo-purple-40-hsl)/0.45) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.45) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-bronze-end: color-mix(in oklab,hsl(var(--illo-orange-70-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-bronze-start: color-mix(in oklab,hsl(var(--illo-orange-30-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-diamond-end: color-mix(in oklab,hsl(var(--illo-purple-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-diamond-start: color-mix(in oklab,hsl(var(--illo-purple-40-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-emerald-end: color-mix(in oklab,hsl(var(--illo-green-60-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-emerald-start: color-mix(in oklab,hsl(var(--illo-green-40-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-gold-end: color-mix(in oklab,hsl(var(--yellow-new-41-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-gold-start: color-mix(in oklab,hsl(var(--illo-yellow-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-opal-end: color-mix(in oklab,hsl(var(--blue-new-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-opal-start: color-mix(in oklab,hsl(var(--teal-new-30-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-platinum-end: color-mix(in oklab,hsl(var(--teal-new-60-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-platinum-start: color-mix(in oklab,hsl(var(--teal-new-20-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-ruby-end: color-mix(in oklab,hsl(var(--red-new-80-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-ruby-start: color-mix(in oklab,hsl(var(--red-new-44-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-silver-end: color-mix(in oklab,hsl(var(--neutral-58-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-silver-start: color-mix(in oklab,hsl(var(--neutral-12-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --gradient-progress-pill-background: color-mix(in oklab,var(--neutral-46) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --guild-profile-banner-background-default: color-mix(in oklab,var(--neutral-73) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --home-background: color-mix(in oklab,var(--primary-645) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --icon-default: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-critical: color-mix(in oklab,var(--red-new-22) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-info: color-mix(in oklab,var(--blue-new-24) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-positive: color-mix(in oklab,var(--green-new-25) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-warning: color-mix(in oklab,var(--yellow-new-46) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-invert: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-link: color-mix(in oklab,var(--blue-new-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-muted: color-mix(in oklab,var(--neutral-23) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-overlay-dark: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-overlay-light: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-dnd: color-mix(in oklab,var(--red-new-45) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-idle: color-mix(in oklab,var(--yellow-new-22) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-offline: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-online: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-strong: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-subtle: color-mix(in oklab,var(--neutral-16) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-transparent: color-mix(in oklab,hsl(var(--transparent-hsl)/0) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0) var(--custom-theme-text-color-amount,0%)); - --icon-voice-connected: color-mix(in oklab,var(--green-new-25) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-voice-disconnected: color-mix(in oklab,var(--red-new-22) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-voice-speaking: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-background-default: color-mix(in oklab,hsl(var(--opacity-black-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --input-background-error-default: color-mix(in oklab,hsl(var(--opacity-red-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-base-color-amount,0%)); - --input-border-active: color-mix(in oklab,var(--blurple-50) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-default: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-error-default: color-mix(in oklab,var(--red-new-22) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-hover: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-readonly: color-mix(in oklab,hsl(var(--opacity-white-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-icon-default: color-mix(in oklab,var(--neutral-16) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-placeholder-text-default: color-mix(in oklab,var(--neutral-31) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-default: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-error-default: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-background-active: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --interactive-background-hover: color-mix(in oklab,hsl(var(--opacity-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --interactive-background-selected: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --interactive-icon-active: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-default: color-mix(in oklab,var(--neutral-16) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-hover: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-muted: color-mix(in oklab,var(--primary-500) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-active: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-default: color-mix(in oklab,var(--neutral-16) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-hover: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --logo-primary: color-mix(in oklab,var(--white) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --mention-background: color-mix(in oklab,hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --mention-foreground: color-mix(in oklab,var(--blurple-8) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --message-automod-background-default: color-mix(in oklab,hsl(var(--red-400-hsl)/0.05) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.05) var(--custom-theme-base-color-amount,0%)); - --message-automod-background-hover: color-mix(in oklab,hsl(var(--red-400-hsl)/0.1) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1) var(--custom-theme-base-color-amount,0%)); - --message-background-hover: color-mix(in oklab,hsl(var(--opacity-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-default: color-mix(in oklab,hsl(var(--opacity-blurple-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-hover: color-mix(in oklab,hsl(var(--opacity-blurple-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-default: color-mix(in oklab,hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-hover: color-mix(in oklab,hsl(var(--opacity-yellow-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-base-color-amount,0%)); - --message-reacted-background-default: color-mix(in oklab,hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --message-reacted-text-default: color-mix(in oklab,var(--blurple-3) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --mobile-background-scrim-opaque: color-mix(in oklab,var(--black) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --mobile-expression-picker-background-default: color-mix(in oklab,var(--neutral-69) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --mobile-text-heading-primary: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --modal-background: color-mix(in oklab,var(--neutral-64) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --modal-footer-background: color-mix(in oklab,var(--neutral-64) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --navigator-header-tint: color-mix(in oklab,var(--white) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --overlay-backdrop-lightbox: color-mix(in oklab,hsl(var(--opacity-black-92-hsl)/0.9215686274509803) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.9215686274509803) var(--custom-theme-base-color-amount,0%)); - --panel-bg: color-mix(in oklab,var(--neutral-66) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --polls-normal-image-background: color-mix(in oklab,var(--primary-660) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --polls-victor-fill: color-mix(in oklab,hsl(var(--green-360-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --polls-voted-fill: color-mix(in oklab,hsl(var(--brand-500-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --premium-nitro-pink-text: color-mix(in oklab,var(--pink-34) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --profile-gradient-note-background: color-mix(in oklab,hsl(var(--black-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-overlay: color-mix(in oklab,hsl(var(--black-hsl)/0.6) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.6) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-overlay-synced-with-user-theme: color-mix(in oklab,hsl(var(--black-hsl)/0.8) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.8) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-role-pill-background: color-mix(in oklab,hsl(var(--primary-660-hsl)/0.5) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.5) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-role-pill-border: color-mix(in oklab,hsl(var(--white-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --profile-gradient-section-box: color-mix(in oklab,hsl(var(--black-hsl)/0.45) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.45) var(--custom-theme-base-color-amount,0%)); - --progressbar-indicator-background: color-mix(in oklab,var(--blurple-50) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --progressbar-track-background: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-pink-for-gradient: color-mix(in oklab,var(--premium-tier-2-pink-for-gradients) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-pressed-background: color-mix(in oklab,hsl(var(--black-hsl)/0.1) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-purple-for-gradient: color-mix(in oklab,var(--premium-tier-2-purple-for-gradients) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-purple-for-gradient-2: color-mix(in oklab,var(--premium-tier-2-purple-for-gradients-2) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-background: color-mix(in oklab,var(--primary-660) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-pressed-background: color-mix(in oklab,var(--primary-560) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-pressed-text: color-mix(in oklab,var(--primary-330) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --redesign-button-tertiary-text: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --scrollbar-auto-scrollbar-color-thumb: color-mix(in oklab,var(--neutral-39) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-auto-scrollbar-color-track: color-mix(in oklab,var(--neutral-74) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-auto-thumb: color-mix(in oklab,var(--neutral-36) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-auto-track: color-mix(in oklab,hsl(var(--transparent-hsl)/0) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0) var(--custom-theme-base-color-amount,0%)); - --scrollbar-thin-thumb: color-mix(in oklab,var(--neutral-38) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-thin-track: color-mix(in oklab,hsl(var(--black-hsl)/0) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0) var(--custom-theme-base-color-amount,0%)); - --slider-track-background: color-mix(in oklab,var(--neutral-47) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --spine-default: color-mix(in oklab,var(--neutral-47) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --spoiler-hidden-background: color-mix(in oklab,var(--neutral-36) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --spoiler-hidden-background-hover: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --spoiler-revealed-background: color-mix(in oklab,var(--primary-660) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --text-brand: color-mix(in oklab,var(--blurple-26) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code: color-mix(in oklab,var(--blue-new-16) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-addition: color-mix(in oklab,var(--green-new-15) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-builtin: color-mix(in oklab,var(--orange-new-14) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-bullet: color-mix(in oklab,var(--yellow-new-32) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-comment: color-mix(in oklab,var(--neutral-26) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-deletion: color-mix(in oklab,var(--red-new-11) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-keyword: color-mix(in oklab,var(--red-new-12) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-section: color-mix(in oklab,var(--blue-new-16) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-string: color-mix(in oklab,var(--teal-new-17) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-tag: color-mix(in oklab,var(--green-new-12) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-title: color-mix(in oklab,var(--blurple-9) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-variable: color-mix(in oklab,var(--blue-new-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-default: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-critical: color-mix(in oklab,var(--red-new-22) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-info: color-mix(in oklab,var(--blue-new-24) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-positive: color-mix(in oklab,var(--green-new-25) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-warning: color-mix(in oklab,var(--yellow-new-46) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-invert: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-link: color-mix(in oklab,var(--blue-new-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-muted: color-mix(in oklab,var(--neutral-23) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-dnd: color-mix(in oklab,var(--red-new-45) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-idle: color-mix(in oklab,var(--yellow-new-22) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-offline: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-online: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-strong: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-subtle: color-mix(in oklab,var(--neutral-16) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-connected: color-mix(in oklab,var(--green-new-25) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-disconnected: color-mix(in oklab,var(--red-new-22) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-speaking: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --textbox-markdown-syntax: color-mix(in oklab,var(--primary-360) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --thread-channel-spine: color-mix(in oklab,var(--primary-500) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-activity-toolbar-background: color-mix(in oklab,var(--neutral-60) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-background-hover: color-mix(in oklab,hsl(var(--opacity-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --user-profile-border: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-note-background-focus: color-mix(in oklab,var(--neutral-73) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-overlay-background: color-mix(in oklab,var(--neutral-60) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-overlay-background-hover: color-mix(in oklab,hsl(var(--opacity-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --user-profile-toolbar-background: color-mix(in oklab,var(--neutral-60) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-toolbar-border: color-mix(in oklab,hsl(var(--opacity-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))) - } -} - -.theme-light { - --app-frame-background: var(--neutral-7); - --app-frame-border: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --app-message-embed-secondary-text: hsl(var(--white-hsl)/0.7); - --background-accent: var(--primary-430); - --background-base-low: var(--neutral-2); - --background-base-lower: var(--neutral-2); - --background-base-lowest: var(--neutral-4); - --background-brand: var(--blurple-50); - --background-code: hsl(var(--opacity-blurple-4-hsl)/0.0392156862745098); - --background-code-addition: hsl(var(--opacity-green-12-hsl)/0.12156862745098039); - --background-code-deletion: hsl(var(--opacity-red-12-hsl)/0.12156862745098039); - --background-feedback-critical: hsl(var(--opacity-red-8-hsl)/0.0784313725490196); - --background-feedback-info: hsl(var(--opacity-blue-8-hsl)/0.0784313725490196); - --background-feedback-notification: var(--red-new-50); - --background-feedback-positive: hsl(var(--opacity-green-8-hsl)/0.0784313725490196); - --background-feedback-warning: hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196); - --background-mod-muted: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --background-mod-normal: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --background-mod-strong: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --background-mod-subtle: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --background-scrim: hsl(var(--opacity-black-52-hsl)/0.5215686274509804); - --background-scrim-lightbox: hsl(var(--opacity-black-92-hsl)/0.9215686274509803); - --background-secondary-alt: var(--primary-160); - --background-surface-high: var(--neutral-1); - --background-surface-higher: var(--neutral-1); - --background-surface-highest: var(--neutral-1); - --background-tile-gradient-pink-end: hsl(var(--illo-pink-30-hsl)/0.3); - --background-tile-gradient-pink-start: hsl(var(--illo-pink-10-hsl)/0.3); - --badge-background-brand: var(--blurple-50); - --badge-background-default: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --badge-expressive-background-default: var(--blurple-50); - --badge-expressive-text-default: var(--neutral-1); - --badge-notification-background: var(--red-new-50); - --badge-text-brand: var(--neutral-1); - --badge-text-default: var(--neutral-71); - --bg-surface-raised: var(--white); - --border-feedback-critical: hsl(var(--opacity-red-24-hsl)/0.23921568627450981); - --border-focus: var(--blue-new-40); - --border-muted: hsl(var(--opacity-20-hsl)/0.2); - --border-normal: hsl(var(--opacity-36-hsl)/0.3607843137254902); - --border-strong: hsl(var(--opacity-48-hsl)/0.47843137254901963); - --border-subtle: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --button-danger-background-disabled: var(--red-new-50); - --button-outline-brand-background-hover: var(--brand-500); - --button-outline-brand-border-active: var(--brand-560); - --button-outline-primary-text: var(--black); - --card-background-default: var(--neutral-1); - --card-primary-pressed-bg: var(--primary-160); - --card-secondary-bg: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --card-secondary-pressed-bg: var(--primary-160); - --channel-icon: var(--neutral-43); - --channel-text-area-placeholder: var(--primary-400); - --channels-default: var(--neutral-43); - --channeltextarea-background: var(--primary-160); - --chat-background: var(--white); - --chat-background-default: var(--neutral-1); - --chat-border: var(--primary-200); - --chat-text-muted: var(--neutral-40); - --checkbox-background-active: var(--blurple-65); - --checkbox-background-default: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --checkbox-background-hover: hsl(var(--opacity-black-4-hsl)/0.0392156862745098); - --checkbox-background-selected-default: var(--blurple-50); - --checkbox-background-selected-hover: var(--blurple-60); - --checkbox-border-active: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --checkbox-border-default: var(--neutral-30); - --checkbox-border-hover: var(--neutral-46); - --checkbox-border-selected-default: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --checkbox-border-selected-hover: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --checkbox-icon-active: var(--neutral-1); - --chip-blurple-dark-background: var(--blurple-70); - --chip-blurple-dark-text: var(--blurple-1); - --chip-blurple-light-background: var(--blurple-20); - --chip-blurple-light-text: var(--blurple-91); - --chip-blurple-medium-background: var(--blurple-40); - --chip-blurple-medium-text: var(--blurple-100); - --chip-gray-dark-background: var(--neutral-75); - --chip-gray-dark-text: var(--neutral-6); - --chip-gray-light-background: var(--neutral-10); - --chip-gray-light-text: var(--neutral-68); - --chip-gray-medium-background: var(--neutral-40); - --chip-gray-medium-text: var(--neutral-1); - --chip-green-dark-background: var(--green-new-70); - --chip-green-dark-text: var(--green-new-1); - --chip-green-light-background: var(--green-new-20); - --chip-green-light-text: var(--green-new-91); - --chip-green-medium-background: var(--green-new-45); - --chip-green-medium-text: var(--green-new-96); - --chip-orange-dark-background: var(--orange-new-70); - --chip-orange-dark-text: var(--orange-new-1); - --chip-orange-light-background: var(--orange-new-20); - --chip-orange-light-text: var(--orange-new-93); - --chip-orange-medium-background: var(--orange-new-40); - --chip-orange-medium-text: var(--orange-new-100); - --chip-pink-dark-background: var(--illo-pink-60); - --chip-pink-dark-text: var(--neutral-1); - --chip-pink-light-background: var(--illo-pink-20); - --chip-pink-light-text: var(--neutral-73); - --chip-pink-medium-background: var(--illo-pink-40); - --chip-pink-medium-text: var(--neutral-100); - --chip-purple-dark-background: var(--illo-purple-60); - --chip-purple-dark-text: var(--neutral-10); - --chip-purple-light-background: var(--illo-purple-20); - --chip-purple-light-text: var(--neutral-75); - --chip-purple-medium-background: var(--illo-purple-40); - --chip-purple-medium-text: var(--neutral-100); - --chip-red-dark-background: var(--red-new-70); - --chip-red-dark-text: var(--red-new-1); - --chip-red-light-background: var(--red-new-20); - --chip-red-light-text: var(--red-new-95); - --chip-red-medium-background: var(--red-new-45); - --chip-red-medium-text: var(--red-new-100); - --chip-yellow-dark-background: var(--yellow-new-70); - --chip-yellow-dark-text: var(--yellow-new-1); - --chip-yellow-light-background: var(--yellow-new-20); - --chip-yellow-light-text: var(--yellow-new-87); - --chip-yellow-medium-background: var(--yellow-new-45); - --chip-yellow-medium-text: var(--yellow-new-80); - --content-inventory-media-seekbar-container: hsl(var(--plum-6-hsl)/0.24); - --content-inventory-overlay-text-primary: hsl(var(--white-hsl)/0.85); - --content-inventory-overlay-text-secondary: hsl(var(--white-hsl)/0.7); - --context-menu-backdrop-background: hsl(var(--opacity-black-52-hsl)/0.5215686274509804); - --control-brand-foreground: var(--brand-500); - --control-brand-foreground-new: var(--brand-500); - --control-connected-background-active: var(--green-new-65); - --control-connected-background-default: var(--green-new-50); - --control-connected-background-hover: var(--green-new-60); - --control-connected-border-active: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-connected-border-default: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-connected-border-hover: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-connected-icon-active: var(--neutral-1); - --control-connected-icon-default: var(--neutral-1); - --control-connected-icon-hover: var(--neutral-1); - --control-connected-text-active: var(--neutral-1); - --control-connected-text-default: var(--neutral-1); - --control-connected-text-hover: var(--neutral-1); - --control-critical-primary-background-active: var(--red-new-65); - --control-critical-primary-background-default: var(--red-new-50); - --control-critical-primary-background-hover: var(--red-new-60); - --control-critical-primary-border-active: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-critical-primary-border-default: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-critical-primary-border-hover: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-critical-primary-icon-active: var(--neutral-1); - --control-critical-primary-icon-default: var(--neutral-1); - --control-critical-primary-icon-hover: var(--neutral-1); - --control-critical-primary-text-active: var(--neutral-1); - --control-critical-primary-text-default: var(--neutral-1); - --control-critical-primary-text-hover: var(--neutral-1); - --control-critical-secondary-background-active: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --control-critical-secondary-background-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-critical-secondary-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-critical-secondary-border-active: hsl(var(--opacity-20-hsl)/0.2); - --control-critical-secondary-border-default: hsl(var(--opacity-20-hsl)/0.2); - --control-critical-secondary-border-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-critical-secondary-icon-active: var(--red-new-53); - --control-critical-secondary-icon-default: var(--red-new-53); - --control-critical-secondary-icon-hover: var(--red-new-53); - --control-critical-secondary-text-active: var(--red-new-53); - --control-critical-secondary-text-default: var(--red-new-53); - --control-critical-secondary-text-hover: var(--red-new-53); - --control-expressive-background-active: var(--blurple-65); - --control-expressive-background-default: var(--blurple-50); - --control-expressive-background-hover: var(--blurple-60); - --control-expressive-border-active: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-expressive-border-default: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-expressive-border-hover: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-expressive-icon-active: var(--neutral-1); - --control-expressive-icon-default: var(--neutral-1); - --control-expressive-icon-hover: var(--neutral-1); - --control-expressive-text-active: var(--neutral-1); - --control-expressive-text-default: var(--neutral-1); - --control-expressive-text-hover: var(--neutral-1); - --control-icon-only-background-active: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --control-icon-only-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-icon-only-border-active: hsl(var(--opacity-20-hsl)/0.2); - --control-icon-only-border-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-icon-only-icon-active: var(--neutral-71); - --control-icon-only-icon-default: var(--neutral-44); - --control-icon-only-icon-hover: var(--neutral-71); - --control-overlay-primary-background-active: var(--neutral-17); - --control-overlay-primary-background-default: var(--neutral-1); - --control-overlay-primary-background-hover: var(--neutral-9); - --control-overlay-primary-border-active: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-overlay-primary-border-default: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-overlay-primary-border-hover: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-overlay-primary-icon-active: var(--neutral-100); - --control-overlay-primary-icon-default: var(--neutral-100); - --control-overlay-primary-icon-hover: var(--neutral-100); - --control-overlay-primary-text-active: var(--neutral-100); - --control-overlay-primary-text-default: var(--neutral-100); - --control-overlay-primary-text-hover: var(--neutral-100); - --control-overlay-secondary-background-active: hsl(var(--opacity-black-48-hsl)/0.47843137254901963); - --control-overlay-secondary-background-default: hsl(var(--opacity-black-52-hsl)/0.5215686274509804); - --control-overlay-secondary-background-hover: hsl(var(--opacity-black-64-hsl)/0.6392156862745098); - --control-overlay-secondary-border-active: hsl(var(--opacity-20-hsl)/0.2); - --control-overlay-secondary-border-default: hsl(var(--opacity-20-hsl)/0.2); - --control-overlay-secondary-border-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-overlay-secondary-icon-active: var(--neutral-1); - --control-overlay-secondary-icon-default: var(--neutral-1); - --control-overlay-secondary-icon-hover: var(--neutral-1); - --control-overlay-secondary-text-active: var(--neutral-1); - --control-overlay-secondary-text-default: var(--neutral-1); - --control-overlay-secondary-text-hover: var(--neutral-1); - --control-primary-background-active: var(--blurple-65); - --control-primary-background-default: var(--blurple-50); - --control-primary-background-hover: var(--blurple-60); - --control-primary-border-active: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-primary-border-default: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-primary-border-hover: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-primary-icon-active: var(--neutral-1); - --control-primary-icon-default: var(--neutral-1); - --control-primary-icon-hover: var(--neutral-1); - --control-primary-text-active: var(--neutral-1); - --control-primary-text-default: var(--neutral-1); - --control-primary-text-hover: var(--neutral-1); - --control-secondary-background-active: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --control-secondary-background-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-secondary-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-secondary-border-active: hsl(var(--opacity-20-hsl)/0.2); - --control-secondary-border-default: hsl(var(--opacity-20-hsl)/0.2); - --control-secondary-border-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-secondary-icon-active: var(--neutral-71); - --control-secondary-icon-default: var(--neutral-71); - --control-secondary-icon-hover: var(--neutral-71); - --control-secondary-text-active: var(--neutral-71); - --control-secondary-text-default: var(--neutral-71); - --control-secondary-text-hover: var(--neutral-71); - --creator-revenue-icon-gradient-end: var(--teal-400); - --creator-revenue-icon-gradient-start: var(--teal-345); - --creator-revenue-info-box-background: hsl(var(--teal-430-hsl)/0.1); - --creator-revenue-info-box-border: var(--teal-400); - --creator-revenue-locked-channel-icon: var(--teal-400); - --creator-revenue-progress-bar: var(--teal-345); - --embed-background: var(--primary-130); - --embed-background-alternate: var(--primary-200); - --experimental-avatar-embed-bg: hsl(var(--opacity-black-52-hsl)/0.5215686274509804); - --expressive-gradient-blue-end: hsl(var(--illo-blue-30-hsl)/0.8); - --expressive-gradient-blue-start: hsl(var(--illo-blue-10-hsl)/0.8); - --expressive-gradient-green-end: hsl(var(--illo-green-30-hsl)/0.8); - --expressive-gradient-green-start: hsl(var(--illo-green-10-hsl)/0.8); - --expressive-gradient-nitro-green-end: hsl(var(--illo-blue-40-hsl)/0.9); - --expressive-gradient-nitro-green-start: hsl(var(--illo-green-10-hsl)/0.9); - --expressive-gradient-nitro-pink-end: hsl(var(--illo-blue-40-hsl)/0.9); - --expressive-gradient-nitro-pink-start: hsl(var(--illo-pink-30-hsl)/0.9); - --expressive-gradient-pink-end: hsl(var(--illo-pink-30-hsl)/0.8); - --expressive-gradient-pink-start: hsl(var(--illo-pink-10-hsl)/0.8); - --expressive-gradient-purple-end: hsl(var(--illo-purple-30-hsl)/0.8); - --expressive-gradient-purple-start: hsl(var(--illo-purple-10-hsl)/0.8); - --expressive-gradient-tenure-badge-bronze-end: hsl(var(--illo-orange-70-hsl)/0.3); - --expressive-gradient-tenure-badge-bronze-start: hsl(var(--illo-orange-30-hsl)/0.3); - --expressive-gradient-tenure-badge-diamond-end: hsl(var(--illo-purple-50-hsl)/0.3); - --expressive-gradient-tenure-badge-diamond-start: hsl(var(--illo-purple-40-hsl)/0.3); - --expressive-gradient-tenure-badge-emerald-end: hsl(var(--illo-green-60-hsl)/0.3); - --expressive-gradient-tenure-badge-emerald-start: hsl(var(--illo-green-40-hsl)/0.3); - --expressive-gradient-tenure-badge-gold-end: hsl(var(--yellow-new-41-hsl)/0.3); - --expressive-gradient-tenure-badge-gold-start: hsl(var(--illo-yellow-50-hsl)/0.3); - --expressive-gradient-tenure-badge-opal-end: hsl(var(--blue-new-50-hsl)/0.3); - --expressive-gradient-tenure-badge-opal-start: hsl(var(--teal-new-30-hsl)/0.3); - --expressive-gradient-tenure-badge-platinum-end: hsl(var(--teal-new-60-hsl)/0.3); - --expressive-gradient-tenure-badge-platinum-start: hsl(var(--teal-new-20-hsl)/0.3); - --expressive-gradient-tenure-badge-ruby-end: hsl(var(--red-new-80-hsl)/0.3); - --expressive-gradient-tenure-badge-ruby-start: hsl(var(--red-new-44-hsl)/0.3); - --expressive-gradient-tenure-badge-silver-end: hsl(var(--neutral-58-hsl)/0.3); - --expressive-gradient-tenure-badge-silver-start: hsl(var(--neutral-12-hsl)/0.3); - --gradient-progress-pill-background: var(--neutral-18); - --guild-profile-banner-background-default: var(--neutral-40); - --home-background: var(--primary-100); - --icon-default: var(--neutral-69); - --icon-feedback-critical: var(--red-new-53); - --icon-feedback-info: var(--blue-new-56); - --icon-feedback-notification: var(--red-new-50); - --icon-feedback-positive: var(--green-new-55); - --icon-feedback-warning: var(--yellow-new-70); - --icon-invert: var(--neutral-1); - --icon-link: var(--blue-new-50); - --icon-muted: var(--neutral-41); - --icon-overlay-dark: var(--neutral-71); - --icon-overlay-light: var(--neutral-2); - --icon-status-dnd: var(--red-new-50); - --icon-status-idle: var(--yellow-new-45); - --icon-status-offline: var(--neutral-45); - --icon-status-online: var(--green-160); - --icon-strong: var(--neutral-71); - --icon-subtle: var(--neutral-44); - --icon-transparent: hsl(var(--transparent-hsl)/0); - --icon-voice-connected: var(--green-new-55); - --icon-voice-disconnected: var(--red-new-53); - --icon-voice-muted: var(--red-new-50); - --icon-voice-speaking: var(--green-new-45); - --input-background-default: hsl(var(--opacity-black-1-hsl)/0.0196078431372549); - --input-background-error-default: hsl(var(--opacity-red-4-hsl)/0.0392156862745098); - --input-border-active: var(--blurple-50); - --input-border-default: hsl(var(--opacity-36-hsl)/0.3607843137254902); - --input-border-error-default: var(--red-new-53); - --input-border-hover: hsl(var(--opacity-36-hsl)/0.3607843137254902); - --input-border-readonly: hsl(var(--opacity-black-4-hsl)/0.0392156862745098); - --input-icon-default: var(--neutral-44); - --input-placeholder-text-default: var(--neutral-42); - --input-text-default: var(--neutral-69); - --input-text-error-default: var(--neutral-69); - --interactive-background-active: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --interactive-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --interactive-background-selected: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --interactive-icon-active: var(--neutral-71); - --interactive-icon-default: var(--neutral-44); - --interactive-icon-hover: var(--neutral-71); - --interactive-muted: var(--primary-300); - --interactive-text-active: var(--neutral-71); - --interactive-text-default: var(--neutral-44); - --interactive-text-hover: var(--neutral-71); - --logo-primary: var(--brand-500); - --mention-background: hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981); - --mention-foreground: var(--blurple-71); - --message-automod-background-default: hsl(var(--red-400-hsl)/0.05); - --message-automod-background-hover: hsl(var(--red-400-hsl)/0.1); - --message-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --message-highlight-background-default: hsl(var(--opacity-blurple-16-hsl)/0.1607843137254902); - --message-highlight-background-hover: hsl(var(--opacity-blurple-20-hsl)/0.2); - --message-mentioned-background-default: hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196); - --message-mentioned-background-hover: hsl(var(--opacity-yellow-12-hsl)/0.12156862745098039); - --message-reacted-background-default: hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981); - --message-reacted-text-default: var(--blurple-75); - --mobile-background-scrim-opaque: var(--black); - --mobile-expression-picker-background-default: var(--neutral-2); - --mobile-text-heading-primary: var(--neutral-71); - --modal-background: var(--neutral-1); - --modal-footer-background: var(--neutral-1); - --navigator-header-tint: var(--primary-500); - --notice-background-critical: var(--red-new-1); - --notice-background-info: var(--blue-new-1); - --notice-background-positive: var(--green-new-1); - --notice-background-warning: var(--yellow-new-1); - --notice-text-critical: var(--red-new-78); - --notice-text-info: var(--blue-new-76); - --notice-text-positive: var(--green-new-76); - --notice-text-warning: var(--yellow-new-85); - --overlay-backdrop-lightbox: hsl(var(--opacity-black-92-hsl)/0.9215686274509803); - --panel-bg: var(--neutral-2); - --polls-normal-image-background: var(--white); - --polls-victor-fill: hsl(var(--green-400-hsl)/0.2); - --polls-voted-fill: hsl(var(--brand-500-hsl)/0.2); - --premium-nitro-pink-text: var(--pink-61); - --profile-gradient-note-background: hsl(var(--white-hsl)/0.3); - --profile-gradient-overlay: hsl(var(--white-hsl)/0.6); - --profile-gradient-overlay-synced-with-user-theme: hsl(var(--white-hsl)/0.8); - --profile-gradient-role-pill-background: hsl(var(--white-hsl)/0.5); - --profile-gradient-role-pill-border: hsl(var(--primary-660-hsl)/0.2); - --profile-gradient-section-box: hsl(var(--white-hsl)/0.45); - --progressbar-indicator-background: var(--blurple-50); - --progressbar-track-background: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --radio-background-active: var(--blurple-65); - --radio-background-default: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --radio-background-hover: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --radio-background-selected-default: var(--blurple-50); - --radio-background-selected-hover: var(--blurple-60); - --radio-border-active: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --radio-border-default: var(--neutral-34); - --radio-border-hover: var(--neutral-46); - --radio-border-selected-default: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --radio-border-selected-hover: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --radio-foreground-active: var(--blurple-50); - --radio-foreground-default: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --radio-foreground-hover: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --radio-thumb-background-active: var(--neutral-1); - --redesign-button-premium-primary-pink-for-gradient: var(--premium-tier-2-pink-for-gradients); - --redesign-button-premium-primary-pressed-background: hsl(var(--black-hsl)/0.1); - --redesign-button-premium-primary-purple-for-gradient: var(--premium-tier-2-purple-for-gradients); - --redesign-button-premium-primary-purple-for-gradient-2: var(--premium-tier-2-purple-for-gradients-2); - --redesign-button-tertiary-background: hsl(var(--black-hsl)/0.08); - --redesign-button-tertiary-pressed-background: hsl(var(--black-hsl)/0.16); - --redesign-button-tertiary-pressed-text: var(--primary-500); - --redesign-button-tertiary-text: var(--neutral-69); - --scrollbar-auto-scrollbar-color-thumb: var(--neutral-33); - --scrollbar-auto-scrollbar-color-track: var(--neutral-5); - --scrollbar-auto-thumb: var(--neutral-31); - --scrollbar-auto-track: hsl(var(--transparent-hsl)/0); - --scrollbar-thin-thumb: var(--neutral-32); - --scrollbar-thin-track: hsl(var(--black-hsl)/0); - --slider-track-background: var(--neutral-16); - --spine-default: var(--neutral-16); - --spoiler-hidden-background: var(--neutral-31); - --spoiler-hidden-background-hover: var(--neutral-40); - --spoiler-revealed-background: var(--primary-160); - --status-danger: var(--red-430); - --status-online: var(--green-400); - --status-positive: var(--green-400); - --status-positive-background: var(--green-430); - --status-positive-text: var(--white); - --status-speaking: var(--green-360); - --status-warning: var(--yellow-400); - --status-warning-background: var(--yellow-400); - --status-warning-text: var(--white); - --switch-background-active: var(--blurple-65); - --switch-background-default: hsl(var(--opacity-black-1-hsl)/0.0196078431372549); - --switch-background-hover: hsl(var(--opacity-black-1-hsl)/0.0196078431372549); - --switch-background-selected-default: var(--blurple-50); - --switch-background-selected-hover: var(--blurple-60); - --switch-border-default: hsl(var(--opacity-black-16-hsl)/0.1607843137254902); - --switch-border-hover: hsl(var(--opacity-black-44-hsl)/0.4392156862745098); - --switch-border-selected-default: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --switch-border-selected-hover: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --switch-thumb-background-default: var(--neutral-44); - --switch-thumb-background-selected-default: var(--neutral-1); - --switch-thumb-icon-active: var(--blurple-50); - --switch-thumb-icon-default: var(--neutral-1); - --text-brand: var(--blurple-50); - --text-code: var(--blue-new-64); - --text-code-addition: var(--green-new-68); - --text-code-builtin: var(--orange-new-63); - --text-code-bullet: var(--yellow-new-77); - --text-code-comment: var(--neutral-41); - --text-code-deletion: var(--red-new-66); - --text-code-keyword: var(--red-new-63); - --text-code-section: var(--blue-new-64); - --text-code-string: var(--teal-new-66); - --text-code-tag: var(--green-new-69); - --text-code-title: var(--blurple-68); - --text-code-variable: var(--blue-new-68); - --text-default: var(--neutral-69); - --text-feedback-critical: var(--red-new-53); - --text-feedback-info: var(--blue-new-56); - --text-feedback-positive: var(--green-new-55); - --text-feedback-warning: var(--yellow-new-70); - --text-invert: var(--neutral-1); - --text-link: var(--blue-new-50); - --text-muted: var(--neutral-41); - --text-overlay-dark: var(--neutral-71); - --text-overlay-light: var(--neutral-2); - --text-status-dnd: var(--red-new-50); - --text-status-idle: var(--yellow-new-45); - --text-status-offline: var(--neutral-45); - --text-status-online: var(--green-160); - --text-strong: var(--neutral-71); - --text-subtle: var(--neutral-44); - --text-voice-connected: var(--green-new-55); - --text-voice-disconnected: var(--red-new-53); - --text-voice-speaking: var(--green-new-45); - --textbox-markdown-syntax: var(--primary-530); - --thread-channel-spine: var(--primary-300); - --user-profile-activity-toolbar-background: var(--neutral-1); - --user-profile-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --user-profile-border: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --user-profile-note-background-focus: var(--neutral-4); - --user-profile-overlay-background: var(--neutral-1); - --user-profile-overlay-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --user-profile-toolbar-background: var(--neutral-1); - --user-profile-toolbar-border: hsl(var(--opacity-20-hsl)/0.2) -} - -@supports (color: color-mix(in lch,red,blue)) { - .theme-light { - --app-frame-background:color-mix(in oklab,var(--neutral-7) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --app-frame-border: color-mix(in oklab,hsl(var(--opacity-28-hsl)/0.2784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2784313725490196) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --app-message-embed-secondary-text: color-mix(in oklab,hsl(var(--white-hsl)/0.7) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.7) var(--custom-theme-text-color-amount,0%)); - --background-accent: color-mix(in oklab,var(--primary-430) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-base-low: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-base-lower: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-base-lowest: color-mix(in oklab,var(--neutral-4) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-code: color-mix(in oklab,hsl(var(--opacity-blurple-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-base-color-amount,0%)); - --background-code-addition: color-mix(in oklab,hsl(var(--opacity-green-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-code-deletion: color-mix(in oklab,hsl(var(--opacity-red-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-critical: color-mix(in oklab,hsl(var(--opacity-red-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-feedback-info: color-mix(in oklab,hsl(var(--opacity-blue-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-feedback-positive: color-mix(in oklab,hsl(var(--opacity-green-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-feedback-warning: color-mix(in oklab,hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-scrim: color-mix(in oklab,hsl(var(--opacity-black-52-hsl)/0.5215686274509804) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.5215686274509804) var(--custom-theme-base-color-amount,0%)); - --background-scrim-lightbox: color-mix(in oklab,hsl(var(--opacity-black-92-hsl)/0.9215686274509803) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.9215686274509803) var(--custom-theme-base-color-amount,0%)); - --background-secondary-alt: color-mix(in oklab,var(--primary-160) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-surface-high: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-surface-higher: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-surface-highest: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-tile-gradient-pink-end: color-mix(in oklab,hsl(var(--illo-pink-30-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --background-tile-gradient-pink-start: color-mix(in oklab,hsl(var(--illo-pink-10-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --bg-surface-raised: color-mix(in oklab,var(--white) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --border-feedback-critical: color-mix(in oklab,hsl(var(--opacity-red-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-focus: color-mix(in oklab,var(--blue-new-40) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-muted: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-normal: color-mix(in oklab,hsl(var(--opacity-36-hsl)/0.3607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3607843137254902) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-strong: color-mix(in oklab,hsl(var(--opacity-48-hsl)/0.47843137254901963) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.47843137254901963) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-subtle: color-mix(in oklab,hsl(var(--opacity-28-hsl)/0.2784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2784313725490196) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --card-background-default: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --card-primary-pressed-bg: color-mix(in oklab,var(--primary-160) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --card-secondary-bg: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --card-secondary-pressed-bg: color-mix(in oklab,var(--primary-160) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --channel-icon: color-mix(in oklab,var(--neutral-43) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channel-text-area-placeholder: color-mix(in oklab,var(--primary-400) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channels-default: color-mix(in oklab,var(--neutral-43) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channeltextarea-background: color-mix(in oklab,var(--primary-160) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --chat-background: color-mix(in oklab,var(--white) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --chat-background-default: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --chat-border: color-mix(in oklab,var(--primary-200) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --chat-text-muted: color-mix(in oklab,var(--neutral-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --content-inventory-media-seekbar-container: color-mix(in oklab,hsl(var(--plum-6-hsl)/0.24) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.24) var(--custom-theme-base-color-amount,0%)); - --content-inventory-overlay-text-primary: color-mix(in oklab,hsl(var(--white-hsl)/0.85) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.85) var(--custom-theme-text-color-amount,0%)); - --content-inventory-overlay-text-secondary: color-mix(in oklab,hsl(var(--white-hsl)/0.7) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.7) var(--custom-theme-text-color-amount,0%)); - --context-menu-backdrop-background: color-mix(in oklab,hsl(var(--opacity-black-52-hsl)/0.5215686274509804) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.5215686274509804) var(--custom-theme-base-color-amount,0%)); - --control-brand-foreground: color-mix(in oklab,var(--brand-500) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --control-brand-foreground-new: color-mix(in oklab,var(--brand-500) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --control-secondary-border-active: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --control-secondary-border-default: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --creator-revenue-icon-gradient-end: color-mix(in oklab,var(--teal-400) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --creator-revenue-icon-gradient-start: color-mix(in oklab,var(--teal-345) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --creator-revenue-info-box-background: color-mix(in oklab,hsl(var(--teal-430-hsl)/0.1) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1) var(--custom-theme-base-color-amount,0%)); - --creator-revenue-info-box-border: color-mix(in oklab,var(--teal-400) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --creator-revenue-locked-channel-icon: color-mix(in oklab,var(--teal-400) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --creator-revenue-progress-bar: color-mix(in oklab,var(--teal-345) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --embed-background: color-mix(in oklab,var(--primary-130) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --embed-background-alternate: color-mix(in oklab,var(--primary-200) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --experimental-avatar-embed-bg: color-mix(in oklab,hsl(var(--opacity-black-52-hsl)/0.5215686274509804) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.5215686274509804) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-blue-end: color-mix(in oklab,hsl(var(--illo-blue-30-hsl)/0.8) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.8) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-blue-start: color-mix(in oklab,hsl(var(--illo-blue-10-hsl)/0.8) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.8) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-green-end: color-mix(in oklab,hsl(var(--illo-green-30-hsl)/0.8) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.8) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-green-start: color-mix(in oklab,hsl(var(--illo-green-10-hsl)/0.8) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.8) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-green-end: color-mix(in oklab,hsl(var(--illo-blue-40-hsl)/0.9) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.9) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-green-start: color-mix(in oklab,hsl(var(--illo-green-10-hsl)/0.9) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.9) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-pink-end: color-mix(in oklab,hsl(var(--illo-blue-40-hsl)/0.9) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.9) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-pink-start: color-mix(in oklab,hsl(var(--illo-pink-30-hsl)/0.9) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.9) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-pink-end: color-mix(in oklab,hsl(var(--illo-pink-30-hsl)/0.8) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.8) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-pink-start: color-mix(in oklab,hsl(var(--illo-pink-10-hsl)/0.8) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.8) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-purple-end: color-mix(in oklab,hsl(var(--illo-purple-30-hsl)/0.8) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.8) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-purple-start: color-mix(in oklab,hsl(var(--illo-purple-10-hsl)/0.8) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.8) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-bronze-end: color-mix(in oklab,hsl(var(--illo-orange-70-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-bronze-start: color-mix(in oklab,hsl(var(--illo-orange-30-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-diamond-end: color-mix(in oklab,hsl(var(--illo-purple-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-diamond-start: color-mix(in oklab,hsl(var(--illo-purple-40-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-emerald-end: color-mix(in oklab,hsl(var(--illo-green-60-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-emerald-start: color-mix(in oklab,hsl(var(--illo-green-40-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-gold-end: color-mix(in oklab,hsl(var(--yellow-new-41-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-gold-start: color-mix(in oklab,hsl(var(--illo-yellow-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-opal-end: color-mix(in oklab,hsl(var(--blue-new-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-opal-start: color-mix(in oklab,hsl(var(--teal-new-30-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-platinum-end: color-mix(in oklab,hsl(var(--teal-new-60-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-platinum-start: color-mix(in oklab,hsl(var(--teal-new-20-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-ruby-end: color-mix(in oklab,hsl(var(--red-new-80-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-ruby-start: color-mix(in oklab,hsl(var(--red-new-44-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-silver-end: color-mix(in oklab,hsl(var(--neutral-58-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-silver-start: color-mix(in oklab,hsl(var(--neutral-12-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --gradient-progress-pill-background: color-mix(in oklab,var(--neutral-18) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --guild-profile-banner-background-default: color-mix(in oklab,var(--neutral-40) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --home-background: color-mix(in oklab,var(--primary-100) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --icon-default: color-mix(in oklab,var(--neutral-69) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-critical: color-mix(in oklab,var(--red-new-53) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-info: color-mix(in oklab,var(--blue-new-56) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-positive: color-mix(in oklab,var(--green-new-55) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-warning: color-mix(in oklab,var(--yellow-new-70) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-invert: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-link: color-mix(in oklab,var(--blue-new-50) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-muted: color-mix(in oklab,var(--neutral-41) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-overlay-dark: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-overlay-light: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-dnd: color-mix(in oklab,var(--red-new-50) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-idle: color-mix(in oklab,var(--yellow-new-45) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-offline: color-mix(in oklab,var(--neutral-45) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-online: color-mix(in oklab,var(--green-160) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-strong: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-subtle: color-mix(in oklab,var(--neutral-44) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-transparent: color-mix(in oklab,hsl(var(--transparent-hsl)/0) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0) var(--custom-theme-text-color-amount,0%)); - --icon-voice-connected: color-mix(in oklab,var(--green-new-55) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-voice-disconnected: color-mix(in oklab,var(--red-new-53) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-voice-speaking: color-mix(in oklab,var(--green-new-45) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-background-default: color-mix(in oklab,hsl(var(--opacity-black-1-hsl)/0.0196078431372549) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0196078431372549) var(--custom-theme-base-color-amount,0%)); - --input-background-error-default: color-mix(in oklab,hsl(var(--opacity-red-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-base-color-amount,0%)); - --input-border-active: color-mix(in oklab,var(--blurple-50) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-default: color-mix(in oklab,hsl(var(--opacity-36-hsl)/0.3607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3607843137254902) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-error-default: color-mix(in oklab,var(--red-new-53) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-hover: color-mix(in oklab,hsl(var(--opacity-36-hsl)/0.3607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3607843137254902) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-readonly: color-mix(in oklab,hsl(var(--opacity-black-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-icon-default: color-mix(in oklab,var(--neutral-44) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-placeholder-text-default: color-mix(in oklab,var(--neutral-42) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-default: color-mix(in oklab,var(--neutral-69) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-error-default: color-mix(in oklab,var(--neutral-69) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-background-active: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --interactive-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --interactive-background-selected: color-mix(in oklab,hsl(var(--opacity-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --interactive-icon-active: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-default: color-mix(in oklab,var(--neutral-44) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-hover: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-muted: color-mix(in oklab,var(--primary-300) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-active: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-default: color-mix(in oklab,var(--neutral-44) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-hover: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --logo-primary: color-mix(in oklab,var(--brand-500) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --mention-background: color-mix(in oklab,hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --mention-foreground: color-mix(in oklab,var(--blurple-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --message-automod-background-default: color-mix(in oklab,hsl(var(--red-400-hsl)/0.05) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.05) var(--custom-theme-base-color-amount,0%)); - --message-automod-background-hover: color-mix(in oklab,hsl(var(--red-400-hsl)/0.1) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1) var(--custom-theme-base-color-amount,0%)); - --message-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-default: color-mix(in oklab,hsl(var(--opacity-blurple-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-hover: color-mix(in oklab,hsl(var(--opacity-blurple-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-default: color-mix(in oklab,hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-hover: color-mix(in oklab,hsl(var(--opacity-yellow-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --message-reacted-background-default: color-mix(in oklab,hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --message-reacted-text-default: color-mix(in oklab,var(--blurple-75) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --mobile-background-scrim-opaque: color-mix(in oklab,var(--black) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --mobile-expression-picker-background-default: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --mobile-text-heading-primary: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --modal-background: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --modal-footer-background: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --navigator-header-tint: color-mix(in oklab,var(--primary-500) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --overlay-backdrop-lightbox: color-mix(in oklab,hsl(var(--opacity-black-92-hsl)/0.9215686274509803) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.9215686274509803) var(--custom-theme-base-color-amount,0%)); - --panel-bg: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --polls-normal-image-background: color-mix(in oklab,var(--white) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --polls-victor-fill: color-mix(in oklab,hsl(var(--green-400-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --polls-voted-fill: color-mix(in oklab,hsl(var(--brand-500-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --premium-nitro-pink-text: color-mix(in oklab,var(--pink-61) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --profile-gradient-note-background: color-mix(in oklab,hsl(var(--white-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-overlay: color-mix(in oklab,hsl(var(--white-hsl)/0.6) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.6) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-overlay-synced-with-user-theme: color-mix(in oklab,hsl(var(--white-hsl)/0.8) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.8) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-role-pill-background: color-mix(in oklab,hsl(var(--white-hsl)/0.5) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.5) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-role-pill-border: color-mix(in oklab,hsl(var(--primary-660-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --profile-gradient-section-box: color-mix(in oklab,hsl(var(--white-hsl)/0.45) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.45) var(--custom-theme-base-color-amount,0%)); - --progressbar-indicator-background: color-mix(in oklab,var(--blurple-50) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --progressbar-track-background: color-mix(in oklab,hsl(var(--opacity-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-pink-for-gradient: color-mix(in oklab,var(--premium-tier-2-pink-for-gradients) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-pressed-background: color-mix(in oklab,hsl(var(--black-hsl)/0.1) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-purple-for-gradient: color-mix(in oklab,var(--premium-tier-2-purple-for-gradients) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-purple-for-gradient-2: color-mix(in oklab,var(--premium-tier-2-purple-for-gradients-2) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-background: color-mix(in oklab,hsl(var(--black-hsl)/0.08) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.08) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-pressed-background: color-mix(in oklab,hsl(var(--black-hsl)/0.16) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.16) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-pressed-text: color-mix(in oklab,var(--primary-500) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --redesign-button-tertiary-text: color-mix(in oklab,var(--neutral-69) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --scrollbar-auto-scrollbar-color-thumb: color-mix(in oklab,var(--neutral-33) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-auto-scrollbar-color-track: color-mix(in oklab,var(--neutral-5) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-auto-thumb: color-mix(in oklab,var(--neutral-31) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-auto-track: color-mix(in oklab,hsl(var(--transparent-hsl)/0) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0) var(--custom-theme-base-color-amount,0%)); - --scrollbar-thin-thumb: color-mix(in oklab,var(--neutral-32) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-thin-track: color-mix(in oklab,hsl(var(--black-hsl)/0) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0) var(--custom-theme-base-color-amount,0%)); - --slider-track-background: color-mix(in oklab,var(--neutral-16) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --spine-default: color-mix(in oklab,var(--neutral-16) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --spoiler-hidden-background: color-mix(in oklab,var(--neutral-31) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --spoiler-hidden-background-hover: color-mix(in oklab,var(--neutral-40) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --spoiler-revealed-background: color-mix(in oklab,var(--primary-160) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --text-brand: color-mix(in oklab,var(--blurple-50) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code: color-mix(in oklab,var(--blue-new-64) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-addition: color-mix(in oklab,var(--green-new-68) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-builtin: color-mix(in oklab,var(--orange-new-63) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-bullet: color-mix(in oklab,var(--yellow-new-77) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-comment: color-mix(in oklab,var(--neutral-41) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-deletion: color-mix(in oklab,var(--red-new-66) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-keyword: color-mix(in oklab,var(--red-new-63) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-section: color-mix(in oklab,var(--blue-new-64) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-string: color-mix(in oklab,var(--teal-new-66) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-tag: color-mix(in oklab,var(--green-new-69) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-title: color-mix(in oklab,var(--blurple-68) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-variable: color-mix(in oklab,var(--blue-new-68) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-default: color-mix(in oklab,var(--neutral-69) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-critical: color-mix(in oklab,var(--red-new-53) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-info: color-mix(in oklab,var(--blue-new-56) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-positive: color-mix(in oklab,var(--green-new-55) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-warning: color-mix(in oklab,var(--yellow-new-70) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-invert: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-link: color-mix(in oklab,var(--blue-new-50) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-muted: color-mix(in oklab,var(--neutral-41) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-dnd: color-mix(in oklab,var(--red-new-50) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-idle: color-mix(in oklab,var(--yellow-new-45) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-offline: color-mix(in oklab,var(--neutral-45) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-online: color-mix(in oklab,var(--green-160) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-strong: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-subtle: color-mix(in oklab,var(--neutral-44) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-connected: color-mix(in oklab,var(--green-new-55) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-disconnected: color-mix(in oklab,var(--red-new-53) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-speaking: color-mix(in oklab,var(--green-new-45) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --textbox-markdown-syntax: color-mix(in oklab,var(--primary-530) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --thread-channel-spine: color-mix(in oklab,var(--primary-300) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-activity-toolbar-background: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --user-profile-border: color-mix(in oklab,hsl(var(--opacity-28-hsl)/0.2784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2784313725490196) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-note-background-focus: color-mix(in oklab,var(--neutral-4) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-overlay-background: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-overlay-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --user-profile-toolbar-background: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-toolbar-border: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))) - } -} - -.theme-midnight { - --app-frame-background: var(--neutral-100); - --app-frame-border: hsl(var(--opacity-20-hsl)/0.2); - --app-message-embed-secondary-text: hsl(var(--white-hsl)/0.7); - --background-accent: var(--plum-17); - --background-base-low: var(--neutral-95); - --background-base-lower: var(--neutral-97); - --background-base-lowest: var(--neutral-100); - --background-brand: var(--blurple-50); - --background-code: hsl(var(--opacity-blurple-8-hsl)/0.0784313725490196); - --background-code-addition: hsl(var(--opacity-green-12-hsl)/0.12156862745098039); - --background-code-deletion: hsl(var(--opacity-red-12-hsl)/0.12156862745098039); - --background-feedback-critical: hsl(var(--opacity-red-8-hsl)/0.0784313725490196); - --background-feedback-info: hsl(var(--opacity-blue-8-hsl)/0.0784313725490196); - --background-feedback-notification: var(--red-new-46); - --background-feedback-positive: hsl(var(--opacity-green-8-hsl)/0.0784313725490196); - --background-feedback-warning: hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196); - --background-mod-muted: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --background-mod-normal: hsl(var(--opacity-20-hsl)/0.2); - --background-mod-strong: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --background-mod-subtle: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --background-scrim: hsl(var(--opacity-black-72-hsl)/0.7215686274509804); - --background-scrim-lightbox: hsl(var(--opacity-black-92-hsl)/0.9215686274509803); - --background-secondary-alt: var(--plum-17); - --background-surface-high: var(--neutral-91); - --background-surface-higher: var(--neutral-87); - --background-surface-highest: var(--neutral-83); - --background-tile-gradient-pink-end: hsl(var(--illo-pink-70-hsl)/0.3); - --background-tile-gradient-pink-start: hsl(var(--illo-pink-50-hsl)/0.3); - --badge-background-brand: var(--blurple-50); - --badge-background-default: hsl(var(--opacity-20-hsl)/0.2); - --badge-expressive-background-default: var(--neutral-1); - --badge-expressive-text-default: var(--neutral-71); - --badge-notification-background: var(--red-new-46); - --badge-text-brand: var(--neutral-1); - --badge-text-default: var(--neutral-8); - --bg-surface-raised: var(--plum-23); - --border-feedback-critical: hsl(var(--opacity-red-24-hsl)/0.23921568627450981); - --border-focus: var(--blue-new-30); - --border-muted: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --border-normal: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --border-strong: hsl(var(--opacity-44-hsl)/0.4392156862745098); - --border-subtle: hsl(var(--opacity-20-hsl)/0.2); - --button-danger-background-disabled: var(--red-new-50); - --button-outline-brand-background-hover: var(--brand-500); - --button-outline-brand-border-active: var(--brand-560); - --button-outline-primary-text: var(--white); - --card-background-default: var(--neutral-91); - --card-primary-pressed-bg: var(--plum-25); - --card-secondary-bg: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --card-secondary-pressed-bg: var(--plum-26); - --channel-icon: var(--neutral-37); - --channel-text-area-placeholder: var(--plum-11); - --channels-default: var(--neutral-37); - --channeltextarea-background: var(--plum-23); - --chat-background: var(--black); - --chat-background-default: var(--neutral-91); - --chat-border: var(--plum-20); - --chat-text-muted: var(--neutral-38); - --checkbox-background-active: var(--blurple-65); - --checkbox-background-default: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --checkbox-background-hover: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --checkbox-background-selected-default: var(--blurple-50); - --checkbox-background-selected-hover: var(--blurple-60); - --checkbox-border-active: hsl(var(--opacity-20-hsl)/0.2); - --checkbox-border-default: var(--neutral-44); - --checkbox-border-hover: hsl(var(--opacity-80-hsl)/0.8); - --checkbox-border-selected-default: hsl(var(--opacity-20-hsl)/0.2); - --checkbox-border-selected-hover: hsl(var(--opacity-20-hsl)/0.2); - --checkbox-icon-active: var(--neutral-1); - --chip-blurple-dark-background: var(--blurple-70); - --chip-blurple-dark-text: var(--blurple-1); - --chip-blurple-light-background: var(--blurple-20); - --chip-blurple-light-text: var(--blurple-91); - --chip-blurple-medium-background: var(--blurple-40); - --chip-blurple-medium-text: var(--blurple-100); - --chip-gray-dark-background: var(--neutral-75); - --chip-gray-dark-text: var(--neutral-6); - --chip-gray-light-background: var(--neutral-10); - --chip-gray-light-text: var(--neutral-68); - --chip-gray-medium-background: var(--neutral-40); - --chip-gray-medium-text: var(--neutral-1); - --chip-green-dark-background: var(--green-new-70); - --chip-green-dark-text: var(--green-new-1); - --chip-green-light-background: var(--green-new-20); - --chip-green-light-text: var(--green-new-91); - --chip-green-medium-background: var(--green-new-45); - --chip-green-medium-text: var(--green-new-96); - --chip-orange-dark-background: var(--orange-new-70); - --chip-orange-dark-text: var(--orange-new-1); - --chip-orange-light-background: var(--orange-new-20); - --chip-orange-light-text: var(--orange-new-93); - --chip-orange-medium-background: var(--orange-new-40); - --chip-orange-medium-text: var(--orange-new-100); - --chip-pink-dark-background: var(--illo-pink-60); - --chip-pink-dark-text: var(--neutral-1); - --chip-pink-light-background: var(--illo-pink-20); - --chip-pink-light-text: var(--neutral-73); - --chip-pink-medium-background: var(--illo-pink-40); - --chip-pink-medium-text: var(--neutral-100); - --chip-purple-dark-background: var(--illo-purple-60); - --chip-purple-dark-text: var(--neutral-10); - --chip-purple-light-background: var(--illo-purple-20); - --chip-purple-light-text: var(--neutral-75); - --chip-purple-medium-background: var(--illo-purple-40); - --chip-purple-medium-text: var(--neutral-100); - --chip-red-dark-background: var(--red-new-70); - --chip-red-dark-text: var(--red-new-1); - --chip-red-light-background: var(--red-new-20); - --chip-red-light-text: var(--red-new-95); - --chip-red-medium-background: var(--red-new-45); - --chip-red-medium-text: var(--red-new-100); - --chip-yellow-dark-background: var(--yellow-new-70); - --chip-yellow-dark-text: var(--yellow-new-1); - --chip-yellow-light-background: var(--yellow-new-20); - --chip-yellow-light-text: var(--yellow-new-87); - --chip-yellow-medium-background: var(--yellow-new-45); - --chip-yellow-medium-text: var(--yellow-new-80); - --content-inventory-media-seekbar-container: hsl(var(--plum-6-hsl)/0.24); - --content-inventory-overlay-text-primary: hsl(var(--white-hsl)/0.85); - --content-inventory-overlay-text-secondary: hsl(var(--white-hsl)/0.7); - --context-menu-backdrop-background: hsl(var(--black-hsl)/0.7); - --control-brand-foreground: var(--brand-360); - --control-brand-foreground-new: var(--brand-360); - --control-connected-background-active: var(--green-new-65); - --control-connected-background-default: var(--green-new-50); - --control-connected-background-hover: var(--green-new-60); - --control-connected-border-active: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-connected-border-default: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-connected-border-hover: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-connected-icon-active: var(--neutral-1); - --control-connected-icon-default: var(--neutral-1); - --control-connected-icon-hover: var(--neutral-1); - --control-connected-text-active: var(--neutral-1); - --control-connected-text-default: var(--neutral-1); - --control-connected-text-hover: var(--neutral-1); - --control-critical-primary-background-active: var(--red-new-65); - --control-critical-primary-background-default: var(--red-new-50); - --control-critical-primary-background-hover: var(--red-new-60); - --control-critical-primary-border-active: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-critical-primary-border-default: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-critical-primary-border-hover: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-critical-primary-icon-active: var(--neutral-1); - --control-critical-primary-icon-default: var(--neutral-1); - --control-critical-primary-icon-hover: var(--neutral-1); - --control-critical-primary-text-active: var(--neutral-1); - --control-critical-primary-text-default: var(--neutral-1); - --control-critical-primary-text-hover: var(--neutral-1); - --control-critical-secondary-background-active: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --control-critical-secondary-background-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-critical-secondary-background-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-critical-secondary-border-active: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-critical-secondary-border-default: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-critical-secondary-border-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-critical-secondary-icon-active: var(--red-new-38); - --control-critical-secondary-icon-default: var(--red-new-38); - --control-critical-secondary-icon-hover: var(--red-new-38); - --control-critical-secondary-text-active: var(--red-new-38); - --control-critical-secondary-text-default: var(--red-new-38); - --control-critical-secondary-text-hover: var(--red-new-38); - --control-expressive-background-active: var(--neutral-5); - --control-expressive-background-default: var(--neutral-1); - --control-expressive-background-hover: var(--neutral-1); - --control-expressive-border-active: hsl(var(--opacity-20-hsl)/0.2); - --control-expressive-border-default: hsl(var(--opacity-20-hsl)/0.2); - --control-expressive-border-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-expressive-icon-active: var(--neutral-100); - --control-expressive-icon-default: var(--neutral-100); - --control-expressive-icon-hover: var(--neutral-100); - --control-expressive-text-active: var(--neutral-100); - --control-expressive-text-default: var(--neutral-100); - --control-expressive-text-hover: var(--neutral-100); - --control-icon-only-background-active: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --control-icon-only-background-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-icon-only-border-active: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-icon-only-border-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-icon-only-icon-active: var(--neutral-8); - --control-icon-only-icon-default: var(--neutral-27); - --control-icon-only-icon-hover: var(--neutral-8); - --control-overlay-primary-background-active: var(--neutral-17); - --control-overlay-primary-background-default: var(--neutral-1); - --control-overlay-primary-background-hover: var(--neutral-9); - --control-overlay-primary-border-active: hsl(var(--opacity-20-hsl)/0.2); - --control-overlay-primary-border-default: hsl(var(--opacity-20-hsl)/0.2); - --control-overlay-primary-border-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-overlay-primary-icon-active: var(--neutral-100); - --control-overlay-primary-icon-default: var(--neutral-100); - --control-overlay-primary-icon-hover: var(--neutral-100); - --control-overlay-primary-text-active: var(--neutral-100); - --control-overlay-primary-text-default: var(--neutral-100); - --control-overlay-primary-text-hover: var(--neutral-100); - --control-overlay-secondary-background-active: hsl(var(--opacity-black-48-hsl)/0.47843137254901963); - --control-overlay-secondary-background-default: hsl(var(--opacity-black-52-hsl)/0.5215686274509804); - --control-overlay-secondary-background-hover: hsl(var(--opacity-black-64-hsl)/0.6392156862745098); - --control-overlay-secondary-border-active: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-overlay-secondary-border-default: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-overlay-secondary-border-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-overlay-secondary-icon-active: var(--neutral-1); - --control-overlay-secondary-icon-default: var(--neutral-1); - --control-overlay-secondary-icon-hover: var(--neutral-1); - --control-overlay-secondary-text-active: var(--neutral-1); - --control-overlay-secondary-text-default: var(--neutral-1); - --control-overlay-secondary-text-hover: var(--neutral-1); - --control-primary-background-active: var(--blurple-65); - --control-primary-background-default: var(--blurple-50); - --control-primary-background-hover: var(--blurple-60); - --control-primary-border-active: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-primary-border-default: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-primary-border-hover: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-primary-icon-active: var(--neutral-1); - --control-primary-icon-default: var(--neutral-1); - --control-primary-icon-hover: var(--neutral-1); - --control-primary-text-active: var(--neutral-1); - --control-primary-text-default: var(--neutral-1); - --control-primary-text-hover: var(--neutral-1); - --control-secondary-background-active: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --control-secondary-background-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-secondary-background-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-secondary-border-active: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-secondary-border-default: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-secondary-border-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-secondary-icon-active: var(--neutral-8); - --control-secondary-icon-default: var(--neutral-8); - --control-secondary-icon-hover: var(--neutral-8); - --control-secondary-text-active: var(--neutral-8); - --control-secondary-text-default: var(--neutral-8); - --control-secondary-text-hover: var(--neutral-8); - --creator-revenue-icon-gradient-end: var(--teal-430); - --creator-revenue-icon-gradient-start: var(--teal-360); - --creator-revenue-info-box-background: hsl(var(--teal-430-hsl)/0.1); - --creator-revenue-info-box-border: var(--teal-400); - --creator-revenue-locked-channel-icon: var(--teal-345); - --creator-revenue-progress-bar: var(--teal-400); - --embed-background: var(--plum-23); - --embed-background-alternate: var(--plum-23); - --experimental-avatar-embed-bg: hsl(var(--opacity-black-52-hsl)/0.5215686274509804); - --expressive-gradient-blue-end: hsl(var(--illo-blue-60-hsl)/0.25); - --expressive-gradient-blue-start: hsl(var(--illo-blue-40-hsl)/0.25); - --expressive-gradient-green-end: hsl(var(--illo-green-70-hsl)/0.25); - --expressive-gradient-green-start: hsl(var(--illo-green-50-hsl)/0.25); - --expressive-gradient-nitro-green-end: hsl(var(--illo-nitro-blue-hsl)/0.3); - --expressive-gradient-nitro-green-start: hsl(var(--illo-green-50-hsl)/0.3); - --expressive-gradient-nitro-pink-end: hsl(var(--illo-nitro-blue-hsl)/0.3); - --expressive-gradient-nitro-pink-start: hsl(var(--illo-pink-60-hsl)/0.3); - --expressive-gradient-pink-end: hsl(var(--illo-pink-70-hsl)/0.25); - --expressive-gradient-pink-start: hsl(var(--illo-pink-50-hsl)/0.25); - --expressive-gradient-purple-end: hsl(var(--illo-purple-60-hsl)/0.25); - --expressive-gradient-purple-start: hsl(var(--illo-purple-40-hsl)/0.25); - --expressive-gradient-tenure-badge-bronze-end: hsl(var(--illo-orange-70-hsl)/0.3); - --expressive-gradient-tenure-badge-bronze-start: hsl(var(--illo-orange-30-hsl)/0.3); - --expressive-gradient-tenure-badge-diamond-end: hsl(var(--illo-purple-50-hsl)/0.3); - --expressive-gradient-tenure-badge-diamond-start: hsl(var(--illo-purple-40-hsl)/0.3); - --expressive-gradient-tenure-badge-emerald-end: hsl(var(--illo-green-60-hsl)/0.3); - --expressive-gradient-tenure-badge-emerald-start: hsl(var(--illo-green-40-hsl)/0.3); - --expressive-gradient-tenure-badge-gold-end: hsl(var(--yellow-new-41-hsl)/0.3); - --expressive-gradient-tenure-badge-gold-start: hsl(var(--illo-yellow-50-hsl)/0.3); - --expressive-gradient-tenure-badge-opal-end: hsl(var(--blue-new-50-hsl)/0.3); - --expressive-gradient-tenure-badge-opal-start: hsl(var(--teal-new-30-hsl)/0.3); - --expressive-gradient-tenure-badge-platinum-end: hsl(var(--teal-new-60-hsl)/0.3); - --expressive-gradient-tenure-badge-platinum-start: hsl(var(--teal-new-20-hsl)/0.3); - --expressive-gradient-tenure-badge-ruby-end: hsl(var(--red-new-80-hsl)/0.3); - --expressive-gradient-tenure-badge-ruby-start: hsl(var(--red-new-44-hsl)/0.3); - --expressive-gradient-tenure-badge-silver-end: hsl(var(--neutral-58-hsl)/0.3); - --expressive-gradient-tenure-badge-silver-start: hsl(var(--neutral-12-hsl)/0.3); - --gradient-progress-pill-background: var(--neutral-63); - --guild-profile-banner-background-default: var(--neutral-100); - --home-background: var(--plum-18); - --icon-default: var(--neutral-10); - --icon-feedback-critical: var(--red-new-38); - --icon-feedback-info: var(--blue-new-41); - --icon-feedback-notification: var(--red-new-46); - --icon-feedback-positive: var(--green-new-40); - --icon-feedback-warning: var(--yellow-new-60); - --icon-invert: var(--neutral-71); - --icon-link: var(--blue-new-42); - --icon-muted: var(--neutral-33); - --icon-overlay-dark: var(--neutral-71); - --icon-overlay-light: var(--neutral-8); - --icon-status-dnd: var(--red-new-45); - --icon-status-idle: var(--yellow-new-22); - --icon-status-offline: var(--neutral-27); - --icon-status-online: var(--green-new-40); - --icon-strong: var(--neutral-8); - --icon-subtle: var(--neutral-27); - --icon-transparent: hsl(var(--transparent-hsl)/0); - --icon-voice-connected: var(--green-new-40); - --icon-voice-disconnected: var(--red-new-38); - --icon-voice-muted: var(--red-new-46); - --icon-voice-speaking: var(--green-new-40); - --input-background-default: hsl(var(--opacity-black-12-hsl)/0.12156862745098039); - --input-background-error-default: hsl(var(--opacity-red-4-hsl)/0.0392156862745098); - --input-border-active: var(--blurple-50); - --input-border-default: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --input-border-error-default: var(--red-new-38); - --input-border-hover: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --input-border-readonly: hsl(var(--opacity-white-12-hsl)/0.12156862745098039); - --input-icon-default: var(--neutral-27); - --input-placeholder-text-default: var(--neutral-39); - --input-text-default: var(--neutral-10); - --input-text-error-default: var(--neutral-10); - --interactive-background-active: hsl(var(--opacity-20-hsl)/0.2); - --interactive-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --interactive-background-selected: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --interactive-icon-active: var(--neutral-8); - --interactive-icon-default: var(--neutral-27); - --interactive-icon-hover: var(--neutral-8); - --interactive-muted: var(--plum-13); - --interactive-text-active: var(--neutral-8); - --interactive-text-default: var(--neutral-27); - --interactive-text-hover: var(--neutral-8); - --logo-primary: var(--white); - --mention-background: hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981); - --mention-foreground: var(--blurple-26); - --message-automod-background-default: hsl(var(--red-345-hsl)/0.08); - --message-automod-background-hover: hsl(var(--red-400-hsl)/0.1); - --message-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --message-highlight-background-default: hsl(var(--opacity-blurple-16-hsl)/0.1607843137254902); - --message-highlight-background-hover: hsl(var(--opacity-blurple-12-hsl)/0.12156862745098039); - --message-mentioned-background-default: hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196); - --message-mentioned-background-hover: hsl(var(--opacity-yellow-4-hsl)/0.0392156862745098); - --message-reacted-background-default: hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981); - --message-reacted-text-default: var(--blurple-21); - --mobile-background-scrim-opaque: var(--plum-19); - --mobile-expression-picker-background-default: var(--neutral-97); - --mobile-text-heading-primary: var(--neutral-8); - --modal-background: var(--neutral-91); - --modal-footer-background: var(--neutral-91); - --navigator-header-tint: var(--white); - --notice-background-critical: var(--red-new-95); - --notice-background-info: var(--blue-new-95); - --notice-background-positive: var(--green-new-98); - --notice-background-warning: var(--yellow-new-97); - --notice-text-critical: var(--red-new-15); - --notice-text-info: var(--blue-new-20); - --notice-text-positive: var(--green-new-21); - --notice-text-warning: var(--yellow-new-41); - --overlay-backdrop-lightbox: hsl(var(--opacity-black-92-hsl)/0.9215686274509803); - --panel-bg: var(--plum-24); - --polls-normal-image-background: var(--primary-660); - --polls-victor-fill: hsl(var(--green-360-hsl)/0.2); - --polls-voted-fill: hsl(var(--brand-500-hsl)/0.2); - --premium-nitro-pink-text: var(--pink-55); - --profile-gradient-note-background: hsl(var(--black-hsl)/0.3); - --profile-gradient-overlay: hsl(var(--black-hsl)/0.6); - --profile-gradient-overlay-synced-with-user-theme: hsl(var(--black-hsl)/0.8); - --profile-gradient-role-pill-background: hsl(var(--primary-660-hsl)/0.5); - --profile-gradient-role-pill-border: hsl(var(--white-hsl)/0.2); - --profile-gradient-section-box: hsl(var(--black-hsl)/0.45); - --progressbar-indicator-background: var(--blurple-50); - --progressbar-track-background: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --radio-background-active: var(--blurple-65); - --radio-background-default: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --radio-background-hover: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --radio-background-selected-default: var(--blurple-50); - --radio-background-selected-hover: var(--blurple-60); - --radio-border-active: hsl(var(--opacity-20-hsl)/0.2); - --radio-border-default: hsl(var(--opacity-64-hsl)/0.6392156862745098); - --radio-border-hover: hsl(var(--opacity-80-hsl)/0.8); - --radio-border-selected-default: hsl(var(--opacity-20-hsl)/0.2); - --radio-border-selected-hover: hsl(var(--opacity-20-hsl)/0.2); - --radio-foreground-active: var(--blurple-50); - --radio-foreground-default: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --radio-foreground-hover: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --radio-thumb-background-active: var(--neutral-1); - --redesign-button-premium-primary-pink-for-gradient: var(--premium-tier-2-pink-for-gradients); - --redesign-button-premium-primary-pressed-background: hsl(var(--black-hsl)/0.1); - --redesign-button-premium-primary-purple-for-gradient: var(--premium-tier-2-purple-for-gradients); - --redesign-button-premium-primary-purple-for-gradient-2: var(--premium-tier-2-purple-for-gradients-2); - --redesign-button-tertiary-background: hsl(var(--opacity-black-12-hsl)/0.12156862745098039); - --redesign-button-tertiary-pressed-background: hsl(var(--plum-11-hsl)/0.2); - --redesign-button-tertiary-pressed-text: var(--plum-6); - --redesign-button-tertiary-text: var(--neutral-10); - --scrollbar-auto-scrollbar-color-thumb: var(--neutral-47); - --scrollbar-auto-scrollbar-color-track: var(--neutral-100); - --scrollbar-auto-thumb: var(--neutral-46); - --scrollbar-auto-track: hsl(var(--transparent-hsl)/0); - --scrollbar-thin-thumb: var(--neutral-47); - --scrollbar-thin-track: hsl(var(--black-hsl)/0); - --slider-track-background: var(--neutral-63); - --spine-default: var(--neutral-63); - --spoiler-hidden-background: var(--neutral-46); - --spoiler-hidden-background-hover: var(--neutral-37); - --spoiler-revealed-background: hsl(var(--plum-11-hsl)/0.16); - --status-danger: var(--red-400); - --status-online: var(--green-360); - --status-positive: var(--green-360); - --status-positive-background: var(--green-430); - --status-positive-text: var(--white); - --status-speaking: var(--green-360); - --status-warning: var(--yellow-300); - --status-warning-background: var(--yellow-300); - --status-warning-text: var(--black); - --switch-background-active: var(--blurple-65); - --switch-background-default: hsl(var(--opacity-black-12-hsl)/0.12156862745098039); - --switch-background-hover: hsl(var(--opacity-black-12-hsl)/0.12156862745098039); - --switch-background-selected-default: var(--blurple-50); - --switch-background-selected-hover: var(--blurple-60); - --switch-border-default: hsl(var(--opacity-20-hsl)/0.2); - --switch-border-hover: hsl(var(--opacity-40-hsl)/0.4); - --switch-border-selected-default: hsl(var(--opacity-20-hsl)/0.2); - --switch-border-selected-hover: hsl(var(--opacity-20-hsl)/0.2); - --switch-thumb-background-default: var(--neutral-1); - --switch-thumb-background-selected-default: var(--neutral-1); - --switch-thumb-icon-active: var(--blurple-50); - --switch-thumb-icon-default: var(--neutral-71); - --text-brand: var(--blurple-42); - --text-code: var(--blue-new-33); - --text-code-addition: var(--green-new-32); - --text-code-builtin: var(--orange-new-31); - --text-code-bullet: var(--yellow-new-56); - --text-code-comment: var(--neutral-37); - --text-code-deletion: var(--red-new-29); - --text-code-keyword: var(--red-new-30); - --text-code-section: var(--blue-new-33); - --text-code-string: var(--teal-new-34); - --text-code-tag: var(--green-new-29); - --text-code-title: var(--blurple-28); - --text-code-variable: var(--blue-new-28); - --text-default: var(--neutral-10); - --text-feedback-critical: var(--red-new-38); - --text-feedback-info: var(--blue-new-41); - --text-feedback-positive: var(--green-new-40); - --text-feedback-warning: var(--yellow-new-60); - --text-invert: var(--neutral-71); - --text-link: var(--blue-new-42); - --text-muted: var(--neutral-33); - --text-overlay-dark: var(--neutral-71); - --text-overlay-light: var(--neutral-8); - --text-status-dnd: var(--red-new-45); - --text-status-idle: var(--yellow-new-22); - --text-status-offline: var(--neutral-27); - --text-status-online: var(--green-new-40); - --text-strong: var(--neutral-8); - --text-subtle: var(--neutral-27); - --text-voice-connected: var(--green-new-40); - --text-voice-disconnected: var(--red-new-38); - --text-voice-speaking: var(--green-new-40); - --textbox-markdown-syntax: var(--plum-9); - --thread-channel-spine: var(--plum-13); - --user-profile-activity-toolbar-background: var(--neutral-83); - --user-profile-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --user-profile-border: hsl(var(--opacity-20-hsl)/0.2); - --user-profile-note-background-focus: var(--neutral-100); - --user-profile-overlay-background: var(--neutral-83); - --user-profile-overlay-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --user-profile-toolbar-background: var(--neutral-83); - --user-profile-toolbar-border: hsl(var(--opacity-16-hsl)/0.1607843137254902) -} - -@supports (color: color-mix(in lch,red,blue)) { - .theme-midnight { - --app-frame-background:color-mix(in oklab,var(--neutral-100) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --app-frame-border: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --app-message-embed-secondary-text: color-mix(in oklab,hsl(var(--white-hsl)/0.7) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.7) var(--custom-theme-text-color-amount,0%)); - --background-accent: color-mix(in oklab,var(--plum-17) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-base-low: color-mix(in oklab,var(--neutral-95) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-base-lower: color-mix(in oklab,var(--neutral-97) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-base-lowest: color-mix(in oklab,var(--neutral-100) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-code: color-mix(in oklab,hsl(var(--opacity-blurple-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-code-addition: color-mix(in oklab,hsl(var(--opacity-green-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-code-deletion: color-mix(in oklab,hsl(var(--opacity-red-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-critical: color-mix(in oklab,hsl(var(--opacity-red-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-feedback-info: color-mix(in oklab,hsl(var(--opacity-blue-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-feedback-positive: color-mix(in oklab,hsl(var(--opacity-green-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-feedback-warning: color-mix(in oklab,hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-scrim: color-mix(in oklab,hsl(var(--opacity-black-72-hsl)/0.7215686274509804) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.7215686274509804) var(--custom-theme-base-color-amount,0%)); - --background-scrim-lightbox: color-mix(in oklab,hsl(var(--opacity-black-92-hsl)/0.9215686274509803) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.9215686274509803) var(--custom-theme-base-color-amount,0%)); - --background-secondary-alt: color-mix(in oklab,var(--plum-17) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-surface-high: color-mix(in oklab,var(--neutral-91) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-surface-higher: color-mix(in oklab,var(--neutral-87) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-surface-highest: color-mix(in oklab,var(--neutral-83) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-tile-gradient-pink-end: color-mix(in oklab,hsl(var(--illo-pink-70-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --background-tile-gradient-pink-start: color-mix(in oklab,hsl(var(--illo-pink-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --bg-surface-raised: color-mix(in oklab,var(--plum-23) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --border-feedback-critical: color-mix(in oklab,hsl(var(--opacity-red-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-focus: color-mix(in oklab,var(--blue-new-30) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-muted: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-normal: color-mix(in oklab,hsl(var(--opacity-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-strong: color-mix(in oklab,hsl(var(--opacity-44-hsl)/0.4392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.4392156862745098) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-subtle: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --card-background-default: color-mix(in oklab,var(--neutral-91) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --card-primary-pressed-bg: color-mix(in oklab,var(--plum-25) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --card-secondary-bg: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --card-secondary-pressed-bg: color-mix(in oklab,var(--plum-26) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --channel-icon: color-mix(in oklab,var(--neutral-37) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channel-text-area-placeholder: color-mix(in oklab,var(--plum-11) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channels-default: color-mix(in oklab,var(--neutral-37) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channeltextarea-background: color-mix(in oklab,var(--plum-23) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --chat-background: color-mix(in oklab,var(--black) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --chat-background-default: color-mix(in oklab,var(--neutral-91) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --chat-border: color-mix(in oklab,var(--plum-20) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --chat-text-muted: color-mix(in oklab,var(--neutral-38) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --content-inventory-media-seekbar-container: color-mix(in oklab,hsl(var(--plum-6-hsl)/0.24) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.24) var(--custom-theme-base-color-amount,0%)); - --content-inventory-overlay-text-primary: color-mix(in oklab,hsl(var(--white-hsl)/0.85) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.85) var(--custom-theme-text-color-amount,0%)); - --content-inventory-overlay-text-secondary: color-mix(in oklab,hsl(var(--white-hsl)/0.7) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.7) var(--custom-theme-text-color-amount,0%)); - --context-menu-backdrop-background: color-mix(in oklab,hsl(var(--black-hsl)/0.7) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.7) var(--custom-theme-base-color-amount,0%)); - --control-brand-foreground: color-mix(in oklab,var(--brand-360) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --control-brand-foreground-new: color-mix(in oklab,var(--brand-360) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --control-secondary-border-active: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --control-secondary-border-default: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --creator-revenue-icon-gradient-end: color-mix(in oklab,var(--teal-430) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --creator-revenue-icon-gradient-start: color-mix(in oklab,var(--teal-360) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --creator-revenue-info-box-background: color-mix(in oklab,hsl(var(--teal-430-hsl)/0.1) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1) var(--custom-theme-base-color-amount,0%)); - --creator-revenue-info-box-border: color-mix(in oklab,var(--teal-400) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --creator-revenue-locked-channel-icon: color-mix(in oklab,var(--teal-345) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --creator-revenue-progress-bar: color-mix(in oklab,var(--teal-400) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --embed-background: color-mix(in oklab,var(--plum-23) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --embed-background-alternate: color-mix(in oklab,var(--plum-23) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --experimental-avatar-embed-bg: color-mix(in oklab,hsl(var(--opacity-black-52-hsl)/0.5215686274509804) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.5215686274509804) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-blue-end: color-mix(in oklab,hsl(var(--illo-blue-60-hsl)/0.25) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.25) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-blue-start: color-mix(in oklab,hsl(var(--illo-blue-40-hsl)/0.25) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.25) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-green-end: color-mix(in oklab,hsl(var(--illo-green-70-hsl)/0.25) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.25) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-green-start: color-mix(in oklab,hsl(var(--illo-green-50-hsl)/0.25) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.25) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-green-end: color-mix(in oklab,hsl(var(--illo-nitro-blue-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-green-start: color-mix(in oklab,hsl(var(--illo-green-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-pink-end: color-mix(in oklab,hsl(var(--illo-nitro-blue-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-pink-start: color-mix(in oklab,hsl(var(--illo-pink-60-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-pink-end: color-mix(in oklab,hsl(var(--illo-pink-70-hsl)/0.25) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.25) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-pink-start: color-mix(in oklab,hsl(var(--illo-pink-50-hsl)/0.25) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.25) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-purple-end: color-mix(in oklab,hsl(var(--illo-purple-60-hsl)/0.25) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.25) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-purple-start: color-mix(in oklab,hsl(var(--illo-purple-40-hsl)/0.25) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.25) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-bronze-end: color-mix(in oklab,hsl(var(--illo-orange-70-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-bronze-start: color-mix(in oklab,hsl(var(--illo-orange-30-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-diamond-end: color-mix(in oklab,hsl(var(--illo-purple-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-diamond-start: color-mix(in oklab,hsl(var(--illo-purple-40-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-emerald-end: color-mix(in oklab,hsl(var(--illo-green-60-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-emerald-start: color-mix(in oklab,hsl(var(--illo-green-40-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-gold-end: color-mix(in oklab,hsl(var(--yellow-new-41-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-gold-start: color-mix(in oklab,hsl(var(--illo-yellow-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-opal-end: color-mix(in oklab,hsl(var(--blue-new-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-opal-start: color-mix(in oklab,hsl(var(--teal-new-30-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-platinum-end: color-mix(in oklab,hsl(var(--teal-new-60-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-platinum-start: color-mix(in oklab,hsl(var(--teal-new-20-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-ruby-end: color-mix(in oklab,hsl(var(--red-new-80-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-ruby-start: color-mix(in oklab,hsl(var(--red-new-44-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-silver-end: color-mix(in oklab,hsl(var(--neutral-58-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-silver-start: color-mix(in oklab,hsl(var(--neutral-12-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --gradient-progress-pill-background: color-mix(in oklab,var(--neutral-63) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --guild-profile-banner-background-default: color-mix(in oklab,var(--neutral-100) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --home-background: color-mix(in oklab,var(--plum-18) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --icon-default: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-critical: color-mix(in oklab,var(--red-new-38) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-info: color-mix(in oklab,var(--blue-new-41) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-positive: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-warning: color-mix(in oklab,var(--yellow-new-60) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-invert: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-link: color-mix(in oklab,var(--blue-new-42) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-muted: color-mix(in oklab,var(--neutral-33) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-overlay-dark: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-overlay-light: color-mix(in oklab,var(--neutral-8) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-dnd: color-mix(in oklab,var(--red-new-45) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-idle: color-mix(in oklab,var(--yellow-new-22) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-offline: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-online: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-strong: color-mix(in oklab,var(--neutral-8) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-subtle: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-transparent: color-mix(in oklab,hsl(var(--transparent-hsl)/0) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0) var(--custom-theme-text-color-amount,0%)); - --icon-voice-connected: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-voice-disconnected: color-mix(in oklab,var(--red-new-38) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-voice-speaking: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-background-default: color-mix(in oklab,hsl(var(--opacity-black-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --input-background-error-default: color-mix(in oklab,hsl(var(--opacity-red-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-base-color-amount,0%)); - --input-border-active: color-mix(in oklab,var(--blurple-50) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-default: color-mix(in oklab,hsl(var(--opacity-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-error-default: color-mix(in oklab,var(--red-new-38) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-hover: color-mix(in oklab,hsl(var(--opacity-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-readonly: color-mix(in oklab,hsl(var(--opacity-white-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-icon-default: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-placeholder-text-default: color-mix(in oklab,var(--neutral-39) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-default: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-error-default: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-background-active: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --interactive-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --interactive-background-selected: color-mix(in oklab,hsl(var(--opacity-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --interactive-icon-active: color-mix(in oklab,var(--neutral-8) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-default: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-hover: color-mix(in oklab,var(--neutral-8) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-muted: color-mix(in oklab,var(--plum-13) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-active: color-mix(in oklab,var(--neutral-8) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-default: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-hover: color-mix(in oklab,var(--neutral-8) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --logo-primary: color-mix(in oklab,var(--white) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --mention-background: color-mix(in oklab,hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --mention-foreground: color-mix(in oklab,var(--blurple-26) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --message-automod-background-default: color-mix(in oklab,hsl(var(--red-345-hsl)/0.08) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.08) var(--custom-theme-base-color-amount,0%)); - --message-automod-background-hover: color-mix(in oklab,hsl(var(--red-400-hsl)/0.1) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1) var(--custom-theme-base-color-amount,0%)); - --message-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-default: color-mix(in oklab,hsl(var(--opacity-blurple-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-hover: color-mix(in oklab,hsl(var(--opacity-blurple-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-default: color-mix(in oklab,hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-hover: color-mix(in oklab,hsl(var(--opacity-yellow-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-base-color-amount,0%)); - --message-reacted-background-default: color-mix(in oklab,hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --message-reacted-text-default: color-mix(in oklab,var(--blurple-21) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --mobile-background-scrim-opaque: color-mix(in oklab,var(--plum-19) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --mobile-expression-picker-background-default: color-mix(in oklab,var(--neutral-97) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --mobile-text-heading-primary: color-mix(in oklab,var(--neutral-8) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --modal-background: color-mix(in oklab,var(--neutral-91) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --modal-footer-background: color-mix(in oklab,var(--neutral-91) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --navigator-header-tint: color-mix(in oklab,var(--white) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --overlay-backdrop-lightbox: color-mix(in oklab,hsl(var(--opacity-black-92-hsl)/0.9215686274509803) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.9215686274509803) var(--custom-theme-base-color-amount,0%)); - --panel-bg: color-mix(in oklab,var(--plum-24) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --polls-normal-image-background: color-mix(in oklab,var(--primary-660) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --polls-victor-fill: color-mix(in oklab,hsl(var(--green-360-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --polls-voted-fill: color-mix(in oklab,hsl(var(--brand-500-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --premium-nitro-pink-text: color-mix(in oklab,var(--pink-55) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --profile-gradient-note-background: color-mix(in oklab,hsl(var(--black-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-overlay: color-mix(in oklab,hsl(var(--black-hsl)/0.6) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.6) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-overlay-synced-with-user-theme: color-mix(in oklab,hsl(var(--black-hsl)/0.8) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.8) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-role-pill-background: color-mix(in oklab,hsl(var(--primary-660-hsl)/0.5) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.5) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-role-pill-border: color-mix(in oklab,hsl(var(--white-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --profile-gradient-section-box: color-mix(in oklab,hsl(var(--black-hsl)/0.45) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.45) var(--custom-theme-base-color-amount,0%)); - --progressbar-indicator-background: color-mix(in oklab,var(--blurple-50) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --progressbar-track-background: color-mix(in oklab,hsl(var(--opacity-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-pink-for-gradient: color-mix(in oklab,var(--premium-tier-2-pink-for-gradients) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-pressed-background: color-mix(in oklab,hsl(var(--black-hsl)/0.1) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-purple-for-gradient: color-mix(in oklab,var(--premium-tier-2-purple-for-gradients) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-purple-for-gradient-2: color-mix(in oklab,var(--premium-tier-2-purple-for-gradients-2) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-background: color-mix(in oklab,hsl(var(--opacity-black-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-pressed-background: color-mix(in oklab,hsl(var(--plum-11-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-pressed-text: color-mix(in oklab,var(--plum-6) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --redesign-button-tertiary-text: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --scrollbar-auto-scrollbar-color-thumb: color-mix(in oklab,var(--neutral-47) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-auto-scrollbar-color-track: color-mix(in oklab,var(--neutral-100) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-auto-thumb: color-mix(in oklab,var(--neutral-46) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-auto-track: color-mix(in oklab,hsl(var(--transparent-hsl)/0) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0) var(--custom-theme-base-color-amount,0%)); - --scrollbar-thin-thumb: color-mix(in oklab,var(--neutral-47) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-thin-track: color-mix(in oklab,hsl(var(--black-hsl)/0) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0) var(--custom-theme-base-color-amount,0%)); - --slider-track-background: color-mix(in oklab,var(--neutral-63) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --spine-default: color-mix(in oklab,var(--neutral-63) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --spoiler-hidden-background: color-mix(in oklab,var(--neutral-46) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --spoiler-hidden-background-hover: color-mix(in oklab,var(--neutral-37) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --spoiler-revealed-background: color-mix(in oklab,hsl(var(--plum-11-hsl)/0.16) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.16) var(--custom-theme-base-color-amount,0%)); - --text-brand: color-mix(in oklab,var(--blurple-42) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code: color-mix(in oklab,var(--blue-new-33) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-addition: color-mix(in oklab,var(--green-new-32) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-builtin: color-mix(in oklab,var(--orange-new-31) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-bullet: color-mix(in oklab,var(--yellow-new-56) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-comment: color-mix(in oklab,var(--neutral-37) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-deletion: color-mix(in oklab,var(--red-new-29) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-keyword: color-mix(in oklab,var(--red-new-30) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-section: color-mix(in oklab,var(--blue-new-33) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-string: color-mix(in oklab,var(--teal-new-34) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-tag: color-mix(in oklab,var(--green-new-29) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-title: color-mix(in oklab,var(--blurple-28) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-variable: color-mix(in oklab,var(--blue-new-28) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-default: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-critical: color-mix(in oklab,var(--red-new-38) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-info: color-mix(in oklab,var(--blue-new-41) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-positive: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-warning: color-mix(in oklab,var(--yellow-new-60) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-invert: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-link: color-mix(in oklab,var(--blue-new-42) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-muted: color-mix(in oklab,var(--neutral-33) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-dnd: color-mix(in oklab,var(--red-new-45) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-idle: color-mix(in oklab,var(--yellow-new-22) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-offline: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-online: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-strong: color-mix(in oklab,var(--neutral-8) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-subtle: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-connected: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-disconnected: color-mix(in oklab,var(--red-new-38) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-speaking: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --textbox-markdown-syntax: color-mix(in oklab,var(--plum-9) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --thread-channel-spine: color-mix(in oklab,var(--plum-13) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-activity-toolbar-background: color-mix(in oklab,var(--neutral-83) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --user-profile-border: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-note-background-focus: color-mix(in oklab,var(--neutral-100) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-overlay-background: color-mix(in oklab,var(--neutral-83) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-overlay-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --user-profile-toolbar-background: color-mix(in oklab,var(--neutral-83) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-toolbar-border: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))) - } -} - -.theme-darker { - --app-frame-background: var(--neutral-97); - --app-frame-border: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --app-message-embed-secondary-text: hsl(var(--white-hsl)/0.7); - --background-accent: var(--plum-15); - --background-base-low: var(--neutral-82); - --background-base-lower: var(--neutral-86); - --background-base-lowest: var(--neutral-92); - --background-brand: var(--blurple-50); - --background-code: hsl(var(--opacity-blurple-8-hsl)/0.0784313725490196); - --background-code-addition: hsl(var(--opacity-green-12-hsl)/0.12156862745098039); - --background-code-deletion: hsl(var(--opacity-red-12-hsl)/0.12156862745098039); - --background-feedback-critical: hsl(var(--opacity-red-8-hsl)/0.0784313725490196); - --background-feedback-info: hsl(var(--opacity-blue-8-hsl)/0.0784313725490196); - --background-feedback-notification: var(--red-new-46); - --background-feedback-positive: hsl(var(--opacity-green-8-hsl)/0.0784313725490196); - --background-feedback-warning: hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196); - --background-mod-muted: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --background-mod-normal: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --background-mod-strong: hsl(var(--opacity-20-hsl)/0.2); - --background-mod-subtle: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --background-scrim: hsl(var(--opacity-black-72-hsl)/0.7215686274509804); - --background-scrim-lightbox: hsl(var(--opacity-black-92-hsl)/0.9215686274509803); - --background-secondary-alt: var(--plum-15); - --background-surface-high: var(--neutral-79); - --background-surface-higher: var(--neutral-76); - --background-surface-highest: var(--neutral-73); - --background-tile-gradient-pink-end: hsl(var(--illo-pink-70-hsl)/0.3); - --background-tile-gradient-pink-start: hsl(var(--illo-pink-50-hsl)/0.3); - --badge-background-brand: var(--blurple-50); - --badge-background-default: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --badge-expressive-background-default: var(--neutral-1); - --badge-expressive-text-default: var(--neutral-71); - --badge-notification-background: var(--red-new-46); - --badge-text-brand: var(--neutral-1); - --badge-text-default: var(--neutral-2); - --bg-surface-raised: var(--plum-18); - --border-feedback-critical: hsl(var(--opacity-red-20-hsl)/0.2); - --border-focus: var(--blue-new-30); - --border-muted: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --border-normal: hsl(var(--opacity-20-hsl)/0.2); - --border-strong: hsl(var(--opacity-44-hsl)/0.4392156862745098); - --border-subtle: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --button-danger-background-disabled: var(--red-new-50); - --button-outline-brand-background-hover: var(--brand-500); - --button-outline-brand-border-active: var(--brand-560); - --button-outline-primary-text: var(--white); - --card-background-default: var(--neutral-79); - --card-primary-pressed-bg: var(--plum-19); - --card-secondary-bg: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --card-secondary-pressed-bg: var(--plum-21); - --channel-icon: var(--neutral-35); - --channel-text-area-placeholder: var(--plum-11); - --channels-default: var(--neutral-35); - --channeltextarea-background: var(--plum-15); - --chat-background: var(--plum-16); - --chat-background-default: var(--neutral-80); - --chat-border: var(--plum-20); - --chat-text-muted: var(--neutral-35); - --checkbox-background-active: var(--blurple-65); - --checkbox-background-default: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --checkbox-background-hover: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --checkbox-background-selected-default: var(--blurple-50); - --checkbox-background-selected-hover: var(--blurple-60); - --checkbox-border-active: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --checkbox-border-default: var(--neutral-41); - --checkbox-border-hover: hsl(var(--opacity-80-hsl)/0.8); - --checkbox-border-selected-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --checkbox-border-selected-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --checkbox-icon-active: var(--neutral-1); - --chip-blurple-dark-background: var(--blurple-70); - --chip-blurple-dark-text: var(--blurple-1); - --chip-blurple-light-background: var(--blurple-20); - --chip-blurple-light-text: var(--blurple-91); - --chip-blurple-medium-background: var(--blurple-40); - --chip-blurple-medium-text: var(--blurple-100); - --chip-gray-dark-background: var(--neutral-75); - --chip-gray-dark-text: var(--neutral-6); - --chip-gray-light-background: var(--neutral-10); - --chip-gray-light-text: var(--neutral-68); - --chip-gray-medium-background: var(--neutral-40); - --chip-gray-medium-text: var(--neutral-1); - --chip-green-dark-background: var(--green-new-70); - --chip-green-dark-text: var(--green-new-1); - --chip-green-light-background: var(--green-new-20); - --chip-green-light-text: var(--green-new-91); - --chip-green-medium-background: var(--green-new-45); - --chip-green-medium-text: var(--green-new-96); - --chip-orange-dark-background: var(--orange-new-70); - --chip-orange-dark-text: var(--orange-new-1); - --chip-orange-light-background: var(--orange-new-20); - --chip-orange-light-text: var(--orange-new-93); - --chip-orange-medium-background: var(--orange-new-40); - --chip-orange-medium-text: var(--orange-new-100); - --chip-pink-dark-background: var(--illo-pink-60); - --chip-pink-dark-text: var(--neutral-1); - --chip-pink-light-background: var(--illo-pink-20); - --chip-pink-light-text: var(--neutral-73); - --chip-pink-medium-background: var(--illo-pink-40); - --chip-pink-medium-text: var(--neutral-100); - --chip-purple-dark-background: var(--illo-purple-60); - --chip-purple-dark-text: var(--neutral-10); - --chip-purple-light-background: var(--illo-purple-20); - --chip-purple-light-text: var(--neutral-75); - --chip-purple-medium-background: var(--illo-purple-40); - --chip-purple-medium-text: var(--neutral-100); - --chip-red-dark-background: var(--red-new-70); - --chip-red-dark-text: var(--red-new-1); - --chip-red-light-background: var(--red-new-20); - --chip-red-light-text: var(--red-new-95); - --chip-red-medium-background: var(--red-new-45); - --chip-red-medium-text: var(--red-new-100); - --chip-yellow-dark-background: var(--yellow-new-70); - --chip-yellow-dark-text: var(--yellow-new-1); - --chip-yellow-light-background: var(--yellow-new-20); - --chip-yellow-light-text: var(--yellow-new-87); - --chip-yellow-medium-background: var(--yellow-new-45); - --chip-yellow-medium-text: var(--yellow-new-80); - --content-inventory-media-seekbar-container: hsl(var(--plum-6-hsl)/0.24); - --content-inventory-overlay-text-primary: hsl(var(--white-hsl)/0.85); - --content-inventory-overlay-text-secondary: hsl(var(--white-hsl)/0.7); - --context-menu-backdrop-background: hsl(var(--opacity-black-72-hsl)/0.7215686274509804); - --control-brand-foreground: var(--brand-360); - --control-brand-foreground-new: var(--brand-360); - --control-connected-background-active: var(--green-new-65); - --control-connected-background-default: var(--green-new-50); - --control-connected-background-hover: var(--green-new-60); - --control-connected-border-active: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-connected-border-default: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-connected-border-hover: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-connected-icon-active: var(--neutral-1); - --control-connected-icon-default: var(--neutral-1); - --control-connected-icon-hover: var(--neutral-1); - --control-connected-text-active: var(--neutral-1); - --control-connected-text-default: var(--neutral-1); - --control-connected-text-hover: var(--neutral-1); - --control-critical-primary-background-active: var(--red-new-65); - --control-critical-primary-background-default: var(--red-new-50); - --control-critical-primary-background-hover: var(--red-new-60); - --control-critical-primary-border-active: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-critical-primary-border-default: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-critical-primary-border-hover: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-critical-primary-icon-active: var(--neutral-1); - --control-critical-primary-icon-default: var(--neutral-1); - --control-critical-primary-icon-hover: var(--neutral-1); - --control-critical-primary-text-active: var(--neutral-1); - --control-critical-primary-text-default: var(--neutral-1); - --control-critical-primary-text-hover: var(--neutral-1); - --control-critical-secondary-background-active: hsl(var(--opacity-20-hsl)/0.2); - --control-critical-secondary-background-default: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --control-critical-secondary-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-critical-secondary-border-active: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-critical-secondary-border-default: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-critical-secondary-border-hover: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-critical-secondary-icon-active: var(--red-new-32); - --control-critical-secondary-icon-default: var(--red-new-32); - --control-critical-secondary-icon-hover: var(--red-new-32); - --control-critical-secondary-text-active: var(--red-new-32); - --control-critical-secondary-text-default: var(--red-new-32); - --control-critical-secondary-text-hover: var(--red-new-32); - --control-expressive-background-active: var(--neutral-5); - --control-expressive-background-default: var(--neutral-1); - --control-expressive-background-hover: var(--neutral-1); - --control-expressive-border-active: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-expressive-border-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-expressive-border-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-expressive-icon-active: var(--neutral-100); - --control-expressive-icon-default: var(--neutral-100); - --control-expressive-icon-hover: var(--neutral-100); - --control-expressive-text-active: var(--neutral-100); - --control-expressive-text-default: var(--neutral-100); - --control-expressive-text-hover: var(--neutral-100); - --control-icon-only-background-active: hsl(var(--opacity-20-hsl)/0.2); - --control-icon-only-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-icon-only-border-active: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-icon-only-border-hover: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-icon-only-icon-active: var(--neutral-2); - --control-icon-only-icon-default: var(--neutral-23); - --control-icon-only-icon-hover: var(--neutral-2); - --control-overlay-primary-background-active: var(--neutral-17); - --control-overlay-primary-background-default: var(--neutral-1); - --control-overlay-primary-background-hover: var(--neutral-9); - --control-overlay-primary-border-active: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-overlay-primary-border-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-overlay-primary-border-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-overlay-primary-icon-active: var(--neutral-100); - --control-overlay-primary-icon-default: var(--neutral-100); - --control-overlay-primary-icon-hover: var(--neutral-100); - --control-overlay-primary-text-active: var(--neutral-100); - --control-overlay-primary-text-default: var(--neutral-100); - --control-overlay-primary-text-hover: var(--neutral-100); - --control-overlay-secondary-background-active: hsl(var(--opacity-black-48-hsl)/0.47843137254901963); - --control-overlay-secondary-background-default: hsl(var(--opacity-black-52-hsl)/0.5215686274509804); - --control-overlay-secondary-background-hover: hsl(var(--opacity-black-64-hsl)/0.6392156862745098); - --control-overlay-secondary-border-active: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-overlay-secondary-border-default: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-overlay-secondary-border-hover: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-overlay-secondary-icon-active: var(--neutral-1); - --control-overlay-secondary-icon-default: var(--neutral-1); - --control-overlay-secondary-icon-hover: var(--neutral-1); - --control-overlay-secondary-text-active: var(--neutral-1); - --control-overlay-secondary-text-default: var(--neutral-1); - --control-overlay-secondary-text-hover: var(--neutral-1); - --control-primary-background-active: var(--blurple-65); - --control-primary-background-default: var(--blurple-50); - --control-primary-background-hover: var(--blurple-60); - --control-primary-border-active: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-primary-border-default: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-primary-border-hover: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --control-primary-icon-active: var(--neutral-1); - --control-primary-icon-default: var(--neutral-1); - --control-primary-icon-hover: var(--neutral-1); - --control-primary-text-active: var(--neutral-1); - --control-primary-text-default: var(--neutral-1); - --control-primary-text-hover: var(--neutral-1); - --control-secondary-background-active: hsl(var(--opacity-20-hsl)/0.2); - --control-secondary-background-default: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --control-secondary-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-secondary-border-active: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-secondary-border-default: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-secondary-border-hover: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --control-secondary-icon-active: var(--neutral-2); - --control-secondary-icon-default: var(--neutral-2); - --control-secondary-icon-hover: var(--neutral-2); - --control-secondary-text-active: var(--neutral-2); - --control-secondary-text-default: var(--neutral-2); - --control-secondary-text-hover: var(--neutral-2); - --creator-revenue-icon-gradient-end: var(--teal-430); - --creator-revenue-icon-gradient-start: var(--teal-360); - --creator-revenue-info-box-background: hsl(var(--teal-430-hsl)/0.1); - --creator-revenue-info-box-border: var(--teal-400); - --creator-revenue-locked-channel-icon: var(--teal-345); - --creator-revenue-progress-bar: var(--teal-400); - --embed-background: var(--plum-18); - --embed-background-alternate: var(--plum-18); - --experimental-avatar-embed-bg: hsl(var(--opacity-black-52-hsl)/0.5215686274509804); - --expressive-gradient-blue-end: hsl(var(--illo-blue-60-hsl)/0.3); - --expressive-gradient-blue-start: hsl(var(--illo-blue-40-hsl)/0.3); - --expressive-gradient-green-end: hsl(var(--illo-green-70-hsl)/0.3); - --expressive-gradient-green-start: hsl(var(--illo-green-50-hsl)/0.3); - --expressive-gradient-nitro-green-end: hsl(var(--illo-nitro-blue-hsl)/0.4); - --expressive-gradient-nitro-green-start: hsl(var(--illo-green-50-hsl)/0.4); - --expressive-gradient-nitro-pink-end: hsl(var(--illo-nitro-blue-hsl)/0.4); - --expressive-gradient-nitro-pink-start: hsl(var(--illo-pink-60-hsl)/0.4); - --expressive-gradient-pink-end: hsl(var(--illo-pink-70-hsl)/0.3); - --expressive-gradient-pink-start: hsl(var(--illo-pink-50-hsl)/0.3); - --expressive-gradient-purple-end: hsl(var(--illo-purple-60-hsl)/0.3); - --expressive-gradient-purple-start: hsl(var(--illo-purple-40-hsl)/0.3); - --expressive-gradient-tenure-badge-bronze-end: hsl(var(--illo-orange-70-hsl)/0.3); - --expressive-gradient-tenure-badge-bronze-start: hsl(var(--illo-orange-30-hsl)/0.3); - --expressive-gradient-tenure-badge-diamond-end: hsl(var(--illo-purple-50-hsl)/0.3); - --expressive-gradient-tenure-badge-diamond-start: hsl(var(--illo-purple-40-hsl)/0.3); - --expressive-gradient-tenure-badge-emerald-end: hsl(var(--illo-green-60-hsl)/0.3); - --expressive-gradient-tenure-badge-emerald-start: hsl(var(--illo-green-40-hsl)/0.3); - --expressive-gradient-tenure-badge-gold-end: hsl(var(--yellow-new-41-hsl)/0.3); - --expressive-gradient-tenure-badge-gold-start: hsl(var(--illo-yellow-50-hsl)/0.3); - --expressive-gradient-tenure-badge-opal-end: hsl(var(--blue-new-50-hsl)/0.3); - --expressive-gradient-tenure-badge-opal-start: hsl(var(--teal-new-30-hsl)/0.3); - --expressive-gradient-tenure-badge-platinum-end: hsl(var(--teal-new-60-hsl)/0.3); - --expressive-gradient-tenure-badge-platinum-start: hsl(var(--teal-new-20-hsl)/0.3); - --expressive-gradient-tenure-badge-ruby-end: hsl(var(--red-new-80-hsl)/0.3); - --expressive-gradient-tenure-badge-ruby-start: hsl(var(--red-new-44-hsl)/0.3); - --expressive-gradient-tenure-badge-silver-end: hsl(var(--neutral-58-hsl)/0.3); - --expressive-gradient-tenure-badge-silver-start: hsl(var(--neutral-12-hsl)/0.3); - --gradient-progress-pill-background: var(--neutral-56); - --guild-profile-banner-background-default: var(--neutral-92); - --home-background: var(--plum-18); - --icon-default: var(--neutral-5); - --icon-feedback-critical: var(--red-new-32); - --icon-feedback-info: var(--blue-new-35); - --icon-feedback-notification: var(--red-new-46); - --icon-feedback-positive: var(--green-new-35); - --icon-feedback-warning: var(--yellow-new-56); - --icon-invert: var(--neutral-71); - --icon-link: var(--blue-new-37); - --icon-muted: var(--neutral-29); - --icon-overlay-dark: var(--neutral-71); - --icon-overlay-light: var(--neutral-2); - --icon-status-dnd: var(--red-new-45); - --icon-status-idle: var(--yellow-new-22); - --icon-status-offline: var(--neutral-27); - --icon-status-online: var(--green-new-40); - --icon-strong: var(--neutral-2); - --icon-subtle: var(--neutral-23); - --icon-transparent: hsl(var(--transparent-hsl)/0); - --icon-voice-connected: var(--green-new-34); - --icon-voice-disconnected: var(--red-new-32); - --icon-voice-muted: var(--red-new-46); - --icon-voice-speaking: var(--green-new-40); - --input-background-default: hsl(var(--opacity-black-12-hsl)/0.12156862745098039); - --input-background-error-default: hsl(var(--opacity-red-4-hsl)/0.0392156862745098); - --input-border-active: var(--blurple-50); - --input-border-default: hsl(var(--opacity-20-hsl)/0.2); - --input-border-error-default: var(--red-new-32); - --input-border-hover: hsl(var(--opacity-20-hsl)/0.2); - --input-border-readonly: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --input-icon-default: var(--neutral-23); - --input-placeholder-text-default: var(--neutral-37); - --input-text-default: var(--neutral-5); - --input-text-error-default: var(--neutral-5); - --interactive-background-active: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --interactive-background-hover: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --interactive-background-selected: hsl(var(--opacity-20-hsl)/0.2); - --interactive-icon-active: var(--neutral-2); - --interactive-icon-default: var(--neutral-23); - --interactive-icon-hover: var(--neutral-2); - --interactive-muted: var(--plum-13); - --interactive-text-active: var(--neutral-2); - --interactive-text-default: var(--neutral-23); - --interactive-text-hover: var(--neutral-2); - --logo-primary: var(--white); - --mention-background: hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981); - --mention-foreground: var(--blurple-19); - --message-automod-background-default: hsl(var(--red-345-hsl)/0.08); - --message-automod-background-hover: hsl(var(--red-400-hsl)/0.1); - --message-background-hover: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --message-highlight-background-default: hsl(var(--opacity-blurple-16-hsl)/0.1607843137254902); - --message-highlight-background-hover: hsl(var(--opacity-blurple-12-hsl)/0.12156862745098039); - --message-mentioned-background-default: hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196); - --message-mentioned-background-hover: hsl(var(--opacity-yellow-4-hsl)/0.0392156862745098); - --message-reacted-background-default: hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981); - --message-reacted-text-default: var(--blurple-14); - --mobile-background-scrim-opaque: var(--black); - --mobile-expression-picker-background-default: var(--neutral-86); - --mobile-text-heading-primary: var(--neutral-2); - --modal-background: var(--neutral-79); - --modal-footer-background: var(--neutral-79); - --navigator-header-tint: var(--white); - --notice-background-critical: var(--red-new-86); - --notice-background-info: var(--blue-new-86); - --notice-background-positive: var(--green-new-88); - --notice-background-warning: var(--yellow-new-92); - --notice-text-critical: var(--red-new-9); - --notice-text-info: var(--blue-new-13); - --notice-text-positive: var(--green-new-14); - --notice-text-warning: var(--yellow-new-28); - --overlay-backdrop-lightbox: hsl(var(--opacity-black-92-hsl)/0.9215686274509803); - --panel-bg: var(--neutral-82); - --polls-normal-image-background: var(--primary-660); - --polls-victor-fill: hsl(var(--green-360-hsl)/0.2); - --polls-voted-fill: hsl(var(--brand-500-hsl)/0.2); - --premium-nitro-pink-text: var(--pink-51); - --profile-gradient-note-background: hsl(var(--black-hsl)/0.3); - --profile-gradient-overlay: hsl(var(--black-hsl)/0.6); - --profile-gradient-overlay-synced-with-user-theme: hsl(var(--black-hsl)/0.8); - --profile-gradient-role-pill-background: hsl(var(--primary-660-hsl)/0.5); - --profile-gradient-role-pill-border: hsl(var(--white-hsl)/0.2); - --profile-gradient-section-box: hsl(var(--black-hsl)/0.45); - --progressbar-indicator-background: var(--blurple-50); - --progressbar-track-background: hsl(var(--opacity-20-hsl)/0.2); - --radio-background-active: var(--blurple-65); - --radio-background-default: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --radio-background-hover: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --radio-background-selected-default: var(--blurple-50); - --radio-background-selected-hover: var(--blurple-60); - --radio-border-active: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --radio-border-default: hsl(var(--opacity-64-hsl)/0.6392156862745098); - --radio-border-hover: hsl(var(--opacity-80-hsl)/0.8); - --radio-border-selected-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --radio-border-selected-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --radio-foreground-active: var(--blurple-50); - --radio-foreground-default: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --radio-foreground-hover: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --radio-thumb-background-active: var(--neutral-1); - --redesign-button-premium-primary-pink-for-gradient: var(--premium-tier-2-pink-for-gradients); - --redesign-button-premium-primary-pressed-background: hsl(var(--black-hsl)/0.1); - --redesign-button-premium-primary-purple-for-gradient: var(--premium-tier-2-purple-for-gradients); - --redesign-button-premium-primary-purple-for-gradient-2: var(--premium-tier-2-purple-for-gradients-2); - --redesign-button-tertiary-background: hsl(var(--plum-11-hsl)/0.12); - --redesign-button-tertiary-pressed-background: hsl(var(--plum-11-hsl)/0.2); - --redesign-button-tertiary-pressed-text: var(--plum-5); - --redesign-button-tertiary-text: var(--neutral-5); - --scrollbar-auto-scrollbar-color-thumb: var(--neutral-45); - --scrollbar-auto-scrollbar-color-track: var(--neutral-94); - --scrollbar-auto-thumb: var(--neutral-43); - --scrollbar-auto-track: hsl(var(--transparent-hsl)/0); - --scrollbar-thin-thumb: var(--neutral-45); - --scrollbar-thin-track: hsl(var(--black-hsl)/0); - --slider-track-background: var(--neutral-55); - --spine-default: var(--neutral-55); - --spoiler-hidden-background: var(--neutral-43); - --spoiler-hidden-background-hover: var(--neutral-34); - --spoiler-revealed-background: hsl(var(--plum-11-hsl)/0.16); - --status-danger: var(--red-400); - --status-online: var(--green-360); - --status-positive: var(--green-360); - --status-positive-background: var(--green-430); - --status-positive-text: var(--white); - --status-speaking: var(--green-360); - --status-warning: var(--yellow-300); - --status-warning-background: var(--yellow-300); - --status-warning-text: var(--black); - --switch-background-active: var(--blurple-65); - --switch-background-default: hsl(var(--opacity-black-12-hsl)/0.12156862745098039); - --switch-background-hover: hsl(var(--opacity-black-12-hsl)/0.12156862745098039); - --switch-background-selected-default: var(--blurple-50); - --switch-background-selected-hover: var(--blurple-60); - --switch-border-default: hsl(var(--opacity-20-hsl)/0.2); - --switch-border-hover: hsl(var(--opacity-40-hsl)/0.4); - --switch-border-selected-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --switch-border-selected-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --switch-thumb-background-default: var(--neutral-1); - --switch-thumb-background-selected-default: var(--neutral-1); - --switch-thumb-icon-active: var(--blurple-50); - --switch-thumb-icon-default: var(--neutral-71); - --text-brand: var(--blurple-36); - --text-code: var(--blue-new-27); - --text-code-addition: var(--green-new-26); - --text-code-builtin: var(--orange-new-25); - --text-code-bullet: var(--yellow-new-52); - --text-code-comment: var(--neutral-33); - --text-code-deletion: var(--red-new-23); - --text-code-keyword: var(--red-new-24); - --text-code-section: var(--blue-new-27); - --text-code-string: var(--teal-new-28); - --text-code-tag: var(--green-new-23); - --text-code-title: var(--blurple-21); - --text-code-variable: var(--blue-new-22); - --text-default: var(--neutral-5); - --text-feedback-critical: var(--red-new-32); - --text-feedback-info: var(--blue-new-35); - --text-feedback-positive: var(--green-new-35); - --text-feedback-warning: var(--yellow-new-56); - --text-invert: var(--neutral-71); - --text-link: var(--blue-new-37); - --text-muted: var(--neutral-29); - --text-overlay-dark: var(--neutral-71); - --text-overlay-light: var(--neutral-2); - --text-status-dnd: var(--red-new-45); - --text-status-idle: var(--yellow-new-22); - --text-status-offline: var(--neutral-27); - --text-status-online: var(--green-new-40); - --text-strong: var(--neutral-2); - --text-subtle: var(--neutral-23); - --text-voice-connected: var(--green-new-34); - --text-voice-disconnected: var(--red-new-32); - --text-voice-speaking: var(--green-new-40); - --textbox-markdown-syntax: var(--plum-9); - --thread-channel-spine: var(--plum-13); - --user-profile-activity-toolbar-background: var(--neutral-73); - --user-profile-background-hover: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --user-profile-border: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --user-profile-note-background-focus: var(--neutral-92); - --user-profile-overlay-background: var(--neutral-73); - --user-profile-overlay-background-hover: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --user-profile-toolbar-background: var(--neutral-73); - --user-profile-toolbar-border: hsl(var(--opacity-4-hsl)/0.0392156862745098) -} - -@supports (color: color-mix(in lch,red,blue)) { - .theme-darker { - --app-frame-background:color-mix(in oklab,var(--neutral-97) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --app-frame-border: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --app-message-embed-secondary-text: color-mix(in oklab,hsl(var(--white-hsl)/0.7) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.7) var(--custom-theme-text-color-amount,0%)); - --background-accent: color-mix(in oklab,var(--plum-15) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-base-low: color-mix(in oklab,var(--neutral-82) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-base-lower: color-mix(in oklab,var(--neutral-86) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-base-lowest: color-mix(in oklab,var(--neutral-92) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-code: color-mix(in oklab,hsl(var(--opacity-blurple-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-code-addition: color-mix(in oklab,hsl(var(--opacity-green-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-code-deletion: color-mix(in oklab,hsl(var(--opacity-red-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-critical: color-mix(in oklab,hsl(var(--opacity-red-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-feedback-info: color-mix(in oklab,hsl(var(--opacity-blue-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-feedback-positive: color-mix(in oklab,hsl(var(--opacity-green-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-feedback-warning: color-mix(in oklab,hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-scrim: color-mix(in oklab,hsl(var(--opacity-black-72-hsl)/0.7215686274509804) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.7215686274509804) var(--custom-theme-base-color-amount,0%)); - --background-scrim-lightbox: color-mix(in oklab,hsl(var(--opacity-black-92-hsl)/0.9215686274509803) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.9215686274509803) var(--custom-theme-base-color-amount,0%)); - --background-secondary-alt: color-mix(in oklab,var(--plum-15) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-surface-high: color-mix(in oklab,var(--neutral-79) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-surface-higher: color-mix(in oklab,var(--neutral-76) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-surface-highest: color-mix(in oklab,var(--neutral-73) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --background-tile-gradient-pink-end: color-mix(in oklab,hsl(var(--illo-pink-70-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --background-tile-gradient-pink-start: color-mix(in oklab,hsl(var(--illo-pink-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --bg-surface-raised: color-mix(in oklab,var(--plum-18) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --border-feedback-critical: color-mix(in oklab,hsl(var(--opacity-red-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-focus: color-mix(in oklab,var(--blue-new-30) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-muted: color-mix(in oklab,hsl(var(--opacity-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-normal: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-strong: color-mix(in oklab,hsl(var(--opacity-44-hsl)/0.4392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.4392156862745098) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-subtle: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --card-background-default: color-mix(in oklab,var(--neutral-79) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --card-primary-pressed-bg: color-mix(in oklab,var(--plum-19) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --card-secondary-bg: color-mix(in oklab,hsl(var(--opacity-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --card-secondary-pressed-bg: color-mix(in oklab,var(--plum-21) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --channel-icon: color-mix(in oklab,var(--neutral-35) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channel-text-area-placeholder: color-mix(in oklab,var(--plum-11) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channels-default: color-mix(in oklab,var(--neutral-35) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channeltextarea-background: color-mix(in oklab,var(--plum-15) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --chat-background: color-mix(in oklab,var(--plum-16) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --chat-background-default: color-mix(in oklab,var(--neutral-80) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --chat-border: color-mix(in oklab,var(--plum-20) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --chat-text-muted: color-mix(in oklab,var(--neutral-35) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --content-inventory-media-seekbar-container: color-mix(in oklab,hsl(var(--plum-6-hsl)/0.24) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.24) var(--custom-theme-base-color-amount,0%)); - --content-inventory-overlay-text-primary: color-mix(in oklab,hsl(var(--white-hsl)/0.85) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.85) var(--custom-theme-text-color-amount,0%)); - --content-inventory-overlay-text-secondary: color-mix(in oklab,hsl(var(--white-hsl)/0.7) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.7) var(--custom-theme-text-color-amount,0%)); - --context-menu-backdrop-background: color-mix(in oklab,hsl(var(--opacity-black-72-hsl)/0.7215686274509804) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.7215686274509804) var(--custom-theme-base-color-amount,0%)); - --control-brand-foreground: color-mix(in oklab,var(--brand-360) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --control-brand-foreground-new: color-mix(in oklab,var(--brand-360) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --control-secondary-border-active: color-mix(in oklab,hsl(var(--opacity-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --control-secondary-border-default: color-mix(in oklab,hsl(var(--opacity-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --creator-revenue-icon-gradient-end: color-mix(in oklab,var(--teal-430) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --creator-revenue-icon-gradient-start: color-mix(in oklab,var(--teal-360) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --creator-revenue-info-box-background: color-mix(in oklab,hsl(var(--teal-430-hsl)/0.1) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1) var(--custom-theme-base-color-amount,0%)); - --creator-revenue-info-box-border: color-mix(in oklab,var(--teal-400) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --creator-revenue-locked-channel-icon: color-mix(in oklab,var(--teal-345) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --creator-revenue-progress-bar: color-mix(in oklab,var(--teal-400) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --embed-background: color-mix(in oklab,var(--plum-18) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --embed-background-alternate: color-mix(in oklab,var(--plum-18) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --experimental-avatar-embed-bg: color-mix(in oklab,hsl(var(--opacity-black-52-hsl)/0.5215686274509804) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.5215686274509804) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-blue-end: color-mix(in oklab,hsl(var(--illo-blue-60-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-blue-start: color-mix(in oklab,hsl(var(--illo-blue-40-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-green-end: color-mix(in oklab,hsl(var(--illo-green-70-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-green-start: color-mix(in oklab,hsl(var(--illo-green-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-green-end: color-mix(in oklab,hsl(var(--illo-nitro-blue-hsl)/0.4) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.4) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-green-start: color-mix(in oklab,hsl(var(--illo-green-50-hsl)/0.4) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.4) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-pink-end: color-mix(in oklab,hsl(var(--illo-nitro-blue-hsl)/0.4) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.4) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-nitro-pink-start: color-mix(in oklab,hsl(var(--illo-pink-60-hsl)/0.4) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.4) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-pink-end: color-mix(in oklab,hsl(var(--illo-pink-70-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-pink-start: color-mix(in oklab,hsl(var(--illo-pink-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-purple-end: color-mix(in oklab,hsl(var(--illo-purple-60-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-purple-start: color-mix(in oklab,hsl(var(--illo-purple-40-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-bronze-end: color-mix(in oklab,hsl(var(--illo-orange-70-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-bronze-start: color-mix(in oklab,hsl(var(--illo-orange-30-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-diamond-end: color-mix(in oklab,hsl(var(--illo-purple-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-diamond-start: color-mix(in oklab,hsl(var(--illo-purple-40-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-emerald-end: color-mix(in oklab,hsl(var(--illo-green-60-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-emerald-start: color-mix(in oklab,hsl(var(--illo-green-40-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-gold-end: color-mix(in oklab,hsl(var(--yellow-new-41-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-gold-start: color-mix(in oklab,hsl(var(--illo-yellow-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-opal-end: color-mix(in oklab,hsl(var(--blue-new-50-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-opal-start: color-mix(in oklab,hsl(var(--teal-new-30-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-platinum-end: color-mix(in oklab,hsl(var(--teal-new-60-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-platinum-start: color-mix(in oklab,hsl(var(--teal-new-20-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-ruby-end: color-mix(in oklab,hsl(var(--red-new-80-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-ruby-start: color-mix(in oklab,hsl(var(--red-new-44-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-silver-end: color-mix(in oklab,hsl(var(--neutral-58-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --expressive-gradient-tenure-badge-silver-start: color-mix(in oklab,hsl(var(--neutral-12-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --gradient-progress-pill-background: color-mix(in oklab,var(--neutral-56) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --guild-profile-banner-background-default: color-mix(in oklab,var(--neutral-92) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --home-background: color-mix(in oklab,var(--plum-18) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --icon-default: color-mix(in oklab,var(--neutral-5) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-critical: color-mix(in oklab,var(--red-new-32) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-info: color-mix(in oklab,var(--blue-new-35) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-positive: color-mix(in oklab,var(--green-new-35) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-warning: color-mix(in oklab,var(--yellow-new-56) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-invert: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-link: color-mix(in oklab,var(--blue-new-37) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-muted: color-mix(in oklab,var(--neutral-29) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-overlay-dark: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-overlay-light: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-dnd: color-mix(in oklab,var(--red-new-45) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-idle: color-mix(in oklab,var(--yellow-new-22) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-offline: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-status-online: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-strong: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-subtle: color-mix(in oklab,var(--neutral-23) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-transparent: color-mix(in oklab,hsl(var(--transparent-hsl)/0) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0) var(--custom-theme-text-color-amount,0%)); - --icon-voice-connected: color-mix(in oklab,var(--green-new-34) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-voice-disconnected: color-mix(in oklab,var(--red-new-32) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-voice-speaking: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-background-default: color-mix(in oklab,hsl(var(--opacity-black-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --input-background-error-default: color-mix(in oklab,hsl(var(--opacity-red-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-base-color-amount,0%)); - --input-border-active: color-mix(in oklab,var(--blurple-50) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-default: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-error-default: color-mix(in oklab,var(--red-new-32) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-hover: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-readonly: color-mix(in oklab,hsl(var(--opacity-white-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-icon-default: color-mix(in oklab,var(--neutral-23) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-placeholder-text-default: color-mix(in oklab,var(--neutral-37) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-default: color-mix(in oklab,var(--neutral-5) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-error-default: color-mix(in oklab,var(--neutral-5) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-background-active: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --interactive-background-hover: color-mix(in oklab,hsl(var(--opacity-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --interactive-background-selected: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --interactive-icon-active: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-default: color-mix(in oklab,var(--neutral-23) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-hover: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-muted: color-mix(in oklab,var(--plum-13) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-active: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-default: color-mix(in oklab,var(--neutral-23) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-hover: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --logo-primary: color-mix(in oklab,var(--white) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --mention-background: color-mix(in oklab,hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --mention-foreground: color-mix(in oklab,var(--blurple-19) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --message-automod-background-default: color-mix(in oklab,hsl(var(--red-345-hsl)/0.08) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.08) var(--custom-theme-base-color-amount,0%)); - --message-automod-background-hover: color-mix(in oklab,hsl(var(--red-400-hsl)/0.1) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1) var(--custom-theme-base-color-amount,0%)); - --message-background-hover: color-mix(in oklab,hsl(var(--opacity-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-default: color-mix(in oklab,hsl(var(--opacity-blurple-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-hover: color-mix(in oklab,hsl(var(--opacity-blurple-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-default: color-mix(in oklab,hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-hover: color-mix(in oklab,hsl(var(--opacity-yellow-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-base-color-amount,0%)); - --message-reacted-background-default: color-mix(in oklab,hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --message-reacted-text-default: color-mix(in oklab,var(--blurple-14) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --mobile-background-scrim-opaque: color-mix(in oklab,var(--black) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --mobile-expression-picker-background-default: color-mix(in oklab,var(--neutral-86) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --mobile-text-heading-primary: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --modal-background: color-mix(in oklab,var(--neutral-79) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --modal-footer-background: color-mix(in oklab,var(--neutral-79) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --navigator-header-tint: color-mix(in oklab,var(--white) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --overlay-backdrop-lightbox: color-mix(in oklab,hsl(var(--opacity-black-92-hsl)/0.9215686274509803) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.9215686274509803) var(--custom-theme-base-color-amount,0%)); - --panel-bg: color-mix(in oklab,var(--neutral-82) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --polls-normal-image-background: color-mix(in oklab,var(--primary-660) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --polls-victor-fill: color-mix(in oklab,hsl(var(--green-360-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --polls-voted-fill: color-mix(in oklab,hsl(var(--brand-500-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --premium-nitro-pink-text: color-mix(in oklab,var(--pink-51) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --profile-gradient-note-background: color-mix(in oklab,hsl(var(--black-hsl)/0.3) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.3) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-overlay: color-mix(in oklab,hsl(var(--black-hsl)/0.6) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.6) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-overlay-synced-with-user-theme: color-mix(in oklab,hsl(var(--black-hsl)/0.8) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.8) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-role-pill-background: color-mix(in oklab,hsl(var(--primary-660-hsl)/0.5) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.5) var(--custom-theme-base-color-amount,0%)); - --profile-gradient-role-pill-border: color-mix(in oklab,hsl(var(--white-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --profile-gradient-section-box: color-mix(in oklab,hsl(var(--black-hsl)/0.45) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.45) var(--custom-theme-base-color-amount,0%)); - --progressbar-indicator-background: color-mix(in oklab,var(--blurple-50) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --progressbar-track-background: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-pink-for-gradient: color-mix(in oklab,var(--premium-tier-2-pink-for-gradients) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-pressed-background: color-mix(in oklab,hsl(var(--black-hsl)/0.1) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-purple-for-gradient: color-mix(in oklab,var(--premium-tier-2-purple-for-gradients) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --redesign-button-premium-primary-purple-for-gradient-2: color-mix(in oklab,var(--premium-tier-2-purple-for-gradients-2) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-background: color-mix(in oklab,hsl(var(--plum-11-hsl)/0.12) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-pressed-background: color-mix(in oklab,hsl(var(--plum-11-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-pressed-text: color-mix(in oklab,var(--plum-5) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --redesign-button-tertiary-text: color-mix(in oklab,var(--neutral-5) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --scrollbar-auto-scrollbar-color-thumb: color-mix(in oklab,var(--neutral-45) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-auto-scrollbar-color-track: color-mix(in oklab,var(--neutral-94) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-auto-thumb: color-mix(in oklab,var(--neutral-43) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-auto-track: color-mix(in oklab,hsl(var(--transparent-hsl)/0) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0) var(--custom-theme-base-color-amount,0%)); - --scrollbar-thin-thumb: color-mix(in oklab,var(--neutral-45) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --scrollbar-thin-track: color-mix(in oklab,hsl(var(--black-hsl)/0) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0) var(--custom-theme-base-color-amount,0%)); - --slider-track-background: color-mix(in oklab,var(--neutral-55) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --spine-default: color-mix(in oklab,var(--neutral-55) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --spoiler-hidden-background: color-mix(in oklab,var(--neutral-43) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --spoiler-hidden-background-hover: color-mix(in oklab,var(--neutral-34) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --spoiler-revealed-background: color-mix(in oklab,hsl(var(--plum-11-hsl)/0.16) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.16) var(--custom-theme-base-color-amount,0%)); - --text-brand: color-mix(in oklab,var(--blurple-36) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code: color-mix(in oklab,var(--blue-new-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-addition: color-mix(in oklab,var(--green-new-26) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-builtin: color-mix(in oklab,var(--orange-new-25) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-bullet: color-mix(in oklab,var(--yellow-new-52) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-comment: color-mix(in oklab,var(--neutral-33) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-deletion: color-mix(in oklab,var(--red-new-23) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-keyword: color-mix(in oklab,var(--red-new-24) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-section: color-mix(in oklab,var(--blue-new-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-string: color-mix(in oklab,var(--teal-new-28) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-tag: color-mix(in oklab,var(--green-new-23) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-title: color-mix(in oklab,var(--blurple-21) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-variable: color-mix(in oklab,var(--blue-new-22) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-default: color-mix(in oklab,var(--neutral-5) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-critical: color-mix(in oklab,var(--red-new-32) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-info: color-mix(in oklab,var(--blue-new-35) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-positive: color-mix(in oklab,var(--green-new-35) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-warning: color-mix(in oklab,var(--yellow-new-56) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-invert: color-mix(in oklab,var(--neutral-71) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-link: color-mix(in oklab,var(--blue-new-37) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-muted: color-mix(in oklab,var(--neutral-29) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-dnd: color-mix(in oklab,var(--red-new-45) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-idle: color-mix(in oklab,var(--yellow-new-22) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-offline: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-status-online: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-strong: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-subtle: color-mix(in oklab,var(--neutral-23) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-connected: color-mix(in oklab,var(--green-new-34) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-disconnected: color-mix(in oklab,var(--red-new-32) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-speaking: color-mix(in oklab,var(--green-new-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --textbox-markdown-syntax: color-mix(in oklab,var(--plum-9) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --thread-channel-spine: color-mix(in oklab,var(--plum-13) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-activity-toolbar-background: color-mix(in oklab,var(--neutral-73) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-background-hover: color-mix(in oklab,hsl(var(--opacity-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --user-profile-border: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-note-background-focus: color-mix(in oklab,var(--neutral-92) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-overlay-background: color-mix(in oklab,var(--neutral-73) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-overlay-background-hover: color-mix(in oklab,hsl(var(--opacity-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --user-profile-toolbar-background: color-mix(in oklab,var(--neutral-73) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --user-profile-toolbar-border: color-mix(in oklab,hsl(var(--opacity-4-hsl)/0.0392156862745098) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0392156862745098) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))) - } -} - -.custom-user-profile-theme.theme-dark { - --user-profile-activity-toolbar-background: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --user-profile-background-hover: hsl(var(--opacity-white-4-hsl)/0.0392156862745098); - --user-profile-border: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --user-profile-note-background-focus: hsl(var(--black-hsl)/0.3); - --user-profile-overlay-background: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --user-profile-overlay-background-hover: hsl(var(--opacity-20-hsl)/0.2); - --user-profile-toolbar-background: hsl(var(--opacity-black-48-hsl)/0.47843137254901963); - --user-profile-toolbar-border: hsl(var(--opacity-4-hsl)/0.0392156862745098) -} - -.custom-user-profile-theme.theme-light { - --user-profile-activity-toolbar-background: hsl(var(--opacity-white-48-hsl)/0.47843137254901963); - --user-profile-background-hover: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --user-profile-border: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --user-profile-note-background-focus: hsl(var(--white-hsl)/0.3); - --user-profile-overlay-background: hsl(var(--opacity-white-24-hsl)/0.23921568627450981); - --user-profile-overlay-background-hover: hsl(var(--opacity-white-36-hsl)/0.3607843137254902); - --user-profile-toolbar-background: hsl(var(--opacity-white-60-hsl)/0.6); - --user-profile-toolbar-border: hsl(var(--opacity-20-hsl)/0.2) -} - -.high-contrast-mode .theme-dark,.high-contrast-mode.theme-dark { - --app-frame-border: var(--neutral-12); - --background-code: hsl(var(--opacity-blurple-12-hsl)/0.12156862745098039); - --background-feedback-critical: hsl(var(--opacity-red-12-hsl)/0.12156862745098039); - --background-feedback-info: hsl(var(--opacity-blue-12-hsl)/0.12156862745098039); - --background-feedback-positive: hsl(var(--opacity-green-12-hsl)/0.12156862745098039); - --background-feedback-warning: hsl(var(--opacity-yellow-12-hsl)/0.12156862745098039); - --background-mod-muted: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --background-mod-normal: hsl(var(--opacity-20-hsl)/0.2); - --background-mod-strong: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --background-mod-subtle: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --badge-background-default: hsl(var(--opacity-20-hsl)/0.2); - --badge-text-default: var(--neutral-1); - --border-focus: var(--blue-new-18); - --border-muted: var(--neutral-26); - --border-normal: var(--neutral-5); - --border-strong: var(--neutral-1); - --border-subtle: var(--neutral-12); - --card-secondary-bg: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --channel-icon: var(--neutral-19); - --channel-text-area-placeholder: var(--neutral-25); - --channels-default: var(--neutral-19); - --chat-text-muted: var(--neutral-17); - --checkbox-border-active: var(--neutral-12); - --checkbox-border-default: var(--neutral-1); - --checkbox-border-hover: var(--neutral-12); - --checkbox-border-selected-default: var(--neutral-12); - --checkbox-border-selected-hover: var(--neutral-12); - --control-connected-border-active: var(--green-new-20); - --control-connected-border-default: var(--green-new-20); - --control-connected-border-hover: var(--green-new-20); - --control-critical-primary-border-active: var(--red-new-15); - --control-critical-primary-border-default: var(--red-new-15); - --control-critical-primary-border-hover: var(--red-new-15); - --control-critical-secondary-background-active: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --control-critical-secondary-background-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-critical-secondary-background-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-critical-secondary-border-active: var(--red-new-15); - --control-critical-secondary-border-default: var(--red-new-15); - --control-critical-secondary-border-hover: var(--red-new-15); - --control-critical-secondary-icon-active: var(--red-new-5); - --control-critical-secondary-icon-default: var(--red-new-5); - --control-critical-secondary-icon-hover: var(--red-new-5); - --control-critical-secondary-text-active: var(--red-new-5); - --control-critical-secondary-text-default: var(--red-new-5); - --control-critical-secondary-text-hover: var(--red-new-5); - --control-icon-only-background-active: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --control-icon-only-background-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-icon-only-border-active: var(--neutral-26); - --control-icon-only-border-hover: var(--neutral-26); - --control-icon-only-icon-active: var(--neutral-1); - --control-icon-only-icon-default: var(--neutral-2); - --control-icon-only-icon-hover: var(--neutral-1); - --control-overlay-secondary-border-active: var(--neutral-26); - --control-overlay-secondary-border-default: var(--neutral-26); - --control-overlay-secondary-border-hover: var(--neutral-26); - --control-primary-border-active: var(--blurple-18); - --control-primary-border-default: var(--blurple-18); - --control-primary-border-hover: var(--blurple-18); - --control-secondary-background-active: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --control-secondary-background-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-secondary-background-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-secondary-border-active: var(--neutral-22); - --control-secondary-border-default: var(--neutral-22); - --control-secondary-border-hover: var(--neutral-22); - --control-secondary-icon-active: var(--neutral-1); - --control-secondary-icon-default: var(--neutral-1); - --control-secondary-icon-hover: var(--neutral-1); - --control-secondary-text-active: var(--neutral-1); - --control-secondary-text-default: var(--neutral-1); - --control-secondary-text-hover: var(--neutral-1); - --gradient-progress-pill-background: var(--neutral-35); - --icon-default: var(--neutral-1); - --icon-feedback-critical: var(--red-new-5); - --icon-feedback-info: var(--blue-new-8); - --icon-feedback-positive: var(--green-new-10); - --icon-feedback-warning: var(--yellow-new-7); - --icon-link: var(--blue-new-18); - --icon-muted: var(--neutral-12); - --icon-strong: var(--neutral-1); - --icon-subtle: var(--neutral-2); - --icon-voice-disconnected: var(--red-new-5); - --input-background-default: hsl(var(--opacity-black-28-hsl)/0.2784313725490196); - --input-border-default: var(--neutral-22); - --input-border-error-default: var(--red-new-5); - --input-border-hover: var(--neutral-5); - --input-icon-default: var(--neutral-2); - --input-placeholder-text-default: var(--neutral-28); - --input-text-default: var(--neutral-1); - --input-text-error-default: var(--neutral-1); - --interactive-background-active: hsl(var(--opacity-20-hsl)/0.2); - --interactive-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --interactive-background-selected: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --interactive-icon-active: var(--neutral-1); - --interactive-icon-default: var(--neutral-2); - --interactive-icon-hover: var(--neutral-1); - --interactive-muted: var(--neutral-27); - --interactive-text-active: var(--neutral-1); - --interactive-text-default: var(--neutral-2); - --interactive-text-hover: var(--neutral-1); - --mention-background: hsl(var(--opacity-blurple-48-hsl)/0.47843137254901963); - --mention-foreground: var(--blurple-1); - --message-automod-background-default: hsl(var(--opacity-red-20-hsl)/0.2); - --message-automod-background-hover: hsl(var(--opacity-red-24-hsl)/0.23921568627450981); - --message-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --message-highlight-background-default: hsl(var(--opacity-blurple-20-hsl)/0.2); - --message-highlight-background-hover: hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981); - --message-mentioned-background-default: hsl(var(--opacity-yellow-20-hsl)/0.2); - --message-mentioned-background-hover: hsl(var(--opacity-yellow-24-hsl)/0.23921568627450981); - --mobile-text-heading-primary: var(--neutral-1); - --progressbar-track-background: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --radio-border-active: var(--neutral-12); - --radio-border-default: var(--neutral-12); - --radio-border-hover: var(--neutral-12); - --radio-border-selected-default: var(--neutral-12); - --radio-border-selected-hover: var(--neutral-12); - --redesign-button-tertiary-text: var(--neutral-1); - --redesign-channel-name-muted-text: var(--neutral-12); - --redesign-channel-name-text: var(--neutral-1); - --spine-default: var(--neutral-32); - --stage-card-pill-bg: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --status-danger: var(--red-new-29); - --switch-border-default: var(--neutral-12); - --switch-border-hover: var(--neutral-12); - --switch-border-selected-default: var(--neutral-12); - --switch-border-selected-hover: var(--neutral-12); - --text-brand: var(--brand-300); - --text-code: var(--blue-new-1); - --text-code-addition: var(--green-new-2); - --text-code-builtin: var(--orange-new-1); - --text-code-bullet: var(--yellow-new-1); - --text-code-comment: var(--neutral-15); - --text-code-deletion: var(--red-new-1); - --text-code-keyword: var(--red-new-1); - --text-code-section: var(--blue-new-1); - --text-code-string: var(--teal-new-3); - --text-code-tag: var(--green-new-3); - --text-code-title: var(--blue-new-1); - --text-code-variable: var(--blue-new-1); - --text-default: var(--neutral-1); - --text-feedback-critical: var(--red-new-5); - --text-feedback-info: var(--blue-new-8); - --text-feedback-positive: var(--green-new-10); - --text-feedback-warning: var(--yellow-new-7); - --text-link: var(--blue-new-18); - --text-muted: var(--neutral-12); - --text-strong: var(--neutral-1); - --text-subtle: var(--neutral-2); - --text-voice-disconnected: var(--red-new-5); - --thread-channel-spine: var(--neutral-27); - --user-profile-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --user-profile-border: var(--neutral-12); - --user-profile-overlay-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --user-profile-toolbar-border: var(--neutral-26) -} - -@supports (color: color-mix(in lch,red,blue)) { - .high-contrast-mode .theme-dark,.high-contrast-mode.theme-dark { - --app-frame-border:color-mix(in oklab,var(--neutral-12) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --background-code: color-mix(in oklab,hsl(var(--opacity-blurple-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-critical: color-mix(in oklab,hsl(var(--opacity-red-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-info: color-mix(in oklab,hsl(var(--opacity-blue-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-positive: color-mix(in oklab,hsl(var(--opacity-green-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-warning: color-mix(in oklab,hsl(var(--opacity-yellow-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --border-focus: color-mix(in oklab,var(--blue-new-18) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-muted: color-mix(in oklab,var(--neutral-26) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-normal: color-mix(in oklab,var(--neutral-5) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-strong: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-subtle: color-mix(in oklab,var(--neutral-12) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --card-secondary-bg: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --channel-icon: color-mix(in oklab,var(--neutral-19) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channel-text-area-placeholder: color-mix(in oklab,var(--neutral-25) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channels-default: color-mix(in oklab,var(--neutral-19) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --chat-text-muted: color-mix(in oklab,var(--neutral-17) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --control-secondary-border-active: color-mix(in oklab,var(--neutral-22) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --control-secondary-border-default: color-mix(in oklab,var(--neutral-22) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --gradient-progress-pill-background: color-mix(in oklab,var(--neutral-35) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --icon-default: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-critical: color-mix(in oklab,var(--red-new-5) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-info: color-mix(in oklab,var(--blue-new-8) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-positive: color-mix(in oklab,var(--green-new-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-warning: color-mix(in oklab,var(--yellow-new-7) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-link: color-mix(in oklab,var(--blue-new-18) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-muted: color-mix(in oklab,var(--neutral-12) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-strong: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-subtle: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-voice-disconnected: color-mix(in oklab,var(--red-new-5) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-background-default: color-mix(in oklab,hsl(var(--opacity-black-28-hsl)/0.2784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2784313725490196) var(--custom-theme-base-color-amount,0%)); - --input-border-default: color-mix(in oklab,var(--neutral-22) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-error-default: color-mix(in oklab,var(--red-new-5) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-hover: color-mix(in oklab,var(--neutral-5) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-icon-default: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-placeholder-text-default: color-mix(in oklab,var(--neutral-28) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-default: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-error-default: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-background-active: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --interactive-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --interactive-background-selected: color-mix(in oklab,hsl(var(--opacity-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --interactive-icon-active: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-default: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-hover: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-muted: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-active: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-default: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-hover: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --mention-background: color-mix(in oklab,hsl(var(--opacity-blurple-48-hsl)/0.47843137254901963) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.47843137254901963) var(--custom-theme-base-color-amount,0%)); - --mention-foreground: color-mix(in oklab,var(--blurple-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --message-automod-background-default: color-mix(in oklab,hsl(var(--opacity-red-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --message-automod-background-hover: color-mix(in oklab,hsl(var(--opacity-red-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --message-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-default: color-mix(in oklab,hsl(var(--opacity-blurple-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-hover: color-mix(in oklab,hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-default: color-mix(in oklab,hsl(var(--opacity-yellow-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-hover: color-mix(in oklab,hsl(var(--opacity-yellow-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --mobile-text-heading-primary: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --progressbar-track-background: color-mix(in oklab,hsl(var(--opacity-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-text: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --redesign-channel-name-muted-text: color-mix(in oklab,var(--neutral-12) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --redesign-channel-name-text: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --spine-default: color-mix(in oklab,var(--neutral-32) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --stage-card-pill-bg: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --text-brand: color-mix(in oklab,var(--brand-300) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code: color-mix(in oklab,var(--blue-new-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-addition: color-mix(in oklab,var(--green-new-2) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-builtin: color-mix(in oklab,var(--orange-new-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-bullet: color-mix(in oklab,var(--yellow-new-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-comment: color-mix(in oklab,var(--neutral-15) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-deletion: color-mix(in oklab,var(--red-new-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-keyword: color-mix(in oklab,var(--red-new-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-section: color-mix(in oklab,var(--blue-new-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-string: color-mix(in oklab,var(--teal-new-3) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-tag: color-mix(in oklab,var(--green-new-3) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-title: color-mix(in oklab,var(--blue-new-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-variable: color-mix(in oklab,var(--blue-new-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-default: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-critical: color-mix(in oklab,var(--red-new-5) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-info: color-mix(in oklab,var(--blue-new-8) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-positive: color-mix(in oklab,var(--green-new-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-warning: color-mix(in oklab,var(--yellow-new-7) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-link: color-mix(in oklab,var(--blue-new-18) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-muted: color-mix(in oklab,var(--neutral-12) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-strong: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-subtle: color-mix(in oklab,var(--neutral-2) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-disconnected: color-mix(in oklab,var(--red-new-5) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --thread-channel-spine: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --user-profile-border: color-mix(in oklab,var(--neutral-12) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-overlay-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --user-profile-toolbar-border: color-mix(in oklab,var(--neutral-26) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))) - } -} - -.high-contrast-mode .theme-light,.high-contrast-mode.theme-light { - --app-frame-border: var(--neutral-50); - --background-code: hsl(var(--opacity-blurple-8-hsl)/0.0784313725490196); - --background-feedback-critical: hsl(var(--opacity-red-12-hsl)/0.12156862745098039); - --background-feedback-info: hsl(var(--opacity-blue-12-hsl)/0.12156862745098039); - --background-feedback-positive: hsl(var(--opacity-green-12-hsl)/0.12156862745098039); - --background-feedback-warning: hsl(var(--opacity-yellow-12-hsl)/0.12156862745098039); - --background-mod-muted: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --background-mod-normal: hsl(var(--opacity-20-hsl)/0.2); - --background-mod-strong: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --background-mod-subtle: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --badge-background-default: hsl(var(--opacity-20-hsl)/0.2); - --badge-text-default: var(--neutral-100); - --border-focus: var(--blue-new-63); - --border-muted: var(--neutral-38); - --border-normal: var(--neutral-59); - --border-strong: var(--neutral-72); - --border-subtle: var(--neutral-50); - --card-secondary-bg: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --channel-icon: var(--neutral-50); - --channel-text-area-placeholder: var(--neutral-39); - --channels-default: var(--neutral-50); - --chat-text-muted: var(--neutral-48); - --checkbox-border-active: var(--neutral-50); - --checkbox-border-default: var(--neutral-72); - --checkbox-border-hover: var(--neutral-50); - --checkbox-border-selected-default: var(--neutral-50); - --checkbox-border-selected-hover: var(--neutral-50); - --control-connected-border-active: var(--green-new-60); - --control-connected-border-default: var(--green-new-60); - --control-connected-border-hover: var(--green-new-60); - --control-critical-primary-border-active: var(--red-new-58); - --control-critical-primary-border-default: var(--red-new-58); - --control-critical-primary-border-hover: var(--red-new-58); - --control-critical-secondary-background-active: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --control-critical-secondary-background-default: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-critical-secondary-background-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-critical-secondary-border-active: var(--red-new-58); - --control-critical-secondary-border-default: var(--red-new-58); - --control-critical-secondary-border-hover: var(--red-new-58); - --control-critical-secondary-icon-active: var(--red-new-63); - --control-critical-secondary-icon-default: var(--red-new-63); - --control-critical-secondary-icon-hover: var(--red-new-63); - --control-critical-secondary-text-active: var(--red-new-63); - --control-critical-secondary-text-default: var(--red-new-63); - --control-critical-secondary-text-hover: var(--red-new-63); - --control-icon-only-background-active: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --control-icon-only-background-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-icon-only-border-active: var(--neutral-38); - --control-icon-only-border-hover: var(--neutral-38); - --control-icon-only-icon-active: var(--neutral-100); - --control-icon-only-icon-default: var(--neutral-64); - --control-icon-only-icon-hover: var(--neutral-100); - --control-overlay-secondary-border-active: var(--neutral-38); - --control-overlay-secondary-border-default: var(--neutral-38); - --control-overlay-secondary-border-hover: var(--neutral-38); - --control-primary-border-active: var(--blurple-59); - --control-primary-border-default: var(--blurple-59); - --control-primary-border-hover: var(--blurple-59); - --control-secondary-background-active: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --control-secondary-background-default: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-secondary-background-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-secondary-border-active: var(--neutral-44); - --control-secondary-border-default: var(--neutral-44); - --control-secondary-border-hover: var(--neutral-44); - --control-secondary-icon-active: var(--neutral-100); - --control-secondary-icon-default: var(--neutral-100); - --control-secondary-icon-hover: var(--neutral-100); - --control-secondary-text-active: var(--neutral-100); - --control-secondary-text-default: var(--neutral-100); - --control-secondary-text-hover: var(--neutral-100); - --gradient-progress-pill-background: var(--neutral-31); - --icon-default: var(--neutral-76); - --icon-feedback-critical: var(--red-new-63); - --icon-feedback-info: var(--blue-new-65); - --icon-feedback-positive: var(--green-new-65); - --icon-feedback-warning: var(--yellow-new-76); - --icon-link: var(--blue-new-63); - --icon-muted: var(--neutral-50); - --icon-strong: var(--neutral-100); - --icon-subtle: var(--neutral-64); - --icon-voice-disconnected: var(--red-new-63); - --input-background-default: hsl(var(--opacity-black-12-hsl)/0.12156862745098039); - --input-border-default: var(--neutral-44); - --input-border-error-default: var(--red-new-63); - --input-border-hover: var(--neutral-59); - --input-icon-default: var(--neutral-64); - --input-placeholder-text-default: var(--neutral-44); - --input-text-default: var(--neutral-76); - --input-text-error-default: var(--neutral-76); - --interactive-background-active: hsl(var(--opacity-20-hsl)/0.2); - --interactive-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --interactive-background-selected: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --interactive-icon-active: var(--neutral-100); - --interactive-icon-default: var(--neutral-64); - --interactive-icon-hover: var(--neutral-100); - --interactive-muted: var(--neutral-40); - --interactive-text-active: var(--neutral-100); - --interactive-text-default: var(--neutral-64); - --interactive-text-hover: var(--neutral-100); - --mention-background: hsl(var(--opacity-blurple-28-hsl)/0.2784313725490196); - --mention-foreground: var(--blurple-82); - --message-automod-background-default: hsl(var(--opacity-red-12-hsl)/0.12156862745098039); - --message-automod-background-hover: hsl(var(--opacity-red-16-hsl)/0.1607843137254902); - --message-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --message-highlight-background-default: hsl(var(--opacity-blurple-20-hsl)/0.2); - --message-highlight-background-hover: hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981); - --message-mentioned-background-default: hsl(var(--opacity-yellow-12-hsl)/0.12156862745098039); - --message-mentioned-background-hover: hsl(var(--opacity-yellow-16-hsl)/0.1607843137254902); - --mobile-text-heading-primary: var(--neutral-100); - --progressbar-track-background: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --radio-border-active: var(--neutral-50); - --radio-border-default: var(--neutral-50); - --radio-border-hover: var(--neutral-50); - --radio-border-selected-default: var(--neutral-50); - --radio-border-selected-hover: var(--neutral-50); - --redesign-button-tertiary-text: var(--neutral-76); - --redesign-channel-name-muted-text: var(--neutral-50); - --redesign-channel-name-text: var(--neutral-100); - --spine-default: var(--neutral-32); - --stage-card-pill-bg: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --status-danger: var(--red-new-48); - --switch-border-default: var(--neutral-50); - --switch-border-hover: var(--neutral-50); - --switch-border-selected-default: var(--neutral-50); - --switch-border-selected-hover: var(--neutral-50); - --text-brand: var(--brand-600); - --text-code: var(--blue-new-74); - --text-code-addition: var(--green-new-79); - --text-code-builtin: var(--orange-new-74); - --text-code-bullet: var(--yellow-new-83); - --text-code-comment: var(--neutral-49); - --text-code-deletion: var(--red-new-77); - --text-code-keyword: var(--red-new-73); - --text-code-section: var(--blue-new-74); - --text-code-string: var(--teal-new-76); - --text-code-tag: var(--green-new-76); - --text-code-title: var(--blue-new-74); - --text-code-variable: var(--blue-new-74); - --text-default: var(--neutral-76); - --text-feedback-critical: var(--red-new-63); - --text-feedback-info: var(--blue-new-65); - --text-feedback-positive: var(--green-new-65); - --text-feedback-warning: var(--yellow-new-76); - --text-link: var(--blue-new-63); - --text-muted: var(--neutral-50); - --text-strong: var(--neutral-100); - --text-subtle: var(--neutral-64); - --text-voice-disconnected: var(--red-new-63); - --thread-channel-spine: var(--neutral-40); - --user-profile-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --user-profile-border: var(--neutral-50); - --user-profile-overlay-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --user-profile-toolbar-border: var(--neutral-38) -} - -@supports (color: color-mix(in lch,red,blue)) { - .high-contrast-mode .theme-light,.high-contrast-mode.theme-light { - --app-frame-border:color-mix(in oklab,var(--neutral-50) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --background-code: color-mix(in oklab,hsl(var(--opacity-blurple-8-hsl)/0.0784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.0784313725490196) var(--custom-theme-base-color-amount,0%)); - --background-feedback-critical: color-mix(in oklab,hsl(var(--opacity-red-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-info: color-mix(in oklab,hsl(var(--opacity-blue-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-positive: color-mix(in oklab,hsl(var(--opacity-green-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-warning: color-mix(in oklab,hsl(var(--opacity-yellow-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --border-focus: color-mix(in oklab,var(--blue-new-63) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-muted: color-mix(in oklab,var(--neutral-38) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-normal: color-mix(in oklab,var(--neutral-59) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-strong: color-mix(in oklab,var(--neutral-72) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-subtle: color-mix(in oklab,var(--neutral-50) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --card-secondary-bg: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --channel-icon: color-mix(in oklab,var(--neutral-50) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channel-text-area-placeholder: color-mix(in oklab,var(--neutral-39) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channels-default: color-mix(in oklab,var(--neutral-50) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --chat-text-muted: color-mix(in oklab,var(--neutral-48) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --control-secondary-border-active: color-mix(in oklab,var(--neutral-44) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --control-secondary-border-default: color-mix(in oklab,var(--neutral-44) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --gradient-progress-pill-background: color-mix(in oklab,var(--neutral-31) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --icon-default: color-mix(in oklab,var(--neutral-76) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-critical: color-mix(in oklab,var(--red-new-63) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-info: color-mix(in oklab,var(--blue-new-65) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-positive: color-mix(in oklab,var(--green-new-65) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-warning: color-mix(in oklab,var(--yellow-new-76) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-link: color-mix(in oklab,var(--blue-new-63) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-muted: color-mix(in oklab,var(--neutral-50) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-strong: color-mix(in oklab,var(--neutral-100) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-subtle: color-mix(in oklab,var(--neutral-64) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-voice-disconnected: color-mix(in oklab,var(--red-new-63) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-background-default: color-mix(in oklab,hsl(var(--opacity-black-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --input-border-default: color-mix(in oklab,var(--neutral-44) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-error-default: color-mix(in oklab,var(--red-new-63) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-hover: color-mix(in oklab,var(--neutral-59) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-icon-default: color-mix(in oklab,var(--neutral-64) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-placeholder-text-default: color-mix(in oklab,var(--neutral-44) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-default: color-mix(in oklab,var(--neutral-76) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-error-default: color-mix(in oklab,var(--neutral-76) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-background-active: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --interactive-background-hover: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --interactive-background-selected: color-mix(in oklab,hsl(var(--opacity-28-hsl)/0.2784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2784313725490196) var(--custom-theme-base-color-amount,0%)); - --interactive-icon-active: color-mix(in oklab,var(--neutral-100) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-default: color-mix(in oklab,var(--neutral-64) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-hover: color-mix(in oklab,var(--neutral-100) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-muted: color-mix(in oklab,var(--neutral-40) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-active: color-mix(in oklab,var(--neutral-100) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-default: color-mix(in oklab,var(--neutral-64) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-hover: color-mix(in oklab,var(--neutral-100) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --mention-background: color-mix(in oklab,hsl(var(--opacity-blurple-28-hsl)/0.2784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2784313725490196) var(--custom-theme-base-color-amount,0%)); - --mention-foreground: color-mix(in oklab,var(--blurple-82) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --message-automod-background-default: color-mix(in oklab,hsl(var(--opacity-red-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --message-automod-background-hover: color-mix(in oklab,hsl(var(--opacity-red-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --message-background-hover: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-default: color-mix(in oklab,hsl(var(--opacity-blurple-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-hover: color-mix(in oklab,hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-default: color-mix(in oklab,hsl(var(--opacity-yellow-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-hover: color-mix(in oklab,hsl(var(--opacity-yellow-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --mobile-text-heading-primary: color-mix(in oklab,var(--neutral-100) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --progressbar-track-background: color-mix(in oklab,hsl(var(--opacity-28-hsl)/0.2784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2784313725490196) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-text: color-mix(in oklab,var(--neutral-76) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --redesign-channel-name-muted-text: color-mix(in oklab,var(--neutral-50) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --redesign-channel-name-text: color-mix(in oklab,var(--neutral-100) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --spine-default: color-mix(in oklab,var(--neutral-32) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --stage-card-pill-bg: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --text-brand: color-mix(in oklab,var(--brand-600) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code: color-mix(in oklab,var(--blue-new-74) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-addition: color-mix(in oklab,var(--green-new-79) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-builtin: color-mix(in oklab,var(--orange-new-74) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-bullet: color-mix(in oklab,var(--yellow-new-83) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-comment: color-mix(in oklab,var(--neutral-49) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-deletion: color-mix(in oklab,var(--red-new-77) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-keyword: color-mix(in oklab,var(--red-new-73) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-section: color-mix(in oklab,var(--blue-new-74) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-string: color-mix(in oklab,var(--teal-new-76) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-tag: color-mix(in oklab,var(--green-new-76) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-title: color-mix(in oklab,var(--blue-new-74) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-variable: color-mix(in oklab,var(--blue-new-74) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-default: color-mix(in oklab,var(--neutral-76) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-critical: color-mix(in oklab,var(--red-new-63) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-info: color-mix(in oklab,var(--blue-new-65) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-positive: color-mix(in oklab,var(--green-new-65) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-warning: color-mix(in oklab,var(--yellow-new-76) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-link: color-mix(in oklab,var(--blue-new-63) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-muted: color-mix(in oklab,var(--neutral-50) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-strong: color-mix(in oklab,var(--neutral-100) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-subtle: color-mix(in oklab,var(--neutral-64) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-disconnected: color-mix(in oklab,var(--red-new-63) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --thread-channel-spine: color-mix(in oklab,var(--neutral-40) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-background-hover: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --user-profile-border: color-mix(in oklab,var(--neutral-50) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-overlay-background-hover: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --user-profile-toolbar-border: color-mix(in oklab,var(--neutral-38) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))) - } -} - -.high-contrast-mode .theme-midnight,.high-contrast-mode.theme-midnight { - --app-frame-border: var(--neutral-24); - --background-code: hsl(var(--opacity-blurple-12-hsl)/0.12156862745098039); - --background-feedback-critical: hsl(var(--opacity-red-12-hsl)/0.12156862745098039); - --background-feedback-info: hsl(var(--opacity-blue-12-hsl)/0.12156862745098039); - --background-feedback-positive: hsl(var(--opacity-green-12-hsl)/0.12156862745098039); - --background-feedback-warning: hsl(var(--opacity-yellow-12-hsl)/0.12156862745098039); - --background-mod-muted: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --background-mod-normal: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --background-mod-strong: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --background-mod-subtle: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --badge-background-default: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --badge-text-default: var(--neutral-1); - --border-focus: var(--blue-new-35); - --border-muted: var(--neutral-36); - --border-normal: var(--neutral-18); - --border-strong: var(--neutral-10); - --border-subtle: var(--neutral-24); - --card-secondary-bg: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --channel-icon: var(--neutral-29); - --channel-text-area-placeholder: var(--neutral-36); - --channels-default: var(--neutral-29); - --chat-text-muted: var(--neutral-28); - --checkbox-border-active: var(--neutral-24); - --checkbox-border-default: var(--neutral-10); - --checkbox-border-hover: var(--neutral-24); - --checkbox-border-selected-default: var(--neutral-24); - --checkbox-border-selected-hover: var(--neutral-24); - --control-connected-border-active: var(--green-new-26); - --control-connected-border-default: var(--green-new-26); - --control-connected-border-hover: var(--green-new-26); - --control-critical-primary-border-active: var(--red-new-23); - --control-critical-primary-border-default: var(--red-new-23); - --control-critical-primary-border-hover: var(--red-new-23); - --control-critical-secondary-background-active: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --control-critical-secondary-background-default: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-critical-secondary-background-hover: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --control-critical-secondary-border-active: var(--red-new-23); - --control-critical-secondary-border-default: var(--red-new-23); - --control-critical-secondary-border-hover: var(--red-new-23); - --control-critical-secondary-icon-active: var(--red-new-23); - --control-critical-secondary-icon-default: var(--red-new-23); - --control-critical-secondary-icon-hover: var(--red-new-23); - --control-critical-secondary-text-active: var(--red-new-23); - --control-critical-secondary-text-default: var(--red-new-23); - --control-critical-secondary-text-hover: var(--red-new-23); - --control-icon-only-background-active: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --control-icon-only-background-hover: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --control-icon-only-border-active: var(--neutral-36); - --control-icon-only-border-hover: var(--neutral-36); - --control-icon-only-icon-active: var(--neutral-1); - --control-icon-only-icon-default: var(--neutral-15); - --control-icon-only-icon-hover: var(--neutral-1); - --control-overlay-secondary-border-active: var(--neutral-36); - --control-overlay-secondary-border-default: var(--neutral-36); - --control-overlay-secondary-border-hover: var(--neutral-36); - --control-primary-border-active: var(--blurple-25); - --control-primary-border-default: var(--blurple-25); - --control-primary-border-hover: var(--blurple-25); - --control-secondary-background-active: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --control-secondary-background-default: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --control-secondary-background-hover: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --control-secondary-border-active: var(--neutral-32); - --control-secondary-border-default: var(--neutral-32); - --control-secondary-border-hover: var(--neutral-32); - --control-secondary-icon-active: var(--neutral-1); - --control-secondary-icon-default: var(--neutral-1); - --control-secondary-icon-hover: var(--neutral-1); - --control-secondary-text-active: var(--neutral-1); - --control-secondary-text-default: var(--neutral-1); - --control-secondary-text-hover: var(--neutral-1); - --gradient-progress-pill-background: var(--neutral-45); - --icon-default: var(--neutral-6); - --icon-feedback-critical: var(--red-new-23); - --icon-feedback-info: var(--blue-new-25); - --icon-feedback-positive: var(--green-new-26); - --icon-feedback-warning: var(--yellow-new-49); - --icon-link: var(--blue-new-28); - --icon-muted: var(--neutral-24); - --icon-strong: var(--neutral-1); - --icon-subtle: var(--neutral-15); - --icon-voice-disconnected: var(--red-new-23); - --input-background-default: hsl(var(--opacity-black-12-hsl)/0.12156862745098039); - --input-border-default: var(--neutral-32); - --input-border-error-default: var(--red-new-23); - --input-border-hover: var(--neutral-18); - --input-icon-default: var(--neutral-15); - --input-placeholder-text-default: var(--neutral-37); - --input-text-default: var(--neutral-6); - --input-text-error-default: var(--neutral-6); - --interactive-background-active: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --interactive-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --interactive-background-selected: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --interactive-icon-active: var(--neutral-1); - --interactive-icon-default: var(--neutral-15); - --interactive-icon-hover: var(--neutral-1); - --interactive-muted: var(--neutral-38); - --interactive-text-active: var(--neutral-1); - --interactive-text-default: var(--neutral-15); - --interactive-text-hover: var(--neutral-1); - --mention-background: hsl(var(--opacity-blurple-48-hsl)/0.47843137254901963); - --mention-foreground: var(--blurple-13); - --message-automod-background-default: hsl(var(--opacity-red-24-hsl)/0.23921568627450981); - --message-automod-background-hover: hsl(var(--opacity-red-28-hsl)/0.2784313725490196); - --message-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --message-highlight-background-default: hsl(var(--opacity-blurple-20-hsl)/0.2); - --message-highlight-background-hover: hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981); - --message-mentioned-background-default: hsl(var(--opacity-yellow-24-hsl)/0.23921568627450981); - --message-mentioned-background-hover: hsl(var(--opacity-yellow-28-hsl)/0.2784313725490196); - --mobile-text-heading-primary: var(--neutral-1); - --progressbar-track-background: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --radio-border-active: var(--neutral-24); - --radio-border-default: var(--neutral-24); - --radio-border-hover: var(--neutral-24); - --radio-border-selected-default: var(--neutral-24); - --radio-border-selected-hover: var(--neutral-24); - --redesign-button-tertiary-text: var(--neutral-6); - --redesign-channel-name-muted-text: var(--neutral-24); - --redesign-channel-name-text: var(--neutral-1); - --spine-default: var(--neutral-42); - --stage-card-pill-bg: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --status-danger: var(--red-new-45); - --switch-border-default: var(--neutral-24); - --switch-border-hover: var(--neutral-24); - --switch-border-selected-default: var(--neutral-24); - --switch-border-selected-hover: var(--neutral-24); - --text-brand: var(--brand-345); - --text-code: var(--blue-new-20); - --text-code-addition: var(--green-new-20); - --text-code-builtin: var(--orange-new-19); - --text-code-bullet: var(--yellow-new-44); - --text-code-comment: var(--neutral-27); - --text-code-deletion: var(--red-new-16); - --text-code-keyword: var(--red-new-17); - --text-code-section: var(--blue-new-20); - --text-code-string: var(--teal-new-22); - --text-code-tag: var(--green-new-21); - --text-code-title: var(--blue-new-20); - --text-code-variable: var(--blue-new-20); - --text-default: var(--neutral-6); - --text-feedback-critical: var(--red-new-23); - --text-feedback-info: var(--blue-new-25); - --text-feedback-positive: var(--green-new-26); - --text-feedback-warning: var(--yellow-new-49); - --text-link: var(--blue-new-28); - --text-muted: var(--neutral-24); - --text-strong: var(--neutral-1); - --text-subtle: var(--neutral-15); - --text-voice-disconnected: var(--red-new-23); - --thread-channel-spine: var(--neutral-38); - --user-profile-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --user-profile-border: var(--neutral-24); - --user-profile-overlay-background-hover: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --user-profile-toolbar-border: var(--neutral-36) -} - -@supports (color: color-mix(in lch,red,blue)) { - .high-contrast-mode .theme-midnight,.high-contrast-mode.theme-midnight { - --app-frame-border:color-mix(in oklab,var(--neutral-24) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --background-code: color-mix(in oklab,hsl(var(--opacity-blurple-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-critical: color-mix(in oklab,hsl(var(--opacity-red-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-info: color-mix(in oklab,hsl(var(--opacity-blue-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-positive: color-mix(in oklab,hsl(var(--opacity-green-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-warning: color-mix(in oklab,hsl(var(--opacity-yellow-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --border-focus: color-mix(in oklab,var(--blue-new-35) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-muted: color-mix(in oklab,var(--neutral-36) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-normal: color-mix(in oklab,var(--neutral-18) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-strong: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-subtle: color-mix(in oklab,var(--neutral-24) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --card-secondary-bg: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --channel-icon: color-mix(in oklab,var(--neutral-29) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channel-text-area-placeholder: color-mix(in oklab,var(--neutral-36) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channels-default: color-mix(in oklab,var(--neutral-29) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --chat-text-muted: color-mix(in oklab,var(--neutral-28) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --control-secondary-border-active: color-mix(in oklab,var(--neutral-32) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --control-secondary-border-default: color-mix(in oklab,var(--neutral-32) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --gradient-progress-pill-background: color-mix(in oklab,var(--neutral-45) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --icon-default: color-mix(in oklab,var(--neutral-6) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-critical: color-mix(in oklab,var(--red-new-23) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-info: color-mix(in oklab,var(--blue-new-25) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-positive: color-mix(in oklab,var(--green-new-26) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-warning: color-mix(in oklab,var(--yellow-new-49) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-link: color-mix(in oklab,var(--blue-new-28) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-muted: color-mix(in oklab,var(--neutral-24) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-strong: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-subtle: color-mix(in oklab,var(--neutral-15) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-voice-disconnected: color-mix(in oklab,var(--red-new-23) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-background-default: color-mix(in oklab,hsl(var(--opacity-black-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --input-border-default: color-mix(in oklab,var(--neutral-32) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-error-default: color-mix(in oklab,var(--red-new-23) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-hover: color-mix(in oklab,var(--neutral-18) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-icon-default: color-mix(in oklab,var(--neutral-15) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-placeholder-text-default: color-mix(in oklab,var(--neutral-37) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-default: color-mix(in oklab,var(--neutral-6) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-error-default: color-mix(in oklab,var(--neutral-6) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-background-active: color-mix(in oklab,hsl(var(--opacity-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --interactive-background-hover: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --interactive-background-selected: color-mix(in oklab,hsl(var(--opacity-28-hsl)/0.2784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2784313725490196) var(--custom-theme-base-color-amount,0%)); - --interactive-icon-active: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-default: color-mix(in oklab,var(--neutral-15) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-hover: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-muted: color-mix(in oklab,var(--neutral-38) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-active: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-default: color-mix(in oklab,var(--neutral-15) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-hover: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --mention-background: color-mix(in oklab,hsl(var(--opacity-blurple-48-hsl)/0.47843137254901963) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.47843137254901963) var(--custom-theme-base-color-amount,0%)); - --mention-foreground: color-mix(in oklab,var(--blurple-13) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --message-automod-background-default: color-mix(in oklab,hsl(var(--opacity-red-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --message-automod-background-hover: color-mix(in oklab,hsl(var(--opacity-red-28-hsl)/0.2784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2784313725490196) var(--custom-theme-base-color-amount,0%)); - --message-background-hover: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-default: color-mix(in oklab,hsl(var(--opacity-blurple-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-hover: color-mix(in oklab,hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-default: color-mix(in oklab,hsl(var(--opacity-yellow-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-hover: color-mix(in oklab,hsl(var(--opacity-yellow-28-hsl)/0.2784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2784313725490196) var(--custom-theme-base-color-amount,0%)); - --mobile-text-heading-primary: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --progressbar-track-background: color-mix(in oklab,hsl(var(--opacity-28-hsl)/0.2784313725490196) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2784313725490196) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-text: color-mix(in oklab,var(--neutral-6) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --redesign-channel-name-muted-text: color-mix(in oklab,var(--neutral-24) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --redesign-channel-name-text: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --spine-default: color-mix(in oklab,var(--neutral-42) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --stage-card-pill-bg: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --text-brand: color-mix(in oklab,var(--brand-345) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code: color-mix(in oklab,var(--blue-new-20) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-addition: color-mix(in oklab,var(--green-new-20) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-builtin: color-mix(in oklab,var(--orange-new-19) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-bullet: color-mix(in oklab,var(--yellow-new-44) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-comment: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-deletion: color-mix(in oklab,var(--red-new-16) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-keyword: color-mix(in oklab,var(--red-new-17) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-section: color-mix(in oklab,var(--blue-new-20) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-string: color-mix(in oklab,var(--teal-new-22) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-tag: color-mix(in oklab,var(--green-new-21) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-title: color-mix(in oklab,var(--blue-new-20) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-variable: color-mix(in oklab,var(--blue-new-20) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-default: color-mix(in oklab,var(--neutral-6) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-critical: color-mix(in oklab,var(--red-new-23) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-info: color-mix(in oklab,var(--blue-new-25) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-positive: color-mix(in oklab,var(--green-new-26) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-warning: color-mix(in oklab,var(--yellow-new-49) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-link: color-mix(in oklab,var(--blue-new-28) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-muted: color-mix(in oklab,var(--neutral-24) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-strong: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-subtle: color-mix(in oklab,var(--neutral-15) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-disconnected: color-mix(in oklab,var(--red-new-23) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --thread-channel-spine: color-mix(in oklab,var(--neutral-38) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-background-hover: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --user-profile-border: color-mix(in oklab,var(--neutral-24) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-overlay-background-hover: color-mix(in oklab,hsl(var(--opacity-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --user-profile-toolbar-border: color-mix(in oklab,var(--neutral-36) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))) - } -} - -.high-contrast-mode .theme-darker,.high-contrast-mode.theme-darker { - --app-frame-border: var(--neutral-19); - --background-code: hsl(var(--opacity-blurple-12-hsl)/0.12156862745098039); - --background-feedback-critical: hsl(var(--opacity-red-12-hsl)/0.12156862745098039); - --background-feedback-info: hsl(var(--opacity-blue-12-hsl)/0.12156862745098039); - --background-feedback-positive: hsl(var(--opacity-green-12-hsl)/0.12156862745098039); - --background-feedback-warning: hsl(var(--opacity-yellow-12-hsl)/0.12156862745098039); - --background-mod-muted: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --background-mod-normal: hsl(var(--opacity-20-hsl)/0.2); - --background-mod-strong: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --background-mod-subtle: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --badge-background-default: hsl(var(--opacity-20-hsl)/0.2); - --badge-text-default: var(--neutral-1); - --border-focus: var(--blue-new-30); - --border-muted: var(--neutral-32); - --border-normal: var(--neutral-13); - --border-strong: var(--neutral-5); - --border-subtle: var(--neutral-19); - --card-secondary-bg: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --channel-icon: var(--neutral-27); - --channel-text-area-placeholder: var(--neutral-33); - --channels-default: var(--neutral-27); - --chat-text-muted: var(--neutral-25); - --checkbox-border-active: var(--neutral-19); - --checkbox-border-default: var(--neutral-5); - --checkbox-border-hover: var(--neutral-19); - --checkbox-border-selected-default: var(--neutral-19); - --checkbox-border-selected-hover: var(--neutral-19); - --control-connected-border-active: var(--green-new-23); - --control-connected-border-default: var(--green-new-23); - --control-connected-border-hover: var(--green-new-23); - --control-critical-primary-border-active: var(--red-new-19); - --control-critical-primary-border-default: var(--red-new-19); - --control-critical-primary-border-hover: var(--red-new-19); - --control-critical-secondary-background-active: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --control-critical-secondary-background-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-critical-secondary-background-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-critical-secondary-border-active: var(--red-new-19); - --control-critical-secondary-border-default: var(--red-new-19); - --control-critical-secondary-border-hover: var(--red-new-19); - --control-critical-secondary-icon-active: var(--red-new-16); - --control-critical-secondary-icon-default: var(--red-new-16); - --control-critical-secondary-icon-hover: var(--red-new-16); - --control-critical-secondary-text-active: var(--red-new-16); - --control-critical-secondary-text-default: var(--red-new-16); - --control-critical-secondary-text-hover: var(--red-new-16); - --control-icon-only-background-active: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --control-icon-only-background-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-icon-only-border-active: var(--neutral-32); - --control-icon-only-border-hover: var(--neutral-32); - --control-icon-only-icon-active: var(--neutral-1); - --control-icon-only-icon-default: var(--neutral-10); - --control-icon-only-icon-hover: var(--neutral-1); - --control-overlay-secondary-border-active: var(--neutral-32); - --control-overlay-secondary-border-default: var(--neutral-32); - --control-overlay-secondary-border-hover: var(--neutral-32); - --control-primary-border-active: var(--blurple-21); - --control-primary-border-default: var(--blurple-21); - --control-primary-border-hover: var(--blurple-21); - --control-secondary-background-active: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --control-secondary-background-default: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --control-secondary-background-hover: hsl(var(--opacity-20-hsl)/0.2); - --control-secondary-border-active: var(--neutral-29); - --control-secondary-border-default: var(--neutral-29); - --control-secondary-border-hover: var(--neutral-29); - --control-secondary-icon-active: var(--neutral-1); - --control-secondary-icon-default: var(--neutral-1); - --control-secondary-icon-hover: var(--neutral-1); - --control-secondary-text-active: var(--neutral-1); - --control-secondary-text-default: var(--neutral-1); - --control-secondary-text-hover: var(--neutral-1); - --gradient-progress-pill-background: var(--neutral-42); - --icon-default: var(--neutral-1); - --icon-feedback-critical: var(--red-new-16); - --icon-feedback-info: var(--blue-new-19); - --icon-feedback-positive: var(--green-new-20); - --icon-feedback-warning: var(--yellow-new-33); - --icon-link: var(--blue-new-21); - --icon-muted: var(--neutral-19); - --icon-strong: var(--neutral-1); - --icon-subtle: var(--neutral-10); - --icon-voice-disconnected: var(--red-new-16); - --input-background-default: hsl(var(--opacity-black-16-hsl)/0.1607843137254902); - --input-border-default: var(--neutral-29); - --input-border-error-default: var(--red-new-16); - --input-border-hover: var(--neutral-13); - --input-icon-default: var(--neutral-10); - --input-placeholder-text-default: var(--neutral-35); - --input-text-default: var(--neutral-1); - --input-text-error-default: var(--neutral-1); - --interactive-background-active: hsl(var(--opacity-20-hsl)/0.2); - --interactive-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --interactive-background-selected: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --interactive-icon-active: var(--neutral-1); - --interactive-icon-default: var(--neutral-10); - --interactive-icon-hover: var(--neutral-1); - --interactive-muted: var(--neutral-35); - --interactive-text-active: var(--neutral-1); - --interactive-text-default: var(--neutral-10); - --interactive-text-hover: var(--neutral-1); - --mention-background: hsl(var(--opacity-blurple-48-hsl)/0.47843137254901963); - --mention-foreground: var(--blurple-5); - --message-automod-background-default: hsl(var(--opacity-red-20-hsl)/0.2); - --message-automod-background-hover: hsl(var(--opacity-red-24-hsl)/0.23921568627450981); - --message-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --message-highlight-background-default: hsl(var(--opacity-blurple-20-hsl)/0.2); - --message-highlight-background-hover: hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981); - --message-mentioned-background-default: hsl(var(--opacity-yellow-20-hsl)/0.2); - --message-mentioned-background-hover: hsl(var(--opacity-yellow-24-hsl)/0.23921568627450981); - --mobile-text-heading-primary: var(--neutral-1); - --progressbar-track-background: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --radio-border-active: var(--neutral-19); - --radio-border-default: var(--neutral-19); - --radio-border-hover: var(--neutral-19); - --radio-border-selected-default: var(--neutral-19); - --radio-border-selected-hover: var(--neutral-19); - --redesign-button-tertiary-text: var(--neutral-1); - --redesign-channel-name-muted-text: var(--neutral-19); - --redesign-channel-name-text: var(--neutral-1); - --spine-default: var(--neutral-38); - --stage-card-pill-bg: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --status-danger: var(--red-new-39); - --switch-border-default: var(--neutral-19); - --switch-border-hover: var(--neutral-19); - --switch-border-selected-default: var(--neutral-19); - --switch-border-selected-hover: var(--neutral-19); - --text-brand: var(--brand-330); - --text-code: var(--blue-new-14); - --text-code-addition: var(--green-new-14); - --text-code-builtin: var(--orange-new-12); - --text-code-bullet: var(--yellow-new-29); - --text-code-comment: var(--neutral-23); - --text-code-deletion: var(--red-new-9); - --text-code-keyword: var(--red-new-10); - --text-code-section: var(--blue-new-14); - --text-code-string: var(--teal-new-15); - --text-code-tag: var(--green-new-15); - --text-code-title: var(--blue-new-14); - --text-code-variable: var(--blue-new-14); - --text-default: var(--neutral-1); - --text-feedback-critical: var(--red-new-16); - --text-feedback-info: var(--blue-new-19); - --text-feedback-positive: var(--green-new-20); - --text-feedback-warning: var(--yellow-new-33); - --text-link: var(--blue-new-21); - --text-muted: var(--neutral-19); - --text-strong: var(--neutral-1); - --text-subtle: var(--neutral-10); - --text-voice-disconnected: var(--red-new-16); - --thread-channel-spine: var(--neutral-35); - --user-profile-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --user-profile-border: var(--neutral-19); - --user-profile-overlay-background-hover: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --user-profile-toolbar-border: var(--neutral-32) -} - -@supports (color: color-mix(in lch,red,blue)) { - .high-contrast-mode .theme-darker,.high-contrast-mode.theme-darker { - --app-frame-border:color-mix(in oklab,var(--neutral-19) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --background-code: color-mix(in oklab,hsl(var(--opacity-blurple-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-critical: color-mix(in oklab,hsl(var(--opacity-red-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-info: color-mix(in oklab,hsl(var(--opacity-blue-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-positive: color-mix(in oklab,hsl(var(--opacity-green-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --background-feedback-warning: color-mix(in oklab,hsl(var(--opacity-yellow-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --border-focus: color-mix(in oklab,var(--blue-new-30) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-muted: color-mix(in oklab,var(--neutral-32) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-normal: color-mix(in oklab,var(--neutral-13) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-strong: color-mix(in oklab,var(--neutral-5) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --border-subtle: color-mix(in oklab,var(--neutral-19) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --card-secondary-bg: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --channel-icon: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channel-text-area-placeholder: color-mix(in oklab,var(--neutral-33) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --channels-default: color-mix(in oklab,var(--neutral-27) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --chat-text-muted: color-mix(in oklab,var(--neutral-25) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --control-secondary-border-active: color-mix(in oklab,var(--neutral-29) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --control-secondary-border-default: color-mix(in oklab,var(--neutral-29) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --gradient-progress-pill-background: color-mix(in oklab,var(--neutral-42) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-base-color-amount,0%)); - --icon-default: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-critical: color-mix(in oklab,var(--red-new-16) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-info: color-mix(in oklab,var(--blue-new-19) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-positive: color-mix(in oklab,var(--green-new-20) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-feedback-warning: color-mix(in oklab,var(--yellow-new-33) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-link: color-mix(in oklab,var(--blue-new-21) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-muted: color-mix(in oklab,var(--neutral-19) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-strong: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-subtle: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --icon-voice-disconnected: color-mix(in oklab,var(--red-new-16) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-background-default: color-mix(in oklab,hsl(var(--opacity-black-16-hsl)/0.1607843137254902) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.1607843137254902) var(--custom-theme-base-color-amount,0%)); - --input-border-default: color-mix(in oklab,var(--neutral-29) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-error-default: color-mix(in oklab,var(--red-new-16) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-border-hover: color-mix(in oklab,var(--neutral-13) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --input-icon-default: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-placeholder-text-default: color-mix(in oklab,var(--neutral-35) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-default: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --input-text-error-default: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-background-active: color-mix(in oklab,hsl(var(--opacity-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --interactive-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --interactive-background-selected: color-mix(in oklab,hsl(var(--opacity-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --interactive-icon-active: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-default: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-icon-hover: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-muted: color-mix(in oklab,var(--neutral-35) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-active: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-default: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --interactive-text-hover: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --mention-background: color-mix(in oklab,hsl(var(--opacity-blurple-48-hsl)/0.47843137254901963) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.47843137254901963) var(--custom-theme-base-color-amount,0%)); - --mention-foreground: color-mix(in oklab,var(--blurple-5) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --message-automod-background-default: color-mix(in oklab,hsl(var(--opacity-red-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --message-automod-background-hover: color-mix(in oklab,hsl(var(--opacity-red-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --message-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-default: color-mix(in oklab,hsl(var(--opacity-blurple-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --message-highlight-background-hover: color-mix(in oklab,hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-default: color-mix(in oklab,hsl(var(--opacity-yellow-20-hsl)/0.2) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.2) var(--custom-theme-base-color-amount,0%)); - --message-mentioned-background-hover: color-mix(in oklab,hsl(var(--opacity-yellow-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --mobile-text-heading-primary: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --progressbar-track-background: color-mix(in oklab,hsl(var(--opacity-24-hsl)/0.23921568627450981) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.23921568627450981) var(--custom-theme-base-color-amount,0%)); - --redesign-button-tertiary-text: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --redesign-channel-name-muted-text: color-mix(in oklab,var(--neutral-19) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --redesign-channel-name-text: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --spine-default: color-mix(in oklab,var(--neutral-38) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --stage-card-pill-bg: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --text-brand: color-mix(in oklab,var(--brand-330) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code: color-mix(in oklab,var(--blue-new-14) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-addition: color-mix(in oklab,var(--green-new-14) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-builtin: color-mix(in oklab,var(--orange-new-12) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-bullet: color-mix(in oklab,var(--yellow-new-29) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-comment: color-mix(in oklab,var(--neutral-23) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-deletion: color-mix(in oklab,var(--red-new-9) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-keyword: color-mix(in oklab,var(--red-new-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-section: color-mix(in oklab,var(--blue-new-14) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-string: color-mix(in oklab,var(--teal-new-15) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-tag: color-mix(in oklab,var(--green-new-15) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-title: color-mix(in oklab,var(--blue-new-14) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-code-variable: color-mix(in oklab,var(--blue-new-14) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-default: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-critical: color-mix(in oklab,var(--red-new-16) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-info: color-mix(in oklab,var(--blue-new-19) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-positive: color-mix(in oklab,var(--green-new-20) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-feedback-warning: color-mix(in oklab,var(--yellow-new-33) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-link: color-mix(in oklab,var(--blue-new-21) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-muted: color-mix(in oklab,var(--neutral-19) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-strong: color-mix(in oklab,var(--neutral-1) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-subtle: color-mix(in oklab,var(--neutral-10) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --text-voice-disconnected: color-mix(in oklab,var(--red-new-16) 100%,var(--custom-theme-text-color,#000) var(--custom-theme-text-color-amount,0%)); - --thread-channel-spine: color-mix(in oklab,var(--neutral-35) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --user-profile-border: color-mix(in oklab,var(--neutral-19) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))); - --user-profile-overlay-background-hover: color-mix(in oklab,hsl(var(--opacity-12-hsl)/0.12156862745098039) 100%,hsl(var(--custom-theme-base-color-hsl,0 0% 0%)/0.12156862745098039) var(--custom-theme-base-color-amount,0%)); - --user-profile-toolbar-border: color-mix(in oklab,var(--neutral-32) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))) - } -} - -:root { - --neutral-1: hsl(var(--neutral-1-hsl)/1); - --neutral-1-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --neutral-2: hsl(var(--neutral-2-hsl)/1); - --neutral-2-hsl: 0 calc(var(--saturation-factor, 1)*0%) 98.431%; - --neutral-3: hsl(var(--neutral-3-hsl)/1); - --neutral-3-hsl: 240 calc(var(--saturation-factor, 1)*6.667%) 97.059%; - --neutral-4: hsl(var(--neutral-4-hsl)/1); - --neutral-4-hsl: 240 calc(var(--saturation-factor, 1)*4.348%) 95.49%; - --neutral-5: hsl(var(--neutral-5-hsl)/1); - --neutral-5-hsl: 240 calc(var(--saturation-factor, 1)*6.667%) 94.118%; - --neutral-6: hsl(var(--neutral-6-hsl)/1); - --neutral-6-hsl: 210 calc(var(--saturation-factor, 1)*5.263%) 92.549%; - --neutral-7: hsl(var(--neutral-7-hsl)/1); - --neutral-7-hsl: 240 calc(var(--saturation-factor, 1)*4.545%) 91.373%; - --neutral-8: hsl(var(--neutral-8-hsl)/1); - --neutral-8-hsl: 240 calc(var(--saturation-factor, 1)*3.846%) 89.804%; - --neutral-9: hsl(var(--neutral-9-hsl)/1); - --neutral-9-hsl: 240 calc(var(--saturation-factor, 1)*5.085%) 88.431%; - --neutral-10: hsl(var(--neutral-10-hsl)/1); - --neutral-10-hsl: 240 calc(var(--saturation-factor, 1)*4.478%) 86.863%; - --neutral-11: hsl(var(--neutral-11-hsl)/1); - --neutral-11-hsl: 225 calc(var(--saturation-factor, 1)*5.405%) 85.49%; - --neutral-12: hsl(var(--neutral-12-hsl)/1); - --neutral-12-hsl: 225 calc(var(--saturation-factor, 1)*4.878%) 83.922%; - --neutral-13: hsl(var(--neutral-13-hsl)/1); - --neutral-13-hsl: 240 calc(var(--saturation-factor, 1)*4.545%) 82.745%; - --neutral-14: hsl(var(--neutral-14-hsl)/1); - --neutral-14-hsl: 240 calc(var(--saturation-factor, 1)*4.167%) 81.176%; - --neutral-15: hsl(var(--neutral-15-hsl)/1); - --neutral-15-hsl: 228 calc(var(--saturation-factor, 1)*4.854%) 79.804%; - --neutral-16: hsl(var(--neutral-16-hsl)/1); - --neutral-16-hsl: 228 calc(var(--saturation-factor, 1)*4.505%) 78.235%; - --neutral-17: hsl(var(--neutral-17-hsl)/1); - --neutral-17-hsl: 240 calc(var(--saturation-factor, 1)*4.274%) 77.059%; - --neutral-18: hsl(var(--neutral-18-hsl)/1); - --neutral-18-hsl: 240 calc(var(--saturation-factor, 1)*4%) 75.49%; - --neutral-19: hsl(var(--neutral-19-hsl)/1); - --neutral-19-hsl: 230 calc(var(--saturation-factor, 1)*4.545%) 74.118%; - --neutral-20: hsl(var(--neutral-20-hsl)/1); - --neutral-20-hsl: 230 calc(var(--saturation-factor, 1)*4.286%) 72.549%; - --neutral-21: hsl(var(--neutral-21-hsl)/1); - --neutral-21-hsl: 240 calc(var(--saturation-factor, 1)*4.11%) 71.373%; - --neutral-22: hsl(var(--neutral-22-hsl)/1); - --neutral-22-hsl: 231.429 calc(var(--saturation-factor, 1)*4.575%) 70%; - --neutral-23: hsl(var(--neutral-23-hsl)/1); - --neutral-23-hsl: 231.429 calc(var(--saturation-factor, 1)*4.348%) 68.431%; - --neutral-24: hsl(var(--neutral-24-hsl)/1); - --neutral-24-hsl: 240 calc(var(--saturation-factor, 1)*4.192%) 67.255%; - --neutral-25: hsl(var(--neutral-25-hsl)/1); - --neutral-25-hsl: 231.429 calc(var(--saturation-factor, 1)*4%) 65.686%; - --neutral-26: hsl(var(--neutral-26-hsl)/1); - --neutral-26-hsl: 232.5 calc(var(--saturation-factor, 1)*4.396%) 64.314%; - --neutral-27: hsl(var(--neutral-27-hsl)/1); - --neutral-27-hsl: 232.5 calc(var(--saturation-factor, 1)*4.255%) 63.137%; - --neutral-28: hsl(var(--neutral-28-hsl)/1); - --neutral-28-hsl: 232.5 calc(var(--saturation-factor, 1)*4.082%) 61.569%; - --neutral-29: hsl(var(--neutral-29-hsl)/1); - --neutral-29-hsl: 232.5 calc(var(--saturation-factor, 1)*3.96%) 60.392%; - --neutral-30: hsl(var(--neutral-30-hsl)/1); - --neutral-30-hsl: 233.333 calc(var(--saturation-factor, 1)*4.306%) 59.02%; - --neutral-31: hsl(var(--neutral-31-hsl)/1); - --neutral-31-hsl: 232.5 calc(var(--saturation-factor, 1)*3.704%) 57.647%; - --neutral-32: hsl(var(--neutral-32-hsl)/1); - --neutral-32-hsl: 233.333 calc(var(--saturation-factor, 1)*4.036%) 56.275%; - --neutral-33: hsl(var(--neutral-33-hsl)/1); - --neutral-33-hsl: 233.333 calc(var(--saturation-factor, 1)*3.93%) 55.098%; - --neutral-34: hsl(var(--neutral-34-hsl)/1); - --neutral-34-hsl: 233.333 calc(var(--saturation-factor, 1)*3.797%) 53.529%; - --neutral-35: hsl(var(--neutral-35-hsl)/1); - --neutral-35-hsl: 233.333 calc(var(--saturation-factor, 1)*3.704%) 52.353%; - --neutral-36: hsl(var(--neutral-36-hsl)/1); - --neutral-36-hsl: 234 calc(var(--saturation-factor, 1)*4%) 50.98%; - --neutral-37: hsl(var(--neutral-37-hsl)/1); - --neutral-37-hsl: 233.333 calc(var(--saturation-factor, 1)*3.557%) 49.608%; - --neutral-38: hsl(var(--neutral-38-hsl)/1); - --neutral-38-hsl: 234 calc(var(--saturation-factor, 1)*4.065%) 48.235%; - --neutral-39: hsl(var(--neutral-39-hsl)/1); - --neutral-39-hsl: 234 calc(var(--saturation-factor, 1)*4.167%) 47.059%; - --neutral-40: hsl(var(--neutral-40-hsl)/1); - --neutral-40-hsl: 234 calc(var(--saturation-factor, 1)*4.274%) 45.882%; - --neutral-41: hsl(var(--neutral-41-hsl)/1); - --neutral-41-hsl: 234 calc(var(--saturation-factor, 1)*4.425%) 44.314%; - --neutral-42: hsl(var(--neutral-42-hsl)/1); - --neutral-42-hsl: 234 calc(var(--saturation-factor, 1)*4.545%) 43.137%; - --neutral-43: hsl(var(--neutral-43-hsl)/1); - --neutral-43-hsl: 234 calc(var(--saturation-factor, 1)*4.673%) 41.961%; - --neutral-44: hsl(var(--neutral-44-hsl)/1); - --neutral-44-hsl: 234.545 calc(var(--saturation-factor, 1)*5.314%) 40.588%; - --neutral-45: hsl(var(--neutral-45-hsl)/1); - --neutral-45-hsl: 234.545 calc(var(--saturation-factor, 1)*5.473%) 39.412%; - --neutral-46: hsl(var(--neutral-46-hsl)/1); - --neutral-46-hsl: 234.545 calc(var(--saturation-factor, 1)*5.641%) 38.235%; - --neutral-47: hsl(var(--neutral-47-hsl)/1); - --neutral-47-hsl: 234 calc(var(--saturation-factor, 1)*5.319%) 36.863%; - --neutral-48: hsl(var(--neutral-48-hsl)/1); - --neutral-48-hsl: 234.545 calc(var(--saturation-factor, 1)*6.077%) 35.49%; - --neutral-49: hsl(var(--neutral-49-hsl)/1); - --neutral-49-hsl: 234.545 calc(var(--saturation-factor, 1)*6.286%) 34.314%; - --neutral-50: hsl(var(--neutral-50-hsl)/1); - --neutral-50-hsl: 234.545 calc(var(--saturation-factor, 1)*6.509%) 33.137%; - --neutral-51: hsl(var(--neutral-51-hsl)/1); - --neutral-51-hsl: 234.545 calc(var(--saturation-factor, 1)*6.667%) 32.353%; - --neutral-52: hsl(var(--neutral-52-hsl)/1); - --neutral-52-hsl: 234 calc(var(--saturation-factor, 1)*6.173%) 31.765%; - --neutral-53: hsl(var(--neutral-53-hsl)/1); - --neutral-53-hsl: 234.545 calc(var(--saturation-factor, 1)*6.918%) 31.176%; - --neutral-54: hsl(var(--neutral-54-hsl)/1); - --neutral-54-hsl: 234 calc(var(--saturation-factor, 1)*6.41%) 30.588%; - --neutral-55: hsl(var(--neutral-55-hsl)/1); - --neutral-55-hsl: 234 calc(var(--saturation-factor, 1)*6.579%) 29.804%; - --neutral-56: hsl(var(--neutral-56-hsl)/1); - --neutral-56-hsl: 233.333 calc(var(--saturation-factor, 1)*6.04%) 29.216%; - --neutral-57: hsl(var(--neutral-57-hsl)/1); - --neutral-57-hsl: 233.333 calc(var(--saturation-factor, 1)*6.207%) 28.431%; - --neutral-58: hsl(var(--neutral-58-hsl)/1); - --neutral-58-hsl: 240 calc(var(--saturation-factor, 1)*6.294%) 28.039%; - --neutral-59: hsl(var(--neutral-59-hsl)/1); - --neutral-59-hsl: 233.333 calc(var(--saturation-factor, 1)*6.475%) 27.255%; - --neutral-60: hsl(var(--neutral-60-hsl)/1); - --neutral-60-hsl: 233.333 calc(var(--saturation-factor, 1)*6.667%) 26.471%; - --neutral-61: hsl(var(--neutral-61-hsl)/1); - --neutral-61-hsl: 232.5 calc(var(--saturation-factor, 1)*6.061%) 25.882%; - --neutral-62: hsl(var(--neutral-62-hsl)/1); - --neutral-62-hsl: 233.333 calc(var(--saturation-factor, 1)*6.977%) 25.294%; - --neutral-63: hsl(var(--neutral-63-hsl)/1); - --neutral-63-hsl: 232.5 calc(var(--saturation-factor, 1)*6.349%) 24.706%; - --neutral-64: hsl(var(--neutral-64-hsl)/1); - --neutral-64-hsl: 232.5 calc(var(--saturation-factor, 1)*6.557%) 23.922%; - --neutral-65: hsl(var(--neutral-65-hsl)/1); - --neutral-65-hsl: 232.5 calc(var(--saturation-factor, 1)*6.667%) 23.529%; - --neutral-66: hsl(var(--neutral-66-hsl)/1); - --neutral-66-hsl: 232.5 calc(var(--saturation-factor, 1)*6.897%) 22.745%; - --neutral-67: hsl(var(--neutral-67-hsl)/1); - --neutral-67-hsl: 231.429 calc(var(--saturation-factor, 1)*6.195%) 22.157%; - --neutral-68: hsl(var(--neutral-68-hsl)/1); - --neutral-68-hsl: 240 calc(var(--saturation-factor, 1)*5.455%) 21.569%; - --neutral-69: hsl(var(--neutral-69-hsl)/1); - --neutral-69-hsl: 231.429 calc(var(--saturation-factor, 1)*6.542%) 20.98%; - --neutral-70: hsl(var(--neutral-70-hsl)/1); - --neutral-70-hsl: 240 calc(var(--saturation-factor, 1)*5.769%) 20.392%; - --neutral-71: hsl(var(--neutral-71-hsl)/1); - --neutral-71-hsl: 230 calc(var(--saturation-factor, 1)*6%) 19.608%; - --neutral-72: hsl(var(--neutral-72-hsl)/1); - --neutral-72-hsl: 240 calc(var(--saturation-factor, 1)*6.122%) 19.216%; - --neutral-73: hsl(var(--neutral-73-hsl)/1); - --neutral-73-hsl: 230 calc(var(--saturation-factor, 1)*6.383%) 18.431%; - --neutral-74: hsl(var(--neutral-74-hsl)/1); - --neutral-74-hsl: 240 calc(var(--saturation-factor, 1)*6.522%) 18.039%; - --neutral-75: hsl(var(--neutral-75-hsl)/1); - --neutral-75-hsl: 230 calc(var(--saturation-factor, 1)*6.818%) 17.255%; - --neutral-76: hsl(var(--neutral-76-hsl)/1); - --neutral-76-hsl: 240 calc(var(--saturation-factor, 1)*5.882%) 16.667%; - --neutral-77: hsl(var(--neutral-77-hsl)/1); - --neutral-77-hsl: 240 calc(var(--saturation-factor, 1)*6.024%) 16.275%; - --neutral-78: hsl(var(--neutral-78-hsl)/1); - --neutral-78-hsl: 228 calc(var(--saturation-factor, 1)*6.329%) 15.49%; - --neutral-79: hsl(var(--neutral-79-hsl)/1); - --neutral-79-hsl: 240 calc(var(--saturation-factor, 1)*6.494%) 15.098%; - --neutral-80: hsl(var(--neutral-80-hsl)/1); - --neutral-80-hsl: 228 calc(var(--saturation-factor, 1)*6.849%) 14.314%; - --neutral-81: hsl(var(--neutral-81-hsl)/1); - --neutral-81-hsl: 240 calc(var(--saturation-factor, 1)*5.714%) 13.725%; - --neutral-82: hsl(var(--neutral-82-hsl)/1); - --neutral-82-hsl: 240 calc(var(--saturation-factor, 1)*5.882%) 13.333%; - --neutral-83: hsl(var(--neutral-83-hsl)/1); - --neutral-83-hsl: 225 calc(var(--saturation-factor, 1)*6.25%) 12.549%; - --neutral-84: hsl(var(--neutral-84-hsl)/1); - --neutral-84-hsl: 240 calc(var(--saturation-factor, 1)*6.452%) 12.157%; - --neutral-85: hsl(var(--neutral-85-hsl)/1); - --neutral-85-hsl: 225 calc(var(--saturation-factor, 1)*6.897%) 11.373%; - --neutral-86: hsl(var(--neutral-86-hsl)/1); - --neutral-86-hsl: 240 calc(var(--saturation-factor, 1)*7.143%) 10.98%; - --neutral-87: hsl(var(--neutral-87-hsl)/1); - --neutral-87-hsl: 240 calc(var(--saturation-factor, 1)*5.66%) 10.392%; - --neutral-88: hsl(var(--neutral-88-hsl)/1); - --neutral-88-hsl: 225 calc(var(--saturation-factor, 1)*8%) 9.804%; - --neutral-89: hsl(var(--neutral-89-hsl)/1); - --neutral-89-hsl: 240 calc(var(--saturation-factor, 1)*6.383%) 9.216%; - --neutral-90: hsl(var(--neutral-90-hsl)/1); - --neutral-90-hsl: 240 calc(var(--saturation-factor, 1)*6.667%) 8.824%; - --neutral-91: hsl(var(--neutral-91-hsl)/1); - --neutral-91-hsl: 220 calc(var(--saturation-factor, 1)*7.317%) 8.039%; - --neutral-92: hsl(var(--neutral-92-hsl)/1); - --neutral-92-hsl: 240 calc(var(--saturation-factor, 1)*5.263%) 7.451%; - --neutral-93: hsl(var(--neutral-93-hsl)/1); - --neutral-93-hsl: 240 calc(var(--saturation-factor, 1)*8.571%) 6.863%; - --neutral-94: hsl(var(--neutral-94-hsl)/1); - --neutral-94-hsl: 240 calc(var(--saturation-factor, 1)*6.667%) 5.882%; - --neutral-95: hsl(var(--neutral-95-hsl)/1); - --neutral-95-hsl: 240 calc(var(--saturation-factor, 1)*7.692%) 5.098%; - --neutral-96: hsl(var(--neutral-96-hsl)/1); - --neutral-96-hsl: 240 calc(var(--saturation-factor, 1)*9.091%) 4.314%; - --neutral-97: hsl(var(--neutral-97-hsl)/1); - --neutral-97-hsl: 240 calc(var(--saturation-factor, 1)*12.5%) 3.137%; - --neutral-98: hsl(var(--neutral-98-hsl)/1); - --neutral-98-hsl: 240 calc(var(--saturation-factor, 1)*9.091%) 2.157%; - --neutral-99: hsl(var(--neutral-99-hsl)/1); - --neutral-99-hsl: 240 calc(var(--saturation-factor, 1)*20%) 0.98%; - --neutral-100: hsl(var(--neutral-100-hsl)/1); - --neutral-100-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --blue-new-1: hsl(var(--blue-new-1-hsl)/1); - --blue-new-1-hsl: 216.923 calc(var(--saturation-factor, 1)*100%) 94.902%; - --blue-new-2: hsl(var(--blue-new-2-hsl)/1); - --blue-new-2-hsl: 216.774 calc(var(--saturation-factor, 1)*100%) 93.922%; - --blue-new-3: hsl(var(--blue-new-3-hsl)/1); - --blue-new-3-hsl: 216 calc(var(--saturation-factor, 1)*100%) 93.137%; - --blue-new-4: hsl(var(--blue-new-4-hsl)/1); - --blue-new-4-hsl: 216 calc(var(--saturation-factor, 1)*100%) 92.157%; - --blue-new-5: hsl(var(--blue-new-5-hsl)/1); - --blue-new-5-hsl: 215.455 calc(var(--saturation-factor, 1)*100%) 91.373%; - --blue-new-6: hsl(var(--blue-new-6-hsl)/1); - --blue-new-6-hsl: 214.286 calc(var(--saturation-factor, 1)*100%) 90.392%; - --blue-new-7: hsl(var(--blue-new-7-hsl)/1); - --blue-new-7-hsl: 215.094 calc(var(--saturation-factor, 1)*100%) 89.608%; - --blue-new-8: hsl(var(--blue-new-8-hsl)/1); - --blue-new-8-hsl: 214.138 calc(var(--saturation-factor, 1)*100%) 88.627%; - --blue-new-9: hsl(var(--blue-new-9-hsl)/1); - --blue-new-9-hsl: 213.871 calc(var(--saturation-factor, 1)*100%) 87.843%; - --blue-new-10: hsl(var(--blue-new-10-hsl)/1); - --blue-new-10-hsl: 214.545 calc(var(--saturation-factor, 1)*100%) 87.059%; - --blue-new-11: hsl(var(--blue-new-11-hsl)/1); - --blue-new-11-hsl: 213.803 calc(var(--saturation-factor, 1)*100%) 86.078%; - --blue-new-12: hsl(var(--blue-new-12-hsl)/1); - --blue-new-12-hsl: 214.4 calc(var(--saturation-factor, 1)*100%) 85.294%; - --blue-new-13: hsl(var(--blue-new-13-hsl)/1); - --blue-new-13-hsl: 214.177 calc(var(--saturation-factor, 1)*100%) 84.51%; - --blue-new-14: hsl(var(--blue-new-14-hsl)/1); - --blue-new-14-hsl: 214.286 calc(var(--saturation-factor, 1)*100%) 83.529%; - --blue-new-15: hsl(var(--blue-new-15-hsl)/1); - --blue-new-15-hsl: 213.793 calc(var(--saturation-factor, 1)*97.753%) 82.549%; - --blue-new-16: hsl(var(--blue-new-16-hsl)/1); - --blue-new-16-hsl: 213.333 calc(var(--saturation-factor, 1)*95.745%) 81.569%; - --blue-new-17: hsl(var(--blue-new-17-hsl)/1); - --blue-new-17-hsl: 213.83 calc(var(--saturation-factor, 1)*95.918%) 80.784%; - --blue-new-18: hsl(var(--blue-new-18-hsl)/1); - --blue-new-18-hsl: 213.402 calc(var(--saturation-factor, 1)*94.175%) 79.804%; - --blue-new-19: hsl(var(--blue-new-19-hsl)/1); - --blue-new-19-hsl: 213.861 calc(var(--saturation-factor, 1)*94.393%) 79.02%; - --blue-new-20: hsl(var(--blue-new-20-hsl)/1); - --blue-new-20-hsl: 213.143 calc(var(--saturation-factor, 1)*92.92%) 77.843%; - --blue-new-21: hsl(var(--blue-new-21-hsl)/1); - --blue-new-21-hsl: 213.333 calc(var(--saturation-factor, 1)*91.525%) 76.863%; - --blue-new-22: hsl(var(--blue-new-22-hsl)/1); - --blue-new-22-hsl: 213.214 calc(var(--saturation-factor, 1)*91.803%) 76.078%; - --blue-new-23: hsl(var(--blue-new-23-hsl)/1); - --blue-new-23-hsl: 213.391 calc(var(--saturation-factor, 1)*90.551%) 75.098%; - --blue-new-24: hsl(var(--blue-new-24-hsl)/1); - --blue-new-24-hsl: 213.051 calc(var(--saturation-factor, 1)*89.394%) 74.118%; - --blue-new-25: hsl(var(--blue-new-25-hsl)/1); - --blue-new-25-hsl: 213.443 calc(var(--saturation-factor, 1)*89.706%) 73.333%; - --blue-new-26: hsl(var(--blue-new-26-hsl)/1); - --blue-new-26-hsl: 213.12 calc(var(--saturation-factor, 1)*88.652%) 72.353%; - --blue-new-27: hsl(var(--blue-new-27-hsl)/1); - --blue-new-27-hsl: 213.281 calc(var(--saturation-factor, 1)*87.671%) 71.373%; - --blue-new-28: hsl(var(--blue-new-28-hsl)/1); - --blue-new-28-hsl: 212.977 calc(var(--saturation-factor, 1)*86.755%) 70.392%; - --blue-new-29: hsl(var(--blue-new-29-hsl)/1); - --blue-new-29-hsl: 213.333 calc(var(--saturation-factor, 1)*87.097%) 69.608%; - --blue-new-30: hsl(var(--blue-new-30-hsl)/1); - --blue-new-30-hsl: 213.043 calc(var(--saturation-factor, 1)*86.25%) 68.627%; - --blue-new-31: hsl(var(--blue-new-31-hsl)/1); - --blue-new-31-hsl: 213.191 calc(var(--saturation-factor, 1)*85.455%) 67.647%; - --blue-new-32: hsl(var(--blue-new-32-hsl)/1); - --blue-new-32-hsl: 212.917 calc(var(--saturation-factor, 1)*84.706%) 66.667%; - --blue-new-33: hsl(var(--blue-new-33-hsl)/1); - --blue-new-33-hsl: 213.061 calc(var(--saturation-factor, 1)*84%) 65.686%; - --blue-new-34: hsl(var(--blue-new-34-hsl)/1); - --blue-new-34-hsl: 212.98 calc(var(--saturation-factor, 1)*84.358%) 64.902%; - --blue-new-35: hsl(var(--blue-new-35-hsl)/1); - --blue-new-35-hsl: 213.117 calc(var(--saturation-factor, 1)*83.696%) 63.922%; - --blue-new-36: hsl(var(--blue-new-36-hsl)/1); - --blue-new-36-hsl: 212.866 calc(var(--saturation-factor, 1)*83.069%) 62.941%; - --blue-new-37: hsl(var(--blue-new-37-hsl)/1); - --blue-new-37-hsl: 212.795 calc(var(--saturation-factor, 1)*82.564%) 61.765%; - --blue-new-38: hsl(var(--blue-new-38-hsl)/1); - --blue-new-38-hsl: 212.927 calc(var(--saturation-factor, 1)*82%) 60.784%; - --blue-new-39: hsl(var(--blue-new-39-hsl)/1); - --blue-new-39-hsl: 212.695 calc(var(--saturation-factor, 1)*81.463%) 59.804%; - --blue-new-40: hsl(var(--blue-new-40-hsl)/1); - --blue-new-40-hsl: 212.791 calc(var(--saturation-factor, 1)*81.905%) 58.824%; - --blue-new-41: hsl(var(--blue-new-41-hsl)/1); - --blue-new-41-hsl: 212.571 calc(var(--saturation-factor, 1)*81.395%) 57.843%; - --blue-new-42: hsl(var(--blue-new-42-hsl)/1); - --blue-new-42-hsl: 212.514 calc(var(--saturation-factor, 1)*80.995%) 56.667%; - --blue-new-43: hsl(var(--blue-new-43-hsl)/1); - --blue-new-43-hsl: 212.459 calc(var(--saturation-factor, 1)*80.617%) 55.49%; - --blue-new-44: hsl(var(--blue-new-44-hsl)/1); - --blue-new-44-hsl: 212.086 calc(var(--saturation-factor, 1)*80.258%) 54.314%; - --blue-new-45: hsl(var(--blue-new-45-hsl)/1); - --blue-new-45-hsl: 211.875 calc(var(--saturation-factor, 1)*80%) 52.941%; - --blue-new-46: hsl(var(--blue-new-46-hsl)/1); - --blue-new-46-hsl: 211.515 calc(var(--saturation-factor, 1)*80.488%) 51.765%; - --blue-new-47: hsl(var(--blue-new-47-hsl)/1); - --blue-new-47-hsl: 211.176 calc(var(--saturation-factor, 1)*80.315%) 50.196%; - --blue-new-48: hsl(var(--blue-new-48-hsl)/1); - --blue-new-48-hsl: 210.711 calc(var(--saturation-factor, 1)*85.425%) 48.431%; - --blue-new-49: hsl(var(--blue-new-49-hsl)/1); - --blue-new-49-hsl: 209.327 calc(var(--saturation-factor, 1)*95.708%) 45.686%; - --blue-new-50: hsl(var(--blue-new-50-hsl)/1); - --blue-new-50-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --blue-new-51: hsl(var(--blue-new-51-hsl)/1); - --blue-new-51-hsl: 209.189 calc(var(--saturation-factor, 1)*100%) 43.529%; - --blue-new-52: hsl(var(--blue-new-52-hsl)/1); - --blue-new-52-hsl: 209.309 calc(var(--saturation-factor, 1)*100%) 42.549%; - --blue-new-53: hsl(var(--blue-new-53-hsl)/1); - --blue-new-53-hsl: 209.151 calc(var(--saturation-factor, 1)*100%) 41.569%; - --blue-new-54: hsl(var(--blue-new-54-hsl)/1); - --blue-new-54-hsl: 209.126 calc(var(--saturation-factor, 1)*99.038%) 40.784%; - --blue-new-55: hsl(var(--blue-new-55-hsl)/1); - --blue-new-55-hsl: 209.4 calc(var(--saturation-factor, 1)*98.039%) 40%; - --blue-new-56: hsl(var(--blue-new-56-hsl)/1); - --blue-new-56-hsl: 209.388 calc(var(--saturation-factor, 1)*98%) 39.216%; - --blue-new-57: hsl(var(--blue-new-57-hsl)/1); - --blue-new-57-hsl: 209.684 calc(var(--saturation-factor, 1)*96.939%) 38.431%; - --blue-new-58: hsl(var(--blue-new-58-hsl)/1); - --blue-new-58-hsl: 209.514 calc(var(--saturation-factor, 1)*96.859%) 37.451%; - --blue-new-59: hsl(var(--blue-new-59-hsl)/1); - --blue-new-59-hsl: 209.503 calc(var(--saturation-factor, 1)*96.791%) 36.667%; - --blue-new-60: hsl(var(--blue-new-60-hsl)/1); - --blue-new-60-hsl: 209.492 calc(var(--saturation-factor, 1)*97.79%) 35.49%; - --blue-new-61: hsl(var(--blue-new-61-hsl)/1); - --blue-new-61-hsl: 209.48 calc(var(--saturation-factor, 1)*97.74%) 34.706%; - --blue-new-62: hsl(var(--blue-new-62-hsl)/1); - --blue-new-62-hsl: 209.294 calc(var(--saturation-factor, 1)*98.837%) 33.725%; - --blue-new-63: hsl(var(--blue-new-63-hsl)/1); - --blue-new-63-hsl: 209.455 calc(var(--saturation-factor, 1)*98.802%) 32.745%; - --blue-new-64: hsl(var(--blue-new-64-hsl)/1); - --blue-new-64-hsl: 209.259 calc(var(--saturation-factor, 1)*100%) 31.765%; - --blue-new-65: hsl(var(--blue-new-65-hsl)/1); - --blue-new-65-hsl: 209.241 calc(var(--saturation-factor, 1)*100%) 30.98%; - --blue-new-66: hsl(var(--blue-new-66-hsl)/1); - --blue-new-66-hsl: 209.221 calc(var(--saturation-factor, 1)*100%) 30.196%; - --blue-new-67: hsl(var(--blue-new-67-hsl)/1); - --blue-new-67-hsl: 209.6 calc(var(--saturation-factor, 1)*100%) 29.412%; - --blue-new-68: hsl(var(--blue-new-68-hsl)/1); - --blue-new-68-hsl: 209.589 calc(var(--saturation-factor, 1)*100%) 28.627%; - --blue-new-69: hsl(var(--blue-new-69-hsl)/1); - --blue-new-69-hsl: 209.577 calc(var(--saturation-factor, 1)*100%) 27.843%; - --blue-new-70: hsl(var(--blue-new-70-hsl)/1); - --blue-new-70-hsl: 209.565 calc(var(--saturation-factor, 1)*100%) 27.059%; - --blue-new-71: hsl(var(--blue-new-71-hsl)/1); - --blue-new-71-hsl: 210 calc(var(--saturation-factor, 1)*100%) 26.275%; - --blue-new-72: hsl(var(--blue-new-72-hsl)/1); - --blue-new-72-hsl: 210 calc(var(--saturation-factor, 1)*100%) 25.49%; - --blue-new-73: hsl(var(--blue-new-73-hsl)/1); - --blue-new-73-hsl: 210 calc(var(--saturation-factor, 1)*100%) 24.706%; - --blue-new-74: hsl(var(--blue-new-74-hsl)/1); - --blue-new-74-hsl: 210 calc(var(--saturation-factor, 1)*100%) 23.922%; - --blue-new-75: hsl(var(--blue-new-75-hsl)/1); - --blue-new-75-hsl: 210.508 calc(var(--saturation-factor, 1)*100%) 23.137%; - --blue-new-76: hsl(var(--blue-new-76-hsl)/1); - --blue-new-76-hsl: 210.783 calc(var(--saturation-factor, 1)*100%) 22.549%; - --blue-new-77: hsl(var(--blue-new-77-hsl)/1); - --blue-new-77-hsl: 210.811 calc(var(--saturation-factor, 1)*100%) 21.765%; - --blue-new-78: hsl(var(--blue-new-78-hsl)/1); - --blue-new-78-hsl: 210.841 calc(var(--saturation-factor, 1)*100%) 20.98%; - --blue-new-79: hsl(var(--blue-new-79-hsl)/1); - --blue-new-79-hsl: 211.154 calc(var(--saturation-factor, 1)*100%) 20.392%; - --blue-new-80: hsl(var(--blue-new-80-hsl)/1); - --blue-new-80-hsl: 211.2 calc(var(--saturation-factor, 1)*100%) 19.608%; - --blue-new-81: hsl(var(--blue-new-81-hsl)/1); - --blue-new-81-hsl: 211.25 calc(var(--saturation-factor, 1)*100%) 18.824%; - --blue-new-82: hsl(var(--blue-new-82-hsl)/1); - --blue-new-82-hsl: 212.258 calc(var(--saturation-factor, 1)*100%) 18.235%; - --blue-new-83: hsl(var(--blue-new-83-hsl)/1); - --blue-new-83-hsl: 212.36 calc(var(--saturation-factor, 1)*100%) 17.451%; - --blue-new-84: hsl(var(--blue-new-84-hsl)/1); - --blue-new-84-hsl: 212.791 calc(var(--saturation-factor, 1)*100%) 16.863%; - --blue-new-85: hsl(var(--blue-new-85-hsl)/1); - --blue-new-85-hsl: 212.927 calc(var(--saturation-factor, 1)*100%) 16.078%; - --blue-new-86: hsl(var(--blue-new-86-hsl)/1); - --blue-new-86-hsl: 213.418 calc(var(--saturation-factor, 1)*100%) 15.49%; - --blue-new-87: hsl(var(--blue-new-87-hsl)/1); - --blue-new-87-hsl: 213.6 calc(var(--saturation-factor, 1)*100%) 14.706%; - --blue-new-88: hsl(var(--blue-new-88-hsl)/1); - --blue-new-88-hsl: 214.167 calc(var(--saturation-factor, 1)*100%) 14.118%; - --blue-new-89: hsl(var(--blue-new-89-hsl)/1); - --blue-new-89-hsl: 214.783 calc(var(--saturation-factor, 1)*100%) 13.529%; - --blue-new-90: hsl(var(--blue-new-90-hsl)/1); - --blue-new-90-hsl: 215.077 calc(var(--saturation-factor, 1)*100%) 12.745%; - --blue-new-91: hsl(var(--blue-new-91-hsl)/1); - --blue-new-91-hsl: 215.806 calc(var(--saturation-factor, 1)*100%) 12.157%; - --blue-new-92: hsl(var(--blue-new-92-hsl)/1); - --blue-new-92-hsl: 216.61 calc(var(--saturation-factor, 1)*100%) 11.569%; - --blue-new-93: hsl(var(--blue-new-93-hsl)/1); - --blue-new-93-hsl: 217.091 calc(var(--saturation-factor, 1)*100%) 10.784%; - --blue-new-94: hsl(var(--blue-new-94-hsl)/1); - --blue-new-94-hsl: 217.647 calc(var(--saturation-factor, 1)*100%) 10%; - --blue-new-95: hsl(var(--blue-new-95-hsl)/1); - --blue-new-95-hsl: 219.574 calc(var(--saturation-factor, 1)*100%) 9.216%; - --blue-new-96: hsl(var(--blue-new-96-hsl)/1); - --blue-new-96-hsl: 221.429 calc(var(--saturation-factor, 1)*100%) 8.235%; - --blue-new-97: hsl(var(--blue-new-97-hsl)/1); - --blue-new-97-hsl: 223.333 calc(var(--saturation-factor, 1)*100%) 7.059%; - --blue-new-98: hsl(var(--blue-new-98-hsl)/1); - --blue-new-98-hsl: 227.586 calc(var(--saturation-factor, 1)*100%) 5.686%; - --blue-new-99: hsl(var(--blue-new-99-hsl)/1); - --blue-new-99-hsl: 231 calc(var(--saturation-factor, 1)*100%) 3.922%; - --blue-new-100: hsl(var(--blue-new-100-hsl)/1); - --blue-new-100-hsl: 240 calc(var(--saturation-factor, 1)*100%) 1.373%; - --blurple-1: hsl(var(--blurple-1-hsl)/1); - --blurple-1-hsl: 230.4 calc(var(--saturation-factor, 1)*100%) 95.098%; - --blurple-2: hsl(var(--blurple-2-hsl)/1); - --blurple-2-hsl: 231.429 calc(var(--saturation-factor, 1)*100%) 94.51%; - --blurple-3: hsl(var(--blurple-3-hsl)/1); - --blurple-3-hsl: 230.625 calc(var(--saturation-factor, 1)*100%) 93.725%; - --blurple-4: hsl(var(--blurple-4-hsl)/1); - --blurple-4-hsl: 228.333 calc(var(--saturation-factor, 1)*100%) 92.941%; - --blurple-5: hsl(var(--blurple-5-hsl)/1); - --blurple-5-hsl: 229.231 calc(var(--saturation-factor, 1)*100%) 92.353%; - --blurple-6: hsl(var(--blurple-6-hsl)/1); - --blurple-6-hsl: 227.442 calc(var(--saturation-factor, 1)*100%) 91.569%; - --blurple-7: hsl(var(--blurple-7-hsl)/1); - --blurple-7-hsl: 228.261 calc(var(--saturation-factor, 1)*100%) 90.98%; - --blurple-8: hsl(var(--blurple-8-hsl)/1); - --blurple-8-hsl: 228 calc(var(--saturation-factor, 1)*100%) 90.196%; - --blurple-9: hsl(var(--blurple-9-hsl)/1); - --blurple-9-hsl: 227.547 calc(var(--saturation-factor, 1)*100%) 89.608%; - --blurple-10: hsl(var(--blurple-10-hsl)/1); - --blurple-10-hsl: 228.214 calc(var(--saturation-factor, 1)*100%) 89.02%; - --blurple-11: hsl(var(--blurple-11-hsl)/1); - --blurple-11-hsl: 227 calc(var(--saturation-factor, 1)*100%) 88.235%; - --blurple-12: hsl(var(--blurple-12-hsl)/1); - --blurple-12-hsl: 227.619 calc(var(--saturation-factor, 1)*100%) 87.647%; - --blurple-13: hsl(var(--blurple-13-hsl)/1); - --blurple-13-hsl: 227.463 calc(var(--saturation-factor, 1)*100%) 86.863%; - --blurple-14: hsl(var(--blurple-14-hsl)/1); - --blurple-14-hsl: 227.143 calc(var(--saturation-factor, 1)*100%) 86.275%; - --blurple-15: hsl(var(--blurple-15-hsl)/1); - --blurple-15-hsl: 227.671 calc(var(--saturation-factor, 1)*100%) 85.686%; - --blurple-16: hsl(var(--blurple-16-hsl)/1); - --blurple-16-hsl: 227.368 calc(var(--saturation-factor, 1)*100%) 85.098%; - --blurple-17: hsl(var(--blurple-17-hsl)/1); - --blurple-17-hsl: 227.848 calc(var(--saturation-factor, 1)*100%) 84.51%; - --blurple-18: hsl(var(--blurple-18-hsl)/1); - --blurple-18-hsl: 227.711 calc(var(--saturation-factor, 1)*100%) 83.725%; - --blurple-19: hsl(var(--blurple-19-hsl)/1); - --blurple-19-hsl: 227.442 calc(var(--saturation-factor, 1)*100%) 83.137%; - --blurple-20: hsl(var(--blurple-20-hsl)/1); - --blurple-20-hsl: 227.865 calc(var(--saturation-factor, 1)*100%) 82.549%; - --blurple-21: hsl(var(--blurple-21-hsl)/1); - --blurple-21-hsl: 228.261 calc(var(--saturation-factor, 1)*100%) 81.961%; - --blurple-22: hsl(var(--blurple-22-hsl)/1); - --blurple-22-hsl: 228 calc(var(--saturation-factor, 1)*100%) 81.373%; - --blurple-23: hsl(var(--blurple-23-hsl)/1); - --blurple-23-hsl: 228.367 calc(var(--saturation-factor, 1)*100%) 80.784%; - --blurple-24: hsl(var(--blurple-24-hsl)/1); - --blurple-24-hsl: 228.713 calc(var(--saturation-factor, 1)*100%) 80.196%; - --blurple-25: hsl(var(--blurple-25-hsl)/1); - --blurple-25-hsl: 228.462 calc(var(--saturation-factor, 1)*100%) 79.608%; - --blurple-26: hsl(var(--blurple-26-hsl)/1); - --blurple-26-hsl: 228.785 calc(var(--saturation-factor, 1)*100%) 79.02%; - --blurple-27: hsl(var(--blurple-27-hsl)/1); - --blurple-27-hsl: 228.991 calc(var(--saturation-factor, 1)*98.198%) 78.235%; - --blurple-28: hsl(var(--blurple-28-hsl)/1); - --blurple-28-hsl: 229.189 calc(var(--saturation-factor, 1)*98.23%) 77.843%; - --blurple-29: hsl(var(--blurple-29-hsl)/1); - --blurple-29-hsl: 229.381 calc(var(--saturation-factor, 1)*96.581%) 77.059%; - --blurple-30: hsl(var(--blurple-30-hsl)/1); - --blurple-30-hsl: 229.655 calc(var(--saturation-factor, 1)*96.667%) 76.471%; - --blurple-31: hsl(var(--blurple-31-hsl)/1); - --blurple-31-hsl: 229.322 calc(var(--saturation-factor, 1)*95.161%) 75.686%; - --blurple-32: hsl(var(--blurple-32-hsl)/1); - --blurple-32-hsl: 229.5 calc(var(--saturation-factor, 1)*93.75%) 74.902%; - --blurple-33: hsl(var(--blurple-33-hsl)/1); - --blurple-33-hsl: 230.164 calc(var(--saturation-factor, 1)*93.846%) 74.51%; - --blurple-34: hsl(var(--blurple-34-hsl)/1); - --blurple-34-hsl: 230.323 calc(var(--saturation-factor, 1)*92.537%) 73.725%; - --blurple-35: hsl(var(--blurple-35-hsl)/1); - --blurple-35-hsl: 230.476 calc(var(--saturation-factor, 1)*92.647%) 73.333%; - --blurple-36: hsl(var(--blurple-36-hsl)/1); - --blurple-36-hsl: 230.625 calc(var(--saturation-factor, 1)*91.429%) 72.549%; - --blurple-37: hsl(var(--blurple-37-hsl)/1); - --blurple-37-hsl: 230.84 calc(var(--saturation-factor, 1)*91.608%) 71.961%; - --blurple-38: hsl(var(--blurple-38-hsl)/1); - --blurple-38-hsl: 231.364 calc(var(--saturation-factor, 1)*90.411%) 71.373%; - --blurple-39: hsl(var(--blurple-39-hsl)/1); - --blurple-39-hsl: 231.111 calc(var(--saturation-factor, 1)*90.604%) 70.784%; - --blurple-40: hsl(var(--blurple-40-hsl)/1); - --blurple-40-hsl: 231.618 calc(var(--saturation-factor, 1)*89.474%) 70.196%; - --blurple-41: hsl(var(--blurple-41-hsl)/1); - --blurple-41-hsl: 231.739 calc(var(--saturation-factor, 1)*88.462%) 69.412%; - --blurple-42: hsl(var(--blurple-42-hsl)/1); - --blurple-42-hsl: 232.286 calc(var(--saturation-factor, 1)*88.608%) 69.02%; - --blurple-43: hsl(var(--blurple-43-hsl)/1); - --blurple-43-hsl: 232.766 calc(var(--saturation-factor, 1)*87.578%) 68.431%; - --blurple-44: hsl(var(--blurple-44-hsl)/1); - --blurple-44-hsl: 232.917 calc(var(--saturation-factor, 1)*87.805%) 67.843%; - --blurple-45: hsl(var(--blurple-45-hsl)/1); - --blurple-45-hsl: 232.966 calc(var(--saturation-factor, 1)*86.826%) 67.255%; - --blurple-46: hsl(var(--blurple-46-hsl)/1); - --blurple-46-hsl: 233.469 calc(var(--saturation-factor, 1)*86.982%) 66.863%; - --blurple-47: hsl(var(--blurple-47-hsl)/1); - --blurple-47-hsl: 233.919 calc(var(--saturation-factor, 1)*86.047%) 66.275%; - --blurple-48: hsl(var(--blurple-48-hsl)/1); - --blurple-48-hsl: 234.4 calc(var(--saturation-factor, 1)*86.207%) 65.882%; - --blurple-49: hsl(var(--blurple-49-hsl)/1); - --blurple-49-hsl: 234.474 calc(var(--saturation-factor, 1)*85.393%) 65.098%; - --blurple-50: hsl(var(--blurple-50-hsl)/1); - --blurple-50-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --blurple-51: hsl(var(--blurple-51-hsl)/1); - --blurple-51-hsl: 234.8 calc(var(--saturation-factor, 1)*79.787%) 63.137%; - --blurple-52: hsl(var(--blurple-52-hsl)/1); - --blurple-52-hsl: 234.658 calc(var(--saturation-factor, 1)*74.49%) 61.569%; - --blurple-53: hsl(var(--blurple-53-hsl)/1); - --blurple-53-hsl: 234.507 calc(var(--saturation-factor, 1)*69.608%) 60%; - --blurple-54: hsl(var(--blurple-54-hsl)/1); - --blurple-54-hsl: 234.388 calc(var(--saturation-factor, 1)*65.877%) 58.627%; - --blurple-55: hsl(var(--blurple-55-hsl)/1); - --blurple-55-hsl: 234.222 calc(var(--saturation-factor, 1)*61.644%) 57.059%; - --blurple-56: hsl(var(--blurple-56-hsl)/1); - --blurple-56-hsl: 234.091 calc(var(--saturation-factor, 1)*58.407%) 55.686%; - --blurple-57: hsl(var(--blurple-57-hsl)/1); - --blurple-57-hsl: 233.488 calc(var(--saturation-factor, 1)*55.365%) 54.314%; - --blurple-58: hsl(var(--blurple-58-hsl)/1); - --blurple-58-hsl: 233.28 calc(var(--saturation-factor, 1)*51.867%) 52.745%; - --blurple-59: hsl(var(--blurple-59-hsl)/1); - --blurple-59-hsl: 233.115 calc(var(--saturation-factor, 1)*49.194%) 51.373%; - --blurple-60: hsl(var(--blurple-60-hsl)/1); - --blurple-60-hsl: 232.941 calc(var(--saturation-factor, 1)*46.667%) 50%; - --blurple-61: hsl(var(--blurple-61-hsl)/1); - --blurple-61-hsl: 232.759 calc(var(--saturation-factor, 1)*46.774%) 48.627%; - --blurple-62: hsl(var(--blurple-62-hsl)/1); - --blurple-62-hsl: 232.566 calc(var(--saturation-factor, 1)*46.888%) 47.255%; - --blurple-63: hsl(var(--blurple-63-hsl)/1); - --blurple-63-hsl: 232.432 calc(var(--saturation-factor, 1)*47.234%) 46.078%; - --blurple-64: hsl(var(--blurple-64-hsl)/1); - --blurple-64-hsl: 232.222 calc(var(--saturation-factor, 1)*47.368%) 44.706%; - --blurple-65: hsl(var(--blurple-65-hsl)/1); - --blurple-65-hsl: 232 calc(var(--saturation-factor, 1)*47.511%) 43.333%; - --blurple-66: hsl(var(--blurple-66-hsl)/1); - --blurple-66-hsl: 231.845 calc(var(--saturation-factor, 1)*47.907%) 42.157%; - --blurple-67: hsl(var(--blurple-67-hsl)/1); - --blurple-67-hsl: 231.6 calc(var(--saturation-factor, 1)*48.077%) 40.784%; - --blurple-68: hsl(var(--blurple-68-hsl)/1); - --blurple-68-hsl: 231.429 calc(var(--saturation-factor, 1)*48.515%) 39.608%; - --blurple-69: hsl(var(--blurple-69-hsl)/1); - --blurple-69-hsl: 231.158 calc(var(--saturation-factor, 1)*48.718%) 38.235%; - --blurple-70: hsl(var(--blurple-70-hsl)/1); - --blurple-70-hsl: 230.968 calc(var(--saturation-factor, 1)*49.206%) 37.059%; - --blurple-71: hsl(var(--blurple-71-hsl)/1); - --blurple-71-hsl: 230.667 calc(var(--saturation-factor, 1)*49.451%) 35.686%; - --blurple-72: hsl(var(--blurple-72-hsl)/1); - --blurple-72-hsl: 230.455 calc(var(--saturation-factor, 1)*50%) 34.51%; - --blurple-73: hsl(var(--blurple-73-hsl)/1); - --blurple-73-hsl: 230.824 calc(var(--saturation-factor, 1)*49.708%) 33.529%; - --blurple-74: hsl(var(--blurple-74-hsl)/1); - --blurple-74-hsl: 230.602 calc(var(--saturation-factor, 1)*50.303%) 32.353%; - --blurple-75: hsl(var(--blurple-75-hsl)/1); - --blurple-75-hsl: 230.37 calc(var(--saturation-factor, 1)*50.943%) 31.176%; - --blurple-76: hsl(var(--blurple-76-hsl)/1); - --blurple-76-hsl: 230 calc(var(--saturation-factor, 1)*51.316%) 29.804%; - --blurple-77: hsl(var(--blurple-77-hsl)/1); - --blurple-77-hsl: 228.947 calc(var(--saturation-factor, 1)*52.055%) 28.627%; - --blurple-78: hsl(var(--blurple-78-hsl)/1); - --blurple-78-hsl: 228.649 calc(var(--saturation-factor, 1)*52.857%) 27.451%; - --blurple-79: hsl(var(--blurple-79-hsl)/1); - --blurple-79-hsl: 228.333 calc(var(--saturation-factor, 1)*53.731%) 26.275%; - --blurple-80: hsl(var(--blurple-80-hsl)/1); - --blurple-80-hsl: 228.696 calc(var(--saturation-factor, 1)*53.488%) 25.294%; - --blurple-81: hsl(var(--blurple-81-hsl)/1); - --blurple-81-hsl: 228.358 calc(var(--saturation-factor, 1)*54.472%) 24.118%; - --blurple-82: hsl(var(--blurple-82-hsl)/1); - --blurple-82-hsl: 228 calc(var(--saturation-factor, 1)*55.556%) 22.941%; - --blurple-83: hsl(var(--blurple-83-hsl)/1); - --blurple-83-hsl: 227.619 calc(var(--saturation-factor, 1)*56.757%) 21.765%; - --blurple-84: hsl(var(--blurple-84-hsl)/1); - --blurple-84-hsl: 228.197 calc(var(--saturation-factor, 1)*57.009%) 20.98%; - --blurple-85: hsl(var(--blurple-85-hsl)/1); - --blurple-85-hsl: 226.78 calc(var(--saturation-factor, 1)*58.416%) 19.804%; - --blurple-86: hsl(var(--blurple-86-hsl)/1); - --blurple-86-hsl: 227.143 calc(var(--saturation-factor, 1)*58.333%) 18.824%; - --blurple-87: hsl(var(--blurple-87-hsl)/1); - --blurple-87-hsl: 226.667 calc(var(--saturation-factor, 1)*60%) 17.647%; - --blurple-88: hsl(var(--blurple-88-hsl)/1); - --blurple-88-hsl: 226.415 calc(var(--saturation-factor, 1)*62.353%) 16.667%; - --blurple-89: hsl(var(--blurple-89-hsl)/1); - --blurple-89-hsl: 226.8 calc(var(--saturation-factor, 1)*62.5%) 15.686%; - --blurple-90: hsl(var(--blurple-90-hsl)/1); - --blurple-90-hsl: 225 calc(var(--saturation-factor, 1)*64.865%) 14.51%; - --blurple-91: hsl(var(--blurple-91-hsl)/1); - --blurple-91-hsl: 225.652 calc(var(--saturation-factor, 1)*65.714%) 13.725%; - --blurple-92: hsl(var(--blurple-92-hsl)/1); - --blurple-92-hsl: 225 calc(var(--saturation-factor, 1)*68.75%) 12.549%; - --blurple-93: hsl(var(--blurple-93-hsl)/1); - --blurple-93-hsl: 225.366 calc(var(--saturation-factor, 1)*69.492%) 11.569%; - --blurple-94: hsl(var(--blurple-94-hsl)/1); - --blurple-94-hsl: 225.789 calc(var(--saturation-factor, 1)*70.37%) 10.588%; - --blurple-95: hsl(var(--blurple-95-hsl)/1); - --blurple-95-hsl: 224.571 calc(var(--saturation-factor, 1)*74.468%) 9.216%; - --blurple-96: hsl(var(--blurple-96-hsl)/1); - --blurple-96-hsl: 224.516 calc(var(--saturation-factor, 1)*75.61%) 8.039%; - --blurple-97: hsl(var(--blurple-97-hsl)/1); - --blurple-97-hsl: 226.154 calc(var(--saturation-factor, 1)*76.471%) 6.667%; - --blurple-98: hsl(var(--blurple-98-hsl)/1); - --blurple-98-hsl: 227.368 calc(var(--saturation-factor, 1)*76%) 4.902%; - --blurple-99: hsl(var(--blurple-99-hsl)/1); - --blurple-99-hsl: 229.091 calc(var(--saturation-factor, 1)*84.615%) 2.549%; - --blurple-100: hsl(var(--blurple-100-hsl)/1); - --blurple-100-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --green-new-1: hsl(var(--green-new-1-hsl)/1); - --green-new-1-hsl: 137.455 calc(var(--saturation-factor, 1)*100%) 89.216%; - --green-new-2: hsl(var(--green-new-2-hsl)/1); - --green-new-2-hsl: 136.552 calc(var(--saturation-factor, 1)*100%) 88.627%; - --green-new-3: hsl(var(--green-new-3-hsl)/1); - --green-new-3-hsl: 136.271 calc(var(--saturation-factor, 1)*90.769%) 87.255%; - --green-new-4: hsl(var(--green-new-4-hsl)/1); - --green-new-4-hsl: 137 calc(var(--saturation-factor, 1)*83.333%) 85.882%; - --green-new-5: hsl(var(--green-new-5-hsl)/1); - --green-new-5-hsl: 135.738 calc(var(--saturation-factor, 1)*79.221%) 84.902%; - --green-new-6: hsl(var(--green-new-6-hsl)/1); - --green-new-6-hsl: 136.452 calc(var(--saturation-factor, 1)*73.81%) 83.529%; - --green-new-7: hsl(var(--green-new-7-hsl)/1); - --green-new-7-hsl: 135.938 calc(var(--saturation-factor, 1)*71.111%) 82.353%; - --green-new-8: hsl(var(--green-new-8-hsl)/1); - --green-new-8-hsl: 135.938 calc(var(--saturation-factor, 1)*66.667%) 81.176%; - --green-new-9: hsl(var(--green-new-9-hsl)/1); - --green-new-9-hsl: 136.364 calc(var(--saturation-factor, 1)*64.706%) 80%; - --green-new-10: hsl(var(--green-new-10-hsl)/1); - --green-new-10-hsl: 136.364 calc(var(--saturation-factor, 1)*61.111%) 78.824%; - --green-new-11: hsl(var(--green-new-11-hsl)/1); - --green-new-11-hsl: 135.882 calc(var(--saturation-factor, 1)*59.649%) 77.647%; - --green-new-12: hsl(var(--green-new-12-hsl)/1); - --green-new-12-hsl: 136.522 calc(var(--saturation-factor, 1)*57.025%) 76.275%; - --green-new-13: hsl(var(--green-new-13-hsl)/1); - --green-new-13-hsl: 136.522 calc(var(--saturation-factor, 1)*54.331%) 75.098%; - --green-new-14: hsl(var(--green-new-14-hsl)/1); - --green-new-14-hsl: 136.901 calc(var(--saturation-factor, 1)*53.383%) 73.922%; - --green-new-15: hsl(var(--green-new-15-hsl)/1); - --green-new-15-hsl: 136.056 calc(var(--saturation-factor, 1)*51.079%) 72.745%; - --green-new-16: hsl(var(--green-new-16-hsl)/1); - --green-new-16-hsl: 136.438 calc(var(--saturation-factor, 1)*50.345%) 71.569%; - --green-new-17: hsl(var(--green-new-17-hsl)/1); - --green-new-17-hsl: 136.438 calc(var(--saturation-factor, 1)*48.344%) 70.392%; - --green-new-18: hsl(var(--green-new-18-hsl)/1); - --green-new-18-hsl: 136.8 calc(var(--saturation-factor, 1)*47.771%) 69.216%; - --green-new-19: hsl(var(--green-new-19-hsl)/1); - --green-new-19-hsl: 136.8 calc(var(--saturation-factor, 1)*46.012%) 68.039%; - --green-new-20: hsl(var(--green-new-20-hsl)/1); - --green-new-20-hsl: 137.368 calc(var(--saturation-factor, 1)*44.706%) 66.667%; - --green-new-21: hsl(var(--green-new-21-hsl)/1); - --green-new-21-hsl: 137.143 calc(var(--saturation-factor, 1)*44%) 65.686%; - --green-new-22: hsl(var(--green-new-22-hsl)/1); - --green-new-22-hsl: 137.143 calc(var(--saturation-factor, 1)*42.541%) 64.51%; - --green-new-23: hsl(var(--green-new-23-hsl)/1); - --green-new-23-hsl: 137.468 calc(var(--saturation-factor, 1)*42.246%) 63.333%; - --green-new-24: hsl(var(--green-new-24-hsl)/1); - --green-new-24-hsl: 137.468 calc(var(--saturation-factor, 1)*40.933%) 62.157%; - --green-new-25: hsl(var(--green-new-25-hsl)/1); - --green-new-25-hsl: 137.778 calc(var(--saturation-factor, 1)*40.704%) 60.98%; - --green-new-26: hsl(var(--green-new-26-hsl)/1); - --green-new-26-hsl: 137.778 calc(var(--saturation-factor, 1)*39.512%) 59.804%; - --green-new-27: hsl(var(--green-new-27-hsl)/1); - --green-new-27-hsl: 138.072 calc(var(--saturation-factor, 1)*39.336%) 58.627%; - --green-new-28: hsl(var(--green-new-28-hsl)/1); - --green-new-28-hsl: 138.072 calc(var(--saturation-factor, 1)*38.249%) 57.451%; - --green-new-29: hsl(var(--green-new-29-hsl)/1); - --green-new-29-hsl: 138.353 calc(var(--saturation-factor, 1)*38.117%) 56.275%; - --green-new-30: hsl(var(--green-new-30-hsl)/1); - --green-new-30-hsl: 138.353 calc(var(--saturation-factor, 1)*37.118%) 55.098%; - --green-new-31: hsl(var(--green-new-31-hsl)/1); - --green-new-31-hsl: 138.837 calc(var(--saturation-factor, 1)*36.441%) 53.725%; - --green-new-32: hsl(var(--green-new-32-hsl)/1); - --green-new-32-hsl: 139.31 calc(var(--saturation-factor, 1)*36.1%) 52.745%; - --green-new-33: hsl(var(--green-new-33-hsl)/1); - --green-new-33-hsl: 139.773 calc(var(--saturation-factor, 1)*35.484%) 51.373%; - --green-new-34: hsl(var(--green-new-34-hsl)/1); - --green-new-34-hsl: 140 calc(var(--saturation-factor, 1)*35.433%) 50.196%; - --green-new-35: hsl(var(--green-new-35-hsl)/1); - --green-new-35-hsl: 140 calc(var(--saturation-factor, 1)*36%) 49.02%; - --green-new-36: hsl(var(--green-new-36-hsl)/1); - --green-new-36-hsl: 140.217 calc(var(--saturation-factor, 1)*37.705%) 47.843%; - --green-new-37: hsl(var(--green-new-37-hsl)/1); - --green-new-37-hsl: 140.645 calc(var(--saturation-factor, 1)*39.241%) 46.471%; - --green-new-38: hsl(var(--green-new-38-hsl)/1); - --green-new-38-hsl: 141.064 calc(var(--saturation-factor, 1)*40.517%) 45.49%; - --green-new-39: hsl(var(--green-new-39-hsl)/1); - --green-new-39-hsl: 141.474 calc(var(--saturation-factor, 1)*42.222%) 44.118%; - --green-new-40: hsl(var(--green-new-40-hsl)/1); - --green-new-40-hsl: 141.649 calc(var(--saturation-factor, 1)*44.292%) 42.941%; - --green-new-41: hsl(var(--green-new-41-hsl)/1); - --green-new-41-hsl: 142.041 calc(var(--saturation-factor, 1)*46.226%) 41.569%; - --green-new-42: hsl(var(--green-new-42-hsl)/1); - --green-new-42-hsl: 143.168 calc(var(--saturation-factor, 1)*49.268%) 40.196%; - --green-new-43: hsl(var(--green-new-43-hsl)/1); - --green-new-43-hsl: 143.529 calc(var(--saturation-factor, 1)*51.515%) 38.824%; - --green-new-44: hsl(var(--green-new-44-hsl)/1); - --green-new-44-hsl: 144 calc(var(--saturation-factor, 1)*54.974%) 37.451%; - --green-new-45: hsl(var(--green-new-45-hsl)/1); - --green-new-45-hsl: 145.234 calc(var(--saturation-factor, 1)*58.47%) 35.882%; - --green-new-46: hsl(var(--green-new-46-hsl)/1); - --green-new-46-hsl: 145.636 calc(var(--saturation-factor, 1)*62.5%) 34.51%; - --green-new-47: hsl(var(--green-new-47-hsl)/1); - --green-new-47-hsl: 146.316 calc(var(--saturation-factor, 1)*67.857%) 32.941%; - --green-new-48: hsl(var(--green-new-48-hsl)/1); - --green-new-48-hsl: 147.731 calc(var(--saturation-factor, 1)*75.796%) 30.784%; - --green-new-49: hsl(var(--green-new-49-hsl)/1); - --green-new-49-hsl: 149.764 calc(var(--saturation-factor, 1)*87.586%) 28.431%; - --green-new-50: hsl(var(--green-new-50-hsl)/1); - --green-new-50-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --green-new-51: hsl(var(--green-new-51-hsl)/1); - --green-new-51-hsl: 151.145 calc(var(--saturation-factor, 1)*100%) 25.686%; - --green-new-52: hsl(var(--green-new-52-hsl)/1); - --green-new-52-hsl: 151.406 calc(var(--saturation-factor, 1)*100%) 25.098%; - --green-new-53: hsl(var(--green-new-53-hsl)/1); - --green-new-53-hsl: 150.968 calc(var(--saturation-factor, 1)*98.413%) 24.706%; - --green-new-54: hsl(var(--green-new-54-hsl)/1); - --green-new-54-hsl: 150.984 calc(var(--saturation-factor, 1)*98.387%) 24.314%; - --green-new-55: hsl(var(--green-new-55-hsl)/1); - --green-new-55-hsl: 150.756 calc(var(--saturation-factor, 1)*98.347%) 23.725%; - --green-new-56: hsl(var(--green-new-56-hsl)/1); - --green-new-56-hsl: 150.769 calc(var(--saturation-factor, 1)*98.319%) 23.333%; - --green-new-57: hsl(var(--green-new-57-hsl)/1); - --green-new-57-hsl: 151.053 calc(var(--saturation-factor, 1)*98.276%) 22.745%; - --green-new-58: hsl(var(--green-new-58-hsl)/1); - --green-new-58-hsl: 150.536 calc(var(--saturation-factor, 1)*98.246%) 22.353%; - --green-new-59: hsl(var(--green-new-59-hsl)/1); - --green-new-59-hsl: 150.826 calc(var(--saturation-factor, 1)*98.198%) 21.765%; - --green-new-60: hsl(var(--green-new-60-hsl)/1); - --green-new-60-hsl: 150.556 calc(var(--saturation-factor, 1)*100%) 21.176%; - --green-new-61: hsl(var(--green-new-61-hsl)/1); - --green-new-61-hsl: 150.857 calc(var(--saturation-factor, 1)*100%) 20.588%; - --green-new-62: hsl(var(--green-new-62-hsl)/1); - --green-new-62-hsl: 150.291 calc(var(--saturation-factor, 1)*100%) 20.196%; - --green-new-63: hsl(var(--green-new-63-hsl)/1); - --green-new-63-hsl: 150.6 calc(var(--saturation-factor, 1)*100%) 19.608%; - --green-new-64: hsl(var(--green-new-64-hsl)/1); - --green-new-64-hsl: 150 calc(var(--saturation-factor, 1)*100%) 19.216%; - --green-new-65: hsl(var(--green-new-65-hsl)/1); - --green-new-65-hsl: 150.316 calc(var(--saturation-factor, 1)*100%) 18.627%; - --green-new-66: hsl(var(--green-new-66-hsl)/1); - --green-new-66-hsl: 149.677 calc(var(--saturation-factor, 1)*100%) 18.235%; - --green-new-67: hsl(var(--green-new-67-hsl)/1); - --green-new-67-hsl: 149.67 calc(var(--saturation-factor, 1)*100%) 17.843%; - --green-new-68: hsl(var(--green-new-68-hsl)/1); - --green-new-68-hsl: 149.318 calc(var(--saturation-factor, 1)*100%) 17.255%; - --green-new-69: hsl(var(--green-new-69-hsl)/1); - --green-new-69-hsl: 149.302 calc(var(--saturation-factor, 1)*100%) 16.863%; - --green-new-70: hsl(var(--green-new-70-hsl)/1); - --green-new-70-hsl: 148.571 calc(var(--saturation-factor, 1)*100%) 16.471%; - --green-new-71: hsl(var(--green-new-71-hsl)/1); - --green-new-71-hsl: 148.889 calc(var(--saturation-factor, 1)*100%) 15.882%; - --green-new-72: hsl(var(--green-new-72-hsl)/1); - --green-new-72-hsl: 148.101 calc(var(--saturation-factor, 1)*100%) 15.49%; - --green-new-73: hsl(var(--green-new-73-hsl)/1); - --green-new-73-hsl: 148.052 calc(var(--saturation-factor, 1)*100%) 15.098%; - --green-new-74: hsl(var(--green-new-74-hsl)/1); - --green-new-74-hsl: 147.568 calc(var(--saturation-factor, 1)*100%) 14.51%; - --green-new-75: hsl(var(--green-new-75-hsl)/1); - --green-new-75-hsl: 147.5 calc(var(--saturation-factor, 1)*100%) 14.118%; - --green-new-76: hsl(var(--green-new-76-hsl)/1); - --green-new-76-hsl: 146.571 calc(var(--saturation-factor, 1)*100%) 13.725%; - --green-new-77: hsl(var(--green-new-77-hsl)/1); - --green-new-77-hsl: 146.866 calc(var(--saturation-factor, 1)*100%) 13.137%; - --green-new-78: hsl(var(--green-new-78-hsl)/1); - --green-new-78-hsl: 145.846 calc(var(--saturation-factor, 1)*100%) 12.745%; - --green-new-79: hsl(var(--green-new-79-hsl)/1); - --green-new-79-hsl: 145.714 calc(var(--saturation-factor, 1)*100%) 12.353%; - --green-new-80: hsl(var(--green-new-80-hsl)/1); - --green-new-80-hsl: 144.59 calc(var(--saturation-factor, 1)*100%) 11.961%; - --green-new-81: hsl(var(--green-new-81-hsl)/1); - --green-new-81-hsl: 144.407 calc(var(--saturation-factor, 1)*100%) 11.569%; - --green-new-82: hsl(var(--green-new-82-hsl)/1); - --green-new-82-hsl: 143.571 calc(var(--saturation-factor, 1)*100%) 10.98%; - --green-new-83: hsl(var(--green-new-83-hsl)/1); - --green-new-83-hsl: 143.333 calc(var(--saturation-factor, 1)*100%) 10.588%; - --green-new-84: hsl(var(--green-new-84-hsl)/1); - --green-new-84-hsl: 143.077 calc(var(--saturation-factor, 1)*100%) 10.196%; - --green-new-85: hsl(var(--green-new-85-hsl)/1); - --green-new-85-hsl: 141.6 calc(var(--saturation-factor, 1)*100%) 9.804%; - --green-new-86: hsl(var(--green-new-86-hsl)/1); - --green-new-86-hsl: 141.25 calc(var(--saturation-factor, 1)*100%) 9.412%; - --green-new-87: hsl(var(--green-new-87-hsl)/1); - --green-new-87-hsl: 139.565 calc(var(--saturation-factor, 1)*100%) 9.02%; - --green-new-88: hsl(var(--green-new-88-hsl)/1); - --green-new-88-hsl: 139.535 calc(var(--saturation-factor, 1)*100%) 8.431%; - --green-new-89: hsl(var(--green-new-89-hsl)/1); - --green-new-89-hsl: 139.024 calc(var(--saturation-factor, 1)*100%) 8.039%; - --green-new-90: hsl(var(--green-new-90-hsl)/1); - --green-new-90-hsl: 138.462 calc(var(--saturation-factor, 1)*100%) 7.647%; - --green-new-91: hsl(var(--green-new-91-hsl)/1); - --green-new-91-hsl: 136.216 calc(var(--saturation-factor, 1)*100%) 7.255%; - --green-new-92: hsl(var(--green-new-92-hsl)/1); - --green-new-92-hsl: 135.429 calc(var(--saturation-factor, 1)*100%) 6.863%; - --green-new-93: hsl(var(--green-new-93-hsl)/1); - --green-new-93-hsl: 134.545 calc(var(--saturation-factor, 1)*100%) 6.471%; - --green-new-94: hsl(var(--green-new-94-hsl)/1); - --green-new-94-hsl: 133.548 calc(var(--saturation-factor, 1)*100%) 6.078%; - --green-new-95: hsl(var(--green-new-95-hsl)/1); - --green-new-95-hsl: 132.414 calc(var(--saturation-factor, 1)*100%) 5.686%; - --green-new-96: hsl(var(--green-new-96-hsl)/1); - --green-new-96-hsl: 131.111 calc(var(--saturation-factor, 1)*100%) 5.294%; - --green-new-97: hsl(var(--green-new-97-hsl)/1); - --green-new-97-hsl: 129.6 calc(var(--saturation-factor, 1)*100%) 4.902%; - --green-new-98: hsl(var(--green-new-98-hsl)/1); - --green-new-98-hsl: 128.182 calc(var(--saturation-factor, 1)*100%) 4.314%; - --green-new-99: hsl(var(--green-new-99-hsl)/1); - --green-new-99-hsl: 129.474 calc(var(--saturation-factor, 1)*100%) 3.725%; - --green-new-100: hsl(var(--green-new-100-hsl)/1); - --green-new-100-hsl: 127.5 calc(var(--saturation-factor, 1)*100%) 3.137%; - --red-new-1: hsl(var(--red-new-1-hsl)/1); - --red-new-1-hsl: 1.765 calc(var(--saturation-factor, 1)*100%) 93.333%; - --red-new-2: hsl(var(--red-new-2-hsl)/1); - --red-new-2-hsl: 1.579 calc(var(--saturation-factor, 1)*100%) 92.549%; - --red-new-3: hsl(var(--red-new-3-hsl)/1); - --red-new-3-hsl: 1.429 calc(var(--saturation-factor, 1)*100%) 91.765%; - --red-new-4: hsl(var(--red-new-4-hsl)/1); - --red-new-4-hsl: 2.609 calc(var(--saturation-factor, 1)*100%) 90.98%; - --red-new-5: hsl(var(--red-new-5-hsl)/1); - --red-new-5-hsl: 2.4 calc(var(--saturation-factor, 1)*100%) 90.196%; - --red-new-6: hsl(var(--red-new-6-hsl)/1); - --red-new-6-hsl: 2.222 calc(var(--saturation-factor, 1)*100%) 89.412%; - --red-new-7: hsl(var(--red-new-7-hsl)/1); - --red-new-7-hsl: 3.103 calc(var(--saturation-factor, 1)*100%) 88.627%; - --red-new-8: hsl(var(--red-new-8-hsl)/1); - --red-new-8-hsl: 2.903 calc(var(--saturation-factor, 1)*100%) 87.843%; - --red-new-9: hsl(var(--red-new-9-hsl)/1); - --red-new-9-hsl: 2.727 calc(var(--saturation-factor, 1)*100%) 87.059%; - --red-new-10: hsl(var(--red-new-10-hsl)/1); - --red-new-10-hsl: 3.429 calc(var(--saturation-factor, 1)*100%) 86.275%; - --red-new-11: hsl(var(--red-new-11-hsl)/1); - --red-new-11-hsl: 3.243 calc(var(--saturation-factor, 1)*100%) 85.49%; - --red-new-12: hsl(var(--red-new-12-hsl)/1); - --red-new-12-hsl: 3.846 calc(var(--saturation-factor, 1)*100%) 84.706%; - --red-new-13: hsl(var(--red-new-13-hsl)/1); - --red-new-13-hsl: 2.963 calc(var(--saturation-factor, 1)*100%) 84.118%; - --red-new-14: hsl(var(--red-new-14-hsl)/1); - --red-new-14-hsl: 2.824 calc(var(--saturation-factor, 1)*100%) 83.333%; - --red-new-15: hsl(var(--red-new-15-hsl)/1); - --red-new-15-hsl: 3.371 calc(var(--saturation-factor, 1)*100%) 82.549%; - --red-new-16: hsl(var(--red-new-16-hsl)/1); - --red-new-16-hsl: 2.609 calc(var(--saturation-factor, 1)*100%) 81.961%; - --red-new-17: hsl(var(--red-new-17-hsl)/1); - --red-new-17-hsl: 3.125 calc(var(--saturation-factor, 1)*100%) 81.176%; - --red-new-18: hsl(var(--red-new-18-hsl)/1); - --red-new-18-hsl: 3 calc(var(--saturation-factor, 1)*100%) 80.392%; - --red-new-19: hsl(var(--red-new-19-hsl)/1); - --red-new-19-hsl: 2.913 calc(var(--saturation-factor, 1)*100%) 79.804%; - --red-new-20: hsl(var(--red-new-20-hsl)/1); - --red-new-20-hsl: 3.364 calc(var(--saturation-factor, 1)*100%) 79.02%; - --red-new-21: hsl(var(--red-new-21-hsl)/1); - --red-new-21-hsl: 2.727 calc(var(--saturation-factor, 1)*100%) 78.431%; - --red-new-22: hsl(var(--red-new-22-hsl)/1); - --red-new-22-hsl: 2.655 calc(var(--saturation-factor, 1)*100%) 77.843%; - --red-new-23: hsl(var(--red-new-23-hsl)/1); - --red-new-23-hsl: 2.586 calc(var(--saturation-factor, 1)*98.305%) 76.863%; - --red-new-24: hsl(var(--red-new-24-hsl)/1); - --red-new-24-hsl: 2.542 calc(var(--saturation-factor, 1)*96.721%) 76.078%; - --red-new-25: hsl(var(--red-new-25-hsl)/1); - --red-new-25-hsl: 2 calc(var(--saturation-factor, 1)*95.238%) 75.294%; - --red-new-26: hsl(var(--red-new-26-hsl)/1); - --red-new-26-hsl: 2.439 calc(var(--saturation-factor, 1)*93.893%) 74.314%; - --red-new-27: hsl(var(--red-new-27-hsl)/1); - --red-new-27-hsl: 2.4 calc(var(--saturation-factor, 1)*92.593%) 73.529%; - --red-new-28: hsl(var(--red-new-28-hsl)/1); - --red-new-28-hsl: 1.905 calc(var(--saturation-factor, 1)*90%) 72.549%; - --red-new-29: hsl(var(--red-new-29-hsl)/1); - --red-new-29-hsl: 1.875 calc(var(--saturation-factor, 1)*88.889%) 71.765%; - --red-new-30: hsl(var(--red-new-30-hsl)/1); - --red-new-30-hsl: 1.385 calc(var(--saturation-factor, 1)*87.838%) 70.98%; - --red-new-31: hsl(var(--red-new-31-hsl)/1); - --red-new-31-hsl: 1.818 calc(var(--saturation-factor, 1)*85.714%) 69.804%; - --red-new-32: hsl(var(--red-new-32-hsl)/1); - --red-new-32-hsl: 1.343 calc(var(--saturation-factor, 1)*84.81%) 69.02%; - --red-new-33: hsl(var(--red-new-33-hsl)/1); - --red-new-33-hsl: 1.333 calc(var(--saturation-factor, 1)*82.822%) 68.039%; - --red-new-34: hsl(var(--red-new-34-hsl)/1); - --red-new-34-hsl: 1.314 calc(var(--saturation-factor, 1)*82.036%) 67.255%; - --red-new-35: hsl(var(--red-new-35-hsl)/1); - --red-new-35-hsl: 0.87 calc(var(--saturation-factor, 1)*80.233%) 66.275%; - --red-new-36: hsl(var(--red-new-36-hsl)/1); - --red-new-36-hsl: 0.863 calc(var(--saturation-factor, 1)*78.531%) 65.294%; - --red-new-37: hsl(var(--red-new-37-hsl)/1); - --red-new-37-hsl: 0.426 calc(var(--saturation-factor, 1)*77.901%) 64.51%; - --red-new-38: hsl(var(--red-new-38-hsl)/1); - --red-new-38-hsl: 0.423 calc(var(--saturation-factor, 1)*76.344%) 63.529%; - --red-new-39: hsl(var(--red-new-39-hsl)/1); - --red-new-39-hsl: 0 calc(var(--saturation-factor, 1)*74.869%) 62.549%; - --red-new-40: hsl(var(--red-new-40-hsl)/1); - --red-new-40-hsl: 359.586 calc(var(--saturation-factor, 1)*73.604%) 61.373%; - --red-new-41: hsl(var(--red-new-41-hsl)/1); - --red-new-41-hsl: 359.592 calc(var(--saturation-factor, 1)*73.134%) 60.588%; - --red-new-42: hsl(var(--red-new-42-hsl)/1); - --red-new-42-hsl: 358.792 calc(var(--saturation-factor, 1)*71.981%) 59.412%; - --red-new-43: hsl(var(--red-new-43-hsl)/1); - --red-new-43-hsl: 358.411 calc(var(--saturation-factor, 1)*70.892%) 58.235%; - --red-new-44: hsl(var(--red-new-44-hsl)/1); - --red-new-44-hsl: 358.421 calc(var(--saturation-factor, 1)*69.725%) 57.255%; - --red-new-45: hsl(var(--red-new-45-hsl)/1); - --red-new-45-hsl: 358.052 calc(var(--saturation-factor, 1)*68.75%) 56.078%; - --red-new-46: hsl(var(--red-new-46-hsl)/1); - --red-new-46-hsl: 357.692 calc(var(--saturation-factor, 1)*67.826%) 54.902%; - --red-new-47: hsl(var(--red-new-47-hsl)/1); - --red-new-47-hsl: 357.342 calc(var(--saturation-factor, 1)*66.949%) 53.725%; - --red-new-48: hsl(var(--red-new-48-hsl)/1); - --red-new-48-hsl: 356.625 calc(var(--saturation-factor, 1)*66.116%) 52.549%; - --red-new-49: hsl(var(--red-new-49-hsl)/1); - --red-new-49-hsl: 355.951 calc(var(--saturation-factor, 1)*65.462%) 51.176%; - --red-new-50: hsl(var(--red-new-50-hsl)/1); - --red-new-50-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --red-new-51: hsl(var(--red-new-51-hsl)/1); - --red-new-51-hsl: 355.556 calc(var(--saturation-factor, 1)*64.8%) 49.02%; - --red-new-52: hsl(var(--red-new-52-hsl)/1); - --red-new-52-hsl: 355.472 calc(var(--saturation-factor, 1)*64.898%) 48.039%; - --red-new-53: hsl(var(--red-new-53-hsl)/1); - --red-new-53-hsl: 355.355 calc(var(--saturation-factor, 1)*64.854%) 46.863%; - --red-new-54: hsl(var(--red-new-54-hsl)/1); - --red-new-54-hsl: 355.263 calc(var(--saturation-factor, 1)*64.957%) 45.882%; - --red-new-55: hsl(var(--red-new-55-hsl)/1); - --red-new-55-hsl: 355.541 calc(var(--saturation-factor, 1)*64.348%) 45.098%; - --red-new-56: hsl(var(--red-new-56-hsl)/1); - --red-new-56-hsl: 355.068 calc(var(--saturation-factor, 1)*65.179%) 43.922%; - --red-new-57: hsl(var(--red-new-57-hsl)/1); - --red-new-57-hsl: 355.385 calc(var(--saturation-factor, 1)*65.297%) 42.941%; - --red-new-58: hsl(var(--red-new-58-hsl)/1); - --red-new-58-hsl: 355.286 calc(var(--saturation-factor, 1)*65.421%) 41.961%; - --red-new-59: hsl(var(--red-new-59-hsl)/1); - --red-new-59-hsl: 355.182 calc(var(--saturation-factor, 1)*65.55%) 40.98%; - --red-new-60: hsl(var(--red-new-60-hsl)/1); - --red-new-60-hsl: 355.075 calc(var(--saturation-factor, 1)*65.686%) 40%; - --red-new-61: hsl(var(--red-new-61-hsl)/1); - --red-new-61-hsl: 355 calc(var(--saturation-factor, 1)*66%) 39.216%; - --red-new-62: hsl(var(--red-new-62-hsl)/1); - --red-new-62-hsl: 355.349 calc(var(--saturation-factor, 1)*66.154%) 38.235%; - --red-new-63: hsl(var(--red-new-63-hsl)/1); - --red-new-63-hsl: 354.803 calc(var(--saturation-factor, 1)*67.196%) 37.059%; - --red-new-64: hsl(var(--red-new-64-hsl)/1); - --red-new-64-hsl: 354.677 calc(var(--saturation-factor, 1)*67.391%) 36.078%; - --red-new-65: hsl(var(--red-new-65-hsl)/1); - --red-new-65-hsl: 354.59 calc(var(--saturation-factor, 1)*67.778%) 35.294%; - --red-new-66: hsl(var(--red-new-66-hsl)/1); - --red-new-66-hsl: 354.5 calc(var(--saturation-factor, 1)*68.966%) 34.118%; - --red-new-67: hsl(var(--red-new-67-hsl)/1); - --red-new-67-hsl: 354.359 calc(var(--saturation-factor, 1)*69.231%) 33.137%; - --red-new-68: hsl(var(--red-new-68-hsl)/1); - --red-new-68-hsl: 353.793 calc(var(--saturation-factor, 1)*70.732%) 32.157%; - --red-new-69: hsl(var(--red-new-69-hsl)/1); - --red-new-69-hsl: 353.628 calc(var(--saturation-factor, 1)*71.069%) 31.176%; - --red-new-70: hsl(var(--red-new-70-hsl)/1); - --red-new-70-hsl: 354.054 calc(var(--saturation-factor, 1)*71.613%) 30.392%; - --red-new-71: hsl(var(--red-new-71-hsl)/1); - --red-new-71-hsl: 353.394 calc(var(--saturation-factor, 1)*73.154%) 29.216%; - --red-new-72: hsl(var(--red-new-72-hsl)/1); - --red-new-72-hsl: 353.271 calc(var(--saturation-factor, 1)*73.793%) 28.431%; - --red-new-73: hsl(var(--red-new-73-hsl)/1); - --red-new-73-hsl: 352.571 calc(var(--saturation-factor, 1)*75.54%) 27.255%; - --red-new-74: hsl(var(--red-new-74-hsl)/1); - --red-new-74-hsl: 353.01 calc(var(--saturation-factor, 1)*76.296%) 26.471%; - --red-new-75: hsl(var(--red-new-75-hsl)/1); - --red-new-75-hsl: 352.277 calc(var(--saturation-factor, 1)*78.295%) 25.294%; - --red-new-76: hsl(var(--red-new-76-hsl)/1); - --red-new-76-hsl: 351.6 calc(var(--saturation-factor, 1)*80.645%) 24.314%; - --red-new-77: hsl(var(--red-new-77-hsl)/1); - --red-new-77-hsl: 351.34 calc(var(--saturation-factor, 1)*81.513%) 23.333%; - --red-new-78: hsl(var(--red-new-78-hsl)/1); - --red-new-78-hsl: 351.25 calc(var(--saturation-factor, 1)*84.211%) 22.353%; - --red-new-79: hsl(var(--red-new-79-hsl)/1); - --red-new-79-hsl: 351.064 calc(var(--saturation-factor, 1)*85.455%) 21.569%; - --red-new-80: hsl(var(--red-new-80-hsl)/1); - --red-new-80-hsl: 350.217 calc(var(--saturation-factor, 1)*88.462%) 20.392%; - --red-new-81: hsl(var(--red-new-81-hsl)/1); - --red-new-81-hsl: 350 calc(var(--saturation-factor, 1)*90%) 19.608%; - --red-new-82: hsl(var(--red-new-82-hsl)/1); - --red-new-82-hsl: 349.773 calc(var(--saturation-factor, 1)*91.667%) 18.824%; - --red-new-83: hsl(var(--red-new-83-hsl)/1); - --red-new-83-hsl: 349.412 calc(var(--saturation-factor, 1)*93.407%) 17.843%; - --red-new-84: hsl(var(--red-new-84-hsl)/1); - --red-new-84-hsl: 349.157 calc(var(--saturation-factor, 1)*95.402%) 17.059%; - --red-new-85: hsl(var(--red-new-85-hsl)/1); - --red-new-85-hsl: 348.889 calc(var(--saturation-factor, 1)*97.59%) 16.275%; - --red-new-86: hsl(var(--red-new-86-hsl)/1); - --red-new-86-hsl: 348.608 calc(var(--saturation-factor, 1)*100%) 15.49%; - --red-new-87: hsl(var(--red-new-87-hsl)/1); - --red-new-87-hsl: 348.8 calc(var(--saturation-factor, 1)*100%) 14.706%; - --red-new-88: hsl(var(--red-new-88-hsl)/1); - --red-new-88-hsl: 349.167 calc(var(--saturation-factor, 1)*100%) 14.118%; - --red-new-89: hsl(var(--red-new-89-hsl)/1); - --red-new-89-hsl: 349.565 calc(var(--saturation-factor, 1)*100%) 13.529%; - --red-new-90: hsl(var(--red-new-90-hsl)/1); - --red-new-90-hsl: 350 calc(var(--saturation-factor, 1)*100%) 12.941%; - --red-new-91: hsl(var(--red-new-91-hsl)/1); - --red-new-91-hsl: 350.476 calc(var(--saturation-factor, 1)*100%) 12.353%; - --red-new-92: hsl(var(--red-new-92-hsl)/1); - --red-new-92-hsl: 351 calc(var(--saturation-factor, 1)*100%) 11.765%; - --red-new-93: hsl(var(--red-new-93-hsl)/1); - --red-new-93-hsl: 351.579 calc(var(--saturation-factor, 1)*100%) 11.176%; - --red-new-94: hsl(var(--red-new-94-hsl)/1); - --red-new-94-hsl: 352.075 calc(var(--saturation-factor, 1)*100%) 10.392%; - --red-new-95: hsl(var(--red-new-95-hsl)/1); - --red-new-95-hsl: 351.6 calc(var(--saturation-factor, 1)*100%) 9.804%; - --red-new-96: hsl(var(--red-new-96-hsl)/1); - --red-new-96-hsl: 352 calc(var(--saturation-factor, 1)*100%) 8.824%; - --red-new-97: hsl(var(--red-new-97-hsl)/1); - --red-new-97-hsl: 352.683 calc(var(--saturation-factor, 1)*100%) 8.039%; - --red-new-98: hsl(var(--red-new-98-hsl)/1); - --red-new-98-hsl: 353.143 calc(var(--saturation-factor, 1)*100%) 6.863%; - --red-new-99: hsl(var(--red-new-99-hsl)/1); - --red-new-99-hsl: 351.724 calc(var(--saturation-factor, 1)*100%) 5.686%; - --red-new-100: hsl(var(--red-new-100-hsl)/1); - --red-new-100-hsl: 354 calc(var(--saturation-factor, 1)*100%) 3.922%; - --teal-new-1: hsl(var(--teal-new-1-hsl)/1); - --teal-new-1-hsl: 183.396 calc(var(--saturation-factor, 1)*100%) 89.608%; - --teal-new-2: hsl(var(--teal-new-2-hsl)/1); - --teal-new-2-hsl: 186.316 calc(var(--saturation-factor, 1)*100%) 88.824%; - --teal-new-3: hsl(var(--teal-new-3-hsl)/1); - --teal-new-3-hsl: 186.102 calc(var(--saturation-factor, 1)*93.651%) 87.647%; - --teal-new-4: hsl(var(--teal-new-4-hsl)/1); - --teal-new-4-hsl: 186.774 calc(var(--saturation-factor, 1)*88.571%) 86.275%; - --teal-new-5: hsl(var(--teal-new-5-hsl)/1); - --teal-new-5-hsl: 185.714 calc(var(--saturation-factor, 1)*81.818%) 84.902%; - --teal-new-6: hsl(var(--teal-new-6-hsl)/1); - --teal-new-6-hsl: 185.538 calc(var(--saturation-factor, 1)*78.313%) 83.725%; - --teal-new-7: hsl(var(--teal-new-7-hsl)/1); - --teal-new-7-hsl: 186.176 calc(var(--saturation-factor, 1)*75.556%) 82.353%; - --teal-new-8: hsl(var(--teal-new-8-hsl)/1); - --teal-new-8-hsl: 186 calc(var(--saturation-factor, 1)*72.917%) 81.176%; - --teal-new-9: hsl(var(--teal-new-9-hsl)/1); - --teal-new-9-hsl: 185.915 calc(var(--saturation-factor, 1)*68.932%) 79.804%; - --teal-new-10: hsl(var(--teal-new-10-hsl)/1); - --teal-new-10-hsl: 185.676 calc(var(--saturation-factor, 1)*67.273%) 78.431%; - --teal-new-11: hsl(var(--teal-new-11-hsl)/1); - --teal-new-11-hsl: 186.316 calc(var(--saturation-factor, 1)*65.517%) 77.255%; - --teal-new-12: hsl(var(--teal-new-12-hsl)/1); - --teal-new-12-hsl: 185.455 calc(var(--saturation-factor, 1)*62.602%) 75.882%; - --teal-new-13: hsl(var(--teal-new-13-hsl)/1); - --teal-new-13-hsl: 185.316 calc(var(--saturation-factor, 1)*61.24%) 74.706%; - --teal-new-14: hsl(var(--teal-new-14-hsl)/1); - --teal-new-14-hsl: 185.926 calc(var(--saturation-factor, 1)*60%) 73.529%; - --teal-new-15: hsl(var(--teal-new-15-hsl)/1); - --teal-new-15-hsl: 185.06 calc(var(--saturation-factor, 1)*58.042%) 71.961%; - --teal-new-16: hsl(var(--teal-new-16-hsl)/1); - --teal-new-16-hsl: 185.647 calc(var(--saturation-factor, 1)*57.047%) 70.784%; - --teal-new-17: hsl(var(--teal-new-17-hsl)/1); - --teal-new-17-hsl: 185.517 calc(var(--saturation-factor, 1)*56.129%) 69.608%; - --teal-new-18: hsl(var(--teal-new-18-hsl)/1); - --teal-new-18-hsl: 186.067 calc(var(--saturation-factor, 1)*55.28%) 68.431%; - --teal-new-19: hsl(var(--teal-new-19-hsl)/1); - --teal-new-19-hsl: 185.275 calc(var(--saturation-factor, 1)*53.846%) 66.863%; - --teal-new-20: hsl(var(--teal-new-20-hsl)/1); - --teal-new-20-hsl: 185.806 calc(var(--saturation-factor, 1)*53.143%) 65.686%; - --teal-new-21: hsl(var(--teal-new-21-hsl)/1); - --teal-new-21-hsl: 185.684 calc(var(--saturation-factor, 1)*52.486%) 64.51%; - --teal-new-22: hsl(var(--teal-new-22-hsl)/1); - --teal-new-22-hsl: 185 calc(var(--saturation-factor, 1)*51.064%) 63.137%; - --teal-new-23: hsl(var(--teal-new-23-hsl)/1); - --teal-new-23-hsl: 185.455 calc(var(--saturation-factor, 1)*50.769%) 61.765%; - --teal-new-24: hsl(var(--teal-new-24-hsl)/1); - --teal-new-24-hsl: 185.347 calc(var(--saturation-factor, 1)*50.249%) 60.588%; - --teal-new-25: hsl(var(--teal-new-25-hsl)/1); - --teal-new-25-hsl: 185.243 calc(var(--saturation-factor, 1)*49.282%) 59.02%; - --teal-new-26: hsl(var(--teal-new-26-hsl)/1); - --teal-new-26-hsl: 185.143 calc(var(--saturation-factor, 1)*48.837%) 57.843%; - --teal-new-27: hsl(var(--teal-new-27-hsl)/1); - --teal-new-27-hsl: 185.556 calc(var(--saturation-factor, 1)*48.649%) 56.471%; - --teal-new-28: hsl(var(--teal-new-28-hsl)/1); - --teal-new-28-hsl: 184.954 calc(var(--saturation-factor, 1)*47.598%) 55.098%; - --teal-new-29: hsl(var(--teal-new-29-hsl)/1); - --teal-new-29-hsl: 184.821 calc(var(--saturation-factor, 1)*47.458%) 53.725%; - --teal-new-30: hsl(var(--teal-new-30-hsl)/1); - --teal-new-30-hsl: 185.217 calc(var(--saturation-factor, 1)*47.325%) 52.353%; - --teal-new-31: hsl(var(--teal-new-31-hsl)/1); - --teal-new-31-hsl: 184.615 calc(var(--saturation-factor, 1)*46.614%) 50.784%; - --teal-new-32: hsl(var(--teal-new-32-hsl)/1); - --teal-new-32-hsl: 184.959 calc(var(--saturation-factor, 1)*48.207%) 49.216%; - --teal-new-33: hsl(var(--teal-new-33-hsl)/1); - --teal-new-33-hsl: 184.839 calc(var(--saturation-factor, 1)*50.82%) 47.843%; - --teal-new-34: hsl(var(--teal-new-34-hsl)/1); - --teal-new-34-hsl: 185.156 calc(var(--saturation-factor, 1)*54.237%) 46.275%; - --teal-new-35: hsl(var(--teal-new-35-hsl)/1); - --teal-new-35-hsl: 184.545 calc(var(--saturation-factor, 1)*58.407%) 44.314%; - --teal-new-36: hsl(var(--teal-new-36-hsl)/1); - --teal-new-36-hsl: 184.38 calc(var(--saturation-factor, 1)*63.134%) 42.549%; - --teal-new-37: hsl(var(--teal-new-37-hsl)/1); - --teal-new-37-hsl: 184.615 calc(var(--saturation-factor, 1)*69.082%) 40.588%; - --teal-new-38: hsl(var(--teal-new-38-hsl)/1); - --teal-new-38-hsl: 183.974 calc(var(--saturation-factor, 1)*78.238%) 37.843%; - --teal-new-39: hsl(var(--teal-new-39-hsl)/1); - --teal-new-39-hsl: 183.976 calc(var(--saturation-factor, 1)*95.402%) 34.118%; - --teal-new-40: hsl(var(--teal-new-40-hsl)/1); - --teal-new-40-hsl: 183.929 calc(var(--saturation-factor, 1)*100%) 32.941%; - --teal-new-41: hsl(var(--teal-new-41-hsl)/1); - --teal-new-41-hsl: 183.976 calc(var(--saturation-factor, 1)*100%) 32.549%; - --teal-new-42: hsl(var(--teal-new-42-hsl)/1); - --teal-new-42-hsl: 184.049 calc(var(--saturation-factor, 1)*100%) 31.961%; - --teal-new-43: hsl(var(--teal-new-43-hsl)/1); - --teal-new-43-hsl: 184.099 calc(var(--saturation-factor, 1)*100%) 31.569%; - --teal-new-44: hsl(var(--teal-new-44-hsl)/1); - --teal-new-44-hsl: 184.528 calc(var(--saturation-factor, 1)*100%) 31.176%; - --teal-new-45: hsl(var(--teal-new-45-hsl)/1); - --teal-new-45-hsl: 184.586 calc(var(--saturation-factor, 1)*100%) 30.784%; - --teal-new-46: hsl(var(--teal-new-46-hsl)/1); - --teal-new-46-hsl: 184.286 calc(var(--saturation-factor, 1)*100%) 30.196%; - --teal-new-47: hsl(var(--teal-new-47-hsl)/1); - --teal-new-47-hsl: 184.371 calc(var(--saturation-factor, 1)*100%) 29.608%; - --teal-new-48: hsl(var(--teal-new-48-hsl)/1); - --teal-new-48-hsl: 184.832 calc(var(--saturation-factor, 1)*100%) 29.216%; - --teal-new-49: hsl(var(--teal-new-49-hsl)/1); - --teal-new-49-hsl: 184.932 calc(var(--saturation-factor, 1)*100%) 28.627%; - --teal-new-50: hsl(var(--teal-new-50-hsl)/1); - --teal-new-50-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --teal-new-51: hsl(var(--teal-new-51-hsl)/1); - --teal-new-51-hsl: 184.714 calc(var(--saturation-factor, 1)*100%) 27.451%; - --teal-new-52: hsl(var(--teal-new-52-hsl)/1); - --teal-new-52-hsl: 184.783 calc(var(--saturation-factor, 1)*100%) 27.059%; - --teal-new-53: hsl(var(--teal-new-53-hsl)/1); - --teal-new-53-hsl: 184.889 calc(var(--saturation-factor, 1)*100%) 26.471%; - --teal-new-54: hsl(var(--teal-new-54-hsl)/1); - --teal-new-54-hsl: 184.545 calc(var(--saturation-factor, 1)*100%) 25.882%; - --teal-new-55: hsl(var(--teal-new-55-hsl)/1); - --teal-new-55-hsl: 185.077 calc(var(--saturation-factor, 1)*100%) 25.49%; - --teal-new-56: hsl(var(--teal-new-56-hsl)/1); - --teal-new-56-hsl: 184.724 calc(var(--saturation-factor, 1)*100%) 24.902%; - --teal-new-57: hsl(var(--teal-new-57-hsl)/1); - --teal-new-57-hsl: 185.28 calc(var(--saturation-factor, 1)*100%) 24.51%; - --teal-new-58: hsl(var(--teal-new-58-hsl)/1); - --teal-new-58-hsl: 184.918 calc(var(--saturation-factor, 1)*100%) 23.922%; - --teal-new-59: hsl(var(--teal-new-59-hsl)/1); - --teal-new-59-hsl: 185.5 calc(var(--saturation-factor, 1)*100%) 23.529%; - --teal-new-60: hsl(var(--teal-new-60-hsl)/1); - --teal-new-60-hsl: 185.128 calc(var(--saturation-factor, 1)*100%) 22.941%; - --teal-new-61: hsl(var(--teal-new-61-hsl)/1); - --teal-new-61-hsl: 185.739 calc(var(--saturation-factor, 1)*100%) 22.549%; - --teal-new-62: hsl(var(--teal-new-62-hsl)/1); - --teal-new-62-hsl: 185.357 calc(var(--saturation-factor, 1)*100%) 21.961%; - --teal-new-63: hsl(var(--teal-new-63-hsl)/1); - --teal-new-63-hsl: 186 calc(var(--saturation-factor, 1)*100%) 21.569%; - --teal-new-64: hsl(var(--teal-new-64-hsl)/1); - --teal-new-64-hsl: 185.607 calc(var(--saturation-factor, 1)*100%) 20.98%; - --teal-new-65: hsl(var(--teal-new-65-hsl)/1); - --teal-new-65-hsl: 186.286 calc(var(--saturation-factor, 1)*100%) 20.588%; - --teal-new-66: hsl(var(--teal-new-66-hsl)/1); - --teal-new-66-hsl: 185.882 calc(var(--saturation-factor, 1)*100%) 20%; - --teal-new-67: hsl(var(--teal-new-67-hsl)/1); - --teal-new-67-hsl: 186.6 calc(var(--saturation-factor, 1)*100%) 19.608%; - --teal-new-68: hsl(var(--teal-new-68-hsl)/1); - --teal-new-68-hsl: 186.186 calc(var(--saturation-factor, 1)*100%) 19.02%; - --teal-new-69: hsl(var(--teal-new-69-hsl)/1); - --teal-new-69-hsl: 186.316 calc(var(--saturation-factor, 1)*100%) 18.627%; - --teal-new-70: hsl(var(--teal-new-70-hsl)/1); - --teal-new-70-hsl: 186.522 calc(var(--saturation-factor, 1)*100%) 18.039%; - --teal-new-71: hsl(var(--teal-new-71-hsl)/1); - --teal-new-71-hsl: 186.667 calc(var(--saturation-factor, 1)*100%) 17.647%; - --teal-new-72: hsl(var(--teal-new-72-hsl)/1); - --teal-new-72-hsl: 186.818 calc(var(--saturation-factor, 1)*100%) 17.255%; - --teal-new-73: hsl(var(--teal-new-73-hsl)/1); - --teal-new-73-hsl: 187.059 calc(var(--saturation-factor, 1)*100%) 16.667%; - --teal-new-74: hsl(var(--teal-new-74-hsl)/1); - --teal-new-74-hsl: 187.229 calc(var(--saturation-factor, 1)*100%) 16.275%; - --teal-new-75: hsl(var(--teal-new-75-hsl)/1); - --teal-new-75-hsl: 186.75 calc(var(--saturation-factor, 1)*100%) 15.686%; - --teal-new-76: hsl(var(--teal-new-76-hsl)/1); - --teal-new-76-hsl: 187.692 calc(var(--saturation-factor, 1)*100%) 15.294%; - --teal-new-77: hsl(var(--teal-new-77-hsl)/1); - --teal-new-77-hsl: 187.895 calc(var(--saturation-factor, 1)*100%) 14.902%; - --teal-new-78: hsl(var(--teal-new-78-hsl)/1); - --teal-new-78-hsl: 187.397 calc(var(--saturation-factor, 1)*100%) 14.314%; - --teal-new-79: hsl(var(--teal-new-79-hsl)/1); - --teal-new-79-hsl: 188.451 calc(var(--saturation-factor, 1)*100%) 13.922%; - --teal-new-80: hsl(var(--teal-new-80-hsl)/1); - --teal-new-80-hsl: 188.696 calc(var(--saturation-factor, 1)*100%) 13.529%; - --teal-new-81: hsl(var(--teal-new-81-hsl)/1); - --teal-new-81-hsl: 188.182 calc(var(--saturation-factor, 1)*100%) 12.941%; - --teal-new-82: hsl(var(--teal-new-82-hsl)/1); - --teal-new-82-hsl: 188.438 calc(var(--saturation-factor, 1)*100%) 12.549%; - --teal-new-83: hsl(var(--teal-new-83-hsl)/1); - --teal-new-83-hsl: 189.677 calc(var(--saturation-factor, 1)*100%) 12.157%; - --teal-new-84: hsl(var(--teal-new-84-hsl)/1); - --teal-new-84-hsl: 189.153 calc(var(--saturation-factor, 1)*100%) 11.569%; - --teal-new-85: hsl(var(--teal-new-85-hsl)/1); - --teal-new-85-hsl: 189.474 calc(var(--saturation-factor, 1)*100%) 11.176%; - --teal-new-86: hsl(var(--teal-new-86-hsl)/1); - --teal-new-86-hsl: 189.818 calc(var(--saturation-factor, 1)*100%) 10.784%; - --teal-new-87: hsl(var(--teal-new-87-hsl)/1); - --teal-new-87-hsl: 190.189 calc(var(--saturation-factor, 1)*100%) 10.392%; - --teal-new-88: hsl(var(--teal-new-88-hsl)/1); - --teal-new-88-hsl: 190.588 calc(var(--saturation-factor, 1)*100%) 10%; - --teal-new-89: hsl(var(--teal-new-89-hsl)/1); - --teal-new-89-hsl: 191.25 calc(var(--saturation-factor, 1)*100%) 9.412%; - --teal-new-90: hsl(var(--teal-new-90-hsl)/1); - --teal-new-90-hsl: 191.739 calc(var(--saturation-factor, 1)*100%) 9.02%; - --teal-new-91: hsl(var(--teal-new-91-hsl)/1); - --teal-new-91-hsl: 192.273 calc(var(--saturation-factor, 1)*100%) 8.627%; - --teal-new-92: hsl(var(--teal-new-92-hsl)/1); - --teal-new-92-hsl: 192.857 calc(var(--saturation-factor, 1)*100%) 8.235%; - --teal-new-93: hsl(var(--teal-new-93-hsl)/1); - --teal-new-93-hsl: 193.5 calc(var(--saturation-factor, 1)*100%) 7.843%; - --teal-new-94: hsl(var(--teal-new-94-hsl)/1); - --teal-new-94-hsl: 194.211 calc(var(--saturation-factor, 1)*100%) 7.451%; - --teal-new-95: hsl(var(--teal-new-95-hsl)/1); - --teal-new-95-hsl: 195 calc(var(--saturation-factor, 1)*100%) 7.059%; - --teal-new-96: hsl(var(--teal-new-96-hsl)/1); - --teal-new-96-hsl: 194.545 calc(var(--saturation-factor, 1)*100%) 6.471%; - --teal-new-97: hsl(var(--teal-new-97-hsl)/1); - --teal-new-97-hsl: 197.419 calc(var(--saturation-factor, 1)*100%) 6.078%; - --teal-new-98: hsl(var(--teal-new-98-hsl)/1); - --teal-new-98-hsl: 197.143 calc(var(--saturation-factor, 1)*100%) 5.49%; - --teal-new-99: hsl(var(--teal-new-99-hsl)/1); - --teal-new-99-hsl: 199.2 calc(var(--saturation-factor, 1)*100%) 4.902%; - --teal-new-100: hsl(var(--teal-new-100-hsl)/1); - --teal-new-100-hsl: 204.545 calc(var(--saturation-factor, 1)*100%) 4.314%; - --yellow-new-1: hsl(var(--yellow-new-1-hsl)/1); - --yellow-new-1-hsl: 30 calc(var(--saturation-factor, 1)*100%) 90.588%; - --yellow-new-2: hsl(var(--yellow-new-2-hsl)/1); - --yellow-new-2-hsl: 31.698 calc(var(--saturation-factor, 1)*100%) 89.608%; - --yellow-new-3: hsl(var(--yellow-new-3-hsl)/1); - --yellow-new-3-hsl: 33.559 calc(var(--saturation-factor, 1)*100%) 88.431%; - --yellow-new-4: hsl(var(--yellow-new-4-hsl)/1); - --yellow-new-4-hsl: 34.688 calc(var(--saturation-factor, 1)*100%) 87.451%; - --yellow-new-5: hsl(var(--yellow-new-5-hsl)/1); - --yellow-new-5-hsl: 34.783 calc(var(--saturation-factor, 1)*100%) 86.471%; - --yellow-new-6: hsl(var(--yellow-new-6-hsl)/1); - --yellow-new-6-hsl: 35.676 calc(var(--saturation-factor, 1)*100%) 85.49%; - --yellow-new-7: hsl(var(--yellow-new-7-hsl)/1); - --yellow-new-7-hsl: 36.456 calc(var(--saturation-factor, 1)*100%) 84.51%; - --yellow-new-8: hsl(var(--yellow-new-8-hsl)/1); - --yellow-new-8-hsl: 36.429 calc(var(--saturation-factor, 1)*100%) 83.529%; - --yellow-new-9: hsl(var(--yellow-new-9-hsl)/1); - --yellow-new-9-hsl: 37.079 calc(var(--saturation-factor, 1)*100%) 82.549%; - --yellow-new-10: hsl(var(--yellow-new-10-hsl)/1); - --yellow-new-10-hsl: 37.66 calc(var(--saturation-factor, 1)*100%) 81.569%; - --yellow-new-11: hsl(var(--yellow-new-11-hsl)/1); - --yellow-new-11-hsl: 37.347 calc(var(--saturation-factor, 1)*100%) 80.784%; - --yellow-new-12: hsl(var(--yellow-new-12-hsl)/1); - --yellow-new-12-hsl: 37.864 calc(var(--saturation-factor, 1)*100%) 79.804%; - --yellow-new-13: hsl(var(--yellow-new-13-hsl)/1); - --yellow-new-13-hsl: 38.131 calc(var(--saturation-factor, 1)*100%) 79.02%; - --yellow-new-14: hsl(var(--yellow-new-14-hsl)/1); - --yellow-new-14-hsl: 38.036 calc(var(--saturation-factor, 1)*100%) 78.039%; - --yellow-new-15: hsl(var(--yellow-new-15-hsl)/1); - --yellow-new-15-hsl: 38.276 calc(var(--saturation-factor, 1)*100%) 77.255%; - --yellow-new-16: hsl(var(--yellow-new-16-hsl)/1); - --yellow-new-16-hsl: 38.5 calc(var(--saturation-factor, 1)*100%) 76.471%; - --yellow-new-17: hsl(var(--yellow-new-17-hsl)/1); - --yellow-new-17-hsl: 38.4 calc(var(--saturation-factor, 1)*100%) 75.49%; - --yellow-new-18: hsl(var(--yellow-new-18-hsl)/1); - --yellow-new-18-hsl: 38.605 calc(var(--saturation-factor, 1)*100%) 74.706%; - --yellow-new-19: hsl(var(--yellow-new-19-hsl)/1); - --yellow-new-19-hsl: 38.797 calc(var(--saturation-factor, 1)*100%) 73.922%; - --yellow-new-20: hsl(var(--yellow-new-20-hsl)/1); - --yellow-new-20-hsl: 38.54 calc(var(--saturation-factor, 1)*100%) 73.137%; - --yellow-new-21: hsl(var(--yellow-new-21-hsl)/1); - --yellow-new-21-hsl: 38.723 calc(var(--saturation-factor, 1)*100%) 72.353%; - --yellow-new-22: hsl(var(--yellow-new-22-hsl)/1); - --yellow-new-22-hsl: 38.483 calc(var(--saturation-factor, 1)*100%) 71.569%; - --yellow-new-23: hsl(var(--yellow-new-23-hsl)/1); - --yellow-new-23-hsl: 38.658 calc(var(--saturation-factor, 1)*100%) 70.784%; - --yellow-new-24: hsl(var(--yellow-new-24-hsl)/1); - --yellow-new-24-hsl: 38.824 calc(var(--saturation-factor, 1)*100%) 70%; - --yellow-new-25: hsl(var(--yellow-new-25-hsl)/1); - --yellow-new-25-hsl: 38.599 calc(var(--saturation-factor, 1)*100%) 69.216%; - --yellow-new-26: hsl(var(--yellow-new-26-hsl)/1); - --yellow-new-26-hsl: 38.758 calc(var(--saturation-factor, 1)*100%) 68.431%; - --yellow-new-27: hsl(var(--yellow-new-27-hsl)/1); - --yellow-new-27-hsl: 38.545 calc(var(--saturation-factor, 1)*100%) 67.647%; - --yellow-new-28: hsl(var(--yellow-new-28-hsl)/1); - --yellow-new-28-hsl: 38.698 calc(var(--saturation-factor, 1)*100%) 66.863%; - --yellow-new-29: hsl(var(--yellow-new-29-hsl)/1); - --yellow-new-29-hsl: 38.844 calc(var(--saturation-factor, 1)*100%) 66.078%; - --yellow-new-30: hsl(var(--yellow-new-30-hsl)/1); - --yellow-new-30-hsl: 38.644 calc(var(--saturation-factor, 1)*100%) 65.294%; - --yellow-new-31: hsl(var(--yellow-new-31-hsl)/1); - --yellow-new-31-hsl: 38.785 calc(var(--saturation-factor, 1)*100%) 64.51%; - --yellow-new-32: hsl(var(--yellow-new-32-hsl)/1); - --yellow-new-32-hsl: 38.919 calc(var(--saturation-factor, 1)*98.93%) 63.333%; - --yellow-new-33: hsl(var(--yellow-new-33-hsl)/1); - --yellow-new-33-hsl: 39.048 calc(var(--saturation-factor, 1)*98.953%) 62.549%; - --yellow-new-34: hsl(var(--yellow-new-34-hsl)/1); - --yellow-new-34-hsl: 38.86 calc(var(--saturation-factor, 1)*98.974%) 61.765%; - --yellow-new-35: hsl(var(--yellow-new-35-hsl)/1); - --yellow-new-35-hsl: 39.289 calc(var(--saturation-factor, 1)*98.01%) 60.588%; - --yellow-new-36: hsl(var(--yellow-new-36-hsl)/1); - --yellow-new-36-hsl: 39.505 calc(var(--saturation-factor, 1)*98.058%) 59.608%; - --yellow-new-37: hsl(var(--yellow-new-37-hsl)/1); - --yellow-new-37-hsl: 39.42 calc(var(--saturation-factor, 1)*98.104%) 58.627%; - --yellow-new-38: hsl(var(--yellow-new-38-hsl)/1); - --yellow-new-38-hsl: 39.81 calc(var(--saturation-factor, 1)*97.235%) 57.451%; - --yellow-new-39: hsl(var(--yellow-new-39-hsl)/1); - --yellow-new-39-hsl: 39.816 calc(var(--saturation-factor, 1)*97.309%) 56.275%; - --yellow-new-40: hsl(var(--yellow-new-40-hsl)/1); - --yellow-new-40-hsl: 40.179 calc(var(--saturation-factor, 1)*97.391%) 54.902%; - --yellow-new-41: hsl(var(--yellow-new-41-hsl)/1); - --yellow-new-41-hsl: 40.345 calc(var(--saturation-factor, 1)*97.479%) 53.333%; - --yellow-new-42: hsl(var(--yellow-new-42-hsl)/1); - --yellow-new-42-hsl: 41.311 calc(var(--saturation-factor, 1)*96.825%) 50.588%; - --yellow-new-43: hsl(var(--yellow-new-43-hsl)/1); - --yellow-new-43-hsl: 41.355 calc(var(--saturation-factor, 1)*100%) 49.216%; - --yellow-new-44: hsl(var(--yellow-new-44-hsl)/1); - --yellow-new-44-hsl: 41.116 calc(var(--saturation-factor, 1)*100%) 49.216%; - --yellow-new-45: hsl(var(--yellow-new-45-hsl)/1); - --yellow-new-45-hsl: 40.8 calc(var(--saturation-factor, 1)*100%) 49.02%; - --yellow-new-46: hsl(var(--yellow-new-46-hsl)/1); - --yellow-new-46-hsl: 40.56 calc(var(--saturation-factor, 1)*100%) 49.02%; - --yellow-new-47: hsl(var(--yellow-new-47-hsl)/1); - --yellow-new-47-hsl: 40.241 calc(var(--saturation-factor, 1)*100%) 48.824%; - --yellow-new-48: hsl(var(--yellow-new-48-hsl)/1); - --yellow-new-48-hsl: 40 calc(var(--saturation-factor, 1)*100%) 48.824%; - --yellow-new-49: hsl(var(--yellow-new-49-hsl)/1); - --yellow-new-49-hsl: 39.518 calc(var(--saturation-factor, 1)*100%) 48.824%; - --yellow-new-50: hsl(var(--yellow-new-50-hsl)/1); - --yellow-new-50-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --yellow-new-51: hsl(var(--yellow-new-51-hsl)/1); - --yellow-new-51-hsl: 39.098 calc(var(--saturation-factor, 1)*100%) 47.843%; - --yellow-new-52: hsl(var(--yellow-new-52-hsl)/1); - --yellow-new-52-hsl: 38.912 calc(var(--saturation-factor, 1)*100%) 46.863%; - --yellow-new-53: hsl(var(--yellow-new-53-hsl)/1); - --yellow-new-53-hsl: 38.974 calc(var(--saturation-factor, 1)*100%) 45.882%; - --yellow-new-54: hsl(var(--yellow-new-54-hsl)/1); - --yellow-new-54-hsl: 38.777 calc(var(--saturation-factor, 1)*100%) 44.902%; - --yellow-new-55: hsl(var(--yellow-new-55-hsl)/1); - --yellow-new-55-hsl: 38.571 calc(var(--saturation-factor, 1)*100%) 43.922%; - --yellow-new-56: hsl(var(--yellow-new-56-hsl)/1); - --yellow-new-56-hsl: 38.455 calc(var(--saturation-factor, 1)*100%) 43.137%; - --yellow-new-57: hsl(var(--yellow-new-57-hsl)/1); - --yellow-new-57-hsl: 38.233 calc(var(--saturation-factor, 1)*100%) 42.157%; - --yellow-new-58: hsl(var(--yellow-new-58-hsl)/1); - --yellow-new-58-hsl: 38 calc(var(--saturation-factor, 1)*100%) 41.176%; - --yellow-new-59: hsl(var(--yellow-new-59-hsl)/1); - --yellow-new-59-hsl: 37.573 calc(var(--saturation-factor, 1)*100%) 40.392%; - --yellow-new-60: hsl(var(--yellow-new-60-hsl)/1); - --yellow-new-60-hsl: 37.612 calc(var(--saturation-factor, 1)*100%) 39.412%; - --yellow-new-61: hsl(var(--yellow-new-61-hsl)/1); - --yellow-new-61-hsl: 37.347 calc(var(--saturation-factor, 1)*100%) 38.431%; - --yellow-new-62: hsl(var(--yellow-new-62-hsl)/1); - --yellow-new-62-hsl: 36.875 calc(var(--saturation-factor, 1)*100%) 37.647%; - --yellow-new-63: hsl(var(--yellow-new-63-hsl)/1); - --yellow-new-63-hsl: 36.898 calc(var(--saturation-factor, 1)*100%) 36.667%; - --yellow-new-64: hsl(var(--yellow-new-64-hsl)/1); - --yellow-new-64-hsl: 36.393 calc(var(--saturation-factor, 1)*100%) 35.882%; - --yellow-new-65: hsl(var(--yellow-new-65-hsl)/1); - --yellow-new-65-hsl: 36.201 calc(var(--saturation-factor, 1)*100%) 35.098%; - --yellow-new-66: hsl(var(--yellow-new-66-hsl)/1); - --yellow-new-66-hsl: 35.862 calc(var(--saturation-factor, 1)*100%) 34.118%; - --yellow-new-67: hsl(var(--yellow-new-67-hsl)/1); - --yellow-new-67-hsl: 35.294 calc(var(--saturation-factor, 1)*100%) 33.333%; - --yellow-new-68: hsl(var(--yellow-new-68-hsl)/1); - --yellow-new-68-hsl: 35.273 calc(var(--saturation-factor, 1)*100%) 32.353%; - --yellow-new-69: hsl(var(--yellow-new-69-hsl)/1); - --yellow-new-69-hsl: 34.658 calc(var(--saturation-factor, 1)*100%) 31.569%; - --yellow-new-70: hsl(var(--yellow-new-70-hsl)/1); - --yellow-new-70-hsl: 34.395 calc(var(--saturation-factor, 1)*100%) 30.784%; - --yellow-new-71: hsl(var(--yellow-new-71-hsl)/1); - --yellow-new-71-hsl: 33.725 calc(var(--saturation-factor, 1)*100%) 30%; - --yellow-new-72: hsl(var(--yellow-new-72-hsl)/1); - --yellow-new-72-hsl: 33.649 calc(var(--saturation-factor, 1)*100%) 29.02%; - --yellow-new-73: hsl(var(--yellow-new-73-hsl)/1); - --yellow-new-73-hsl: 32.917 calc(var(--saturation-factor, 1)*100%) 28.235%; - --yellow-new-74: hsl(var(--yellow-new-74-hsl)/1); - --yellow-new-74-hsl: 32.571 calc(var(--saturation-factor, 1)*100%) 27.451%; - --yellow-new-75: hsl(var(--yellow-new-75-hsl)/1); - --yellow-new-75-hsl: 31.765 calc(var(--saturation-factor, 1)*100%) 26.667%; - --yellow-new-76: hsl(var(--yellow-new-76-hsl)/1); - --yellow-new-76-hsl: 31.603 calc(var(--saturation-factor, 1)*100%) 25.686%; - --yellow-new-77: hsl(var(--yellow-new-77-hsl)/1); - --yellow-new-77-hsl: 30.709 calc(var(--saturation-factor, 1)*100%) 24.902%; - --yellow-new-78: hsl(var(--yellow-new-78-hsl)/1); - --yellow-new-78-hsl: 30.244 calc(var(--saturation-factor, 1)*100%) 24.118%; - --yellow-new-79: hsl(var(--yellow-new-79-hsl)/1); - --yellow-new-79-hsl: 29.748 calc(var(--saturation-factor, 1)*100%) 23.333%; - --yellow-new-80: hsl(var(--yellow-new-80-hsl)/1); - --yellow-new-80-hsl: 28.696 calc(var(--saturation-factor, 1)*100%) 22.549%; - --yellow-new-81: hsl(var(--yellow-new-81-hsl)/1); - --yellow-new-81-hsl: 28.108 calc(var(--saturation-factor, 1)*100%) 21.765%; - --yellow-new-82: hsl(var(--yellow-new-82-hsl)/1); - --yellow-new-82-hsl: 27.477 calc(var(--saturation-factor, 1)*100%) 20.98%; - --yellow-new-83: hsl(var(--yellow-new-83-hsl)/1); - --yellow-new-83-hsl: 26.214 calc(var(--saturation-factor, 1)*100%) 20.196%; - --yellow-new-84: hsl(var(--yellow-new-84-hsl)/1); - --yellow-new-84-hsl: 25.714 calc(var(--saturation-factor, 1)*100%) 19.216%; - --yellow-new-85: hsl(var(--yellow-new-85-hsl)/1); - --yellow-new-85-hsl: 24.894 calc(var(--saturation-factor, 1)*100%) 18.431%; - --yellow-new-86: hsl(var(--yellow-new-86-hsl)/1); - --yellow-new-86-hsl: 24 calc(var(--saturation-factor, 1)*100%) 17.647%; - --yellow-new-87: hsl(var(--yellow-new-87-hsl)/1); - --yellow-new-87-hsl: 22.326 calc(var(--saturation-factor, 1)*100%) 16.863%; - --yellow-new-88: hsl(var(--yellow-new-88-hsl)/1); - --yellow-new-88-hsl: 21.22 calc(var(--saturation-factor, 1)*100%) 16.078%; - --yellow-new-89: hsl(var(--yellow-new-89-hsl)/1); - --yellow-new-89-hsl: 19.747 calc(var(--saturation-factor, 1)*100%) 15.49%; - --yellow-new-90: hsl(var(--yellow-new-90-hsl)/1); - --yellow-new-90-hsl: 18.4 calc(var(--saturation-factor, 1)*100%) 14.706%; - --yellow-new-91: hsl(var(--yellow-new-91-hsl)/1); - --yellow-new-91-hsl: 16.056 calc(var(--saturation-factor, 1)*100%) 13.922%; - --yellow-new-92: hsl(var(--yellow-new-92-hsl)/1); - --yellow-new-92-hsl: 14.328 calc(var(--saturation-factor, 1)*100%) 13.137%; - --yellow-new-93: hsl(var(--yellow-new-93-hsl)/1); - --yellow-new-93-hsl: 12.381 calc(var(--saturation-factor, 1)*100%) 12.353%; - --yellow-new-94: hsl(var(--yellow-new-94-hsl)/1); - --yellow-new-94-hsl: 9.153 calc(var(--saturation-factor, 1)*100%) 11.569%; - --yellow-new-95: hsl(var(--yellow-new-95-hsl)/1); - --yellow-new-95-hsl: 6.545 calc(var(--saturation-factor, 1)*100%) 10.784%; - --yellow-new-96: hsl(var(--yellow-new-96-hsl)/1); - --yellow-new-96-hsl: 4.706 calc(var(--saturation-factor, 1)*100%) 10%; - --yellow-new-97: hsl(var(--yellow-new-97-hsl)/1); - --yellow-new-97-hsl: 1.277 calc(var(--saturation-factor, 1)*100%) 9.216%; - --yellow-new-98: hsl(var(--yellow-new-98-hsl)/1); - --yellow-new-98-hsl: 0 calc(var(--saturation-factor, 1)*100%) 8.235%; - --yellow-new-99: hsl(var(--yellow-new-99-hsl)/1); - --yellow-new-99-hsl: 0 calc(var(--saturation-factor, 1)*100%) 7.059%; - --yellow-new-100: hsl(var(--yellow-new-100-hsl)/1); - --yellow-new-100-hsl: 0 calc(var(--saturation-factor, 1)*100%) 5.294%; - --orange-new-1: hsl(var(--orange-new-1-hsl)/1); - --orange-new-1-hsl: 15.789 calc(var(--saturation-factor, 1)*100%) 92.549%; - --orange-new-2: hsl(var(--orange-new-2-hsl)/1); - --orange-new-2-hsl: 17.727 calc(var(--saturation-factor, 1)*100%) 91.373%; - --orange-new-3: hsl(var(--orange-new-3-hsl)/1); - --orange-new-3-hsl: 17.143 calc(var(--saturation-factor, 1)*100%) 90.392%; - --orange-new-4: hsl(var(--orange-new-4-hsl)/1); - --orange-new-4-hsl: 17.778 calc(var(--saturation-factor, 1)*100%) 89.412%; - --orange-new-5: hsl(var(--orange-new-5-hsl)/1); - --orange-new-5-hsl: 19 calc(var(--saturation-factor, 1)*100%) 88.235%; - --orange-new-6: hsl(var(--orange-new-6-hsl)/1); - --orange-new-6-hsl: 19.385 calc(var(--saturation-factor, 1)*100%) 87.255%; - --orange-new-7: hsl(var(--orange-new-7-hsl)/1); - --orange-new-7-hsl: 19.714 calc(var(--saturation-factor, 1)*100%) 86.275%; - --orange-new-8: hsl(var(--orange-new-8-hsl)/1); - --orange-new-8-hsl: 20 calc(var(--saturation-factor, 1)*100%) 85.294%; - --orange-new-9: hsl(var(--orange-new-9-hsl)/1); - --orange-new-9-hsl: 20.506 calc(var(--saturation-factor, 1)*97.531%) 84.118%; - --orange-new-10: hsl(var(--orange-new-10-hsl)/1); - --orange-new-10-hsl: 20.241 calc(var(--saturation-factor, 1)*95.402%) 82.941%; - --orange-new-11: hsl(var(--orange-new-11-hsl)/1); - --orange-new-11-hsl: 20.455 calc(var(--saturation-factor, 1)*95.652%) 81.961%; - --orange-new-12: hsl(var(--orange-new-12-hsl)/1); - --orange-new-12-hsl: 20.44 calc(var(--saturation-factor, 1)*93.814%) 80.98%; - --orange-new-13: hsl(var(--orange-new-13-hsl)/1); - --orange-new-13-hsl: 21.064 calc(var(--saturation-factor, 1)*90.385%) 79.608%; - --orange-new-14: hsl(var(--orange-new-14-hsl)/1); - --orange-new-14-hsl: 21.429 calc(var(--saturation-factor, 1)*89.091%) 78.431%; - --orange-new-15: hsl(var(--orange-new-15-hsl)/1); - --orange-new-15-hsl: 21.386 calc(var(--saturation-factor, 1)*87.826%) 77.451%; - --orange-new-16: hsl(var(--orange-new-16-hsl)/1); - --orange-new-16-hsl: 21.714 calc(var(--saturation-factor, 1)*86.777%) 76.275%; - --orange-new-17: hsl(var(--orange-new-17-hsl)/1); - --orange-new-17-hsl: 21.667 calc(var(--saturation-factor, 1)*85.714%) 75.294%; - --orange-new-18: hsl(var(--orange-new-18-hsl)/1); - --orange-new-18-hsl: 21.622 calc(var(--saturation-factor, 1)*84.733%) 74.314%; - --orange-new-19: hsl(var(--orange-new-19-hsl)/1); - --orange-new-19-hsl: 22.105 calc(var(--saturation-factor, 1)*82.609%) 72.941%; - --orange-new-20: hsl(var(--orange-new-20-hsl)/1); - --orange-new-20-hsl: 22.051 calc(var(--saturation-factor, 1)*81.818%) 71.961%; - --orange-new-21: hsl(var(--orange-new-21-hsl)/1); - --orange-new-21-hsl: 22 calc(var(--saturation-factor, 1)*81.081%) 70.98%; - --orange-new-22: hsl(var(--orange-new-22-hsl)/1); - --orange-new-22-hsl: 22.131 calc(var(--saturation-factor, 1)*79.221%) 69.804%; - --orange-new-23: hsl(var(--orange-new-23-hsl)/1); - --orange-new-23-hsl: 22.08 calc(var(--saturation-factor, 1)*78.616%) 68.824%; - --orange-new-24: hsl(var(--orange-new-24-hsl)/1); - --orange-new-24-hsl: 22.205 calc(var(--saturation-factor, 1)*76.97%) 67.647%; - --orange-new-25: hsl(var(--orange-new-25-hsl)/1); - --orange-new-25-hsl: 22.154 calc(var(--saturation-factor, 1)*76.471%) 66.667%; - --orange-new-26: hsl(var(--orange-new-26-hsl)/1); - --orange-new-26-hsl: 22.273 calc(var(--saturation-factor, 1)*75%) 65.49%; - --orange-new-27: hsl(var(--orange-new-27-hsl)/1); - --orange-new-27-hsl: 22.222 calc(var(--saturation-factor, 1)*74.586%) 64.51%; - --orange-new-28: hsl(var(--orange-new-28-hsl)/1); - --orange-new-28-hsl: 22.336 calc(var(--saturation-factor, 1)*73.262%) 63.333%; - --orange-new-29: hsl(var(--orange-new-29-hsl)/1); - --orange-new-29-hsl: 22.174 calc(var(--saturation-factor, 1)*71.875%) 62.353%; - --orange-new-30: hsl(var(--orange-new-30-hsl)/1); - --orange-new-30-hsl: 22.128 calc(var(--saturation-factor, 1)*71.574%) 61.373%; - --orange-new-31: hsl(var(--orange-new-31-hsl)/1); - --orange-new-31-hsl: 22.238 calc(var(--saturation-factor, 1)*70.443%) 60.196%; - --orange-new-32: hsl(var(--orange-new-32-hsl)/1); - --orange-new-32-hsl: 22.345 calc(var(--saturation-factor, 1)*69.378%) 59.02%; - --orange-new-33: hsl(var(--orange-new-33-hsl)/1); - --orange-new-33-hsl: 22.041 calc(var(--saturation-factor, 1)*69.014%) 58.235%; - --orange-new-34: hsl(var(--orange-new-34-hsl)/1); - --orange-new-34-hsl: 22.148 calc(var(--saturation-factor, 1)*68.037%) 57.059%; - --orange-new-35: hsl(var(--orange-new-35-hsl)/1); - --orange-new-35-hsl: 22.252 calc(var(--saturation-factor, 1)*67.111%) 55.882%; - --orange-new-36: hsl(var(--orange-new-36-hsl)/1); - --orange-new-36-hsl: 22.353 calc(var(--saturation-factor, 1)*66.234%) 54.706%; - --orange-new-37: hsl(var(--orange-new-37-hsl)/1); - --orange-new-37-hsl: 22.208 calc(var(--saturation-factor, 1)*65.254%) 53.725%; - --orange-new-38: hsl(var(--orange-new-38-hsl)/1); - --orange-new-38-hsl: 22.166 calc(var(--saturation-factor, 1)*65.145%) 52.745%; - --orange-new-39: hsl(var(--orange-new-39-hsl)/1); - --orange-new-39-hsl: 22.264 calc(var(--saturation-factor, 1)*64.372%) 51.569%; - --orange-new-40: hsl(var(--orange-new-40-hsl)/1); - --orange-new-40-hsl: 22.125 calc(var(--saturation-factor, 1)*63.492%) 50.588%; - --orange-new-41: hsl(var(--orange-new-41-hsl)/1); - --orange-new-41-hsl: 22.222 calc(var(--saturation-factor, 1)*64.286%) 49.412%; - --orange-new-42: hsl(var(--orange-new-42-hsl)/1); - --orange-new-42-hsl: 22.683 calc(var(--saturation-factor, 1)*66.667%) 48.235%; - --orange-new-43: hsl(var(--orange-new-43-hsl)/1); - --orange-new-43-hsl: 22.771 calc(var(--saturation-factor, 1)*69.167%) 47.059%; - --orange-new-44: hsl(var(--orange-new-44-hsl)/1); - --orange-new-44-hsl: 22.857 calc(var(--saturation-factor, 1)*71.795%) 45.882%; - --orange-new-45: hsl(var(--orange-new-45-hsl)/1); - --orange-new-45-hsl: 22.941 calc(var(--saturation-factor, 1)*74.561%) 44.706%; - --orange-new-46: hsl(var(--orange-new-46-hsl)/1); - --orange-new-46-hsl: 23.237 calc(var(--saturation-factor, 1)*78.281%) 43.333%; - --orange-new-47: hsl(var(--orange-new-47-hsl)/1); - --orange-new-47-hsl: 23.523 calc(var(--saturation-factor, 1)*82.243%) 41.961%; - --orange-new-48: hsl(var(--orange-new-48-hsl)/1); - --orange-new-48-hsl: 24 calc(var(--saturation-factor, 1)*87.379%) 40.392%; - --orange-new-49: hsl(var(--orange-new-49-hsl)/1); - --orange-new-49-hsl: 24.649 calc(var(--saturation-factor, 1)*93.909%) 38.627%; - --orange-new-50: hsl(var(--orange-new-50-hsl)/1); - --orange-new-50-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --orange-new-51: hsl(var(--orange-new-51-hsl)/1); - --orange-new-51-hsl: 24.783 calc(var(--saturation-factor, 1)*98.925%) 36.471%; - --orange-new-52: hsl(var(--orange-new-52-hsl)/1); - --orange-new-52-hsl: 24.333 calc(var(--saturation-factor, 1)*97.826%) 36.078%; - --orange-new-53: hsl(var(--orange-new-53-hsl)/1); - --orange-new-53-hsl: 24.343 calc(var(--saturation-factor, 1)*96.685%) 35.49%; - --orange-new-54: hsl(var(--orange-new-54-hsl)/1); - --orange-new-54-hsl: 24.07 calc(var(--saturation-factor, 1)*96.629%) 34.902%; - --orange-new-55: hsl(var(--orange-new-55-hsl)/1); - --orange-new-55-hsl: 23.713 calc(var(--saturation-factor, 1)*95.429%) 34.314%; - --orange-new-56: hsl(var(--orange-new-56-hsl)/1); - --orange-new-56-hsl: 23.558 calc(var(--saturation-factor, 1)*95.322%) 33.529%; - --orange-new-57: hsl(var(--orange-new-57-hsl)/1); - --orange-new-57-hsl: 23.625 calc(var(--saturation-factor, 1)*95.238%) 32.941%; - --orange-new-58: hsl(var(--orange-new-58-hsl)/1); - --orange-new-58-hsl: 23.312 calc(var(--saturation-factor, 1)*95.152%) 32.353%; - --orange-new-59: hsl(var(--orange-new-59-hsl)/1); - --orange-new-59-hsl: 23.137 calc(var(--saturation-factor, 1)*95.031%) 31.569%; - --orange-new-60: hsl(var(--orange-new-60-hsl)/1); - --orange-new-60-hsl: 23.046 calc(var(--saturation-factor, 1)*96.178%) 30.784%; - --orange-new-61: hsl(var(--orange-new-61-hsl)/1); - --orange-new-61-hsl: 22.857 calc(var(--saturation-factor, 1)*96.078%) 30%; - --orange-new-62: hsl(var(--orange-new-62-hsl)/1); - --orange-new-62-hsl: 22.917 calc(var(--saturation-factor, 1)*96%) 29.412%; - --orange-new-63: hsl(var(--orange-new-63-hsl)/1); - --orange-new-63-hsl: 22.817 calc(var(--saturation-factor, 1)*97.26%) 28.627%; - --orange-new-64: hsl(var(--orange-new-64-hsl)/1); - --orange-new-64-hsl: 22.446 calc(var(--saturation-factor, 1)*97.203%) 28.039%; - --orange-new-65: hsl(var(--orange-new-65-hsl)/1); - --orange-new-65-hsl: 22.5 calc(var(--saturation-factor, 1)*98.551%) 27.059%; - --orange-new-66: hsl(var(--orange-new-66-hsl)/1); - --orange-new-66-hsl: 22.105 calc(var(--saturation-factor, 1)*98.519%) 26.471%; - --orange-new-67: hsl(var(--orange-new-67-hsl)/1); - --orange-new-67-hsl: 22.443 calc(var(--saturation-factor, 1)*100%) 25.686%; - --orange-new-68: hsl(var(--orange-new-68-hsl)/1); - --orange-new-68-hsl: 22.031 calc(var(--saturation-factor, 1)*100%) 25.098%; - --orange-new-69: hsl(var(--orange-new-69-hsl)/1); - --orange-new-69-hsl: 21.6 calc(var(--saturation-factor, 1)*100%) 24.51%; - --orange-new-70: hsl(var(--orange-new-70-hsl)/1); - --orange-new-70-hsl: 21.322 calc(var(--saturation-factor, 1)*100%) 23.725%; - --orange-new-71: hsl(var(--orange-new-71-hsl)/1); - --orange-new-71-hsl: 20.847 calc(var(--saturation-factor, 1)*100%) 23.137%; - --orange-new-72: hsl(var(--orange-new-72-hsl)/1); - --orange-new-72-hsl: 20.348 calc(var(--saturation-factor, 1)*100%) 22.549%; - --orange-new-73: hsl(var(--orange-new-73-hsl)/1); - --orange-new-73-hsl: 20.357 calc(var(--saturation-factor, 1)*100%) 21.961%; - --orange-new-74: hsl(var(--orange-new-74-hsl)/1); - --orange-new-74-hsl: 19.817 calc(var(--saturation-factor, 1)*100%) 21.373%; - --orange-new-75: hsl(var(--orange-new-75-hsl)/1); - --orange-new-75-hsl: 19.245 calc(var(--saturation-factor, 1)*100%) 20.784%; - --orange-new-76: hsl(var(--orange-new-76-hsl)/1); - --orange-new-76-hsl: 18.641 calc(var(--saturation-factor, 1)*100%) 20.196%; - --orange-new-77: hsl(var(--orange-new-77-hsl)/1); - --orange-new-77-hsl: 18 calc(var(--saturation-factor, 1)*100%) 19.608%; - --orange-new-78: hsl(var(--orange-new-78-hsl)/1); - --orange-new-78-hsl: 17.32 calc(var(--saturation-factor, 1)*100%) 19.02%; - --orange-new-79: hsl(var(--orange-new-79-hsl)/1); - --orange-new-79-hsl: 16.596 calc(var(--saturation-factor, 1)*100%) 18.431%; - --orange-new-80: hsl(var(--orange-new-80-hsl)/1); - --orange-new-80-hsl: 16.484 calc(var(--saturation-factor, 1)*100%) 17.843%; - --orange-new-81: hsl(var(--orange-new-81-hsl)/1); - --orange-new-81-hsl: 15.682 calc(var(--saturation-factor, 1)*100%) 17.255%; - --orange-new-82: hsl(var(--orange-new-82-hsl)/1); - --orange-new-82-hsl: 14.824 calc(var(--saturation-factor, 1)*100%) 16.667%; - --orange-new-83: hsl(var(--orange-new-83-hsl)/1); - --orange-new-83-hsl: 13.735 calc(var(--saturation-factor, 1)*100%) 16.275%; - --orange-new-84: hsl(var(--orange-new-84-hsl)/1); - --orange-new-84-hsl: 12.75 calc(var(--saturation-factor, 1)*100%) 15.686%; - --orange-new-85: hsl(var(--orange-new-85-hsl)/1); - --orange-new-85-hsl: 11.688 calc(var(--saturation-factor, 1)*100%) 15.098%; - --orange-new-86: hsl(var(--orange-new-86-hsl)/1); - --orange-new-86-hsl: 10.541 calc(var(--saturation-factor, 1)*100%) 14.51%; - --orange-new-87: hsl(var(--orange-new-87-hsl)/1); - --orange-new-87-hsl: 9.296 calc(var(--saturation-factor, 1)*100%) 13.922%; - --orange-new-88: hsl(var(--orange-new-88-hsl)/1); - --orange-new-88-hsl: 7.941 calc(var(--saturation-factor, 1)*100%) 13.333%; - --orange-new-89: hsl(var(--orange-new-89-hsl)/1); - --orange-new-89-hsl: 7.273 calc(var(--saturation-factor, 1)*100%) 12.941%; - --orange-new-90: hsl(var(--orange-new-90-hsl)/1); - --orange-new-90-hsl: 5.714 calc(var(--saturation-factor, 1)*100%) 12.353%; - --orange-new-91: hsl(var(--orange-new-91-hsl)/1); - --orange-new-91-hsl: 4 calc(var(--saturation-factor, 1)*100%) 11.765%; - --orange-new-92: hsl(var(--orange-new-92-hsl)/1); - --orange-new-92-hsl: 3.158 calc(var(--saturation-factor, 1)*100%) 11.176%; - --orange-new-93: hsl(var(--orange-new-93-hsl)/1); - --orange-new-93-hsl: 2.182 calc(var(--saturation-factor, 1)*100%) 10.784%; - --orange-new-94: hsl(var(--orange-new-94-hsl)/1); - --orange-new-94-hsl: 1.154 calc(var(--saturation-factor, 1)*100%) 10.196%; - --orange-new-95: hsl(var(--orange-new-95-hsl)/1); - --orange-new-95-hsl: 358.776 calc(var(--saturation-factor, 1)*100%) 9.608%; - --orange-new-96: hsl(var(--orange-new-96-hsl)/1); - --orange-new-96-hsl: 358.667 calc(var(--saturation-factor, 1)*100%) 8.824%; - --orange-new-97: hsl(var(--orange-new-97-hsl)/1); - --orange-new-97-hsl: 358.537 calc(var(--saturation-factor, 1)*100%) 8.039%; - --orange-new-98: hsl(var(--orange-new-98-hsl)/1); - --orange-new-98-hsl: 356.757 calc(var(--saturation-factor, 1)*100%) 7.255%; - --orange-new-99: hsl(var(--orange-new-99-hsl)/1); - --orange-new-99-hsl: 356.129 calc(var(--saturation-factor, 1)*100%) 6.078%; - --orange-new-100: hsl(var(--orange-new-100-hsl)/1); - --orange-new-100-hsl: 355 calc(var(--saturation-factor, 1)*100%) 4.706%; - --pink-1: hsl(var(--pink-1-hsl)/1); - --pink-1-hsl: 316.5 calc(var(--saturation-factor, 1)*100%) 92.157%; - --pink-2: hsl(var(--pink-2-hsl)/1); - --pink-2-hsl: 316.744 calc(var(--saturation-factor, 1)*100%) 91.569%; - --pink-3: hsl(var(--pink-3-hsl)/1); - --pink-3-hsl: 315.652 calc(var(--saturation-factor, 1)*100%) 90.98%; - --pink-4: hsl(var(--pink-4-hsl)/1); - --pink-4-hsl: 315.918 calc(var(--saturation-factor, 1)*100%) 90.392%; - --pink-5: hsl(var(--pink-5-hsl)/1); - --pink-5-hsl: 315 calc(var(--saturation-factor, 1)*100%) 89.804%; - --pink-6: hsl(var(--pink-6-hsl)/1); - --pink-6-hsl: 315.556 calc(var(--saturation-factor, 1)*100%) 89.412%; - --pink-7: hsl(var(--pink-7-hsl)/1); - --pink-7-hsl: 314.737 calc(var(--saturation-factor, 1)*100%) 88.824%; - --pink-8: hsl(var(--pink-8-hsl)/1); - --pink-8-hsl: 315 calc(var(--saturation-factor, 1)*100%) 88.235%; - --pink-9: hsl(var(--pink-9-hsl)/1); - --pink-9-hsl: 314.516 calc(var(--saturation-factor, 1)*100%) 87.843%; - --pink-10: hsl(var(--pink-10-hsl)/1); - --pink-10-hsl: 314.769 calc(var(--saturation-factor, 1)*100%) 87.255%; - --pink-11: hsl(var(--pink-11-hsl)/1); - --pink-11-hsl: 314.118 calc(var(--saturation-factor, 1)*100%) 86.667%; - --pink-12: hsl(var(--pink-12-hsl)/1); - --pink-12-hsl: 314.366 calc(var(--saturation-factor, 1)*100%) 86.078%; - --pink-13: hsl(var(--pink-13-hsl)/1); - --pink-13-hsl: 314.795 calc(var(--saturation-factor, 1)*100%) 85.686%; - --pink-14: hsl(var(--pink-14-hsl)/1); - --pink-14-hsl: 315 calc(var(--saturation-factor, 1)*100%) 85.098%; - --pink-15: hsl(var(--pink-15-hsl)/1); - --pink-15-hsl: 314.615 calc(var(--saturation-factor, 1)*100%) 84.706%; - --pink-16: hsl(var(--pink-16-hsl)/1); - --pink-16-hsl: 314.815 calc(var(--saturation-factor, 1)*100%) 84.118%; - --pink-17: hsl(var(--pink-17-hsl)/1); - --pink-17-hsl: 315.181 calc(var(--saturation-factor, 1)*100%) 83.725%; - --pink-18: hsl(var(--pink-18-hsl)/1); - --pink-18-hsl: 315.349 calc(var(--saturation-factor, 1)*100%) 83.137%; - --pink-19: hsl(var(--pink-19-hsl)/1); - --pink-19-hsl: 315 calc(var(--saturation-factor, 1)*100%) 82.745%; - --pink-20: hsl(var(--pink-20-hsl)/1); - --pink-20-hsl: 315.165 calc(var(--saturation-factor, 1)*100%) 82.157%; - --pink-21: hsl(var(--pink-21-hsl)/1); - --pink-21-hsl: 315.319 calc(var(--saturation-factor, 1)*100%) 81.569%; - --pink-22: hsl(var(--pink-22-hsl)/1); - --pink-22-hsl: 315 calc(var(--saturation-factor, 1)*100%) 81.176%; - --pink-23: hsl(var(--pink-23-hsl)/1); - --pink-23-hsl: 315.152 calc(var(--saturation-factor, 1)*100%) 80.588%; - --pink-24: hsl(var(--pink-24-hsl)/1); - --pink-24-hsl: 315.294 calc(var(--saturation-factor, 1)*100%) 80%; - --pink-25: hsl(var(--pink-25-hsl)/1); - --pink-25-hsl: 315 calc(var(--saturation-factor, 1)*100%) 79.608%; - --pink-26: hsl(var(--pink-26-hsl)/1); - --pink-26-hsl: 315.14 calc(var(--saturation-factor, 1)*100%) 79.02%; - --pink-27: hsl(var(--pink-27-hsl)/1); - --pink-27-hsl: 315.273 calc(var(--saturation-factor, 1)*100%) 78.431%; - --pink-28: hsl(var(--pink-28-hsl)/1); - --pink-28-hsl: 314.867 calc(var(--saturation-factor, 1)*100%) 77.843%; - --pink-29: hsl(var(--pink-29-hsl)/1); - --pink-29-hsl: 315 calc(var(--saturation-factor, 1)*100%) 77.255%; - --pink-30: hsl(var(--pink-30-hsl)/1); - --pink-30-hsl: 315.126 calc(var(--saturation-factor, 1)*100%) 76.667%; - --pink-31: hsl(var(--pink-31-hsl)/1); - --pink-31-hsl: 315.372 calc(var(--saturation-factor, 1)*100%) 76.275%; - --pink-32: hsl(var(--pink-32-hsl)/1); - --pink-32-hsl: 315 calc(var(--saturation-factor, 1)*100%) 75.686%; - --pink-33: hsl(var(--pink-33-hsl)/1); - --pink-33-hsl: 315.118 calc(var(--saturation-factor, 1)*100%) 75.098%; - --pink-34: hsl(var(--pink-34-hsl)/1); - --pink-34-hsl: 315.231 calc(var(--saturation-factor, 1)*100%) 74.51%; - --pink-35: hsl(var(--pink-35-hsl)/1); - --pink-35-hsl: 315.455 calc(var(--saturation-factor, 1)*100%) 74.118%; - --pink-36: hsl(var(--pink-36-hsl)/1); - --pink-36-hsl: 315.111 calc(var(--saturation-factor, 1)*100%) 73.529%; - --pink-37: hsl(var(--pink-37-hsl)/1); - --pink-37-hsl: 315.217 calc(var(--saturation-factor, 1)*100%) 72.941%; - --pink-38: hsl(var(--pink-38-hsl)/1); - --pink-38-hsl: 315.319 calc(var(--saturation-factor, 1)*100%) 72.353%; - --pink-39: hsl(var(--pink-39-hsl)/1); - --pink-39-hsl: 315.417 calc(var(--saturation-factor, 1)*100%) 71.765%; - --pink-40: hsl(var(--pink-40-hsl)/1); - --pink-40-hsl: 315.51 calc(var(--saturation-factor, 1)*100%) 71.176%; - --pink-41: hsl(var(--pink-41-hsl)/1); - --pink-41-hsl: 315.2 calc(var(--saturation-factor, 1)*100%) 70.588%; - --pink-42: hsl(var(--pink-42-hsl)/1); - --pink-42-hsl: 315.294 calc(var(--saturation-factor, 1)*100%) 70%; - --pink-43: hsl(var(--pink-43-hsl)/1); - --pink-43-hsl: 315.385 calc(var(--saturation-factor, 1)*100%) 69.412%; - --pink-44: hsl(var(--pink-44-hsl)/1); - --pink-44-hsl: 315.472 calc(var(--saturation-factor, 1)*100%) 68.824%; - --pink-45: hsl(var(--pink-45-hsl)/1); - --pink-45-hsl: 315.185 calc(var(--saturation-factor, 1)*100%) 68.235%; - --pink-46: hsl(var(--pink-46-hsl)/1); - --pink-46-hsl: 315.181 calc(var(--saturation-factor, 1)*100%) 67.451%; - --pink-47: hsl(var(--pink-47-hsl)/1); - --pink-47-hsl: 315.266 calc(var(--saturation-factor, 1)*100%) 66.863%; - --pink-48: hsl(var(--pink-48-hsl)/1); - --pink-48-hsl: 315.349 calc(var(--saturation-factor, 1)*100%) 66.275%; - --pink-49: hsl(var(--pink-49-hsl)/1); - --pink-49-hsl: 315 calc(var(--saturation-factor, 1)*100%) 65.49%; - --pink-50: hsl(var(--pink-50-hsl)/1); - --pink-50-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --pink-51: hsl(var(--pink-51-hsl)/1); - --pink-51-hsl: 315.341 calc(var(--saturation-factor, 1)*94.624%) 63.529%; - --pink-52: hsl(var(--pink-52-hsl)/1); - --pink-52-hsl: 315.349 calc(var(--saturation-factor, 1)*89.583%) 62.353%; - --pink-53: hsl(var(--pink-53-hsl)/1); - --pink-53-hsl: 315.357 calc(var(--saturation-factor, 1)*84%) 60.784%; - --pink-54: hsl(var(--pink-54-hsl)/1); - --pink-54-hsl: 315.366 calc(var(--saturation-factor, 1)*79.612%) 59.608%; - --pink-55: hsl(var(--pink-55-hsl)/1); - --pink-55-hsl: 315.375 calc(var(--saturation-factor, 1)*74.766%) 58.039%; - --pink-56: hsl(var(--pink-56-hsl)/1); - --pink-56-hsl: 315.287 calc(var(--saturation-factor, 1)*71.041%) 56.667%; - --pink-57: hsl(var(--pink-57-hsl)/1); - --pink-57-hsl: 315.294 calc(var(--saturation-factor, 1)*67.401%) 55.49%; - --pink-58: hsl(var(--pink-58-hsl)/1); - --pink-58-hsl: 315.6 calc(var(--saturation-factor, 1)*64.103%) 54.118%; - --pink-59: hsl(var(--pink-59-hsl)/1); - --pink-59-hsl: 315.51 calc(var(--saturation-factor, 1)*60.996%) 52.745%; - --pink-60: hsl(var(--pink-60-hsl)/1); - --pink-60-hsl: 315.417 calc(var(--saturation-factor, 1)*58.065%) 51.373%; - --pink-61: hsl(var(--pink-61-hsl)/1); - --pink-61-hsl: 315.319 calc(var(--saturation-factor, 1)*55.294%) 50%; - --pink-62: hsl(var(--pink-62-hsl)/1); - --pink-62-hsl: 315.766 calc(var(--saturation-factor, 1)*55.02%) 48.824%; - --pink-63: hsl(var(--pink-63-hsl)/1); - --pink-63-hsl: 315.672 calc(var(--saturation-factor, 1)*55.372%) 47.451%; - --pink-64: hsl(var(--pink-64-hsl)/1); - --pink-64-hsl: 315.573 calc(var(--saturation-factor, 1)*55.745%) 46.078%; - --pink-65: hsl(var(--pink-65-hsl)/1); - --pink-65-hsl: 315.469 calc(var(--saturation-factor, 1)*56.14%) 44.706%; - --pink-66: hsl(var(--pink-66-hsl)/1); - --pink-66-hsl: 315.714 calc(var(--saturation-factor, 1)*56.757%) 43.529%; - --pink-67: hsl(var(--pink-67-hsl)/1); - --pink-67-hsl: 315.61 calc(var(--saturation-factor, 1)*57.209%) 42.157%; - --pink-68: hsl(var(--pink-68-hsl)/1); - --pink-68-hsl: 315.372 calc(var(--saturation-factor, 1)*58.454%) 40.588%; - --pink-69: hsl(var(--pink-69-hsl)/1); - --pink-69-hsl: 315.126 calc(var(--saturation-factor, 1)*59.204%) 39.412%; - --pink-70: hsl(var(--pink-70-hsl)/1); - --pink-70-hsl: 315 calc(var(--saturation-factor, 1)*59.794%) 38.039%; - --pink-71: hsl(var(--pink-71-hsl)/1); - --pink-71-hsl: 315.263 calc(var(--saturation-factor, 1)*60.638%) 36.863%; - --pink-72: hsl(var(--pink-72-hsl)/1); - --pink-72-hsl: 315.135 calc(var(--saturation-factor, 1)*61.326%) 35.49%; - --pink-73: hsl(var(--pink-73-hsl)/1); - --pink-73-hsl: 315.413 calc(var(--saturation-factor, 1)*62.286%) 34.314%; - --pink-74: hsl(var(--pink-74-hsl)/1); - --pink-74-hsl: 315.283 calc(var(--saturation-factor, 1)*63.095%) 32.941%; - --pink-75: hsl(var(--pink-75-hsl)/1); - --pink-75-hsl: 315 calc(var(--saturation-factor, 1)*64.198%) 31.765%; - --pink-76: hsl(var(--pink-76-hsl)/1); - --pink-76-hsl: 314.851 calc(var(--saturation-factor, 1)*65.161%) 30.392%; - --pink-77: hsl(var(--pink-77-hsl)/1); - --pink-77-hsl: 315 calc(var(--saturation-factor, 1)*67.568%) 29.02%; - --pink-78: hsl(var(--pink-78-hsl)/1); - --pink-78-hsl: 314.694 calc(var(--saturation-factor, 1)*69.014%) 27.843%; - --pink-79: hsl(var(--pink-79-hsl)/1); - --pink-79-hsl: 314.526 calc(var(--saturation-factor, 1)*70.37%) 26.471%; - --pink-80: hsl(var(--pink-80-hsl)/1); - --pink-80-hsl: 314.839 calc(var(--saturation-factor, 1)*72.093%) 25.294%; - --pink-81: hsl(var(--pink-81-hsl)/1); - --pink-81-hsl: 314.505 calc(var(--saturation-factor, 1)*73.984%) 24.118%; - --pink-82: hsl(var(--pink-82-hsl)/1); - --pink-82-hsl: 314.157 calc(var(--saturation-factor, 1)*77.391%) 22.549%; - --pink-83: hsl(var(--pink-83-hsl)/1); - --pink-83-hsl: 313.793 calc(var(--saturation-factor, 1)*79.817%) 21.373%; - --pink-84: hsl(var(--pink-84-hsl)/1); - --pink-84-hsl: 314.118 calc(var(--saturation-factor, 1)*82.524%) 20.196%; - --pink-85: hsl(var(--pink-85-hsl)/1); - --pink-85-hsl: 313.735 calc(var(--saturation-factor, 1)*85.567%) 19.02%; - --pink-86: hsl(var(--pink-86-hsl)/1); - --pink-86-hsl: 314.074 calc(var(--saturation-factor, 1)*89.011%) 17.843%; - --pink-87: hsl(var(--pink-87-hsl)/1); - --pink-87-hsl: 313.671 calc(var(--saturation-factor, 1)*92.941%) 16.667%; - --pink-88: hsl(var(--pink-88-hsl)/1); - --pink-88-hsl: 313.421 calc(var(--saturation-factor, 1)*95%) 15.686%; - --pink-89: hsl(var(--pink-89-hsl)/1); - --pink-89-hsl: 313.973 calc(var(--saturation-factor, 1)*97.333%) 14.706%; - --pink-90: hsl(var(--pink-90-hsl)/1); - --pink-90-hsl: 313.714 calc(var(--saturation-factor, 1)*100%) 13.725%; - --pink-91: hsl(var(--pink-91-hsl)/1); - --pink-91-hsl: 313.636 calc(var(--saturation-factor, 1)*100%) 12.941%; - --pink-92: hsl(var(--pink-92-hsl)/1); - --pink-92-hsl: 314.516 calc(var(--saturation-factor, 1)*100%) 12.157%; - --pink-93: hsl(var(--pink-93-hsl)/1); - --pink-93-hsl: 314.483 calc(var(--saturation-factor, 1)*100%) 11.373%; - --pink-94: hsl(var(--pink-94-hsl)/1); - --pink-94-hsl: 314.444 calc(var(--saturation-factor, 1)*100%) 10.588%; - --pink-95: hsl(var(--pink-95-hsl)/1); - --pink-95-hsl: 315.6 calc(var(--saturation-factor, 1)*100%) 9.804%; - --pink-96: hsl(var(--pink-96-hsl)/1); - --pink-96-hsl: 314.667 calc(var(--saturation-factor, 1)*100%) 8.824%; - --pink-97: hsl(var(--pink-97-hsl)/1); - --pink-97-hsl: 316.5 calc(var(--saturation-factor, 1)*100%) 7.843%; - --pink-98: hsl(var(--pink-98-hsl)/1); - --pink-98-hsl: 316.364 calc(var(--saturation-factor, 1)*100%) 6.471%; - --pink-99: hsl(var(--pink-99-hsl)/1); - --pink-99-hsl: 319.2 calc(var(--saturation-factor, 1)*100%) 4.902%; - --pink-100: hsl(var(--pink-100-hsl)/1); - --pink-100-hsl: 327.273 calc(var(--saturation-factor, 1)*100%) 2.157%; - --opacity-1: hsl(var(--opacity-1-hsl)/0.0196078431372549); - --opacity-1-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-4: hsl(var(--opacity-4-hsl)/0.0392156862745098); - --opacity-4-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-8: hsl(var(--opacity-8-hsl)/0.0784313725490196); - --opacity-8-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-12: hsl(var(--opacity-12-hsl)/0.12156862745098039); - --opacity-12-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-16: hsl(var(--opacity-16-hsl)/0.1607843137254902); - --opacity-16-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-20: hsl(var(--opacity-20-hsl)/0.2); - --opacity-20-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-24: hsl(var(--opacity-24-hsl)/0.23921568627450981); - --opacity-24-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-28: hsl(var(--opacity-28-hsl)/0.2784313725490196); - --opacity-28-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-32: hsl(var(--opacity-32-hsl)/0.3215686274509804); - --opacity-32-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-36: hsl(var(--opacity-36-hsl)/0.3607843137254902); - --opacity-36-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-40: hsl(var(--opacity-40-hsl)/0.4); - --opacity-40-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-44: hsl(var(--opacity-44-hsl)/0.4392156862745098); - --opacity-44-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-48: hsl(var(--opacity-48-hsl)/0.47843137254901963); - --opacity-48-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-52: hsl(var(--opacity-52-hsl)/0.5215686274509804); - --opacity-52-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-56: hsl(var(--opacity-56-hsl)/0.5607843137254902); - --opacity-56-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-60: hsl(var(--opacity-60-hsl)/0.6); - --opacity-60-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-64: hsl(var(--opacity-64-hsl)/0.6392156862745098); - --opacity-64-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-68: hsl(var(--opacity-68-hsl)/0.6784313725490196); - --opacity-68-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-72: hsl(var(--opacity-72-hsl)/0.7215686274509804); - --opacity-72-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-76: hsl(var(--opacity-76-hsl)/0.7607843137254902); - --opacity-76-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-80: hsl(var(--opacity-80-hsl)/0.8); - --opacity-80-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-84: hsl(var(--opacity-84-hsl)/0.8392156862745098); - --opacity-84-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-88: hsl(var(--opacity-88-hsl)/0.8784313725490196); - --opacity-88-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-92: hsl(var(--opacity-92-hsl)/0.9215686274509803); - --opacity-92-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-96: hsl(var(--opacity-96-hsl)/0.9607843137254902); - --opacity-96-hsl: 240 calc(var(--saturation-factor, 1)*4%) 60.784%; - --opacity-blue-1: hsl(var(--opacity-blue-1-hsl)/0.0196078431372549); - --opacity-blue-1-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-4: hsl(var(--opacity-blue-4-hsl)/0.0392156862745098); - --opacity-blue-4-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-8: hsl(var(--opacity-blue-8-hsl)/0.0784313725490196); - --opacity-blue-8-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-12: hsl(var(--opacity-blue-12-hsl)/0.12156862745098039); - --opacity-blue-12-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-16: hsl(var(--opacity-blue-16-hsl)/0.1607843137254902); - --opacity-blue-16-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-20: hsl(var(--opacity-blue-20-hsl)/0.2); - --opacity-blue-20-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-24: hsl(var(--opacity-blue-24-hsl)/0.23921568627450981); - --opacity-blue-24-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-28: hsl(var(--opacity-blue-28-hsl)/0.2784313725490196); - --opacity-blue-28-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-32: hsl(var(--opacity-blue-32-hsl)/0.3215686274509804); - --opacity-blue-32-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-36: hsl(var(--opacity-blue-36-hsl)/0.3607843137254902); - --opacity-blue-36-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-40: hsl(var(--opacity-blue-40-hsl)/0.4); - --opacity-blue-40-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-44: hsl(var(--opacity-blue-44-hsl)/0.4392156862745098); - --opacity-blue-44-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-48: hsl(var(--opacity-blue-48-hsl)/0.47843137254901963); - --opacity-blue-48-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-52: hsl(var(--opacity-blue-52-hsl)/0.5215686274509804); - --opacity-blue-52-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-56: hsl(var(--opacity-blue-56-hsl)/0.5607843137254902); - --opacity-blue-56-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-60: hsl(var(--opacity-blue-60-hsl)/0.6); - --opacity-blue-60-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-64: hsl(var(--opacity-blue-64-hsl)/0.6392156862745098); - --opacity-blue-64-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-68: hsl(var(--opacity-blue-68-hsl)/0.6784313725490196); - --opacity-blue-68-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-72: hsl(var(--opacity-blue-72-hsl)/0.7215686274509804); - --opacity-blue-72-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-76: hsl(var(--opacity-blue-76-hsl)/0.7607843137254902); - --opacity-blue-76-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-80: hsl(var(--opacity-blue-80-hsl)/0.8); - --opacity-blue-80-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-84: hsl(var(--opacity-blue-84-hsl)/0.8392156862745098); - --opacity-blue-84-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-88: hsl(var(--opacity-blue-88-hsl)/0.8784313725490196); - --opacity-blue-88-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-92: hsl(var(--opacity-blue-92-hsl)/0.9215686274509803); - --opacity-blue-92-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blue-96: hsl(var(--opacity-blue-96-hsl)/0.9607843137254902); - --opacity-blue-96-hsl: 209.339 calc(var(--saturation-factor, 1)*100%) 44.51%; - --opacity-blurple-1: hsl(var(--opacity-blurple-1-hsl)/0.0196078431372549); - --opacity-blurple-1-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-4: hsl(var(--opacity-blurple-4-hsl)/0.0392156862745098); - --opacity-blurple-4-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-8: hsl(var(--opacity-blurple-8-hsl)/0.0784313725490196); - --opacity-blurple-8-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-12: hsl(var(--opacity-blurple-12-hsl)/0.12156862745098039); - --opacity-blurple-12-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-16: hsl(var(--opacity-blurple-16-hsl)/0.1607843137254902); - --opacity-blurple-16-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-20: hsl(var(--opacity-blurple-20-hsl)/0.2); - --opacity-blurple-20-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-24: hsl(var(--opacity-blurple-24-hsl)/0.23921568627450981); - --opacity-blurple-24-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-28: hsl(var(--opacity-blurple-28-hsl)/0.2784313725490196); - --opacity-blurple-28-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-32: hsl(var(--opacity-blurple-32-hsl)/0.3215686274509804); - --opacity-blurple-32-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-36: hsl(var(--opacity-blurple-36-hsl)/0.3607843137254902); - --opacity-blurple-36-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-40: hsl(var(--opacity-blurple-40-hsl)/0.4); - --opacity-blurple-40-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-44: hsl(var(--opacity-blurple-44-hsl)/0.4392156862745098); - --opacity-blurple-44-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-48: hsl(var(--opacity-blurple-48-hsl)/0.47843137254901963); - --opacity-blurple-48-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-52: hsl(var(--opacity-blurple-52-hsl)/0.5215686274509804); - --opacity-blurple-52-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-56: hsl(var(--opacity-blurple-56-hsl)/0.5607843137254902); - --opacity-blurple-56-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-60: hsl(var(--opacity-blurple-60-hsl)/0.6); - --opacity-blurple-60-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-64: hsl(var(--opacity-blurple-64-hsl)/0.6392156862745098); - --opacity-blurple-64-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-68: hsl(var(--opacity-blurple-68-hsl)/0.6784313725490196); - --opacity-blurple-68-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-72: hsl(var(--opacity-blurple-72-hsl)/0.7215686274509804); - --opacity-blurple-72-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-76: hsl(var(--opacity-blurple-76-hsl)/0.7607843137254902); - --opacity-blurple-76-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-80: hsl(var(--opacity-blurple-80-hsl)/0.8); - --opacity-blurple-80-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-84: hsl(var(--opacity-blurple-84-hsl)/0.8392156862745098); - --opacity-blurple-84-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-88: hsl(var(--opacity-blurple-88-hsl)/0.8784313725490196); - --opacity-blurple-88-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-92: hsl(var(--opacity-blurple-92-hsl)/0.9215686274509803); - --opacity-blurple-92-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-blurple-96: hsl(var(--opacity-blurple-96-hsl)/0.9607843137254902); - --opacity-blurple-96-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --opacity-green-1: hsl(var(--opacity-green-1-hsl)/0.0196078431372549); - --opacity-green-1-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-4: hsl(var(--opacity-green-4-hsl)/0.0392156862745098); - --opacity-green-4-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-8: hsl(var(--opacity-green-8-hsl)/0.0784313725490196); - --opacity-green-8-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-12: hsl(var(--opacity-green-12-hsl)/0.12156862745098039); - --opacity-green-12-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-16: hsl(var(--opacity-green-16-hsl)/0.1607843137254902); - --opacity-green-16-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-20: hsl(var(--opacity-green-20-hsl)/0.2); - --opacity-green-20-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-24: hsl(var(--opacity-green-24-hsl)/0.23921568627450981); - --opacity-green-24-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-28: hsl(var(--opacity-green-28-hsl)/0.2784313725490196); - --opacity-green-28-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-32: hsl(var(--opacity-green-32-hsl)/0.3215686274509804); - --opacity-green-32-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-36: hsl(var(--opacity-green-36-hsl)/0.3607843137254902); - --opacity-green-36-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-40: hsl(var(--opacity-green-40-hsl)/0.4); - --opacity-green-40-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-44: hsl(var(--opacity-green-44-hsl)/0.4392156862745098); - --opacity-green-44-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-48: hsl(var(--opacity-green-48-hsl)/0.47843137254901963); - --opacity-green-48-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-52: hsl(var(--opacity-green-52-hsl)/0.5215686274509804); - --opacity-green-52-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-56: hsl(var(--opacity-green-56-hsl)/0.5607843137254902); - --opacity-green-56-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-60: hsl(var(--opacity-green-60-hsl)/0.6); - --opacity-green-60-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-64: hsl(var(--opacity-green-64-hsl)/0.6392156862745098); - --opacity-green-64-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-68: hsl(var(--opacity-green-68-hsl)/0.6784313725490196); - --opacity-green-68-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-72: hsl(var(--opacity-green-72-hsl)/0.7215686274509804); - --opacity-green-72-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-76: hsl(var(--opacity-green-76-hsl)/0.7607843137254902); - --opacity-green-76-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-80: hsl(var(--opacity-green-80-hsl)/0.8); - --opacity-green-80-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-84: hsl(var(--opacity-green-84-hsl)/0.8392156862745098); - --opacity-green-84-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-88: hsl(var(--opacity-green-88-hsl)/0.8784313725490196); - --opacity-green-88-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-92: hsl(var(--opacity-green-92-hsl)/0.9215686274509803); - --opacity-green-92-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-green-96: hsl(var(--opacity-green-96-hsl)/0.9607843137254902); - --opacity-green-96-hsl: 151.128 calc(var(--saturation-factor, 1)*100%) 26.078%; - --opacity-red-1: hsl(var(--opacity-red-1-hsl)/0.0196078431372549); - --opacity-red-1-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-4: hsl(var(--opacity-red-4-hsl)/0.0392156862745098); - --opacity-red-4-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-8: hsl(var(--opacity-red-8-hsl)/0.0784313725490196); - --opacity-red-8-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-12: hsl(var(--opacity-red-12-hsl)/0.12156862745098039); - --opacity-red-12-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-16: hsl(var(--opacity-red-16-hsl)/0.1607843137254902); - --opacity-red-16-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-20: hsl(var(--opacity-red-20-hsl)/0.2); - --opacity-red-20-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-24: hsl(var(--opacity-red-24-hsl)/0.23921568627450981); - --opacity-red-24-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-28: hsl(var(--opacity-red-28-hsl)/0.2784313725490196); - --opacity-red-28-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-32: hsl(var(--opacity-red-32-hsl)/0.3215686274509804); - --opacity-red-32-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-36: hsl(var(--opacity-red-36-hsl)/0.3607843137254902); - --opacity-red-36-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-40: hsl(var(--opacity-red-40-hsl)/0.4); - --opacity-red-40-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-44: hsl(var(--opacity-red-44-hsl)/0.4392156862745098); - --opacity-red-44-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-48: hsl(var(--opacity-red-48-hsl)/0.47843137254901963); - --opacity-red-48-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-52: hsl(var(--opacity-red-52-hsl)/0.5215686274509804); - --opacity-red-52-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-56: hsl(var(--opacity-red-56-hsl)/0.5607843137254902); - --opacity-red-56-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-60: hsl(var(--opacity-red-60-hsl)/0.6); - --opacity-red-60-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-64: hsl(var(--opacity-red-64-hsl)/0.6392156862745098); - --opacity-red-64-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-68: hsl(var(--opacity-red-68-hsl)/0.6784313725490196); - --opacity-red-68-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-72: hsl(var(--opacity-red-72-hsl)/0.7215686274509804); - --opacity-red-72-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-76: hsl(var(--opacity-red-76-hsl)/0.7607843137254902); - --opacity-red-76-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-80: hsl(var(--opacity-red-80-hsl)/0.8); - --opacity-red-80-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-84: hsl(var(--opacity-red-84-hsl)/0.8392156862745098); - --opacity-red-84-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-88: hsl(var(--opacity-red-88-hsl)/0.8784313725490196); - --opacity-red-88-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-92: hsl(var(--opacity-red-92-hsl)/0.9215686274509803); - --opacity-red-92-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-red-96: hsl(var(--opacity-red-96-hsl)/0.9607843137254902); - --opacity-red-96-hsl: 355.636 calc(var(--saturation-factor, 1)*64.706%) 50%; - --opacity-teal-1: hsl(var(--opacity-teal-1-hsl)/0.0196078431372549); - --opacity-teal-1-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-4: hsl(var(--opacity-teal-4-hsl)/0.0392156862745098); - --opacity-teal-4-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-8: hsl(var(--opacity-teal-8-hsl)/0.0784313725490196); - --opacity-teal-8-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-12: hsl(var(--opacity-teal-12-hsl)/0.12156862745098039); - --opacity-teal-12-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-16: hsl(var(--opacity-teal-16-hsl)/0.1607843137254902); - --opacity-teal-16-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-20: hsl(var(--opacity-teal-20-hsl)/0.2); - --opacity-teal-20-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-24: hsl(var(--opacity-teal-24-hsl)/0.23921568627450981); - --opacity-teal-24-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-28: hsl(var(--opacity-teal-28-hsl)/0.2784313725490196); - --opacity-teal-28-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-32: hsl(var(--opacity-teal-32-hsl)/0.3215686274509804); - --opacity-teal-32-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-36: hsl(var(--opacity-teal-36-hsl)/0.3607843137254902); - --opacity-teal-36-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-40: hsl(var(--opacity-teal-40-hsl)/0.4); - --opacity-teal-40-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-44: hsl(var(--opacity-teal-44-hsl)/0.4392156862745098); - --opacity-teal-44-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-48: hsl(var(--opacity-teal-48-hsl)/0.47843137254901963); - --opacity-teal-48-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-52: hsl(var(--opacity-teal-52-hsl)/0.5215686274509804); - --opacity-teal-52-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-56: hsl(var(--opacity-teal-56-hsl)/0.5607843137254902); - --opacity-teal-56-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-60: hsl(var(--opacity-teal-60-hsl)/0.6); - --opacity-teal-60-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-64: hsl(var(--opacity-teal-64-hsl)/0.6392156862745098); - --opacity-teal-64-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-68: hsl(var(--opacity-teal-68-hsl)/0.6784313725490196); - --opacity-teal-68-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-72: hsl(var(--opacity-teal-72-hsl)/0.7215686274509804); - --opacity-teal-72-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-76: hsl(var(--opacity-teal-76-hsl)/0.7607843137254902); - --opacity-teal-76-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-80: hsl(var(--opacity-teal-80-hsl)/0.8); - --opacity-teal-80-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-84: hsl(var(--opacity-teal-84-hsl)/0.8392156862745098); - --opacity-teal-84-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-88: hsl(var(--opacity-teal-88-hsl)/0.8784313725490196); - --opacity-teal-88-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-92: hsl(var(--opacity-teal-92-hsl)/0.9215686274509803); - --opacity-teal-92-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-teal-96: hsl(var(--opacity-teal-96-hsl)/0.9607843137254902); - --opacity-teal-96-hsl: 184.615 calc(var(--saturation-factor, 1)*100%) 28.039%; - --opacity-yellow-1: hsl(var(--opacity-yellow-1-hsl)/0.0196078431372549); - --opacity-yellow-1-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-4: hsl(var(--opacity-yellow-4-hsl)/0.0392156862745098); - --opacity-yellow-4-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-8: hsl(var(--opacity-yellow-8-hsl)/0.0784313725490196); - --opacity-yellow-8-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-12: hsl(var(--opacity-yellow-12-hsl)/0.12156862745098039); - --opacity-yellow-12-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-16: hsl(var(--opacity-yellow-16-hsl)/0.1607843137254902); - --opacity-yellow-16-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-20: hsl(var(--opacity-yellow-20-hsl)/0.2); - --opacity-yellow-20-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-24: hsl(var(--opacity-yellow-24-hsl)/0.23921568627450981); - --opacity-yellow-24-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-28: hsl(var(--opacity-yellow-28-hsl)/0.2784313725490196); - --opacity-yellow-28-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-32: hsl(var(--opacity-yellow-32-hsl)/0.3215686274509804); - --opacity-yellow-32-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-36: hsl(var(--opacity-yellow-36-hsl)/0.3607843137254902); - --opacity-yellow-36-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-40: hsl(var(--opacity-yellow-40-hsl)/0.4); - --opacity-yellow-40-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-44: hsl(var(--opacity-yellow-44-hsl)/0.4392156862745098); - --opacity-yellow-44-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-48: hsl(var(--opacity-yellow-48-hsl)/0.47843137254901963); - --opacity-yellow-48-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-52: hsl(var(--opacity-yellow-52-hsl)/0.5215686274509804); - --opacity-yellow-52-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-56: hsl(var(--opacity-yellow-56-hsl)/0.5607843137254902); - --opacity-yellow-56-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-60: hsl(var(--opacity-yellow-60-hsl)/0.6); - --opacity-yellow-60-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-64: hsl(var(--opacity-yellow-64-hsl)/0.6392156862745098); - --opacity-yellow-64-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-68: hsl(var(--opacity-yellow-68-hsl)/0.6784313725490196); - --opacity-yellow-68-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-72: hsl(var(--opacity-yellow-72-hsl)/0.7215686274509804); - --opacity-yellow-72-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-76: hsl(var(--opacity-yellow-76-hsl)/0.7607843137254902); - --opacity-yellow-76-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-80: hsl(var(--opacity-yellow-80-hsl)/0.8); - --opacity-yellow-80-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-84: hsl(var(--opacity-yellow-84-hsl)/0.8392156862745098); - --opacity-yellow-84-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-88: hsl(var(--opacity-yellow-88-hsl)/0.8784313725490196); - --opacity-yellow-88-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-92: hsl(var(--opacity-yellow-92-hsl)/0.9215686274509803); - --opacity-yellow-92-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-yellow-96: hsl(var(--opacity-yellow-96-hsl)/0.9607843137254902); - --opacity-yellow-96-hsl: 39.435 calc(var(--saturation-factor, 1)*100%) 48.627%; - --opacity-orange-1: hsl(var(--opacity-orange-1-hsl)/0.0196078431372549); - --opacity-orange-1-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-4: hsl(var(--opacity-orange-4-hsl)/0.0392156862745098); - --opacity-orange-4-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-8: hsl(var(--opacity-orange-8-hsl)/0.0784313725490196); - --opacity-orange-8-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-12: hsl(var(--opacity-orange-12-hsl)/0.12156862745098039); - --opacity-orange-12-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-16: hsl(var(--opacity-orange-16-hsl)/0.1607843137254902); - --opacity-orange-16-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-20: hsl(var(--opacity-orange-20-hsl)/0.2); - --opacity-orange-20-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-24: hsl(var(--opacity-orange-24-hsl)/0.23921568627450981); - --opacity-orange-24-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-28: hsl(var(--opacity-orange-28-hsl)/0.2784313725490196); - --opacity-orange-28-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-32: hsl(var(--opacity-orange-32-hsl)/0.3215686274509804); - --opacity-orange-32-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-36: hsl(var(--opacity-orange-36-hsl)/0.3607843137254902); - --opacity-orange-36-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-40: hsl(var(--opacity-orange-40-hsl)/0.4); - --opacity-orange-40-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-44: hsl(var(--opacity-orange-44-hsl)/0.4392156862745098); - --opacity-orange-44-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-48: hsl(var(--opacity-orange-48-hsl)/0.47843137254901963); - --opacity-orange-48-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-52: hsl(var(--opacity-orange-52-hsl)/0.5215686274509804); - --opacity-orange-52-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-56: hsl(var(--opacity-orange-56-hsl)/0.5607843137254902); - --opacity-orange-56-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-60: hsl(var(--opacity-orange-60-hsl)/0.6); - --opacity-orange-60-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-64: hsl(var(--opacity-orange-64-hsl)/0.6392156862745098); - --opacity-orange-64-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-68: hsl(var(--opacity-orange-68-hsl)/0.6784313725490196); - --opacity-orange-68-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-72: hsl(var(--opacity-orange-72-hsl)/0.7215686274509804); - --opacity-orange-72-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-76: hsl(var(--opacity-orange-76-hsl)/0.7607843137254902); - --opacity-orange-76-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-80: hsl(var(--opacity-orange-80-hsl)/0.8); - --opacity-orange-80-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-84: hsl(var(--opacity-orange-84-hsl)/0.8392156862745098); - --opacity-orange-84-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-88: hsl(var(--opacity-orange-88-hsl)/0.8784313725490196); - --opacity-orange-88-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-92: hsl(var(--opacity-orange-92-hsl)/0.9215686274509803); - --opacity-orange-92-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-orange-96: hsl(var(--opacity-orange-96-hsl)/0.9607843137254902); - --opacity-orange-96-hsl: 25.079 calc(var(--saturation-factor, 1)*100%) 37.059%; - --opacity-pink-1: hsl(var(--opacity-pink-1-hsl)/0.0196078431372549); - --opacity-pink-1-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-4: hsl(var(--opacity-pink-4-hsl)/0.0392156862745098); - --opacity-pink-4-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-8: hsl(var(--opacity-pink-8-hsl)/0.0784313725490196); - --opacity-pink-8-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-12: hsl(var(--opacity-pink-12-hsl)/0.12156862745098039); - --opacity-pink-12-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-16: hsl(var(--opacity-pink-16-hsl)/0.1607843137254902); - --opacity-pink-16-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-20: hsl(var(--opacity-pink-20-hsl)/0.2); - --opacity-pink-20-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-24: hsl(var(--opacity-pink-24-hsl)/0.23921568627450981); - --opacity-pink-24-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-28: hsl(var(--opacity-pink-28-hsl)/0.2784313725490196); - --opacity-pink-28-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-32: hsl(var(--opacity-pink-32-hsl)/0.3215686274509804); - --opacity-pink-32-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-36: hsl(var(--opacity-pink-36-hsl)/0.3607843137254902); - --opacity-pink-36-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-40: hsl(var(--opacity-pink-40-hsl)/0.4); - --opacity-pink-40-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-44: hsl(var(--opacity-pink-44-hsl)/0.4392156862745098); - --opacity-pink-44-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-48: hsl(var(--opacity-pink-48-hsl)/0.47843137254901963); - --opacity-pink-48-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-52: hsl(var(--opacity-pink-52-hsl)/0.5215686274509804); - --opacity-pink-52-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-56: hsl(var(--opacity-pink-56-hsl)/0.5607843137254902); - --opacity-pink-56-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-60: hsl(var(--opacity-pink-60-hsl)/0.6); - --opacity-pink-60-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-64: hsl(var(--opacity-pink-64-hsl)/0.6392156862745098); - --opacity-pink-64-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-68: hsl(var(--opacity-pink-68-hsl)/0.6784313725490196); - --opacity-pink-68-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-72: hsl(var(--opacity-pink-72-hsl)/0.7215686274509804); - --opacity-pink-72-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-76: hsl(var(--opacity-pink-76-hsl)/0.7607843137254902); - --opacity-pink-76-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-80: hsl(var(--opacity-pink-80-hsl)/0.8); - --opacity-pink-80-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-84: hsl(var(--opacity-pink-84-hsl)/0.8392156862745098); - --opacity-pink-84-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-88: hsl(var(--opacity-pink-88-hsl)/0.8784313725490196); - --opacity-pink-88-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-92: hsl(var(--opacity-pink-92-hsl)/0.9215686274509803); - --opacity-pink-92-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-pink-96: hsl(var(--opacity-pink-96-hsl)/0.9607843137254902); - --opacity-pink-96-hsl: 315 calc(var(--saturation-factor, 1)*100%) 64.706%; - --opacity-white-1: hsl(var(--opacity-white-1-hsl)/0.0196078431372549); - --opacity-white-1-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-4: hsl(var(--opacity-white-4-hsl)/0.0392156862745098); - --opacity-white-4-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-8: hsl(var(--opacity-white-8-hsl)/0.0784313725490196); - --opacity-white-8-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-12: hsl(var(--opacity-white-12-hsl)/0.12156862745098039); - --opacity-white-12-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-16: hsl(var(--opacity-white-16-hsl)/0.1607843137254902); - --opacity-white-16-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-20: hsl(var(--opacity-white-20-hsl)/0.2); - --opacity-white-20-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-24: hsl(var(--opacity-white-24-hsl)/0.23921568627450981); - --opacity-white-24-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-28: hsl(var(--opacity-white-28-hsl)/0.2784313725490196); - --opacity-white-28-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-32: hsl(var(--opacity-white-32-hsl)/0.3215686274509804); - --opacity-white-32-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-36: hsl(var(--opacity-white-36-hsl)/0.3607843137254902); - --opacity-white-36-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-40: hsl(var(--opacity-white-40-hsl)/0.4); - --opacity-white-40-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-44: hsl(var(--opacity-white-44-hsl)/0.4392156862745098); - --opacity-white-44-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-48: hsl(var(--opacity-white-48-hsl)/0.47843137254901963); - --opacity-white-48-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-52: hsl(var(--opacity-white-52-hsl)/0.5215686274509804); - --opacity-white-52-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-56: hsl(var(--opacity-white-56-hsl)/0.5607843137254902); - --opacity-white-56-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-60: hsl(var(--opacity-white-60-hsl)/0.6); - --opacity-white-60-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-64: hsl(var(--opacity-white-64-hsl)/0.6392156862745098); - --opacity-white-64-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-68: hsl(var(--opacity-white-68-hsl)/0.6784313725490196); - --opacity-white-68-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-72: hsl(var(--opacity-white-72-hsl)/0.7215686274509804); - --opacity-white-72-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-76: hsl(var(--opacity-white-76-hsl)/0.7607843137254902); - --opacity-white-76-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-80: hsl(var(--opacity-white-80-hsl)/0.8); - --opacity-white-80-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-84: hsl(var(--opacity-white-84-hsl)/0.8392156862745098); - --opacity-white-84-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-88: hsl(var(--opacity-white-88-hsl)/0.8784313725490196); - --opacity-white-88-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-92: hsl(var(--opacity-white-92-hsl)/0.9215686274509803); - --opacity-white-92-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-white-96: hsl(var(--opacity-white-96-hsl)/0.9607843137254902); - --opacity-white-96-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --opacity-black-1: hsl(var(--opacity-black-1-hsl)/0.0196078431372549); - --opacity-black-1-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-4: hsl(var(--opacity-black-4-hsl)/0.0392156862745098); - --opacity-black-4-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-8: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --opacity-black-8-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-12: hsl(var(--opacity-black-12-hsl)/0.12156862745098039); - --opacity-black-12-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-16: hsl(var(--opacity-black-16-hsl)/0.1607843137254902); - --opacity-black-16-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-20: hsl(var(--opacity-black-20-hsl)/0.2); - --opacity-black-20-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-24: hsl(var(--opacity-black-24-hsl)/0.23921568627450981); - --opacity-black-24-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-28: hsl(var(--opacity-black-28-hsl)/0.2784313725490196); - --opacity-black-28-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-32: hsl(var(--opacity-black-32-hsl)/0.3215686274509804); - --opacity-black-32-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-36: hsl(var(--opacity-black-36-hsl)/0.3607843137254902); - --opacity-black-36-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-40: hsl(var(--opacity-black-40-hsl)/0.4); - --opacity-black-40-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-44: hsl(var(--opacity-black-44-hsl)/0.4392156862745098); - --opacity-black-44-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-48: hsl(var(--opacity-black-48-hsl)/0.47843137254901963); - --opacity-black-48-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-52: hsl(var(--opacity-black-52-hsl)/0.5215686274509804); - --opacity-black-52-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-56: hsl(var(--opacity-black-56-hsl)/0.5607843137254902); - --opacity-black-56-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-60: hsl(var(--opacity-black-60-hsl)/0.6); - --opacity-black-60-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-64: hsl(var(--opacity-black-64-hsl)/0.6392156862745098); - --opacity-black-64-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-68: hsl(var(--opacity-black-68-hsl)/0.6784313725490196); - --opacity-black-68-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-72: hsl(var(--opacity-black-72-hsl)/0.7215686274509804); - --opacity-black-72-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-76: hsl(var(--opacity-black-76-hsl)/0.7607843137254902); - --opacity-black-76-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-80: hsl(var(--opacity-black-80-hsl)/0.8); - --opacity-black-80-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-84: hsl(var(--opacity-black-84-hsl)/0.8392156862745098); - --opacity-black-84-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-88: hsl(var(--opacity-black-88-hsl)/0.8784313725490196); - --opacity-black-88-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-92: hsl(var(--opacity-black-92-hsl)/0.9215686274509803); - --opacity-black-92-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --opacity-black-96: hsl(var(--opacity-black-96-hsl)/0.9607843137254902); - --opacity-black-96-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --red-100: hsl(var(--red-100-hsl)/1); - --red-100-hsl: 350 calc(var(--saturation-factor, 1)*75%) 98.431%; - --red-130: hsl(var(--red-130-hsl)/1); - --red-130-hsl: 0 calc(var(--saturation-factor, 1)*83.333%) 97.647%; - --red-160: hsl(var(--red-160-hsl)/1); - --red-160-hsl: 0 calc(var(--saturation-factor, 1)*90.909%) 95.686%; - --red-200: hsl(var(--red-200-hsl)/1); - --red-200-hsl: 358.125 calc(var(--saturation-factor, 1)*88.889%) 92.941%; - --red-230: hsl(var(--red-230-hsl)/1); - --red-230-hsl: 358.696 calc(var(--saturation-factor, 1)*85.185%) 89.412%; - --red-260: hsl(var(--red-260-hsl)/1); - --red-260-hsl: 359.077 calc(var(--saturation-factor, 1)*89.041%) 85.686%; - --red-300: hsl(var(--red-300-hsl)/1); - --red-300-hsl: 358.588 calc(var(--saturation-factor, 1)*91.398%) 81.765%; - --red-330: hsl(var(--red-330-hsl)/1); - --red-330-hsl: 358.857 calc(var(--saturation-factor, 1)*91.304%) 77.451%; - --red-345: hsl(var(--red-345-hsl)/1); - --red-345-hsl: 358.168 calc(var(--saturation-factor, 1)*92.908%) 72.353%; - --red-360: hsl(var(--red-360-hsl)/1); - --red-360-hsl: 358.471 calc(var(--saturation-factor, 1)*91.813%) 66.471%; - --red-400: hsl(var(--red-400-hsl)/1); - --red-400-hsl: 358.659 calc(var(--saturation-factor, 1)*87.317%) 59.804%; - --red-430: hsl(var(--red-430-hsl)/1); - --red-430-hsl: 358.16 calc(var(--saturation-factor, 1)*68.776%) 53.529%; - --red-460: hsl(var(--red-460-hsl)/1); - --red-460-hsl: 358.705 calc(var(--saturation-factor, 1)*59.149%) 46.078%; - --red-500: hsl(var(--red-500-hsl)/1); - --red-500-hsl: 359.504 calc(var(--saturation-factor, 1)*60.199%) 39.412%; - --red-530: hsl(var(--red-530-hsl)/1); - --red-530-hsl: 358.919 calc(var(--saturation-factor, 1)*63.429%) 34.314%; - --red-560: hsl(var(--red-560-hsl)/1); - --red-560-hsl: 358.788 calc(var(--saturation-factor, 1)*63.871%) 30.392%; - --red-600: hsl(var(--red-600-hsl)/1); - --red-600-hsl: 358.636 calc(var(--saturation-factor, 1)*64.706%) 26.667%; - --red-630: hsl(var(--red-630-hsl)/1); - --red-630-hsl: 358.5 calc(var(--saturation-factor, 1)*65.574%) 23.922%; - --red-660: hsl(var(--red-660-hsl)/1); - --red-660-hsl: 359.155 calc(var(--saturation-factor, 1)*65.138%) 21.373%; - --red-700: hsl(var(--red-700-hsl)/1); - --red-700-hsl: 358.125 calc(var(--saturation-factor, 1)*66.667%) 18.824%; - --red-730: hsl(var(--red-730-hsl)/1); - --red-730-hsl: 358.929 calc(var(--saturation-factor, 1)*66.667%) 16.471%; - --red-760: hsl(var(--red-760-hsl)/1); - --red-760-hsl: 358.846 calc(var(--saturation-factor, 1)*70.27%) 14.51%; - --red-800: hsl(var(--red-800-hsl)/1); - --red-800-hsl: 358.636 calc(var(--saturation-factor, 1)*68.75%) 12.549%; - --red-830: hsl(var(--red-830-hsl)/1); - --red-830-hsl: 358.378 calc(var(--saturation-factor, 1)*67.273%) 10.784%; - --red-860: hsl(var(--red-860-hsl)/1); - --red-860-hsl: 0 calc(var(--saturation-factor, 1)*66.667%) 9.412%; - --red-900: hsl(var(--red-900-hsl)/1); - --red-900-hsl: 0 calc(var(--saturation-factor, 1)*70%) 7.843%; - --orange-100: hsl(var(--orange-100-hsl)/1); - --orange-100-hsl: 18 calc(var(--saturation-factor, 1)*100%) 98.039%; - --orange-130: hsl(var(--orange-130-hsl)/1); - --orange-130-hsl: 23.333 calc(var(--saturation-factor, 1)*100%) 96.471%; - --orange-160: hsl(var(--orange-160-hsl)/1); - --orange-160-hsl: 21.29 calc(var(--saturation-factor, 1)*93.939%) 93.529%; - --orange-200: hsl(var(--orange-200-hsl)/1); - --orange-200-hsl: 21.25 calc(var(--saturation-factor, 1)*92.308%) 89.804%; - --orange-230: hsl(var(--orange-230-hsl)/1); - --orange-230-hsl: 23.514 calc(var(--saturation-factor, 1)*92.5%) 84.314%; - --orange-260: hsl(var(--orange-260-hsl)/1); - --orange-260-hsl: 23.301 calc(var(--saturation-factor, 1)*94.495%) 78.627%; - --orange-300: hsl(var(--orange-300-hsl)/1); - --orange-300-hsl: 25.468 calc(var(--saturation-factor, 1)*94.558%) 71.176%; - --orange-330: hsl(var(--orange-330-hsl)/1); - --orange-330-hsl: 27 calc(var(--saturation-factor, 1)*94.737%) 62.745%; - --orange-345: hsl(var(--orange-345-hsl)/1); - --orange-345-hsl: 28.342 calc(var(--saturation-factor, 1)*87.665%) 55.49%; - --orange-360: hsl(var(--orange-360-hsl)/1); - --orange-360-hsl: 26.145 calc(var(--saturation-factor, 1)*71.315%) 50.784%; - --orange-400: hsl(var(--orange-400-hsl)/1); - --orange-400-hsl: 25.443 calc(var(--saturation-factor, 1)*67.521%) 45.882%; - --orange-430: hsl(var(--orange-430-hsl)/1); - --orange-430-hsl: 24.681 calc(var(--saturation-factor, 1)*69.458%) 39.804%; - --orange-460: hsl(var(--orange-460-hsl)/1); - --orange-460-hsl: 23.538 calc(var(--saturation-factor, 1)*72.222%) 35.294%; - --orange-500: hsl(var(--orange-500-hsl)/1); - --orange-500-hsl: 22.314 calc(var(--saturation-factor, 1)*76.101%) 31.176%; - --orange-530: hsl(var(--orange-530-hsl)/1); - --orange-530-hsl: 21.869 calc(var(--saturation-factor, 1)*76.978%) 27.255%; - --orange-560: hsl(var(--orange-560-hsl)/1); - --orange-560-hsl: 21.474 calc(var(--saturation-factor, 1)*77.236%) 24.118%; - --orange-600: hsl(var(--orange-600-hsl)/1); - --orange-600-hsl: 22.857 calc(var(--saturation-factor, 1)*79.245%) 20.784%; - --orange-630: hsl(var(--orange-630-hsl)/1); - --orange-630-hsl: 22.105 calc(var(--saturation-factor, 1)*79.167%) 18.824%; - --orange-660: hsl(var(--orange-660-hsl)/1); - --orange-660-hsl: 22.388 calc(var(--saturation-factor, 1)*78.824%) 16.667%; - --orange-700: hsl(var(--orange-700-hsl)/1); - --orange-700-hsl: 21.724 calc(var(--saturation-factor, 1)*76.316%) 14.902%; - --orange-730: hsl(var(--orange-730-hsl)/1); - --orange-730-hsl: 21.923 calc(var(--saturation-factor, 1)*78.788%) 12.941%; - --orange-760: hsl(var(--orange-760-hsl)/1); - --orange-760-hsl: 21.333 calc(var(--saturation-factor, 1)*78.947%) 11.176%; - --orange-800: hsl(var(--orange-800-hsl)/1); - --orange-800-hsl: 21 calc(var(--saturation-factor, 1)*80%) 9.804%; - --orange-830: hsl(var(--orange-830-hsl)/1); - --orange-830-hsl: 20 calc(var(--saturation-factor, 1)*76.744%) 8.431%; - --orange-860: hsl(var(--orange-860-hsl)/1); - --orange-860-hsl: 20 calc(var(--saturation-factor, 1)*72.973%) 7.255%; - --orange-900: hsl(var(--orange-900-hsl)/1); - --orange-900-hsl: 25.714 calc(var(--saturation-factor, 1)*72.414%) 5.686%; - --yellow-100: hsl(var(--yellow-100-hsl)/1); - --yellow-100-hsl: 33.75 calc(var(--saturation-factor, 1)*100%) 96.863%; - --yellow-130: hsl(var(--yellow-130-hsl)/1); - --yellow-130-hsl: 31.304 calc(var(--saturation-factor, 1)*100%) 95.49%; - --yellow-160: hsl(var(--yellow-160-hsl)/1); - --yellow-160-hsl: 32.727 calc(var(--saturation-factor, 1)*100%) 91.373%; - --yellow-200: hsl(var(--yellow-200-hsl)/1); - --yellow-200-hsl: 35 calc(var(--saturation-factor, 1)*97.297%) 85.49%; - --yellow-230: hsl(var(--yellow-230-hsl)/1); - --yellow-230-hsl: 36.637 calc(var(--saturation-factor, 1)*96.581%) 77.059%; - --yellow-260: hsl(var(--yellow-260-hsl)/1); - --yellow-260-hsl: 40.656 calc(var(--saturation-factor, 1)*96.825%) 62.941%; - --yellow-300: hsl(var(--yellow-300-hsl)/1); - --yellow-300-hsl: 40.421 calc(var(--saturation-factor, 1)*86.364%) 56.863%; - --yellow-330: hsl(var(--yellow-330-hsl)/1); - --yellow-330-hsl: 40 calc(var(--saturation-factor, 1)*75.309%) 52.353%; - --yellow-345: hsl(var(--yellow-345-hsl)/1); - --yellow-345-hsl: 39.545 calc(var(--saturation-factor, 1)*70.968%) 48.627%; - --yellow-360: hsl(var(--yellow-360-hsl)/1); - --yellow-360-hsl: 39.018 calc(var(--saturation-factor, 1)*74.429%) 42.941%; - --yellow-400: hsl(var(--yellow-400-hsl)/1); - --yellow-400-hsl: 37.792 calc(var(--saturation-factor, 1)*78.571%) 38.431%; - --yellow-430: hsl(var(--yellow-430-hsl)/1); - --yellow-430-hsl: 37.447 calc(var(--saturation-factor, 1)*84.431%) 32.745%; - --yellow-460: hsl(var(--yellow-460-hsl)/1); - --yellow-460-hsl: 36.279 calc(var(--saturation-factor, 1)*87.755%) 28.824%; - --yellow-500: hsl(var(--yellow-500-hsl)/1); - --yellow-500-hsl: 35.5 calc(var(--saturation-factor, 1)*93.75%) 25.098%; - --yellow-530: hsl(var(--yellow-530-hsl)/1); - --yellow-530-hsl: 34.857 calc(var(--saturation-factor, 1)*92.92%) 22.157%; - --yellow-560: hsl(var(--yellow-560-hsl)/1); - --yellow-560-hsl: 33.83 calc(var(--saturation-factor, 1)*94%) 19.608%; - --yellow-600: hsl(var(--yellow-600-hsl)/1); - --yellow-600-hsl: 34.815 calc(var(--saturation-factor, 1)*93.103%) 17.059%; - --yellow-630: hsl(var(--yellow-630-hsl)/1); - --yellow-630-hsl: 34.521 calc(var(--saturation-factor, 1)*92.405%) 15.49%; - --yellow-660: hsl(var(--yellow-660-hsl)/1); - --yellow-660-hsl: 34.688 calc(var(--saturation-factor, 1)*91.429%) 13.725%; - --yellow-700: hsl(var(--yellow-700-hsl)/1); - --yellow-700-hsl: 34.286 calc(var(--saturation-factor, 1)*90.323%) 12.157%; - --yellow-730: hsl(var(--yellow-730-hsl)/1); - --yellow-730-hsl: 32.941 calc(var(--saturation-factor, 1)*92.727%) 10.784%; - --yellow-760: hsl(var(--yellow-760-hsl)/1); - --yellow-760-hsl: 32.727 calc(var(--saturation-factor, 1)*91.667%) 9.412%; - --yellow-800: hsl(var(--yellow-800-hsl)/1); - --yellow-800-hsl: 32.432 calc(var(--saturation-factor, 1)*90.244%) 8.039%; - --yellow-830: hsl(var(--yellow-830-hsl)/1); - --yellow-830-hsl: 31.875 calc(var(--saturation-factor, 1)*88.889%) 7.059%; - --yellow-860: hsl(var(--yellow-860-hsl)/1); - --yellow-860-hsl: 32.308 calc(var(--saturation-factor, 1)*86.667%) 5.882%; - --yellow-900: hsl(var(--yellow-900-hsl)/1); - --yellow-900-hsl: 36 calc(var(--saturation-factor, 1)*83.333%) 4.706%; - --green-100: hsl(var(--green-100-hsl)/1); - --green-100-hsl: 136.667 calc(var(--saturation-factor, 1)*90%) 96.078%; - --green-130: hsl(var(--green-130-hsl)/1); - --green-130-hsl: 136.875 calc(var(--saturation-factor, 1)*94.118%) 93.333%; - --green-160: hsl(var(--green-160-hsl)/1); - --green-160-hsl: 140.952 calc(var(--saturation-factor, 1)*91.304%) 86.471%; - --green-200: hsl(var(--green-200-hsl)/1); - --green-200-hsl: 143.478 calc(var(--saturation-factor, 1)*93.496%) 75.882%; - --green-230: hsl(var(--green-230-hsl)/1); - --green-230-hsl: 146.323 calc(var(--saturation-factor, 1)*86.592%) 64.902%; - --green-260: hsl(var(--green-260-hsl)/1); - --green-260-hsl: 145.605 calc(var(--saturation-factor, 1)*75.12%) 59.02%; - --green-300: hsl(var(--green-300-hsl)/1); - --green-300-hsl: 146.323 calc(var(--saturation-factor, 1)*65.401%) 53.529%; - --green-330: hsl(var(--green-330-hsl)/1); - --green-330-hsl: 146.494 calc(var(--saturation-factor, 1)*63.115%) 47.843%; - --green-345: hsl(var(--green-345-hsl)/1); - --green-345-hsl: 146.939 calc(var(--saturation-factor, 1)*65.919%) 43.725%; - --green-360: hsl(var(--green-360-hsl)/1); - --green-360-hsl: 145.385 calc(var(--saturation-factor, 1)*65%) 39.216%; - --green-400: hsl(var(--green-400-hsl)/1); - --green-400-hsl: 142.703 calc(var(--saturation-factor, 1)*60.656%) 35.882%; - --green-430: hsl(var(--green-430-hsl)/1); - --green-430-hsl: 141.522 calc(var(--saturation-factor, 1)*56.098%) 32.157%; - --green-460: hsl(var(--green-460-hsl)/1); - --green-460-hsl: 141.481 calc(var(--saturation-factor, 1)*56.643%) 28.039%; - --green-500: hsl(var(--green-500-hsl)/1); - --green-500-hsl: 141.37 calc(var(--saturation-factor, 1)*58.4%) 24.51%; - --green-530: hsl(var(--green-530-hsl)/1); - --green-530-hsl: 140.308 calc(var(--saturation-factor, 1)*60.748%) 20.98%; - --green-560: hsl(var(--green-560-hsl)/1); - --green-560-hsl: 138.621 calc(var(--saturation-factor, 1)*61.702%) 18.431%; - --green-600: hsl(var(--green-600-hsl)/1); - --green-600-hsl: 139.245 calc(var(--saturation-factor, 1)*65.432%) 15.882%; - --green-630: hsl(var(--green-630-hsl)/1); - --green-630-hsl: 140 calc(var(--saturation-factor, 1)*66.667%) 14.118%; - --green-660: hsl(var(--green-660-hsl)/1); - --green-660-hsl: 139.091 calc(var(--saturation-factor, 1)*68.75%) 12.549%; - --green-700: hsl(var(--green-700-hsl)/1); - --green-700-hsl: 141 calc(var(--saturation-factor, 1)*74.074%) 10.588%; - --green-730: hsl(var(--green-730-hsl)/1); - --green-730-hsl: 138.333 calc(var(--saturation-factor, 1)*78.261%) 9.02%; - --green-760: hsl(var(--green-760-hsl)/1); - --green-760-hsl: 140 calc(var(--saturation-factor, 1)*84.615%) 7.647%; - --green-800: hsl(var(--green-800-hsl)/1); - --green-800-hsl: 139.286 calc(var(--saturation-factor, 1)*82.353%) 6.667%; - --green-830: hsl(var(--green-830-hsl)/1); - --green-830-hsl: 137.5 calc(var(--saturation-factor, 1)*80%) 5.882%; - --green-860: hsl(var(--green-860-hsl)/1); - --green-860-hsl: 132.632 calc(var(--saturation-factor, 1)*70.37%) 5.294%; - --green-900: hsl(var(--green-900-hsl)/1); - --green-900-hsl: 128.571 calc(var(--saturation-factor, 1)*58.333%) 4.706%; - --blue-100: hsl(var(--blue-100-hsl)/1); - --blue-100-hsl: 210 calc(var(--saturation-factor, 1)*80%) 98.039%; - --blue-130: hsl(var(--blue-130-hsl)/1); - --blue-130-hsl: 210 calc(var(--saturation-factor, 1)*87.5%) 96.863%; - --blue-160: hsl(var(--blue-160-hsl)/1); - --blue-160-hsl: 208.889 calc(var(--saturation-factor, 1)*87.097%) 93.922%; - --blue-200: hsl(var(--blue-200-hsl)/1); - --blue-200-hsl: 206.25 calc(var(--saturation-factor, 1)*92.308%) 89.804%; - --blue-230: hsl(var(--blue-230-hsl)/1); - --blue-230-hsl: 205.135 calc(var(--saturation-factor, 1)*92.5%) 84.314%; - --blue-260: hsl(var(--blue-260-hsl)/1); - --blue-260-hsl: 204.231 calc(var(--saturation-factor, 1)*94.545%) 78.431%; - --blue-300: hsl(var(--blue-300-hsl)/1); - --blue-300-hsl: 202.649 calc(var(--saturation-factor, 1)*97.419%) 69.608%; - --blue-330: hsl(var(--blue-330-hsl)/1); - --blue-330-hsl: 200.957 calc(var(--saturation-factor, 1)*100%) 59.02%; - --blue-345: hsl(var(--blue-345-hsl)/1); - --blue-345-hsl: 199.524 calc(var(--saturation-factor, 1)*100%) 49.412%; - --blue-360: hsl(var(--blue-360-hsl)/1); - --blue-360-hsl: 202.562 calc(var(--saturation-factor, 1)*100%) 47.451%; - --blue-400: hsl(var(--blue-400-hsl)/1); - --blue-400-hsl: 206.809 calc(var(--saturation-factor, 1)*100%) 46.078%; - --blue-430: hsl(var(--blue-430-hsl)/1); - --blue-430-hsl: 212.208 calc(var(--saturation-factor, 1)*100%) 45.294%; - --blue-460: hsl(var(--blue-460-hsl)/1); - --blue-460-hsl: 213.589 calc(var(--saturation-factor, 1)*100%) 40.98%; - --blue-500: hsl(var(--blue-500-hsl)/1); - --blue-500-hsl: 213.297 calc(var(--saturation-factor, 1)*100%) 35.686%; - --blue-530: hsl(var(--blue-530-hsl)/1); - --blue-530-hsl: 212.129 calc(var(--saturation-factor, 1)*100%) 30.392%; - --blue-560: hsl(var(--blue-560-hsl)/1); - --blue-560-hsl: 211.765 calc(var(--saturation-factor, 1)*100%) 26.667%; - --blue-600: hsl(var(--blue-600-hsl)/1); - --blue-600-hsl: 211.017 calc(var(--saturation-factor, 1)*100%) 23.137%; - --blue-630: hsl(var(--blue-630-hsl)/1); - --blue-630-hsl: 211.132 calc(var(--saturation-factor, 1)*100%) 20.784%; - --blue-660: hsl(var(--blue-660-hsl)/1); - --blue-660-hsl: 211.579 calc(var(--saturation-factor, 1)*100%) 18.627%; - --blue-700: hsl(var(--blue-700-hsl)/1); - --blue-700-hsl: 211.765 calc(var(--saturation-factor, 1)*100%) 16.667%; - --blue-730: hsl(var(--blue-730-hsl)/1); - --blue-730-hsl: 210.833 calc(var(--saturation-factor, 1)*100%) 14.118%; - --blue-760: hsl(var(--blue-760-hsl)/1); - --blue-760-hsl: 211.429 calc(var(--saturation-factor, 1)*100%) 12.353%; - --blue-800: hsl(var(--blue-800-hsl)/1); - --blue-800-hsl: 211.111 calc(var(--saturation-factor, 1)*100%) 10.588%; - --blue-830: hsl(var(--blue-830-hsl)/1); - --blue-830-hsl: 212.5 calc(var(--saturation-factor, 1)*100%) 9.412%; - --blue-860: hsl(var(--blue-860-hsl)/1); - --blue-860-hsl: 213.488 calc(var(--saturation-factor, 1)*100%) 8.431%; - --blue-900: hsl(var(--blue-900-hsl)/1); - --blue-900-hsl: 213.333 calc(var(--saturation-factor, 1)*100%) 7.059%; - --teal-100: hsl(var(--teal-100-hsl)/1); - --teal-100-hsl: 193.333 calc(var(--saturation-factor, 1)*69.231%) 97.451%; - --teal-130: hsl(var(--teal-130-hsl)/1); - --teal-130-hsl: 192 calc(var(--saturation-factor, 1)*83.333%) 95.294%; - --teal-160: hsl(var(--teal-160-hsl)/1); - --teal-160-hsl: 190.5 calc(var(--saturation-factor, 1)*83.333%) 90.588%; - --teal-200: hsl(var(--teal-200-hsl)/1); - --teal-200-hsl: 188.333 calc(var(--saturation-factor, 1)*85.714%) 83.529%; - --teal-230: hsl(var(--teal-230-hsl)/1); - --teal-230-hsl: 187.934 calc(var(--saturation-factor, 1)*88.321%) 73.137%; - --teal-260: hsl(var(--teal-260-hsl)/1); - --teal-260-hsl: 188.276 calc(var(--saturation-factor, 1)*81.921%) 65.294%; - --teal-300: hsl(var(--teal-300-hsl)/1); - --teal-300-hsl: 188.903 calc(var(--saturation-factor, 1)*72.77%) 58.235%; - --teal-330: hsl(var(--teal-330-hsl)/1); - --teal-330-hsl: 189.375 calc(var(--saturation-factor, 1)*65.574%) 52.157%; - --teal-345: hsl(var(--teal-345-hsl)/1); - --teal-345-hsl: 189.677 calc(var(--saturation-factor, 1)*62.753%) 48.431%; - --teal-360: hsl(var(--teal-360-hsl)/1); - --teal-360-hsl: 189.718 calc(var(--saturation-factor, 1)*63.964%) 43.529%; - --teal-400: hsl(var(--teal-400-hsl)/1); - --teal-400-hsl: 189.6 calc(var(--saturation-factor, 1)*63.452%) 38.627%; - --teal-430: hsl(var(--teal-430-hsl)/1); - --teal-430-hsl: 190.459 calc(var(--saturation-factor, 1)*63.006%) 33.922%; - --teal-460: hsl(var(--teal-460-hsl)/1); - --teal-460-hsl: 190.515 calc(var(--saturation-factor, 1)*64.238%) 29.608%; - --teal-500: hsl(var(--teal-500-hsl)/1); - --teal-500-hsl: 190.465 calc(var(--saturation-factor, 1)*65.152%) 25.882%; - --teal-530: hsl(var(--teal-530-hsl)/1); - --teal-530-hsl: 190.263 calc(var(--saturation-factor, 1)*66.667%) 22.353%; - --teal-560: hsl(var(--teal-560-hsl)/1); - --teal-560-hsl: 189.565 calc(var(--saturation-factor, 1)*69.697%) 19.412%; - --teal-600: hsl(var(--teal-600-hsl)/1); - --teal-600-hsl: 188.852 calc(var(--saturation-factor, 1)*71.765%) 16.667%; - --teal-630: hsl(var(--teal-630-hsl)/1); - --teal-630-hsl: 189.643 calc(var(--saturation-factor, 1)*73.684%) 14.902%; - --teal-660: hsl(var(--teal-660-hsl)/1); - --teal-660-hsl: 188.4 calc(var(--saturation-factor, 1)*75.758%) 12.941%; - --teal-700: hsl(var(--teal-700-hsl)/1); - --teal-700-hsl: 189.13 calc(var(--saturation-factor, 1)*79.31%) 11.373%; - --teal-730: hsl(var(--teal-730-hsl)/1); - --teal-730-hsl: 189 calc(var(--saturation-factor, 1)*80%) 9.804%; - --teal-760: hsl(var(--teal-760-hsl)/1); - --teal-760-hsl: 188.571 calc(var(--saturation-factor, 1)*81.395%) 8.431%; - --teal-800: hsl(var(--teal-800-hsl)/1); - --teal-800-hsl: 188 calc(var(--saturation-factor, 1)*83.333%) 7.059%; - --teal-830: hsl(var(--teal-830-hsl)/1); - --teal-830-hsl: 188.889 calc(var(--saturation-factor, 1)*87.097%) 6.078%; - --teal-860: hsl(var(--teal-860-hsl)/1); - --teal-860-hsl: 187.826 calc(var(--saturation-factor, 1)*85.185%) 5.294%; - --teal-900: hsl(var(--teal-900-hsl)/1); - --teal-900-hsl: 189 calc(var(--saturation-factor, 1)*90.909%) 4.314%; - --white: hsl(var(--white-hsl)/1); - --white-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --white-500: hsl(var(--white-500-hsl)/1); - --white-500-hsl: 0 calc(var(--saturation-factor, 1)*0%) 100%; - --black: hsl(var(--black-hsl)/1); - --black-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --black-500: hsl(var(--black-500-hsl)/1); - --black-500-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0%; - --brand-100: hsl(var(--brand-100-hsl)/1); - --brand-100-hsl: 240 calc(var(--saturation-factor, 1)*77.778%) 98.235%; - --brand-130: hsl(var(--brand-130-hsl)/1); - --brand-130-hsl: 235.714 calc(var(--saturation-factor, 1)*87.5%) 96.863%; - --brand-160: hsl(var(--brand-160-hsl)/1); - --brand-160-hsl: 234.545 calc(var(--saturation-factor, 1)*84.615%) 94.902%; - --brand-200: hsl(var(--brand-200-hsl)/1); - --brand-200-hsl: 236 calc(var(--saturation-factor, 1)*83.333%) 92.941%; - --brand-230: hsl(var(--brand-230-hsl)/1); - --brand-230-hsl: 235.5 calc(var(--saturation-factor, 1)*86.957%) 90.98%; - --brand-260: hsl(var(--brand-260-hsl)/1); - --brand-260-hsl: 235.2 calc(var(--saturation-factor, 1)*86.207%) 88.627%; - --brand-300: hsl(var(--brand-300-hsl)/1); - --brand-300-hsl: 235.161 calc(var(--saturation-factor, 1)*86.111%) 85.882%; - --brand-330: hsl(var(--brand-330-hsl)/1); - --brand-330-hsl: 234.75 calc(var(--saturation-factor, 1)*85.106%) 81.569%; - --brand-345: hsl(var(--brand-345-hsl)/1); - --brand-345-hsl: 234.783 calc(var(--saturation-factor, 1)*85.185%) 78.824%; - --brand-360: hsl(var(--brand-360-hsl)/1); - --brand-360-hsl: 235.152 calc(var(--saturation-factor, 1)*86.087%) 77.451%; - --brand-400: hsl(var(--brand-400-hsl)/1); - --brand-400-hsl: 234.677 calc(var(--saturation-factor, 1)*86.111%) 71.765%; - --brand-430: hsl(var(--brand-430-hsl)/1); - --brand-430-hsl: 235 calc(var(--saturation-factor, 1)*85.714%) 69.804%; - --brand-460: hsl(var(--brand-460-hsl)/1); - --brand-460-hsl: 234.93 calc(var(--saturation-factor, 1)*85.542%) 67.451%; - --brand-500: hsl(var(--brand-500-hsl)/1); - --brand-500-hsl: 234.935 calc(var(--saturation-factor, 1)*85.556%) 64.706%; - --brand-530: hsl(var(--brand-530-hsl)/1); - --brand-530-hsl: 234.857 calc(var(--saturation-factor, 1)*66.667%) 58.824%; - --brand-560: hsl(var(--brand-560-hsl)/1); - --brand-560-hsl: 234.72 calc(var(--saturation-factor, 1)*51.44%) 52.353%; - --brand-600: hsl(var(--brand-600-hsl)/1); - --brand-600-hsl: 234.857 calc(var(--saturation-factor, 1)*46.667%) 44.118%; - --brand-630: hsl(var(--brand-630-hsl)/1); - --brand-630-hsl: 235.385 calc(var(--saturation-factor, 1)*46.667%) 38.235%; - --brand-660: hsl(var(--brand-660-hsl)/1); - --brand-660-hsl: 234.75 calc(var(--saturation-factor, 1)*47.059%) 33.333%; - --brand-700: hsl(var(--brand-700-hsl)/1); - --brand-700-hsl: 235.161 calc(var(--saturation-factor, 1)*46.97%) 25.882%; - --brand-730: hsl(var(--brand-730-hsl)/1); - --brand-730-hsl: 234.828 calc(var(--saturation-factor, 1)*46.774%) 24.314%; - --brand-760: hsl(var(--brand-760-hsl)/1); - --brand-760-hsl: 234.34 calc(var(--saturation-factor, 1)*46.903%) 22.157%; - --brand-800: hsl(var(--brand-800-hsl)/1); - --brand-800-hsl: 234.894 calc(var(--saturation-factor, 1)*47.475%) 19.412%; - --brand-830: hsl(var(--brand-830-hsl)/1); - --brand-830-hsl: 235 calc(var(--saturation-factor, 1)*47.368%) 14.902%; - --brand-860: hsl(var(--brand-860-hsl)/1); - --brand-860-hsl: 234.783 calc(var(--saturation-factor, 1)*46.939%) 9.608%; - --brand-900: hsl(var(--brand-900-hsl)/1); - --brand-900-hsl: 232.5 calc(var(--saturation-factor, 1)*50%) 3.137%; - --primary-100: hsl(var(--primary-100-hsl)/1); - --primary-100-hsl: 0 calc(var(--saturation-factor, 1)*0%) 97.647%; - --primary-130: hsl(var(--primary-130-hsl)/1); - --primary-130-hsl: 220 calc(var(--saturation-factor, 1)*13.043%) 95.49%; - --primary-160: hsl(var(--primary-160-hsl)/1); - --primary-160-hsl: 210 calc(var(--saturation-factor, 1)*11.111%) 92.941%; - --primary-200: hsl(var(--primary-200-hsl)/1); - --primary-200-hsl: 216 calc(var(--saturation-factor, 1)*9.804%) 90%; - --primary-230: hsl(var(--primary-230-hsl)/1); - --primary-230-hsl: 210 calc(var(--saturation-factor, 1)*9.091%) 87.059%; - --primary-260: hsl(var(--primary-260-hsl)/1); - --primary-260-hsl: 214.286 calc(var(--saturation-factor, 1)*8.434%) 83.725%; - --primary-300: hsl(var(--primary-300-hsl)/1); - --primary-300-hsl: 210 calc(var(--saturation-factor, 1)*9.259%) 78.824%; - --primary-330: hsl(var(--primary-330-hsl)/1); - --primary-330-hsl: 215 calc(var(--saturation-factor, 1)*8.824%) 73.333%; - --primary-345: hsl(var(--primary-345-hsl)/1); - --primary-345-hsl: 214.286 calc(var(--saturation-factor, 1)*8.434%) 67.451%; - --primary-360: hsl(var(--primary-360-hsl)/1); - --primary-360-hsl: 213.75 calc(var(--saturation-factor, 1)*8.081%) 61.176%; - --primary-400: hsl(var(--primary-400-hsl)/1); - --primary-400-hsl: 222.857 calc(var(--saturation-factor, 1)*5.833%) 52.941%; - --primary-430: hsl(var(--primary-430-hsl)/1); - --primary-430-hsl: 229.091 calc(var(--saturation-factor, 1)*4.803%) 44.902%; - --primary-460: hsl(var(--primary-460-hsl)/1); - --primary-460-hsl: 228 calc(var(--saturation-factor, 1)*5.155%) 38.039%; - --primary-500: hsl(var(--primary-500-hsl)/1); - --primary-500-hsl: 228 calc(var(--saturation-factor, 1)*6.024%) 32.549%; - --primary-530: hsl(var(--primary-530-hsl)/1); - --primary-530-hsl: 226.667 calc(var(--saturation-factor, 1)*6.475%) 27.255%; - --primary-560: hsl(var(--primary-560-hsl)/1); - --primary-560-hsl: 225 calc(var(--saturation-factor, 1)*6.667%) 23.529%; - --primary-600: hsl(var(--primary-600-hsl)/1); - --primary-600-hsl: 222.857 calc(var(--saturation-factor, 1)*6.667%) 20.588%; - --primary-630: hsl(var(--primary-630-hsl)/1); - --primary-630-hsl: 220 calc(var(--saturation-factor, 1)*6.522%) 18.039%; - --primary-645: hsl(var(--primary-645-hsl)/1); - --primary-645-hsl: 220 calc(var(--saturation-factor, 1)*6.977%) 16.863%; - --primary-660: hsl(var(--primary-660-hsl)/1); - --primary-660-hsl: 228 calc(var(--saturation-factor, 1)*6.667%) 14.706%; - --primary-700: hsl(var(--primary-700-hsl)/1); - --primary-700-hsl: 225 calc(var(--saturation-factor, 1)*6.25%) 12.549%; - --primary-730: hsl(var(--primary-730-hsl)/1); - --primary-730-hsl: 225 calc(var(--saturation-factor, 1)*7.143%) 10.98%; - --primary-760: hsl(var(--primary-760-hsl)/1); - --primary-760-hsl: 220 calc(var(--saturation-factor, 1)*6.383%) 9.216%; - --primary-800: hsl(var(--primary-800-hsl)/1); - --primary-800-hsl: 220 calc(var(--saturation-factor, 1)*8.108%) 7.255%; - --primary-830: hsl(var(--primary-830-hsl)/1); - --primary-830-hsl: 240 calc(var(--saturation-factor, 1)*4%) 4.902%; - --primary-860: hsl(var(--primary-860-hsl)/1); - --primary-860-hsl: 240 calc(var(--saturation-factor, 1)*7.692%) 2.549%; - --primary-900: hsl(var(--primary-900-hsl)/1); - --primary-900-hsl: 0 calc(var(--saturation-factor, 1)*0%) 0.784%; - --plum-0: hsl(var(--plum-0-hsl)/1); - --plum-0-hsl: 240 calc(var(--saturation-factor, 1)*9.091%) 97.843%; - --plum-1: hsl(var(--plum-1-hsl)/1); - --plum-1-hsl: 240 calc(var(--saturation-factor, 1)*4.348%) 95.49%; - --plum-2: hsl(var(--plum-2-hsl)/1); - --plum-2-hsl: 220 calc(var(--saturation-factor, 1)*8.571%) 93.137%; - --plum-3: hsl(var(--plum-3-hsl)/1); - --plum-3-hsl: 225 calc(var(--saturation-factor, 1)*8%) 90.196%; - --plum-4: hsl(var(--plum-4-hsl)/1); - --plum-4-hsl: 225 calc(var(--saturation-factor, 1)*6.25%) 87.451%; - --plum-5: hsl(var(--plum-5-hsl)/1); - --plum-5-hsl: 220 calc(var(--saturation-factor, 1)*7.317%) 83.922%; - --plum-6: hsl(var(--plum-6-hsl)/1); - --plum-6-hsl: 231.429 calc(var(--saturation-factor, 1)*6.667%) 79.412%; - --plum-7: hsl(var(--plum-7-hsl)/1); - --plum-7-hsl: 226.667 calc(var(--saturation-factor, 1)*6.767%) 73.922%; - --plum-8: hsl(var(--plum-8-hsl)/1); - --plum-8-hsl: 230 calc(var(--saturation-factor, 1)*7.407%) 68.235%; - --plum-9: hsl(var(--plum-9-hsl)/1); - --plum-9-hsl: 231.429 calc(var(--saturation-factor, 1)*7.071%) 61.176%; - --plum-10: hsl(var(--plum-10-hsl)/1); - --plum-10-hsl: 228.75 calc(var(--saturation-factor, 1)*6.78%) 53.725%; - --plum-11: hsl(var(--plum-11-hsl)/1); - --plum-11-hsl: 228.75 calc(var(--saturation-factor, 1)*6.897%) 45.49%; - --plum-12: hsl(var(--plum-12-hsl)/1); - --plum-12-hsl: 228 calc(var(--saturation-factor, 1)*7.614%) 38.627%; - --plum-13: hsl(var(--plum-13-hsl)/1); - --plum-13-hsl: 227.143 calc(var(--saturation-factor, 1)*8.333%) 32.941%; - --plum-14: hsl(var(--plum-14-hsl)/1); - --plum-14-hsl: 230 calc(var(--saturation-factor, 1)*8.451%) 27.843%; - --plum-15: hsl(var(--plum-15-hsl)/1); - --plum-15-hsl: 229.091 calc(var(--saturation-factor, 1)*8.943%) 24.118%; - --plum-16: hsl(var(--plum-16-hsl)/1); - --plum-16-hsl: 234 calc(var(--saturation-factor, 1)*9.259%) 21.176%; - --plum-17: hsl(var(--plum-17-hsl)/1); - --plum-17-hsl: 233.333 calc(var(--saturation-factor, 1)*9.278%) 19.02%; - --plum-18: hsl(var(--plum-18-hsl)/1); - --plum-18-hsl: 233.333 calc(var(--saturation-factor, 1)*10.588%) 16.667%; - --plum-19: hsl(var(--plum-19-hsl)/1); - --plum-19-hsl: 232.5 calc(var(--saturation-factor, 1)*10.811%) 14.51%; - --plum-20: hsl(var(--plum-20-hsl)/1); - --plum-20-hsl: 231.429 calc(var(--saturation-factor, 1)*11.111%) 12.353%; - --plum-21: hsl(var(--plum-21-hsl)/1); - --plum-21-hsl: 231.429 calc(var(--saturation-factor, 1)*12.727%) 10.784%; - --plum-22: hsl(var(--plum-22-hsl)/1); - --plum-22-hsl: 240 calc(var(--saturation-factor, 1)*12%) 9.804%; - --plum-23: hsl(var(--plum-23-hsl)/1); - --plum-23-hsl: 240 calc(var(--saturation-factor, 1)*11.628%) 8.431%; - --plum-24: hsl(var(--plum-24-hsl)/1); - --plum-24-hsl: 228 calc(var(--saturation-factor, 1)*14.286%) 6.863%; - --plum-25: hsl(var(--plum-25-hsl)/1); - --plum-25-hsl: 240 calc(var(--saturation-factor, 1)*14.286%) 5.49%; - --plum-26: hsl(var(--plum-26-hsl)/1); - --plum-26-hsl: 240 calc(var(--saturation-factor, 1)*15.789%) 3.725% -} - -.visual-refresh { - --blue-100: var(--blue-new-1); - --blue-100-hsl: var(--blue-new-1-hsl); - --blue-130: var(--blue-new-1); - --blue-130-hsl: var(--blue-new-1-hsl); - --blue-160: var(--blue-new-1); - --blue-160-hsl: var(--blue-new-1-hsl); - --blue-200: var(--blue-new-5); - --blue-200-hsl: var(--blue-new-5-hsl); - --blue-230: var(--blue-new-11); - --blue-230-hsl: var(--blue-new-11-hsl); - --blue-260: var(--blue-new-16); - --blue-260-hsl: var(--blue-new-16-hsl); - --blue-300: var(--blue-new-24); - --blue-300-hsl: var(--blue-new-24-hsl); - --blue-330: var(--blue-new-30); - --blue-330-hsl: var(--blue-new-30-hsl); - --blue-345: var(--blue-new-36); - --blue-345-hsl: var(--blue-new-36-hsl); - --blue-360: var(--blue-new-40); - --blue-360-hsl: var(--blue-new-40-hsl); - --blue-400: var(--blue-new-46); - --blue-400-hsl: var(--blue-new-46-hsl); - --blue-430: var(--blue-new-52); - --blue-430-hsl: var(--blue-new-52-hsl); - --blue-460: var(--blue-new-57); - --blue-460-hsl: var(--blue-new-57-hsl); - --blue-500: var(--blue-new-62); - --blue-500-hsl: var(--blue-new-62-hsl); - --blue-530: var(--blue-new-67); - --blue-530-hsl: var(--blue-new-67-hsl); - --blue-560: var(--blue-new-71); - --blue-560-hsl: var(--blue-new-71-hsl); - --blue-600: var(--blue-new-75); - --blue-600-hsl: var(--blue-new-75-hsl); - --blue-630: var(--blue-new-78); - --blue-630-hsl: var(--blue-new-78-hsl); - --blue-660: var(--blue-new-81); - --blue-660-hsl: var(--blue-new-81-hsl); - --blue-700: var(--blue-new-84); - --blue-700-hsl: var(--blue-new-84-hsl); - --blue-730: var(--blue-new-87); - --blue-730-hsl: var(--blue-new-87-hsl); - --blue-760: var(--blue-new-90); - --blue-760-hsl: var(--blue-new-90-hsl); - --blue-800: var(--blue-new-92); - --blue-800-hsl: var(--blue-new-92-hsl); - --blue-830: var(--blue-new-94); - --blue-830-hsl: var(--blue-new-94-hsl); - --blue-860: var(--blue-new-95); - --blue-860-hsl: var(--blue-new-95-hsl); - --blue-900: var(--blue-new-96); - --blue-900-hsl: var(--blue-new-96-hsl); - --brand-100: var(--blurple-1); - --brand-100-hsl: var(--blurple-1-hsl); - --brand-130: var(--blurple-1); - --brand-130-hsl: var(--blurple-1-hsl); - --brand-160: var(--blurple-1); - --brand-160-hsl: var(--blurple-1-hsl); - --brand-200: var(--blurple-4); - --brand-200-hsl: var(--blurple-4-hsl); - --brand-230: var(--blurple-7); - --brand-230-hsl: var(--blurple-7-hsl); - --brand-260: var(--blurple-10); - --brand-260-hsl: var(--blurple-10-hsl); - --brand-300: var(--blurple-15); - --brand-300-hsl: var(--blurple-15-hsl); - --brand-330: var(--blurple-21); - --brand-330-hsl: var(--blurple-21-hsl); - --brand-345: var(--blurple-26); - --brand-345-hsl: var(--blurple-26-hsl); - --brand-360: var(--blurple-29); - --brand-360-hsl: var(--blurple-29-hsl); - --brand-400: var(--blurple-38); - --brand-400-hsl: var(--blurple-38-hsl); - --brand-430: var(--blurple-41); - --brand-430-hsl: var(--blurple-41-hsl); - --brand-460: var(--blurple-45); - --brand-460-hsl: var(--blurple-45-hsl); - --brand-500: var(--blurple-50); - --brand-500-hsl: var(--blurple-50-hsl); - --brand-530: var(--blurple-54); - --brand-530-hsl: var(--blurple-54-hsl); - --brand-560: var(--blurple-59); - --brand-560-hsl: var(--blurple-59-hsl); - --brand-600: var(--blurple-65); - --brand-600-hsl: var(--blurple-65-hsl); - --brand-630: var(--blurple-70); - --brand-630-hsl: var(--blurple-70-hsl); - --brand-660: var(--blurple-74); - --brand-660-hsl: var(--blurple-74-hsl); - --brand-700: var(--blurple-81); - --brand-700-hsl: var(--blurple-81-hsl); - --brand-730: var(--blurple-82); - --brand-730-hsl: var(--blurple-82-hsl); - --brand-760: var(--blurple-84); - --brand-760-hsl: var(--blurple-84-hsl); - --brand-800: var(--blurple-86); - --brand-800-hsl: var(--blurple-86-hsl); - --brand-830: var(--blurple-91); - --brand-830-hsl: var(--blurple-91-hsl); - --brand-860: var(--blurple-96); - --brand-860-hsl: var(--blurple-96-hsl); - --brand-900: var(--blurple-99); - --brand-900-hsl: var(--blurple-99-hsl); - --green-100: var(--green-new-1); - --green-100-hsl: var(--green-new-1-hsl); - --green-130: var(--green-new-1); - --green-130-hsl: var(--green-new-1-hsl); - --green-160: var(--green-new-3); - --green-160-hsl: var(--green-new-3-hsl); - --green-200: var(--green-new-9); - --green-200-hsl: var(--green-new-9-hsl); - --green-230: var(--green-new-16); - --green-230-hsl: var(--green-new-16-hsl); - --green-260: var(--green-new-21); - --green-260-hsl: var(--green-new-21-hsl); - --green-300: var(--green-new-25); - --green-300-hsl: var(--green-new-25-hsl); - --green-330: var(--green-new-29); - --green-330-hsl: var(--green-new-29-hsl); - --green-345: var(--green-new-34); - --green-345-hsl: var(--green-new-34-hsl); - --green-360: var(--green-new-40); - --green-360-hsl: var(--green-new-40-hsl); - --green-400: var(--green-new-45); - --green-400-hsl: var(--green-new-45-hsl); - --green-430: var(--green-new-52); - --green-430-hsl: var(--green-new-52-hsl); - --green-460: var(--green-new-58); - --green-460-hsl: var(--green-new-58-hsl); - --green-500: var(--green-new-63); - --green-500-hsl: var(--green-new-63-hsl); - --green-530: var(--green-new-69); - --green-530-hsl: var(--green-new-69-hsl); - --green-560: var(--green-new-73); - --green-560-hsl: var(--green-new-73-hsl); - --green-600: var(--green-new-77); - --green-600-hsl: var(--green-new-77-hsl); - --green-630: var(--green-new-80); - --green-630-hsl: var(--green-new-80-hsl); - --green-660: var(--green-new-83); - --green-660-hsl: var(--green-new-83-hsl); - --green-700: var(--green-new-86); - --green-700-hsl: var(--green-new-86-hsl); - --green-730: var(--green-new-89); - --green-730-hsl: var(--green-new-89-hsl); - --green-760: var(--green-new-91); - --green-760-hsl: var(--green-new-91-hsl); - --green-800: var(--green-new-94); - --green-800-hsl: var(--green-new-94-hsl); - --green-830: var(--green-new-96); - --green-830-hsl: var(--green-new-96-hsl); - --green-860: var(--green-new-98); - --green-860-hsl: var(--green-new-98-hsl); - --green-900: var(--green-new-99); - --green-900-hsl: var(--green-new-99-hsl); - --orange-100: var(--orange-new-1); - --orange-100-hsl: var(--orange-new-1-hsl); - --orange-130: var(--orange-new-1); - --orange-130-hsl: var(--orange-new-1-hsl); - --orange-160: var(--orange-new-1); - --orange-160-hsl: var(--orange-new-1-hsl); - --orange-200: var(--orange-new-3); - --orange-200-hsl: var(--orange-new-3-hsl); - --orange-230: var(--orange-new-8); - --orange-230-hsl: var(--orange-new-8-hsl); - --orange-260: var(--orange-new-14); - --orange-260-hsl: var(--orange-new-14-hsl); - --orange-300: var(--orange-new-21); - --orange-300-hsl: var(--orange-new-21-hsl); - --orange-330: var(--orange-new-30); - --orange-330-hsl: var(--orange-new-30-hsl); - --orange-345: var(--orange-new-35); - --orange-345-hsl: var(--orange-new-35-hsl); - --orange-360: var(--orange-new-39); - --orange-360-hsl: var(--orange-new-39-hsl); - --orange-400: var(--orange-new-42); - --orange-400-hsl: var(--orange-new-42-hsl); - --orange-430: var(--orange-new-45); - --orange-430-hsl: var(--orange-new-45-hsl); - --orange-460: var(--orange-new-57); - --orange-460-hsl: var(--orange-new-57-hsl); - --orange-500: var(--orange-new-62); - --orange-500-hsl: var(--orange-new-62-hsl); - --orange-530: var(--orange-new-67); - --orange-530-hsl: var(--orange-new-67-hsl); - --orange-560: var(--orange-new-73); - --orange-560-hsl: var(--orange-new-73-hsl); - --orange-600: var(--orange-new-75); - --orange-600-hsl: var(--orange-new-75-hsl); - --orange-630: var(--orange-new-80); - --orange-630-hsl: var(--orange-new-80-hsl); - --orange-660: var(--orange-new-81); - --orange-660-hsl: var(--orange-new-81-hsl); - --orange-700: var(--orange-new-82); - --orange-700-hsl: var(--orange-new-82-hsl); - --orange-730: var(--orange-new-87); - --orange-730-hsl: var(--orange-new-87-hsl); - --orange-760: var(--orange-new-89); - --orange-760-hsl: var(--orange-new-89-hsl); - --orange-800: var(--orange-new-92); - --orange-800-hsl: var(--orange-new-92-hsl); - --orange-830: var(--orange-new-99); - --orange-830-hsl: var(--orange-new-99-hsl); - --orange-860: var(--orange-new-99); - --orange-860-hsl: var(--orange-new-99-hsl); - --orange-900: var(--orange-new-100); - --orange-900-hsl: var(--orange-new-100-hsl); - --plum-0: var(--neutral-2); - --plum-0-hsl: var(--neutral-2-hsl); - --plum-1: var(--neutral-4); - --plum-1-hsl: var(--neutral-4-hsl); - --plum-10: var(--neutral-34); - --plum-10-hsl: var(--neutral-34-hsl); - --plum-11: var(--neutral-41); - --plum-11-hsl: var(--neutral-41-hsl); - --plum-12: var(--neutral-46); - --plum-12-hsl: var(--neutral-46-hsl); - --plum-13: var(--neutral-50); - --plum-13-hsl: var(--neutral-50-hsl); - --plum-14: var(--neutral-58); - --plum-14-hsl: var(--neutral-58-hsl); - --plum-15: var(--neutral-64); - --plum-15-hsl: var(--neutral-64-hsl); - --plum-16: var(--neutral-69); - --plum-16-hsl: var(--neutral-69-hsl); - --plum-17: var(--neutral-73); - --plum-17-hsl: var(--neutral-73-hsl); - --plum-18: var(--neutral-77); - --plum-18-hsl: var(--neutral-77-hsl); - --plum-19: var(--neutral-80); - --plum-19-hsl: var(--neutral-80-hsl); - --plum-2: var(--neutral-6); - --plum-2-hsl: var(--neutral-6-hsl); - --plum-20: var(--neutral-84); - --plum-20-hsl: var(--neutral-84-hsl); - --plum-21: var(--neutral-86); - --plum-21-hsl: var(--neutral-86-hsl); - --plum-22: var(--neutral-89); - --plum-22-hsl: var(--neutral-89-hsl); - --plum-23: var(--neutral-91); - --plum-23-hsl: var(--neutral-91-hsl); - --plum-24: var(--neutral-93); - --plum-24-hsl: var(--neutral-93-hsl); - --plum-25: var(--neutral-95); - --plum-25-hsl: var(--neutral-95-hsl); - --plum-26: var(--neutral-97); - --plum-26-hsl: var(--neutral-97-hsl); - --plum-3: var(--neutral-8); - --plum-3-hsl: var(--neutral-8-hsl); - --plum-4: var(--neutral-10); - --plum-4-hsl: var(--neutral-10-hsl); - --plum-5: var(--neutral-12); - --plum-5-hsl: var(--neutral-12-hsl); - --plum-6: var(--neutral-15); - --plum-6-hsl: var(--neutral-15-hsl); - --plum-7: var(--neutral-19); - --plum-7-hsl: var(--neutral-19-hsl); - --plum-8: var(--neutral-24); - --plum-8-hsl: var(--neutral-24-hsl); - --plum-9: var(--neutral-29); - --plum-9-hsl: var(--neutral-29-hsl); - --primary-100: var(--neutral-2); - --primary-100-hsl: var(--neutral-2-hsl); - --primary-130: var(--neutral-4); - --primary-130-hsl: var(--neutral-4-hsl); - --primary-160: var(--neutral-6); - --primary-160-hsl: var(--neutral-6-hsl); - --primary-200: var(--neutral-8); - --primary-200-hsl: var(--neutral-8-hsl); - --primary-230: var(--neutral-10); - --primary-230-hsl: var(--neutral-10-hsl); - --primary-260: var(--neutral-12); - --primary-260-hsl: var(--neutral-12-hsl); - --primary-300: var(--neutral-15); - --primary-300-hsl: var(--neutral-15-hsl); - --primary-330: var(--neutral-19); - --primary-330-hsl: var(--neutral-19-hsl); - --primary-345: var(--neutral-23); - --primary-345-hsl: var(--neutral-23-hsl); - --primary-360: var(--neutral-28); - --primary-360-hsl: var(--neutral-28-hsl); - --primary-400: var(--neutral-34); - --primary-400-hsl: var(--neutral-34-hsl); - --primary-430: var(--neutral-41); - --primary-430-hsl: var(--neutral-41-hsl); - --primary-460: var(--neutral-46); - --primary-460-hsl: var(--neutral-46-hsl); - --primary-500: var(--neutral-50); - --primary-500-hsl: var(--neutral-50-hsl); - --primary-530: var(--neutral-59); - --primary-530-hsl: var(--neutral-59-hsl); - --primary-560: var(--neutral-64); - --primary-560-hsl: var(--neutral-64-hsl); - --primary-600: var(--neutral-69); - --primary-600-hsl: var(--neutral-69-hsl); - --primary-630: var(--neutral-73); - --primary-630-hsl: var(--neutral-73-hsl); - --primary-645: var(--neutral-75); - --primary-645-hsl: var(--neutral-75-hsl); - --primary-660: var(--neutral-79); - --primary-660-hsl: var(--neutral-79-hsl); - --primary-700: var(--neutral-83); - --primary-700-hsl: var(--neutral-83-hsl); - --primary-730: var(--neutral-86); - --primary-730-hsl: var(--neutral-86-hsl); - --primary-760: var(--neutral-89); - --primary-760-hsl: var(--neutral-89-hsl); - --primary-800: var(--neutral-92); - --primary-800-hsl: var(--neutral-92-hsl); - --primary-830: var(--neutral-95); - --primary-830-hsl: var(--neutral-95-hsl); - --primary-860: var(--neutral-98); - --primary-860-hsl: var(--neutral-98-hsl); - --primary-900: var(--neutral-99); - --primary-900-hsl: var(--neutral-99-hsl); - --red-100: var(--red-new-1); - --red-100-hsl: var(--red-new-1-hsl); - --red-130: var(--red-new-1); - --red-130-hsl: var(--red-new-1-hsl); - --red-160: var(--red-new-1); - --red-160-hsl: var(--red-new-1-hsl); - --red-200: var(--red-new-1); - --red-200-hsl: var(--red-new-1-hsl); - --red-230: var(--red-new-5); - --red-230-hsl: var(--red-new-5-hsl); - --red-260: var(--red-new-10); - --red-260-hsl: var(--red-new-10-hsl); - --red-300: var(--red-new-16); - --red-300-hsl: var(--red-new-16-hsl); - --red-330: var(--red-new-21); - --red-330-hsl: var(--red-new-21-hsl); - --red-345: var(--red-new-30); - --red-345-hsl: var(--red-new-30-hsl); - --red-360: var(--red-new-38); - --red-360-hsl: var(--red-new-38-hsl); - --red-400: var(--red-new-46); - --red-400-hsl: var(--red-new-46-hsl); - --red-430: var(--red-new-48); - --red-430-hsl: var(--red-new-48-hsl); - --red-460: var(--red-new-55); - --red-460-hsl: var(--red-new-55-hsl); - --red-500: var(--red-new-62); - --red-500-hsl: var(--red-new-62-hsl); - --red-530: var(--red-new-67); - --red-530-hsl: var(--red-new-67-hsl); - --red-560: var(--red-new-71); - --red-560-hsl: var(--red-new-71-hsl); - --red-600: var(--red-new-75); - --red-600-hsl: var(--red-new-75-hsl); - --red-630: var(--red-new-77); - --red-630-hsl: var(--red-new-77-hsl); - --red-660: var(--red-new-81); - --red-660-hsl: var(--red-new-81-hsl); - --red-700: var(--red-new-84); - --red-700-hsl: var(--red-new-84-hsl); - --red-730: var(--red-new-89); - --red-730-hsl: var(--red-new-89-hsl); - --red-760: var(--red-new-92); - --red-760-hsl: var(--red-new-92-hsl); - --red-800: var(--red-new-95); - --red-800-hsl: var(--red-new-95-hsl); - --red-830: var(--red-new-96); - --red-830-hsl: var(--red-new-96-hsl); - --red-860: var(--red-new-98); - --red-860-hsl: var(--red-new-98-hsl); - --red-900: var(--red-new-99); - --red-900-hsl: var(--red-new-99-hsl); - --teal-100: var(--teal-new-1); - --teal-100-hsl: var(--teal-new-1-hsl); - --teal-130: var(--teal-new-1); - --teal-130-hsl: var(--teal-new-1-hsl); - --teal-160: var(--teal-new-1); - --teal-160-hsl: var(--teal-new-1-hsl); - --teal-200: var(--teal-new-7); - --teal-200-hsl: var(--teal-new-7-hsl); - --teal-230: var(--teal-new-13); - --teal-230-hsl: var(--teal-new-13-hsl); - --teal-260: var(--teal-new-19); - --teal-260-hsl: var(--teal-new-19-hsl); - --teal-300: var(--teal-new-24); - --teal-300-hsl: var(--teal-new-24-hsl); - --teal-330: var(--teal-new-30); - --teal-330-hsl: var(--teal-new-30-hsl); - --teal-345: var(--teal-new-33); - --teal-345-hsl: var(--teal-new-33-hsl); - --teal-360: var(--teal-new-38); - --teal-360-hsl: var(--teal-new-38-hsl); - --teal-400: var(--teal-new-45); - --teal-400-hsl: var(--teal-new-45-hsl); - --teal-430: var(--teal-new-53); - --teal-430-hsl: var(--teal-new-53-hsl); - --teal-460: var(--teal-new-59); - --teal-460-hsl: var(--teal-new-59-hsl); - --teal-500: var(--teal-new-65); - --teal-500-hsl: var(--teal-new-65-hsl); - --teal-530: var(--teal-new-70); - --teal-530-hsl: var(--teal-new-70-hsl); - --teal-560: var(--teal-new-74); - --teal-560-hsl: var(--teal-new-74-hsl); - --teal-600: var(--teal-new-78); - --teal-600-hsl: var(--teal-new-78-hsl); - --teal-630: var(--teal-new-81); - --teal-630-hsl: var(--teal-new-81-hsl); - --teal-660: var(--teal-new-84); - --teal-660-hsl: var(--teal-new-84-hsl); - --teal-700: var(--teal-new-86); - --teal-700-hsl: var(--teal-new-86-hsl); - --teal-730: var(--teal-new-89); - --teal-730-hsl: var(--teal-new-89-hsl); - --teal-760: var(--teal-new-92); - --teal-760-hsl: var(--teal-new-92-hsl); - --teal-800: var(--teal-new-94); - --teal-800-hsl: var(--teal-new-94-hsl); - --teal-830: var(--teal-new-96); - --teal-830-hsl: var(--teal-new-96-hsl); - --teal-860: var(--teal-new-98); - --teal-860-hsl: var(--teal-new-98-hsl); - --teal-900: var(--teal-new-99); - --teal-900-hsl: var(--teal-new-99-hsl); - --yellow-100: var(--yellow-new-1); - --yellow-100-hsl: var(--yellow-new-1-hsl); - --yellow-130: var(--yellow-new-1); - --yellow-130-hsl: var(--yellow-new-1-hsl); - --yellow-160: var(--yellow-new-1); - --yellow-160-hsl: var(--yellow-new-1-hsl); - --yellow-200: var(--yellow-new-6); - --yellow-200-hsl: var(--yellow-new-6-hsl); - --yellow-230: var(--yellow-new-15); - --yellow-230-hsl: var(--yellow-new-15-hsl); - --yellow-260: var(--yellow-new-31); - --yellow-260-hsl: var(--yellow-new-31-hsl); - --yellow-300: var(--yellow-new-36); - --yellow-300-hsl: var(--yellow-new-36-hsl); - --yellow-330: var(--yellow-new-39); - --yellow-330-hsl: var(--yellow-new-39-hsl); - --yellow-345: var(--yellow-new-55); - --yellow-345-hsl: var(--yellow-new-55-hsl); - --yellow-360: var(--yellow-new-60); - --yellow-360-hsl: var(--yellow-new-60-hsl); - --yellow-400: var(--yellow-new-63); - --yellow-400-hsl: var(--yellow-new-63-hsl); - --yellow-430: var(--yellow-new-68); - --yellow-430-hsl: var(--yellow-new-68-hsl); - --yellow-460: var(--yellow-new-72); - --yellow-460-hsl: var(--yellow-new-72-hsl); - --yellow-500: var(--yellow-new-76); - --yellow-500-hsl: var(--yellow-new-76-hsl); - --yellow-530: var(--yellow-new-79); - --yellow-530-hsl: var(--yellow-new-79-hsl); - --yellow-560: var(--yellow-new-82); - --yellow-560-hsl: var(--yellow-new-82-hsl); - --yellow-600: var(--yellow-new-84); - --yellow-600-hsl: var(--yellow-new-84-hsl); - --yellow-630: var(--yellow-new-86); - --yellow-630-hsl: var(--yellow-new-86-hsl); - --yellow-660: var(--yellow-new-86); - --yellow-660-hsl: var(--yellow-new-86-hsl); - --yellow-700: var(--yellow-new-88); - --yellow-700-hsl: var(--yellow-new-88-hsl); - --yellow-730: var(--yellow-new-90); - --yellow-730-hsl: var(--yellow-new-90-hsl); - --yellow-760: var(--yellow-new-92); - --yellow-760-hsl: var(--yellow-new-92-hsl); - --yellow-800: var(--yellow-new-93); - --yellow-800-hsl: var(--yellow-new-93-hsl); - --yellow-830: var(--yellow-new-96); - --yellow-830-hsl: var(--yellow-new-96-hsl); - --yellow-860: var(--yellow-new-100); - --yellow-860-hsl: var(--yellow-new-100-hsl); - --yellow-900: var(--yellow-new-100); - --yellow-900-hsl: var(--yellow-new-100-hsl) -} - -.refresh-fast-follow-distinct-borders .theme-dark,.refresh-fast-follow-distinct-borders.theme-dark { - --app-frame-border: var(--neutral-52) -} - -@supports (color: color-mix(in lch,red,blue)) { - .refresh-fast-follow-distinct-borders .theme-dark,.refresh-fast-follow-distinct-borders.theme-dark { - --app-frame-border:color-mix(in oklab,var(--neutral-52) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))) - } -} - -.refresh-fast-follow-distinct-borders .theme-light,.refresh-fast-follow-distinct-borders.theme-light { - --app-frame-border: var(--neutral-12) -} - -@supports (color: color-mix(in lch,red,blue)) { - .refresh-fast-follow-distinct-borders .theme-light,.refresh-fast-follow-distinct-borders.theme-light { - --app-frame-border:color-mix(in oklab,var(--neutral-12) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))) - } -} - -.refresh-fast-follow-distinct-borders .theme-midnight,.refresh-fast-follow-distinct-borders.theme-midnight { - --app-frame-border: var(--neutral-73) -} - -@supports (color: color-mix(in lch,red,blue)) { - .refresh-fast-follow-distinct-borders .theme-midnight,.refresh-fast-follow-distinct-borders.theme-midnight { - --app-frame-border:color-mix(in oklab,var(--neutral-73) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))) - } -} - -.refresh-fast-follow-distinct-borders .theme-darker,.refresh-fast-follow-distinct-borders.theme-darker { - --app-frame-border: var(--neutral-65) -} - -@supports (color: color-mix(in lch,red,blue)) { - .refresh-fast-follow-distinct-borders .theme-darker,.refresh-fast-follow-distinct-borders.theme-darker { - --app-frame-border:color-mix(in oklab,var(--neutral-65) 100%,var(--custom-theme-base-color,#000) var(--custom-theme-border-color-amount,var(--custom-theme-base-color-amount,0%))) - } -} - -.mana-toggle-inputs .theme-dark,.mana-toggle-inputs.theme-dark { - --checkbox-background-default: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --checkbox-border-default: hsl(var(--opacity-64-hsl)/0.6392156862745098) -} - -.mana-toggle-inputs .theme-light,.mana-toggle-inputs.theme-light { - --checkbox-background-default: hsl(var(--opacity-black-4-hsl)/0.0392156862745098); - --checkbox-border-default: var(--neutral-34) -} - -.mana-toggle-inputs .theme-darker,.mana-toggle-inputs .theme-midnight,.mana-toggle-inputs.theme-darker,.mana-toggle-inputs.theme-midnight { - --checkbox-background-default: hsl(var(--opacity-black-8-hsl)/0.0784313725490196); - --checkbox-border-default: hsl(var(--opacity-64-hsl)/0.6392156862745098) -} - -.theme-dark { - --shadow-border: 0 0 0 1px hsl(none 0% 100%/0.08); - --shadow-border-filter: drop-shadow(0 0 1px hsl(none 0% 100%/0.08)); - --shadow-button-overlay: 0 12px 24px 0 hsl(none 0% 0%/0.24); - --shadow-button-overlay-filter: drop-shadow(0 12px 24px hsl(none 0% 0%/0.24)); - --shadow-high: 0 12px 24px 0 hsl(none 0% 0%/0.24); - --shadow-high-filter: drop-shadow(0 12px 24px hsl(none 0% 0%/0.24)); - --shadow-ledge: 0 2px 0 0 hsl(none 0% 0%/0.05),0 1.5px 0 0 hsl(none 0% 0%/0.05),0 1px 0 0 hsl(none 0% 0%/0.16); - --shadow-ledge-filter: drop-shadow(0 1.5px 0 hsl(none 0% 0%/0.24)); - --shadow-low: 0 1px 4px 0 hsl(none 0% 0%/0.14); - --shadow-low-filter: drop-shadow(0 1px 4px hsl(none 0% 0%/0.14)); - --shadow-low-active: 0 0 4px 0 hsl(none 0% 0%/0.14); - --shadow-low-active-filter: drop-shadow(0 0 4px hsl(none 0% 0%/0.14)); - --shadow-low-hover: 0 4px 10px 0 hsl(none 0% 0%/0.14); - --shadow-low-hover-filter: drop-shadow(0 4px 10px hsl(none 0% 0%/0.14)); - --shadow-medium: 0 4px 8px 0 hsl(none 0% 0%/0.16); - --shadow-medium-filter: drop-shadow(0 4px 8px hsl(none 0% 0%/0.16)); - --shadow-mobile-navigator-x: 0 0 10px 0 hsl(none 0% 0%/0.22); - --shadow-mobile-navigator-x-filter: drop-shadow(0 0 10px hsl(none 0% 0%/0.22)); - --shadow-top-high: 0 -12px 32px 0 hsl(none 0% 0%/0.24); - --shadow-top-high-filter: drop-shadow(0 -12px 32px hsl(none 0% 0%/0.24)); - --shadow-top-ledge: 0 -2px 0 0 hsl(none 0% 0%/0.05),0 -1.5px 0 0 hsl(none 0% 0%/0.05),0 -1px 0 0 hsl(none 0% 0%/0.16); - --shadow-top-ledge-filter: drop-shadow(0 -1.5px 0 hsl(none 0% 0%/0.24)); - --shadow-top-low: 0 -1px 4px 0 hsl(none 0% 0%/0.14); - --shadow-top-low-filter: drop-shadow(0 -1px 4px hsl(none 0% 0%/0.14)) -} - -.theme-light { - --shadow-border: 0 0 0 1px hsl(none 0% 0%/0.08); - --shadow-border-filter: drop-shadow(0 0 1px hsl(none 0% 0%/0.08)); - --shadow-button-overlay: 0 12px 24px 0 hsl(none 0% 0%/0.24); - --shadow-button-overlay-filter: drop-shadow(0 12px 24px hsl(none 0% 0%/0.24)); - --shadow-high: 0 12px 36px 0 hsl(none 0% 0%/0.12); - --shadow-high-filter: drop-shadow(0 12px 36px hsl(none 0% 0%/0.12)); - --shadow-ledge: 0 2px 0 0 hsl(none 0% 0%/0.03),0 1.5px 0 0 hsl(none 0% 0%/0.03),0 1px 0 0 hsl(none 0% 0%/0.08); - --shadow-ledge-filter: drop-shadow(0 1.5px 0 hsl(none 0% 0%/0.12)); - --shadow-low: 0 1px 4px 0 hsl(none 0% 0%/0.08); - --shadow-low-filter: drop-shadow(0 1px 4px hsl(none 0% 0%/0.08)); - --shadow-low-active: 0 0 4px 0 hsl(none 0% 0%/0.08); - --shadow-low-active-filter: drop-shadow(0 0 4px hsl(none 0% 0%/0.08)); - --shadow-low-hover: 0 4px 8px 0 hsl(none 0% 0%/0.08); - --shadow-low-hover-filter: drop-shadow(0 4px 8px hsl(none 0% 0%/0.08)); - --shadow-medium: 0 4px 8px 0 hsl(none 0% 0%/0.08); - --shadow-medium-filter: drop-shadow(0 4px 8px hsl(none 0% 0%/0.08)); - --shadow-mobile-navigator-x: 0 0 9px 0 hsl(none 0% 0%/0.13); - --shadow-mobile-navigator-x-filter: drop-shadow(0 0 9px hsl(none 0% 0%/0.13)); - --shadow-top-high: 0 -12px 36px 0 hsl(none 0% 0%/0.12); - --shadow-top-high-filter: drop-shadow(0 -12px 36px hsl(none 0% 0%/0.12)); - --shadow-top-ledge: 0 -2px 0 0 hsl(none 0% 0%/0.03),0 -1.5px 0 0 hsl(none 0% 0%/0.03),0 -1px 0 0 hsl(none 0% 0%/0.08); - --shadow-top-ledge-filter: drop-shadow(0 -1.5px 0 hsl(none 0% 0%/0.12)); - --shadow-top-low: 0 -1px 4px 0 hsl(none 0% 0%/0.08); - --shadow-top-low-filter: drop-shadow(0 -1px 4px hsl(none 0% 0%/0.08)) -} - -.theme-darker,.theme-midnight { - --shadow-border: 0 0 0 1px hsl(none 0% 100%/0.08); - --shadow-border-filter: drop-shadow(0 0 1px hsl(none 0% 100%/0.08)); - --shadow-button-overlay: 0 12px 24px 0 hsl(none 0% 0%/0.24); - --shadow-button-overlay-filter: drop-shadow(0 12px 24px hsl(none 0% 0%/0.24)); - --shadow-high: 0 12px 24px 0 hsl(none 0% 0%/0.24); - --shadow-high-filter: drop-shadow(0 12px 24px hsl(none 0% 0%/0.24)); - --shadow-ledge: 0 2px 0 0 hsl(none 0% 0%/0.05),0 1.5px 0 0 hsl(none 0% 0%/0.05),0 1px 0 0 hsl(none 0% 0%/0.16); - --shadow-ledge-filter: drop-shadow(0 1.5px 0 hsl(none 0% 0%/0.24)); - --shadow-low: 0 1px 4px 0 hsl(none 0% 0%/0.14); - --shadow-low-filter: drop-shadow(0 1px 4px hsl(none 0% 0%/0.14)); - --shadow-low-active: 0 0 4px 0 hsl(none 0% 0%/0.14); - --shadow-low-active-filter: drop-shadow(0 0 4px hsl(none 0% 0%/0.14)); - --shadow-low-hover: 0 4px 10px 0 hsl(none 0% 0%/0.14); - --shadow-low-hover-filter: drop-shadow(0 4px 10px hsl(none 0% 0%/0.14)); - --shadow-medium: 0 4px 8px 0 hsl(none 0% 0%/0.16); - --shadow-medium-filter: drop-shadow(0 4px 8px hsl(none 0% 0%/0.16)); - --shadow-mobile-navigator-x: 0 0 10px 0 hsl(none 0% 0%/0.22); - --shadow-mobile-navigator-x-filter: drop-shadow(0 0 10px hsl(none 0% 0%/0.22)); - --shadow-top-high: 0 -12px 32px 0 hsl(none 0% 0%/0.24); - --shadow-top-high-filter: drop-shadow(0 -12px 32px hsl(none 0% 0%/0.24)); - --shadow-top-ledge: 0 -2px 0 0 hsl(none 0% 0%/0.05),0 -1.5px 0 0 hsl(none 0% 0%/0.05),0 -1px 0 0 hsl(none 0% 0%/0.16); - --shadow-top-ledge-filter: drop-shadow(0 -1.5px 0 hsl(none 0% 0%/0.24)); - --shadow-top-low: 0 -1px 4px 0 hsl(none 0% 0%/0.14); - --shadow-top-low-filter: drop-shadow(0 -1px 4px hsl(none 0% 0%/0.14)) -} - -:root { - --size-0: 0px; - --size-4: 4px; - --size-8: 8px; - --size-12: 12px; - --size-16: 16px; - --size-20: 20px; - --size-24: 24px; - --size-32: 32px; - --size-48: 48px; - --size-64: 64px; - --size-80: 80px; - --size-96: 96px; - --size-128: 128px; - --size-160: 160px; - --size-192: 192px; - --size-xxs: var(--size-4); - --size-xs: var(--size-8); - --size-sm: var(--size-12); - --size-md: var(--size-16); - --size-lg: var(--size-20); - --size-xl: var(--size-24); - --size-xxl: var(--size-32); - --breakpoint-480: 480px; - --breakpoint-640: 640px; - --breakpoint-768: 768px; - --breakpoint-1024: 1024px; - --breakpoint-1280: 1280px; - --breakpoint-1536: 1536px; - --breakpoint-1800: 1800px; - --breakpoint-2500: 2500px; - --breakpoint-xxs: 480px; - --breakpoint-xs: 640px; - --breakpoint-sm: 768px; - --breakpoint-md: 1024px; - --breakpoint-lg: 1280px; - --breakpoint-xl: 1536px; - --breakpoint-xxl: 1800px; - --breakpoint-max: 2500px; - --space-0: 0px; - --space-4: 4px; - --space-6: 6px; - --space-8: 8px; - --space-10: 10px; - --space-12: 12px; - --space-16: 16px; - --space-20: 20px; - --space-24: 24px; - --space-26: 26px; - --space-30: 30px; - --space-32: 32px; - --space-40: 40px; - --space-48: 48px; - --space-64: 64px; - --space-80: 80px; - --space-96: 96px; - --space-128: 128px; - --space-160: 160px; - --space-192: 192px; - --space-xxs: var(--space-4); - --space-xs: var(--space-8); - --space-sm: var(--space-12); - --space-md: var(--space-16); - --space-lg: var(--space-20); - --space-xl: var(--space-24); - --space-xxl: var(--space-32) -} - -.density-compact { - --space-xxs: var(--space-4); - --space-xs: var(--space-6); - --space-sm: var(--space-10); - --space-md: var(--space-12); - --space-lg: var(--space-16); - --space-xl: var(--space-20); - --space-xxl: var(--space-26) -} - -.density-cozy { - --space-xxs: var(--space-4); - --space-xs: var(--space-10); - --space-sm: var(--space-16); - --space-md: var(--space-20); - --space-lg: var(--space-24); - --space-xl: var(--space-30); - --space-xxl: var(--space-40) -} - -.density-default { - --space-xxs: var(--space-4); - --space-xs: var(--space-8); - --space-sm: var(--space-12); - --space-md: var(--space-16); - --space-lg: var(--space-20); - --space-xl: var(--space-24); - --space-xxl: var(--space-32) -} - -:root { - --radius-none: 0px; - --radius-xs: 4px; - --radius-sm: 8px; - --radius-md: 12px; - --radius-lg: 16px; - --radius-xl: 24px; - --radius-xxl: 32px; - --radius-round: 2147483647px -} - -.theme-dark { - --guild-header-text-shadow: 0 1px 1px hsl(var(--black-hsl)/0.4); - --elevation-stroke: 0 0 0 1px hsl(var(--primary-900-hsl)/0.15); - --elevation-low: 0 1px 0 hsl(var(--primary-900-hsl)/0.2),0 1.5px 0 hsl(var(--primary-860-hsl)/0.05),0 2px 0 hsl(var(--primary-900-hsl)/0.05); - --elevation-medium: 0 4px 4px hsl(var(--black-hsl)/0.16); - --elevation-high: 0 8px 16px hsl(var(--black-hsl)/0.24) -} - -.theme-light { - --guild-header-text-shadow: 0 1px 1px hsl(var(--white-hsl)/0.4); - --elevation-stroke: 0 0 0 1px hsl(var(--primary-860-hsl)/0.08); - --elevation-low: 0 1px 0 hsl(var(--primary-860-hsl)/0.1),0 1.5px 0 hsl(var(--primary-900-hsl)/0.025),0 2px 0 hsl(var(--primary-860-hsl)/0.025); - --elevation-medium: 0 4px 4px hsl(var(--black-hsl)/0.08); - --elevation-high: 0 8px 16px hsl(var(--black-hsl)/0.16) -} - -@font-face { - font-family: ABC Ginto Nord; - font-style: normal; - font-weight: 800; - src: url(/assets/097b737553f77c92.woff2) format("woff2") -} - -@font-face { - font-family: ABC Ginto Nord; - font-style: italic; - font-weight: 800; - src: url(/assets/d7f3d9317a5ff964.woff2) format("woff2") -} - -@font-face { - font-family: ABC Ginto Nord Discord; - font-style: normal; - font-weight: 700; - src: url(/assets/bb673ee29d9d9269.woff2) format("woff2") -} - -@font-face { - font-family: ABC Ginto Nord Discord; - font-style: italic; - font-weight: 700; - src: url(/assets/1a1e4ba0da23278e.woff2) format("woff2") -} - -@font-face { - font-family: ABC Ginto Normal; - font-style: normal; - font-weight: 300; - src: url(/assets/2d538acdbc2eb6b8.woff2) format("woff2") -} - -@font-face { - font-family: ABC Ginto Normal; - font-style: italic; - font-weight: 300; - src: url(/assets/6897c8395199e044.woff2) format("woff2") -} - -@font-face { - font-family: ABC Ginto Normal; - font-style: normal; - font-weight: 400; - src: url(/assets/89cc835cf102bf12.woff2) format("woff2") -} - -@font-face { - font-family: ABC Ginto Normal; - font-style: italic; - font-weight: 400; - src: url(/assets/9e78b20620cb79ff.woff2) format("woff2") -} - -@font-face { - font-family: ABC Ginto Normal; - font-style: normal; - font-weight: 500; - src: url(/assets/094adef82c637a3c.woff2) format("woff2") -} - -@font-face { - font-family: ABC Ginto Normal; - font-style: italic; - font-weight: 500; - src: url(/assets/5b3d873665de628f.woff2) format("woff2") -} - -@font-face { - font-family: ABC Ginto Normal; - font-style: normal; - font-weight: 700; - src: url(/assets/a893ac26791508c7.woff2) format("woff2") -} - -@font-face { - font-family: ABC Ginto Normal; - font-style: italic; - font-weight: 700; - src: url(/assets/5a4f9cc740b6d92b.woff2) format("woff2") -} - -@font-face { - font-family: ABC Ginto Normal; - font-style: normal; - font-weight: 800; - src: url(/assets/908064caebce5abe.woff2) format("woff2") -} - -@font-face { - font-family: ABC Ginto Normal; - font-style: italic; - font-weight: 800; - src: url(/assets/f2d2cc13894d7060.woff2) format("woff2") -} - -@font-face { - font-family: ABC Ginto Discord; - font-style: normal; - font-weight: 400; - src: url(/assets/bd0f5c73464e9717.woff2) format("woff2") -} - -@font-face { - font-family: ABC Ginto Discord; - font-style: normal; - font-weight: 500; - src: url(/assets/a47177db4e543b05.woff2) format("woff2") -} - -@font-face { - font-family: gg sans; - font-style: normal; - font-weight: 400; - src: url(/assets/66d715454104d24e.woff2) format("woff2") -} - -@font-face { - font-family: gg sans; - font-style: italic; - font-weight: 400; - src: url(/assets/dd24010f3cf7def7.woff2) format("woff2") -} - -@font-face { - font-family: gg sans; - font-style: normal; - font-weight: 500; - src: url(/assets/b272b33815319bae.woff2) format("woff2") -} - -@font-face { - font-family: gg sans; - font-style: italic; - font-weight: 500; - src: url(/assets/6a1346ad3821ff3c.woff2) format("woff2") -} - -@font-face { - font-family: gg sans; - font-style: normal; - font-weight: 600; - src: url(/assets/2df2c3ff74408972.woff2) format("woff2") -} - -@font-face { - font-family: gg sans; - font-style: italic; - font-weight: 600; - src: url(/assets/d5d789aeb6282532.woff2) format("woff2") -} - -@font-face { - font-family: gg sans; - font-style: normal; - font-weight: 700; - src: url(/assets/189422196a4f8b53.woff2) format("woff2") -} - -@font-face { - font-family: gg sans; - font-style: italic; - font-weight: 700; - src: url(/assets/ce3b8055f5114434.woff2) format("woff2") -} - -@font-face { - font-family: gg sans; - font-style: normal; - font-weight: 800; - src: url(/assets/b2fdbe507d6ce9ef.woff2) format("woff2") -} - -@font-face { - font-family: gg sans; - font-style: italic; - font-weight: 800; - src: url(/assets/03dcf979852e8b8e.woff2) format("woff2") -} - -@font-face { - font-family: gg mono; - font-style: normal; - font-weight: 400; - src: url(/assets/249d0a057895c668.woff2) format("woff2") -} - -@font-face { - font-family: gg mono; - font-style: normal; - font-weight: 700; - src: url(/assets/45efa6936fdfb918.woff2) format("woff2") -} - -@font-face { - font-family: Noto Sans; - font-style: normal; - font-weight: 400; - src: url(/assets/f72b5ce64feb2086.woff2) format("woff2") -} - -@font-face { - font-family: Noto Sans; - font-style: italic; - font-weight: 400; - src: url(/assets/7a6a566c2e88a35d.woff2) format("woff2") -} - -@font-face { - font-family: Noto Sans; - font-style: normal; - font-weight: 500; - src: url(/assets/a4a3d323feb11add.woff2) format("woff2") -} - -@font-face { - font-family: Noto Sans; - font-style: italic; - font-weight: 500; - src: url(/assets/1a9d6f15e3bade15.woff2) format("woff2") -} - -@font-face { - font-family: Noto Sans; - font-style: normal; - font-weight: 600; - src: url(/assets/36e7b68ea0c05ae7.woff2) format("woff2") -} - -@font-face { - font-family: Noto Sans; - font-style: italic; - font-weight: 600; - src: url(/assets/7b652d8bbf885aea.woff2) format("woff2") -} - -@font-face { - font-family: Noto Sans; - font-style: normal; - font-weight: 700; - src: url(/assets/cb2006dbced0e246.woff2) format("woff2") -} - -@font-face { - font-family: Noto Sans; - font-style: italic; - font-weight: 700; - src: url(/assets/e52f0cba712e2fb4.woff2) format("woff2") -} - -@font-face { - font-family: Noto Sans; - font-style: normal; - font-weight: 800; - src: url(/assets/772df2968ca0cf92.woff2) format("woff2") -} - -@font-face { - font-family: Noto Sans; - font-style: italic; - font-weight: 800; - src: url(/assets/19797abd0807f76b.woff2) format("woff2") -} - -@font-face { - font-family: Source Code Pro; - font-style: normal; - font-weight: 400; - src: url(/assets/268aaee6b96a3789.woff2) format("woff2") -} - -@font-face { - font-family: Source Code Pro; - font-style: normal; - font-weight: 700; - src: url(/assets/c76eb070f0fcec44.woff2) format("woff2") -} - -@font-face { - font-family: Corinthia; - font-style: normal; - font-weight: 400; - src: url(/assets/b598312a5e479904.woff2) format("woff2") -} - -@font-face { - font-family: Fraunces; - font-style: normal; - font-weight: 300 800; - src: url(/assets/943f151cdf1b637e.woff2) format("woff2") -} - -@font-face { - font-family: Munro; - font-style: normal; - font-weight: 400; - src: url(/assets/b4099c935ba38494.woff2) format("woff2") -} - -@font-face { - font-family: Cherry Bomb One; - font-style: normal; - font-weight: 400; - src: url(/assets/27a6daf95bd8adde.woff2) format("woff2") -} - -@font-face { - font-family: Chicle; - font-style: normal; - font-weight: 400; - src: url(/assets/08d347453f57b1f5.woff2) format("woff2") -} - -@font-face { - font-family: Neo Castel; - font-style: normal; - font-weight: 400; - src: url(/assets/b98eacfe1a3d06eb.woff2) format("woff2") -} - -@font-face { - font-family: Museo Moderno; - font-style: normal; - font-weight: 500; - src: url(/assets/8b6d8d408e0e52a7.woff2) format("woff2") -} - -@font-face { - font-family: Pixelify Sans; - font-style: normal; - font-weight: 400; - src: url(/assets/752009d1dc4553e8.woff2) format("woff2") -} - -@font-face { - font-family: Sinistre; - font-style: normal; - font-weight: 400; - src: url(/assets/9b974db0b9197a22.woff2) format("woff2") -} - -@font-face { - font-family: Zilla Slab; - font-style: normal; - font-weight: 600; - src: url(/assets/8698e980e38d61ed.woff2) format("woff2") -} - -:root { - --font-primary: "gg sans","Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display: "gg sans","Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-headline: "ABC Ginto Nord","Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-code: "gg mono","Source Code Pro",Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace; - --font-clan-body: Fraunces,"gg sans",serif,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-clan-signature: Corinthia,"gg sans",cursive,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display-marketing: "ABC Ginto Discord","gg sans",serif,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display-marketing-header: "ABC Ginto Nord Discord","gg sans",serif,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif -} - -:root:lang(bg),:root:lang(el),:root:lang(ru),:root:lang(uk) { - --font-primary: "gg sans","Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display: "gg sans","Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-headline: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-code: "gg mono","Source Code Pro",Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace; - --font-clan-body: Fraunces,"gg sans",serif,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-clan-signature: Corinthia,"gg sans",cursive,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display-marketing: "ABC Ginto Discord","gg sans",serif,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display-marketing-header: "ABC Ginto Nord Discord","gg sans",serif,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif -} - -:root:lang(ko) { - --font-primary: "gg sans","Apple SD Gothic Neo",NanumBarunGothic,"맑은 고딕","Malgun Gothic",Gulim,굴림,Dotum,돋움,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display: "gg sans","Apple SD Gothic Neo",NanumBarunGothic,"맑은 고딕","Malgun Gothic",Gulim,굴림,Dotum,돋움,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-headline: "ABC Ginto Nord","Apple SD Gothic Neo",NanumBarunGothic,"맑은 고딕","Malgun Gothic",Gulim,굴림,Dotum,돋움,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-code: "gg mono","Source Code Pro",Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace; - --font-clan-body: Fraunces,"gg sans",serif,"Apple SD Gothic Neo",NanumBarunGothic,"맑은 고딕","Malgun Gothic",Gulim,굴림,Dotum,돋움,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-clan-signature: Corinthia,"gg sans",cursive,"Apple SD Gothic Neo",NanumBarunGothic,"맑은 고딕","Malgun Gothic",Gulim,굴림,Dotum,돋움,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display-marketing: "ABC Ginto Discord","gg sans",serif,"Apple SD Gothic Neo",NanumBarunGothic,"맑은 고딕","Malgun Gothic",Gulim,굴림,Dotum,돋움,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display-marketing-header: "ABC Ginto Nord Discord","gg sans",serif,"Apple SD Gothic Neo",NanumBarunGothic,"맑은 고딕","Malgun Gothic",Gulim,굴림,Dotum,돋움,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif -} - -:root:lang(ja) { - --font-primary: "gg sans","Hiragino Sans","ヒラギノ角ゴ ProN W3","Hiragino Kaku Gothic ProN",メイリオ,Meiryo,Osaka,"MS PGothic","Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display: "gg sans","Hiragino Sans","ヒラギノ角ゴ ProN W3","Hiragino Kaku Gothic ProN",メイリオ,Meiryo,Osaka,"MS PGothic","Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-headline: "ABC Ginto Nord","Hiragino Sans","ヒラギノ角ゴ ProN W3","Hiragino Kaku Gothic ProN",メイリオ,Meiryo,Osaka,"MS PGothic","Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-code: "gg mono","Source Code Pro",Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace; - --font-clan-body: Fraunces,"gg sans",serif,"Hiragino Sans","ヒラギノ角ゴ ProN W3","Hiragino Kaku Gothic ProN",メイリオ,Meiryo,Osaka,"MS PGothic","Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-clan-signature: Corinthia,"gg sans",cursive,"Hiragino Sans","ヒラギノ角ゴ ProN W3","Hiragino Kaku Gothic ProN",メイリオ,Meiryo,Osaka,"MS PGothic","Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display-marketing: "ABC Ginto Discord","gg sans",serif,"Hiragino Sans","ヒラギノ角ゴ ProN W3","Hiragino Kaku Gothic ProN",メイリオ,Meiryo,Osaka,"MS PGothic","Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display-marketing-header: "ABC Ginto Nord Discord","gg sans",serif,"Hiragino Sans","ヒラギノ角ゴ ProN W3","Hiragino Kaku Gothic ProN",メイリオ,Meiryo,Osaka,"MS PGothic","Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif -} - -:root:lang(zh-CN) { - --font-primary: "gg sans","Microsoft YaHei New",微软雅黑,"Microsoft Yahei","Microsoft JhengHei",宋体,SimSun,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display: "gg sans","Microsoft YaHei New",微软雅黑,"Microsoft Yahei","Microsoft JhengHei",宋体,SimSun,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-headline: "ABC Ginto Nord","Microsoft YaHei New",微软雅黑,"Microsoft Yahei","Microsoft JhengHei",宋体,SimSun,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-code: "gg mono","Source Code Pro",Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace; - --font-clan-body: Fraunces,"gg sans",serif,"Microsoft YaHei New",微软雅黑,"Microsoft Yahei","Microsoft JhengHei",宋体,SimSun,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-clan-signature: Corinthia,"gg sans",cursive,"Microsoft YaHei New",微软雅黑,"Microsoft Yahei","Microsoft JhengHei",宋体,SimSun,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display-marketing: "ABC Ginto Discord","gg sans",serif,"Microsoft YaHei New",微软雅黑,"Microsoft Yahei","Microsoft JhengHei",宋体,SimSun,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display-marketing-header: "ABC Ginto Nord Discord","gg sans",serif,"Microsoft YaHei New",微软雅黑,"Microsoft Yahei","Microsoft JhengHei",宋体,SimSun,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif -} - -:root:lang(zh-TW) { - --font-primary: "gg sans","Microsoft JhengHei",微軟正黑體,"Microsoft JhengHei UI","Microsoft YaHei",微軟雅黑,宋体,SimSun,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display: "gg sans","Microsoft JhengHei",微軟正黑體,"Microsoft JhengHei UI","Microsoft YaHei",微軟雅黑,宋体,SimSun,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-headline: "ABC Ginto Nord","Microsoft JhengHei",微軟正黑體,"Microsoft JhengHei UI","Microsoft YaHei",微軟雅黑,宋体,SimSun,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-code: "gg mono","Source Code Pro",Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace; - --font-clan-body: Fraunces,"gg sans",serif,"Microsoft JhengHei",微軟正黑體,"Microsoft JhengHei UI","Microsoft YaHei",微軟雅黑,宋体,SimSun,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-clan-signature: Corinthia,"gg sans",cursive,"Microsoft JhengHei",微軟正黑體,"Microsoft JhengHei UI","Microsoft YaHei",微軟雅黑,宋体,SimSun,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display-marketing: "ABC Ginto Discord","gg sans",serif,"Microsoft JhengHei",微軟正黑體,"Microsoft JhengHei UI","Microsoft YaHei",微軟雅黑,宋体,SimSun,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; - --font-display-marketing-header: "ABC Ginto Nord Discord","gg sans",serif,"Microsoft JhengHei",微軟正黑體,"Microsoft JhengHei UI","Microsoft YaHei",微軟雅黑,宋体,SimSun,"Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif -} - -:root { - --font-weight-light: 300; - --font-weight-normal: 400; - --font-weight-medium: 500; - --font-weight-semibold: 600; - --font-weight-bold: 700; - --font-weight-extra-bold: 800; - --channels-name-line-height: 24px; - --channels-spine-inverted-offset-top: 6px; - --channels-spine-offset-left: 24px; - --chat-avatar-size: 40px; - --chat-input-icon-size: 20px; - --chat-markup-line-height: 1.375rem; - --chat-resize-handle-width: 8px; - --control-input-height-md: 40px; - --control-input-height-sm: 32px; - --control-item-height-md: 40px; - --control-item-height-sm: 32px; - --form-input-height: 44px; - --guildbar-avatar-size: 40px; - --guildbar-folder-size: 48px; - --icon-size-lg: 32px; - --icon-size-md: 24px; - --icon-size-sm: 18px; - --icon-size-xs: 16px; - --icon-size-xxs: 12px; - --modal-horizontal-padding: 24px; - --modal-vertical-padding: 16px; - --modal-width-large: 800px; - --modal-width-medium: 602px; - --modal-width-small: 442px; - --select-max-width: 248px; - --select-option-height: 40px -} - -.refresh-fast-follow-avatars.density-compact { - --guildbar-avatar-size: 40px; - --guildbar-folder-size: 48px -} - -.refresh-fast-follow-avatars.density-cozy,.refresh-fast-follow-avatars.density-default { - --guildbar-avatar-size: 44px; - --guildbar-folder-size: 52px -} - -.density-compact { - --channels-name-line-height: 20px; - --channels-spine-inverted-offset-top: 2px; - --channels-spine-offset-left: 20px -} - -.density-default { - --channels-name-line-height: 24px; - --channels-spine-inverted-offset-top: 6px; - --channels-spine-offset-left: 24px -} - -.density-cozy { - --channels-name-line-height: 28px; - --channels-spine-inverted-offset-top: 9px; - --channels-spine-offset-left: 28px -} - -a,abbr,acronym,address,applet,big,blockquote,body,caption,cite,code,dd,del,dfn,div,dl,dt,em,fieldset,form,h1,h2,h3,h4,h5,h6,html,iframe,img,ins,kbd,label,legend,li,object,ol,p,pre,q,s,samp,small,span,strike,strong,table,tbody,td,tfoot,th,thead,tr,tt,ul,var { - border: 0; - font-family: inherit; - font-size: 100%; - font-style: inherit; - font-weight: inherit; - margin: 0; - padding: 0; - vertical-align: baseline -} - -a { - color: var(--text-link); - cursor: pointer; - text-decoration: none -} - -a img { - border: none -} - -body { - background: transparent; - font-family: var(--font-primary); - line-height: 1; - margin: 0; - overflow: hidden; - padding: 0; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none -} - -p { - margin: 14px 0 -} - -ol,ul { - list-style: none -} - -::-moz-placeholder { - font-weight: var(--font-weight-normal) -} - -::placeholder,input,select,textarea { - font-family: var(--font-primary); - font-weight: var(--font-weight-normal) -} - -@media (-webkit-max-device-pixel-ratio: 1) { - .theme-light ::-moz-placeholder { - font-weight:var(--font-weight-medium) - } - - .theme-light ::placeholder,.theme-light input,.theme-light select,.theme-light textarea { - font-weight: var(--font-weight-medium) - } -} - -strong { - font-weight: var(--font-weight-semibold) -} - -button { - border: 0; - cursor: pointer; - font-family: var(--font-primary); - font-weight: var(--font-weight-medium) -} - -code { - font-family: var(--font-code); - font-size: 14px; - line-height: 16px -} - -:root { - --custom-index-scrollbar-width: 10px; - --custom-index-scrollbar-margin: 3px; - --custom-auth-box-auth-box-padding: 32px; - --custom-wave-splash-responsive-width-mobile: 485px; - --custom-wave-splash-responsive-width-mobile-first: 486px; - --custom-wave-splash-responsive-width-desktop: 1080px; - --custom-wave-splash-max-qr-login-width: 830px; - --custom-channel-text-area-button-hover-scale: 0.85714; - --custom-drag-resize-container-handle-size: 8px; - --custom-drag-resize-container-handle-bleed: 2px; - --custom-drag-resize-container-handle-offset: calc(var(--custom-drag-resize-container-handle-bleed) - var(--custom-drag-resize-container-handle-size)); - --custom-embed-spoiler-blur-radius: 44px; - --custom-gradient-progress-notch-width: 8px; - --custom-gradient-progress-notch-height: 16px; - --custom-gradient-progress-notch-margin: 2px; - --custom-guild-discovery-card-card-height: 320px; - --custom-guild-discovery-card-card-height-with-tags: 350px; - --custom-icon-button-icon-lg-size: 36px; - --custom-icon-button-icon-md-size: 24px; - --custom-icon-button-icon-sm-size: 18px; - --custom-icon-button-icon-xs-size: 12px; - --custom-invite-button-resolving-background-width: 380px; - --custom-keybind-space-around-key: 8px; - --custom-keybind-shadow-width: 2px; - --custom-keybind-vertical-padding-total-height: 8px; - --custom-keybind-applied-vertical-padding: calc((var(--custom-keybind-vertical-padding-total-height) - var(--custom-keybind-shadow-width))/2); - --custom-full-screen-layer-animation-duration: 150ms; - --custom-layout-sidebar-width: 232px; - --custom-message-avatar-size: 40px; - --custom-message-avatar-decoration-size: calc(var(--custom-message-avatar-size)*var(--decoration-to-avatar-ratio)); - --custom-message-margin-compact-indent: 5rem; - --custom-message-spacing-vertical-container-cozy: 0.125rem; - --custom-message-padding-vertical-container-compact: 0.125rem; - --custom-message-meta-space: 0.25rem; - --custom-message-reply-indent: 0.625rem; - --custom-message-margin-left-content-cozy: calc(var(--custom-message-avatar-size, 40px) + var(--custom-message-margin-horizontal) + var(--custom-message-margin-horizontal)); - --custom-message-reply-message-preview-line-height: 1.125rem; - --custom-message-attachment-spoiler-blur-radius: 44px; - --custom-user-premium-guild-subscription-easter-egg-size: 196px; - --custom-notification-spacing: 12px; - --custom-notification-container-width: 300px; - --custom-notification-space-around-divider: 12px; - --custom-notification-box-shadow-opacity: 0.8; - --custom-notification-box-shadow-blur-radius: 7px; - --custom-notification-box-shadow-spread-radius: 3px; - --custom-widget-max-widget-height: 100vh; - --custom-widget-bar-padding: 12px; - --custom-widget-body-padding: 4px; - --custom-widget-bar-height: 20px; - --custom-premium-guild-progress-bar-progress-bar-width: 24px; - --custom-premium-guild-progress-bar-tier-marker-size: 16px; - --custom-call-avatar-outgoing-duration: 2.5s; - --custom-call-avatar-incoming-duration: 5.407s; - --custom-resizable-icon-size: 16px; - --custom-resizable-icon-padding: 4px; - --custom-resizable-extension-size: 4px; - --custom-live-indicator-border-radius: 16px; - --custom-tutorial-popout-padding-content: 16px; - --custom-tutorial-popout-height-media-approx: 80px; - --custom-emoji-size-emoji: 1.375em; - --custom-emoji-size-jumbo-emoji: 3rem; - --custom-margin-margin-x-small: 4px; - --custom-margin-margin-small: 8px; - --custom-margin-margin-medium: 20px; - --custom-margin-margin-large: 40px; - --custom-margin-margin-x-large: 60px; - --custom-media-queries-media-query-mobile-header: 849px; - --custom-media-queries-media-query-mobile: 768px; - --custom-scrollbar-scrollbar-width: 14px; - --custom-scrollbar-scrollbar-margin: 3px; - --custom-scrollbar-border-radius: calc(var(--custom-scrollbar-scrollbar-width)/2); - --custom-voice-channel-effect-voice-channel-effect-animation-size: 800px; - --custom-voice-channel-effects-bar-effect-bar-padding-x: 16px; - --custom-voice-channel-effects-bar-effect-bar-padding-y: 12px; - --custom-voice-channel-effects-bar-max-width: 498px; - --custom-base-tile-border-radius: 8px; - --video-calls-base-tile-border-radius: var(--radius-lg); - --custom-summary-avatars-avatar-diameter: 16px; - --custom-guild-settings-premium-tier-status-progress-with-subscriptions-margin-top: 7px; - --custom-guild-stickers-tiers-grid-breakpoint: 1010px; - --custom-guild-sticker-upload-modal-emoji-size: 22px; - --custom-guild-sticker-upload-modal-emoji-margin: 10px; - --custom-stickers-constants-stickers-list-padding-top: 0; - --custom-stickers-constants-stickers-list-padding-top-search-results: 8px; - --custom-stickers-constants-stickers-list-padding-right: 0; - --custom-stickers-constants-stickers-list-padding-bottom: 0; - --custom-stickers-constants-stickers-list-padding-left: 8px; - --custom-stickers-constants-stickers-list-divider-height: 30px; - --custom-stickers-constants-stickers-list-empty-guild-upsell-height: 54px; - --custom-stickers-constants-stickers-shop-list-section-heading-height: 66px; - --custom-stickers-constants-stickers-shop-divider-border-top-width: 1px; - --custom-stickers-constants-stickers-shop-divider-height: 1px; - --custom-stickers-constants-stickers-shop-list-section-footer-height: 32px; - --custom-stickers-constants-stickers-shop-list-section-footer-stacked-margin: 8px; - --custom-stickers-constants-sticker-picker-preview-dimensions: 96px; - --custom-stickers-constants-sticker-picker-preview-dimensions-small: 80px; - --custom-stickers-constants-sticker-picker-preview-padding: 2px; - --custom-stickers-constants-sticker-shop-modal-preview-dimensions: 72px; - --custom-stickers-constants-sticker-picker-preview-margin: 12px; - --custom-stickers-constants-sticker-picker-preview-margin-small: 4px; - --custom-stickers-constants-sticker-picker-preview-border-radius: 4px; - --custom-stickers-constants-sticker-category-list-padding: 8px; - --custom-stickers-constants-sticker-category-icon-margin: 8px; - --custom-stickers-constants-sticker-category-icon-size: 32px; - --custom-stickers-constants-sticker-category-unicode-icon-size: 24px; - --custom-stickers-constants-sticker-dimension: 160px; - --custom-stickers-constants-sticker-purchase-pack-preview-dimensions: 76px; - --custom-stickers-constants-sticker-purchase-pack-preview-padding: 2px; - --custom-stickers-constants-sticker-purchase-pack-preview-margin: 0; - --custom-stickers-constants-sticker-picker-breakpoint-small: 360px; - --custom-stickers-constants-sticker-picker-breakpoint-medium: 446px; - --custom-stickers-constants-category-separator-size: 1px; - --custom-stickers-constants-category-separator-margin-vertical: 12px; - --custom-header-icon-color-expired: #4e5058; - --custom-reaction-picker-border-radius: 8px; - --custom-outbound-promotion-redemption-modal-art-height: 120px; - --custom-premium-colors-premium-gradient-tier-0: linear-gradient(to right,var(--premium-tier-0-purple-for-gradients),var(--premium-tier-0-blue-for-gradients)); - --custom-premium-colors-premium-gradient-tier-1: linear-gradient(to right,var(--premium-tier-1-blue-for-gradients),var(--premium-tier-1-dark-blue-for-gradients)); - --custom-premium-colors-premium-gradient-tier-2: linear-gradient(to right,var(--premium-tier-2-purple-for-gradients),var(--premium-tier-2-pink-for-gradients)); - --custom-premium-colors-premium-gradient-tier-2-transparent: linear-gradient(to right,hsl(var(--premium-tier-2-purple-for-gradients-hsl)/0.3),hsl(var(--premium-tier-2-pink-for-gradients-hsl)/0.3)); - --custom-premium-colors-premium-gradient-tier-2-diagonal: linear-gradient(45deg,var(--premium-tier-2-purple-for-gradients),var(--premium-tier-2-pink-for-gradients)); - --custom-premium-colors-premium-gradient-tier-2-tri-color: linear-gradient(to right,var(--premium-tier-2-purple-for-gradients),var(--premium-tier-2-purple-for-gradients-2),var(--premium-tier-2-pink-for-gradients)); - --custom-premium-colors-premium-gradient-tier-2-tri-color-reverse: linear-gradient(to left,var(--premium-tier-2-purple-for-gradients),var(--premium-tier-2-purple-for-gradients-2),var(--premium-tier-2-pink-for-gradients)); - --custom-premium-colors-premium-gradient-tier-2-tri-color-vertical: linear-gradient(to top,var(--premium-tier-2-purple-for-gradients),var(--premium-tier-2-purple-for-gradients-2),var(--premium-tier-2-pink-for-gradients)); - --custom-premium-colors-premium-gradient-tier-2-old: linear-gradient(to right,var(--premium-tier-2-purple),var(--premium-tier-2-pink)); - --custom-premium-colors-banner-yellow: #ffeaa0; - --custom-premium-colors-banner-black: #232428; - --custom-pending-row-max-width: 1040px; - --custom-people-page-max-width-with-toolbar: 940px; - --custom-family-center-page-responsive-width-mobile: 900px; - --custom-text-widget-background-color-header-default: var(--primary-600); - --custom-voice-widget-top-margin: calc(var(--custom-widget-bar-height) + var(--custom-widget-bar-padding)*2); - --custom-voice-widget-widget-width: 272px; - --custom-party-avatars-avatar-diameter: 24px; - --custom-mobile-web-handoff-link-mobile-web-handoff-height: 70px; - --custom-embedded-application-invite-content-height: 130px; - --custom-embedded-application-invite-content-height-tall: 162px; - --custom-embedded-application-invite-content-width: 576px; - --custom-embedded-application-invite-image-width-small: 225px; - --custom-embedded-application-invite-image-width-large: 288px; - --custom-message-helpers-popout-content-width: 256px; - --custom-message-helpers-popout-padding-width: 16px; - --custom-message-helpers-popout-container-width: calc(var(--custom-message-helpers-popout-content-width) + var(--custom-message-helpers-popout-padding-width)*2); - --custom-media-post-attachments-horizontal-padding: 48px; - --custom-media-post-embed-spoiler-blur-radius: 20px; - --custom-media-post-embed-transition-duration: 170ms; - --custom-raging-demon-duration: 0.15s; - --custom-interaction-iframe-modal-modal-min-width: 320px; - --custom-interaction-iframe-modal-modal-margin: 80px; - --custom-interaction-iframe-modal-modal-max-width: 1280px; - --custom-interaction-iframe-modal-modal-header-height: 52px; - --custom-accept-invite-detail-components-small-mobile-breakpoint: 640px; - --custom-accept-invite-mobile-small-mobile-breakpoint: 640px; - --custom-hub-discovery-add-hub-card-card-height: 320px; - --custom-hub-discovery-guild-list-card-height: 320px; - --custom-hub-discovery-guild-list-min-card-width: 248px; - --custom-hub-discovery-guild-list-gutter-size: 16px; - --custom-hub-discovery-page-max-page-width: 1608px; - --custom-guild-tooltip-icon-size: 20px; - --custom-guild-tooltip-icon-size-v-2: 16px; - --custom-folder-item-animation-duration: 150ms; - --custom-folder-item-guild-icon-margin: 8px; - --custom-category-channel-space-before-category: 16px -} - -.density-compact { - --custom-category-channel-space-before-category: var(--space-8) -} - -:root { - --custom-add-permissions-modal-focus-ring-width: 4px; - --custom-custom-role-icon-form-item-role-icon-preview-size: 32px; - --custom-guild-settings-roles-edit-shared-sidebar-width: 232px; - --custom-guild-settings-roles-intro-roles-transition: 250ms; - --custom-guild-settings-roles-intro-pause-transition: 166ms; - --custom-guild-settings-roles-intro-background-transition: 500ms; - --custom-guild-settings-roles-intro-banner-transition-delay: calc(var(--custom-guild-settings-roles-intro-roles-transition) + var(--custom-guild-settings-roles-intro-pause-transition)); - --custom-guild-settings-roles-intro-roles-transition-delay: calc(var(--custom-guild-settings-roles-intro-roles-transition) + var(--custom-guild-settings-roles-intro-pause-transition)*2 + var(--custom-guild-settings-roles-intro-background-transition)); - --custom-guild-settings-community-intro-content-spacing: 32px; - --custom-guild-settings-community-intro-hover-distance: -12px; - --custom-guild-settings-community-intro-text-spacing: 8px; - --custom-guild-settings-discovery-landing-page-max-width-tab: 905px; - --custom-guild-settings-discovery-landing-page-settings-max-width: 520px; - --custom-guild-settings-partner-content-spacing: 32px; - --custom-event-detail-info-tab-base-spacing: 8px; - --custom-subscription-listing-previews-carousel-cards-get-cut-off-width: 724px; - --custom-editable-benefits-list-emoji-size: 24px; - --custom-edit-benefit-modal-emoji-size: 22px; - --custom-edit-benefit-modal-emoji-margin: 10px; - --custom-guild-settings-role-subscriptions-max-width: 905px; - --custom-guild-settings-role-subscriptions-overview-settings-max-width: 520px; - --custom-guild-settings-store-page-settings-max-width: 520px; - --custom-importable-benefits-list-listing-image-size: 40px; - --custom-import-benefits-modal-icon-size: 24px; - --custom-import-benefits-modal-role-icon-size: 40px; - --custom-role-icon-uploader-icon-size: 24px; - --custom-guild-role-subscription-style-constants-cover-image-aspect-ratio: 4; - --custom-historic-earnings-table-toggle-expand-column-width: 30px; - --custom-guild-role-subscription-card-basic-info-tier-image-size: 80px; - --custom-guild-role-subscription-card-basic-info-tier-image-size-mobile: 48px; - --custom-guild-role-subscriptions-overview-page-page-max-width: 1180px; - --custom-guild-dialog-popout-width: 250px; - --custom-guild-dialog-splash-ratio: 1.77778; - --custom-guild-dialog-icon-size: 84px; - --custom-guild-dialog-icon-padding: 4px; - --custom-guild-product-download-modal-header-image-width: 119px; - --custom-guild-onboarding-home-page-max-page-width: 1128px; - --custom-guild-onboarding-home-page-max-single-column-width: 704px; - --custom-home-resource-channels-obscured-blur-radius: 20px; - --custom-guild-member-application-review-sidebar-width: 29vw; - --custom-featured-items-popout-featured-items-popout-footer-height: 120px; - --custom-guild-boosting-sidebar-display-conditional-bottom-margin: 12px; - --custom-guild-boosting-marketing-progress-bar-marker-dimensions: 32px; - --custom-guild-boosting-marketing-progress-bar-end-markers-margin: 4px; - --custom-guild-boosting-marketing-progress-bar-marker-marker-dimensions: 32px; - --custom-guild-boosting-marketing-tier-cards-tier-card-border-radius: 16px; - --custom-go-live-modal-art-height: 112px; - --custom-gif-picker-gutter-size: 0 16px 12px 16px; - --custom-gif-picker-search-results-desired-item-width: 160px; - --custom-forum-composer-attachments-attachment-size: 78px; - --custom-forum-post-obscured-blur-radius: 20px; - --custom-forum-post-grid-view-obscured-blur-radius: 20px; - --custom-demo-forum-channel-padding-large: 20px; - --custom-demo-forum-channel-post-padding: 12px; - --custom-demo-forum-channel-gap-size: 8px; - --custom-feedback-modal-emoji-size: 64px; - --custom-feedback-modal-close-button-margin: 12px; - --custom-expression-suggestions-container-padding: 8px; - --custom-expression-suggestions-sticker-suggestion-size: 48px; - --custom-expression-suggestions-sticker-suggestion-margin: 8px; - --custom-expression-picker-constants-expression-picker-list-section-heading-height: 32px; - --custom-expression-picker-constants-expression-picker-inspector-bar-graphic-primary-dimensions: 28px; - --custom-expression-picker-constants-expression-picker-inspector-bar-graphic-secondary-dimensions: 32px; - --custom-expression-picker-constants-expression-picker-inspector-bar-height: 48px; - --custom-emoji-picker-border-radius: 8px; - --custom-emoji-picker-constants-min-emoji-picker-width: 498px; - --custom-emoji-picker-constants-emoji-size-medium: 40px; - --custom-emoji-picker-constants-emoji-size-large: 48px; - --custom-emoji-picker-constants-emoji-container-padding-horizontal: 4px; - --custom-emoji-picker-constants-emoji-container-padding-vertical: 4px; - --custom-emoji-picker-constants-emoji-picker-height: 440px; - --custom-emoji-picker-constants-emoji-section-margin-bottom: 12px; - --custom-emoji-picker-constants-emoji-list-padding-top: 0; - --custom-emoji-picker-constants-emoji-list-padding-right: 0; - --custom-emoji-picker-constants-emoji-list-padding-bottom: 8px; - --custom-emoji-picker-constants-emoji-list-padding-left: 8px; - --custom-emoji-picker-constants-emoji-list-search-results-padding-top: 8px; - --custom-emoji-picker-constants-unicode-category-icon-margin-vertical: 2px; - --custom-emoji-picker-constants-unicode-category-icon-size: 24px; - --custom-emoji-picker-constants-unicode-category-icon-padding: 4px; - --custom-emoji-picker-constants-unicode-category-shortcut-height: 48px; - --custom-emoji-picker-constants-guild-category-icon-size: 32px; - --custom-emoji-picker-constants-guild-category-icon-margin-verical: 8px; - --custom-emoji-picker-constants-category-separator-size: 1px; - --custom-emoji-picker-constants-category-separator-margin-vertical: 12px; - --custom-emoji-picker-constants-diversity-emoji-size: 24px; - --custom-emoji-picker-constants-emoji-premium-upsell-height: 54px; - --custom-emoji-picker-constants-emoji-premium-upsell-margin-top: 16px; - --custom-emoji-picker-constants-newly-added-emoji-badge-height: 16px; - --custom-discover-static-guild-card-card-height: 258px; - --custom-discover-featured-guilds-section-card-height: 320px; - --custom-discover-featured-guilds-section-min-card-width: 248px; - --custom-discover-featured-guilds-section-gutter-size: 16px; - --custom-discover-search-results-max-search-bar-width: 720px; - --custom-guild-directory-min-card-width: 248px; - --custom-guild-directory-gutter-size: 16px; - --custom-guild-directory-min-content-width: 320px; - --custom-guild-directory-max-page-width: 1608px; - --custom-guild-directory-entry-card-card-height: 274px; - --custom-guild-directory-landing-min-header-height: 200px; - --custom-guild-shop-page-two-column-max-width: 1439px; - --custom-aspect-stable-image-container-padding: 20px; - --custom-monetization-info-table-expandable-rows-toggle-expand-column-width: 30px; - --custom-guild-shop-content-width: 1044px; - --custom-guild-shop-content-width-reduced: 788px; - --custom-guild-shop-content-width-minimum: 688px; - --custom-guild-shop-channel-row-gradient: linear-gradient(113deg,#2f3570 1.98%,#422c70 94.48%); - --custom-guild-shop-channel-row-gradient-hover: linear-gradient(113deg,#383f86 1.98%,#4d3385 94.48%); - --custom-guild-shop-channel-row-border-gradient: linear-gradient(113deg,#6591ff,#d150ff); - --custom-guild-shop-channel-row-glow: 0 0 4px rgba(189,149,255,.5); - --custom-guild-shop-preview-pill-shadow-dark: -4px 5px #1d1d1d; - --custom-guild-shop-preview-pill-shadow-light: -4px 5px #d7dce8; - --custom-guild-shop-gradient-start: #686bff; - --custom-guild-shop-gradient-end: #c356fd; - --custom-clips-enabled-indicator-medium-break-point: 920px; - --custom-clips-enabled-indicator-badge-icon-dimension-override: 20px; - --custom-client-themes-editor-content-width: calc(var(--custom-theme-selection-selection-size)*3 + var(--custom-theme-selection-group-column-gap)*2); - --custom-client-themes-editor-editor-padding: 16px; - --custom-theme-selection-selection-size: 60px; - --custom-theme-selection-group-column-gap: 24px; - --custom-channel-attachment-upload-spoiler-blur-radius: 44px; - --custom-channel-attachment-upload-mini-attachment-size: 78px; - --custom-channel-textarea-text-area-height: 44px; - --custom-channel-textarea-text-area-max-height: 50vh; - --custom-channel-notice-icon-size: 16px; - --custom-channel-notice-padding: 12px; - --custom-channel-call-participants-popout-padding-value: 16px; - --custom-stream-upsell-modal-art-height: 149px; - --custom-voice-channel-status-modal-emoji-size: 22px; - --custom-voice-channel-status-modal-emoji-margin: 10px; - --custom-broadcasting-tooltip-image-offset: 40px; - --custom-application-directory-content-min-width: 600px; - --custom-application-directory-content-max-width: 1024px; - --custom-guild-count-small-icon-size: 16px; - --custom-guild-count-large-icon-size: 20px; - --custom-collection-gallery-text-container-width: 400px; - --custom-collection-gallery-media-breakpoint: 1024px; - --custom-collection-gallery-column-card-height: 600px; - --custom-collection-gallery-row-card-height: 283px; - --custom-collection-list-card-gap: 16px; - --custom-collection-list-with-image-grid-gap: 16px; - --custom-collections-collection-gap: 32px; - --custom-application-directory-profile-sidebar-width: 192px; - --custom-application-directory-profile-sidebar-margin-right: 48px; - --custom-application-directory-profile-icon-size: 122px; - --custom-application-directory-search-sidebar-width: 200px; - --custom-application-directory-search-sidebar-margin-right: 32px; - --custom-accept-invite-modal-invite-modal-height: 420px; - --custom-accept-invite-modal-small-screen-width: 720px; - --custom-avatar-avatar-decoration-border-position: calc((1 - var(--decoration-to-avatar-ratio))/2*100%); - --custom-button-button-xl-width: 148px; - --custom-button-button-xl-height: 50px; - --custom-button-button-lg-width: 130px; - --custom-button-button-lg-height: 44px; - --custom-button-button-md-width: 96px; - --custom-button-button-md-height: 38px; - --custom-button-button-sm-width: 60px; - --custom-button-button-sm-height: 32px; - --custom-button-button-tn-height: 24px; - --custom-button-button-tn-width: 52px; - --custom-button-link-underline-width: 1px; - --custom-button-link-underline-offset: 1px; - --custom-button-link-underline-stop: calc(var(--custom-button-link-underline-width) + var(--custom-button-link-underline-offset)); - --custom-button-filled-hover: 0.1; - --custom-button-filled-active: 0.2; - --custom-button-transition-duration: 170ms; - --custom-modal-min-width-large: 800px; - --custom-special-markdown-small-break-point: 600px; - --custom-special-markdown-medium-break-point: 768px; - --custom-user-profile-hype-squad-badge-icon-size: 24px; - --custom-user-profile-hype-squad-badge-shine-size-offset: 64px; - --custom-guild-discovery-gutter-size: 16px; - --custom-guild-discovery-max-page-width: 1608px; - --custom-dropdown-button-small-dropdown-size: 16px; - --custom-dropdown-button-medium-dropdown-size: 24px; - --custom-dropdown-button-large-dropdown-size: 32px; - --custom-dropdown-button-separator-padding: 4px; - --custom-dropdown-button-hitbox-padding: 8px; - --custom-responsive-embed-tile-loading-background-width: 271px; - --custom-game-install-locations-item-padding: 20px; - --custom-game-list-row-min-height: 62px; - --custom-game-list-linked-to-glow-duration: 2000ms; - --custom-application-store-home-store-home-width: 1245px; - --custom-application-store-listing-body-max-width: 880px; - --custom-store-colors-primary-750: #191b1d; - --custom-store-colors-premium-gradient: linear-gradient(to right,var(--premium-tier-2-purple),var(--premium-tier-2-pink)); - --custom-member-list-item-avatar-decoration-padding: 2px; - --custom-messages-popout-messages-popout-footer-height: 120px; - --custom-radio-image-border-thickness: 2px; - --custom-standard-sidebar-view-sidebar-content-width: 192px; - --custom-standard-sidebar-view-standard-padding: 20px; - --custom-standard-sidebar-view-sidebar-content-scrollbar-padding: 6px; - --custom-standard-sidebar-view-sidebar-total-width: 264px; - --decoration-to-avatar-ratio: 1.2; - --font-weight-normal: 400; - --font-weight-medium: 500; - --font-weight-semibold: 600; - --font-weight-bold: 700; - --font-weight-extrabold: 800 -} - -.theme-dark.custom-theme-background { - --custom-background-gradient-color: 0 0 0; - --custom-background-gradient-opacity-chat: 0.8; - --custom-background-gradient-highest-opacity: 0.65; - --custom-background-gradient-lowest-opacity: 0.9; - --custom-background-gradient-lower-opacity: 0.85; - --custom-background-gradient-low-opacity: 0.8; - --custom-background-gradient-high-opacity: 0.75; - --custom-background-gradient-higher-opacity: 0.7; - --custom-background-gradient-opacity-app-frame: var(--custom-background-gradient-higher-opacity) -} - -.theme-dark.custom-theme-background.custom-client-theme { - --custom-background-gradient-lowest-opacity: calc(0.54 + var(--custom-background-gradient-opacity-mix-amount, 1)*0.4*0.9); - --custom-background-gradient-lower-opacity: calc(0.51 + var(--custom-background-gradient-opacity-mix-amount, 1)*0.4*0.85); - --custom-background-gradient-low-opacity: calc(0.48 + var(--custom-background-gradient-opacity-mix-amount, 1)*0.4*0.8); - --custom-background-gradient-high-opacity: calc(0.45 + var(--custom-background-gradient-opacity-mix-amount, 1)*0.4*0.75); - --custom-background-gradient-higher-opacity: calc(0.42 + var(--custom-background-gradient-opacity-mix-amount, 1)*0.4*0.7); - --custom-background-gradient-opacity-chat: calc(0.48 + var(--custom-background-gradient-opacity-mix-amount, 1)*0.4*0.8); - --custom-background-gradient-highest-opacity: calc(0.39 + var(--custom-background-gradient-opacity-mix-amount, 1)*0.4*0.65) -} - -.custom-theme-background .theme-dark,.theme-dark.custom-theme-background { - --custom-theme-base-color: var(--custom-theme-base-color-dark); - --custom-theme-text-color: var(--custom-theme-text-color-dark); - --custom-theme-base-color-hsl: var(--custom-theme-base-color-dark-hsl) -} - -.theme-light.custom-theme-background { - --custom-background-gradient-color: 255 255 255; - --custom-background-gradient-highest-opacity: 0.95; - --custom-background-gradient-opacity-chat: 0.85; - --custom-background-gradient-lowest-opacity: 0.85; - --custom-background-gradient-lower-opacity: 0.8; - --custom-background-gradient-low-opacity: 0.75; - --custom-background-gradient-high-opacity: 0.7; - --custom-background-gradient-higher-opacity: 0.65; - --custom-background-gradient-opacity-app-frame: var(--custom-background-gradient-higher-opacity) -} - -.theme-light.custom-theme-background.custom-client-theme { - --custom-background-gradient-lowest-opacity: calc(0.51 + var(--custom-background-gradient-opacity-mix-amount, 1)*0.4*0.85); - --custom-background-gradient-lower-opacity: calc(0.48 + var(--custom-background-gradient-opacity-mix-amount, 1)*0.4*0.8); - --custom-background-gradient-low-opacity: calc(0.45 + var(--custom-background-gradient-opacity-mix-amount, 1)*0.4*0.75); - --custom-background-gradient-high-opacity: calc(0.42 + var(--custom-background-gradient-opacity-mix-amount, 1)*0.4*0.7); - --custom-background-gradient-higher-opacity: calc(0.325 + var(--custom-background-gradient-opacity-mix-amount, 1)*0.5*0.65); - --custom-background-gradient-opacity-chat: calc(var(--custom-background-gradient-chat-opacity-base-light, 0.85)*(0.6 + var(--custom-background-gradient-opacity-mix-amount, 1)*0.4)); - --custom-background-gradient-highest-opacity: calc(var(--custom-background-gradient-highest-opacity-base-light, 0.95)*(0.8 + var(--custom-background-gradient-opacity-mix-amount, 1)*0.2)) -} - -.custom-theme-background .theme-light,.theme-light.custom-theme-background { - --custom-theme-base-color: var(--custom-theme-base-color-light); - --custom-theme-text-color: var(--custom-theme-text-color-light); - --custom-theme-base-color-hsl: var(--custom-theme-base-color-light-hsl) -} - -.custom-theme-background { - --custom-background-gradient-lowest-color: var(--custom-background-gradient-color); - --custom-background-gradient-lower-color: var(--custom-background-gradient-color); - --custom-background-gradient-low-color: var(--custom-background-gradient-color); - --custom-background-gradient-high-color: var(--custom-background-gradient-color); - --custom-background-gradient-higher-color: var(--custom-background-gradient-color); - --custom-background-gradient-chat-color: var(--custom-background-gradient-color); - --custom-background-gradient-app-frame-color: var(--custom-background-gradient-color); - --custom-background-gradient-highest-color: var(--custom-background-gradient-color); - --custom-background-gradient-cover: fixed 0 0/cover,var(--custom-theme-background) fixed 0 0 /cover; - --background-gradient-lowest: linear-gradient(rgb(var(--custom-background-gradient-lowest-color)/var(--custom-background-gradient-lowest-opacity)),rgb(var(--custom-background-gradient-lowest-color)/var(--custom-background-gradient-lowest-opacity))) var(--custom-background-gradient-cover); - --background-gradient-lower: linear-gradient(rgb(var(--custom-background-gradient-lower-color)/var(--custom-background-gradient-lower-opacity)),rgb(var(--custom-background-gradient-lower-color)/var(--custom-background-gradient-lower-opacity))) var(--custom-background-gradient-cover); - --background-gradient-low: linear-gradient(rgb(var(--custom-background-gradient-low-color)/var(--custom-background-gradient-low-opacity)),rgb(var(--custom-background-gradient-low-color)/var(--custom-background-gradient-low-opacity))) var(--custom-background-gradient-cover); - --background-gradient-high: linear-gradient(rgb(var(--custom-background-gradient-high-color)/var(--custom-background-gradient-high-opacity)),rgb(var(--custom-background-gradient-high-color)/var(--custom-background-gradient-high-opacity))) var(--custom-background-gradient-cover); - --background-gradient-higher: linear-gradient(rgb(var(--custom-background-gradient-higher-color)/var(--custom-background-gradient-higher-opacity)),rgb(var(--custom-background-gradient-higher-color)/var(--custom-background-gradient-higher-opacity))) var(--custom-background-gradient-cover); - --background-gradient-chat: linear-gradient(rgb(var(--custom-background-gradient-chat-color)/var(--custom-background-gradient-opacity-chat)),rgb(var(--custom-background-gradient-chat-color)/var(--custom-background-gradient-opacity-chat))) var(--custom-background-gradient-cover); - --background-gradient-app-frame: linear-gradient(rgb(var(--custom-background-gradient-app-frame-color)/var(--custom-background-gradient-opacity-app-frame)),rgb(var(--custom-background-gradient-app-frame-color)/var(--custom-background-gradient-opacity-app-frame))) var(--custom-background-gradient-cover); - --background-gradient-highest: linear-gradient(rgb(var(--custom-background-gradient-highest-color)/var(--custom-background-gradient-highest-opacity)),rgb(var(--custom-background-gradient-highest-color)/var(--custom-background-gradient-highest-opacity))) var(--custom-background-gradient-cover); - --background-gradient-chat-preview: linear-gradient(rgb(var(--custom-background-gradient-chat-color)/var(--custom-background-gradient-opacity-chat)),rgb(var(--custom-background-gradient-chat-color)/var(--custom-background-gradient-opacity-chat))),var(--custom-theme-background) -} - -.disable-adaptive-theme { - --custom-theme-base-color-amount: 0%!important; - --custom-theme-text-color-amount: 0%!important -} - -.reduce-adaptive-theme { - --custom-theme-base-color-amount: 20%!important; - --custom-theme-text-color-amount: 20%!important -} - -@supports (color: color-mix(in lch,red,blue)) { - .custom-theme-background:not(.custom-client-theme) { - --custom-app-border-frame-base:color-mix(in oklab,var(--custom-theme-primary-color) 50%,var(--custom-theme-secondary-color) 50%); - --app-border-frame: color-mix(in oklab,var(--custom-app-border-frame-base) 24%,var(--border-subtle) 100%) - } -} - -.appMount__51fd7,body,html { - height: 100%; - width: 100% -} - -.appMount__51fd7 { - color: var(--background-base-lowest); - display: flex; - flex-direction: column; - overflow: hidden; - position: absolute -} - -.appMount__51fd7,body { - background: var(--background-base-lowest); - text-rendering: optimizeLegibility -} - -.overlay .appMount__51fd7,.overlay body { - background: transparent -} - -::-moz-placeholder { - font-family: var(--font-primary); - text-rendering: optimizeLegibility -} - -::placeholder,body,button,input,select,textarea { - font-family: var(--font-primary); - text-rendering: optimizeLegibility -} - -a,button,div,input,label,select,span,strong,textarea { - outline: 0 -} - -img[alt] { - text-indent: -9999px -} - -@media (-webkit-max-device-pixel-ratio: 1.5) { - .theme-light { - --font-weight-semibold-1x-light-theme:600 - } -} - -:root { - --custom-guild-list-padding: var(--space-md); - --custom-guild-list-width: calc(var(--guildbar-avatar-size) + var(--custom-guild-list-padding)*2); - --custom-guild-sidebar-width: 268px; - --custom-app-sidebar-target-width: calc(var(--custom-guild-sidebar-width) + var(--custom-guild-list-width)); - --custom-rtc-account-height: 44px; - --custom-app-top-bar-height: 32px; - --custom-app-top-bar-item-radius: 6px; - --custom-channel-header-height: calc(var(--guildbar-avatar-size) + var(--space-xs)); - --custom-member-list-width: 264px; - --custom-channel-textarea-text-area-height: 56px; - --custom-chat-aligned-icon-offset: ((var(--chat-avatar-size) - var(--chat-input-icon-size))/2); - --custom-message-margin-horizontal: var(--space-md) -} - -.refresh-title-bar-small { - --custom-app-top-bar-height: 24px -} - -.refresh-title-bar-large { - --custom-app-top-bar-height: 40px -} - -.platform-osx { - --custom-app-top-bar-height: max(calc(100/var(--custom-zoom, 100)*32px),32px) -} - -.platform-osx.refresh-title-bar-small { - --custom-app-top-bar-height: max(calc(100/var(--custom-zoom, 100)*24px),24px) -} - -.platform-osx.refresh-title-bar-large { - --custom-app-top-bar-height: max(calc(100/var(--custom-zoom, 100)*40px),40px) -} - -.refresh-fast-follow-avatars { - --custom-guild-list-padding: min(var(--space-md),var(--space-16)); - --custom-guild-list-width: calc(var(--guildbar-avatar-size) + var(--custom-guild-list-padding)*2); - --custom-channel-header-height: calc(40px + var(--space-xs)) -} - -.density-compact { - --custom-member-list-width: 256px -} - -.density-cozy { - --custom-member-list-width: 268px -} - -.grecaptcha-badge { - visibility: hidden -} - -.enable-forced-colors:not(.platform-web) body:before,html.enable-forced-colors:not(.platform-web) body:before { - border: 6px solid #d3d3d3; - border-top: 0 solid #d3d3d3; - box-sizing: border-box; - content: ""; - display: block; - forced-color-adjust: none; - height: 100%; - inset-inline-start: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 100% -} - -.enable-forced-colors:not(.platform-web) #app-mount__51fd7,.enable-forced-colors:not(.platform-web) .appMount__51fd7,html.enable-forced-colors:not(.platform-web) #app-mount__51fd7,html.enable-forced-colors:not(.platform-web) .appMount__51fd7 { - background-color: Canvas; - box-sizing: border-box; - height: calc(100% - 6px); - margin: 0 6px 6px; - width: calc(100% - 12px) -} - -.enable-forced-colors:not(.platform-web).theme-dark body:before,html.enable-forced-colors:not(.platform-web).theme-dark body:before { - border-color: #d3d3d3 -} - -.enable-forced-colors:not(.platform-web).theme-dark.app-focused body:before,html.enable-forced-colors:not(.platform-web).theme-dark.app-focused body:before { - border-color: gold -} - -.enable-forced-colors:not(.platform-web).theme-light body:before,html.enable-forced-colors:not(.platform-web).theme-light body:before { - border-color: gray -} - -.enable-forced-colors:not(.platform-web).theme-light.app-focused body:before,html.enable-forced-colors:not(.platform-web).theme-light.app-focused body:before { - border-color: #483d8b -} - -.high-contrast-mode,.high-contrast-mode .theme-light,.high-contrast-mode.theme-light,.high-contrast-mode .theme-dark,.high-contrast-mode.theme-dark,.high-contrast-mode .theme-darker,.high-contrast-mode.theme-darker,.high-contrast-mode .theme-midnight,.high-contrast-mode.theme-midnight { - --shadow-border: 0 0 0 1px var(--border-strong)!important -} - -/*# sourceMappingURL=web.cf1abc4e5994931f.css.map*/ diff --git a/package-lock.json b/package-lock.json index abae194..dff3ef6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ }, "apps/android": { "name": "@discord-clone/android", - "version": "1.0.36", + "version": "1.0.40", "dependencies": { "@capacitor/android": "^6.0.0", "@capacitor/app": "^6.0.0", @@ -29,7 +29,7 @@ }, "apps/electron": { "name": "@discord-clone/electron", - "version": "1.0.36", + "version": "1.0.40", "dependencies": { "@discord-clone/shared": "*", "electron-log": "^5.4.3", @@ -46,7 +46,7 @@ }, "apps/web": { "name": "@discord-clone/web", - "version": "1.0.36", + "version": "1.0.40", "dependencies": { "@discord-clone/platform-web": "*", "@discord-clone/shared": "*" @@ -1771,6 +1771,10 @@ "resolved": "apps/android", "link": true }, + "node_modules/@discord-clone/constants": { + "resolved": "packages/constants", + "link": true + }, "node_modules/@discord-clone/electron": { "resolved": "apps/electron", "link": true @@ -1783,6 +1787,10 @@ "resolved": "packages/shared", "link": true }, + "node_modules/@discord-clone/ui": { + "resolved": "packages/ui", + "link": true + }, "node_modules/@discord-clone/web": { "resolved": "apps/web", "link": true @@ -2559,12 +2567,12 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz", - "integrity": "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", "license": "MIT", "dependencies": { - "@floating-ui/utils": "^0.2.10" + "@floating-ui/utils": "^0.2.11" } }, "node_modules/@floating-ui/dom": { @@ -2577,10 +2585,48 @@ "@floating-ui/utils": "^0.2.10" } }, + "node_modules/@floating-ui/react": { + "version": "0.26.28", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", + "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.8", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", + "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.6" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom/node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, "node_modules/@floating-ui/utils": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", - "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", "license": "MIT" }, "node_modules/@gar/promisify": { @@ -3069,6 +3115,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@phosphor-icons/react": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@phosphor-icons/react/-/react-2.1.10.tgz", + "integrity": "sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">= 16.8", + "react-dom": ">= 16.8" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -3737,11 +3796,20 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", @@ -5590,8 +5658,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/data-view-buffer": { "version": "1.0.2", @@ -6777,6 +6844,33 @@ "node": ">=0.4.x" } }, + "node_modules/framer-motion": { + "version": "11.18.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", + "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", + "license": "MIT", + "dependencies": { + "motion-dom": "^11.18.1", + "motion-utils": "^11.18.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -9790,6 +9884,21 @@ "node": ">=10" } }, + "node_modules/motion-dom": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", + "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", + "license": "MIT", + "dependencies": { + "motion-utils": "^11.18.1" + } + }, + "node_modules/motion-utils": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", + "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -12021,6 +12130,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", + "license": "MIT" + }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", @@ -13661,6 +13776,13 @@ "url": "https://github.com/sponsors/wooorm" } }, + "packages/constants": { + "name": "@discord-clone/constants", + "version": "1.0.0", + "devDependencies": { + "typescript": "^5.7.0" + } + }, "packages/platform-web": { "name": "@discord-clone/platform-web", "version": "1.0.0", @@ -13670,16 +13792,22 @@ }, "packages/shared": { "name": "@discord-clone/shared", - "version": "1.0.36", + "version": "1.0.40", "dependencies": { "@convex-dev/presence": "^0.3.0", + "@discord-clone/constants": "*", + "@discord-clone/ui": "*", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", + "@floating-ui/react": "^0.26.0", "@livekit/components-react": "^2.9.17", "@livekit/components-styles": "^1.2.0", + "@phosphor-icons/react": "^2.1.7", "@use-gesture/react": "^10.3.1", + "clsx": "^2.1.1", "convex": "^1.31.2", + "framer-motion": "^11.0.0", "livekit-client": "^2.16.1", "react": "^19.2.0", "react-dom": "^19.2.0", @@ -13690,6 +13818,26 @@ "react-virtuoso": "^4.18.1", "remark-gfm": "^4.0.1", "sql.js": "^1.12.0" + }, + "devDependencies": { + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "typescript": "^5.7.0" + } + }, + "packages/ui": { + "name": "@discord-clone/ui", + "version": "1.0.0", + "dependencies": { + "@floating-ui/react": "^0.26.0", + "@phosphor-icons/react": "^2.1.7", + "clsx": "^2.1.1", + "framer-motion": "^11.0.0", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "typescript": "^5.7.0" } } } diff --git a/packages/constants/package.json b/packages/constants/package.json new file mode 100644 index 0000000..23e9e3d --- /dev/null +++ b/packages/constants/package.json @@ -0,0 +1,10 @@ +{ + "name": "@discord-clone/constants", + "private": true, + "version": "1.0.0", + "type": "module", + "main": "src/index.ts", + "devDependencies": { + "typescript": "^5.7.0" + } +} diff --git a/packages/constants/src/ChannelTypes.ts b/packages/constants/src/ChannelTypes.ts new file mode 100644 index 0000000..5e6a2cc --- /dev/null +++ b/packages/constants/src/ChannelTypes.ts @@ -0,0 +1,9 @@ +export const ChannelTypes = { + TEXT: 'text', + VOICE: 'voice', + CATEGORY: 'category', + DM: 'dm', + GROUP_DM: 'group_dm', +} as const; + +export type ChannelType = (typeof ChannelTypes)[keyof typeof ChannelTypes]; diff --git a/packages/constants/src/PermissionFlags.ts b/packages/constants/src/PermissionFlags.ts new file mode 100644 index 0000000..1813bc6 --- /dev/null +++ b/packages/constants/src/PermissionFlags.ts @@ -0,0 +1,37 @@ +export const PermissionFlags = { + MANAGE_SERVER: 1 << 0, + MANAGE_CHANNELS: 1 << 1, + MANAGE_ROLES: 1 << 2, + MANAGE_MESSAGES: 1 << 3, + KICK_MEMBERS: 1 << 4, + BAN_MEMBERS: 1 << 5, + SEND_MESSAGES: 1 << 6, + SEND_FILES: 1 << 7, + ADD_REACTIONS: 1 << 8, + CONNECT_VOICE: 1 << 9, + SPEAK: 1 << 10, + STREAM: 1 << 11, + MUTE_MEMBERS: 1 << 12, + DEAFEN_MEMBERS: 1 << 13, + MOVE_MEMBERS: 1 << 14, + MENTION_EVERYONE: 1 << 15, + VIEW_CHANNEL: 1 << 16, + CREATE_INVITE: 1 << 17, + CHANGE_NICKNAME: 1 << 18, + MANAGE_NICKNAMES: 1 << 19, + MANAGE_EMOJIS: 1 << 20, + ADMINISTRATOR: 1 << 31, +} as const; + +export type PermissionFlag = (typeof PermissionFlags)[keyof typeof PermissionFlags]; + +export function hasPermission(permissions: number, flag: number): boolean { + if (permissions & PermissionFlags.ADMINISTRATOR) return true; + return (permissions & flag) === flag; +} + +export function combinePermissions(...flags: number[]): number { + return flags.reduce((acc, flag) => acc | flag, 0); +} + +export const MAX_ROLES_PER_SERVER = 25; diff --git a/packages/constants/src/Routes.ts b/packages/constants/src/Routes.ts new file mode 100644 index 0000000..30eb4ae --- /dev/null +++ b/packages/constants/src/Routes.ts @@ -0,0 +1,19 @@ +export const Routes = { + HOME: '/', + LOGIN: '/login', + REGISTER: '/register', + SETUP_ENCRYPTION: '/setup-encryption', + + ME: '/channels/@me', + NOTIFICATIONS: '/notifications', + YOU: '/you', + + dmChannel: (channelId: string) => `/channels/@me/${channelId}`, + serverChannel: (serverId: string, channelId?: string) => + channelId ? `/channels/${serverId}/${channelId}` : `/channels/${serverId}`, + channelMessage: (serverId: string, channelId: string, messageId: string) => + `/channels/${serverId}/${channelId}/${messageId}`, + + isDMRoute: (path: string) => path.startsWith('/channels/@me'), + isServerRoute: (path: string) => path.startsWith('/channels/') && !path.startsWith('/channels/@'), +} as const; diff --git a/packages/constants/src/StatusTypes.ts b/packages/constants/src/StatusTypes.ts new file mode 100644 index 0000000..06f1f06 --- /dev/null +++ b/packages/constants/src/StatusTypes.ts @@ -0,0 +1,17 @@ +export const StatusTypes = { + ONLINE: 'online', + IDLE: 'idle', + DND: 'dnd', + OFFLINE: 'offline', + INVISIBLE: 'invisible', +} as const; + +export type StatusType = (typeof StatusTypes)[keyof typeof StatusTypes]; + +export enum PresenceStatus { + Online = 'online', + Idle = 'idle', + DoNotDisturb = 'dnd', + Offline = 'offline', + Invisible = 'invisible', +} diff --git a/packages/constants/src/index.ts b/packages/constants/src/index.ts new file mode 100644 index 0000000..02b8fa5 --- /dev/null +++ b/packages/constants/src/index.ts @@ -0,0 +1,9 @@ +export { ChannelTypes } from './ChannelTypes'; +export { + PermissionFlags, + hasPermission, + combinePermissions, + MAX_ROLES_PER_SERVER, +} from './PermissionFlags'; +export { StatusTypes, PresenceStatus } from './StatusTypes'; +export { Routes } from './Routes'; diff --git a/packages/constants/tsconfig.json b/packages/constants/tsconfig.json new file mode 100644 index 0000000..564a599 --- /dev/null +++ b/packages/constants/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src"] +} diff --git a/packages/shared/package.json b/packages/shared/package.json index 59e9b8c..bbf8381 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,18 +1,24 @@ { "name": "@discord-clone/shared", "private": true, - "version": "1.0.40", + "version": "1.0.50", "type": "module", - "main": "src/App.jsx", + "main": "src/App.tsx", "dependencies": { "@convex-dev/presence": "^0.3.0", + "@discord-clone/ui": "*", + "@discord-clone/constants": "*", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", + "@floating-ui/react": "^0.26.0", "@livekit/components-react": "^2.9.17", "@livekit/components-styles": "^1.2.0", + "@phosphor-icons/react": "^2.1.7", "@use-gesture/react": "^10.3.1", + "clsx": "^2.1.1", "convex": "^1.31.2", + "framer-motion": "^11.0.0", "livekit-client": "^2.16.1", "react": "^19.2.0", "react-dom": "^19.2.0", @@ -23,5 +29,10 @@ "react-virtuoso": "^4.18.1", "remark-gfm": "^4.0.1", "sql.js": "^1.12.0" + }, + "devDependencies": { + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "typescript": "^5.7.0" } } diff --git a/packages/shared/src/App.css b/packages/shared/src/App.css deleted file mode 100644 index b9d355d..0000000 --- a/packages/shared/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/packages/shared/src/App.jsx b/packages/shared/src/App.jsx deleted file mode 100644 index fc7fecb..0000000 --- a/packages/shared/src/App.jsx +++ /dev/null @@ -1,119 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { Routes, Route, Navigate, useLocation, useNavigate } from 'react-router-dom'; -import Login from './pages/Login'; -import Register from './pages/Register'; -import Recovery from './pages/Recovery'; -import Chat from './pages/Chat'; -import { usePlatform } from './platform'; -import { useSearch } from './contexts/SearchContext'; -import { useSystemBars } from './hooks/useSystemBars'; - -const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000; - -function AuthGuard({ children }) { - const [authState, setAuthState] = useState('loading'); // 'loading' | 'authenticated' | 'unauthenticated' - const location = useLocation(); - const navigate = useNavigate(); - const { session, settings } = usePlatform(); - const searchCtx = useSearch(); - useSystemBars(null); - - useEffect(() => { - let cancelled = false; - - async function restoreSession() { - // Already have keys in sessionStorage — current session is active - if (sessionStorage.getItem('privateKey') && sessionStorage.getItem('signingKey')) { - searchCtx?.initialize(); - if (!cancelled) setAuthState('authenticated'); - return; - } - - // Try restoring from safeStorage - if (session) { - try { - const savedSession = await session.load(); - if (savedSession && savedSession.savedAt && (Date.now() - savedSession.savedAt) < THIRTY_DAYS_MS) { - // Restore to localStorage + sessionStorage - localStorage.setItem('userId', savedSession.userId); - localStorage.setItem('username', savedSession.username); - if (savedSession.publicKey) localStorage.setItem('publicKey', savedSession.publicKey); - sessionStorage.setItem('signingKey', savedSession.signingKey); - sessionStorage.setItem('privateKey', savedSession.privateKey); - if (savedSession.masterKey) sessionStorage.setItem('masterKey', savedSession.masterKey); - if (savedSession.searchDbKey) sessionStorage.setItem('searchDbKey', savedSession.searchDbKey); - searchCtx?.initialize(); - // Restore user preferences from file-based backup into localStorage - if (settings) { - try { - const savedPrefs = await settings.get(`userPrefs_${savedSession.userId}`); - if (savedPrefs && typeof savedPrefs === 'object') { - localStorage.setItem(`userPrefs_${savedSession.userId}`, JSON.stringify(savedPrefs)); - } - } catch {} - } - if (!cancelled) setAuthState('authenticated'); - return; - } - // Expired — clear stale session - if (savedSession && savedSession.savedAt) { - await session.clear(); - } - } catch (err) { - console.error('Session restore failed:', err); - } - } - - if (!cancelled) setAuthState('unauthenticated'); - } - - restoreSession(); - return () => { cancelled = true; }; - }, []); - - useEffect(() => { - if (authState === 'loading') return; - - const isAuthPage = location.pathname === '/' || location.pathname === '/register' || location.pathname === '/recovery'; - const hasSession = sessionStorage.getItem('privateKey') && sessionStorage.getItem('signingKey'); - - if (hasSession && isAuthPage) { - navigate('/chat', { replace: true }); - } else if (!hasSession && !isAuthPage) { - navigate('/', { replace: true }); - } - }, [authState, location.pathname]); - - if (authState === 'loading') { - return ( - <div style={{ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - height: '100vh', - backgroundColor: 'var(--bg-primary, #313338)', - color: 'var(--text-normal, #dbdee1)', - fontSize: '16px', - }}> - Loading... - </div> - ); - } - - return children; -} - -function App() { - return ( - <AuthGuard> - <Routes> - <Route path="/" element={<Login />} /> - <Route path="/register" element={<Register />} /> - <Route path="/recovery" element={<Recovery />} /> - <Route path="/chat" element={<Chat />} /> - </Routes> - </AuthGuard> - ); -} - -export default App; diff --git a/packages/shared/src/App.tsx b/packages/shared/src/App.tsx new file mode 100644 index 0000000..30a8634 --- /dev/null +++ b/packages/shared/src/App.tsx @@ -0,0 +1,181 @@ +import { useEffect, useState, type ReactNode } from 'react'; +import { Routes, Route, Navigate, useLocation, useNavigate } from 'react-router-dom'; +import { IconContext } from '@phosphor-icons/react'; +import { LoginPage } from './components/auth/LoginPage'; +import { RegisterPage } from './components/auth/RegisterPage'; +import { InviteAcceptPage } from './components/auth/InviteAcceptPage'; +import { AppLayout } from './components/layout/AppLayout'; +import { MobileYouPage } from './components/layout/MobileYouPage'; +import { TitleBar } from './components/layout/TitleBar'; +import { ChannelView } from './components/channel/ChannelView'; +import Recovery from './pages/Recovery'; +import { usePlatform } from './platform'; +import { useSearch } from './contexts/SearchContext'; +import { useSystemBars } from './hooks/useSystemBars'; +import './global.css'; + +const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000; + +type AuthState = 'loading' | 'authenticated' | 'unauthenticated'; + +function AuthGuard({ children }: { children: ReactNode }) { + const [authState, setAuthState] = useState<AuthState>('loading'); + const location = useLocation(); + const navigate = useNavigate(); + const { session, settings } = usePlatform(); + const searchCtx = useSearch(); + useSystemBars(null); + + useEffect(() => { + let cancelled = false; + + async function restoreSession() { + if (sessionStorage.getItem('privateKey') && sessionStorage.getItem('signingKey')) { + searchCtx?.initialize(); + if (!cancelled) setAuthState('authenticated'); + return; + } + + if (session) { + try { + const savedSession = await session.load(); + if (savedSession && savedSession.savedAt && Date.now() - savedSession.savedAt < THIRTY_DAYS_MS) { + localStorage.setItem('userId', savedSession.userId); + localStorage.setItem('username', savedSession.username); + if (savedSession.publicKey) localStorage.setItem('publicKey', savedSession.publicKey); + sessionStorage.setItem('signingKey', savedSession.signingKey); + sessionStorage.setItem('privateKey', savedSession.privateKey); + if (savedSession.masterKey) sessionStorage.setItem('masterKey', savedSession.masterKey); + if (savedSession.searchDbKey) sessionStorage.setItem('searchDbKey', savedSession.searchDbKey); + searchCtx?.initialize(); + if (settings) { + try { + const savedPrefs = await settings.get(`userPrefs_${savedSession.userId}`); + if (savedPrefs && typeof savedPrefs === 'object') { + localStorage.setItem(`userPrefs_${savedSession.userId}`, JSON.stringify(savedPrefs)); + } + } catch {} + } + if (!cancelled) setAuthState('authenticated'); + return; + } + if (savedSession?.savedAt) { + await session.clear(); + } + } catch (err) { + console.error('Session restore failed:', err); + } + } + + if (!cancelled) setAuthState('unauthenticated'); + } + + restoreSession(); + return () => { + cancelled = true; + }; + }, []); + + useEffect(() => { + if (authState === 'loading') return; + + const isAuthPage = + location.pathname === '/login' || + location.pathname === '/register' || + location.pathname === '/recovery' || + location.pathname.startsWith('/invite/'); + const hasSession = !!(sessionStorage.getItem('privateKey') && sessionStorage.getItem('signingKey')); + + // Invite accept runs regardless of session — if the viewer is + // already logged in, the InviteAcceptPage itself will forward + // them home after stashing the keys. Don't redirect them away. + const isInvitePage = location.pathname.startsWith('/invite/'); + + if (hasSession && isAuthPage && !isInvitePage) { + navigate('/channels/@me', { replace: true }); + } else if (!hasSession && !isAuthPage) { + navigate('/login', { replace: true }); + } + }, [authState, location.pathname]); + + if (authState === 'loading') { + return ( + <div + style={{ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + height: '100vh', + backgroundColor: 'var(--background-primary)', + color: 'var(--text-primary)', + fontSize: 16, + }} + > + Loading... + </div> + ); + } + + return <>{children}</>; +} + +function DMHomePage() { + return ( + <div + style={{ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + flex: 1, + color: 'var(--text-secondary)', + }} + > + <div style={{ textAlign: 'center' }}> + <h2 style={{ color: 'var(--text-primary)', marginBottom: 8 }}>Welcome to Brycord</h2> + <p>Select a conversation or start a new one</p> + </div> + </div> + ); +} + +function ServerPage() { + return ( + <div + style={{ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + flex: 1, + color: 'var(--text-secondary)', + }} + > + <p>Select a channel to start chatting</p> + </div> + ); +} + +function App() { + return ( + <IconContext.Provider value={{ color: 'currentColor', weight: 'fill' }}> + <TitleBar /> + <AuthGuard> + <Routes> + <Route path="/login" element={<LoginPage />} /> + <Route path="/register" element={<RegisterPage />} /> + <Route path="/invite/:code" element={<InviteAcceptPage />} /> + <Route path="/recovery" element={<Recovery />} /> + <Route path="/channels/*" element={<AppLayout />}> + <Route path="@me" element={<DMHomePage />} /> + <Route path="@me/:channelId" element={<ChannelView />} /> + <Route path="you" element={<MobileYouPage />} /> + <Route path=":serverId" element={<ServerPage />} /> + <Route path=":serverId/:channelId" element={<ChannelView />} /> + </Route> + <Route path="*" element={<Navigate to="/channels/@me" replace />} /> + </Routes> + </AuthGuard> + </IconContext.Provider> + ); +} + +export default App; diff --git a/packages/shared/src/_shims/actions/MatrixActions.ts b/packages/shared/src/_shims/actions/MatrixActions.ts new file mode 100644 index 0000000..15e215b --- /dev/null +++ b/packages/shared/src/_shims/actions/MatrixActions.ts @@ -0,0 +1,13 @@ +/** + * MatrixActions shim — the original wired Matrix SDK event listeners. + * In this project, reactive state comes from Convex useQuery, so these + * are no-ops kept only to satisfy imports. + */ + +export const initializeMatrix = async (): Promise<void> => {}; +export const teardownMatrix = async (): Promise<void> => {}; + +export default { + initializeMatrix, + teardownMatrix, +}; diff --git a/packages/shared/src/_shims/actions/MessageActionCreators.ts b/packages/shared/src/_shims/actions/MessageActionCreators.ts new file mode 100644 index 0000000..029cd16 --- /dev/null +++ b/packages/shared/src/_shims/actions/MessageActionCreators.ts @@ -0,0 +1,29 @@ +/** + * MessageActionCreators shim — stub message action flows. + * Real sends go through Convex; these no-ops keep the UI compiling + * until each call site is rewired. + */ + +import type { Attachment } from '../matrix-client'; + +export interface SendMessageArgs { + channelId: string; + body: string; + formattedBody?: string; + replyToId?: string | null; + attachments?: Attachment[]; + mentions?: string[]; +} + +export const MessageActionCreators = { + sendMessage: async (_args: SendMessageArgs): Promise<string> => '', + editMessage: async (_channelId: string, _messageId: string, _body: string) => {}, + deleteMessage: async (_channelId: string, _messageId: string) => {}, + addReaction: async (_channelId: string, _messageId: string, _key: string) => {}, + removeReaction: async (_channelId: string, _messageId: string, _key: string) => {}, + markRead: async (_channelId: string, _messageId: string) => {}, + flushPendingAttachments: async (_channelId: string) => {}, + createPoll: async (_args: unknown) => {}, +}; + +export default MessageActionCreators; diff --git a/packages/shared/src/_shims/actions/index.ts b/packages/shared/src/_shims/actions/index.ts new file mode 100644 index 0000000..1c03f23 --- /dev/null +++ b/packages/shared/src/_shims/actions/index.ts @@ -0,0 +1,2 @@ +export * from './MessageActionCreators'; +export * from './MatrixActions'; diff --git a/packages/shared/src/_shims/app/data/emoji-shortcuts.json b/packages/shared/src/_shims/app/data/emoji-shortcuts.json new file mode 100644 index 0000000..e687467 --- /dev/null +++ b/packages/shared/src/_shims/app/data/emoji-shortcuts.json @@ -0,0 +1,119 @@ +[ + { + "emoji": "angry", + "shortcuts": [">:(", ">:-(", ">=(", ">=-("] + }, + { + "emoji": "blush", + "shortcuts": [":\")", ":-\")", "=\")", "=-\")"] + }, + { + "emoji": "broken_heart", + "shortcuts": ["</3", "<\\3"] + }, + { + "emoji": "confused", + "shortcuts": [":-\\", ":-/", "=-\\", "=-/"] + }, + { + "emoji": "cry", + "shortcuts": [":'(", ":'-(", ":,(", ":,-(", "='(", "='-(", "=,(", "=,-("] + }, + { + "emoji": "frowning", + "shortcuts": [":(", ":-(", "=(", "=-("] + }, + { + "emoji": "heart", + "shortcuts": ["<3", "♡"] + }, + { + "emoji": "imp", + "shortcuts": ["]:(", "]:-(", "]=(", "]=-("] + }, + { + "emoji": "innocent", + "shortcuts": ["o:)", "O:)", "o:-)", "O:-)", "0:)", "0:-)", "o=)", "O=)", "o=-)", "O=-)", "0=)", "0=-)"] + }, + { + "emoji": "joy", + "shortcuts": [ + ":')", + ":'-)", + ":,)", + ":,-)", + ":'D", + ":'-D", + ":,D", + ":,-D", + "=')", + "='-)", + "=,)", + "=,-)", + "='D", + "='-D", + "=,D", + "=,-D" + ] + }, + { + "emoji": "kissing", + "shortcuts": [":*", ":-*", "=*", "=-*"] + }, + { + "emoji": "laughing", + "shortcuts": ["x-)", "X-)"] + }, + { + "emoji": "neutral_face", + "shortcuts": [":|", ":-|", "=|", "=-|"] + }, + { + "emoji": "open_mouth", + "shortcuts": [":o", ":-o", ":O", ":-O", "=o", "=-o", "=O", "=-O"] + }, + { + "emoji": "rage", + "shortcuts": [":@", ":-@", "=@", "=-@"] + }, + { + "emoji": "smile", + "shortcuts": [":D", ":-D", "=D", "=-D"] + }, + { + "emoji": "smiley", + "shortcuts": [":)", ":-)", "=)", "=-)"] + }, + { + "emoji": "smiling_imp", + "shortcuts": ["]:)", "]:-)", "]=)", "]=-)"] + }, + { + "emoji": "sob", + "shortcuts": [":,'(", ":,'-(", ";(", ";-(", "=,'(", "=,'-("] + }, + { + "emoji": "stuck_out_tongue", + "shortcuts": [":P", ":-P", "=P", "=-P"] + }, + { + "emoji": "sunglasses", + "shortcuts": ["8-)", "B-)"] + }, + { + "emoji": "sweat", + "shortcuts": [",:(", ",:-(", ",=(", ",=-("] + }, + { + "emoji": "sweat_smile", + "shortcuts": [",:)", ",:-)", ",=)", ",=-)"] + }, + { + "emoji": "unamused", + "shortcuts": [":s", ":-S", ":z", ":-Z", ":$", ":-$", "=s", "=-S", "=z", "=-Z", "=$", "=-$"] + }, + { + "emoji": "wink", + "shortcuts": [";)", ";-)"] + } +] diff --git a/packages/shared/src/_shims/app/data/emojis.json b/packages/shared/src/_shims/app/data/emojis.json new file mode 100644 index 0000000..5bfc484 --- /dev/null +++ b/packages/shared/src/_shims/app/data/emojis.json @@ -0,0 +1 @@ +{"people":[{"names":["grinning","grinning_face"],"surrogates":"😀"},{"names":["smiley"],"surrogates":"😃"},{"names":["smile"],"surrogates":"😄"},{"names":["grin"],"surrogates":"😁"},{"names":["laughing","satisfied"],"surrogates":"😆"},{"names":["face_holding_back_tears"],"surrogates":"🥹"},{"names":["sweat_smile"],"surrogates":"😅"},{"names":["joy"],"surrogates":"😂"},{"names":["rofl","rolling_on_the_floor_laughing"],"surrogates":"🤣"},{"names":["smiling_face_with_tear"],"surrogates":"🥲"},{"names":["relaxed","smiling_face"],"surrogates":"☺️"},{"names":["blush"],"surrogates":"😊"},{"names":["innocent"],"surrogates":"😇"},{"names":["slight_smile","slightly_smiling_face"],"surrogates":"🙂"},{"names":["upside_down","upside_down_face"],"surrogates":"🙃"},{"names":["wink","winking_face"],"surrogates":"😉"},{"names":["relieved","relieved_face"],"surrogates":"😌"},{"names":["heart_eyes"],"surrogates":"😍"},{"names":["smiling_face_with_3_hearts"],"surrogates":"🥰"},{"names":["kissing_heart"],"surrogates":"😘"},{"names":["kissing","kissing_face"],"surrogates":"😗"},{"names":["kissing_smiling_eyes"],"surrogates":"😙"},{"names":["kissing_closed_eyes"],"surrogates":"😚"},{"names":["yum"],"surrogates":"😋"},{"names":["stuck_out_tongue"],"surrogates":"😛"},{"names":["stuck_out_tongue_closed_eyes"],"surrogates":"😝"},{"names":["stuck_out_tongue_winking_eye"],"surrogates":"😜"},{"names":["zany_face"],"surrogates":"🤪"},{"names":["face_with_raised_eyebrow"],"surrogates":"🤨"},{"names":["face_with_monocle"],"surrogates":"🧐"},{"names":["nerd","nerd_face"],"surrogates":"🤓"},{"names":["sunglasses"],"surrogates":"😎"},{"names":["disguised_face"],"surrogates":"🥸"},{"names":["star_struck"],"surrogates":"🤩"},{"names":["partying_face"],"surrogates":"🥳"},{"names":["smirk","smirking_face"],"surrogates":"😏"},{"names":["unamused","unamused_face"],"surrogates":"😒"},{"names":["disappointed"],"surrogates":"😞"},{"names":["pensive","pensive_face"],"surrogates":"😔"},{"names":["worried","worried_face"],"surrogates":"😟"},{"names":["confused","confused_face"],"surrogates":"😕"},{"names":["slight_frown","slightly_frowning_face"],"surrogates":"🙁"},{"names":["frowning2","white_frowning_face","frowning_face"],"surrogates":"☹️"},{"names":["persevere"],"surrogates":"😣"},{"names":["confounded"],"surrogates":"😖"},{"names":["tired_face"],"surrogates":"😫"},{"names":["weary","weary_face"],"surrogates":"😩"},{"names":["pleading_face"],"surrogates":"🥺"},{"names":["cry","crying_face"],"surrogates":"😢"},{"names":["sob"],"surrogates":"😭"},{"names":["triumph"],"surrogates":"😤"},{"names":["angry","angry_face"],"surrogates":"😠"},{"names":["rage","pouting_face"],"surrogates":"😡"},{"names":["face_with_symbols_over_mouth"],"surrogates":"🤬"},{"names":["exploding_head"],"surrogates":"🤯"},{"names":["flushed","flushed_face"],"surrogates":"😳"},{"names":["hot_face"],"surrogates":"🥵"},{"names":["cold_face"],"surrogates":"🥶"},{"names":["face_in_clouds"],"surrogates":"😶‍🌫️"},{"names":["scream"],"surrogates":"😱"},{"names":["fearful","fearful_face"],"surrogates":"😨"},{"names":["cold_sweat"],"surrogates":"😰"},{"names":["disappointed_relieved"],"surrogates":"😥"},{"names":["sweat"],"surrogates":"😓"},{"names":["hugging","hugging_face"],"surrogates":"🤗"},{"names":["thinking","thinking_face"],"surrogates":"🤔"},{"names":["face_with_peeking_eye"],"surrogates":"🫣"},{"names":["face_with_hand_over_mouth"],"surrogates":"🤭"},{"names":["face_with_open_eyes_and_hand_over_mouth"],"surrogates":"🫢"},{"names":["saluting_face"],"surrogates":"🫡"},{"names":["shushing_face"],"surrogates":"🤫"},{"names":["melting_face"],"surrogates":"🫠"},{"names":["lying_face","liar"],"surrogates":"🤥"},{"names":["no_mouth"],"surrogates":"😶"},{"names":["dotted_line_face"],"surrogates":"🫥"},{"names":["neutral_face"],"surrogates":"😐"},{"names":["face_with_diagonal_mouth"],"surrogates":"🫤"},{"names":["expressionless"],"surrogates":"😑"},{"names":["shaking_face"],"surrogates":"🫨"},{"names":["grimacing"],"surrogates":"😬"},{"names":["rolling_eyes","face_with_rolling_eyes"],"surrogates":"🙄"},{"names":["hushed","hushed_face"],"surrogates":"😯"},{"names":["frowning"],"surrogates":"😦"},{"names":["anguished"],"surrogates":"😧"},{"names":["open_mouth"],"surrogates":"😮"},{"names":["astonished"],"surrogates":"😲"},{"names":["yawning_face"],"surrogates":"🥱"},{"names":["sleeping","sleeping_face"],"surrogates":"😴"},{"names":["drooling_face","drool"],"surrogates":"🤤"},{"names":["sleepy","sleepy_face"],"surrogates":"😪"},{"names":["face_exhaling"],"surrogates":"😮‍💨"},{"names":["dizzy_face"],"surrogates":"😵"},{"names":["face_with_spiral_eyes"],"surrogates":"😵‍💫"},{"names":["zipper_mouth","zipper_mouth_face"],"surrogates":"🤐"},{"names":["woozy_face"],"surrogates":"🥴"},{"names":["nauseated_face","sick"],"surrogates":"🤢"},{"names":["face_vomiting"],"surrogates":"🤮"},{"names":["sneezing_face","sneeze"],"surrogates":"🤧"},{"names":["mask"],"surrogates":"😷"},{"names":["thermometer_face","face_with_thermometer"],"surrogates":"🤒"},{"names":["head_bandage","face_with_head_bandage"],"surrogates":"🤕"},{"names":["money_mouth","money_mouth_face"],"surrogates":"🤑"},{"names":["cowboy","face_with_cowboy_hat"],"surrogates":"🤠"},{"names":["smiling_imp"],"surrogates":"😈"},{"names":["imp"],"surrogates":"👿"},{"names":["japanese_ogre","ogre"],"surrogates":"👹"},{"names":["japanese_goblin","goblin"],"surrogates":"👺"},{"names":["clown","clown_face"],"surrogates":"🤡"},{"names":["poop","shit","hankey","poo","pile_of_poo"],"surrogates":"💩"},{"names":["ghost"],"surrogates":"👻"},{"names":["skull","skeleton"],"surrogates":"💀"},{"names":["skull_crossbones","skull_and_crossbones"],"surrogates":"☠️"},{"names":["alien"],"surrogates":"👽"},{"names":["space_invader","alien_monster"],"surrogates":"👾"},{"names":["robot","robot_face"],"surrogates":"🤖"},{"names":["jack_o_lantern"],"surrogates":"🎃"},{"names":["smiley_cat","grinning_cat"],"surrogates":"😺"},{"names":["smile_cat"],"surrogates":"😸"},{"names":["joy_cat"],"surrogates":"😹"},{"names":["heart_eyes_cat"],"surrogates":"😻"},{"names":["smirk_cat"],"surrogates":"😼"},{"names":["kissing_cat"],"surrogates":"😽"},{"names":["scream_cat","weary_cat"],"surrogates":"🙀"},{"names":["crying_cat_face","crying_cat"],"surrogates":"😿"},{"names":["pouting_cat"],"surrogates":"😾"},{"names":["handshake","shaking_hands"],"surrogates":"🤝","skins":[{"names":["handshake_tone1"],"surrogates":"🤝🏻"},{"names":["handshake_tone2"],"surrogates":"🤝🏼"},{"names":["handshake_tone3"],"surrogates":"🤝🏽"},{"names":["handshake_tone4"],"surrogates":"🤝🏾"},{"names":["handshake_tone5"],"surrogates":"🤝🏿"},{"names":["handshake_tone6"],"surrogates":"🫱🏻‍🫲🏼"},{"names":["handshake_tone7"],"surrogates":"🫱🏻‍🫲🏽"},{"names":["handshake_tone8"],"surrogates":"🫱🏻‍🫲🏾"},{"names":["handshake_tone9"],"surrogates":"🫱🏻‍🫲🏿"},{"names":["handshake_tone10"],"surrogates":"🫱🏼‍🫲🏻"},{"names":["handshake_tone11"],"surrogates":"🫱🏼‍🫲🏽"},{"names":["handshake_tone12"],"surrogates":"🫱🏼‍🫲🏾"},{"names":["handshake_tone13"],"surrogates":"🫱🏼‍🫲🏿"},{"names":["handshake_tone14"],"surrogates":"🫱🏽‍🫲🏻"},{"names":["handshake_tone15"],"surrogates":"🫱🏽‍🫲🏼"},{"names":["handshake_tone16"],"surrogates":"🫱🏽‍🫲🏾"},{"names":["handshake_tone17"],"surrogates":"🫱🏽‍🫲🏿"},{"names":["handshake_tone18"],"surrogates":"🫱🏾‍🫲🏻"},{"names":["handshake_tone19"],"surrogates":"🫱🏾‍🫲🏼"},{"names":["handshake_tone20"],"surrogates":"🫱🏾‍🫲🏽"},{"names":["handshake_tone21"],"surrogates":"🫱🏾‍🫲🏿"},{"names":["handshake_tone22"],"surrogates":"🫱🏿‍🫲🏻"},{"names":["handshake_tone23"],"surrogates":"🫱🏿‍🫲🏼"},{"names":["handshake_tone24"],"surrogates":"🫱🏿‍🫲🏽"},{"names":["handshake_tone25"],"surrogates":"🫱🏿‍🫲🏾"}]},{"names":["heart_hands"],"surrogates":"🫶","skins":[{"names":["heart_hands_tone1"],"surrogates":"🫶🏻"},{"names":["heart_hands_tone2"],"surrogates":"🫶🏼"},{"names":["heart_hands_tone3"],"surrogates":"🫶🏽"},{"names":["heart_hands_tone4"],"surrogates":"🫶🏾"},{"names":["heart_hands_tone5"],"surrogates":"🫶🏿"}]},{"names":["palms_up_together"],"surrogates":"🤲","skins":[{"names":["palms_up_together_tone1"],"surrogates":"🤲🏻"},{"names":["palms_up_together_tone2"],"surrogates":"🤲🏼"},{"names":["palms_up_together_tone3"],"surrogates":"🤲🏽"},{"names":["palms_up_together_tone4"],"surrogates":"🤲🏾"},{"names":["palms_up_together_tone5"],"surrogates":"🤲🏿"}]},{"names":["open_hands"],"surrogates":"👐","skins":[{"names":["open_hands_tone1"],"surrogates":"👐🏻"},{"names":["open_hands_tone2"],"surrogates":"👐🏼"},{"names":["open_hands_tone3"],"surrogates":"👐🏽"},{"names":["open_hands_tone4"],"surrogates":"👐🏾"},{"names":["open_hands_tone5"],"surrogates":"👐🏿"}]},{"names":["raised_hands","raising_hands"],"surrogates":"🙌","skins":[{"names":["raised_hands_tone1"],"surrogates":"🙌🏻"},{"names":["raised_hands_tone2"],"surrogates":"🙌🏼"},{"names":["raised_hands_tone3"],"surrogates":"🙌🏽"},{"names":["raised_hands_tone4"],"surrogates":"🙌🏾"},{"names":["raised_hands_tone5"],"surrogates":"🙌🏿"}]},{"names":["clap"],"surrogates":"👏","skins":[{"names":["clap_tone1"],"surrogates":"👏🏻"},{"names":["clap_tone2"],"surrogates":"👏🏼"},{"names":["clap_tone3"],"surrogates":"👏🏽"},{"names":["clap_tone4"],"surrogates":"👏🏾"},{"names":["clap_tone5"],"surrogates":"👏🏿"}]},{"names":["thumbsup","+1","thumbup","thumbs_up"],"surrogates":"👍","skins":[{"names":["thumbsup_tone1"],"surrogates":"👍🏻"},{"names":["thumbsup_tone2"],"surrogates":"👍🏼"},{"names":["thumbsup_tone3"],"surrogates":"👍🏽"},{"names":["thumbsup_tone4"],"surrogates":"👍🏾"},{"names":["thumbsup_tone5"],"surrogates":"👍🏿"}]},{"names":["thumbsdown","-1","thumbdown","thumbs_down"],"surrogates":"👎","skins":[{"names":["thumbsdown_tone1"],"surrogates":"👎🏻"},{"names":["thumbsdown_tone2"],"surrogates":"👎🏼"},{"names":["thumbsdown_tone3"],"surrogates":"👎🏽"},{"names":["thumbsdown_tone4"],"surrogates":"👎🏾"},{"names":["thumbsdown_tone5"],"surrogates":"👎🏿"}]},{"names":["punch","oncoming_fist"],"surrogates":"👊","skins":[{"names":["punch_tone1"],"surrogates":"👊🏻"},{"names":["punch_tone2"],"surrogates":"👊🏼"},{"names":["punch_tone3"],"surrogates":"👊🏽"},{"names":["punch_tone4"],"surrogates":"👊🏾"},{"names":["punch_tone5"],"surrogates":"👊🏿"}]},{"names":["fist","raised_fist"],"surrogates":"✊","skins":[{"names":["fist_tone1"],"surrogates":"✊🏻"},{"names":["fist_tone2"],"surrogates":"✊🏼"},{"names":["fist_tone3"],"surrogates":"✊🏽"},{"names":["fist_tone4"],"surrogates":"✊🏾"},{"names":["fist_tone5"],"surrogates":"✊🏿"}]},{"names":["left_facing_fist","left_fist"],"surrogates":"🤛","skins":[{"names":["left_facing_fist_tone1"],"surrogates":"🤛🏻"},{"names":["left_facing_fist_tone2"],"surrogates":"🤛🏼"},{"names":["left_facing_fist_tone3"],"surrogates":"🤛🏽"},{"names":["left_facing_fist_tone4"],"surrogates":"🤛🏾"},{"names":["left_facing_fist_tone5"],"surrogates":"🤛🏿"}]},{"names":["right_facing_fist","right_fist"],"surrogates":"🤜","skins":[{"names":["right_facing_fist_tone1"],"surrogates":"🤜🏻"},{"names":["right_facing_fist_tone2"],"surrogates":"🤜🏼"},{"names":["right_facing_fist_tone3"],"surrogates":"🤜🏽"},{"names":["right_facing_fist_tone4"],"surrogates":"🤜🏾"},{"names":["right_facing_fist_tone5"],"surrogates":"🤜🏿"}]},{"names":["leftwards_pushing_hand"],"surrogates":"🫷","skins":[{"names":["leftwards_pushing_hand_tone1"],"surrogates":"🫷🏻"},{"names":["leftwards_pushing_hand_tone2"],"surrogates":"🫷🏼"},{"names":["leftwards_pushing_hand_tone3"],"surrogates":"🫷🏽"},{"names":["leftwards_pushing_hand_tone4"],"surrogates":"🫷🏾"},{"names":["leftwards_pushing_hand_tone5"],"surrogates":"🫷🏿"}]},{"names":["rightwards_pushing_hand"],"surrogates":"🫸","skins":[{"names":["rightwards_pushing_hand_tone1"],"surrogates":"🫸🏻"},{"names":["rightwards_pushing_hand_tone2"],"surrogates":"🫸🏼"},{"names":["rightwards_pushing_hand_tone3"],"surrogates":"🫸🏽"},{"names":["rightwards_pushing_hand_tone4"],"surrogates":"🫸🏾"},{"names":["rightwards_pushing_hand_tone5"],"surrogates":"🫸🏿"}]},{"names":["fingers_crossed","hand_with_index_and_middle_finger_crossed"],"surrogates":"🤞","skins":[{"names":["fingers_crossed_tone1"],"surrogates":"🤞🏻"},{"names":["fingers_crossed_tone2"],"surrogates":"🤞🏼"},{"names":["fingers_crossed_tone3"],"surrogates":"🤞🏽"},{"names":["fingers_crossed_tone4"],"surrogates":"🤞🏾"},{"names":["fingers_crossed_tone5"],"surrogates":"🤞🏿"}]},{"names":["v","victory_hand"],"surrogates":"✌️","skins":[{"names":["v_tone1"],"surrogates":"✌🏻"},{"names":["v_tone2"],"surrogates":"✌🏼"},{"names":["v_tone3"],"surrogates":"✌🏽"},{"names":["v_tone4"],"surrogates":"✌🏾"},{"names":["v_tone5"],"surrogates":"✌🏿"}]},{"names":["hand_with_index_finger_and_thumb_crossed"],"surrogates":"🫰","skins":[{"names":["hand_with_index_finger_and_thumb_crossed_tone1"],"surrogates":"🫰🏻"},{"names":["hand_with_index_finger_and_thumb_crossed_tone2"],"surrogates":"🫰🏼"},{"names":["hand_with_index_finger_and_thumb_crossed_tone3"],"surrogates":"🫰🏽"},{"names":["hand_with_index_finger_and_thumb_crossed_tone4"],"surrogates":"🫰🏾"},{"names":["hand_with_index_finger_and_thumb_crossed_tone5"],"surrogates":"🫰🏿"}]},{"names":["love_you_gesture"],"surrogates":"🤟","skins":[{"names":["love_you_gesture_tone1"],"surrogates":"🤟🏻"},{"names":["love_you_gesture_tone2"],"surrogates":"🤟🏼"},{"names":["love_you_gesture_tone3"],"surrogates":"🤟🏽"},{"names":["love_you_gesture_tone4"],"surrogates":"🤟🏾"},{"names":["love_you_gesture_tone5"],"surrogates":"🤟🏿"}]},{"names":["metal","sign_of_the_horns"],"surrogates":"🤘","skins":[{"names":["metal_tone1"],"surrogates":"🤘🏻"},{"names":["metal_tone2"],"surrogates":"🤘🏼"},{"names":["metal_tone3"],"surrogates":"🤘🏽"},{"names":["metal_tone4"],"surrogates":"🤘🏾"},{"names":["metal_tone5"],"surrogates":"🤘🏿"}]},{"names":["ok_hand"],"surrogates":"👌","skins":[{"names":["ok_hand_tone1"],"surrogates":"👌🏻"},{"names":["ok_hand_tone2"],"surrogates":"👌🏼"},{"names":["ok_hand_tone3"],"surrogates":"👌🏽"},{"names":["ok_hand_tone4"],"surrogates":"👌🏾"},{"names":["ok_hand_tone5"],"surrogates":"👌🏿"}]},{"names":["pinched_fingers"],"surrogates":"🤌","skins":[{"names":["pinched_fingers_tone1"],"surrogates":"🤌🏻"},{"names":["pinched_fingers_tone2"],"surrogates":"🤌🏼"},{"names":["pinched_fingers_tone3"],"surrogates":"🤌🏽"},{"names":["pinched_fingers_tone4"],"surrogates":"🤌🏾"},{"names":["pinched_fingers_tone5"],"surrogates":"🤌🏿"}]},{"names":["pinching_hand"],"surrogates":"🤏","skins":[{"names":["pinching_hand_tone1"],"surrogates":"🤏🏻"},{"names":["pinching_hand_tone2"],"surrogates":"🤏🏼"},{"names":["pinching_hand_tone3"],"surrogates":"🤏🏽"},{"names":["pinching_hand_tone4"],"surrogates":"🤏🏾"},{"names":["pinching_hand_tone5"],"surrogates":"🤏🏿"}]},{"names":["palm_down_hand"],"surrogates":"🫳","skins":[{"names":["palm_down_hand_tone1"],"surrogates":"🫳🏻"},{"names":["palm_down_hand_tone2"],"surrogates":"🫳🏼"},{"names":["palm_down_hand_tone3"],"surrogates":"🫳🏽"},{"names":["palm_down_hand_tone4"],"surrogates":"🫳🏾"},{"names":["palm_down_hand_tone5"],"surrogates":"🫳🏿"}]},{"names":["palm_up_hand"],"surrogates":"🫴","skins":[{"names":["palm_up_hand_tone1"],"surrogates":"🫴🏻"},{"names":["palm_up_hand_tone2"],"surrogates":"🫴🏼"},{"names":["palm_up_hand_tone3"],"surrogates":"🫴🏽"},{"names":["palm_up_hand_tone4"],"surrogates":"🫴🏾"},{"names":["palm_up_hand_tone5"],"surrogates":"🫴🏿"}]},{"names":["point_left"],"surrogates":"👈","skins":[{"names":["point_left_tone1"],"surrogates":"👈🏻"},{"names":["point_left_tone2"],"surrogates":"👈🏼"},{"names":["point_left_tone3"],"surrogates":"👈🏽"},{"names":["point_left_tone4"],"surrogates":"👈🏾"},{"names":["point_left_tone5"],"surrogates":"👈🏿"}]},{"names":["point_right"],"surrogates":"👉","skins":[{"names":["point_right_tone1"],"surrogates":"👉🏻"},{"names":["point_right_tone2"],"surrogates":"👉🏼"},{"names":["point_right_tone3"],"surrogates":"👉🏽"},{"names":["point_right_tone4"],"surrogates":"👉🏾"},{"names":["point_right_tone5"],"surrogates":"👉🏿"}]},{"names":["point_up_2"],"surrogates":"👆","skins":[{"names":["point_up_2_tone1"],"surrogates":"👆🏻"},{"names":["point_up_2_tone2"],"surrogates":"👆🏼"},{"names":["point_up_2_tone3"],"surrogates":"👆🏽"},{"names":["point_up_2_tone4"],"surrogates":"👆🏾"},{"names":["point_up_2_tone5"],"surrogates":"👆🏿"}]},{"names":["point_down"],"surrogates":"👇","skins":[{"names":["point_down_tone1"],"surrogates":"👇🏻"},{"names":["point_down_tone2"],"surrogates":"👇🏼"},{"names":["point_down_tone3"],"surrogates":"👇🏽"},{"names":["point_down_tone4"],"surrogates":"👇🏾"},{"names":["point_down_tone5"],"surrogates":"👇🏿"}]},{"names":["point_up"],"surrogates":"☝️","skins":[{"names":["point_up_tone1"],"surrogates":"☝🏻"},{"names":["point_up_tone2"],"surrogates":"☝🏼"},{"names":["point_up_tone3"],"surrogates":"☝🏽"},{"names":["point_up_tone4"],"surrogates":"☝🏾"},{"names":["point_up_tone5"],"surrogates":"☝🏿"}]},{"names":["raised_hand"],"surrogates":"✋","skins":[{"names":["raised_hand_tone1"],"surrogates":"✋🏻"},{"names":["raised_hand_tone2"],"surrogates":"✋🏼"},{"names":["raised_hand_tone3"],"surrogates":"✋🏽"},{"names":["raised_hand_tone4"],"surrogates":"✋🏾"},{"names":["raised_hand_tone5"],"surrogates":"✋🏿"}]},{"names":["raised_back_of_hand","back_of_hand"],"surrogates":"🤚","skins":[{"names":["raised_back_of_hand_tone1"],"surrogates":"🤚🏻"},{"names":["raised_back_of_hand_tone2"],"surrogates":"🤚🏼"},{"names":["raised_back_of_hand_tone3"],"surrogates":"🤚🏽"},{"names":["raised_back_of_hand_tone4"],"surrogates":"🤚🏾"},{"names":["raised_back_of_hand_tone5"],"surrogates":"🤚🏿"}]},{"names":["hand_splayed","raised_hand_with_fingers_splayed"],"surrogates":"🖐️","skins":[{"names":["hand_splayed_tone1"],"surrogates":"🖐🏻"},{"names":["hand_splayed_tone2"],"surrogates":"🖐🏼"},{"names":["hand_splayed_tone3"],"surrogates":"🖐🏽"},{"names":["hand_splayed_tone4"],"surrogates":"🖐🏾"},{"names":["hand_splayed_tone5"],"surrogates":"🖐🏿"}]},{"names":["vulcan","raised_hand_with_part_between_middle_and_ring_fingers","vulcan_salute"],"surrogates":"🖖","skins":[{"names":["vulcan_tone1"],"surrogates":"🖖🏻"},{"names":["vulcan_tone2"],"surrogates":"🖖🏼"},{"names":["vulcan_tone3"],"surrogates":"🖖🏽"},{"names":["vulcan_tone4"],"surrogates":"🖖🏾"},{"names":["vulcan_tone5"],"surrogates":"🖖🏿"}]},{"names":["wave","waving_hand"],"surrogates":"👋","skins":[{"names":["wave_tone1"],"surrogates":"👋🏻"},{"names":["wave_tone2"],"surrogates":"👋🏼"},{"names":["wave_tone3"],"surrogates":"👋🏽"},{"names":["wave_tone4"],"surrogates":"👋🏾"},{"names":["wave_tone5"],"surrogates":"👋🏿"}]},{"names":["call_me","call_me_hand"],"surrogates":"🤙","skins":[{"names":["call_me_tone1"],"surrogates":"🤙🏻"},{"names":["call_me_tone2"],"surrogates":"🤙🏼"},{"names":["call_me_tone3"],"surrogates":"🤙🏽"},{"names":["call_me_tone4"],"surrogates":"🤙🏾"},{"names":["call_me_tone5"],"surrogates":"🤙🏿"}]},{"names":["leftwards_hand"],"surrogates":"🫲","skins":[{"names":["leftwards_hand_tone1"],"surrogates":"🫲🏻"},{"names":["leftwards_hand_tone2"],"surrogates":"🫲🏼"},{"names":["leftwards_hand_tone3"],"surrogates":"🫲🏽"},{"names":["leftwards_hand_tone4"],"surrogates":"🫲🏾"},{"names":["leftwards_hand_tone5"],"surrogates":"🫲🏿"}]},{"names":["rightwards_hand"],"surrogates":"🫱","skins":[{"names":["rightwards_hand_tone1"],"surrogates":"🫱🏻"},{"names":["rightwards_hand_tone2"],"surrogates":"🫱🏼"},{"names":["rightwards_hand_tone3"],"surrogates":"🫱🏽"},{"names":["rightwards_hand_tone4"],"surrogates":"🫱🏾"},{"names":["rightwards_hand_tone5"],"surrogates":"🫱🏿"}]},{"names":["muscle","flexed_biceps"],"surrogates":"💪","skins":[{"names":["muscle_tone1"],"surrogates":"💪🏻"},{"names":["muscle_tone2"],"surrogates":"💪🏼"},{"names":["muscle_tone3"],"surrogates":"💪🏽"},{"names":["muscle_tone4"],"surrogates":"💪🏾"},{"names":["muscle_tone5"],"surrogates":"💪🏿"}]},{"names":["mechanical_arm"],"surrogates":"🦾"},{"names":["middle_finger","reversed_hand_with_middle_finger_extended"],"surrogates":"🖕","skins":[{"names":["middle_finger_tone1"],"surrogates":"🖕🏻"},{"names":["middle_finger_tone2"],"surrogates":"🖕🏼"},{"names":["middle_finger_tone3"],"surrogates":"🖕🏽"},{"names":["middle_finger_tone4"],"surrogates":"🖕🏾"},{"names":["middle_finger_tone5"],"surrogates":"🖕🏿"}]},{"names":["writing_hand"],"surrogates":"✍️","skins":[{"names":["writing_hand_tone1"],"surrogates":"✍🏻"},{"names":["writing_hand_tone2"],"surrogates":"✍🏼"},{"names":["writing_hand_tone3"],"surrogates":"✍🏽"},{"names":["writing_hand_tone4"],"surrogates":"✍🏾"},{"names":["writing_hand_tone5"],"surrogates":"✍🏿"}]},{"names":["pray","folded_hands"],"surrogates":"🙏","skins":[{"names":["pray_tone1"],"surrogates":"🙏🏻"},{"names":["pray_tone2"],"surrogates":"🙏🏼"},{"names":["pray_tone3"],"surrogates":"🙏🏽"},{"names":["pray_tone4"],"surrogates":"🙏🏾"},{"names":["pray_tone5"],"surrogates":"🙏🏿"}]},{"names":["index_pointing_at_the_viewer"],"surrogates":"🫵","skins":[{"names":["index_pointing_at_the_viewer_tone1"],"surrogates":"🫵🏻"},{"names":["index_pointing_at_the_viewer_tone2"],"surrogates":"🫵🏼"},{"names":["index_pointing_at_the_viewer_tone3"],"surrogates":"🫵🏽"},{"names":["index_pointing_at_the_viewer_tone4"],"surrogates":"🫵🏾"},{"names":["index_pointing_at_the_viewer_tone5"],"surrogates":"🫵🏿"}]},{"names":["foot"],"surrogates":"🦶","skins":[{"names":["foot_tone1"],"surrogates":"🦶🏻"},{"names":["foot_tone2"],"surrogates":"🦶🏼"},{"names":["foot_tone3"],"surrogates":"🦶🏽"},{"names":["foot_tone4"],"surrogates":"🦶🏾"},{"names":["foot_tone5"],"surrogates":"🦶🏿"}]},{"names":["leg"],"surrogates":"🦵","skins":[{"names":["leg_tone1"],"surrogates":"🦵🏻"},{"names":["leg_tone2"],"surrogates":"🦵🏼"},{"names":["leg_tone3"],"surrogates":"🦵🏽"},{"names":["leg_tone4"],"surrogates":"🦵🏾"},{"names":["leg_tone5"],"surrogates":"🦵🏿"}]},{"names":["mechanical_leg"],"surrogates":"🦿"},{"names":["lipstick"],"surrogates":"💄"},{"names":["kiss","kiss_mark"],"surrogates":"💋"},{"names":["lips","mouth"],"surrogates":"👄"},{"names":["biting_lip"],"surrogates":"🫦"},{"names":["tooth"],"surrogates":"🦷"},{"names":["tongue"],"surrogates":"👅"},{"names":["ear"],"surrogates":"👂","skins":[{"names":["ear_tone1"],"surrogates":"👂🏻"},{"names":["ear_tone2"],"surrogates":"👂🏼"},{"names":["ear_tone3"],"surrogates":"👂🏽"},{"names":["ear_tone4"],"surrogates":"👂🏾"},{"names":["ear_tone5"],"surrogates":"👂🏿"}]},{"names":["ear_with_hearing_aid"],"surrogates":"🦻","skins":[{"names":["ear_with_hearing_aid_tone1"],"surrogates":"🦻🏻"},{"names":["ear_with_hearing_aid_tone2"],"surrogates":"🦻🏼"},{"names":["ear_with_hearing_aid_tone3"],"surrogates":"🦻🏽"},{"names":["ear_with_hearing_aid_tone4"],"surrogates":"🦻🏾"},{"names":["ear_with_hearing_aid_tone5"],"surrogates":"🦻🏿"}]},{"names":["nose"],"surrogates":"👃","skins":[{"names":["nose_tone1"],"surrogates":"👃🏻"},{"names":["nose_tone2"],"surrogates":"👃🏼"},{"names":["nose_tone3"],"surrogates":"👃🏽"},{"names":["nose_tone4"],"surrogates":"👃🏾"},{"names":["nose_tone5"],"surrogates":"👃🏿"}]},{"names":["footprints"],"surrogates":"👣"},{"names":["eye"],"surrogates":"👁️"},{"names":["eyes"],"surrogates":"👀"},{"names":["anatomical_heart"],"surrogates":"🫀"},{"names":["lungs"],"surrogates":"🫁"},{"names":["brain"],"surrogates":"🧠"},{"names":["speaking_head","speaking_head_in_silhouette"],"surrogates":"🗣️"},{"names":["bust_in_silhouette"],"surrogates":"👤"},{"names":["busts_in_silhouette"],"surrogates":"👥"},{"names":["people_hugging"],"surrogates":"🫂"},{"names":["baby"],"surrogates":"👶","skins":[{"names":["baby_tone1"],"surrogates":"👶🏻"},{"names":["baby_tone2"],"surrogates":"👶🏼"},{"names":["baby_tone3"],"surrogates":"👶🏽"},{"names":["baby_tone4"],"surrogates":"👶🏾"},{"names":["baby_tone5"],"surrogates":"👶🏿"}]},{"names":["child"],"surrogates":"🧒","skins":[{"names":["child_tone1"],"surrogates":"🧒🏻"},{"names":["child_tone2"],"surrogates":"🧒🏼"},{"names":["child_tone3"],"surrogates":"🧒🏽"},{"names":["child_tone4"],"surrogates":"🧒🏾"},{"names":["child_tone5"],"surrogates":"🧒🏿"}]},{"names":["girl"],"surrogates":"👧","skins":[{"names":["girl_tone1"],"surrogates":"👧🏻"},{"names":["girl_tone2"],"surrogates":"👧🏼"},{"names":["girl_tone3"],"surrogates":"👧🏽"},{"names":["girl_tone4"],"surrogates":"👧🏾"},{"names":["girl_tone5"],"surrogates":"👧🏿"}]},{"names":["boy"],"surrogates":"👦","skins":[{"names":["boy_tone1"],"surrogates":"👦🏻"},{"names":["boy_tone2"],"surrogates":"👦🏼"},{"names":["boy_tone3"],"surrogates":"👦🏽"},{"names":["boy_tone4"],"surrogates":"👦🏾"},{"names":["boy_tone5"],"surrogates":"👦🏿"}]},{"names":["adult","person"],"surrogates":"🧑","skins":[{"names":["adult_tone1"],"surrogates":"🧑🏻"},{"names":["adult_tone2"],"surrogates":"🧑🏼"},{"names":["adult_tone3"],"surrogates":"🧑🏽"},{"names":["adult_tone4"],"surrogates":"🧑🏾"},{"names":["adult_tone5"],"surrogates":"🧑🏿"}]},{"names":["woman"],"surrogates":"👩","skins":[{"names":["woman_tone1"],"surrogates":"👩🏻"},{"names":["woman_tone2"],"surrogates":"👩🏼"},{"names":["woman_tone3"],"surrogates":"👩🏽"},{"names":["woman_tone4"],"surrogates":"👩🏾"},{"names":["woman_tone5"],"surrogates":"👩🏿"}]},{"names":["man"],"surrogates":"👨","skins":[{"names":["man_tone1"],"surrogates":"👨🏻"},{"names":["man_tone2"],"surrogates":"👨🏼"},{"names":["man_tone3"],"surrogates":"👨🏽"},{"names":["man_tone4"],"surrogates":"👨🏾"},{"names":["man_tone5"],"surrogates":"👨🏿"}]},{"names":["person_curly_hair"],"surrogates":"🧑‍🦱","skins":[{"names":["person_curly_hair_tone1"],"surrogates":"🧑🏻‍🦱"},{"names":["person_curly_hair_tone2"],"surrogates":"🧑🏼‍🦱"},{"names":["person_curly_hair_tone3"],"surrogates":"🧑🏽‍🦱"},{"names":["person_curly_hair_tone4"],"surrogates":"🧑🏾‍🦱"},{"names":["person_curly_hair_tone5"],"surrogates":"🧑🏿‍🦱"}]},{"names":["woman_curly_haired"],"surrogates":"👩‍🦱","skins":[{"names":["woman_curly_haired_tone1"],"surrogates":"👩🏻‍🦱"},{"names":["woman_curly_haired_tone2"],"surrogates":"👩🏼‍🦱"},{"names":["woman_curly_haired_tone3"],"surrogates":"👩🏽‍🦱"},{"names":["woman_curly_haired_tone4"],"surrogates":"👩🏾‍🦱"},{"names":["woman_curly_haired_tone5"],"surrogates":"👩🏿‍🦱"}]},{"names":["man_curly_haired"],"surrogates":"👨‍🦱","skins":[{"names":["man_curly_haired_tone1"],"surrogates":"👨🏻‍🦱"},{"names":["man_curly_haired_tone2"],"surrogates":"👨🏼‍🦱"},{"names":["man_curly_haired_tone3"],"surrogates":"👨🏽‍🦱"},{"names":["man_curly_haired_tone4"],"surrogates":"👨🏾‍🦱"},{"names":["man_curly_haired_tone5"],"surrogates":"👨🏿‍🦱"}]},{"names":["person_red_hair"],"surrogates":"🧑‍🦰","skins":[{"names":["person_red_hair_tone1"],"surrogates":"🧑🏻‍🦰"},{"names":["person_red_hair_tone2"],"surrogates":"🧑🏼‍🦰"},{"names":["person_red_hair_tone3"],"surrogates":"🧑🏽‍🦰"},{"names":["person_red_hair_tone4"],"surrogates":"🧑🏾‍🦰"},{"names":["person_red_hair_tone5"],"surrogates":"🧑🏿‍🦰"}]},{"names":["woman_red_haired"],"surrogates":"👩‍🦰","skins":[{"names":["woman_red_haired_tone1"],"surrogates":"👩🏻‍🦰"},{"names":["woman_red_haired_tone2"],"surrogates":"👩🏼‍🦰"},{"names":["woman_red_haired_tone3"],"surrogates":"👩🏽‍🦰"},{"names":["woman_red_haired_tone4"],"surrogates":"👩🏾‍🦰"},{"names":["woman_red_haired_tone5"],"surrogates":"👩🏿‍🦰"}]},{"names":["man_red_haired","man_red_hair"],"surrogates":"👨‍🦰","skins":[{"names":["man_red_haired_tone1"],"surrogates":"👨🏻‍🦰"},{"names":["man_red_haired_tone2"],"surrogates":"👨🏼‍🦰"},{"names":["man_red_haired_tone3"],"surrogates":"👨🏽‍🦰"},{"names":["man_red_haired_tone4"],"surrogates":"👨🏾‍🦰"},{"names":["man_red_haired_tone5"],"surrogates":"👨🏿‍🦰"}]},{"names":["blond_haired_person","person_with_blond_hair"],"surrogates":"👱","skins":[{"names":["blond_haired_person_tone1"],"surrogates":"👱🏻"},{"names":["blond_haired_person_tone2"],"surrogates":"👱🏼"},{"names":["blond_haired_person_tone3"],"surrogates":"👱🏽"},{"names":["blond_haired_person_tone4"],"surrogates":"👱🏾"},{"names":["blond_haired_person_tone5"],"surrogates":"👱🏿"}]},{"names":["blond_haired_woman"],"surrogates":"👱‍♀️","skins":[{"names":["blond_haired_woman_tone1"],"surrogates":"👱🏻‍♀️"},{"names":["blond_haired_woman_tone2"],"surrogates":"👱🏼‍♀️"},{"names":["blond_haired_woman_tone3"],"surrogates":"👱🏽‍♀️"},{"names":["blond_haired_woman_tone4"],"surrogates":"👱🏾‍♀️"},{"names":["blond_haired_woman_tone5"],"surrogates":"👱🏿‍♀️"}]},{"names":["blond_haired_man"],"surrogates":"👱‍♂️","skins":[{"names":["blond_haired_man_tone1"],"surrogates":"👱🏻‍♂️"},{"names":["blond_haired_man_tone2"],"surrogates":"👱🏼‍♂️"},{"names":["blond_haired_man_tone3"],"surrogates":"👱🏽‍♂️"},{"names":["blond_haired_man_tone4"],"surrogates":"👱🏾‍♂️"},{"names":["blond_haired_man_tone5"],"surrogates":"👱🏿‍♂️"}]},{"names":["person_white_hair"],"surrogates":"🧑‍🦳","skins":[{"names":["person_white_hair_tone1"],"surrogates":"🧑🏻‍🦳"},{"names":["person_white_hair_tone2"],"surrogates":"🧑🏼‍🦳"},{"names":["person_white_hair_tone3"],"surrogates":"🧑🏽‍🦳"},{"names":["person_white_hair_tone4"],"surrogates":"🧑🏾‍🦳"},{"names":["person_white_hair_tone5"],"surrogates":"🧑🏿‍🦳"}]},{"names":["woman_white_haired"],"surrogates":"👩‍🦳","skins":[{"names":["woman_white_haired_tone1"],"surrogates":"👩🏻‍🦳"},{"names":["woman_white_haired_tone2"],"surrogates":"👩🏼‍🦳"},{"names":["woman_white_haired_tone3"],"surrogates":"👩🏽‍🦳"},{"names":["woman_white_haired_tone4"],"surrogates":"👩🏾‍🦳"},{"names":["woman_white_haired_tone5"],"surrogates":"👩🏿‍🦳"}]},{"names":["man_white_haired"],"surrogates":"👨‍🦳","skins":[{"names":["man_white_haired_tone1"],"surrogates":"👨🏻‍🦳"},{"names":["man_white_haired_tone2"],"surrogates":"👨🏼‍🦳"},{"names":["man_white_haired_tone3"],"surrogates":"👨🏽‍🦳"},{"names":["man_white_haired_tone4"],"surrogates":"👨🏾‍🦳"},{"names":["man_white_haired_tone5"],"surrogates":"👨🏿‍🦳"}]},{"names":["person_bald"],"surrogates":"🧑‍🦲","skins":[{"names":["person_bald_tone1"],"surrogates":"🧑🏻‍🦲"},{"names":["person_bald_tone2"],"surrogates":"🧑🏼‍🦲"},{"names":["person_bald_tone3"],"surrogates":"🧑🏽‍🦲"},{"names":["person_bald_tone4"],"surrogates":"🧑🏾‍🦲"},{"names":["person_bald_tone5"],"surrogates":"🧑🏿‍🦲"}]},{"names":["woman_bald"],"surrogates":"👩‍🦲","skins":[{"names":["woman_bald_tone1"],"surrogates":"👩🏻‍🦲"},{"names":["woman_bald_tone2"],"surrogates":"👩🏼‍🦲"},{"names":["woman_bald_tone3"],"surrogates":"👩🏽‍🦲"},{"names":["woman_bald_tone4"],"surrogates":"👩🏾‍🦲"},{"names":["woman_bald_tone5"],"surrogates":"👩🏿‍🦲"}]},{"names":["man_bald"],"surrogates":"👨‍🦲","skins":[{"names":["man_bald_tone1"],"surrogates":"👨🏻‍🦲"},{"names":["man_bald_tone2"],"surrogates":"👨🏼‍🦲"},{"names":["man_bald_tone3"],"surrogates":"👨🏽‍🦲"},{"names":["man_bald_tone4"],"surrogates":"👨🏾‍🦲"},{"names":["man_bald_tone5"],"surrogates":"👨🏿‍🦲"}]},{"names":["bearded_person","person_beard"],"surrogates":"🧔","skins":[{"names":["bearded_person_tone1"],"surrogates":"🧔🏻"},{"names":["bearded_person_tone2"],"surrogates":"🧔🏼"},{"names":["bearded_person_tone3"],"surrogates":"🧔🏽"},{"names":["bearded_person_tone4"],"surrogates":"🧔🏾"},{"names":["bearded_person_tone5"],"surrogates":"🧔🏿"}]},{"names":["woman_beard"],"surrogates":"🧔‍♀️","skins":[{"names":["woman_beard_tone1"],"surrogates":"🧔🏻‍♀️"},{"names":["woman_beard_tone2"],"surrogates":"🧔🏼‍♀️"},{"names":["woman_beard_tone3"],"surrogates":"🧔🏽‍♀️"},{"names":["woman_beard_tone4"],"surrogates":"🧔🏾‍♀️"},{"names":["woman_beard_tone5"],"surrogates":"🧔🏿‍♀️"}]},{"names":["man_beard"],"surrogates":"🧔‍♂️","skins":[{"names":["man_beard_tone1"],"surrogates":"🧔🏻‍♂️"},{"names":["man_beard_tone2"],"surrogates":"🧔🏼‍♂️"},{"names":["man_beard_tone3"],"surrogates":"🧔🏽‍♂️"},{"names":["man_beard_tone4"],"surrogates":"🧔🏾‍♂️"},{"names":["man_beard_tone5"],"surrogates":"🧔🏿‍♂️"}]},{"names":["older_adult","older_person"],"surrogates":"🧓","skins":[{"names":["older_adult_tone1"],"surrogates":"🧓🏻"},{"names":["older_adult_tone2"],"surrogates":"🧓🏼"},{"names":["older_adult_tone3"],"surrogates":"🧓🏽"},{"names":["older_adult_tone4"],"surrogates":"🧓🏾"},{"names":["older_adult_tone5"],"surrogates":"🧓🏿"}]},{"names":["older_woman","grandma","old_woman"],"surrogates":"👵","skins":[{"names":["older_woman_tone1"],"surrogates":"👵🏻"},{"names":["older_woman_tone2"],"surrogates":"👵🏼"},{"names":["older_woman_tone3"],"surrogates":"👵🏽"},{"names":["older_woman_tone4"],"surrogates":"👵🏾"},{"names":["older_woman_tone5"],"surrogates":"👵🏿"}]},{"names":["older_man","old_man"],"surrogates":"👴","skins":[{"names":["older_man_tone1"],"surrogates":"👴🏻"},{"names":["older_man_tone2"],"surrogates":"👴🏼"},{"names":["older_man_tone3"],"surrogates":"👴🏽"},{"names":["older_man_tone4"],"surrogates":"👴🏾"},{"names":["older_man_tone5"],"surrogates":"👴🏿"}]},{"names":["man_with_chinese_cap","man_with_gua_pi_mao"],"surrogates":"👲","skins":[{"names":["man_with_chinese_cap_tone1"],"surrogates":"👲🏻"},{"names":["man_with_chinese_cap_tone2"],"surrogates":"👲🏼"},{"names":["man_with_chinese_cap_tone3"],"surrogates":"👲🏽"},{"names":["man_with_chinese_cap_tone4"],"surrogates":"👲🏾"},{"names":["man_with_chinese_cap_tone5"],"surrogates":"👲🏿"}]},{"names":["person_wearing_turban","man_with_turban"],"surrogates":"👳","skins":[{"names":["person_wearing_turban_tone1"],"surrogates":"👳🏻"},{"names":["person_wearing_turban_tone2"],"surrogates":"👳🏼"},{"names":["person_wearing_turban_tone3"],"surrogates":"👳🏽"},{"names":["person_wearing_turban_tone4"],"surrogates":"👳🏾"},{"names":["person_wearing_turban_tone5"],"surrogates":"👳🏿"}]},{"names":["woman_wearing_turban"],"surrogates":"👳‍♀️","skins":[{"names":["woman_wearing_turban_tone1"],"surrogates":"👳🏻‍♀️"},{"names":["woman_wearing_turban_tone2"],"surrogates":"👳🏼‍♀️"},{"names":["woman_wearing_turban_tone3"],"surrogates":"👳🏽‍♀️"},{"names":["woman_wearing_turban_tone4"],"surrogates":"👳🏾‍♀️"},{"names":["woman_wearing_turban_tone5"],"surrogates":"👳🏿‍♀️"}]},{"names":["man_wearing_turban"],"surrogates":"👳‍♂️","skins":[{"names":["man_wearing_turban_tone1"],"surrogates":"👳🏻‍♂️"},{"names":["man_wearing_turban_tone2"],"surrogates":"👳🏼‍♂️"},{"names":["man_wearing_turban_tone3"],"surrogates":"👳🏽‍♂️"},{"names":["man_wearing_turban_tone4"],"surrogates":"👳🏾‍♂️"},{"names":["man_wearing_turban_tone5"],"surrogates":"👳🏿‍♂️"}]},{"names":["woman_with_headscarf"],"surrogates":"🧕","skins":[{"names":["woman_with_headscarf_tone1"],"surrogates":"🧕🏻"},{"names":["woman_with_headscarf_tone2"],"surrogates":"🧕🏼"},{"names":["woman_with_headscarf_tone3"],"surrogates":"🧕🏽"},{"names":["woman_with_headscarf_tone4"],"surrogates":"🧕🏾"},{"names":["woman_with_headscarf_tone5"],"surrogates":"🧕🏿"}]},{"names":["police_officer","cop"],"surrogates":"👮","skins":[{"names":["police_officer_tone1"],"surrogates":"👮🏻"},{"names":["police_officer_tone2"],"surrogates":"👮🏼"},{"names":["police_officer_tone3"],"surrogates":"👮🏽"},{"names":["police_officer_tone4"],"surrogates":"👮🏾"},{"names":["police_officer_tone5"],"surrogates":"👮🏿"}]},{"names":["woman_police_officer"],"surrogates":"👮‍♀️","skins":[{"names":["woman_police_officer_tone1"],"surrogates":"👮🏻‍♀️"},{"names":["woman_police_officer_tone2"],"surrogates":"👮🏼‍♀️"},{"names":["woman_police_officer_tone3"],"surrogates":"👮🏽‍♀️"},{"names":["woman_police_officer_tone4"],"surrogates":"👮🏾‍♀️"},{"names":["woman_police_officer_tone5"],"surrogates":"👮🏿‍♀️"}]},{"names":["man_police_officer"],"surrogates":"👮‍♂️","skins":[{"names":["man_police_officer_tone1"],"surrogates":"👮🏻‍♂️"},{"names":["man_police_officer_tone2"],"surrogates":"👮🏼‍♂️"},{"names":["man_police_officer_tone3"],"surrogates":"👮🏽‍♂️"},{"names":["man_police_officer_tone4"],"surrogates":"👮🏾‍♂️"},{"names":["man_police_officer_tone5"],"surrogates":"👮🏿‍♂️"}]},{"names":["construction_worker"],"surrogates":"👷","skins":[{"names":["construction_worker_tone1"],"surrogates":"👷🏻"},{"names":["construction_worker_tone2"],"surrogates":"👷🏼"},{"names":["construction_worker_tone3"],"surrogates":"👷🏽"},{"names":["construction_worker_tone4"],"surrogates":"👷🏾"},{"names":["construction_worker_tone5"],"surrogates":"👷🏿"}]},{"names":["woman_construction_worker"],"surrogates":"👷‍♀️","skins":[{"names":["woman_construction_worker_tone1"],"surrogates":"👷🏻‍♀️"},{"names":["woman_construction_worker_tone2"],"surrogates":"👷🏼‍♀️"},{"names":["woman_construction_worker_tone3"],"surrogates":"👷🏽‍♀️"},{"names":["woman_construction_worker_tone4"],"surrogates":"👷🏾‍♀️"},{"names":["woman_construction_worker_tone5"],"surrogates":"👷🏿‍♀️"}]},{"names":["man_construction_worker"],"surrogates":"👷‍♂️","skins":[{"names":["man_construction_worker_tone1"],"surrogates":"👷🏻‍♂️"},{"names":["man_construction_worker_tone2"],"surrogates":"👷🏼‍♂️"},{"names":["man_construction_worker_tone3"],"surrogates":"👷🏽‍♂️"},{"names":["man_construction_worker_tone4"],"surrogates":"👷🏾‍♂️"},{"names":["man_construction_worker_tone5"],"surrogates":"👷🏿‍♂️"}]},{"names":["guard","guardsman"],"surrogates":"💂","skins":[{"names":["guard_tone1"],"surrogates":"💂🏻"},{"names":["guard_tone2"],"surrogates":"💂🏼"},{"names":["guard_tone3"],"surrogates":"💂🏽"},{"names":["guard_tone4"],"surrogates":"💂🏾"},{"names":["guard_tone5"],"surrogates":"💂🏿"}]},{"names":["woman_guard"],"surrogates":"💂‍♀️","skins":[{"names":["woman_guard_tone1"],"surrogates":"💂🏻‍♀️"},{"names":["woman_guard_tone2"],"surrogates":"💂🏼‍♀️"},{"names":["woman_guard_tone3"],"surrogates":"💂🏽‍♀️"},{"names":["woman_guard_tone4"],"surrogates":"💂🏾‍♀️"},{"names":["woman_guard_tone5"],"surrogates":"💂🏿‍♀️"}]},{"names":["man_guard"],"surrogates":"💂‍♂️","skins":[{"names":["man_guard_tone1"],"surrogates":"💂🏻‍♂️"},{"names":["man_guard_tone2"],"surrogates":"💂🏼‍♂️"},{"names":["man_guard_tone3"],"surrogates":"💂🏽‍♂️"},{"names":["man_guard_tone4"],"surrogates":"💂🏾‍♂️"},{"names":["man_guard_tone5"],"surrogates":"💂🏿‍♂️"}]},{"names":["detective","spy","sleuth_or_spy"],"surrogates":"🕵️","skins":[{"names":["detective_tone1"],"surrogates":"🕵🏻"},{"names":["detective_tone2"],"surrogates":"🕵🏼"},{"names":["detective_tone3"],"surrogates":"🕵🏽"},{"names":["detective_tone4"],"surrogates":"🕵🏾"},{"names":["detective_tone5"],"surrogates":"🕵🏿"}]},{"names":["woman_detective"],"surrogates":"🕵️‍♀️","skins":[{"names":["woman_detective_tone1"],"surrogates":"🕵🏻‍♀️"},{"names":["woman_detective_tone2"],"surrogates":"🕵🏼‍♀️"},{"names":["woman_detective_tone3"],"surrogates":"🕵🏽‍♀️"},{"names":["woman_detective_tone4"],"surrogates":"🕵🏾‍♀️"},{"names":["woman_detective_tone5"],"surrogates":"🕵🏿‍♀️"}]},{"names":["man_detective"],"surrogates":"🕵️‍♂️","skins":[{"names":["man_detective_tone1"],"surrogates":"🕵🏻‍♂️"},{"names":["man_detective_tone2"],"surrogates":"🕵🏼‍♂️"},{"names":["man_detective_tone3"],"surrogates":"🕵🏽‍♂️"},{"names":["man_detective_tone4"],"surrogates":"🕵🏾‍♂️"},{"names":["man_detective_tone5"],"surrogates":"🕵🏿‍♂️"}]},{"names":["health_worker"],"surrogates":"🧑‍⚕️","skins":[{"names":["health_worker_tone1"],"surrogates":"🧑🏻‍⚕️"},{"names":["health_worker_tone2"],"surrogates":"🧑🏼‍⚕️"},{"names":["health_worker_tone3"],"surrogates":"🧑🏽‍⚕️"},{"names":["health_worker_tone4"],"surrogates":"🧑🏾‍⚕️"},{"names":["health_worker_tone5"],"surrogates":"🧑🏿‍⚕️"}]},{"names":["woman_health_worker"],"surrogates":"👩‍⚕️","skins":[{"names":["woman_health_worker_tone1"],"surrogates":"👩🏻‍⚕️"},{"names":["woman_health_worker_tone2"],"surrogates":"👩🏼‍⚕️"},{"names":["woman_health_worker_tone3"],"surrogates":"👩🏽‍⚕️"},{"names":["woman_health_worker_tone4"],"surrogates":"👩🏾‍⚕️"},{"names":["woman_health_worker_tone5"],"surrogates":"👩🏿‍⚕️"}]},{"names":["man_health_worker"],"surrogates":"👨‍⚕️","skins":[{"names":["man_health_worker_tone1"],"surrogates":"👨🏻‍⚕️"},{"names":["man_health_worker_tone2"],"surrogates":"👨🏼‍⚕️"},{"names":["man_health_worker_tone3"],"surrogates":"👨🏽‍⚕️"},{"names":["man_health_worker_tone4"],"surrogates":"👨🏾‍⚕️"},{"names":["man_health_worker_tone5"],"surrogates":"👨🏿‍⚕️"}]},{"names":["farmer"],"surrogates":"🧑‍🌾","skins":[{"names":["farmer_tone1"],"surrogates":"🧑🏻‍🌾"},{"names":["farmer_tone2"],"surrogates":"🧑🏼‍🌾"},{"names":["farmer_tone3"],"surrogates":"🧑🏽‍🌾"},{"names":["farmer_tone4"],"surrogates":"🧑🏾‍🌾"},{"names":["farmer_tone5"],"surrogates":"🧑🏿‍🌾"}]},{"names":["woman_farmer"],"surrogates":"👩‍🌾","skins":[{"names":["woman_farmer_tone1"],"surrogates":"👩🏻‍🌾"},{"names":["woman_farmer_tone2"],"surrogates":"👩🏼‍🌾"},{"names":["woman_farmer_tone3"],"surrogates":"👩🏽‍🌾"},{"names":["woman_farmer_tone4"],"surrogates":"👩🏾‍🌾"},{"names":["woman_farmer_tone5"],"surrogates":"👩🏿‍🌾"}]},{"names":["man_farmer"],"surrogates":"👨‍🌾","skins":[{"names":["man_farmer_tone1"],"surrogates":"👨🏻‍🌾"},{"names":["man_farmer_tone2"],"surrogates":"👨🏼‍🌾"},{"names":["man_farmer_tone3"],"surrogates":"👨🏽‍🌾"},{"names":["man_farmer_tone4"],"surrogates":"👨🏾‍🌾"},{"names":["man_farmer_tone5"],"surrogates":"👨🏿‍🌾"}]},{"names":["cook"],"surrogates":"🧑‍🍳","skins":[{"names":["cook_tone1"],"surrogates":"🧑🏻‍🍳"},{"names":["cook_tone2"],"surrogates":"🧑🏼‍🍳"},{"names":["cook_tone3"],"surrogates":"🧑🏽‍🍳"},{"names":["cook_tone4"],"surrogates":"🧑🏾‍🍳"},{"names":["cook_tone5"],"surrogates":"🧑🏿‍🍳"}]},{"names":["woman_cook"],"surrogates":"👩‍🍳","skins":[{"names":["woman_cook_tone1"],"surrogates":"👩🏻‍🍳"},{"names":["woman_cook_tone2"],"surrogates":"👩🏼‍🍳"},{"names":["woman_cook_tone3"],"surrogates":"👩🏽‍🍳"},{"names":["woman_cook_tone4"],"surrogates":"👩🏾‍🍳"},{"names":["woman_cook_tone5"],"surrogates":"👩🏿‍🍳"}]},{"names":["man_cook"],"surrogates":"👨‍🍳","skins":[{"names":["man_cook_tone1"],"surrogates":"👨🏻‍🍳"},{"names":["man_cook_tone2"],"surrogates":"👨🏼‍🍳"},{"names":["man_cook_tone3"],"surrogates":"👨🏽‍🍳"},{"names":["man_cook_tone4"],"surrogates":"👨🏾‍🍳"},{"names":["man_cook_tone5"],"surrogates":"👨🏿‍🍳"}]},{"names":["student"],"surrogates":"🧑‍🎓","skins":[{"names":["student_tone1"],"surrogates":"🧑🏻‍🎓"},{"names":["student_tone2"],"surrogates":"🧑🏼‍🎓"},{"names":["student_tone3"],"surrogates":"🧑🏽‍🎓"},{"names":["student_tone4"],"surrogates":"🧑🏾‍🎓"},{"names":["student_tone5"],"surrogates":"🧑🏿‍🎓"}]},{"names":["woman_student"],"surrogates":"👩‍🎓","skins":[{"names":["woman_student_tone1"],"surrogates":"👩🏻‍🎓"},{"names":["woman_student_tone2"],"surrogates":"👩🏼‍🎓"},{"names":["woman_student_tone3"],"surrogates":"👩🏽‍🎓"},{"names":["woman_student_tone4"],"surrogates":"👩🏾‍🎓"},{"names":["woman_student_tone5"],"surrogates":"👩🏿‍🎓"}]},{"names":["man_student"],"surrogates":"👨‍🎓","skins":[{"names":["man_student_tone1"],"surrogates":"👨🏻‍🎓"},{"names":["man_student_tone2"],"surrogates":"👨🏼‍🎓"},{"names":["man_student_tone3"],"surrogates":"👨🏽‍🎓"},{"names":["man_student_tone4"],"surrogates":"👨🏾‍🎓"},{"names":["man_student_tone5"],"surrogates":"👨🏿‍🎓"}]},{"names":["singer"],"surrogates":"🧑‍🎤","skins":[{"names":["singer_tone1"],"surrogates":"🧑🏻‍🎤"},{"names":["singer_tone2"],"surrogates":"🧑🏼‍🎤"},{"names":["singer_tone3"],"surrogates":"🧑🏽‍🎤"},{"names":["singer_tone4"],"surrogates":"🧑🏾‍🎤"},{"names":["singer_tone5"],"surrogates":"🧑🏿‍🎤"}]},{"names":["woman_singer"],"surrogates":"👩‍🎤","skins":[{"names":["woman_singer_tone1"],"surrogates":"👩🏻‍🎤"},{"names":["woman_singer_tone2"],"surrogates":"👩🏼‍🎤"},{"names":["woman_singer_tone3"],"surrogates":"👩🏽‍🎤"},{"names":["woman_singer_tone4"],"surrogates":"👩🏾‍🎤"},{"names":["woman_singer_tone5"],"surrogates":"👩🏿‍🎤"}]},{"names":["man_singer"],"surrogates":"👨‍🎤","skins":[{"names":["man_singer_tone1"],"surrogates":"👨🏻‍🎤"},{"names":["man_singer_tone2"],"surrogates":"👨🏼‍🎤"},{"names":["man_singer_tone3"],"surrogates":"👨🏽‍🎤"},{"names":["man_singer_tone4"],"surrogates":"👨🏾‍🎤"},{"names":["man_singer_tone5"],"surrogates":"👨🏿‍🎤"}]},{"names":["teacher"],"surrogates":"🧑‍🏫","skins":[{"names":["teacher_tone1"],"surrogates":"🧑🏻‍🏫"},{"names":["teacher_tone2"],"surrogates":"🧑🏼‍🏫"},{"names":["teacher_tone3"],"surrogates":"🧑🏽‍🏫"},{"names":["teacher_tone4"],"surrogates":"🧑🏾‍🏫"},{"names":["teacher_tone5"],"surrogates":"🧑🏿‍🏫"}]},{"names":["woman_teacher"],"surrogates":"👩‍🏫","skins":[{"names":["woman_teacher_tone1"],"surrogates":"👩🏻‍🏫"},{"names":["woman_teacher_tone2"],"surrogates":"👩🏼‍🏫"},{"names":["woman_teacher_tone3"],"surrogates":"👩🏽‍🏫"},{"names":["woman_teacher_tone4"],"surrogates":"👩🏾‍🏫"},{"names":["woman_teacher_tone5"],"surrogates":"👩🏿‍🏫"}]},{"names":["man_teacher"],"surrogates":"👨‍🏫","skins":[{"names":["man_teacher_tone1"],"surrogates":"👨🏻‍🏫"},{"names":["man_teacher_tone2"],"surrogates":"👨🏼‍🏫"},{"names":["man_teacher_tone3"],"surrogates":"👨🏽‍🏫"},{"names":["man_teacher_tone4"],"surrogates":"👨🏾‍🏫"},{"names":["man_teacher_tone5"],"surrogates":"👨🏿‍🏫"}]},{"names":["factory_worker"],"surrogates":"🧑‍🏭","skins":[{"names":["factory_worker_tone1"],"surrogates":"🧑🏻‍🏭"},{"names":["factory_worker_tone2"],"surrogates":"🧑🏼‍🏭"},{"names":["factory_worker_tone3"],"surrogates":"🧑🏽‍🏭"},{"names":["factory_worker_tone4"],"surrogates":"🧑🏾‍🏭"},{"names":["factory_worker_tone5"],"surrogates":"🧑🏿‍🏭"}]},{"names":["woman_factory_worker"],"surrogates":"👩‍🏭","skins":[{"names":["woman_factory_worker_tone1"],"surrogates":"👩🏻‍🏭"},{"names":["woman_factory_worker_tone2"],"surrogates":"👩🏼‍🏭"},{"names":["woman_factory_worker_tone3"],"surrogates":"👩🏽‍🏭"},{"names":["woman_factory_worker_tone4"],"surrogates":"👩🏾‍🏭"},{"names":["woman_factory_worker_tone5"],"surrogates":"👩🏿‍🏭"}]},{"names":["man_factory_worker"],"surrogates":"👨‍🏭","skins":[{"names":["man_factory_worker_tone1"],"surrogates":"👨🏻‍🏭"},{"names":["man_factory_worker_tone2"],"surrogates":"👨🏼‍🏭"},{"names":["man_factory_worker_tone3"],"surrogates":"👨🏽‍🏭"},{"names":["man_factory_worker_tone4"],"surrogates":"👨🏾‍🏭"},{"names":["man_factory_worker_tone5"],"surrogates":"👨🏿‍🏭"}]},{"names":["technologist"],"surrogates":"🧑‍💻","skins":[{"names":["technologist_tone1"],"surrogates":"🧑🏻‍💻"},{"names":["technologist_tone2"],"surrogates":"🧑🏼‍💻"},{"names":["technologist_tone3"],"surrogates":"🧑🏽‍💻"},{"names":["technologist_tone4"],"surrogates":"🧑🏾‍💻"},{"names":["technologist_tone5"],"surrogates":"🧑🏿‍💻"}]},{"names":["woman_technologist"],"surrogates":"👩‍💻","skins":[{"names":["woman_technologist_tone1"],"surrogates":"👩🏻‍💻"},{"names":["woman_technologist_tone2"],"surrogates":"👩🏼‍💻"},{"names":["woman_technologist_tone3"],"surrogates":"👩🏽‍💻"},{"names":["woman_technologist_tone4"],"surrogates":"👩🏾‍💻"},{"names":["woman_technologist_tone5"],"surrogates":"👩🏿‍💻"}]},{"names":["man_technologist"],"surrogates":"👨‍💻","skins":[{"names":["man_technologist_tone1"],"surrogates":"👨🏻‍💻"},{"names":["man_technologist_tone2"],"surrogates":"👨🏼‍💻"},{"names":["man_technologist_tone3"],"surrogates":"👨🏽‍💻"},{"names":["man_technologist_tone4"],"surrogates":"👨🏾‍💻"},{"names":["man_technologist_tone5"],"surrogates":"👨🏿‍💻"}]},{"names":["office_worker"],"surrogates":"🧑‍💼","skins":[{"names":["office_worker_tone1"],"surrogates":"🧑🏻‍💼"},{"names":["office_worker_tone2"],"surrogates":"🧑🏼‍💼"},{"names":["office_worker_tone3"],"surrogates":"🧑🏽‍💼"},{"names":["office_worker_tone4"],"surrogates":"🧑🏾‍💼"},{"names":["office_worker_tone5"],"surrogates":"🧑🏿‍💼"}]},{"names":["woman_office_worker"],"surrogates":"👩‍💼","skins":[{"names":["woman_office_worker_tone1"],"surrogates":"👩🏻‍💼"},{"names":["woman_office_worker_tone2"],"surrogates":"👩🏼‍💼"},{"names":["woman_office_worker_tone3"],"surrogates":"👩🏽‍💼"},{"names":["woman_office_worker_tone4"],"surrogates":"👩🏾‍💼"},{"names":["woman_office_worker_tone5"],"surrogates":"👩🏿‍💼"}]},{"names":["man_office_worker"],"surrogates":"👨‍💼","skins":[{"names":["man_office_worker_tone1"],"surrogates":"👨🏻‍💼"},{"names":["man_office_worker_tone2"],"surrogates":"👨🏼‍💼"},{"names":["man_office_worker_tone3"],"surrogates":"👨🏽‍💼"},{"names":["man_office_worker_tone4"],"surrogates":"👨🏾‍💼"},{"names":["man_office_worker_tone5"],"surrogates":"👨🏿‍💼"}]},{"names":["mechanic"],"surrogates":"🧑‍🔧","skins":[{"names":["mechanic_tone1"],"surrogates":"🧑🏻‍🔧"},{"names":["mechanic_tone2"],"surrogates":"🧑🏼‍🔧"},{"names":["mechanic_tone3"],"surrogates":"🧑🏽‍🔧"},{"names":["mechanic_tone4"],"surrogates":"🧑🏾‍🔧"},{"names":["mechanic_tone5"],"surrogates":"🧑🏿‍🔧"}]},{"names":["woman_mechanic"],"surrogates":"👩‍🔧","skins":[{"names":["woman_mechanic_tone1"],"surrogates":"👩🏻‍🔧"},{"names":["woman_mechanic_tone2"],"surrogates":"👩🏼‍🔧"},{"names":["woman_mechanic_tone3"],"surrogates":"👩🏽‍🔧"},{"names":["woman_mechanic_tone4"],"surrogates":"👩🏾‍🔧"},{"names":["woman_mechanic_tone5"],"surrogates":"👩🏿‍🔧"}]},{"names":["man_mechanic"],"surrogates":"👨‍🔧","skins":[{"names":["man_mechanic_tone1"],"surrogates":"👨🏻‍🔧"},{"names":["man_mechanic_tone2"],"surrogates":"👨🏼‍🔧"},{"names":["man_mechanic_tone3"],"surrogates":"👨🏽‍🔧"},{"names":["man_mechanic_tone4"],"surrogates":"👨🏾‍🔧"},{"names":["man_mechanic_tone5"],"surrogates":"👨🏿‍🔧"}]},{"names":["scientist"],"surrogates":"🧑‍🔬","skins":[{"names":["scientist_tone1"],"surrogates":"🧑🏻‍🔬"},{"names":["scientist_tone2"],"surrogates":"🧑🏼‍🔬"},{"names":["scientist_tone3"],"surrogates":"🧑🏽‍🔬"},{"names":["scientist_tone4"],"surrogates":"🧑🏾‍🔬"},{"names":["scientist_tone5"],"surrogates":"🧑🏿‍🔬"}]},{"names":["woman_scientist"],"surrogates":"👩‍🔬","skins":[{"names":["woman_scientist_tone1"],"surrogates":"👩🏻‍🔬"},{"names":["woman_scientist_tone2"],"surrogates":"👩🏼‍🔬"},{"names":["woman_scientist_tone3"],"surrogates":"👩🏽‍🔬"},{"names":["woman_scientist_tone4"],"surrogates":"👩🏾‍🔬"},{"names":["woman_scientist_tone5"],"surrogates":"👩🏿‍🔬"}]},{"names":["man_scientist"],"surrogates":"👨‍🔬","skins":[{"names":["man_scientist_tone1"],"surrogates":"👨🏻‍🔬"},{"names":["man_scientist_tone2"],"surrogates":"👨🏼‍🔬"},{"names":["man_scientist_tone3"],"surrogates":"👨🏽‍🔬"},{"names":["man_scientist_tone4"],"surrogates":"👨🏾‍🔬"},{"names":["man_scientist_tone5"],"surrogates":"👨🏿‍🔬"}]},{"names":["artist"],"surrogates":"🧑‍🎨","skins":[{"names":["artist_tone1"],"surrogates":"🧑🏻‍🎨"},{"names":["artist_tone2"],"surrogates":"🧑🏼‍🎨"},{"names":["artist_tone3"],"surrogates":"🧑🏽‍🎨"},{"names":["artist_tone4"],"surrogates":"🧑🏾‍🎨"},{"names":["artist_tone5"],"surrogates":"🧑🏿‍🎨"}]},{"names":["woman_artist"],"surrogates":"👩‍🎨","skins":[{"names":["woman_artist_tone1"],"surrogates":"👩🏻‍🎨"},{"names":["woman_artist_tone2"],"surrogates":"👩🏼‍🎨"},{"names":["woman_artist_tone3"],"surrogates":"👩🏽‍🎨"},{"names":["woman_artist_tone4"],"surrogates":"👩🏾‍🎨"},{"names":["woman_artist_tone5"],"surrogates":"👩🏿‍🎨"}]},{"names":["man_artist"],"surrogates":"👨‍🎨","skins":[{"names":["man_artist_tone1"],"surrogates":"👨🏻‍🎨"},{"names":["man_artist_tone2"],"surrogates":"👨🏼‍🎨"},{"names":["man_artist_tone3"],"surrogates":"👨🏽‍🎨"},{"names":["man_artist_tone4"],"surrogates":"👨🏾‍🎨"},{"names":["man_artist_tone5"],"surrogates":"👨🏿‍🎨"}]},{"names":["firefighter"],"surrogates":"🧑‍🚒","skins":[{"names":["firefighter_tone1"],"surrogates":"🧑🏻‍🚒"},{"names":["firefighter_tone2"],"surrogates":"🧑🏼‍🚒"},{"names":["firefighter_tone3"],"surrogates":"🧑🏽‍🚒"},{"names":["firefighter_tone4"],"surrogates":"🧑🏾‍🚒"},{"names":["firefighter_tone5"],"surrogates":"🧑🏿‍🚒"}]},{"names":["woman_firefighter"],"surrogates":"👩‍🚒","skins":[{"names":["woman_firefighter_tone1"],"surrogates":"👩🏻‍🚒"},{"names":["woman_firefighter_tone2"],"surrogates":"👩🏼‍🚒"},{"names":["woman_firefighter_tone3"],"surrogates":"👩🏽‍🚒"},{"names":["woman_firefighter_tone4"],"surrogates":"👩🏾‍🚒"},{"names":["woman_firefighter_tone5"],"surrogates":"👩🏿‍🚒"}]},{"names":["man_firefighter"],"surrogates":"👨‍🚒","skins":[{"names":["man_firefighter_tone1"],"surrogates":"👨🏻‍🚒"},{"names":["man_firefighter_tone2"],"surrogates":"👨🏼‍🚒"},{"names":["man_firefighter_tone3"],"surrogates":"👨🏽‍🚒"},{"names":["man_firefighter_tone4"],"surrogates":"👨🏾‍🚒"},{"names":["man_firefighter_tone5"],"surrogates":"👨🏿‍🚒"}]},{"names":["pilot"],"surrogates":"🧑‍✈️","skins":[{"names":["pilot_tone1"],"surrogates":"🧑🏻‍✈️"},{"names":["pilot_tone2"],"surrogates":"🧑🏼‍✈️"},{"names":["pilot_tone3"],"surrogates":"🧑🏽‍✈️"},{"names":["pilot_tone4"],"surrogates":"🧑🏾‍✈️"},{"names":["pilot_tone5"],"surrogates":"🧑🏿‍✈️"}]},{"names":["woman_pilot"],"surrogates":"👩‍✈️","skins":[{"names":["woman_pilot_tone1"],"surrogates":"👩🏻‍✈️"},{"names":["woman_pilot_tone2"],"surrogates":"👩🏼‍✈️"},{"names":["woman_pilot_tone3"],"surrogates":"👩🏽‍✈️"},{"names":["woman_pilot_tone4"],"surrogates":"👩🏾‍✈️"},{"names":["woman_pilot_tone5"],"surrogates":"👩🏿‍✈️"}]},{"names":["man_pilot"],"surrogates":"👨‍✈️","skins":[{"names":["man_pilot_tone1"],"surrogates":"👨🏻‍✈️"},{"names":["man_pilot_tone2"],"surrogates":"👨🏼‍✈️"},{"names":["man_pilot_tone3"],"surrogates":"👨🏽‍✈️"},{"names":["man_pilot_tone4"],"surrogates":"👨🏾‍✈️"},{"names":["man_pilot_tone5"],"surrogates":"👨🏿‍✈️"}]},{"names":["astronaut"],"surrogates":"🧑‍🚀","skins":[{"names":["astronaut_tone1"],"surrogates":"🧑🏻‍🚀"},{"names":["astronaut_tone2"],"surrogates":"🧑🏼‍🚀"},{"names":["astronaut_tone3"],"surrogates":"🧑🏽‍🚀"},{"names":["astronaut_tone4"],"surrogates":"🧑🏾‍🚀"},{"names":["astronaut_tone5"],"surrogates":"🧑🏿‍🚀"}]},{"names":["woman_astronaut"],"surrogates":"👩‍🚀","skins":[{"names":["woman_astronaut_tone1"],"surrogates":"👩🏻‍🚀"},{"names":["woman_astronaut_tone2"],"surrogates":"👩🏼‍🚀"},{"names":["woman_astronaut_tone3"],"surrogates":"👩🏽‍🚀"},{"names":["woman_astronaut_tone4"],"surrogates":"👩🏾‍🚀"},{"names":["woman_astronaut_tone5"],"surrogates":"👩🏿‍🚀"}]},{"names":["man_astronaut"],"surrogates":"👨‍🚀","skins":[{"names":["man_astronaut_tone1"],"surrogates":"👨🏻‍🚀"},{"names":["man_astronaut_tone2"],"surrogates":"👨🏼‍🚀"},{"names":["man_astronaut_tone3"],"surrogates":"👨🏽‍🚀"},{"names":["man_astronaut_tone4"],"surrogates":"👨🏾‍🚀"},{"names":["man_astronaut_tone5"],"surrogates":"👨🏿‍🚀"}]},{"names":["judge"],"surrogates":"🧑‍⚖️","skins":[{"names":["judge_tone1"],"surrogates":"🧑🏻‍⚖️"},{"names":["judge_tone2"],"surrogates":"🧑🏼‍⚖️"},{"names":["judge_tone3"],"surrogates":"🧑🏽‍⚖️"},{"names":["judge_tone4"],"surrogates":"🧑🏾‍⚖️"},{"names":["judge_tone5"],"surrogates":"🧑🏿‍⚖️"}]},{"names":["woman_judge"],"surrogates":"👩‍⚖️","skins":[{"names":["woman_judge_tone1"],"surrogates":"👩🏻‍⚖️"},{"names":["woman_judge_tone2"],"surrogates":"👩🏼‍⚖️"},{"names":["woman_judge_tone3"],"surrogates":"👩🏽‍⚖️"},{"names":["woman_judge_tone4"],"surrogates":"👩🏾‍⚖️"},{"names":["woman_judge_tone5"],"surrogates":"👩🏿‍⚖️"}]},{"names":["man_judge"],"surrogates":"👨‍⚖️","skins":[{"names":["man_judge_tone1"],"surrogates":"👨🏻‍⚖️"},{"names":["man_judge_tone2"],"surrogates":"👨🏼‍⚖️"},{"names":["man_judge_tone3"],"surrogates":"👨🏽‍⚖️"},{"names":["man_judge_tone4"],"surrogates":"👨🏾‍⚖️"},{"names":["man_judge_tone5"],"surrogates":"👨🏿‍⚖️"}]},{"names":["person_with_veil"],"surrogates":"👰","skins":[{"names":["person_with_veil_tone1"],"surrogates":"👰🏻"},{"names":["person_with_veil_tone2"],"surrogates":"👰🏼"},{"names":["person_with_veil_tone3"],"surrogates":"👰🏽"},{"names":["person_with_veil_tone4"],"surrogates":"👰🏾"},{"names":["person_with_veil_tone5"],"surrogates":"👰🏿"}]},{"names":["woman_with_veil","bride_with_veil"],"surrogates":"👰","skins":[{"names":["woman_with_veil_tone1"],"surrogates":"👰🏻"},{"names":["woman_with_veil_tone2"],"surrogates":"👰🏼"},{"names":["woman_with_veil_tone3"],"surrogates":"👰🏽"},{"names":["woman_with_veil_tone4"],"surrogates":"👰🏾"},{"names":["woman_with_veil_tone5"],"surrogates":"👰🏿"}]},{"names":["man_with_veil"],"surrogates":"👰‍♂️","skins":[{"names":["man_with_veil_tone1"],"surrogates":"👰🏻‍♂️"},{"names":["man_with_veil_tone2"],"surrogates":"👰🏼‍♂️"},{"names":["man_with_veil_tone3"],"surrogates":"👰🏽‍♂️"},{"names":["man_with_veil_tone4"],"surrogates":"👰🏾‍♂️"},{"names":["man_with_veil_tone5"],"surrogates":"👰🏿‍♂️"}]},{"names":["person_in_tuxedo"],"surrogates":"🤵","skins":[{"names":["person_in_tuxedo_tone1"],"surrogates":"🤵🏻"},{"names":["person_in_tuxedo_tone2"],"surrogates":"🤵🏼"},{"names":["person_in_tuxedo_tone3"],"surrogates":"🤵🏽"},{"names":["person_in_tuxedo_tone4"],"surrogates":"🤵🏾"},{"names":["person_in_tuxedo_tone5"],"surrogates":"🤵🏿"}]},{"names":["woman_in_tuxedo"],"surrogates":"🤵‍♀️","skins":[{"names":["woman_in_tuxedo_tone1"],"surrogates":"🤵🏻‍♀️"},{"names":["woman_in_tuxedo_tone2"],"surrogates":"🤵🏼‍♀️"},{"names":["woman_in_tuxedo_tone3"],"surrogates":"🤵🏽‍♀️"},{"names":["woman_in_tuxedo_tone4"],"surrogates":"🤵🏾‍♀️"},{"names":["woman_in_tuxedo_tone5"],"surrogates":"🤵🏿‍♀️"}]},{"names":["man_in_tuxedo"],"surrogates":"🤵‍♂️","skins":[{"names":["man_in_tuxedo_tone1"],"surrogates":"🤵🏻‍♂️"},{"names":["man_in_tuxedo_tone2"],"surrogates":"🤵🏼‍♂️"},{"names":["man_in_tuxedo_tone3"],"surrogates":"🤵🏽‍♂️"},{"names":["man_in_tuxedo_tone4"],"surrogates":"🤵🏾‍♂️"},{"names":["man_in_tuxedo_tone5"],"surrogates":"🤵🏿‍♂️"}]},{"names":["person_with_crown"],"surrogates":"🫅","skins":[{"names":["person_with_crown_tone1"],"surrogates":"🫅🏻"},{"names":["person_with_crown_tone2"],"surrogates":"🫅🏼"},{"names":["person_with_crown_tone3"],"surrogates":"🫅🏽"},{"names":["person_with_crown_tone4"],"surrogates":"🫅🏾"},{"names":["person_with_crown_tone5"],"surrogates":"🫅🏿"}]},{"names":["princess"],"surrogates":"👸","skins":[{"names":["princess_tone1"],"surrogates":"👸🏻"},{"names":["princess_tone2"],"surrogates":"👸🏼"},{"names":["princess_tone3"],"surrogates":"👸🏽"},{"names":["princess_tone4"],"surrogates":"👸🏾"},{"names":["princess_tone5"],"surrogates":"👸🏿"}]},{"names":["prince"],"surrogates":"🤴","skins":[{"names":["prince_tone1"],"surrogates":"🤴🏻"},{"names":["prince_tone2"],"surrogates":"🤴🏼"},{"names":["prince_tone3"],"surrogates":"🤴🏽"},{"names":["prince_tone4"],"surrogates":"🤴🏾"},{"names":["prince_tone5"],"surrogates":"🤴🏿"}]},{"names":["superhero"],"surrogates":"🦸","skins":[{"names":["superhero_tone1"],"surrogates":"🦸🏻"},{"names":["superhero_tone2"],"surrogates":"🦸🏼"},{"names":["superhero_tone3"],"surrogates":"🦸🏽"},{"names":["superhero_tone4"],"surrogates":"🦸🏾"},{"names":["superhero_tone5"],"surrogates":"🦸🏿"}]},{"names":["woman_superhero"],"surrogates":"🧘‍♀️","skins":[{"names":["woman_superhero_tone1"],"surrogates":"🧘🏻‍♀️"},{"names":["woman_superhero_tone2"],"surrogates":"🧘🏼‍♀️"},{"names":["woman_superhero_tone3"],"surrogates":"🧘🏽‍♀️"},{"names":["woman_superhero_tone4"],"surrogates":"🧘🏾‍♀️"},{"names":["woman_superhero_tone5"],"surrogates":"🧘🏿‍♀️"}]},{"names":["man_superhero"],"surrogates":"🧘‍♂️","skins":[{"names":["man_superhero_tone1"],"surrogates":"🧘🏻‍♂️"},{"names":["man_superhero_tone2"],"surrogates":"🧘🏼‍♂️"},{"names":["man_superhero_tone3"],"surrogates":"🧘🏽‍♂️"},{"names":["man_superhero_tone4"],"surrogates":"🧘🏾‍♂️"},{"names":["man_superhero_tone5"],"surrogates":"🧘🏿‍♂️"}]},{"names":["supervillain"],"surrogates":"🦹","skins":[{"names":["supervillain_tone1"],"surrogates":"🦹🏻"},{"names":["supervillain_tone2"],"surrogates":"🦹🏼"},{"names":["supervillain_tone3"],"surrogates":"🦹🏽"},{"names":["supervillain_tone4"],"surrogates":"🦹🏾"},{"names":["supervillain_tone5"],"surrogates":"🦹🏿"}]},{"names":["woman_supervillain"],"surrogates":"🧙‍♀️","skins":[{"names":["woman_supervillain_tone1"],"surrogates":"🧙🏻‍♀️"},{"names":["woman_supervillain_tone2"],"surrogates":"🧙🏼‍♀️"},{"names":["woman_supervillain_tone3"],"surrogates":"🧙🏽‍♀️"},{"names":["woman_supervillain_tone4"],"surrogates":"🧙🏾‍♀️"},{"names":["woman_supervillain_tone5"],"surrogates":"🧙🏿‍♀️"}]},{"names":["man_supervillain"],"surrogates":"🧙‍♂️","skins":[{"names":["man_supervillain_tone1"],"surrogates":"🧙🏻‍♂️"},{"names":["man_supervillain_tone2"],"surrogates":"🧙🏼‍♂️"},{"names":["man_supervillain_tone3"],"surrogates":"🧙🏽‍♂️"},{"names":["man_supervillain_tone4"],"surrogates":"🧙🏾‍♂️"},{"names":["man_supervillain_tone5"],"surrogates":"🧙🏿‍♂️"}]},{"names":["ninja"],"surrogates":"🥷","skins":[{"names":["ninja_tone1"],"surrogates":"🥷🏻"},{"names":["ninja_tone2"],"surrogates":"🥷🏼"},{"names":["ninja_tone3"],"surrogates":"🥷🏽"},{"names":["ninja_tone4"],"surrogates":"🥷🏾"},{"names":["ninja_tone5"],"surrogates":"🥷🏿"}]},{"names":["mx_claus"],"surrogates":"🧑‍🎄","skins":[{"names":["mx_claus_tone1"],"surrogates":"🧑🏻‍🎄"},{"names":["mx_claus_tone2"],"surrogates":"🧑🏼‍🎄"},{"names":["mx_claus_tone3"],"surrogates":"🧑🏽‍🎄"},{"names":["mx_claus_tone4"],"surrogates":"🧑🏾‍🎄"},{"names":["mx_claus_tone5"],"surrogates":"🧑🏿‍🎄"}]},{"names":["mrs_claus","mother_christmas"],"surrogates":"🤶","skins":[{"names":["mrs_claus_tone1"],"surrogates":"🤶🏻"},{"names":["mrs_claus_tone2"],"surrogates":"🤶🏼"},{"names":["mrs_claus_tone3"],"surrogates":"🤶🏽"},{"names":["mrs_claus_tone4"],"surrogates":"🤶🏾"},{"names":["mrs_claus_tone5"],"surrogates":"🤶🏿"}]},{"names":["santa","santa_claus"],"surrogates":"🎅","skins":[{"names":["santa_tone1"],"surrogates":"🎅🏻"},{"names":["santa_tone2"],"surrogates":"🎅🏼"},{"names":["santa_tone3"],"surrogates":"🎅🏽"},{"names":["santa_tone4"],"surrogates":"🎅🏾"},{"names":["santa_tone5"],"surrogates":"🎅🏿"}]},{"names":["mage"],"surrogates":"🧙","skins":[{"names":["mage_tone1"],"surrogates":"🧙🏻"},{"names":["mage_tone2"],"surrogates":"🧙🏼"},{"names":["mage_tone3"],"surrogates":"🧙🏽"},{"names":["mage_tone4"],"surrogates":"🧙🏾"},{"names":["mage_tone5"],"surrogates":"🧙🏿"}]},{"names":["woman_mage"],"surrogates":"🧙‍♀️","skins":[{"names":["woman_mage_tone1"],"surrogates":"🧙🏻‍♀️"},{"names":["woman_mage_tone2"],"surrogates":"🧙🏼‍♀️"},{"names":["woman_mage_tone3"],"surrogates":"🧙🏽‍♀️"},{"names":["woman_mage_tone4"],"surrogates":"🧙🏾‍♀️"},{"names":["woman_mage_tone5"],"surrogates":"🧙🏿‍♀️"}]},{"names":["man_mage"],"surrogates":"🧙‍♂️","skins":[{"names":["man_mage_tone1"],"surrogates":"🧙🏻‍♂️"},{"names":["man_mage_tone2"],"surrogates":"🧙🏼‍♂️"},{"names":["man_mage_tone3"],"surrogates":"🧙🏽‍♂️"},{"names":["man_mage_tone4"],"surrogates":"🧙🏾‍♂️"},{"names":["man_mage_tone5"],"surrogates":"🧙🏿‍♂️"}]},{"names":["elf"],"surrogates":"🧝","skins":[{"names":["elf_tone1"],"surrogates":"🧝🏻"},{"names":["elf_tone2"],"surrogates":"🧝🏼"},{"names":["elf_tone3"],"surrogates":"🧝🏽"},{"names":["elf_tone4"],"surrogates":"🧝🏾"},{"names":["elf_tone5"],"surrogates":"🧝🏿"}]},{"names":["woman_elf"],"surrogates":"🧝‍♀️","skins":[{"names":["woman_elf_tone1"],"surrogates":"🧝🏻‍♀️"},{"names":["woman_elf_tone2"],"surrogates":"🧝🏼‍♀️"},{"names":["woman_elf_tone3"],"surrogates":"🧝🏽‍♀️"},{"names":["woman_elf_tone4"],"surrogates":"🧝🏾‍♀️"},{"names":["woman_elf_tone5"],"surrogates":"🧝🏿‍♀️"}]},{"names":["man_elf"],"surrogates":"🧝‍♂️","skins":[{"names":["man_elf_tone1"],"surrogates":"🧝🏻‍♂️"},{"names":["man_elf_tone2"],"surrogates":"🧝🏼‍♂️"},{"names":["man_elf_tone3"],"surrogates":"🧝🏽‍♂️"},{"names":["man_elf_tone4"],"surrogates":"🧝🏾‍♂️"},{"names":["man_elf_tone5"],"surrogates":"🧝🏿‍♂️"}]},{"names":["troll"],"surrogates":"🧌"},{"names":["vampire"],"surrogates":"🧛","skins":[{"names":["vampire_tone1"],"surrogates":"🧛🏻"},{"names":["vampire_tone2"],"surrogates":"🧛🏼"},{"names":["vampire_tone3"],"surrogates":"🧛🏽"},{"names":["vampire_tone4"],"surrogates":"🧛🏾"},{"names":["vampire_tone5"],"surrogates":"🧛🏿"}]},{"names":["woman_vampire"],"surrogates":"🧛‍♀️","skins":[{"names":["woman_vampire_tone1"],"surrogates":"🧛🏻‍♀️"},{"names":["woman_vampire_tone2"],"surrogates":"🧛🏼‍♀️"},{"names":["woman_vampire_tone3"],"surrogates":"🧛🏽‍♀️"},{"names":["woman_vampire_tone4"],"surrogates":"🧛🏾‍♀️"},{"names":["woman_vampire_tone5"],"surrogates":"🧛🏿‍♀️"}]},{"names":["man_vampire"],"surrogates":"🧛‍♂️","skins":[{"names":["man_vampire_tone1"],"surrogates":"🧛🏻‍♂️"},{"names":["man_vampire_tone2"],"surrogates":"🧛🏼‍♂️"},{"names":["man_vampire_tone3"],"surrogates":"🧛🏽‍♂️"},{"names":["man_vampire_tone4"],"surrogates":"🧛🏾‍♂️"},{"names":["man_vampire_tone5"],"surrogates":"🧛🏿‍♂️"}]},{"names":["zombie"],"surrogates":"🧟"},{"names":["woman_zombie"],"surrogates":"🧟‍♀️"},{"names":["man_zombie"],"surrogates":"🧟‍♂️"},{"names":["genie"],"surrogates":"🧞"},{"names":["woman_genie"],"surrogates":"🧞‍♀️"},{"names":["man_genie"],"surrogates":"🧞‍♂️"},{"names":["merperson"],"surrogates":"🧜","skins":[{"names":["merperson_tone1"],"surrogates":"🧜🏻"},{"names":["merperson_tone2"],"surrogates":"🧜🏼"},{"names":["merperson_tone3"],"surrogates":"🧜🏽"},{"names":["merperson_tone4"],"surrogates":"🧜🏾"},{"names":["merperson_tone5"],"surrogates":"🧜🏿"}]},{"names":["mermaid"],"surrogates":"🧜‍♀️","skins":[{"names":["mermaid_tone1"],"surrogates":"🧜🏻‍♀️"},{"names":["mermaid_tone2"],"surrogates":"🧜🏼‍♀️"},{"names":["mermaid_tone3"],"surrogates":"🧜🏽‍♀️"},{"names":["mermaid_tone4"],"surrogates":"🧜🏾‍♀️"},{"names":["mermaid_tone5"],"surrogates":"🧜🏿‍♀️"}]},{"names":["merman"],"surrogates":"🧜‍♂️","skins":[{"names":["merman_tone1"],"surrogates":"🧜🏻‍♂️"},{"names":["merman_tone2"],"surrogates":"🧜🏼‍♂️"},{"names":["merman_tone3"],"surrogates":"🧜🏽‍♂️"},{"names":["merman_tone4"],"surrogates":"🧜🏾‍♂️"},{"names":["merman_tone5"],"surrogates":"🧜🏿‍♂️"}]},{"names":["fairy"],"surrogates":"🧚","skins":[{"names":["fairy_tone1"],"surrogates":"🧚🏻"},{"names":["fairy_tone2"],"surrogates":"🧚🏼"},{"names":["fairy_tone3"],"surrogates":"🧚🏽"},{"names":["fairy_tone4"],"surrogates":"🧚🏾"},{"names":["fairy_tone5"],"surrogates":"🧚🏿"}]},{"names":["woman_fairy"],"surrogates":"🧚‍♀️","skins":[{"names":["woman_fairy_tone1"],"surrogates":"🧚🏻‍♀️"},{"names":["woman_fairy_tone2"],"surrogates":"🧚🏼‍♀️"},{"names":["woman_fairy_tone3"],"surrogates":"🧚🏽‍♀️"},{"names":["woman_fairy_tone4"],"surrogates":"🧚🏾‍♀️"},{"names":["woman_fairy_tone5"],"surrogates":"🧚🏿‍♀️"}]},{"names":["man_fairy"],"surrogates":"🧚‍♂️","skins":[{"names":["man_fairy_tone1"],"surrogates":"🧚🏻‍♂️"},{"names":["man_fairy_tone2"],"surrogates":"🧚🏼‍♂️"},{"names":["man_fairy_tone3"],"surrogates":"🧚🏽‍♂️"},{"names":["man_fairy_tone4"],"surrogates":"🧚🏾‍♂️"},{"names":["man_fairy_tone5"],"surrogates":"🧚🏿‍♂️"}]},{"names":["angel","baby_angel"],"surrogates":"👼","skins":[{"names":["angel_tone1"],"surrogates":"👼🏻"},{"names":["angel_tone2"],"surrogates":"👼🏼"},{"names":["angel_tone3"],"surrogates":"👼🏽"},{"names":["angel_tone4"],"surrogates":"👼🏾"},{"names":["angel_tone5"],"surrogates":"👼🏿"}]},{"names":["pregnant_person"],"surrogates":"🫄","skins":[{"names":["pregnant_person_tone1"],"surrogates":"🫄🏻"},{"names":["pregnant_person_tone2"],"surrogates":"🫄🏼"},{"names":["pregnant_person_tone3"],"surrogates":"🫄🏽"},{"names":["pregnant_person_tone4"],"surrogates":"🫄🏾"},{"names":["pregnant_person_tone5"],"surrogates":"🫄🏿"}]},{"names":["pregnant_woman","expecting_woman"],"surrogates":"🤰","skins":[{"names":["pregnant_woman_tone1"],"surrogates":"🤰🏻"},{"names":["pregnant_woman_tone2"],"surrogates":"🤰🏼"},{"names":["pregnant_woman_tone3"],"surrogates":"🤰🏽"},{"names":["pregnant_woman_tone4"],"surrogates":"🤰🏾"},{"names":["pregnant_woman_tone5"],"surrogates":"🤰🏿"}]},{"names":["pregnant_man"],"surrogates":"🫃","skins":[{"names":["pregnant_man_tone1"],"surrogates":"🫃🏻"},{"names":["pregnant_man_tone2"],"surrogates":"🫃🏼"},{"names":["pregnant_man_tone3"],"surrogates":"🫃🏽"},{"names":["pregnant_man_tone4"],"surrogates":"🫃🏾"},{"names":["pregnant_man_tone5"],"surrogates":"🫃🏿"}]},{"names":["breast_feeding"],"surrogates":"🤱","skins":[{"names":["breast_feeding_tone1"],"surrogates":"🤱🏻"},{"names":["breast_feeding_tone2"],"surrogates":"🤱🏼"},{"names":["breast_feeding_tone3"],"surrogates":"🤱🏽"},{"names":["breast_feeding_tone4"],"surrogates":"🤱🏾"},{"names":["breast_feeding_tone5"],"surrogates":"🤱🏿"}]},{"names":["person_feeding_baby"],"surrogates":"🧑‍🍼","skins":[{"names":["person_feeding_baby_tone1"],"surrogates":"🧑🏻‍🍼"},{"names":["person_feeding_baby_tone2"],"surrogates":"🧑🏼‍🍼"},{"names":["person_feeding_baby_tone3"],"surrogates":"🧑🏽‍🍼"},{"names":["person_feeding_baby_tone4"],"surrogates":"🧑🏾‍🍼"},{"names":["person_feeding_baby_tone5"],"surrogates":"🧑🏿‍🍼"}]},{"names":["woman_feeding_baby"],"surrogates":"👩‍🍼","skins":[{"names":["woman_feeding_baby_tone1"],"surrogates":"👩🏻‍🍼"},{"names":["woman_feeding_baby_tone2"],"surrogates":"👩🏼‍🍼"},{"names":["woman_feeding_baby_tone3"],"surrogates":"👩🏽‍🍼"},{"names":["woman_feeding_baby_tone4"],"surrogates":"👩🏾‍🍼"},{"names":["woman_feeding_baby_tone5"],"surrogates":"👩🏿‍🍼"}]},{"names":["man_feeding_baby"],"surrogates":"👨‍🍼","skins":[{"names":["man_feeding_baby_tone1"],"surrogates":"👨🏻‍🍼"},{"names":["man_feeding_baby_tone2"],"surrogates":"👨🏼‍🍼"},{"names":["man_feeding_baby_tone3"],"surrogates":"👨🏽‍🍼"},{"names":["man_feeding_baby_tone4"],"surrogates":"👨🏾‍🍼"},{"names":["man_feeding_baby_tone5"],"surrogates":"👨🏿‍🍼"}]},{"names":["person_bowing","bow"],"surrogates":"🙇","skins":[{"names":["person_bowing_tone1"],"surrogates":"🙇🏻"},{"names":["person_bowing_tone2"],"surrogates":"🙇🏼"},{"names":["person_bowing_tone3"],"surrogates":"🙇🏽"},{"names":["person_bowing_tone4"],"surrogates":"🙇🏾"},{"names":["person_bowing_tone5"],"surrogates":"🙇🏿"}]},{"names":["woman_bowing"],"surrogates":"🙇‍♀️","skins":[{"names":["woman_bowing_tone1"],"surrogates":"🙇🏻‍♀️"},{"names":["woman_bowing_tone2"],"surrogates":"🙇🏼‍♀️"},{"names":["woman_bowing_tone3"],"surrogates":"🙇🏽‍♀️"},{"names":["woman_bowing_tone4"],"surrogates":"🙇🏾‍♀️"},{"names":["woman_bowing_tone5"],"surrogates":"🙇🏿‍♀️"}]},{"names":["man_bowing"],"surrogates":"🙇‍♂️","skins":[{"names":["man_bowing_tone1"],"surrogates":"🙇🏻‍♂️"},{"names":["man_bowing_tone2"],"surrogates":"🙇🏼‍♂️"},{"names":["man_bowing_tone3"],"surrogates":"🙇🏽‍♂️"},{"names":["man_bowing_tone4"],"surrogates":"🙇🏾‍♂️"},{"names":["man_bowing_tone5"],"surrogates":"🙇🏿‍♂️"}]},{"names":["person_tipping_hand","information_desk_person"],"surrogates":"💁","skins":[{"names":["person_tipping_hand_tone1"],"surrogates":"💁🏻"},{"names":["person_tipping_hand_tone2"],"surrogates":"💁🏼"},{"names":["person_tipping_hand_tone3"],"surrogates":"💁🏽"},{"names":["person_tipping_hand_tone4"],"surrogates":"💁🏾"},{"names":["person_tipping_hand_tone5"],"surrogates":"💁🏿"}]},{"names":["woman_tipping_hand"],"surrogates":"💁‍♀️","skins":[{"names":["woman_tipping_hand_tone1"],"surrogates":"💁🏻‍♀️"},{"names":["woman_tipping_hand_tone2"],"surrogates":"💁🏼‍♀️"},{"names":["woman_tipping_hand_tone3"],"surrogates":"💁🏽‍♀️"},{"names":["woman_tipping_hand_tone4"],"surrogates":"💁🏾‍♀️"},{"names":["woman_tipping_hand_tone5"],"surrogates":"💁🏿‍♀️"}]},{"names":["man_tipping_hand"],"surrogates":"💁‍♂️","skins":[{"names":["man_tipping_hand_tone1"],"surrogates":"💁🏻‍♂️"},{"names":["man_tipping_hand_tone2"],"surrogates":"💁🏼‍♂️"},{"names":["man_tipping_hand_tone3"],"surrogates":"💁🏽‍♂️"},{"names":["man_tipping_hand_tone4"],"surrogates":"💁🏾‍♂️"},{"names":["man_tipping_hand_tone5"],"surrogates":"💁🏿‍♂️"}]},{"names":["person_gesturing_no","no_good"],"surrogates":"🙅","skins":[{"names":["person_gesturing_no_tone1"],"surrogates":"🙅🏻"},{"names":["person_gesturing_no_tone2"],"surrogates":"🙅🏼"},{"names":["person_gesturing_no_tone3"],"surrogates":"🙅🏽"},{"names":["person_gesturing_no_tone4"],"surrogates":"🙅🏾"},{"names":["person_gesturing_no_tone5"],"surrogates":"🙅🏿"}]},{"names":["woman_gesturing_no"],"surrogates":"🙅‍♀️","skins":[{"names":["woman_gesturing_no_tone1"],"surrogates":"🙅🏻‍♀️"},{"names":["woman_gesturing_no_tone2"],"surrogates":"🙅🏼‍♀️"},{"names":["woman_gesturing_no_tone3"],"surrogates":"🙅🏽‍♀️"},{"names":["woman_gesturing_no_tone4"],"surrogates":"🙅🏾‍♀️"},{"names":["woman_gesturing_no_tone5"],"surrogates":"🙅🏿‍♀️"}]},{"names":["man_gesturing_no"],"surrogates":"🙅‍♂️","skins":[{"names":["man_gesturing_no_tone1"],"surrogates":"🙅🏻‍♂️"},{"names":["man_gesturing_no_tone2"],"surrogates":"🙅🏼‍♂️"},{"names":["man_gesturing_no_tone3"],"surrogates":"🙅🏽‍♂️"},{"names":["man_gesturing_no_tone4"],"surrogates":"🙅🏾‍♂️"},{"names":["man_gesturing_no_tone5"],"surrogates":"🙅🏿‍♂️"}]},{"names":["person_gesturing_ok"],"surrogates":"🙆","skins":[{"names":["person_gesturing_ok_tone1"],"surrogates":"🙆🏻"},{"names":["person_gesturing_ok_tone2"],"surrogates":"🙆🏼"},{"names":["person_gesturing_ok_tone3"],"surrogates":"🙆🏽"},{"names":["person_gesturing_ok_tone4"],"surrogates":"🙆🏾"},{"names":["person_gesturing_ok_tone5"],"surrogates":"🙆🏿"}]},{"names":["woman_gesturing_ok"],"surrogates":"🙆‍♀️","skins":[{"names":["woman_gesturing_ok_tone1"],"surrogates":"🙆🏻‍♀️"},{"names":["woman_gesturing_ok_tone2"],"surrogates":"🙆🏼‍♀️"},{"names":["woman_gesturing_ok_tone3"],"surrogates":"🙆🏽‍♀️"},{"names":["woman_gesturing_ok_tone4"],"surrogates":"🙆🏾‍♀️"},{"names":["woman_gesturing_ok_tone5"],"surrogates":"🙆🏿‍♀️"}]},{"names":["man_gesturing_ok"],"surrogates":"🙆‍♂️","skins":[{"names":["man_gesturing_ok_tone1"],"surrogates":"🙆🏻‍♂️"},{"names":["man_gesturing_ok_tone2"],"surrogates":"🙆🏼‍♂️"},{"names":["man_gesturing_ok_tone3"],"surrogates":"🙆🏽‍♂️"},{"names":["man_gesturing_ok_tone4"],"surrogates":"🙆🏾‍♂️"},{"names":["man_gesturing_ok_tone5"],"surrogates":"🙆🏿‍♂️"}]},{"names":["person_raising_hand","raising_hand"],"surrogates":"🙋","skins":[{"names":["person_raising_hand_tone1"],"surrogates":"🙋🏻"},{"names":["person_raising_hand_tone2"],"surrogates":"🙋🏼"},{"names":["person_raising_hand_tone3"],"surrogates":"🙋🏽"},{"names":["person_raising_hand_tone4"],"surrogates":"🙋🏾"},{"names":["person_raising_hand_tone5"],"surrogates":"🙋🏿"}]},{"names":["woman_raising_hand"],"surrogates":"🙋‍♀️","skins":[{"names":["woman_raising_hand_tone1"],"surrogates":"🙋🏻‍♀️"},{"names":["woman_raising_hand_tone2"],"surrogates":"🙋🏼‍♀️"},{"names":["woman_raising_hand_tone3"],"surrogates":"🙋🏽‍♀️"},{"names":["woman_raising_hand_tone4"],"surrogates":"🙋🏾‍♀️"},{"names":["woman_raising_hand_tone5"],"surrogates":"🙋🏿‍♀️"}]},{"names":["man_raising_hand"],"surrogates":"🙋‍♂️","skins":[{"names":["man_raising_hand_tone1"],"surrogates":"🙋🏻‍♂️"},{"names":["man_raising_hand_tone2"],"surrogates":"🙋🏼‍♂️"},{"names":["man_raising_hand_tone3"],"surrogates":"🙋🏽‍♂️"},{"names":["man_raising_hand_tone4"],"surrogates":"🙋🏾‍♂️"},{"names":["man_raising_hand_tone5"],"surrogates":"🙋🏿‍♂️"}]},{"names":["deaf_person"],"surrogates":"🧏","skins":[{"names":["deaf_person_tone1"],"surrogates":"🧏🏻"},{"names":["deaf_person_tone2"],"surrogates":"🧏🏼"},{"names":["deaf_person_tone3"],"surrogates":"🧏🏽"},{"names":["deaf_person_tone4"],"surrogates":"🧏🏾"},{"names":["deaf_person_tone5"],"surrogates":"🧏🏿"}]},{"names":["deaf_woman"],"surrogates":"🧏‍♀️","skins":[{"names":["deaf_woman_tone1"],"surrogates":"🧏🏻‍♀️"},{"names":["deaf_woman_tone2"],"surrogates":"🧏🏼‍♀️"},{"names":["deaf_woman_tone3"],"surrogates":"🧏🏽‍♀️"},{"names":["deaf_woman_tone4"],"surrogates":"🧏🏾‍♀️"},{"names":["deaf_woman_tone5"],"surrogates":"🧏🏿‍♀️"}]},{"names":["deaf_man"],"surrogates":"🧏‍♂️","skins":[{"names":["deaf_man_tone1"],"surrogates":"🧏🏻‍♂️"},{"names":["deaf_man_tone2"],"surrogates":"🧏🏼‍♂️"},{"names":["deaf_man_tone3"],"surrogates":"🧏🏽‍♂️"},{"names":["deaf_man_tone4"],"surrogates":"🧏🏾‍♂️"},{"names":["deaf_man_tone5"],"surrogates":"🧏🏿‍♂️"}]},{"names":["person_facepalming","face_palm","facepalm"],"surrogates":"🤦","skins":[{"names":["person_facepalming_tone1"],"surrogates":"🤦🏻"},{"names":["person_facepalming_tone2"],"surrogates":"🤦🏼"},{"names":["person_facepalming_tone3"],"surrogates":"🤦🏽"},{"names":["person_facepalming_tone4"],"surrogates":"🤦🏾"},{"names":["person_facepalming_tone5"],"surrogates":"🤦🏿"}]},{"names":["woman_facepalming"],"surrogates":"🤦‍♀️","skins":[{"names":["woman_facepalming_tone1"],"surrogates":"🤦🏻‍♀️"},{"names":["woman_facepalming_tone2"],"surrogates":"🤦🏼‍♀️"},{"names":["woman_facepalming_tone3"],"surrogates":"🤦🏽‍♀️"},{"names":["woman_facepalming_tone4"],"surrogates":"🤦🏾‍♀️"},{"names":["woman_facepalming_tone5"],"surrogates":"🤦🏿‍♀️"}]},{"names":["man_facepalming"],"surrogates":"🤦‍♂️","skins":[{"names":["man_facepalming_tone1"],"surrogates":"🤦🏻‍♂️"},{"names":["man_facepalming_tone2"],"surrogates":"🤦🏼‍♂️"},{"names":["man_facepalming_tone3"],"surrogates":"🤦🏽‍♂️"},{"names":["man_facepalming_tone4"],"surrogates":"🤦🏾‍♂️"},{"names":["man_facepalming_tone5"],"surrogates":"🤦🏿‍♂️"}]},{"names":["person_shrugging","shrug"],"surrogates":"🤷","skins":[{"names":["person_shrugging_tone1"],"surrogates":"🤷🏻"},{"names":["person_shrugging_tone2"],"surrogates":"🤷🏼"},{"names":["person_shrugging_tone3"],"surrogates":"🤷🏽"},{"names":["person_shrugging_tone4"],"surrogates":"🤷🏾"},{"names":["person_shrugging_tone5"],"surrogates":"🤷🏿"}]},{"names":["woman_shrugging"],"surrogates":"🤷‍♀️","skins":[{"names":["woman_shrugging_tone1"],"surrogates":"🤷🏻‍♀️"},{"names":["woman_shrugging_tone2"],"surrogates":"🤷🏼‍♀️"},{"names":["woman_shrugging_tone3"],"surrogates":"🤷🏽‍♀️"},{"names":["woman_shrugging_tone4"],"surrogates":"🤷🏾‍♀️"},{"names":["woman_shrugging_tone5"],"surrogates":"🤷🏿‍♀️"}]},{"names":["man_shrugging"],"surrogates":"🤷‍♂️","skins":[{"names":["man_shrugging_tone1"],"surrogates":"🤷🏻‍♂️"},{"names":["man_shrugging_tone2"],"surrogates":"🤷🏼‍♂️"},{"names":["man_shrugging_tone3"],"surrogates":"🤷🏽‍♂️"},{"names":["man_shrugging_tone4"],"surrogates":"🤷🏾‍♂️"},{"names":["man_shrugging_tone5"],"surrogates":"🤷🏿‍♂️"}]},{"names":["person_pouting","person_with_pouting_face"],"surrogates":"🙎","skins":[{"names":["person_pouting_tone1"],"surrogates":"🙎🏻"},{"names":["person_pouting_tone2"],"surrogates":"🙎🏼"},{"names":["person_pouting_tone3"],"surrogates":"🙎🏽"},{"names":["person_pouting_tone4"],"surrogates":"🙎🏾"},{"names":["person_pouting_tone5"],"surrogates":"🙎🏿"}]},{"names":["woman_pouting"],"surrogates":"🙎‍♀️","skins":[{"names":["woman_pouting_tone1"],"surrogates":"🙎🏻‍♀️"},{"names":["woman_pouting_tone2"],"surrogates":"🙎🏼‍♀️"},{"names":["woman_pouting_tone3"],"surrogates":"🙎🏽‍♀️"},{"names":["woman_pouting_tone4"],"surrogates":"🙎🏾‍♀️"},{"names":["woman_pouting_tone5"],"surrogates":"🙎🏿‍♀️"}]},{"names":["man_pouting"],"surrogates":"🙎‍♂️","skins":[{"names":["man_pouting_tone1"],"surrogates":"🙎🏻‍♂️"},{"names":["man_pouting_tone2"],"surrogates":"🙎🏼‍♂️"},{"names":["man_pouting_tone3"],"surrogates":"🙎🏽‍♂️"},{"names":["man_pouting_tone4"],"surrogates":"🙎🏾‍♂️"},{"names":["man_pouting_tone5"],"surrogates":"🙎🏿‍♂️"}]},{"names":["person_frowning"],"surrogates":"🙍","skins":[{"names":["person_frowning_tone1"],"surrogates":"🙍🏻"},{"names":["person_frowning_tone2"],"surrogates":"🙍🏼"},{"names":["person_frowning_tone3"],"surrogates":"🙍🏽"},{"names":["person_frowning_tone4"],"surrogates":"🙍🏾"},{"names":["person_frowning_tone5"],"surrogates":"🙍🏿"}]},{"names":["woman_frowning"],"surrogates":"🙍‍♀️","skins":[{"names":["woman_frowning_tone1"],"surrogates":"🙍🏻‍♀️"},{"names":["woman_frowning_tone2"],"surrogates":"🙍🏼‍♀️"},{"names":["woman_frowning_tone3"],"surrogates":"🙍🏽‍♀️"},{"names":["woman_frowning_tone4"],"surrogates":"🙍🏾‍♀️"},{"names":["woman_frowning_tone5"],"surrogates":"🙍🏿‍♀️"}]},{"names":["man_frowning"],"surrogates":"🙍‍♂️","skins":[{"names":["man_frowning_tone1"],"surrogates":"🙍🏻‍♂️"},{"names":["man_frowning_tone2"],"surrogates":"🙍🏼‍♂️"},{"names":["man_frowning_tone3"],"surrogates":"🙍🏽‍♂️"},{"names":["man_frowning_tone4"],"surrogates":"🙍🏾‍♂️"},{"names":["man_frowning_tone5"],"surrogates":"🙍🏿‍♂️"}]},{"names":["person_getting_haircut","haircut"],"surrogates":"💇","skins":[{"names":["person_getting_haircut_tone1"],"surrogates":"💇🏻"},{"names":["person_getting_haircut_tone2"],"surrogates":"💇🏼"},{"names":["person_getting_haircut_tone3"],"surrogates":"💇🏽"},{"names":["person_getting_haircut_tone4"],"surrogates":"💇🏾"},{"names":["person_getting_haircut_tone5"],"surrogates":"💇🏿"}]},{"names":["woman_getting_haircut"],"surrogates":"💇‍♀️","skins":[{"names":["woman_getting_haircut_tone1"],"surrogates":"💇🏻‍♀️"},{"names":["woman_getting_haircut_tone2"],"surrogates":"💇🏼‍♀️"},{"names":["woman_getting_haircut_tone3"],"surrogates":"💇🏽‍♀️"},{"names":["woman_getting_haircut_tone4"],"surrogates":"💇🏾‍♀️"},{"names":["woman_getting_haircut_tone5"],"surrogates":"💇🏿‍♀️"}]},{"names":["man_getting_haircut"],"surrogates":"💇‍♂️","skins":[{"names":["man_getting_haircut_tone1"],"surrogates":"💇🏻‍♂️"},{"names":["man_getting_haircut_tone2"],"surrogates":"💇🏼‍♂️"},{"names":["man_getting_haircut_tone3"],"surrogates":"💇🏽‍♂️"},{"names":["man_getting_haircut_tone4"],"surrogates":"💇🏾‍♂️"},{"names":["man_getting_haircut_tone5"],"surrogates":"💇🏿‍♂️"}]},{"names":["person_getting_massage","massage"],"surrogates":"💆","skins":[{"names":["person_getting_massage_tone1"],"surrogates":"💆🏻"},{"names":["person_getting_massage_tone2"],"surrogates":"💆🏼"},{"names":["person_getting_massage_tone3"],"surrogates":"💆🏽"},{"names":["person_getting_massage_tone4"],"surrogates":"💆🏾"},{"names":["person_getting_massage_tone5"],"surrogates":"💆🏿"}]},{"names":["woman_getting_face_massage"],"surrogates":"💆‍♀️","skins":[{"names":["woman_getting_face_massage_tone1"],"surrogates":"💆🏻‍♀️"},{"names":["woman_getting_face_massage_tone2"],"surrogates":"💆🏼‍♀️"},{"names":["woman_getting_face_massage_tone3"],"surrogates":"💆🏽‍♀️"},{"names":["woman_getting_face_massage_tone4"],"surrogates":"💆🏾‍♀️"},{"names":["woman_getting_face_massage_tone5"],"surrogates":"💆🏿‍♀️"}]},{"names":["man_getting_face_massage"],"surrogates":"💆‍♂️","skins":[{"names":["man_getting_face_massage_tone1"],"surrogates":"💆🏻‍♂️"},{"names":["man_getting_face_massage_tone2"],"surrogates":"💆🏼‍♂️"},{"names":["man_getting_face_massage_tone3"],"surrogates":"💆🏽‍♂️"},{"names":["man_getting_face_massage_tone4"],"surrogates":"💆🏾‍♂️"},{"names":["man_getting_face_massage_tone5"],"surrogates":"💆🏿‍♂️"}]},{"names":["person_in_steamy_room"],"surrogates":"🧖","skins":[{"names":["person_in_steamy_room_tone1"],"surrogates":"🧖🏻"},{"names":["person_in_steamy_room_tone2"],"surrogates":"🧖🏼"},{"names":["person_in_steamy_room_tone3"],"surrogates":"🧖🏽"},{"names":["person_in_steamy_room_tone4"],"surrogates":"🧖🏾"},{"names":["person_in_steamy_room_tone5"],"surrogates":"🧖🏿"}]},{"names":["woman_in_steamy_room"],"surrogates":"🧖‍♀️","skins":[{"names":["woman_in_steamy_room_tone1"],"surrogates":"🧖🏻‍♀️"},{"names":["woman_in_steamy_room_tone2"],"surrogates":"🧖🏼‍♀️"},{"names":["woman_in_steamy_room_tone3"],"surrogates":"🧖🏽‍♀️"},{"names":["woman_in_steamy_room_tone4"],"surrogates":"🧖🏾‍♀️"},{"names":["woman_in_steamy_room_tone5"],"surrogates":"🧖🏿‍♀️"}]},{"names":["man_in_steamy_room"],"surrogates":"🧖‍♂️","skins":[{"names":["man_in_steamy_room_tone1"],"surrogates":"🧖🏻‍♂️"},{"names":["man_in_steamy_room_tone2"],"surrogates":"🧖🏼‍♂️"},{"names":["man_in_steamy_room_tone3"],"surrogates":"🧖🏽‍♂️"},{"names":["man_in_steamy_room_tone4"],"surrogates":"🧖🏾‍♂️"},{"names":["man_in_steamy_room_tone5"],"surrogates":"🧖🏿‍♂️"}]},{"names":["nail_care","nail_polish"],"surrogates":"💅","skins":[{"names":["nail_care_tone1"],"surrogates":"💅🏻"},{"names":["nail_care_tone2"],"surrogates":"💅🏼"},{"names":["nail_care_tone3"],"surrogates":"💅🏽"},{"names":["nail_care_tone4"],"surrogates":"💅🏾"},{"names":["nail_care_tone5"],"surrogates":"💅🏿"}]},{"names":["selfie"],"surrogates":"🤳","skins":[{"names":["selfie_tone1"],"surrogates":"🤳🏻"},{"names":["selfie_tone2"],"surrogates":"🤳🏼"},{"names":["selfie_tone3"],"surrogates":"🤳🏽"},{"names":["selfie_tone4"],"surrogates":"🤳🏾"},{"names":["selfie_tone5"],"surrogates":"🤳🏿"}]},{"names":["dancer","woman_dancing"],"surrogates":"💃","skins":[{"names":["dancer_tone1"],"surrogates":"💃🏻"},{"names":["dancer_tone2"],"surrogates":"💃🏼"},{"names":["dancer_tone3"],"surrogates":"💃🏽"},{"names":["dancer_tone4"],"surrogates":"💃🏾"},{"names":["dancer_tone5"],"surrogates":"💃🏿"}]},{"names":["man_dancing","male_dancer"],"surrogates":"🕺","skins":[{"names":["man_dancing_tone1"],"surrogates":"🕺🏻"},{"names":["man_dancing_tone2"],"surrogates":"🕺🏼"},{"names":["man_dancing_tone3"],"surrogates":"🕺🏽"},{"names":["man_dancing_tone4"],"surrogates":"🕺🏾"},{"names":["man_dancing_tone5"],"surrogates":"🕺🏿"}]},{"names":["people_with_bunny_ears_partying","dancers"],"surrogates":"👯"},{"names":["women_with_bunny_ears_partying"],"surrogates":"👯‍♀️"},{"names":["men_with_bunny_ears_partying"],"surrogates":"👯‍♂️"},{"names":["levitate","man_in_business_suit_levitating"],"surrogates":"🕴️","skins":[{"names":["levitate_tone1"],"surrogates":"🕴🏻"},{"names":["levitate_tone2"],"surrogates":"🕴🏼"},{"names":["levitate_tone3"],"surrogates":"🕴🏽"},{"names":["levitate_tone4"],"surrogates":"🕴🏾"},{"names":["levitate_tone5"],"surrogates":"🕴🏿"}]},{"names":["person_in_manual_wheelchair"],"surrogates":"🧑‍🦽","skins":[{"names":["person_in_manual_wheelchair_tone1"],"surrogates":"🧑🏻‍🦽"},{"names":["person_in_manual_wheelchair_tone2"],"surrogates":"🧑🏼‍🦽"},{"names":["person_in_manual_wheelchair_tone3"],"surrogates":"🧑🏽‍🦽"},{"names":["person_in_manual_wheelchair_tone4"],"surrogates":"🧑🏾‍🦽"},{"names":["person_in_manual_wheelchair_tone5"],"surrogates":"🧑🏿‍🦽"}]},{"names":["woman_in_manual_wheelchair"],"surrogates":"👩‍🦽","skins":[{"names":["woman_in_manual_wheelchair_tone1"],"surrogates":"👩🏻‍🦽"},{"names":["woman_in_manual_wheelchair_tone2"],"surrogates":"👩🏼‍🦽"},{"names":["woman_in_manual_wheelchair_tone3"],"surrogates":"👩🏽‍🦽"},{"names":["woman_in_manual_wheelchair_tone4"],"surrogates":"👩🏾‍🦽"},{"names":["woman_in_manual_wheelchair_tone5"],"surrogates":"👩🏿‍🦽"}]},{"names":["man_in_manual_wheelchair"],"surrogates":"👨‍🦽","skins":[{"names":["man_in_manual_wheelchair_tone1"],"surrogates":"👨🏻‍🦽"},{"names":["man_in_manual_wheelchair_tone2"],"surrogates":"👨🏼‍🦽"},{"names":["man_in_manual_wheelchair_tone3"],"surrogates":"👨🏽‍🦽"},{"names":["man_in_manual_wheelchair_tone4"],"surrogates":"👨🏾‍🦽"},{"names":["man_in_manual_wheelchair_tone5"],"surrogates":"👨🏿‍🦽"}]},{"names":["person_in_motorized_wheelchair"],"surrogates":"🧑‍🦼","skins":[{"names":["person_in_motorized_wheelchair_tone1"],"surrogates":"🧑🏻‍🦼"},{"names":["person_in_motorized_wheelchair_tone2"],"surrogates":"🧑🏼‍🦼"},{"names":["person_in_motorized_wheelchair_tone3"],"surrogates":"🧑🏽‍🦼"},{"names":["person_in_motorized_wheelchair_tone4"],"surrogates":"🧑🏾‍🦼"},{"names":["person_in_motorized_wheelchair_tone5"],"surrogates":"🧑🏿‍🦼"}]},{"names":["woman_in_motorized_wheelchair"],"surrogates":"👩‍🦼","skins":[{"names":["woman_in_motorized_wheelchair_tone1"],"surrogates":"👩🏻‍🦼"},{"names":["woman_in_motorized_wheelchair_tone2"],"surrogates":"👩🏼‍🦼"},{"names":["woman_in_motorized_wheelchair_tone3"],"surrogates":"👩🏽‍🦼"},{"names":["woman_in_motorized_wheelchair_tone4"],"surrogates":"👩🏾‍🦼"},{"names":["woman_in_motorized_wheelchair_tone5"],"surrogates":"👩🏿‍🦼"}]},{"names":["man_in_motorized_wheelchair"],"surrogates":"👨‍🦼","skins":[{"names":["man_in_motorized_wheelchair_tone1"],"surrogates":"👨🏻‍🦼"},{"names":["man_in_motorized_wheelchair_tone2"],"surrogates":"👨🏼‍🦼"},{"names":["man_in_motorized_wheelchair_tone3"],"surrogates":"👨🏽‍🦼"},{"names":["man_in_motorized_wheelchair_tone4"],"surrogates":"👨🏾‍🦼"},{"names":["man_in_motorized_wheelchair_tone5"],"surrogates":"👨🏿‍🦼"}]},{"names":["person_walking","walking"],"surrogates":"🚶","skins":[{"names":["person_walking_tone1"],"surrogates":"🚶🏻"},{"names":["person_walking_tone2"],"surrogates":"🚶🏼"},{"names":["person_walking_tone3"],"surrogates":"🚶🏽"},{"names":["person_walking_tone4"],"surrogates":"🚶🏾"},{"names":["person_walking_tone5"],"surrogates":"🚶🏿"}]},{"names":["woman_walking"],"surrogates":"🚶‍♀️","skins":[{"names":["woman_walking_tone1"],"surrogates":"🚶🏻‍♀️"},{"names":["woman_walking_tone2"],"surrogates":"🚶🏼‍♀️"},{"names":["woman_walking_tone3"],"surrogates":"🚶🏽‍♀️"},{"names":["woman_walking_tone4"],"surrogates":"🚶🏾‍♀️"},{"names":["woman_walking_tone5"],"surrogates":"🚶🏿‍♀️"}]},{"names":["man_walking"],"surrogates":"🚶‍♂️","skins":[{"names":["man_walking_tone1"],"surrogates":"🚶🏻‍♂️"},{"names":["man_walking_tone2"],"surrogates":"🚶🏼‍♂️"},{"names":["man_walking_tone3"],"surrogates":"🚶🏽‍♂️"},{"names":["man_walking_tone4"],"surrogates":"🚶🏾‍♂️"},{"names":["man_walking_tone5"],"surrogates":"🚶🏿‍♂️"}]},{"names":["person_with_probing_cane"],"surrogates":"🧑‍🦯","skins":[{"names":["person_with_probing_cane_tone1"],"surrogates":"🧑🏻‍🦯"},{"names":["person_with_probing_cane_tone2"],"surrogates":"🧑🏼‍🦯"},{"names":["person_with_probing_cane_tone3"],"surrogates":"🧑🏽‍🦯"},{"names":["person_with_probing_cane_tone4"],"surrogates":"🧑🏾‍🦯"},{"names":["person_with_probing_cane_tone5"],"surrogates":"🧑🏿‍🦯"}]},{"names":["woman_with_probing_cane"],"surrogates":"👩‍🦯","skins":[{"names":["woman_with_probing_cane_tone1"],"surrogates":"👩🏻‍🦯"},{"names":["woman_with_probing_cane_tone2"],"surrogates":"👩🏼‍🦯"},{"names":["woman_with_probing_cane_tone3"],"surrogates":"👩🏽‍🦯"},{"names":["woman_with_probing_cane_tone4"],"surrogates":"👩🏾‍🦯"},{"names":["woman_with_probing_cane_tone5"],"surrogates":"👩🏿‍🦯"}]},{"names":["man_with_probing_cane"],"surrogates":"👨‍🦯","skins":[{"names":["man_with_probing_cane_tone1"],"surrogates":"👨🏻‍🦯"},{"names":["man_with_probing_cane_tone2"],"surrogates":"👨🏼‍🦯"},{"names":["man_with_probing_cane_tone3"],"surrogates":"👨🏽‍🦯"},{"names":["man_with_probing_cane_tone4"],"surrogates":"👨🏾‍🦯"},{"names":["man_with_probing_cane_tone5"],"surrogates":"👨🏿‍🦯"}]},{"names":["person_kneeling"],"surrogates":"🧎","skins":[{"names":["person_kneeling_tone1"],"surrogates":"🧎🏻"},{"names":["person_kneeling_tone2"],"surrogates":"🧎🏼"},{"names":["person_kneeling_tone3"],"surrogates":"🧎🏽"},{"names":["person_kneeling_tone4"],"surrogates":"🧎🏾"},{"names":["person_kneeling_tone5"],"surrogates":"🧎🏿"}]},{"names":["woman_kneeling"],"surrogates":"🧎‍♀️","skins":[{"names":["woman_kneeling_tone1"],"surrogates":"🧎🏻‍♀️"},{"names":["woman_kneeling_tone2"],"surrogates":"🧎🏼‍♀️"},{"names":["woman_kneeling_tone3"],"surrogates":"🧎🏽‍♀️"},{"names":["woman_kneeling_tone4"],"surrogates":"🧎🏾‍♀️"},{"names":["woman_kneeling_tone5"],"surrogates":"🧎🏿‍♀️"}]},{"names":["man_kneeling"],"surrogates":"🧎‍♂️","skins":[{"names":["man_kneeling_tone1"],"surrogates":"🧎🏻‍♂️"},{"names":["man_kneeling_tone2"],"surrogates":"🧎🏼‍♂️"},{"names":["man_kneeling_tone3"],"surrogates":"🧎🏽‍♂️"},{"names":["man_kneeling_tone4"],"surrogates":"🧎🏾‍♂️"},{"names":["man_kneeling_tone5"],"surrogates":"🧎🏿‍♂️"}]},{"names":["person_running","runner"],"surrogates":"🏃","skins":[{"names":["person_running_tone1"],"surrogates":"🏃🏻"},{"names":["person_running_tone2"],"surrogates":"🏃🏼"},{"names":["person_running_tone3"],"surrogates":"🏃🏽"},{"names":["person_running_tone4"],"surrogates":"🏃🏾"},{"names":["person_running_tone5"],"surrogates":"🏃🏿"}]},{"names":["woman_running"],"surrogates":"🏃‍♀️","skins":[{"names":["woman_running_tone1"],"surrogates":"🏃🏻‍♀️"},{"names":["woman_running_tone2"],"surrogates":"🏃🏼‍♀️"},{"names":["woman_running_tone3"],"surrogates":"🏃🏽‍♀️"},{"names":["woman_running_tone4"],"surrogates":"🏃🏾‍♀️"},{"names":["woman_running_tone5"],"surrogates":"🏃🏿‍♀️"}]},{"names":["man_running"],"surrogates":"🏃‍♂️","skins":[{"names":["man_running_tone1"],"surrogates":"🏃🏻‍♂️"},{"names":["man_running_tone2"],"surrogates":"🏃🏼‍♂️"},{"names":["man_running_tone3"],"surrogates":"🏃🏽‍♂️"},{"names":["man_running_tone4"],"surrogates":"🏃🏾‍♂️"},{"names":["man_running_tone5"],"surrogates":"🏃🏿‍♂️"}]},{"names":["person_standing"],"surrogates":"🧍","skins":[{"names":["person_standing_tone1"],"surrogates":"🧍🏻"},{"names":["person_standing_tone2"],"surrogates":"🧍🏼"},{"names":["person_standing_tone3"],"surrogates":"🧍🏽"},{"names":["person_standing_tone4"],"surrogates":"🧍🏾"},{"names":["person_standing_tone5"],"surrogates":"🧍🏿"}]},{"names":["woman_standing"],"surrogates":"🧍‍♀️","skins":[{"names":["woman_standing_tone1"],"surrogates":"🧍🏻‍♀️"},{"names":["woman_standing_tone2"],"surrogates":"🧍🏼‍♀️"},{"names":["woman_standing_tone3"],"surrogates":"🧍🏽‍♀️"},{"names":["woman_standing_tone4"],"surrogates":"🧍🏾‍♀️"},{"names":["woman_standing_tone5"],"surrogates":"🧍🏿‍♀️"}]},{"names":["man_standing"],"surrogates":"🧍‍♂️","skins":[{"names":["man_standing_tone1"],"surrogates":"🧍🏻‍♂️"},{"names":["man_standing_tone2"],"surrogates":"🧍🏼‍♂️"},{"names":["man_standing_tone3"],"surrogates":"🧍🏽‍♂️"},{"names":["man_standing_tone4"],"surrogates":"🧍🏾‍♂️"},{"names":["man_standing_tone5"],"surrogates":"🧍🏿‍♂️"}]},{"names":["people_holding_hands"],"surrogates":"🧑‍🤝‍🧑","skins":[{"names":["people_holding_hands_tone1"],"surrogates":"🧑🏻‍🤝‍🧑🏻"},{"names":["people_holding_hands_tone2"],"surrogates":"🧑🏻‍🤝‍🧑🏼"},{"names":["people_holding_hands_tone3"],"surrogates":"🧑🏻‍🤝‍🧑🏽"},{"names":["people_holding_hands_tone4"],"surrogates":"🧑🏻‍🤝‍🧑🏾"},{"names":["people_holding_hands_tone5"],"surrogates":"🧑🏻‍🤝‍🧑🏿"},{"names":["people_holding_hands_tone6"],"surrogates":"🧑🏼‍🤝‍🧑🏻"},{"names":["people_holding_hands_tone7"],"surrogates":"🧑🏼‍🤝‍🧑🏼"},{"names":["people_holding_hands_tone8"],"surrogates":"🧑🏼‍🤝‍🧑🏽"},{"names":["people_holding_hands_tone9"],"surrogates":"🧑🏼‍🤝‍🧑🏾"},{"names":["people_holding_hands_tone10"],"surrogates":"🧑🏼‍🤝‍🧑🏿"},{"names":["people_holding_hands_tone11"],"surrogates":"🧑🏽‍🤝‍🧑🏻"},{"names":["people_holding_hands_tone12"],"surrogates":"🧑🏽‍🤝‍🧑🏼"},{"names":["people_holding_hands_tone13"],"surrogates":"🧑🏽‍🤝‍🧑🏽"},{"names":["people_holding_hands_tone14"],"surrogates":"🧑🏽‍🤝‍🧑🏾"},{"names":["people_holding_hands_tone15"],"surrogates":"🧑🏽‍🤝‍🧑🏿"},{"names":["people_holding_hands_tone16"],"surrogates":"🧑🏾‍🤝‍🧑🏻"},{"names":["people_holding_hands_tone17"],"surrogates":"🧑🏾‍🤝‍🧑🏼"},{"names":["people_holding_hands_tone18"],"surrogates":"🧑🏾‍🤝‍🧑🏽"},{"names":["people_holding_hands_tone19"],"surrogates":"🧑🏾‍🤝‍🧑🏾"},{"names":["people_holding_hands_tone20"],"surrogates":"🧑🏾‍🤝‍🧑🏿"},{"names":["people_holding_hands_tone21"],"surrogates":"🧑🏿‍🤝‍🧑🏻"},{"names":["people_holding_hands_tone22"],"surrogates":"🧑🏿‍🤝‍🧑🏼"},{"names":["people_holding_hands_tone23"],"surrogates":"🧑🏿‍🤝‍🧑🏽"},{"names":["people_holding_hands_tone24"],"surrogates":"🧑🏿‍🤝‍🧑🏾"},{"names":["people_holding_hands_tone25"],"surrogates":"🧑🏿‍🤝‍🧑🏿"}]},{"names":["couple"],"surrogates":"👫","skins":[{"names":["couple_tone1"],"surrogates":"👫🏻"},{"names":["couple_tone2"],"surrogates":"👫🏼"},{"names":["couple_tone3"],"surrogates":"👫🏽"},{"names":["couple_tone4"],"surrogates":"👫🏾"},{"names":["couple_tone5"],"surrogates":"👫🏿"},{"names":["couple_tone6"],"surrogates":"👩🏻‍🤝‍👨🏼"},{"names":["couple_tone7"],"surrogates":"👩🏻‍🤝‍👨🏽"},{"names":["couple_tone8"],"surrogates":"👩🏻‍🤝‍👨🏾"},{"names":["couple_tone9"],"surrogates":"👩🏻‍🤝‍👨🏿"},{"names":["couple_tone10"],"surrogates":"👩🏼‍🤝‍👨🏻"},{"names":["couple_tone11"],"surrogates":"👩🏼‍🤝‍👨🏽"},{"names":["couple_tone12"],"surrogates":"👩🏼‍🤝‍👨🏾"},{"names":["couple_tone13"],"surrogates":"👩🏼‍🤝‍👨🏿"},{"names":["couple_tone14"],"surrogates":"👩🏽‍🤝‍👨🏻"},{"names":["couple_tone15"],"surrogates":"👩🏽‍🤝‍👨🏼"},{"names":["couple_tone16"],"surrogates":"👩🏽‍🤝‍👨🏾"},{"names":["couple_tone17"],"surrogates":"👩🏽‍🤝‍👨🏿"},{"names":["couple_tone18"],"surrogates":"👩🏾‍🤝‍👨🏻"},{"names":["couple_tone19"],"surrogates":"👩🏾‍🤝‍👨🏼"},{"names":["couple_tone20"],"surrogates":"👩🏾‍🤝‍👨🏽"},{"names":["couple_tone21"],"surrogates":"👩🏾‍🤝‍👨🏿"},{"names":["couple_tone22"],"surrogates":"👩🏿‍🤝‍👨🏻"},{"names":["couple_tone23"],"surrogates":"👩🏿‍🤝‍👨🏼"},{"names":["couple_tone24"],"surrogates":"👩🏿‍🤝‍👨🏽"},{"names":["couple_tone25"],"surrogates":"👩🏿‍🤝‍👨🏾"}]},{"names":["two_women_holding_hands"],"surrogates":"👭","skins":[{"names":["two_women_holding_hands_tone1"],"surrogates":"👭🏻"},{"names":["two_women_holding_hands_tone2"],"surrogates":"👭🏼"},{"names":["two_women_holding_hands_tone3"],"surrogates":"👭🏽"},{"names":["two_women_holding_hands_tone4"],"surrogates":"👭🏾"},{"names":["two_women_holding_hands_tone5"],"surrogates":"👭🏿"},{"names":["two_women_holding_hands_tone6"],"surrogates":"👩🏻‍🤝‍👩🏼"},{"names":["two_women_holding_hands_tone7"],"surrogates":"👩🏻‍🤝‍👩🏽"},{"names":["two_women_holding_hands_tone8"],"surrogates":"👩🏻‍🤝‍👩🏾"},{"names":["two_women_holding_hands_tone9"],"surrogates":"👩🏻‍🤝‍👩🏿"},{"names":["two_women_holding_hands_tone10"],"surrogates":"👩🏼‍🤝‍👩🏻"},{"names":["two_women_holding_hands_tone11"],"surrogates":"👩🏼‍🤝‍👩🏽"},{"names":["two_women_holding_hands_tone12"],"surrogates":"👩🏼‍🤝‍👩🏾"},{"names":["two_women_holding_hands_tone13"],"surrogates":"👩🏼‍🤝‍👩🏿"},{"names":["two_women_holding_hands_tone14"],"surrogates":"👩🏽‍🤝‍👩🏻"},{"names":["two_women_holding_hands_tone15"],"surrogates":"👩🏽‍🤝‍👩🏼"},{"names":["two_women_holding_hands_tone16"],"surrogates":"👩🏽‍🤝‍👩🏾"},{"names":["two_women_holding_hands_tone17"],"surrogates":"👩🏽‍🤝‍👩🏿"},{"names":["two_women_holding_hands_tone18"],"surrogates":"👩🏾‍🤝‍👩🏻"},{"names":["two_women_holding_hands_tone19"],"surrogates":"👩🏾‍🤝‍👩🏼"},{"names":["two_women_holding_hands_tone20"],"surrogates":"👩🏾‍🤝‍👩🏽"},{"names":["two_women_holding_hands_tone21"],"surrogates":"👩🏾‍🤝‍👩🏿"},{"names":["two_women_holding_hands_tone22"],"surrogates":"👩🏿‍🤝‍👩🏻"},{"names":["two_women_holding_hands_tone23"],"surrogates":"👩🏿‍🤝‍👩🏼"},{"names":["two_women_holding_hands_tone24"],"surrogates":"👩🏿‍🤝‍👩🏽"},{"names":["two_women_holding_hands_tone25"],"surrogates":"👩🏿‍🤝‍👩🏾"}]},{"names":["two_men_holding_hands"],"surrogates":"👬","skins":[{"names":["two_men_holding_hands_tone1"],"surrogates":"👬🏻"},{"names":["two_men_holding_hands_tone2"],"surrogates":"👬🏼"},{"names":["two_men_holding_hands_tone3"],"surrogates":"👬🏽"},{"names":["two_men_holding_hands_tone4"],"surrogates":"👬🏾"},{"names":["two_men_holding_hands_tone5"],"surrogates":"👬🏿"},{"names":["two_men_holding_hands_tone6"],"surrogates":"👨🏻‍🤝‍👨🏼"},{"names":["two_men_holding_hands_tone7"],"surrogates":"👨🏻‍🤝‍👨🏽"},{"names":["two_men_holding_hands_tone8"],"surrogates":"👨🏻‍🤝‍👨🏾"},{"names":["two_men_holding_hands_tone9"],"surrogates":"👨🏻‍🤝‍👨🏿"},{"names":["two_men_holding_hands_tone10"],"surrogates":"👨🏼‍🤝‍👨🏻"},{"names":["two_men_holding_hands_tone11"],"surrogates":"👨🏼‍🤝‍👨🏽"},{"names":["two_men_holding_hands_tone12"],"surrogates":"👨🏼‍🤝‍👨🏾"},{"names":["two_men_holding_hands_tone13"],"surrogates":"👨🏼‍🤝‍👨🏿"},{"names":["two_men_holding_hands_tone14"],"surrogates":"👨🏽‍🤝‍👨🏻"},{"names":["two_men_holding_hands_tone15"],"surrogates":"👨🏽‍🤝‍👨🏼"},{"names":["two_men_holding_hands_tone16"],"surrogates":"👨🏽‍🤝‍👨🏾"},{"names":["two_men_holding_hands_tone17"],"surrogates":"👨🏽‍🤝‍👨🏿"},{"names":["two_men_holding_hands_tone18"],"surrogates":"👨🏾‍🤝‍👨🏻"},{"names":["two_men_holding_hands_tone19"],"surrogates":"👨🏾‍🤝‍👨🏼"},{"names":["two_men_holding_hands_tone20"],"surrogates":"👨🏾‍🤝‍👨🏽"},{"names":["two_men_holding_hands_tone21"],"surrogates":"👨🏾‍🤝‍👨🏿"},{"names":["two_men_holding_hands_tone22"],"surrogates":"👨🏿‍🤝‍👨🏻"},{"names":["two_men_holding_hands_tone23"],"surrogates":"👨🏿‍🤝‍👨🏼"},{"names":["two_men_holding_hands_tone24"],"surrogates":"👨🏿‍🤝‍👨🏽"},{"names":["two_men_holding_hands_tone25"],"surrogates":"👨🏿‍🤝‍👨🏾"}]},{"names":["couple_with_heart"],"surrogates":"💑","skins":[{"names":["couple_with_heart_tone1"],"surrogates":"💑🏻"},{"names":["couple_with_heart_tone2"],"surrogates":"💑🏼"},{"names":["couple_with_heart_tone3"],"surrogates":"💑🏽"},{"names":["couple_with_heart_tone4"],"surrogates":"💑🏾"},{"names":["couple_with_heart_tone5"],"surrogates":"💑🏿"},{"names":["couple_with_heart_tone6"],"surrogates":"🧑🏻‍❤️‍🧑🏼"},{"names":["couple_with_heart_tone7"],"surrogates":"🧑🏻‍❤️‍🧑🏽"},{"names":["couple_with_heart_tone8"],"surrogates":"🧑🏻‍❤️‍🧑🏾"},{"names":["couple_with_heart_tone9"],"surrogates":"🧑🏻‍❤️‍🧑🏿"},{"names":["couple_with_heart_tone10"],"surrogates":"🧑🏼‍❤️‍🧑🏻"},{"names":["couple_with_heart_tone11"],"surrogates":"🧑🏼‍❤️‍🧑🏽"},{"names":["couple_with_heart_tone12"],"surrogates":"🧑🏼‍❤️‍🧑🏾"},{"names":["couple_with_heart_tone13"],"surrogates":"🧑🏼‍❤️‍🧑🏿"},{"names":["couple_with_heart_tone14"],"surrogates":"🧑🏽‍❤️‍🧑🏻"},{"names":["couple_with_heart_tone15"],"surrogates":"🧑🏽‍❤️‍🧑🏼"},{"names":["couple_with_heart_tone16"],"surrogates":"🧑🏽‍❤️‍🧑🏾"},{"names":["couple_with_heart_tone17"],"surrogates":"🧑🏽‍❤️‍🧑🏿"},{"names":["couple_with_heart_tone18"],"surrogates":"🧑🏾‍❤️‍🧑🏻"},{"names":["couple_with_heart_tone19"],"surrogates":"🧑🏾‍❤️‍🧑🏼"},{"names":["couple_with_heart_tone20"],"surrogates":"🧑🏾‍❤️‍🧑🏽"},{"names":["couple_with_heart_tone21"],"surrogates":"🧑🏾‍❤️‍🧑🏿"},{"names":["couple_with_heart_tone22"],"surrogates":"🧑🏿‍❤️‍🧑🏻"},{"names":["couple_with_heart_tone23"],"surrogates":"🧑🏿‍❤️‍🧑🏼"},{"names":["couple_with_heart_tone24"],"surrogates":"🧑🏿‍❤️‍🧑🏽"},{"names":["couple_with_heart_tone25"],"surrogates":"🧑🏿‍❤️‍🧑🏾"}]},{"names":["couple_with_heart_woman_man"],"surrogates":"👩‍❤️‍👨","skins":[{"names":["couple_with_heart_woman_man_tone1"],"surrogates":"👩🏻‍❤️‍👨🏻"},{"names":["couple_with_heart_woman_man_tone2"],"surrogates":"👩🏻‍❤️‍👨🏼"},{"names":["couple_with_heart_woman_man_tone3"],"surrogates":"👩🏻‍❤️‍👨🏽"},{"names":["couple_with_heart_woman_man_tone4"],"surrogates":"👩🏻‍❤️‍👨🏾"},{"names":["couple_with_heart_woman_man_tone5"],"surrogates":"👩🏻‍❤️‍👨🏿"},{"names":["couple_with_heart_woman_man_tone6"],"surrogates":"👩🏼‍❤️‍👨🏻"},{"names":["couple_with_heart_woman_man_tone7"],"surrogates":"👩🏼‍❤️‍👨🏼"},{"names":["couple_with_heart_woman_man_tone8"],"surrogates":"👩🏼‍❤️‍👨🏽"},{"names":["couple_with_heart_woman_man_tone9"],"surrogates":"👩🏼‍❤️‍👨🏾"},{"names":["couple_with_heart_woman_man_tone10"],"surrogates":"👩🏼‍❤️‍👨🏿"},{"names":["couple_with_heart_woman_man_tone11"],"surrogates":"👩🏽‍❤️‍👨🏻"},{"names":["couple_with_heart_woman_man_tone12"],"surrogates":"👩🏽‍❤️‍👨🏼"},{"names":["couple_with_heart_woman_man_tone13"],"surrogates":"👩🏽‍❤️‍👨🏽"},{"names":["couple_with_heart_woman_man_tone14"],"surrogates":"👩🏽‍❤️‍👨🏾"},{"names":["couple_with_heart_woman_man_tone15"],"surrogates":"👩🏽‍❤️‍👨🏿"},{"names":["couple_with_heart_woman_man_tone16"],"surrogates":"👩🏾‍❤️‍👨🏻"},{"names":["couple_with_heart_woman_man_tone17"],"surrogates":"👩🏾‍❤️‍👨🏼"},{"names":["couple_with_heart_woman_man_tone18"],"surrogates":"👩🏾‍❤️‍👨🏽"},{"names":["couple_with_heart_woman_man_tone19"],"surrogates":"👩🏾‍❤️‍👨🏾"},{"names":["couple_with_heart_woman_man_tone20"],"surrogates":"👩🏾‍❤️‍👨🏿"},{"names":["couple_with_heart_woman_man_tone21"],"surrogates":"👩🏿‍❤️‍👨🏻"},{"names":["couple_with_heart_woman_man_tone22"],"surrogates":"👩🏿‍❤️‍👨🏼"},{"names":["couple_with_heart_woman_man_tone23"],"surrogates":"👩🏿‍❤️‍👨🏽"},{"names":["couple_with_heart_woman_man_tone24"],"surrogates":"👩🏿‍❤️‍👨🏾"},{"names":["couple_with_heart_woman_man_tone25"],"surrogates":"👩🏿‍❤️‍👨🏿"}]},{"names":["couple_ww","couple_with_heart_ww"],"surrogates":"👩‍❤️‍👩","skins":[{"names":["couple_ww_tone1"],"surrogates":"👩🏻‍❤️‍👩🏻"},{"names":["couple_ww_tone2"],"surrogates":"👩🏻‍❤️‍👩🏼"},{"names":["couple_ww_tone3"],"surrogates":"👩🏻‍❤️‍👩🏽"},{"names":["couple_ww_tone4"],"surrogates":"👩🏻‍❤️‍👩🏾"},{"names":["couple_ww_tone5"],"surrogates":"👩🏻‍❤️‍👩🏿"},{"names":["couple_ww_tone6"],"surrogates":"👩🏼‍❤️‍👩🏻"},{"names":["couple_ww_tone7"],"surrogates":"👩🏼‍❤️‍👩🏼"},{"names":["couple_ww_tone8"],"surrogates":"👩🏼‍❤️‍👩🏽"},{"names":["couple_ww_tone9"],"surrogates":"👩🏼‍❤️‍👩🏾"},{"names":["couple_ww_tone10"],"surrogates":"👩🏼‍❤️‍👩🏿"},{"names":["couple_ww_tone11"],"surrogates":"👩🏽‍❤️‍👩🏻"},{"names":["couple_ww_tone12"],"surrogates":"👩🏽‍❤️‍👩🏼"},{"names":["couple_ww_tone13"],"surrogates":"👩🏽‍❤️‍👩🏽"},{"names":["couple_ww_tone14"],"surrogates":"👩🏽‍❤️‍👩🏾"},{"names":["couple_ww_tone15"],"surrogates":"👩🏽‍❤️‍👩🏿"},{"names":["couple_ww_tone16"],"surrogates":"👩🏾‍❤️‍👩🏻"},{"names":["couple_ww_tone17"],"surrogates":"👩🏾‍❤️‍👩🏼"},{"names":["couple_ww_tone18"],"surrogates":"👩🏾‍❤️‍👩🏽"},{"names":["couple_ww_tone19"],"surrogates":"👩🏾‍❤️‍👩🏾"},{"names":["couple_ww_tone20"],"surrogates":"👩🏾‍❤️‍👩🏿"},{"names":["couple_ww_tone21"],"surrogates":"👩🏿‍❤️‍👩🏻"},{"names":["couple_ww_tone22"],"surrogates":"👩🏿‍❤️‍👩🏼"},{"names":["couple_ww_tone23"],"surrogates":"👩🏿‍❤️‍👩🏽"},{"names":["couple_ww_tone24"],"surrogates":"👩🏿‍❤️‍👩🏾"},{"names":["couple_ww_tone25"],"surrogates":"👩🏿‍❤️‍👩🏿"}]},{"names":["couple_mm","couple_with_heart_mm"],"surrogates":"👨‍❤️‍👨","skins":[{"names":["couple_mm_tone1"],"surrogates":"👨🏻‍❤️‍👨🏻"},{"names":["couple_mm_tone2"],"surrogates":"👨🏻‍❤️‍👨🏼"},{"names":["couple_mm_tone3"],"surrogates":"👨🏻‍❤️‍👨🏽"},{"names":["couple_mm_tone4"],"surrogates":"👨🏻‍❤️‍👨🏾"},{"names":["couple_mm_tone5"],"surrogates":"👨🏻‍❤️‍👨🏿"},{"names":["couple_mm_tone6"],"surrogates":"👨🏼‍❤️‍👨🏻"},{"names":["couple_mm_tone7"],"surrogates":"👨🏼‍❤️‍👨🏼"},{"names":["couple_mm_tone8"],"surrogates":"👨🏼‍❤️‍👨🏽"},{"names":["couple_mm_tone9"],"surrogates":"👨🏼‍❤️‍👨🏾"},{"names":["couple_mm_tone10"],"surrogates":"👨🏼‍❤️‍👨🏿"},{"names":["couple_mm_tone11"],"surrogates":"👨🏽‍❤️‍👨🏻"},{"names":["couple_mm_tone12"],"surrogates":"👨🏽‍❤️‍👨🏼"},{"names":["couple_mm_tone13"],"surrogates":"👨🏽‍❤️‍👨🏽"},{"names":["couple_mm_tone14"],"surrogates":"👨🏽‍❤️‍👨🏾"},{"names":["couple_mm_tone15"],"surrogates":"👨🏽‍❤️‍👨🏿"},{"names":["couple_mm_tone16"],"surrogates":"👨🏾‍❤️‍👨🏻"},{"names":["couple_mm_tone17"],"surrogates":"👨🏾‍❤️‍👨🏼"},{"names":["couple_mm_tone18"],"surrogates":"👨🏾‍❤️‍👨🏽"},{"names":["couple_mm_tone19"],"surrogates":"👨🏾‍❤️‍👨🏾"},{"names":["couple_mm_tone20"],"surrogates":"👨🏾‍❤️‍👨🏿"},{"names":["couple_mm_tone21"],"surrogates":"👨🏿‍❤️‍👨🏻"},{"names":["couple_mm_tone22"],"surrogates":"👨🏿‍❤️‍👨🏼"},{"names":["couple_mm_tone23"],"surrogates":"👨🏿‍❤️‍👨🏽"},{"names":["couple_mm_tone24"],"surrogates":"👨🏿‍❤️‍👨🏾"},{"names":["couple_mm_tone25"],"surrogates":"👨🏿‍❤️‍👨🏿"}]},{"names":["couplekiss"],"surrogates":"💏","skins":[{"names":["couplekiss_tone1"],"surrogates":"💏🏻"},{"names":["couplekiss_tone2"],"surrogates":"💏🏼"},{"names":["couplekiss_tone3"],"surrogates":"💏🏽"},{"names":["couplekiss_tone4"],"surrogates":"💏🏾"},{"names":["couplekiss_tone5"],"surrogates":"💏🏿"},{"names":["couplekiss_tone6"],"surrogates":"🧑🏻‍❤️‍💋‍🧑🏼"},{"names":["couplekiss_tone7"],"surrogates":"🧑🏻‍❤️‍💋‍🧑🏽"},{"names":["couplekiss_tone8"],"surrogates":"🧑🏻‍❤️‍💋‍🧑🏾"},{"names":["couplekiss_tone9"],"surrogates":"🧑🏻‍❤️‍💋‍🧑🏿"},{"names":["couplekiss_tone10"],"surrogates":"🧑🏼‍❤️‍💋‍🧑🏻"},{"names":["couplekiss_tone11"],"surrogates":"🧑🏼‍❤️‍💋‍🧑🏽"},{"names":["couplekiss_tone12"],"surrogates":"🧑🏼‍❤️‍💋‍🧑🏾"},{"names":["couplekiss_tone13"],"surrogates":"🧑🏼‍❤️‍💋‍🧑🏿"},{"names":["couplekiss_tone14"],"surrogates":"🧑🏽‍❤️‍💋‍🧑🏻"},{"names":["couplekiss_tone15"],"surrogates":"🧑🏽‍❤️‍💋‍🧑🏼"},{"names":["couplekiss_tone16"],"surrogates":"🧑🏽‍❤️‍💋‍🧑🏾"},{"names":["couplekiss_tone17"],"surrogates":"🧑🏽‍❤️‍💋‍🧑🏿"},{"names":["couplekiss_tone18"],"surrogates":"🧑🏾‍❤️‍💋‍🧑🏻"},{"names":["couplekiss_tone19"],"surrogates":"🧑🏾‍❤️‍💋‍🧑🏼"},{"names":["couplekiss_tone20"],"surrogates":"🧑🏾‍❤️‍💋‍🧑🏽"},{"names":["couplekiss_tone21"],"surrogates":"🧑🏾‍❤️‍💋‍🧑🏿"},{"names":["couplekiss_tone22"],"surrogates":"🧑🏿‍❤️‍💋‍🧑🏻"},{"names":["couplekiss_tone23"],"surrogates":"🧑🏿‍❤️‍💋‍🧑🏼"},{"names":["couplekiss_tone24"],"surrogates":"🧑🏿‍❤️‍💋‍🧑🏽"},{"names":["couplekiss_tone25"],"surrogates":"🧑🏿‍❤️‍💋‍🧑🏾"}]},{"names":["kiss_woman_man"],"surrogates":"👩‍❤️‍💋‍👨","skins":[{"names":["kiss_woman_man_tone1"],"surrogates":"👩🏻‍❤️‍💋‍👨🏻"},{"names":["kiss_woman_man_tone2"],"surrogates":"👩🏻‍❤️‍💋‍👨🏼"},{"names":["kiss_woman_man_tone3"],"surrogates":"👩🏻‍❤️‍💋‍👨🏽"},{"names":["kiss_woman_man_tone4"],"surrogates":"👩🏻‍❤️‍💋‍👨🏾"},{"names":["kiss_woman_man_tone5"],"surrogates":"👩🏻‍❤️‍💋‍👨🏿"},{"names":["kiss_woman_man_tone6"],"surrogates":"👩🏼‍❤️‍💋‍👨🏻"},{"names":["kiss_woman_man_tone7"],"surrogates":"👩🏼‍❤️‍💋‍👨🏼"},{"names":["kiss_woman_man_tone8"],"surrogates":"👩🏼‍❤️‍💋‍👨🏽"},{"names":["kiss_woman_man_tone9"],"surrogates":"👩🏼‍❤️‍💋‍👨🏾"},{"names":["kiss_woman_man_tone10"],"surrogates":"👩🏼‍❤️‍💋‍👨🏿"},{"names":["kiss_woman_man_tone11"],"surrogates":"👩🏽‍❤️‍💋‍👨🏻"},{"names":["kiss_woman_man_tone12"],"surrogates":"👩🏽‍❤️‍💋‍👨🏼"},{"names":["kiss_woman_man_tone13"],"surrogates":"👩🏽‍❤️‍💋‍👨🏽"},{"names":["kiss_woman_man_tone14"],"surrogates":"👩🏽‍❤️‍💋‍👨🏾"},{"names":["kiss_woman_man_tone15"],"surrogates":"👩🏽‍❤️‍💋‍👨🏿"},{"names":["kiss_woman_man_tone16"],"surrogates":"👩🏾‍❤️‍💋‍👨🏻"},{"names":["kiss_woman_man_tone17"],"surrogates":"👩🏾‍❤️‍💋‍👨🏼"},{"names":["kiss_woman_man_tone18"],"surrogates":"👩🏾‍❤️‍💋‍👨🏽"},{"names":["kiss_woman_man_tone19"],"surrogates":"👩🏾‍❤️‍💋‍👨🏾"},{"names":["kiss_woman_man_tone20"],"surrogates":"👩🏾‍❤️‍💋‍👨🏿"},{"names":["kiss_woman_man_tone21"],"surrogates":"👩🏿‍❤️‍💋‍👨🏻"},{"names":["kiss_woman_man_tone22"],"surrogates":"👩🏿‍❤️‍💋‍👨🏼"},{"names":["kiss_woman_man_tone23"],"surrogates":"👩🏿‍❤️‍💋‍👨🏽"},{"names":["kiss_woman_man_tone24"],"surrogates":"👩🏿‍❤️‍💋‍👨🏾"},{"names":["kiss_woman_man_tone25"],"surrogates":"👩🏿‍❤️‍💋‍👨🏿"}]},{"names":["kiss_ww","couplekiss_ww"],"surrogates":"👩‍❤️‍💋‍👩","skins":[{"names":["kiss_ww_tone1"],"surrogates":"👩🏻‍❤️‍💋‍👩🏻"},{"names":["kiss_ww_tone2"],"surrogates":"👩🏻‍❤️‍💋‍👩🏼"},{"names":["kiss_ww_tone3"],"surrogates":"👩🏻‍❤️‍💋‍👩🏽"},{"names":["kiss_ww_tone4"],"surrogates":"👩🏻‍❤️‍💋‍👩🏾"},{"names":["kiss_ww_tone5"],"surrogates":"👩🏻‍❤️‍💋‍👩🏿"},{"names":["kiss_ww_tone6"],"surrogates":"👩🏼‍❤️‍💋‍👩🏻"},{"names":["kiss_ww_tone7"],"surrogates":"👩🏼‍❤️‍💋‍👩🏼"},{"names":["kiss_ww_tone8"],"surrogates":"👩🏼‍❤️‍💋‍👩🏽"},{"names":["kiss_ww_tone9"],"surrogates":"👩🏼‍❤️‍💋‍👩🏾"},{"names":["kiss_ww_tone10"],"surrogates":"👩🏼‍❤️‍💋‍👩🏿"},{"names":["kiss_ww_tone11"],"surrogates":"👩🏽‍❤️‍💋‍👩🏻"},{"names":["kiss_ww_tone12"],"surrogates":"👩🏽‍❤️‍💋‍👩🏼"},{"names":["kiss_ww_tone13"],"surrogates":"👩🏽‍❤️‍💋‍👩🏽"},{"names":["kiss_ww_tone14"],"surrogates":"👩🏽‍❤️‍💋‍👩🏾"},{"names":["kiss_ww_tone15"],"surrogates":"👩🏽‍❤️‍💋‍👩🏿"},{"names":["kiss_ww_tone16"],"surrogates":"👩🏾‍❤️‍💋‍👩🏻"},{"names":["kiss_ww_tone17"],"surrogates":"👩🏾‍❤️‍💋‍👩🏼"},{"names":["kiss_ww_tone18"],"surrogates":"👩🏾‍❤️‍💋‍👩🏽"},{"names":["kiss_ww_tone19"],"surrogates":"👩🏾‍❤️‍💋‍👩🏾"},{"names":["kiss_ww_tone20"],"surrogates":"👩🏾‍❤️‍💋‍👩🏿"},{"names":["kiss_ww_tone21"],"surrogates":"👩🏿‍❤️‍💋‍👩🏻"},{"names":["kiss_ww_tone22"],"surrogates":"👩🏿‍❤️‍💋‍👩🏼"},{"names":["kiss_ww_tone23"],"surrogates":"👩🏿‍❤️‍💋‍👩🏽"},{"names":["kiss_ww_tone24"],"surrogates":"👩🏿‍❤️‍💋‍👩🏾"},{"names":["kiss_ww_tone25"],"surrogates":"👩🏿‍❤️‍💋‍👩🏿"}]},{"names":["kiss_mm","couplekiss_mm","kiss_man_man"],"surrogates":"👨‍❤️‍💋‍👨","skins":[{"names":["kiss_mm_tone1"],"surrogates":"👨🏻‍❤️‍💋‍👨🏻"},{"names":["kiss_mm_tone2"],"surrogates":"👨🏻‍❤️‍💋‍👨🏼"},{"names":["kiss_mm_tone3"],"surrogates":"👨🏻‍❤️‍💋‍👨🏽"},{"names":["kiss_mm_tone4"],"surrogates":"👨🏻‍❤️‍💋‍👨🏾"},{"names":["kiss_mm_tone5"],"surrogates":"👨🏻‍❤️‍💋‍👨🏿"},{"names":["kiss_mm_tone6"],"surrogates":"👨🏼‍❤️‍💋‍👨🏻"},{"names":["kiss_mm_tone7"],"surrogates":"👨🏼‍❤️‍💋‍👨🏼"},{"names":["kiss_mm_tone8"],"surrogates":"👨🏼‍❤️‍💋‍👨🏽"},{"names":["kiss_mm_tone9"],"surrogates":"👨🏼‍❤️‍💋‍👨🏾"},{"names":["kiss_mm_tone10"],"surrogates":"👨🏼‍❤️‍💋‍👨🏿"},{"names":["kiss_mm_tone11"],"surrogates":"👨🏽‍❤️‍💋‍👨🏻"},{"names":["kiss_mm_tone12"],"surrogates":"👨🏽‍❤️‍💋‍👨🏼"},{"names":["kiss_mm_tone13"],"surrogates":"👨🏽‍❤️‍💋‍👨🏽"},{"names":["kiss_mm_tone14"],"surrogates":"👨🏽‍❤️‍💋‍👨🏾"},{"names":["kiss_mm_tone15"],"surrogates":"👨🏽‍❤️‍💋‍👨🏿"},{"names":["kiss_mm_tone16"],"surrogates":"👨🏾‍❤️‍💋‍👨🏻"},{"names":["kiss_mm_tone17"],"surrogates":"👨🏾‍❤️‍💋‍👨🏼"},{"names":["kiss_mm_tone18"],"surrogates":"👨🏾‍❤️‍💋‍👨🏽"},{"names":["kiss_mm_tone19"],"surrogates":"👨🏾‍❤️‍💋‍👨🏾"},{"names":["kiss_mm_tone20"],"surrogates":"👨🏾‍❤️‍💋‍👨🏿"},{"names":["kiss_mm_tone21"],"surrogates":"👨🏿‍❤️‍💋‍👨🏻"},{"names":["kiss_mm_tone22"],"surrogates":"👨🏿‍❤️‍💋‍👨🏼"},{"names":["kiss_mm_tone23"],"surrogates":"👨🏿‍❤️‍💋‍👨🏽"},{"names":["kiss_mm_tone24"],"surrogates":"👨🏿‍❤️‍💋‍👨🏾"},{"names":["kiss_mm_tone25"],"surrogates":"👨🏿‍❤️‍💋‍👨🏿"}]},{"names":["family"],"surrogates":"👪"},{"names":["family_man_woman_boy"],"surrogates":"👨‍👩‍👦"},{"names":["family_mwg"],"surrogates":"👨‍👩‍👧"},{"names":["family_mwgb"],"surrogates":"👨‍👩‍👧‍👦"},{"names":["family_mwbb"],"surrogates":"👨‍👩‍👦‍👦"},{"names":["family_mwgg"],"surrogates":"👨‍👩‍👧‍👧"},{"names":["family_wwb"],"surrogates":"👩‍👩‍👦"},{"names":["family_wwg"],"surrogates":"👩‍👩‍👧"},{"names":["family_wwgb"],"surrogates":"👩‍👩‍👧‍👦"},{"names":["family_wwbb"],"surrogates":"👩‍👩‍👦‍👦"},{"names":["family_wwgg"],"surrogates":"👩‍👩‍👧‍👧"},{"names":["family_mmb"],"surrogates":"👨‍👨‍👦"},{"names":["family_mmg"],"surrogates":"👨‍👨‍👧"},{"names":["family_mmgb"],"surrogates":"👨‍👨‍👧‍👦"},{"names":["family_mmbb"],"surrogates":"👨‍👨‍👦‍👦"},{"names":["family_mmgg"],"surrogates":"👨‍👨‍👧‍👧"},{"names":["family_woman_boy"],"surrogates":"👩‍👦"},{"names":["family_woman_girl"],"surrogates":"👩‍👧"},{"names":["family_woman_girl_boy"],"surrogates":"👩‍👧‍👦"},{"names":["family_woman_boy_boy"],"surrogates":"👩‍👦‍👦"},{"names":["family_woman_girl_girl"],"surrogates":"👩‍👧‍👧"},{"names":["family_man_boy"],"surrogates":"👨‍👦"},{"names":["family_man_girl"],"surrogates":"👨‍👧"},{"names":["family_man_girl_boy"],"surrogates":"👨‍👧‍👦"},{"names":["family_man_boy_boy"],"surrogates":"👨‍👦‍👦"},{"names":["family_man_girl_girl"],"surrogates":"👨‍👧‍👧"},{"names":["knot"],"surrogates":"🪢"},{"names":["yarn"],"surrogates":"🧶"},{"names":["thread"],"surrogates":"🧵"},{"names":["sewing_needle"],"surrogates":"🪡"},{"names":["coat"],"surrogates":"🧥"},{"names":["lab_coat"],"surrogates":"🥼"},{"names":["safety_vest"],"surrogates":"🦺"},{"names":["womans_clothes"],"surrogates":"👚"},{"names":["shirt","t_shirt"],"surrogates":"👕"},{"names":["jeans"],"surrogates":"👖"},{"names":["briefs"],"surrogates":"🩲"},{"names":["shorts"],"surrogates":"🩳"},{"names":["necktie"],"surrogates":"👔"},{"names":["dress"],"surrogates":"👗"},{"names":["bikini"],"surrogates":"👙"},{"names":["one_piece_swimsuit"],"surrogates":"🩱"},{"names":["kimono"],"surrogates":"👘"},{"names":["sari"],"surrogates":"🥻"},{"names":["thong_sandal"],"surrogates":"🩴"},{"names":["womans_flat_shoe","flat_shoe"],"surrogates":"🥿"},{"names":["high_heel"],"surrogates":"👠"},{"names":["sandal","womans_sandal"],"surrogates":"👡"},{"names":["boot","womans_boot"],"surrogates":"👢"},{"names":["mans_shoe"],"surrogates":"👞"},{"names":["athletic_shoe","running_shoe"],"surrogates":"👟"},{"names":["hiking_boot"],"surrogates":"🥾"},{"names":["socks"],"surrogates":"🧦"},{"names":["gloves"],"surrogates":"🧤"},{"names":["scarf"],"surrogates":"🧣"},{"names":["tophat","top_hat"],"surrogates":"🎩"},{"names":["billed_cap"],"surrogates":"🧢"},{"names":["womans_hat"],"surrogates":"👒"},{"names":["mortar_board"],"surrogates":"🎓"},{"names":["helmet_with_cross","helmet_with_white_cross"],"surrogates":"⛑️"},{"names":["military_helmet"],"surrogates":"🪖"},{"names":["crown"],"surrogates":"👑"},{"names":["ring"],"surrogates":"💍"},{"names":["pouch","clutch_bag"],"surrogates":"👝"},{"names":["purse"],"surrogates":"👛"},{"names":["handbag"],"surrogates":"👜"},{"names":["briefcase"],"surrogates":"💼"},{"names":["school_satchel","backpack"],"surrogates":"🎒"},{"names":["luggage"],"surrogates":"🧳"},{"names":["eyeglasses","glasses"],"surrogates":"👓"},{"names":["dark_sunglasses"],"surrogates":"🕶️"},{"names":["goggles"],"surrogates":"🥽"},{"names":["closed_umbrella"],"surrogates":"🌂"}],"nature":[{"names":["dog","dog_face"],"surrogates":"🐶"},{"names":["cat","cat_face"],"surrogates":"🐱"},{"names":["mouse","mouse_face"],"surrogates":"🐭"},{"names":["hamster"],"surrogates":"🐹"},{"names":["rabbit","rabbit_face"],"surrogates":"🐰"},{"names":["fox","fox_face"],"surrogates":"🦊"},{"names":["bear"],"surrogates":"🐻"},{"names":["panda_face","panda"],"surrogates":"🐼"},{"names":["polar_bear"],"surrogates":"🐻‍❄️"},{"names":["koala"],"surrogates":"🐨"},{"names":["tiger","tiger_face"],"surrogates":"🐯"},{"names":["lion_face","lion"],"surrogates":"🦁"},{"names":["cow","cow_face"],"surrogates":"🐮"},{"names":["pig","pig_face"],"surrogates":"🐷"},{"names":["pig_nose"],"surrogates":"🐽"},{"names":["frog"],"surrogates":"🐸"},{"names":["monkey_face"],"surrogates":"🐵"},{"names":["see_no_evil"],"surrogates":"🙈"},{"names":["hear_no_evil"],"surrogates":"🙉"},{"names":["speak_no_evil"],"surrogates":"🙊"},{"names":["monkey"],"surrogates":"🐒"},{"names":["chicken"],"surrogates":"🐔"},{"names":["penguin"],"surrogates":"🐧"},{"names":["bird"],"surrogates":"🐦"},{"names":["baby_chick"],"surrogates":"🐤"},{"names":["hatching_chick"],"surrogates":"🐣"},{"names":["hatched_chick"],"surrogates":"🐥"},{"names":["goose"],"surrogates":"🪿"},{"names":["duck"],"surrogates":"🦆"},{"names":["black_bird"],"surrogates":"🐦‍⬛"},{"names":["eagle"],"surrogates":"🦅"},{"names":["owl"],"surrogates":"🦉"},{"names":["bat"],"surrogates":"🦇"},{"names":["wolf"],"surrogates":"🐺"},{"names":["boar"],"surrogates":"🐗"},{"names":["horse","horse_face"],"surrogates":"🐴"},{"names":["unicorn","unicorn_face"],"surrogates":"🦄"},{"names":["moose"],"surrogates":"🫎"},{"names":["bee","honeybee"],"surrogates":"🐝"},{"names":["worm"],"surrogates":"🪱"},{"names":["bug"],"surrogates":"🐛"},{"names":["butterfly"],"surrogates":"🦋"},{"names":["snail"],"surrogates":"🐌"},{"names":["lady_beetle"],"surrogates":"🐞"},{"names":["ant"],"surrogates":"🐜"},{"names":["fly"],"surrogates":"🪰"},{"names":["beetle"],"surrogates":"🪲"},{"names":["cockroach"],"surrogates":"🪳"},{"names":["mosquito"],"surrogates":"🦟"},{"names":["cricket"],"surrogates":"🦗"},{"names":["spider"],"surrogates":"🕷️"},{"names":["spider_web"],"surrogates":"🕸️"},{"names":["scorpion"],"surrogates":"🦂"},{"names":["turtle"],"surrogates":"🐢"},{"names":["snake"],"surrogates":"🐍"},{"names":["lizard"],"surrogates":"🦎"},{"names":["t_rex"],"surrogates":"🦖"},{"names":["sauropod"],"surrogates":"🦕"},{"names":["octopus"],"surrogates":"🐙"},{"names":["squid"],"surrogates":"🦑"},{"names":["jellyfish"],"surrogates":"🪼"},{"names":["shrimp"],"surrogates":"🦐"},{"names":["lobster"],"surrogates":"🦞"},{"names":["crab"],"surrogates":"🦀"},{"names":["blowfish"],"surrogates":"🐡"},{"names":["tropical_fish"],"surrogates":"🐠"},{"names":["fish"],"surrogates":"🐟"},{"names":["dolphin"],"surrogates":"🐬"},{"names":["whale"],"surrogates":"🐳"},{"names":["whale2"],"surrogates":"🐋"},{"names":["shark"],"surrogates":"🦈"},{"names":["seal"],"surrogates":"🦭"},{"names":["crocodile"],"surrogates":"🐊"},{"names":["tiger2"],"surrogates":"🐅"},{"names":["leopard"],"surrogates":"🐆"},{"names":["zebra"],"surrogates":"🦓"},{"names":["gorilla"],"surrogates":"🦍"},{"names":["orangutan"],"surrogates":"🦧"},{"names":["mammoth"],"surrogates":"🦣"},{"names":["elephant"],"surrogates":"🐘"},{"names":["hippopotamus"],"surrogates":"🦛"},{"names":["rhino","rhinoceros"],"surrogates":"🦏"},{"names":["dromedary_camel"],"surrogates":"🐪"},{"names":["camel"],"surrogates":"🐫"},{"names":["giraffe"],"surrogates":"🦒"},{"names":["kangaroo"],"surrogates":"🦘"},{"names":["bison"],"surrogates":"🦬"},{"names":["water_buffalo"],"surrogates":"🐃"},{"names":["ox"],"surrogates":"🐂"},{"names":["cow2"],"surrogates":"🐄"},{"names":["donkey"],"surrogates":"🫏"},{"names":["racehorse"],"surrogates":"🐎"},{"names":["pig2"],"surrogates":"🐖"},{"names":["ram"],"surrogates":"🐏"},{"names":["sheep","ewe"],"surrogates":"🐑"},{"names":["llama"],"surrogates":"🦙"},{"names":["goat"],"surrogates":"🐐"},{"names":["deer"],"surrogates":"🦌"},{"names":["dog2"],"surrogates":"🐕"},{"names":["poodle"],"surrogates":"🐩"},{"names":["guide_dog"],"surrogates":"🦮"},{"names":["service_dog"],"surrogates":"🐕‍🦺"},{"names":["cat2"],"surrogates":"🐈"},{"names":["black_cat"],"surrogates":"🐈‍⬛"},{"names":["feather"],"surrogates":"🪶"},{"names":["wing"],"surrogates":"🪽"},{"names":["rooster"],"surrogates":"🐓"},{"names":["turkey"],"surrogates":"🦃"},{"names":["dodo"],"surrogates":"🦤"},{"names":["peacock"],"surrogates":"🦚"},{"names":["parrot"],"surrogates":"🦜"},{"names":["swan"],"surrogates":"🦢"},{"names":["flamingo"],"surrogates":"🦩"},{"names":["dove","dove_of_peace"],"surrogates":"🕊️"},{"names":["rabbit2"],"surrogates":"🐇"},{"names":["raccoon"],"surrogates":"🦝"},{"names":["skunk"],"surrogates":"🦨"},{"names":["badger"],"surrogates":"🦡"},{"names":["beaver"],"surrogates":"🦫"},{"names":["otter"],"surrogates":"🦦"},{"names":["sloth"],"surrogates":"🦥"},{"names":["mouse2"],"surrogates":"🐁"},{"names":["rat"],"surrogates":"🐀"},{"names":["chipmunk"],"surrogates":"🐿️"},{"names":["hedgehog"],"surrogates":"🦔"},{"names":["feet","paw_prints"],"surrogates":"🐾"},{"names":["dragon"],"surrogates":"🐉"},{"names":["dragon_face"],"surrogates":"🐲"},{"names":["cactus"],"surrogates":"🌵"},{"names":["christmas_tree"],"surrogates":"🎄"},{"names":["evergreen_tree"],"surrogates":"🌲"},{"names":["deciduous_tree"],"surrogates":"🌳"},{"names":["palm_tree"],"surrogates":"🌴"},{"names":["wood"],"surrogates":"🪵"},{"names":["seedling"],"surrogates":"🌱"},{"names":["herb"],"surrogates":"🌿"},{"names":["shamrock"],"surrogates":"☘️"},{"names":["four_leaf_clover"],"surrogates":"🍀"},{"names":["bamboo"],"surrogates":"🎍"},{"names":["potted_plant"],"surrogates":"🪴"},{"names":["tanabata_tree"],"surrogates":"🎋"},{"names":["leaves"],"surrogates":"🍃"},{"names":["fallen_leaf"],"surrogates":"🍂"},{"names":["maple_leaf"],"surrogates":"🍁"},{"names":["nest_with_eggs"],"surrogates":"🪺"},{"names":["empty_nest"],"surrogates":"🪹"},{"names":["mushroom"],"surrogates":"🍄"},{"names":["shell","spiral_shell"],"surrogates":"🐚"},{"names":["coral"],"surrogates":"🪸"},{"names":["rock"],"surrogates":"🪨"},{"names":["ear_of_rice","sheaf_of_rice"],"surrogates":"🌾"},{"names":["bouquet"],"surrogates":"💐"},{"names":["tulip"],"surrogates":"🌷"},{"names":["rose"],"surrogates":"🌹"},{"names":["wilted_rose","wilted_flower"],"surrogates":"🥀"},{"names":["hyacinth"],"surrogates":"🪻"},{"names":["lotus"],"surrogates":"🪷"},{"names":["hibiscus"],"surrogates":"🌺"},{"names":["cherry_blossom"],"surrogates":"🌸"},{"names":["blossom"],"surrogates":"🌼"},{"names":["sunflower"],"surrogates":"🌻"},{"names":["sun_with_face"],"surrogates":"🌞"},{"names":["full_moon_with_face"],"surrogates":"🌝"},{"names":["first_quarter_moon_with_face"],"surrogates":"🌛"},{"names":["last_quarter_moon_with_face"],"surrogates":"🌜"},{"names":["new_moon_with_face","new_moon_face"],"surrogates":"🌚"},{"names":["full_moon"],"surrogates":"🌕"},{"names":["waning_gibbous_moon"],"surrogates":"🌖"},{"names":["last_quarter_moon"],"surrogates":"🌗"},{"names":["waning_crescent_moon"],"surrogates":"🌘"},{"names":["new_moon"],"surrogates":"🌑"},{"names":["waxing_crescent_moon"],"surrogates":"🌒"},{"names":["first_quarter_moon"],"surrogates":"🌓"},{"names":["waxing_gibbous_moon"],"surrogates":"🌔"},{"names":["crescent_moon"],"surrogates":"🌙"},{"names":["earth_americas"],"surrogates":"🌎"},{"names":["earth_africa"],"surrogates":"🌍"},{"names":["earth_asia"],"surrogates":"🌏"},{"names":["ringed_planet"],"surrogates":"🪐"},{"names":["dizzy"],"surrogates":"💫"},{"names":["star"],"surrogates":"⭐"},{"names":["star2","glowing_star"],"surrogates":"🌟"},{"names":["sparkles"],"surrogates":"✨"},{"names":["zap","high_voltage"],"surrogates":"⚡"},{"names":["comet"],"surrogates":"☄️"},{"names":["boom","collision"],"surrogates":"💥"},{"names":["fire","flame"],"surrogates":"🔥"},{"names":["cloud_tornado","cloud_with_tornado","tornado"],"surrogates":"🌪️"},{"names":["rainbow"],"surrogates":"🌈"},{"names":["sunny","sun"],"surrogates":"☀️"},{"names":["white_sun_small_cloud","white_sun_with_small_cloud"],"surrogates":"🌤️"},{"names":["partly_sunny"],"surrogates":"⛅"},{"names":["white_sun_cloud","white_sun_behind_cloud"],"surrogates":"🌥️"},{"names":["cloud"],"surrogates":"☁️"},{"names":["white_sun_rain_cloud","white_sun_behind_cloud_with_rain"],"surrogates":"🌦️"},{"names":["cloud_rain","cloud_with_rain"],"surrogates":"🌧️"},{"names":["thunder_cloud_rain","thunder_cloud_and_rain"],"surrogates":"⛈️"},{"names":["cloud_lightning","cloud_with_lightning"],"surrogates":"🌩️"},{"names":["cloud_snow","cloud_with_snow"],"surrogates":"🌨️"},{"names":["snowflake"],"surrogates":"❄️"},{"names":["snowman2"],"surrogates":"☃️"},{"names":["snowman"],"surrogates":"⛄"},{"names":["wind_blowing_face","wind_face"],"surrogates":"🌬️"},{"names":["dash","dashing_away"],"surrogates":"💨"},{"names":["droplet"],"surrogates":"💧"},{"names":["sweat_drops"],"surrogates":"💦"},{"names":["bubbles"],"surrogates":"🫧"},{"names":["umbrella"],"surrogates":"☔"},{"names":["umbrella2"],"surrogates":"☂️"},{"names":["ocean","water_wave"],"surrogates":"🌊"},{"names":["fog"],"surrogates":"🌫️"}],"food":[{"names":["green_apple"],"surrogates":"🍏"},{"names":["apple","red_apple"],"surrogates":"🍎"},{"names":["pear"],"surrogates":"🍐"},{"names":["tangerine"],"surrogates":"🍊"},{"names":["lemon"],"surrogates":"🍋"},{"names":["banana"],"surrogates":"🍌"},{"names":["watermelon"],"surrogates":"🍉"},{"names":["grapes"],"surrogates":"🍇"},{"names":["strawberry"],"surrogates":"🍓"},{"names":["blueberries"],"surrogates":"🫐"},{"names":["melon"],"surrogates":"🍈"},{"names":["cherries"],"surrogates":"🍒"},{"names":["peach"],"surrogates":"🍑"},{"names":["mango"],"surrogates":"🥭"},{"names":["pineapple"],"surrogates":"🍍"},{"names":["coconut"],"surrogates":"🥥"},{"names":["kiwi","kiwifruit","kiwi_fruit"],"surrogates":"🥝"},{"names":["tomato"],"surrogates":"🍅"},{"names":["eggplant"],"surrogates":"🍆"},{"names":["avocado"],"surrogates":"🥑"},{"names":["pea_pod"],"surrogates":"🫛"},{"names":["broccoli"],"surrogates":"🥦"},{"names":["leafy_green"],"surrogates":"🥬"},{"names":["cucumber"],"surrogates":"🥒"},{"names":["hot_pepper"],"surrogates":"🌶️"},{"names":["bell_pepper"],"surrogates":"🫑"},{"names":["corn","ear_of_corn"],"surrogates":"🌽"},{"names":["carrot"],"surrogates":"🥕"},{"names":["olive"],"surrogates":"🫒"},{"names":["garlic"],"surrogates":"🧄"},{"names":["onion"],"surrogates":"🧅"},{"names":["potato"],"surrogates":"🥔"},{"names":["sweet_potato"],"surrogates":"🍠"},{"names":["ginger_root"],"surrogates":"🫚"},{"names":["croissant"],"surrogates":"🥐"},{"names":["bagel"],"surrogates":"🥯"},{"names":["bread"],"surrogates":"🍞"},{"names":["french_bread","baguette_bread"],"surrogates":"🥖"},{"names":["pretzel"],"surrogates":"🥨"},{"names":["cheese","cheese_wedge"],"surrogates":"🧀"},{"names":["egg"],"surrogates":"🥚"},{"names":["cooking"],"surrogates":"🍳"},{"names":["butter"],"surrogates":"🧈"},{"names":["pancakes"],"surrogates":"🥞"},{"names":["waffle"],"surrogates":"🧇"},{"names":["bacon"],"surrogates":"🥓"},{"names":["cut_of_meat"],"surrogates":"🥩"},{"names":["poultry_leg"],"surrogates":"🍗"},{"names":["meat_on_bone"],"surrogates":"🍖"},{"names":["bone"],"surrogates":"🦴"},{"names":["hotdog","hot_dog"],"surrogates":"🌭"},{"names":["hamburger"],"surrogates":"🍔"},{"names":["fries","french_fries"],"surrogates":"🍟"},{"names":["pizza"],"surrogates":"🍕"},{"names":["flatbread"],"surrogates":"🫓"},{"names":["sandwich"],"surrogates":"🥪"},{"names":["stuffed_flatbread","stuffed_pita"],"surrogates":"🥙"},{"names":["falafel"],"surrogates":"🧆"},{"names":["taco"],"surrogates":"🌮"},{"names":["burrito"],"surrogates":"🌯"},{"names":["tamale"],"surrogates":"🫔"},{"names":["salad","green_salad"],"surrogates":"🥗"},{"names":["shallow_pan_of_food","paella"],"surrogates":"🥘"},{"names":["fondue"],"surrogates":"🫕"},{"names":["canned_food"],"surrogates":"🥫"},{"names":["jar"],"surrogates":"🫙"},{"names":["spaghetti"],"surrogates":"🍝"},{"names":["ramen","steaming_bowl"],"surrogates":"🍜"},{"names":["stew","pot_of_food"],"surrogates":"🍲"},{"names":["curry","curry_rice"],"surrogates":"🍛"},{"names":["sushi"],"surrogates":"🍣"},{"names":["bento","bento_box"],"surrogates":"🍱"},{"names":["dumpling"],"surrogates":"🥟"},{"names":["oyster"],"surrogates":"🦪"},{"names":["fried_shrimp"],"surrogates":"🍤"},{"names":["rice_ball"],"surrogates":"🍙"},{"names":["rice","cooked_rice"],"surrogates":"🍚"},{"names":["rice_cracker"],"surrogates":"🍘"},{"names":["fish_cake"],"surrogates":"🍥"},{"names":["fortune_cookie"],"surrogates":"🥠"},{"names":["moon_cake"],"surrogates":"🥮"},{"names":["oden"],"surrogates":"🍢"},{"names":["dango"],"surrogates":"🍡"},{"names":["shaved_ice"],"surrogates":"🍧"},{"names":["ice_cream"],"surrogates":"🍨"},{"names":["icecream"],"surrogates":"🍦"},{"names":["pie"],"surrogates":"🥧"},{"names":["cupcake"],"surrogates":"🧁"},{"names":["cake","shortcake"],"surrogates":"🍰"},{"names":["birthday","birthday_cake"],"surrogates":"🎂"},{"names":["custard","pudding","flan"],"surrogates":"🍮"},{"names":["lollipop"],"surrogates":"🍭"},{"names":["candy"],"surrogates":"🍬"},{"names":["chocolate_bar"],"surrogates":"🍫"},{"names":["popcorn"],"surrogates":"🍿"},{"names":["doughnut"],"surrogates":"🍩"},{"names":["cookie"],"surrogates":"🍪"},{"names":["chestnut"],"surrogates":"🌰"},{"names":["peanuts","shelled_peanut"],"surrogates":"🥜"},{"names":["beans"],"surrogates":"🫘"},{"names":["honey_pot"],"surrogates":"🍯"},{"names":["milk","glass_of_milk"],"surrogates":"🥛"},{"names":["pouring_liquid"],"surrogates":"🫗"},{"names":["baby_bottle"],"surrogates":"🍼"},{"names":["teapot"],"surrogates":"🫖"},{"names":["coffee","hot_beverage"],"surrogates":"☕"},{"names":["tea"],"surrogates":"🍵"},{"names":["mate"],"surrogates":"🧉"},{"names":["beverage_box"],"surrogates":"🧃"},{"names":["cup_with_straw"],"surrogates":"🥤"},{"names":["bubble_tea"],"surrogates":"🧋"},{"names":["sake"],"surrogates":"🍶"},{"names":["beer","beer_mug"],"surrogates":"🍺"},{"names":["beers"],"surrogates":"🍻"},{"names":["champagne_glass","clinking_glass"],"surrogates":"🥂"},{"names":["wine_glass"],"surrogates":"🍷"},{"names":["tumbler_glass","whisky"],"surrogates":"🥃"},{"names":["cocktail"],"surrogates":"🍸"},{"names":["tropical_drink"],"surrogates":"🍹"},{"names":["champagne","bottle_with_popping_cork"],"surrogates":"🍾"},{"names":["ice_cube"],"surrogates":"🧊"},{"names":["spoon"],"surrogates":"🥄"},{"names":["fork_and_knife"],"surrogates":"🍴"},{"names":["fork_knife_plate","fork_and_knife_with_plate"],"surrogates":"🍽️"},{"names":["bowl_with_spoon"],"surrogates":"🥣"},{"names":["takeout_box"],"surrogates":"🥡"},{"names":["chopsticks"],"surrogates":"🥢"},{"names":["salt"],"surrogates":"🧂"}],"activity":[{"names":["soccer","soccer_ball"],"surrogates":"⚽"},{"names":["basketball"],"surrogates":"🏀"},{"names":["football"],"surrogates":"🏈"},{"names":["baseball"],"surrogates":"⚾"},{"names":["softball"],"surrogates":"🥎"},{"names":["tennis"],"surrogates":"🎾"},{"names":["volleyball"],"surrogates":"🏐"},{"names":["rugby_football"],"surrogates":"🏉"},{"names":["flying_disc"],"surrogates":"🥏"},{"names":["8ball"],"surrogates":"🎱"},{"names":["yo_yo"],"surrogates":"🪀"},{"names":["ping_pong","table_tennis"],"surrogates":"🏓"},{"names":["badminton"],"surrogates":"🏸"},{"names":["hockey","ice_hockey"],"surrogates":"🏒"},{"names":["field_hockey"],"surrogates":"🏑"},{"names":["lacrosse"],"surrogates":"🥍"},{"names":["cricket_game","cricket_bat_ball"],"surrogates":"🏏"},{"names":["boomerang"],"surrogates":"🪃"},{"names":["goal","goal_net"],"surrogates":"🥅"},{"names":["golf","flag_in_hole"],"surrogates":"⛳"},{"names":["kite"],"surrogates":"🪁"},{"names":["playground_slide"],"surrogates":"🛝"},{"names":["bow_and_arrow","archery"],"surrogates":"🏹"},{"names":["fishing_pole_and_fish","fishing_pole"],"surrogates":"🎣"},{"names":["diving_mask"],"surrogates":"🤿"},{"names":["boxing_glove","boxing_gloves"],"surrogates":"🥊"},{"names":["martial_arts_uniform","karate_uniform"],"surrogates":"🥋"},{"names":["running_shirt_with_sash","running_shirt"],"surrogates":"🎽"},{"names":["skateboard"],"surrogates":"🛹"},{"names":["roller_skate"],"surrogates":"🛼"},{"names":["sled"],"surrogates":"🛷"},{"names":["ice_skate"],"surrogates":"⛸️"},{"names":["curling_stone"],"surrogates":"🥌"},{"names":["ski","skis"],"surrogates":"🎿"},{"names":["skier"],"surrogates":"⛷️"},{"names":["snowboarder"],"surrogates":"🏂","skins":[{"names":["snowboarder_tone1","snowboarder_light_skin_tone"],"surrogates":"🏂🏻"},{"names":["snowboarder_tone2","snowboarder_medium_light_skin_tone"],"surrogates":"🏂🏼"},{"names":["snowboarder_tone3","snowboarder_medium_skin_tone"],"surrogates":"🏂🏽"},{"names":["snowboarder_tone4","snowboarder_medium_dark_skin_tone"],"surrogates":"🏂🏾"},{"names":["snowboarder_tone5","snowboarder_dark_skin_tone"],"surrogates":"🏂🏿"}]},{"names":["parachute"],"surrogates":"🪂"},{"names":["person_lifting_weights","lifter","weight_lifter"],"surrogates":"🏋️","skins":[{"names":["person_lifting_weights_tone1","lifter_tone1","weight_lifter_tone1"],"surrogates":"🏋🏻"},{"names":["person_lifting_weights_tone2","lifter_tone2","weight_lifter_tone2"],"surrogates":"🏋🏼"},{"names":["person_lifting_weights_tone3","lifter_tone3","weight_lifter_tone3"],"surrogates":"🏋🏽"},{"names":["person_lifting_weights_tone4","lifter_tone4","weight_lifter_tone4"],"surrogates":"🏋🏾"},{"names":["person_lifting_weights_tone5","lifter_tone5","weight_lifter_tone5"],"surrogates":"🏋🏿"}]},{"names":["woman_lifting_weights"],"surrogates":"🏋️‍♀️"},{"names":["man_lifting_weights"],"surrogates":"🏋️‍♂️"},{"names":["people_wrestling","wrestlers","wrestling"],"surrogates":"🤼"},{"names":["women_wrestling"],"surrogates":"🤼‍♀️"},{"names":["men_wrestling"],"surrogates":"🤼‍♂️"},{"names":["person_doing_cartwheel","cartwheel"],"surrogates":"🤸","skins":[{"names":["person_doing_cartwheel_tone1","cartwheel_tone1"],"surrogates":"🤸🏻"},{"names":["person_doing_cartwheel_tone2","cartwheel_tone2"],"surrogates":"🤸🏼"},{"names":["person_doing_cartwheel_tone3","cartwheel_tone3"],"surrogates":"🤸🏽"},{"names":["person_doing_cartwheel_tone4","cartwheel_tone4"],"surrogates":"🤸🏾"},{"names":["person_doing_cartwheel_tone5","cartwheel_tone5"],"surrogates":"🤸🏿"}]},{"names":["woman_cartwheeling"],"surrogates":"🤸‍♀️"},{"names":["man_cartwheeling"],"surrogates":"🤸‍♂️"},{"names":["person_bouncing_ball","basketball_player","person_with_ball"],"surrogates":"⛹️","skins":[{"names":["person_bouncing_ball_tone1","basketball_player_tone1","person_with_ball_tone1"],"surrogates":"⛹🏻"},{"names":["person_bouncing_ball_tone2","basketball_player_tone2","person_with_ball_tone2"],"surrogates":"⛹🏼"},{"names":["person_bouncing_ball_tone3","basketball_player_tone3","person_with_ball_tone3"],"surrogates":"⛹🏽"},{"names":["person_bouncing_ball_tone4","basketball_player_tone4","person_with_ball_tone4"],"surrogates":"⛹🏾"},{"names":["person_bouncing_ball_tone5","basketball_player_tone5","person_with_ball_tone5"],"surrogates":"⛹🏿"}]},{"names":["woman_bouncing_ball"],"surrogates":"⛹️‍♀️"},{"names":["man_bouncing_ball"],"surrogates":"⛹️‍♂️"},{"names":["person_fencing","fencer","fencing"],"surrogates":"🤺"},{"names":["person_playing_handball","handball"],"surrogates":"🤾","skins":[{"names":["person_playing_handball_tone1","handball_tone1"],"surrogates":"🤾🏻"},{"names":["person_playing_handball_tone2","handball_tone2"],"surrogates":"🤾🏼"},{"names":["person_playing_handball_tone3","handball_tone3"],"surrogates":"🤾🏽"},{"names":["person_playing_handball_tone4","handball_tone4"],"surrogates":"🤾🏾"},{"names":["person_playing_handball_tone5","handball_tone5"],"surrogates":"🤾🏿"}]},{"names":["woman_playing_handball"],"surrogates":"🤾‍♀️"},{"names":["man_playing_handball"],"surrogates":"🤾‍♂️"},{"names":["person_golfing","golfer"],"surrogates":"🏌️","skins":[{"names":["person_golfing_tone1","person_golfing_light_skin_tone"],"surrogates":"🏌🏻"},{"names":["person_golfing_tone2","person_golfing_medium_light_skin_tone"],"surrogates":"🏌🏼"},{"names":["person_golfing_tone3","person_golfing_medium_skin_tone"],"surrogates":"🏌🏽"},{"names":["person_golfing_tone4","person_golfing_medium_dark_skin_tone"],"surrogates":"🏌🏾"},{"names":["person_golfing_tone5","person_golfing_dark_skin_tone"],"surrogates":"🏌🏿"}]},{"names":["woman_golfing"],"surrogates":"🏌️‍♀️"},{"names":["man_golfing"],"surrogates":"🏌️‍♂️"},{"names":["horse_racing"],"surrogates":"🏇","skins":[{"names":["horse_racing_tone1"],"surrogates":"🏇🏻"},{"names":["horse_racing_tone2"],"surrogates":"🏇🏼"},{"names":["horse_racing_tone3"],"surrogates":"🏇🏽"},{"names":["horse_racing_tone4"],"surrogates":"🏇🏾"},{"names":["horse_racing_tone5"],"surrogates":"🏇🏿"}]},{"names":["person_in_lotus_position"],"surrogates":"🧘","skins":[{"names":["person_in_lotus_position_tone1","person_in_lotus_position_light_skin_tone"],"surrogates":"🧘🏻"},{"names":["person_in_lotus_position_tone2","person_in_lotus_position_medium_light_skin_tone"],"surrogates":"🧘🏼"},{"names":["person_in_lotus_position_tone3","person_in_lotus_position_medium_skin_tone"],"surrogates":"🧘🏽"},{"names":["person_in_lotus_position_tone4","person_in_lotus_position_medium_dark_skin_tone"],"surrogates":"🧘🏾"},{"names":["person_in_lotus_position_tone5","person_in_lotus_position_dark_skin_tone"],"surrogates":"🧘🏿"}]},{"names":["woman_in_lotus_position"],"surrogates":"🧘‍♀️","skins":[{"names":["woman_in_lotus_position_tone1","woman_in_lotus_position_light_skin_tone"],"surrogates":"🧘🏻‍♀️"},{"names":["woman_in_lotus_position_tone2","woman_in_lotus_position_medium_light_skin_tone"],"surrogates":"🧘🏼‍♀️"},{"names":["woman_in_lotus_position_tone3","woman_in_lotus_position_medium_skin_tone"],"surrogates":"🧘🏽‍♀️"},{"names":["woman_in_lotus_position_tone4","woman_in_lotus_position_medium_dark_skin_tone"],"surrogates":"🧘🏾‍♀️"},{"names":["woman_in_lotus_position_tone5","woman_in_lotus_position_dark_skin_tone"],"surrogates":"🧘🏿‍♀️"}]},{"names":["man_in_lotus_position"],"surrogates":"🧘‍♂️","skins":[{"names":["man_in_lotus_position_tone1","man_in_lotus_position_light_skin_tone"],"surrogates":"🧘🏻‍♂️"},{"names":["man_in_lotus_position_tone2","man_in_lotus_position_medium_light_skin_tone"],"surrogates":"🧘🏼‍♂️"},{"names":["man_in_lotus_position_tone3","man_in_lotus_position_medium_skin_tone"],"surrogates":"🧘🏽‍♂️"},{"names":["man_in_lotus_position_tone4","man_in_lotus_position_medium_dark_skin_tone"],"surrogates":"🧘🏾‍♂️"},{"names":["man_in_lotus_position_tone5","man_in_lotus_position_dark_skin_tone"],"surrogates":"🧘🏿‍♂️"}]},{"names":["person_surfing","surfer"],"surrogates":"🏄","skins":[{"names":["person_surfing_tone1","surfer_tone1"],"surrogates":"🏄🏻"},{"names":["person_surfing_tone2","surfer_tone2"],"surrogates":"🏄🏼"},{"names":["person_surfing_tone3","surfer_tone3"],"surrogates":"🏄🏽"},{"names":["person_surfing_tone4","surfer_tone4"],"surrogates":"🏄🏾"},{"names":["person_surfing_tone5","surfer_tone5"],"surrogates":"🏄🏿"}]},{"names":["woman_surfing"],"surrogates":"🏄‍♀️"},{"names":["man_surfing"],"surrogates":"🏄‍♂️"},{"names":["person_swimming","swimmer"],"surrogates":"🏊","skins":[{"names":["person_swimming_tone1","swimmer_tone1"],"surrogates":"🏊🏻"},{"names":["person_swimming_tone2","swimmer_tone2"],"surrogates":"🏊🏼"},{"names":["person_swimming_tone3","swimmer_tone3"],"surrogates":"🏊🏽"},{"names":["person_swimming_tone4","swimmer_tone4"],"surrogates":"🏊🏾"},{"names":["person_swimming_tone5","swimmer_tone5"],"surrogates":"🏊🏿"}]},{"names":["woman_swimming"],"surrogates":"🏊‍♀️"},{"names":["man_swimming"],"surrogates":"🏊‍♂️"},{"names":["person_playing_water_polo","water_polo"],"surrogates":"🤽","skins":[{"names":["person_playing_water_polo_tone1","water_polo_tone1"],"surrogates":"🤽🏻"},{"names":["person_playing_water_polo_tone2","water_polo_tone2"],"surrogates":"🤽🏼"},{"names":["person_playing_water_polo_tone3","water_polo_tone3"],"surrogates":"🤽🏽"},{"names":["person_playing_water_polo_tone4","water_polo_tone4"],"surrogates":"🤽🏾"},{"names":["person_playing_water_polo_tone5","water_polo_tone5"],"surrogates":"🤽🏿"}]},{"names":["woman_playing_water_polo"],"surrogates":"🤽‍♀️"},{"names":["man_playing_water_polo"],"surrogates":"🤽‍♂️"},{"names":["person_rowing_boat","rowboat"],"surrogates":"🚣","skins":[{"names":["person_rowing_boat_tone1","rowboat_tone1"],"surrogates":"🚣🏻"},{"names":["person_rowing_boat_tone2","rowboat_tone2"],"surrogates":"🚣🏼"},{"names":["person_rowing_boat_tone3","rowboat_tone3"],"surrogates":"🚣🏽"},{"names":["person_rowing_boat_tone4","rowboat_tone4"],"surrogates":"🚣🏾"},{"names":["person_rowing_boat_tone5","rowboat_tone5"],"surrogates":"🚣🏿"}]},{"names":["woman_rowing_boat"],"surrogates":"🚣‍♀️"},{"names":["man_rowing_boat"],"surrogates":"🚣‍♂️"},{"names":["person_climbing"],"surrogates":"🧗","skins":[{"names":["person_climbing_tone1","person_climbing_light_skin_tone"],"surrogates":"🧗🏻"},{"names":["person_climbing_tone2","person_climbing_medium_light_skin_tone"],"surrogates":"🧗🏼"},{"names":["person_climbing_tone3","person_climbing_medium_skin_tone"],"surrogates":"🧗🏽"},{"names":["person_climbing_tone4","person_climbing_medium_dark_skin_tone"],"surrogates":"🧗🏾"},{"names":["person_climbing_tone5","person_climbing_dark_skin_tone"],"surrogates":"🧗🏿"}]},{"names":["woman_climbing"],"surrogates":"🧗‍♀️","skins":[{"names":["woman_climbing_tone1","woman_climbing_light_skin_tone"],"surrogates":"🧗🏻‍♀️"},{"names":["woman_climbing_tone2","woman_climbing_medium_light_skin_tone"],"surrogates":"🧗🏼‍♀️"},{"names":["woman_climbing_tone3","woman_climbing_medium_skin_tone"],"surrogates":"🧗🏽‍♀️"},{"names":["woman_climbing_tone4","woman_climbing_medium_dark_skin_tone"],"surrogates":"🧗🏾‍♀️"},{"names":["woman_climbing_tone5","woman_climbing_dark_skin_tone"],"surrogates":"🧗🏿‍♀️"}]},{"names":["man_climbing"],"surrogates":"🧗‍♂️","skins":[{"names":["man_climbing_tone1","man_climbing_light_skin_tone"],"surrogates":"🧗🏻‍♂️"},{"names":["man_climbing_tone2","man_climbing_medium_light_skin_tone"],"surrogates":"🧗🏼‍♂️"},{"names":["man_climbing_tone3","man_climbing_medium_skin_tone"],"surrogates":"🧗🏽‍♂️"},{"names":["man_climbing_tone4","man_climbing_medium_dark_skin_tone"],"surrogates":"🧗🏾‍♂️"},{"names":["man_climbing_tone5","man_climbing_dark_skin_tone"],"surrogates":"🧗🏿‍♂️"}]},{"names":["person_mountain_biking","mountain_bicyclist"],"surrogates":"🚵","skins":[{"names":["person_mountain_biking_tone1","mountain_bicyclist_tone1"],"surrogates":"🚵🏻"},{"names":["person_mountain_biking_tone2","mountain_bicyclist_tone2"],"surrogates":"🚵🏼"},{"names":["person_mountain_biking_tone3","mountain_bicyclist_tone3"],"surrogates":"🚵🏽"},{"names":["person_mountain_biking_tone4","mountain_bicyclist_tone4"],"surrogates":"🚵🏾"},{"names":["person_mountain_biking_tone5","mountain_bicyclist_tone5"],"surrogates":"🚵🏿"}]},{"names":["woman_mountain_biking"],"surrogates":"🚵‍♀️"},{"names":["man_mountain_biking"],"surrogates":"🚵‍♂️"},{"names":["person_biking","bicyclist"],"surrogates":"🚴","skins":[{"names":["person_biking_tone1","bicyclist_tone1"],"surrogates":"🚴🏻"},{"names":["person_biking_tone2","bicyclist_tone2"],"surrogates":"🚴🏼"},{"names":["person_biking_tone3","bicyclist_tone3"],"surrogates":"🚴🏽"},{"names":["person_biking_tone4","bicyclist_tone4"],"surrogates":"🚴🏾"},{"names":["person_biking_tone5","bicyclist_tone5"],"surrogates":"🚴🏿"}]},{"names":["woman_biking"],"surrogates":"🚴‍♀️"},{"names":["man_biking"],"surrogates":"🚴‍♂️"},{"names":["trophy"],"surrogates":"🏆"},{"names":["first_place","first_place_medal"],"surrogates":"🥇"},{"names":["second_place","second_place_medal"],"surrogates":"🥈"},{"names":["third_place","third_place_medal"],"surrogates":"🥉"},{"names":["medal","sports_medal"],"surrogates":"🏅"},{"names":["military_medal"],"surrogates":"🎖️"},{"names":["rosette"],"surrogates":"🏵️"},{"names":["reminder_ribbon"],"surrogates":"🎗️"},{"names":["ticket"],"surrogates":"🎫"},{"names":["tickets","admission_tickets"],"surrogates":"🎟️"},{"names":["circus_tent"],"surrogates":"🎪"},{"names":["person_juggling","juggling","juggler"],"surrogates":"🤹","skins":[{"names":["person_juggling_tone1","juggling_tone1","juggler_tone1"],"surrogates":"🤹🏻"},{"names":["person_juggling_tone2","juggling_tone2","juggler_tone2"],"surrogates":"🤹🏼"},{"names":["person_juggling_tone3","juggling_tone3","juggler_tone3"],"surrogates":"🤹🏽"},{"names":["person_juggling_tone4","juggling_tone4","juggler_tone4"],"surrogates":"🤹🏾"},{"names":["person_juggling_tone5","juggling_tone5","juggler_tone5"],"surrogates":"🤹🏿"}]},{"names":["woman_juggling"],"surrogates":"🤹‍♀️"},{"names":["man_juggling"],"surrogates":"🤹‍♂️"},{"names":["performing_arts"],"surrogates":"🎭"},{"names":["ballet_shoes"],"surrogates":"🩰"},{"names":["art"],"surrogates":"🎨"},{"names":["clapper","clapper_board"],"surrogates":"🎬"},{"names":["microphone"],"surrogates":"🎤"},{"names":["headphones","headphone"],"surrogates":"🎧"},{"names":["musical_score"],"surrogates":"🎼"},{"names":["musical_keyboard"],"surrogates":"🎹"},{"names":["maracas"],"surrogates":"🪇"},{"names":["drum","drum_with_drumsticks"],"surrogates":"🥁"},{"names":["long_drum"],"surrogates":"🪘"},{"names":["saxophone"],"surrogates":"🎷"},{"names":["trumpet"],"surrogates":"🎺"},{"names":["accordion"],"surrogates":"🪗"},{"names":["guitar"],"surrogates":"🎸"},{"names":["banjo"],"surrogates":"🪕"},{"names":["violin"],"surrogates":"🎻"},{"names":["flute"],"surrogates":"🪈"},{"names":["game_die"],"surrogates":"🎲"},{"names":["chess_pawn"],"surrogates":"♟️"},{"names":["dart","direct_hit"],"surrogates":"🎯"},{"names":["bowling"],"surrogates":"🎳"},{"names":["video_game"],"surrogates":"🎮"},{"names":["slot_machine"],"surrogates":"🎰"},{"names":["jigsaw","puzzle_piece"],"surrogates":"🧩"}],"travel":[{"names":["red_car","automobile"],"surrogates":"🚗"},{"names":["taxi"],"surrogates":"🚕"},{"names":["blue_car"],"surrogates":"🚙"},{"names":["pickup_truck"],"surrogates":"🛻"},{"names":["minibus"],"surrogates":"🚐"},{"names":["bus"],"surrogates":"🚌"},{"names":["trolleybus"],"surrogates":"🚎"},{"names":["race_car","racing_car"],"surrogates":"🏎️"},{"names":["police_car"],"surrogates":"🚓"},{"names":["ambulance"],"surrogates":"🚑"},{"names":["fire_engine"],"surrogates":"🚒"},{"names":["truck"],"surrogates":"🚚"},{"names":["articulated_lorry"],"surrogates":"🚛"},{"names":["tractor"],"surrogates":"🚜"},{"names":["probing_cane"],"surrogates":"🦯"},{"names":["manual_wheelchair"],"surrogates":"🦽"},{"names":["motorized_wheelchair"],"surrogates":"🦼"},{"names":["crutch"],"surrogates":"🩼"},{"names":["scooter","kick_scooter"],"surrogates":"🛴"},{"names":["bike","bicycle"],"surrogates":"🚲"},{"names":["motor_scooter","motorbike"],"surrogates":"🛵"},{"names":["motorcycle","racing_motorcycle"],"surrogates":"🏍️"},{"names":["auto_rickshaw"],"surrogates":"🛺"},{"names":["wheel"],"surrogates":"🛞"},{"names":["rotating_light"],"surrogates":"🚨"},{"names":["oncoming_police_car"],"surrogates":"🚔"},{"names":["oncoming_bus"],"surrogates":"🚍"},{"names":["oncoming_automobile"],"surrogates":"🚘"},{"names":["oncoming_taxi"],"surrogates":"🚖"},{"names":["aerial_tramway"],"surrogates":"🚡"},{"names":["mountain_cableway"],"surrogates":"🚠"},{"names":["suspension_railway"],"surrogates":"🚟"},{"names":["railway_car"],"surrogates":"🚃"},{"names":["train","tram_car"],"surrogates":"🚋"},{"names":["mountain_railway"],"surrogates":"🚞"},{"names":["monorail"],"surrogates":"🚝"},{"names":["bullettrain_side"],"surrogates":"🚄"},{"names":["bullettrain_front","bullet_train"],"surrogates":"🚅"},{"names":["light_rail"],"surrogates":"🚈"},{"names":["steam_locomotive","locomotive"],"surrogates":"🚂"},{"names":["train2"],"surrogates":"🚆"},{"names":["metro"],"surrogates":"🚇"},{"names":["tram"],"surrogates":"🚊"},{"names":["station"],"surrogates":"🚉"},{"names":["airplane"],"surrogates":"✈️"},{"names":["airplane_departure"],"surrogates":"🛫"},{"names":["airplane_arriving"],"surrogates":"🛬"},{"names":["airplane_small","small_airplane"],"surrogates":"🛩️"},{"names":["seat"],"surrogates":"💺"},{"names":["satellite_orbital"],"surrogates":"🛰️"},{"names":["rocket"],"surrogates":"🚀"},{"names":["flying_saucer"],"surrogates":"🛸"},{"names":["helicopter"],"surrogates":"🚁"},{"names":["canoe","kayak"],"surrogates":"🛶"},{"names":["sailboat"],"surrogates":"⛵"},{"names":["speedboat"],"surrogates":"🚤"},{"names":["motorboat","motor_boat"],"surrogates":"🛥️"},{"names":["cruise_ship","passenger_ship"],"surrogates":"🛳️"},{"names":["ferry"],"surrogates":"⛴️"},{"names":["ship"],"surrogates":"🚢"},{"names":["ring_buoy"],"surrogates":"🛟"},{"names":["anchor"],"surrogates":"⚓"},{"names":["hook"],"surrogates":"🪝"},{"names":["fuelpump","fuel_pump"],"surrogates":"⛽"},{"names":["construction"],"surrogates":"🚧"},{"names":["vertical_traffic_light"],"surrogates":"🚦"},{"names":["traffic_light"],"surrogates":"🚥"},{"names":["busstop","bus_stop"],"surrogates":"🚏"},{"names":["map","world_map"],"surrogates":"🗺️"},{"names":["moyai","moai"],"surrogates":"🗿"},{"names":["statue_of_liberty"],"surrogates":"🗽"},{"names":["tokyo_tower"],"surrogates":"🗼"},{"names":["european_castle","castle"],"surrogates":"🏰"},{"names":["japanese_castle"],"surrogates":"🏯"},{"names":["stadium"],"surrogates":"🏟️"},{"names":["ferris_wheel"],"surrogates":"🎡"},{"names":["roller_coaster"],"surrogates":"🎢"},{"names":["carousel_horse"],"surrogates":"🎠"},{"names":["fountain"],"surrogates":"⛲"},{"names":["beach_umbrella","umbrella_on_ground"],"surrogates":"⛱️"},{"names":["beach","beach_with_umbrella"],"surrogates":"🏖️"},{"names":["island","desert_island"],"surrogates":"🏝️"},{"names":["desert"],"surrogates":"🏜️"},{"names":["volcano"],"surrogates":"🌋"},{"names":["mountain"],"surrogates":"⛰️"},{"names":["mountain_snow","snow_capped_mountain"],"surrogates":"🏔️"},{"names":["mount_fuji"],"surrogates":"🗻"},{"names":["camping"],"surrogates":"🏕️"},{"names":["tent"],"surrogates":"⛺"},{"names":["house"],"surrogates":"🏠"},{"names":["house_with_garden"],"surrogates":"🏡"},{"names":["homes","house_buildings","houses"],"surrogates":"🏘️"},{"names":["house_abandoned","derelict_house_building"],"surrogates":"🏚️"},{"names":["hut"],"surrogates":"🛖"},{"names":["construction_site","building_construction"],"surrogates":"🏗️"},{"names":["factory"],"surrogates":"🏭"},{"names":["office"],"surrogates":"🏢"},{"names":["department_store"],"surrogates":"🏬"},{"names":["post_office"],"surrogates":"🏣"},{"names":["european_post_office"],"surrogates":"🏤"},{"names":["hospital"],"surrogates":"🏥"},{"names":["bank"],"surrogates":"🏦"},{"names":["hotel"],"surrogates":"🏨"},{"names":["convenience_store"],"surrogates":"🏪"},{"names":["school"],"surrogates":"🏫"},{"names":["love_hotel"],"surrogates":"🏩"},{"names":["wedding"],"surrogates":"💒"},{"names":["classical_building"],"surrogates":"🏛️"},{"names":["church"],"surrogates":"⛪"},{"names":["mosque"],"surrogates":"🕌"},{"names":["synagogue"],"surrogates":"🕍"},{"names":["hindu_temple"],"surrogates":"🛕"},{"names":["kaaba"],"surrogates":"🕋"},{"names":["shinto_shrine"],"surrogates":"⛩️"},{"names":["railway_track","railroad_track"],"surrogates":"🛤️"},{"names":["motorway"],"surrogates":"🛣️"},{"names":["japan","map_of_japan"],"surrogates":"🗾"},{"names":["rice_scene"],"surrogates":"🎑"},{"names":["park","national_park"],"surrogates":"🏞️"},{"names":["sunrise"],"surrogates":"🌅"},{"names":["sunrise_over_mountains"],"surrogates":"🌄"},{"names":["stars","shooting_star"],"surrogates":"🌠"},{"names":["sparkler"],"surrogates":"🎇"},{"names":["fireworks"],"surrogates":"🎆"},{"names":["city_sunset","city_sunrise","sunset"],"surrogates":"🌆"},{"names":["city_dusk"],"surrogates":"🌆"},{"names":["cityscape"],"surrogates":"🏙️"},{"names":["night_with_stars"],"surrogates":"🌃"},{"names":["milky_way"],"surrogates":"🌌"},{"names":["bridge_at_night"],"surrogates":"🌉"},{"names":["foggy"],"surrogates":"🌁"}],"objects":[{"names":["watch"],"surrogates":"⌚"},{"names":["mobile_phone","iphone"],"surrogates":"📱"},{"names":["calling"],"surrogates":"📲"},{"names":["computer"],"surrogates":"💻"},{"names":["keyboard"],"surrogates":"⌨️"},{"names":["desktop","desktop_computer"],"surrogates":"🖥️"},{"names":["printer"],"surrogates":"🖨️"},{"names":["mouse_three_button","three_button_mouse"],"surrogates":"🖱️"},{"names":["trackball"],"surrogates":"🖲️"},{"names":["joystick"],"surrogates":"🕹️"},{"names":["compression","clamp"],"surrogates":"🗜️"},{"names":["minidisc","computer_disk"],"surrogates":"💽"},{"names":["floppy_disk"],"surrogates":"💾"},{"names":["cd","optical_disk"],"surrogates":"💿"},{"names":["dvd"],"surrogates":"📀"},{"names":["vhs","videocassette"],"surrogates":"📼"},{"names":["camera"],"surrogates":"📷"},{"names":["camera_with_flash"],"surrogates":"📸"},{"names":["video_camera"],"surrogates":"📹"},{"names":["movie_camera"],"surrogates":"🎥"},{"names":["projector","film_projector"],"surrogates":"📽️"},{"names":["film_frames"],"surrogates":"🎞️"},{"names":["telephone_receiver"],"surrogates":"📞"},{"names":["telephone"],"surrogates":"☎"},{"names":["pager"],"surrogates":"📟"},{"names":["fax","fax_machine"],"surrogates":"📠"},{"names":["tv","television"],"surrogates":"📺"},{"names":["radio"],"surrogates":"📻"},{"names":["microphone2","studio_microphone"],"surrogates":"🎙️"},{"names":["level_slider"],"surrogates":"🎚️"},{"names":["control_knobs"],"surrogates":"🎛️"},{"names":["compass"],"surrogates":"🧭"},{"names":["stopwatch"],"surrogates":"⏱️"},{"names":["timer","timer_clock"],"surrogates":"⏲️"},{"names":["alarm_clock"],"surrogates":"⏰"},{"names":["clock","mantlepiece_clock"],"surrogates":"🕰️"},{"names":["hourglass"],"surrogates":"⌛"},{"names":["hourglass_flowing_sand"],"surrogates":"⏳"},{"names":["satellite"],"surrogates":"🛰️"},{"names":["battery"],"surrogates":"🔋"},{"names":["low_battery"],"surrogates":"🪫"},{"names":["electric_plug"],"surrogates":"🔌"},{"names":["bulb","light_bulb"],"surrogates":"💡"},{"names":["flashlight"],"surrogates":"🔦"},{"names":["candle"],"surrogates":"🕯️"},{"names":["diya_lamp"],"surrogates":"🪔"},{"names":["fire_extinguisher"],"surrogates":"🧯"},{"names":["oil","oil_drum"],"surrogates":"🛢️"},{"names":["money_with_wings"],"surrogates":"💸"},{"names":["dollar"],"surrogates":"💵"},{"names":["yen","yen_banknote"],"surrogates":"💴"},{"names":["euro","euro_banknote"],"surrogates":"💶"},{"names":["pound"],"surrogates":"💷"},{"names":["coin"],"surrogates":"🪙"},{"names":["moneybag","money_bag"],"surrogates":"💰"},{"names":["credit_card"],"surrogates":"💳"},{"names":["identification_card"],"surrogates":"🪪"},{"names":["gem","gem_stone"],"surrogates":"💎"},{"names":["scales","balance_scale"],"surrogates":"⚖️"},{"names":["ladder"],"surrogates":"🪜"},{"names":["toolbox"],"surrogates":"🧰"},{"names":["screwdriver"],"surrogates":"🪛"},{"names":["wrench"],"surrogates":"🔧"},{"names":["hammer"],"surrogates":"🔨"},{"names":["hammer_pick","hammer_and_pick"],"surrogates":"⚒️"},{"names":["tools","hammer_and_wrench"],"surrogates":"🛠️"},{"names":["pick"],"surrogates":"⛏️"},{"names":["carpentry_saw"],"surrogates":"🪚"},{"names":["nut_and_bolt"],"surrogates":"🔩"},{"names":["gear"],"surrogates":"⚙️"},{"names":["mouse_trap"],"surrogates":"🪤"},{"names":["bricks","brick"],"surrogates":"🧱"},{"names":["chains"],"surrogates":"⛓️"},{"names":["magnet"],"surrogates":"🧲"},{"names":["gun","pistol"],"surrogates":"🔫"},{"names":["bomb"],"surrogates":"💣"},{"names":["firecracker"],"surrogates":"🧨"},{"names":["axe"],"surrogates":"🪓"},{"names":["knife","kitchen_knife"],"surrogates":"🔪"},{"names":["dagger","dagger_knife"],"surrogates":"🗡️"},{"names":["crossed_swords"],"surrogates":"⚔️"},{"names":["shield"],"surrogates":"🛡️"},{"names":["smoking","cigarette"],"surrogates":"🚬"},{"names":["coffin"],"surrogates":"⚰️"},{"names":["headstone"],"surrogates":"🪦"},{"names":["urn","funeral_urn"],"surrogates":"⚱️"},{"names":["amphora"],"surrogates":"🏺"},{"names":["crystal_ball"],"surrogates":"🔮"},{"names":["prayer_beads"],"surrogates":"📿"},{"names":["nazar_amulet"],"surrogates":"🧿"},{"names":["hamsa"],"surrogates":"🪬"},{"names":["barber","barber_pole"],"surrogates":"💈"},{"names":["alembic"],"surrogates":"⚗️"},{"names":["telescope"],"surrogates":"🔭"},{"names":["microscope"],"surrogates":"🔬"},{"names":["hole"],"surrogates":"🕳️"},{"names":["x_ray"],"surrogates":"🩻"},{"names":["adhesive_bandage"],"surrogates":"🩹"},{"names":["stethoscope"],"surrogates":"🩺"},{"names":["pill"],"surrogates":"💊"},{"names":["syringe"],"surrogates":"💉"},{"names":["drop_of_blood"],"surrogates":"🩸"},{"names":["dna"],"surrogates":"🧬"},{"names":["microbe"],"surrogates":"🦠"},{"names":["petri_dish"],"surrogates":"🧫"},{"names":["test_tube"],"surrogates":"🧪"},{"names":["thermometer"],"surrogates":"🌡️"},{"names":["broom"],"surrogates":"🧹"},{"names":["plunger"],"surrogates":"🪠"},{"names":["basket"],"surrogates":"🧺"},{"names":["roll_of_paper"],"surrogates":"🧻"},{"names":["toilet"],"surrogates":"🚽"},{"names":["potable_water"],"surrogates":"🚰"},{"names":["shower"],"surrogates":"🚿"},{"names":["bathtub"],"surrogates":"🛁"},{"names":["bath"],"surrogates":"🛀","skins":[{"names":["bath_tone1"],"surrogates":"🛀🏻"},{"names":["bath_tone2"],"surrogates":"🛀🏼"},{"names":["bath_tone3"],"surrogates":"🛀🏽"},{"names":["bath_tone4"],"surrogates":"🛀🏾"},{"names":["bath_tone5"],"surrogates":"🛀🏿"}]},{"names":["soap"],"surrogates":"🧼"},{"names":["toothbrush"],"surrogates":"🪥"},{"names":["razor"],"surrogates":"🪒"},{"names":["hair_pick"],"surrogates":"🪮"},{"names":["sponge"],"surrogates":"🧽"},{"names":["bucket"],"surrogates":"🪣"},{"names":["squeeze_bottle","lotion_bottle"],"surrogates":"🧴"},{"names":["bellhop","bellhop_bell"],"surrogates":"🛎️"},{"names":["key"],"surrogates":"🔑"},{"names":["key2","old_key"],"surrogates":"🗝️"},{"names":["door"],"surrogates":"🚪"},{"names":["chair"],"surrogates":"🪑"},{"names":["couch","couch_and_lamp"],"surrogates":"🛋️"},{"names":["bed"],"surrogates":"🛏️"},{"names":["sleeping_accommodation","person_in_bed"],"surrogates":"🛌","skins":[{"names":["person_in_bed_tone1","person_in_bed_light_skin_tone"],"surrogates":"🛌🏻"},{"names":["person_in_bed_tone2","person_in_bed_medium_light_skin_tone"],"surrogates":"🛌🏼"},{"names":["person_in_bed_tone3","person_in_bed_medium_skin_tone"],"surrogates":"🛌🏽"},{"names":["person_in_bed_tone4","person_in_bed_medium_dark_skin_tone"],"surrogates":"🛌🏾"},{"names":["person_in_bed_tone5","person_in_bed_dark_skin_tone"],"surrogates":"🛌🏿"}]},{"names":["teddy_bear"],"surrogates":"🧸"},{"names":["nesting_dolls"],"surrogates":"🪆"},{"names":["frame_photo","frame_with_picture"],"surrogates":"🖼️"},{"names":["mirror"],"surrogates":"🪞"},{"names":["window"],"surrogates":"🪟"},{"names":["shopping_bags"],"surrogates":"🛍️"},{"names":["shopping_cart","shopping_trolley"],"surrogates":"🛒"},{"names":["gift","wrapped_gift"],"surrogates":"🎁"},{"names":["balloon"],"surrogates":"🎈"},{"names":["flags","carp_streamer"],"surrogates":"🎏"},{"names":["ribbon"],"surrogates":"🎀"},{"names":["magic_wand"],"surrogates":"🪄"},{"names":["piñata"],"surrogates":"🪅"},{"names":["confetti_ball"],"surrogates":"🎊"},{"names":["tada","party_popper"],"surrogates":"🎉"},{"names":["dolls"],"surrogates":"🎎"},{"names":["folding_hand_fan"],"surrogates":"🪭"},{"names":["izakaya_lantern"],"surrogates":"🏮"},{"names":["wind_chime"],"surrogates":"🎐"},{"names":["mirror_ball"],"surrogates":"🪩"},{"names":["red_envelope"],"surrogates":"🧧"},{"names":["envelope"],"surrogates":"✉"},{"names":["envelope_with_arrow"],"surrogates":"📩"},{"names":["incoming_envelope"],"surrogates":"📨"},{"names":["e_mail","email"],"surrogates":"✉️"},{"names":["love_letter"],"surrogates":"💌"},{"names":["inbox_tray"],"surrogates":"📥"},{"names":["outbox_tray"],"surrogates":"📤"},{"names":["package"],"surrogates":"📦"},{"names":["label"],"surrogates":"🏷️"},{"names":["placard"],"surrogates":"🪧"},{"names":["mailbox_closed"],"surrogates":"📪"},{"names":["mailbox"],"surrogates":"📫"},{"names":["mailbox_with_mail"],"surrogates":"📬"},{"names":["mailbox_with_no_mail"],"surrogates":"📭"},{"names":["postbox"],"surrogates":"📮"},{"names":["postal_horn"],"surrogates":"📯"},{"names":["scroll"],"surrogates":"📜"},{"names":["page_with_curl"],"surrogates":"📃"},{"names":["page_facing_up"],"surrogates":"📄"},{"names":["bookmark_tabs"],"surrogates":"📑"},{"names":["receipt"],"surrogates":"🧾"},{"names":["bar_chart"],"surrogates":"📊"},{"names":["chart_with_upwards_trend"],"surrogates":"📈"},{"names":["chart_with_downwards_trend"],"surrogates":"📉"},{"names":["notepad_spiral","spiral_note_pad"],"surrogates":"🗒️"},{"names":["calendar_spiral","spiral_calendar_pad"],"surrogates":"🗓️"},{"names":["calendar"],"surrogates":"📆"},{"names":["date"],"surrogates":"📅"},{"names":["wastebasket"],"surrogates":"🗑️"},{"names":["card_index"],"surrogates":"📇"},{"names":["card_box","card_file_box"],"surrogates":"🗃️"},{"names":["ballot_box","ballot_box_with_ballot"],"surrogates":"🗳️"},{"names":["file_cabinet"],"surrogates":"🗄️"},{"names":["clipboard"],"surrogates":"📋"},{"names":["file_folder"],"surrogates":"📁"},{"names":["open_file_folder"],"surrogates":"📂"},{"names":["dividers","card_index_dividers"],"surrogates":"🗂️"},{"names":["newspaper2","rolled_up_newspaper"],"surrogates":"🗞️"},{"names":["newspaper"],"surrogates":"📰"},{"names":["notebook"],"surrogates":"📓"},{"names":["notebook_with_decorative_cover"],"surrogates":"📔"},{"names":["ledger"],"surrogates":"📒"},{"names":["closed_book"],"surrogates":"📕"},{"names":["green_book"],"surrogates":"📗"},{"names":["blue_book"],"surrogates":"📘"},{"names":["orange_book"],"surrogates":"📙"},{"names":["books"],"surrogates":"📚"},{"names":["book","open_book"],"surrogates":"📖"},{"names":["bookmark"],"surrogates":"🔖"},{"names":["safety_pin"],"surrogates":"🧷"},{"names":["link"],"surrogates":"🔗"},{"names":["paperclip"],"surrogates":"📎"},{"names":["paperclips","linked_paperclips"],"surrogates":"🖇️"},{"names":["triangular_ruler"],"surrogates":"📐"},{"names":["straight_ruler"],"surrogates":"📏"},{"names":["abacus"],"surrogates":"🧮"},{"names":["pushpin"],"surrogates":"📌"},{"names":["round_pushpin"],"surrogates":"📍"},{"names":["scissors"],"surrogates":"✂️"},{"names":["pen_ballpoint","lower_left_ballpoint_pen","pen"],"surrogates":"🖊️"},{"names":["pen_fountain","lower_left_fountain_pen","fountain_pen"],"surrogates":"🖋️"},{"names":["black_nib"],"surrogates":"✒️"},{"names":["paintbrush","lower_left_paintbrush"],"surrogates":"🖌️"},{"names":["crayon","lower_left_crayon"],"surrogates":"🖍️"},{"names":["pencil","memo"],"surrogates":"📝"},{"names":["pencil2"],"surrogates":"✏️"},{"names":["mag"],"surrogates":"🔍"},{"names":["mag_right"],"surrogates":"🔎"},{"names":["lock_with_ink_pen"],"surrogates":"🔏"},{"names":["closed_lock_with_key"],"surrogates":"🔐"},{"names":["lock","locked"],"surrogates":"🔒"},{"names":["unlock","unlocked"],"surrogates":"🔓"}],"symbols":[{"names":["pink_heart"],"surrogates":"🩷"},{"names":["heart","red_heart"],"surrogates":"❤️"},{"names":["orange_heart"],"surrogates":"🧡"},{"names":["yellow_heart"],"surrogates":"💛"},{"names":["green_heart"],"surrogates":"💚"},{"names":["light_blue_heart"],"surrogates":"🩵"},{"names":["blue_heart"],"surrogates":"💙"},{"names":["purple_heart"],"surrogates":"💜"},{"names":["black_heart"],"surrogates":"🖤"},{"names":["grey_heart"],"surrogates":"🩶"},{"names":["white_heart"],"surrogates":"🤍"},{"names":["brown_heart"],"surrogates":"🤎"},{"names":["broken_heart"],"surrogates":"💔"},{"names":["heart_exclamation","heavy_heart_exclamation_mark_ornament"],"surrogates":"❣️"},{"names":["two_hearts"],"surrogates":"💕"},{"names":["revolving_hearts"],"surrogates":"💞"},{"names":["heartbeat","beating_heart"],"surrogates":"💓"},{"names":["heartpulse","growing_heart"],"surrogates":"💗"},{"names":["sparkling_heart"],"surrogates":"💖"},{"names":["cupid"],"surrogates":"💘"},{"names":["gift_heart"],"surrogates":"💝"},{"names":["mending_heart"],"surrogates":"❤️‍🩹"},{"names":["heart_on_fire"],"surrogates":"❤️‍🔥"},{"names":["heart_decoration"],"surrogates":"💟"},{"names":["peace","peace_symbol"],"surrogates":"☮️"},{"names":["cross","latin_cross"],"surrogates":"✝️"},{"names":["star_and_crescent"],"surrogates":"☪️"},{"names":["om_symbol"],"surrogates":"🕉️"},{"names":["wheel_of_dharma"],"surrogates":"☸️"},{"names":["khanda"],"surrogates":"🪯"},{"names":["star_of_david"],"surrogates":"✡️"},{"names":["six_pointed_star"],"surrogates":"🔯"},{"names":["menorah"],"surrogates":"🕎"},{"names":["yin_yang"],"surrogates":"☯️"},{"names":["orthodox_cross"],"surrogates":"☦️"},{"names":["place_of_worship","worship_symbol"],"surrogates":"🛐"},{"names":["ophiuchus"],"surrogates":"⛎"},{"names":["aries"],"surrogates":"♈"},{"names":["taurus"],"surrogates":"♉"},{"names":["gemini"],"surrogates":"♊"},{"names":["cancer"],"surrogates":"♋"},{"names":["leo"],"surrogates":"♌"},{"names":["virgo"],"surrogates":"♍"},{"names":["libra"],"surrogates":"♎"},{"names":["scorpius","scorpio"],"surrogates":"♏"},{"names":["sagittarius"],"surrogates":"♐"},{"names":["capricorn"],"surrogates":"♑"},{"names":["aquarius"],"surrogates":"♒"},{"names":["pisces"],"surrogates":"♓"},{"names":["id"],"surrogates":"🆔"},{"names":["atom","atom_symbol"],"surrogates":"⚛️"},{"names":["accept"],"surrogates":"🉑"},{"names":["radioactive","radioactive_sign"],"surrogates":"☢️"},{"names":["biohazard","biohazard_sign"],"surrogates":"☣️"},{"names":["mobile_phone_off"],"surrogates":"📴"},{"names":["vibration_mode"],"surrogates":"📳"},{"names":["u6709"],"surrogates":"🈶"},{"names":["u7121"],"surrogates":"🈚"},{"names":["u7533"],"surrogates":"🈸"},{"names":["u55b6"],"surrogates":"🈺"},{"names":["u6708"],"surrogates":"🈷️"},{"names":["eight_pointed_black_star"],"surrogates":"✴️"},{"names":["vs"],"surrogates":"🆚"},{"names":["white_flower"],"surrogates":"💮"},{"names":["ideograph_advantage"],"surrogates":"🉐"},{"names":["secret"],"surrogates":"㊙️"},{"names":["congratulations"],"surrogates":"㊗️"},{"names":["u5408"],"surrogates":"🈴"},{"names":["u6e80"],"surrogates":"🈵"},{"names":["u5272"],"surrogates":"🈹"},{"names":["u7981"],"surrogates":"🈲"},{"names":["a"],"surrogates":"🅰️"},{"names":["b"],"surrogates":"🅱️"},{"names":["ab"],"surrogates":"🆎"},{"names":["cl"],"surrogates":"🆑"},{"names":["o2"],"surrogates":"🅾️"},{"names":["sos"],"surrogates":"🆘"},{"names":["x","cross_mark"],"surrogates":"❌"},{"names":["o"],"surrogates":"⭕"},{"names":["octagonal_sign","stop_sign"],"surrogates":"🛑"},{"names":["no_entry"],"surrogates":"⛔"},{"names":["name_badge"],"surrogates":"📛"},{"names":["no_entry_sign","prohibited"],"surrogates":"🚫"},{"names":["100"],"surrogates":"💯"},{"names":["anger"],"surrogates":"💢"},{"names":["hotsprings","hot_springs"],"surrogates":"♨️"},{"names":["no_pedestrians"],"surrogates":"🚷"},{"names":["do_not_litter","no_littering"],"surrogates":"🚯"},{"names":["no_bicycles"],"surrogates":"🚳"},{"names":["non_potable_water"],"surrogates":"🚱"},{"names":["underage"],"surrogates":"🔞"},{"names":["no_mobile_phones"],"surrogates":"📵"},{"names":["no_smoking"],"surrogates":"🚭"},{"names":["exclamation"],"surrogates":"❗"},{"names":["grey_exclamation"],"surrogates":"❕"},{"names":["question","question_mark"],"surrogates":"❓"},{"names":["grey_question"],"surrogates":"❔"},{"names":["bangbang"],"surrogates":"‼️"},{"names":["interrobang"],"surrogates":"⁉️"},{"names":["low_brightness"],"surrogates":"🔅"},{"names":["high_brightness"],"surrogates":"🔆"},{"names":["part_alternation_mark"],"surrogates":"〽️"},{"names":["warning"],"surrogates":"⚠️"},{"names":["children_crossing"],"surrogates":"🚸"},{"names":["trident"],"surrogates":"🔱"},{"names":["fleur_de_lis"],"surrogates":"⚜️"},{"names":["beginner"],"surrogates":"🔰"},{"names":["recycle"],"surrogates":"♻️"},{"names":["white_check_mark"],"surrogates":"✅"},{"names":["u6307"],"surrogates":"🈯"},{"names":["chart"],"surrogates":"💹"},{"names":["sparkle"],"surrogates":"❇️"},{"names":["eight_spoked_asterisk"],"surrogates":"✳️"},{"names":["negative_squared_cross_mark"],"surrogates":"❎"},{"names":["globe_with_meridians"],"surrogates":"🌐"},{"names":["diamond_shape_with_a_dot_inside"],"surrogates":"💠"},{"names":["m","circled_m"],"surrogates":"Ⓜ️"},{"names":["cyclone"],"surrogates":"🌀"},{"names":["zzz"],"surrogates":"💤"},{"names":["atm"],"surrogates":"🏧"},{"names":["wc","water_closet"],"surrogates":"🚾"},{"names":["wheelchair"],"surrogates":"♿"},{"names":["parking"],"surrogates":"🅿️"},{"names":["elevator"],"surrogates":"🛗"},{"names":["u7a7a"],"surrogates":"🈳"},{"names":["sa"],"surrogates":"🈂️"},{"names":["passport_control"],"surrogates":"🛂"},{"names":["customs"],"surrogates":"🛃"},{"names":["baggage_claim"],"surrogates":"🛄"},{"names":["left_luggage"],"surrogates":"🛅"},{"names":["wireless"],"surrogates":"🛜"},{"names":["mens","mens_room"],"surrogates":"🚹"},{"names":["womens","womens_room"],"surrogates":"🚺"},{"names":["baby_symbol"],"surrogates":"🚼"},{"names":["restroom"],"surrogates":"🚻"},{"names":["put_litter_in_its_place"],"surrogates":"🚮"},{"names":["cinema"],"surrogates":"🎦"},{"names":["signal_strength","antenna_bars"],"surrogates":"📶"},{"names":["koko"],"surrogates":"🈁"},{"names":["symbols","input_symbols"],"surrogates":"🔣"},{"names":["information_source","information"],"surrogates":"ℹ️"},{"names":["abc"],"surrogates":"🔤"},{"names":["abcd"],"surrogates":"🔡"},{"names":["capital_abcd"],"surrogates":"🔠"},{"names":["ng"],"surrogates":"🆖"},{"names":["ok"],"surrogates":"🆗"},{"names":["up"],"surrogates":"🆙"},{"names":["cool"],"surrogates":"🆒"},{"names":["new"],"surrogates":"🆕"},{"names":["free"],"surrogates":"🆓"},{"names":["zero","number_0"],"surrogates":"0️⃣"},{"names":["one","number_1"],"surrogates":"1️⃣"},{"names":["two","number_2"],"surrogates":"2️⃣"},{"names":["three","number_3"],"surrogates":"3️⃣"},{"names":["four","number_4"],"surrogates":"4️⃣"},{"names":["five","number_5"],"surrogates":"5️⃣"},{"names":["six","number_6"],"surrogates":"6️⃣"},{"names":["seven","number_7"],"surrogates":"7️⃣"},{"names":["eight","number_8"],"surrogates":"8️⃣"},{"names":["nine","number_9"],"surrogates":"9️⃣"},{"names":["keycap_ten","number_10"],"surrogates":"🔟"},{"names":["1234","input_numbers"],"surrogates":"🔢"},{"names":["hash"],"surrogates":"#️⃣"},{"names":["asterisk","keycap_asterisk"],"surrogates":"*️⃣"},{"names":["eject","eject_symbol"],"surrogates":"⏏️"},{"names":["arrow_forward"],"surrogates":"▶️"},{"names":["pause_button","double_vertical_bar"],"surrogates":"⏸️"},{"names":["play_pause"],"surrogates":"⏯️"},{"names":["stop_button"],"surrogates":"⏹️"},{"names":["record_button"],"surrogates":"⏺️"},{"names":["track_next","next_track"],"surrogates":"⏭️"},{"names":["track_previous","previous_track"],"surrogates":"⏮️"},{"names":["fast_forward"],"surrogates":"⏩"},{"names":["rewind"],"surrogates":"⏪"},{"names":["arrow_double_up"],"surrogates":"⏫"},{"names":["arrow_double_down"],"surrogates":"⏬"},{"names":["arrow_backward"],"surrogates":"◀️"},{"names":["arrow_up_small"],"surrogates":"🔼"},{"names":["arrow_down_small"],"surrogates":"🔽"},{"names":["arrow_right","right_arrow"],"surrogates":"➡️"},{"names":["arrow_left","left_arrow"],"surrogates":"⬅️"},{"names":["arrow_up","up_arrow"],"surrogates":"⬆️"},{"names":["arrow_down","down_arrow"],"surrogates":"⬇️"},{"names":["arrow_upper_right"],"surrogates":"↗️"},{"names":["arrow_lower_right"],"surrogates":"↘️"},{"names":["arrow_lower_left"],"surrogates":"↙️"},{"names":["arrow_upper_left","up_left_arrow"],"surrogates":"↖️"},{"names":["arrow_up_down","up_down_arrow"],"surrogates":"↕️"},{"names":["left_right_arrow"],"surrogates":"↔️"},{"names":["arrow_right_hook"],"surrogates":"↪️"},{"names":["leftwards_arrow_with_hook"],"surrogates":"↩️"},{"names":["arrow_heading_up"],"surrogates":"⤴️"},{"names":["arrow_heading_down"],"surrogates":"⤵️"},{"names":["twisted_rightwards_arrows"],"surrogates":"🔀"},{"names":["repeat"],"surrogates":"🔁"},{"names":["repeat_one"],"surrogates":"🔂"},{"names":["arrows_counterclockwise"],"surrogates":"🔄"},{"names":["arrows_clockwise"],"surrogates":"🔃"},{"names":["musical_note"],"surrogates":"🎵"},{"names":["notes","musical_notes"],"surrogates":"🎶"},{"names":["heavy_plus_sign"],"surrogates":"➕"},{"names":["heavy_minus_sign"],"surrogates":"➖"},{"names":["heavy_division_sign"],"surrogates":"➗"},{"names":["heavy_multiplication_x"],"surrogates":"✖️"},{"names":["heavy_equals_sign"],"surrogates":"🟰"},{"names":["infinity"],"surrogates":"♾️"},{"names":["heavy_dollar_sign"],"surrogates":"💲"},{"names":["currency_exchange"],"surrogates":"💱"},{"names":["tm","trade_mark"],"surrogates":"™️"},{"names":["copyright"],"surrogates":"©️"},{"names":["registered"],"surrogates":"®️"},{"names":["wavy_dash"],"surrogates":"〰️"},{"names":["curly_loop"],"surrogates":"➰"},{"names":["loop"],"surrogates":"➿"},{"names":["end","end_arrow"],"surrogates":"🔚"},{"names":["back","back_arrow"],"surrogates":"🔙"},{"names":["on","on_arrow"],"surrogates":"🔛"},{"names":["top","top_arrow"],"surrogates":"🔝"},{"names":["soon","soon_arrow"],"surrogates":"🔜"},{"names":["heavy_check_mark","check_mark"],"surrogates":"✔️"},{"names":["ballot_box_with_check"],"surrogates":"☑️"},{"names":["radio_button"],"surrogates":"🔘"},{"names":["white_circle"],"surrogates":"⚪"},{"names":["black_circle"],"surrogates":"⚫"},{"names":["red_circle"],"surrogates":"🔴"},{"names":["blue_circle"],"surrogates":"🔵"},{"names":["brown_circle"],"surrogates":"🟤"},{"names":["purple_circle"],"surrogates":"🟣"},{"names":["green_circle"],"surrogates":"🟢"},{"names":["yellow_circle"],"surrogates":"🟡"},{"names":["orange_circle"],"surrogates":"🟠"},{"names":["small_red_triangle"],"surrogates":"🔺"},{"names":["small_red_triangle_down"],"surrogates":"🔻"},{"names":["small_orange_diamond"],"surrogates":"🔸"},{"names":["small_blue_diamond"],"surrogates":"🔹"},{"names":["large_orange_diamond"],"surrogates":"🔶"},{"names":["large_blue_diamond"],"surrogates":"🔷"},{"names":["white_square_button"],"surrogates":"🔳"},{"names":["black_square_button"],"surrogates":"🔲"},{"names":["black_small_square"],"surrogates":"▪️"},{"names":["white_small_square"],"surrogates":"▫️"},{"names":["black_medium_small_square"],"surrogates":"◾"},{"names":["white_medium_small_square"],"surrogates":"◽"},{"names":["black_medium_square"],"surrogates":"◼️"},{"names":["white_medium_square"],"surrogates":"◻️"},{"names":["black_large_square"],"surrogates":"⬛"},{"names":["white_large_square"],"surrogates":"⬜"},{"names":["orange_square"],"surrogates":"🟧"},{"names":["blue_square"],"surrogates":"🟦"},{"names":["red_square"],"surrogates":"🟥"},{"names":["brown_square"],"surrogates":"🟫"},{"names":["purple_square"],"surrogates":"🟪"},{"names":["green_square"],"surrogates":"🟩"},{"names":["yellow_square"],"surrogates":"🟨"},{"names":["speaker"],"surrogates":"🔈"},{"names":["mute","muted_speaker"],"surrogates":"🔇"},{"names":["sound"],"surrogates":"🔉"},{"names":["loud_sound"],"surrogates":"🔊"},{"names":["bell"],"surrogates":"🔔"},{"names":["no_bell"],"surrogates":"🔕"},{"names":["mega","megaphone"],"surrogates":"📣"},{"names":["loudspeaker"],"surrogates":"📢"},{"names":["speech_left","left_speech_bubble"],"surrogates":"🗨️"},{"names":["eye_in_speech_bubble"],"surrogates":"👁‍🗨"},{"names":["speech_balloon"],"surrogates":"💬"},{"names":["thought_balloon"],"surrogates":"💭"},{"names":["anger_right","right_anger_bubble"],"surrogates":"🗯️"},{"names":["spades","spade_suit"],"surrogates":"♠️"},{"names":["clubs","club_suit"],"surrogates":"♣️"},{"names":["hearts","heart_suit"],"surrogates":"♥️"},{"names":["diamonds","diamond_suit"],"surrogates":"♦️"},{"names":["black_joker","joker"],"surrogates":"🃏"},{"names":["flower_playing_cards"],"surrogates":"🎴"},{"names":["mahjong"],"surrogates":"🀄"},{"names":["clock1","one_oclock"],"surrogates":"🕐"},{"names":["clock2","two_oclock"],"surrogates":"🕑"},{"names":["clock3","three_oclock"],"surrogates":"🕒"},{"names":["clock4","four_oclock"],"surrogates":"🕓"},{"names":["clock5","five_oclock"],"surrogates":"🕔"},{"names":["clock6","six_oclock"],"surrogates":"🕕"},{"names":["clock7","seven_oclock"],"surrogates":"🕖"},{"names":["clock8","eight_oclock"],"surrogates":"🕗"},{"names":["clock9","nine_oclock"],"surrogates":"🕘"},{"names":["clock10","ten_oclock"],"surrogates":"🕙"},{"names":["clock11","eleven_oclock"],"surrogates":"🕚"},{"names":["clock12","twelve_oclock"],"surrogates":"🕛"},{"names":["clock130","one_thirty"],"surrogates":"🕜"},{"names":["clock230","two_thirty"],"surrogates":"🕝"},{"names":["clock330","three_thirty"],"surrogates":"🕞"},{"names":["clock430","four_thirty"],"surrogates":"🕟"},{"names":["clock530","five_thirty"],"surrogates":"🕠"},{"names":["clock630","six_thirty"],"surrogates":"🕡"},{"names":["clock730","seven_thirty"],"surrogates":"🕢"},{"names":["clock830","eight_thirty"],"surrogates":"🕣"},{"names":["clock930","nine_thirty"],"surrogates":"🕤"},{"names":["clock1030","ten_thirty"],"surrogates":"🕥"},{"names":["clock1130","eleven_thirty"],"surrogates":"🕦"},{"names":["clock1230","twelve_thirty"],"surrogates":"🕧"},{"names":["female_sign"],"surrogates":"♀️"},{"names":["male_sign"],"surrogates":"♂️"},{"names":["transgender_symbol"],"surrogates":"⚧️"},{"names":["medical_symbol"],"surrogates":"⚕️"},{"names":["regional_indicator_z"],"surrogates":"🇿"},{"names":["regional_indicator_y"],"surrogates":"🇾"},{"names":["regional_indicator_x"],"surrogates":"🇽"},{"names":["regional_indicator_w"],"surrogates":"🇼"},{"names":["regional_indicator_v"],"surrogates":"🇻"},{"names":["regional_indicator_u"],"surrogates":"🇺"},{"names":["regional_indicator_t"],"surrogates":"🇹"},{"names":["regional_indicator_s"],"surrogates":"🇸"},{"names":["regional_indicator_r"],"surrogates":"🇷"},{"names":["regional_indicator_q"],"surrogates":"🇶"},{"names":["regional_indicator_p"],"surrogates":"🇵"},{"names":["regional_indicator_o"],"surrogates":"🇴"},{"names":["regional_indicator_n"],"surrogates":"🇳"},{"names":["regional_indicator_m"],"surrogates":"🇲"},{"names":["regional_indicator_l"],"surrogates":"🇱"},{"names":["regional_indicator_k"],"surrogates":"🇰"},{"names":["regional_indicator_j"],"surrogates":"🇯"},{"names":["regional_indicator_i"],"surrogates":"🇮"},{"names":["regional_indicator_h"],"surrogates":"🇭"},{"names":["regional_indicator_g"],"surrogates":"🇬"},{"names":["regional_indicator_f"],"surrogates":"🇫"},{"names":["regional_indicator_e"],"surrogates":"🇪"},{"names":["regional_indicator_d"],"surrogates":"🇩"},{"names":["regional_indicator_c"],"surrogates":"🇨"},{"names":["regional_indicator_b"],"surrogates":"🇧"},{"names":["regional_indicator_a"],"surrogates":"🇦"}],"flags":[{"names":["flag_white"],"surrogates":"🏳️"},{"names":["flag_black"],"surrogates":"🏴"},{"names":["pirate_flag"],"surrogates":"🏴‍☠️"},{"names":["checkered_flag"],"surrogates":"🏁"},{"names":["triangular_flag_on_post"],"surrogates":"🚩"},{"names":["rainbow_flag","gay_pride_flag"],"surrogates":"🏳️‍🌈"},{"names":["transgender_flag"],"surrogates":"🏳️‍⚧️"},{"names":["united_nations"],"surrogates":"🇺🇳"},{"names":["flag_af"],"surrogates":"🇦🇫"},{"names":["flag_ax"],"surrogates":"🇦🇽"},{"names":["flag_al"],"surrogates":"🇦🇱"},{"names":["flag_dz"],"surrogates":"🇩🇿"},{"names":["flag_as"],"surrogates":"🇦🇸"},{"names":["flag_ad"],"surrogates":"🇦🇩"},{"names":["flag_ao"],"surrogates":"🇦🇴"},{"names":["flag_ai"],"surrogates":"🇦🇮"},{"names":["flag_aq"],"surrogates":"🇦🇶"},{"names":["flag_ag"],"surrogates":"🇦🇬"},{"names":["flag_ar"],"surrogates":"🇦🇷"},{"names":["flag_am"],"surrogates":"🇦🇲"},{"names":["flag_aw"],"surrogates":"🇦🇼"},{"names":["flag_au"],"surrogates":"🇦🇺"},{"names":["flag_at"],"surrogates":"🇦🇹"},{"names":["flag_az"],"surrogates":"🇦🇿"},{"names":["flag_bs"],"surrogates":"🇧🇸"},{"names":["flag_bh"],"surrogates":"🇧🇭"},{"names":["flag_bd"],"surrogates":"🇧🇩"},{"names":["flag_bb"],"surrogates":"🇧🇧"},{"names":["flag_by"],"surrogates":"🇧🇾"},{"names":["flag_be"],"surrogates":"🇧🇪"},{"names":["flag_bz"],"surrogates":"🇧🇿"},{"names":["flag_bj"],"surrogates":"🇧🇯"},{"names":["flag_bm"],"surrogates":"🇧🇲"},{"names":["flag_bt"],"surrogates":"🇧🇹"},{"names":["flag_bo"],"surrogates":"🇧🇴"},{"names":["flag_ba"],"surrogates":"🇧🇦"},{"names":["flag_bw"],"surrogates":"🇧🇼"},{"names":["flag_br"],"surrogates":"🇧🇷"},{"names":["flag_io"],"surrogates":"🇮🇴"},{"names":["flag_vg"],"surrogates":"🇻🇬"},{"names":["flag_bn"],"surrogates":"🇧🇳"},{"names":["flag_bg"],"surrogates":"🇧🇬"},{"names":["flag_bf"],"surrogates":"🇧🇫"},{"names":["flag_bi"],"surrogates":"🇧🇮"},{"names":["flag_kh"],"surrogates":"🇰🇭"},{"names":["flag_cm"],"surrogates":"🇨🇲"},{"names":["flag_ca"],"surrogates":"🇨🇦"},{"names":["flag_ic"],"surrogates":"🇮🇨"},{"names":["flag_cv"],"surrogates":"🇨🇻"},{"names":["flag_bq"],"surrogates":"🇧🇶"},{"names":["flag_ky"],"surrogates":"🇰🇾"},{"names":["flag_cf"],"surrogates":"🇨🇫"},{"names":["flag_td"],"surrogates":"🇹🇩"},{"names":["flag_cl"],"surrogates":"🇨🇱"},{"names":["flag_cn"],"surrogates":"🇨🇳"},{"names":["flag_cx"],"surrogates":"🇨🇽"},{"names":["flag_cc"],"surrogates":"🇨🇨"},{"names":["flag_co"],"surrogates":"🇨🇴"},{"names":["flag_km"],"surrogates":"🇰🇲"},{"names":["flag_cg"],"surrogates":"🇨🇬"},{"names":["flag_cd"],"surrogates":"🇨🇩"},{"names":["flag_ck"],"surrogates":"🇨🇰"},{"names":["flag_cr"],"surrogates":"🇨🇷"},{"names":["flag_ci"],"surrogates":"🇨🇮"},{"names":["flag_hr"],"surrogates":"🇭🇷"},{"names":["flag_cu"],"surrogates":"🇨🇺"},{"names":["flag_cw"],"surrogates":"🇨🇼"},{"names":["flag_cy"],"surrogates":"🇨🇾"},{"names":["flag_cz"],"surrogates":"🇨🇿"},{"names":["flag_dk"],"surrogates":"🇩🇰"},{"names":["flag_dj"],"surrogates":"🇩🇯"},{"names":["flag_dm"],"surrogates":"🇩🇲"},{"names":["flag_do"],"surrogates":"🇩🇴"},{"names":["flag_ec"],"surrogates":"🇪🇨"},{"names":["flag_eg"],"surrogates":"🇪🇬"},{"names":["flag_sv"],"surrogates":"🇸🇻"},{"names":["flag_gq"],"surrogates":"🇬🇶"},{"names":["flag_er"],"surrogates":"🇪🇷"},{"names":["flag_ee"],"surrogates":"🇪🇪"},{"names":["flag_et"],"surrogates":"🇪🇹"},{"names":["flag_eu"],"surrogates":"🇪🇺"},{"names":["flag_fk"],"surrogates":"🇫🇰"},{"names":["flag_fo"],"surrogates":"🇫🇴"},{"names":["flag_fj"],"surrogates":"🇫🇯"},{"names":["flag_fi"],"surrogates":"🇫🇮"},{"names":["flag_fr"],"surrogates":"🇫🇷"},{"names":["flag_gf"],"surrogates":"🇬🇫"},{"names":["flag_pf"],"surrogates":"🇵🇫"},{"names":["flag_tf"],"surrogates":"🇹🇫"},{"names":["flag_ga"],"surrogates":"🇬🇦"},{"names":["flag_gm"],"surrogates":"🇬🇲"},{"names":["flag_ge"],"surrogates":"🇬🇪"},{"names":["flag_de"],"surrogates":"🇩🇪"},{"names":["flag_gh"],"surrogates":"🇬🇭"},{"names":["flag_gi"],"surrogates":"🇬🇮"},{"names":["flag_gr"],"surrogates":"🇬🇷"},{"names":["flag_gl"],"surrogates":"🇬🇱"},{"names":["flag_gd"],"surrogates":"🇬🇩"},{"names":["flag_gp"],"surrogates":"🇬🇵"},{"names":["flag_gu"],"surrogates":"🇬🇺"},{"names":["flag_gt"],"surrogates":"🇬🇹"},{"names":["flag_gg"],"surrogates":"🇬🇬"},{"names":["flag_gn"],"surrogates":"🇬🇳"},{"names":["flag_gw"],"surrogates":"🇬🇼"},{"names":["flag_gy"],"surrogates":"🇬🇾"},{"names":["flag_ht"],"surrogates":"🇭🇹"},{"names":["flag_hn"],"surrogates":"🇭🇳"},{"names":["flag_hk"],"surrogates":"🇭🇰"},{"names":["flag_hu"],"surrogates":"🇭🇺"},{"names":["flag_is"],"surrogates":"🇮🇸"},{"names":["flag_in"],"surrogates":"🇮🇳"},{"names":["flag_id"],"surrogates":"🇮🇩"},{"names":["flag_ir"],"surrogates":"🇮🇷"},{"names":["flag_iq"],"surrogates":"🇮🇶"},{"names":["flag_ie"],"surrogates":"🇮🇪"},{"names":["flag_im"],"surrogates":"🇮🇲"},{"names":["flag_il"],"surrogates":"🇮🇱"},{"names":["flag_it"],"surrogates":"🇮🇹"},{"names":["flag_jm"],"surrogates":"🇯🇲"},{"names":["flag_jp"],"surrogates":"🇯🇵"},{"names":["crossed_flags"],"surrogates":"🎌"},{"names":["flag_je"],"surrogates":"🇯🇪"},{"names":["flag_jo"],"surrogates":"🇯🇴"},{"names":["flag_kz"],"surrogates":"🇰🇿"},{"names":["flag_ke"],"surrogates":"🇰🇪"},{"names":["flag_ki"],"surrogates":"🇰🇮"},{"names":["flag_xk"],"surrogates":"🇽🇰"},{"names":["flag_kw"],"surrogates":"🇰🇼"},{"names":["flag_kg"],"surrogates":"🇰🇬"},{"names":["flag_la"],"surrogates":"🇱🇦"},{"names":["flag_lv"],"surrogates":"🇱🇻"},{"names":["flag_lb"],"surrogates":"🇱🇧"},{"names":["flag_ls"],"surrogates":"🇱🇸"},{"names":["flag_lr"],"surrogates":"🇱🇷"},{"names":["flag_ly"],"surrogates":"🇱🇾"},{"names":["flag_li"],"surrogates":"🇱🇮"},{"names":["flag_lt"],"surrogates":"🇱🇹"},{"names":["flag_lu"],"surrogates":"🇱🇺"},{"names":["flag_mo"],"surrogates":"🇲🇴"},{"names":["flag_mk"],"surrogates":"🇲🇰"},{"names":["flag_mg"],"surrogates":"🇲🇬"},{"names":["flag_mw"],"surrogates":"🇲🇼"},{"names":["flag_my"],"surrogates":"🇲🇾"},{"names":["flag_mv"],"surrogates":"🇲🇻"},{"names":["flag_ml"],"surrogates":"🇲🇱"},{"names":["flag_mt"],"surrogates":"🇲🇹"},{"names":["flag_mh"],"surrogates":"🇲🇭"},{"names":["flag_mq"],"surrogates":"🇲🇶"},{"names":["flag_mr"],"surrogates":"🇲🇷"},{"names":["flag_mu"],"surrogates":"🇲🇺"},{"names":["flag_yt"],"surrogates":"🇾🇹"},{"names":["flag_mx"],"surrogates":"🇲🇽"},{"names":["flag_fm"],"surrogates":"🇫🇲"},{"names":["flag_md"],"surrogates":"🇲🇩"},{"names":["flag_mc"],"surrogates":"🇲🇨"},{"names":["flag_mn"],"surrogates":"🇲🇳"},{"names":["flag_me"],"surrogates":"🇲🇪"},{"names":["flag_ms"],"surrogates":"🇲🇸"},{"names":["flag_ma"],"surrogates":"🇲🇦"},{"names":["flag_mz"],"surrogates":"🇲🇿"},{"names":["flag_mm"],"surrogates":"🇲🇲"},{"names":["flag_na"],"surrogates":"🇳🇦"},{"names":["flag_nr"],"surrogates":"🇳🇷"},{"names":["flag_np"],"surrogates":"🇳🇵"},{"names":["flag_nl"],"surrogates":"🇳🇱"},{"names":["flag_nc"],"surrogates":"🇳🇨"},{"names":["flag_nz"],"surrogates":"🇳🇿"},{"names":["flag_ni"],"surrogates":"🇳🇮"},{"names":["flag_ne"],"surrogates":"🇳🇪"},{"names":["flag_ng"],"surrogates":"🇳🇬"},{"names":["flag_nu"],"surrogates":"🇳🇺"},{"names":["flag_nf"],"surrogates":"🇳🇫"},{"names":["flag_kp"],"surrogates":"🇰🇵"},{"names":["flag_mp"],"surrogates":"🇲🇵"},{"names":["flag_no"],"surrogates":"🇳🇴"},{"names":["flag_om"],"surrogates":"🇴🇲"},{"names":["flag_pk"],"surrogates":"🇵🇰"},{"names":["flag_pw"],"surrogates":"🇵🇼"},{"names":["flag_ps"],"surrogates":"🇵🇸"},{"names":["flag_pa"],"surrogates":"🇵🇦"},{"names":["flag_pg"],"surrogates":"🇵🇬"},{"names":["flag_py"],"surrogates":"🇵🇾"},{"names":["flag_pe"],"surrogates":"🇵🇪"},{"names":["flag_ph"],"surrogates":"🇵🇭"},{"names":["flag_pn"],"surrogates":"🇵🇳"},{"names":["flag_pl"],"surrogates":"🇵🇱"},{"names":["flag_pt"],"surrogates":"🇵🇹"},{"names":["flag_pr"],"surrogates":"🇵🇷"},{"names":["flag_qa"],"surrogates":"🇶🇦"},{"names":["flag_re"],"surrogates":"🇷🇪"},{"names":["flag_ro"],"surrogates":"🇷🇴"},{"names":["flag_ru"],"surrogates":"🇷🇺"},{"names":["flag_rw"],"surrogates":"🇷🇼"},{"names":["flag_ws"],"surrogates":"🇼🇸"},{"names":["flag_sm"],"surrogates":"🇸🇲"},{"names":["flag_st"],"surrogates":"🇸🇹"},{"names":["flag_sa"],"surrogates":"🇸🇦"},{"names":["flag_sn"],"surrogates":"🇸🇳"},{"names":["flag_rs"],"surrogates":"🇷🇸"},{"names":["flag_sc"],"surrogates":"🇸🇨"},{"names":["flag_sl"],"surrogates":"🇸🇱"},{"names":["flag_sg"],"surrogates":"🇸🇬"},{"names":["flag_sx"],"surrogates":"🇸🇽"},{"names":["flag_sk"],"surrogates":"🇸🇰"},{"names":["flag_si"],"surrogates":"🇸🇮"},{"names":["flag_gs"],"surrogates":"🇬🇸"},{"names":["flag_sb"],"surrogates":"🇸🇧"},{"names":["flag_so"],"surrogates":"🇸🇴"},{"names":["flag_za"],"surrogates":"🇿🇦"},{"names":["flag_kr"],"surrogates":"🇰🇷"},{"names":["flag_ss"],"surrogates":"🇸🇸"},{"names":["flag_es"],"surrogates":"🇪🇸"},{"names":["flag_lk"],"surrogates":"🇱🇰"},{"names":["flag_bl"],"surrogates":"🇧🇱"},{"names":["flag_sh"],"surrogates":"🇸🇭"},{"names":["flag_kn"],"surrogates":"🇰🇳"},{"names":["flag_lc"],"surrogates":"🇱🇨"},{"names":["flag_pm"],"surrogates":"🇵🇲"},{"names":["flag_vc"],"surrogates":"🇻🇨"},{"names":["flag_sd"],"surrogates":"🇸🇩"},{"names":["flag_sr"],"surrogates":"🇸🇷"},{"names":["flag_sz"],"surrogates":"🇸🇿"},{"names":["flag_se"],"surrogates":"🇸🇪"},{"names":["flag_ch"],"surrogates":"🇨🇭"},{"names":["flag_sy"],"surrogates":"🇸🇾"},{"names":["flag_tw"],"surrogates":"🇹🇼"},{"names":["flag_tj"],"surrogates":"🇹🇯"},{"names":["flag_tz"],"surrogates":"🇹🇿"},{"names":["flag_th"],"surrogates":"🇹🇭"},{"names":["flag_tl"],"surrogates":"🇹🇱"},{"names":["flag_tg"],"surrogates":"🇹🇬"},{"names":["flag_tk"],"surrogates":"🇹🇰"},{"names":["flag_to"],"surrogates":"🇹🇴"},{"names":["flag_tt"],"surrogates":"🇹🇹"},{"names":["flag_tn"],"surrogates":"🇹🇳"},{"names":["flag_tr"],"surrogates":"🇹🇷"},{"names":["flag_tm"],"surrogates":"🇹🇲"},{"names":["flag_tc"],"surrogates":"🇹🇨"},{"names":["flag_vi"],"surrogates":"🇻🇮"},{"names":["flag_tv"],"surrogates":"🇹🇻"},{"names":["flag_ug"],"surrogates":"🇺🇬"},{"names":["flag_ua"],"surrogates":"🇺🇦"},{"names":["flag_ae"],"surrogates":"🇦🇪"},{"names":["flag_gb"],"surrogates":"🇬🇧"},{"names":["england"],"surrogates":"🏴󠁧󠁢󠁥󠁮󠁧󠁿"},{"names":["scotland"],"surrogates":"🏴󠁧󠁢󠁳󠁣󠁴󠁿"},{"names":["wales"],"surrogates":"🏴󠁧󠁢󠁷󠁬󠁳󠁿"},{"names":["flag_us"],"surrogates":"🇺🇸"},{"names":["flag_uy"],"surrogates":"🇺🇾"},{"names":["flag_uz"],"surrogates":"🇺🇿"},{"names":["flag_vu"],"surrogates":"🇻🇺"},{"names":["flag_va"],"surrogates":"🇻🇦"},{"names":["flag_ve"],"surrogates":"🇻🇪"},{"names":["flag_vn"],"surrogates":"🇻🇳"},{"names":["flag_wf"],"surrogates":"🇼🇫"},{"names":["flag_eh"],"surrogates":"🇪🇭"},{"names":["flag_ye"],"surrogates":"🇾🇪"},{"names":["flag_zm"],"surrogates":"🇿🇲"},{"names":["flag_zw"],"surrogates":"🇿🇼"},{"names":["flag_ac"],"surrogates":"🇦🇨"},{"names":["flag_bv"],"surrogates":"🇧🇻"},{"names":["flag_cp"],"surrogates":"🇨🇵"},{"names":["flag_ea"],"surrogates":"🇪🇦"},{"names":["flag_dg"],"surrogates":"🇩🇬"},{"names":["flag_hm"],"surrogates":"🇭🇲"},{"names":["flag_mf"],"surrogates":"🇲🇫"},{"names":["flag_sj"],"surrogates":"🇸🇯"},{"names":["flag_ta"],"surrogates":"🇹🇦"},{"names":["flag_um"],"surrogates":"🇺🇲"}]} diff --git a/packages/shared/src/_shims/app/hooks/useMxcUrl.ts b/packages/shared/src/_shims/app/hooks/useMxcUrl.ts new file mode 100644 index 0000000..0797e07 --- /dev/null +++ b/packages/shared/src/_shims/app/hooks/useMxcUrl.ts @@ -0,0 +1,11 @@ +/** + * useMxcUrl shim — the new UI's Matrix media URL resolver. + * In Convex we already have direct URLs from storage, so this passes through. + */ + +export function useMxcUrl(src: string | null | undefined): string | undefined { + if (!src) return undefined; + return src; +} + +export default useMxcUrl; diff --git a/packages/shared/src/_shims/app/utils/dmOtherUser.ts b/packages/shared/src/_shims/app/utils/dmOtherUser.ts new file mode 100644 index 0000000..a62d15e --- /dev/null +++ b/packages/shared/src/_shims/app/utils/dmOtherUser.ts @@ -0,0 +1,20 @@ +/** + * dmOtherUser shim — given a DM channel, return the other participant. + * Real implementation reads from Convex dm query. For now, return null. + */ + +import type { Channel, Member } from '../../matrix-client'; + +export function dmOtherUser(_channel: Channel | null): Member | null { + return null; +} + +export function getDmOtherUser(_channel: Channel | null): Member | null { + return null; +} + +export function getDmOtherUserId(_channel: Channel | null): string | null { + return null; +} + +export default dmOtherUser; diff --git a/packages/shared/src/_shims/app/utils/joinSound.ts b/packages/shared/src/_shims/app/utils/joinSound.ts new file mode 100644 index 0000000..30d7ee3 --- /dev/null +++ b/packages/shared/src/_shims/app/utils/joinSound.ts @@ -0,0 +1,41 @@ +/** + * joinSound shim — voice channel join-sound manager + preferences. + */ + +export function playJoinSound(_url?: string | null): void {} +export function playLeaveSound(): void {} +export function stopAllSounds(): void {} + +export function getJoinSoundEnabled(): boolean { + if (typeof localStorage === 'undefined') return true; + const v = localStorage.getItem('joinSoundEnabled'); + return v === null ? true : v === 'true'; +} + +export function setJoinSoundEnabled(enabled: boolean): void { + if (typeof localStorage !== 'undefined') { + localStorage.setItem('joinSoundEnabled', enabled ? 'true' : 'false'); + } +} + +export function getJoinSoundVolume(): number { + if (typeof localStorage === 'undefined') return 100; + const v = parseInt(localStorage.getItem('joinSoundVolume') || '100', 10); + return Number.isFinite(v) ? v : 100; +} + +export function setJoinSoundVolume(volume: number): void { + if (typeof localStorage !== 'undefined') { + localStorage.setItem('joinSoundVolume', String(volume)); + } +} + +export default { + playJoinSound, + playLeaveSound, + stopAllSounds, + getJoinSoundEnabled, + setJoinSoundEnabled, + getJoinSoundVolume, + setJoinSoundVolume, +}; diff --git a/packages/shared/src/_shims/app/utils/twemojiBroken.ts b/packages/shared/src/_shims/app/utils/twemojiBroken.ts new file mode 100644 index 0000000..39bcb7e --- /dev/null +++ b/packages/shared/src/_shims/app/utils/twemojiBroken.ts @@ -0,0 +1,22 @@ +/** + * twemojiBroken shim — tracks which emoji codepoints Twemoji fails on. + */ + +import { useState } from 'react'; + +const broken: Set<string> = new Set(); + +export function isTwemojiBroken(codepoint: string): boolean { + return broken.has(codepoint); +} + +export function markTwemojiBroken(codepoint: string): void { + broken.add(codepoint); +} + +export function useBrokenTwemojiVersion(): number { + const [version] = useState(0); + return version; +} + +export default { isTwemojiBroken, markTwemojiBroken, useBrokenTwemojiVersion }; diff --git a/packages/shared/src/_shims/matrix-client.ts b/packages/shared/src/_shims/matrix-client.ts new file mode 100644 index 0000000..18ad51c --- /dev/null +++ b/packages/shared/src/_shims/matrix-client.ts @@ -0,0 +1,399 @@ +/** + * Matrix client shim. + * + * The new UI was built against a Matrix client SDK (`@brycord/matrix-client`). + * This project uses Convex instead. This file provides stub types and + * manager classes so the UI compiles. Every component that reads from these + * stubs must eventually be rewired to use Convex queries/mutations — but + * having them here lets the big-bang UI replacement compile in one step. + * + * Each manager is a singleton exposing `getInstance()` + no-op methods that + * return sensible empty values (empty arrays, null, false). UI still renders. + */ + +// ─── Shared primitive types ─────────────────────────────────────────────── + +export type ChannelType = 'text' | 'voice' | 'category' | 'dm' | 'group_dm'; + +export interface Channel { + id: string; + roomId: string; + name: string; + type: ChannelType; + topic?: string; + parentId?: string | null; + position?: number; + nsfw?: boolean; + slowmode?: number; + icon?: string | null; + isDM?: boolean; + avatarUrl?: string | null; + lastMessageTs?: number; + memberCount?: number; + userLimit?: number; + bitrate?: number; + rateLimitPerUser?: number; +} + +export interface Server { + id: string; + name: string; + icon?: string | null; + avatarUrl?: string | null; + memberCount?: number; + onlineCount?: number; + ownerId?: string; + description?: string; + roomIds?: string[]; +} + +export interface Member { + userId: string; + id: string; + displayName: string; + name?: string; + avatarUrl?: string | null; + powerLevel?: number; + roles?: string[]; + presence?: PresenceStatus; + status?: string; + accentColor?: string; + bio?: string; + joinedAt?: number; +} + +export interface Attachment { + id?: string; + url: string; + name: string; + filename?: string; + size: number; + mimeType: string; + width?: number; + height?: number; + duration?: number; + waveform?: number[]; + thumbnailUrl?: string | null; + spoiler?: boolean; + description?: string; +} + +export interface Message { + id: string; + eventId: string; + channelId: string; + senderId: string; + authorId: string; + authorName?: string; + authorAvatarUrl?: string | null; + content: string; + body?: string; + formattedBody?: string; + timestamp: number; + editedTimestamp?: number | null; + attachments?: Attachment[]; + replyToId?: string | null; + reactions?: MessageReaction[]; + mentions?: string[]; + mentionRoles?: string[]; + pinned?: boolean; + type?: string; + isRedacted?: boolean; + isEncrypted?: boolean; + isPending?: boolean; + error?: string | null; +} + +export interface MessageReaction { + key: string; + count: number; + userIds: string[]; + me: boolean; +} + +export interface CustomEmoji { + id: string; + name: string; + shortcode: string; + url: string; + animated?: boolean; + packId?: string; +} + +export interface EmojiPack { + id: string; + name: string; + emojis: CustomEmoji[]; +} + +export type SavedMediaKind = 'image' | 'video' | 'audio' | 'gif' | 'sticker'; + +export interface SavedMediaItem { + id: string; + kind: SavedMediaKind; + url: string; + mimeType?: string; + name?: string; + width?: number; + height?: number; + addedAt: number; +} + +export interface Role { + id: string; + name: string; + color: string; + position: number; + permissions: number; + hoist?: boolean; + mentionable?: boolean; + powerLevel?: number; +} + +export interface PowerLevelAbilities { + canKick: boolean; + canBan: boolean; + canInvite: boolean; + canRedact: boolean; + canSetState: boolean; + canManageRoles: boolean; + canManageChannels: boolean; +} + +export type PresenceStatus = 'online' | 'idle' | 'dnd' | 'offline' | 'invisible' | 'unavailable'; + +export interface PollAnswer { + id: string; + text: string; +} + +export interface Poll { + id: string; + question: string; + answers: PollAnswer[]; + multiple: boolean; + endsAt?: number; + votes?: Record<string, string[]>; +} + +export interface DeviceTrustInfo { + deviceId: string; + userId: string; + trusted: boolean; + verified: boolean; + ed25519Key?: string; +} + +export type VoiceConnectionState = + | 'disconnected' + | 'connecting' + | 'connected' + | 'reconnecting' + | 'disconnecting' + | 'failed' + | 'rejoinable'; + +export type VoiceErrorReason = 'token' | 'network' | 'permission' | 'unknown'; + +export interface VoiceError { + reason: VoiceErrorReason; + message: string; +} + +export interface VoiceParticipantSnapshot { + userId: string; + identity: string; + displayName: string; + avatarUrl?: string | null; + isMuted: boolean; + isDeafened: boolean; + isSpeaking: boolean; + isScreenSharing: boolean; + isCameraOn: boolean; + isLocal: boolean; + connectionQuality?: 'excellent' | 'good' | 'poor' | 'lost' | 'unknown'; + volume?: number; +} + +// ─── Manager singletons (stubs) ─────────────────────────────────────────── + +function singleton<T extends object>(factory: () => T): { getInstance(): T } { + let inst: T | null = null; + return { + getInstance() { + if (!inst) inst = factory(); + return inst; + }, + }; +} + +export const MatrixClientManager = singleton(() => ({ + getClient: () => null as any, + getUserId: () => (typeof localStorage !== 'undefined' ? localStorage.getItem('userId') || '' : ''), + getDisplayName: () => (typeof localStorage !== 'undefined' ? localStorage.getItem('username') || '' : ''), + getDeviceId: () => '', + getHomeserverUrl: () => '', + isReady: () => true, + start: async () => {}, + stop: async () => {}, + logout: async () => {}, +})); + +export const RoomManager = singleton(() => ({ + getRoom: (_id: string): Channel | null => null, + listRooms: (): Channel[] => [], + createChannel: async (_args: unknown): Promise<string> => '', + renameChannel: async (_id: string, _name: string) => {}, + deleteChannel: async (_id: string) => {}, + setTopic: async (_id: string, _topic: string) => {}, + getPowerLevel: (_id: string) => 0, + getStateDefault: (_id: string) => 0, + canManageChannel: (_id: string) => true, +})); + +export const MemberManager = singleton(() => ({ + getMember: (_roomId: string, _userId: string): Member | null => null, + listMembers: (_roomId: string): Member[] => [], + getMemberSnapshot: async (_roomId: string, _userId: string): Promise<Member | null> => null, + setNickname: async (_roomId: string, _userId: string, _nickname: string) => {}, + kick: async (_roomId: string, _userId: string, _reason?: string) => {}, + ban: async (_roomId: string, _userId: string, _reason?: string) => {}, +})); + +export const MessageManager = singleton(() => ({ + listMessages: (_channelId: string): Message[] => [], + sendMessage: async (_channelId: string, _body: string): Promise<string> => '', + editMessage: async (_channelId: string, _id: string, _body: string) => {}, + deleteMessage: async (_channelId: string, _id: string) => {}, + addReaction: async (_channelId: string, _id: string, _key: string) => {}, + removeReaction: async (_channelId: string, _id: string, _key: string) => {}, +})); + +export const MediaManager = singleton(() => ({ + resolveMxcToObjectUrl: async (_mxc: string): Promise<string | null> => null, + resolveMxcThumbnailToObjectUrl: async ( + _mxc: string, + _w: number, + _h: number, + _mode?: string, + ): Promise<string | null> => null, + uploadFile: async (_file: File): Promise<string> => '', + getFileUrl: (_url: string): string => _url, +})); + +export const CryptoManager = singleton(() => ({ + isReady: () => true, + getDeviceTrustInfo: (_userId: string, _deviceId: string): DeviceTrustInfo | null => null, + verifyDevice: async (_userId: string, _deviceId: string) => {}, + exportRoomKeys: async (): Promise<string> => '', + importRoomKeys: async (_json: string) => {}, +})); + +export const PinsManager = singleton(() => ({ + getPinned: (_channelId: string): Message[] => [], + pin: async (_channelId: string, _messageId: string) => {}, + unpin: async (_channelId: string, _messageId: string) => {}, +})); + +export const SavedMediaManager = singleton(() => ({ + list: (_kind?: SavedMediaKind): SavedMediaItem[] => [], + save: async (_item: SavedMediaItem) => {}, + remove: async (_id: string) => {}, + has: (_url: string): boolean => false, +})); + +export const FriendManager = singleton(() => ({ + listFriends: (): Member[] => [], + listPending: (): Member[] => [], + listIgnored: (): Member[] => [], + addFriend: async (_userId: string) => {}, + acceptFriend: async (_userId: string) => {}, + ignoreFriend: async (_userId: string) => {}, + removeFriend: async (_userId: string) => {}, + unignoreFriend: async (_userId: string) => {}, +})); + +export const PersonalNotesManager = singleton(() => ({ + getRoomId: (): string | null => null, + ensureRoom: async (): Promise<string> => '', +})); + +export const SpaceManager = singleton(() => ({ + listSpaces: (): Server[] => [], + getSpace: (_id: string): Server | null => null, + createSpace: async (_name: string): Promise<string> => '', + leaveSpace: async (_id: string) => {}, +})); + +export const PresenceManager = singleton(() => ({ + getPresence: (_userId: string): PresenceStatus => 'offline', + setPresence: async (_status: PresenceStatus) => {}, +})); + +export const RoleManager = singleton(() => ({ + listRoles: (_serverId: string): Role[] => [], + getRole: (_serverId: string, _roleId: string): Role | null => null, + createRole: async (_serverId: string, _name: string): Promise<string> => '', + updateRole: async (_serverId: string, _roleId: string, _patch: Partial<Role>) => {}, + deleteRole: async (_serverId: string, _roleId: string) => {}, + assignRole: async (_serverId: string, _userId: string, _roleId: string) => {}, + unassignRole: async (_serverId: string, _userId: string, _roleId: string) => {}, + getMemberRoles: (_serverId: string, _userId: string): string[] => [], + getAbilitiesForMember: (_serverId: string, _userId: string): PowerLevelAbilities => ({ + canKick: false, + canBan: false, + canInvite: false, + canRedact: false, + canSetState: false, + canManageRoles: false, + canManageChannels: false, + }), +})); + +export const VoiceManager = singleton(() => ({ + connect: async (_channelId: string) => {}, + disconnect: async () => {}, + isConnected: (): boolean => false, + getConnectionState: (): VoiceConnectionState => 'disconnected', + getParticipants: (_channelId: string): VoiceParticipantSnapshot[] => [], + setMuted: async (_muted: boolean) => {}, + setDeafened: async (_deafened: boolean) => {}, + setCameraOn: async (_on: boolean) => {}, + setScreenShare: async (_on: boolean) => {}, +})); + +export const EmojiPackManager = singleton(() => ({ + listPacks: (): EmojiPack[] => [], + getPack: (_id: string): EmojiPack | null => null, +})); + +export const VoiceModerationManager = singleton(() => ({ + serverMute: async (_userId: string) => {}, + serverUnmute: async (_userId: string) => {}, + moveMember: async (_userId: string, _channelId: string) => {}, + disconnectMember: async (_userId: string) => {}, +})); + +// ─── Constants / helpers ────────────────────────────────────────────────── + +export const MAX_EMOJIS_PER_PACK = 100; + +/** Classify a MIME type into a SavedMediaKind. */ +export function classifyMediaKind(mimeType: string): SavedMediaKind { + if (mimeType.startsWith('image/gif')) return 'gif'; + if (mimeType.startsWith('image/')) return 'image'; + if (mimeType.startsWith('video/')) return 'video'; + if (mimeType.startsWith('audio/')) return 'audio'; + return 'image'; +} + +/** Parse a LiveKit participant identity in the format `@user:server:device`. */ +export function parseMatrixUserFromIdentity(identity: string): { userId: string; deviceId: string } | null { + if (!identity) return null; + const lastColon = identity.lastIndexOf(':'); + if (lastColon === -1) return null; + return { + userId: identity.slice(0, lastColon), + deviceId: identity.slice(lastColon + 1), + }; +} diff --git a/packages/shared/src/_shims/mobx-react-lite.ts b/packages/shared/src/_shims/mobx-react-lite.ts new file mode 100644 index 0000000..188b518 --- /dev/null +++ b/packages/shared/src/_shims/mobx-react-lite.ts @@ -0,0 +1,19 @@ +/** + * mobx-react-lite shim — replaces MobX observer with an identity HOC. + * Our stores are plain objects (not reactive), so observer() becomes a no-op. + * Components still re-render on React state changes from Convex hooks. + */ + +import type { ComponentType, FC } from 'react'; + +export function observer<T extends ComponentType<any>>(component: T): T { + return component; +} + +export function useLocalObservable<T>(factory: () => T): T { + return factory(); +} + +export const Observer: FC<{ children: () => JSX.Element | null }> = ({ children }) => children() as any; + +export default { observer, useLocalObservable, Observer }; diff --git a/packages/shared/src/_shims/mobx.ts b/packages/shared/src/_shims/mobx.ts new file mode 100644 index 0000000..89cc971 --- /dev/null +++ b/packages/shared/src/_shims/mobx.ts @@ -0,0 +1,46 @@ +/** + * mobx shim — makeAutoObservable etc. become no-ops. + */ + +export function makeAutoObservable<T>(obj: T): T { + return obj; +} + +export function makeObservable<T>(obj: T): T { + return obj; +} + +export function observable<T>(v: T): T { + return v; +} + +export function action<T extends (...args: any[]) => any>(fn: T): T { + return fn; +} + +export function computed<T>(fn: () => T): { get(): T } { + return { get: fn }; +} + +export function runInAction<T>(fn: () => T): T { + return fn(); +} + +export function reaction<T>(_expression: () => T, _effect: (v: T) => void): () => void { + return () => {}; +} + +export function autorun(_fn: () => void): () => void { + return () => {}; +} + +export default { + makeAutoObservable, + makeObservable, + observable, + action, + computed, + runInAction, + reaction, + autorun, +}; diff --git a/packages/shared/src/_shims/stores/AuthenticationStore.ts b/packages/shared/src/_shims/stores/AuthenticationStore.ts new file mode 100644 index 0000000..dbd6a88 --- /dev/null +++ b/packages/shared/src/_shims/stores/AuthenticationStore.ts @@ -0,0 +1,3 @@ +import { AuthenticationStore } from "."; +export default AuthenticationStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/ChannelStore.ts b/packages/shared/src/_shims/stores/ChannelStore.ts new file mode 100644 index 0000000..70e5e9f --- /dev/null +++ b/packages/shared/src/_shims/stores/ChannelStore.ts @@ -0,0 +1,3 @@ +import { ChannelStore } from "."; +export default ChannelStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/CryptoStore.ts b/packages/shared/src/_shims/stores/CryptoStore.ts new file mode 100644 index 0000000..9f4f173 --- /dev/null +++ b/packages/shared/src/_shims/stores/CryptoStore.ts @@ -0,0 +1,3 @@ +import { CryptoStore } from "."; +export default CryptoStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/EmojiPackStore.ts b/packages/shared/src/_shims/stores/EmojiPackStore.ts new file mode 100644 index 0000000..ba4ee23 --- /dev/null +++ b/packages/shared/src/_shims/stores/EmojiPackStore.ts @@ -0,0 +1,3 @@ +import { EmojiPackStore } from "."; +export default EmojiPackStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/FriendStore.ts b/packages/shared/src/_shims/stores/FriendStore.ts new file mode 100644 index 0000000..67ff131 --- /dev/null +++ b/packages/shared/src/_shims/stores/FriendStore.ts @@ -0,0 +1,3 @@ +import { FriendStore } from "."; +export default FriendStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/KeybindStore.ts b/packages/shared/src/_shims/stores/KeybindStore.ts new file mode 100644 index 0000000..53d2376 --- /dev/null +++ b/packages/shared/src/_shims/stores/KeybindStore.ts @@ -0,0 +1,28 @@ +import { KeybindStore } from '.'; + +export type KeybindAction = string; +export type KeybindCategory = string; + +export function formatCombo(combo: string): string { + return combo + .split('+') + .map((t) => t.trim()) + .filter(Boolean) + .map((t) => (t.length === 1 ? t.toUpperCase() : t[0].toUpperCase() + t.slice(1))) + .join('+'); +} + +export function eventToCombo(e: KeyboardEvent): string { + const parts: string[] = []; + if (e.ctrlKey) parts.push('ctrl'); + if (e.shiftKey) parts.push('shift'); + if (e.altKey) parts.push('alt'); + if (e.metaKey) parts.push('meta'); + if (e.key && !['Control', 'Shift', 'Alt', 'Meta'].includes(e.key)) { + parts.push(e.key.toLowerCase()); + } + return parts.join('+'); +} + +export default KeybindStore; +export { KeybindStore }; diff --git a/packages/shared/src/_shims/stores/MatrixConnectionStore.ts b/packages/shared/src/_shims/stores/MatrixConnectionStore.ts new file mode 100644 index 0000000..9c5cdd0 --- /dev/null +++ b/packages/shared/src/_shims/stores/MatrixConnectionStore.ts @@ -0,0 +1,3 @@ +import { MatrixConnectionStore } from "."; +export default MatrixConnectionStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/MessageStore.ts b/packages/shared/src/_shims/stores/MessageStore.ts new file mode 100644 index 0000000..35f585e --- /dev/null +++ b/packages/shared/src/_shims/stores/MessageStore.ts @@ -0,0 +1,3 @@ +import { MessageStore } from "."; +export default MessageStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/PendingAttachmentStore.ts b/packages/shared/src/_shims/stores/PendingAttachmentStore.ts new file mode 100644 index 0000000..f3b5359 --- /dev/null +++ b/packages/shared/src/_shims/stores/PendingAttachmentStore.ts @@ -0,0 +1,5 @@ +import { PendingAttachmentStore, type PendingAttachment } from '.'; + +export type { PendingAttachment }; +export default PendingAttachmentStore; +export { PendingAttachmentStore }; diff --git a/packages/shared/src/_shims/stores/PersonalNotesStore.ts b/packages/shared/src/_shims/stores/PersonalNotesStore.ts new file mode 100644 index 0000000..9642228 --- /dev/null +++ b/packages/shared/src/_shims/stores/PersonalNotesStore.ts @@ -0,0 +1,3 @@ +import { PersonalNotesStore } from "."; +export default PersonalNotesStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/PinStore.ts b/packages/shared/src/_shims/stores/PinStore.ts new file mode 100644 index 0000000..3e44375 --- /dev/null +++ b/packages/shared/src/_shims/stores/PinStore.ts @@ -0,0 +1,3 @@ +import { PinStore } from "."; +export default PinStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/ReadStateStore.ts b/packages/shared/src/_shims/stores/ReadStateStore.ts new file mode 100644 index 0000000..34aa635 --- /dev/null +++ b/packages/shared/src/_shims/stores/ReadStateStore.ts @@ -0,0 +1,3 @@ +import { ReadStateStore } from "."; +export default ReadStateStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/RoleStore.ts b/packages/shared/src/_shims/stores/RoleStore.ts new file mode 100644 index 0000000..ec0c412 --- /dev/null +++ b/packages/shared/src/_shims/stores/RoleStore.ts @@ -0,0 +1,3 @@ +import { RoleStore } from "."; +export default RoleStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/SavedMediaStore.ts b/packages/shared/src/_shims/stores/SavedMediaStore.ts new file mode 100644 index 0000000..90d587f --- /dev/null +++ b/packages/shared/src/_shims/stores/SavedMediaStore.ts @@ -0,0 +1,3 @@ +import { SavedMediaStore } from "."; +export default SavedMediaStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/SearchStore.ts b/packages/shared/src/_shims/stores/SearchStore.ts new file mode 100644 index 0000000..43b30b2 --- /dev/null +++ b/packages/shared/src/_shims/stores/SearchStore.ts @@ -0,0 +1,11 @@ +import { SearchStore } from '.'; +import type { Message } from '../matrix-client'; + +export interface SearchResult { + message: Message; + channelId: string; + matches?: string[]; +} + +export default SearchStore; +export { SearchStore }; diff --git a/packages/shared/src/_shims/stores/SelectionStore.ts b/packages/shared/src/_shims/stores/SelectionStore.ts new file mode 100644 index 0000000..1d5d2b5 --- /dev/null +++ b/packages/shared/src/_shims/stores/SelectionStore.ts @@ -0,0 +1,3 @@ +import { SelectionStore } from "."; +export default SelectionStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/ServerStore.ts b/packages/shared/src/_shims/stores/ServerStore.ts new file mode 100644 index 0000000..d9808e0 --- /dev/null +++ b/packages/shared/src/_shims/stores/ServerStore.ts @@ -0,0 +1,3 @@ +import { ServerStore } from "."; +export default ServerStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/ThemeStore.ts b/packages/shared/src/_shims/stores/ThemeStore.ts new file mode 100644 index 0000000..cf65fbf --- /dev/null +++ b/packages/shared/src/_shims/stores/ThemeStore.ts @@ -0,0 +1,3 @@ +import { ThemeStore } from "."; +export default ThemeStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/TypingStore.ts b/packages/shared/src/_shims/stores/TypingStore.ts new file mode 100644 index 0000000..0884d94 --- /dev/null +++ b/packages/shared/src/_shims/stores/TypingStore.ts @@ -0,0 +1,3 @@ +import { TypingStore } from "."; +export default TypingStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/UserStore.ts b/packages/shared/src/_shims/stores/UserStore.ts new file mode 100644 index 0000000..000969e --- /dev/null +++ b/packages/shared/src/_shims/stores/UserStore.ts @@ -0,0 +1,3 @@ +import { UserStore } from "."; +export default UserStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/VoiceSettingsStore.ts b/packages/shared/src/_shims/stores/VoiceSettingsStore.ts new file mode 100644 index 0000000..d8e86af --- /dev/null +++ b/packages/shared/src/_shims/stores/VoiceSettingsStore.ts @@ -0,0 +1,6 @@ +import { VoiceSettingsStore } from '.'; + +export type InputMode = 'voice_activity' | 'push_to_talk'; + +export default VoiceSettingsStore; +export { VoiceSettingsStore }; diff --git a/packages/shared/src/_shims/stores/VoiceStore.ts b/packages/shared/src/_shims/stores/VoiceStore.ts new file mode 100644 index 0000000..1263afd --- /dev/null +++ b/packages/shared/src/_shims/stores/VoiceStore.ts @@ -0,0 +1,3 @@ +import { VoiceStore } from "."; +export default VoiceStore; +export * from "."; diff --git a/packages/shared/src/_shims/stores/index.ts b/packages/shared/src/_shims/stores/index.ts new file mode 100644 index 0000000..65967d8 --- /dev/null +++ b/packages/shared/src/_shims/stores/index.ts @@ -0,0 +1,292 @@ +/** + * Stores shim index. + * + * The new UI uses MobX stores. This project uses Convex + React Contexts. + * Each store here is a plain object with getter methods returning sane + * defaults. Components rewired to Convex will stop reading these stubs. + */ + +import type { + Channel, + Server, + Member, + Message, + Role, + PresenceStatus, + VoiceParticipantSnapshot, + CustomEmoji, + EmojiPack, + SavedMediaItem, + SavedMediaKind, +} from '../matrix-client'; + +// ─── Helper: reactive-compatible getter wrappers ────────────────────────── +// The original stores used MobX `makeAutoObservable`. Components call these +// getters inside `observer()` wrappers. Our shims just return plain values, +// and we remove `observer` via the mobx-react-lite shim. + +const ok = <T>(v: T): T => v; + +// ─── AuthenticationStore ───────────────────────────────────────────────── +// isAuthenticated is a getter so the latest sessionStorage state is read +// every access. Keeps the new AppLayout redirect logic in sync with the +// existing session-restore flow in App.tsx (which sets sessionStorage). +export const AuthenticationStore = { + get isAuthenticated(): boolean { + if (typeof sessionStorage === 'undefined') return false; + return !!(sessionStorage.getItem('privateKey') && sessionStorage.getItem('signingKey')); + }, + get userId(): string { + return typeof localStorage !== 'undefined' ? localStorage.getItem('userId') || '' : ''; + }, + get username(): string { + return typeof localStorage !== 'undefined' ? localStorage.getItem('username') || '' : ''; + }, + isLoading: false, + login: async (_username: string, _password: string) => {}, + register: async (_username: string, _password: string) => {}, + logout: async () => {}, + getUserId: () => (typeof localStorage !== 'undefined' ? localStorage.getItem('userId') || '' : ''), + getUsername: () => (typeof localStorage !== 'undefined' ? localStorage.getItem('username') || '' : ''), + async restoreSession(): Promise<boolean> { + if (typeof sessionStorage === 'undefined') return false; + return !!(sessionStorage.getItem('privateKey') && sessionStorage.getItem('signingKey')); + }, +}; + +// ─── ChannelStore ──────────────────────────────────────────────────────── +export const ChannelStore = { + getChannel: (_id: string): Channel | null => null, + getChannels: (_serverId: string): Channel[] => [], + listChannels: (_serverId: string): Channel[] => [], + getCategories: (_serverId: string): Channel[] => [], + getChannelsInCategory: (_serverId: string, _categoryId: string | null): Channel[] => [], + setChannel: (_c: Channel) => {}, + removeChannel: (_id: string) => {}, +}; + +// ─── ServerStore ───────────────────────────────────────────────────────── +export const ServerStore = { + getServers: (): Server[] => [], + getServer: (_id: string): Server | null => null, + setServer: (_s: Server) => {}, + removeServer: (_id: string) => {}, +}; + +// ─── MessageStore ──────────────────────────────────────────────────────── +export const MessageStore = { + getMessages: (_channelId: string): Message[] => [], + getMessage: (_channelId: string, _id: string): Message | null => null, + addMessage: (_m: Message) => {}, + updateMessage: (_channelId: string, _id: string, _patch: Partial<Message>) => {}, + removeMessage: (_channelId: string, _id: string) => {}, + getLastReadId: (_channelId: string): string | null => null, +}; + +// ─── UserStore ─────────────────────────────────────────────────────────── +export const UserStore = { + getUser: (_id: string): Member | null => null, + getMe: (): Member | null => null, + getDisplayName: (_id: string): string => '', + getAvatarUrl: (_id: string): string | null => null, + getPresence: (_id: string): PresenceStatus => 'offline', + getAccentColor: (_id: string): string | null => null, + setStatus: async (_status: PresenceStatus) => {}, + updateProfile: async (_patch: unknown) => {}, +}; + +// ─── SelectionStore ────────────────────────────────────────────────────── +export const SelectionStore = { + selectedServerId: null as string | null, + selectedChannelId: null as string | null, + isMobileViewport: typeof window !== 'undefined' ? window.innerWidth <= 768 : false, + selectServer: (_id: string | null) => {}, + selectChannel: (_id: string | null) => {}, + syncFromPath: (_path: string) => {}, +}; + +// ─── VoiceStore ────────────────────────────────────────────────────────── +export const VoiceStore = { + connectedChannelId: null as string | null, + isMuted: false, + isDeafened: false, + isCameraOn: false, + isScreenSharing: false, + connectionState: 'disconnected' as const, + participants: [] as VoiceParticipantSnapshot[], + getParticipants: (_channelId: string): VoiceParticipantSnapshot[] => [], + toggleMute: () => {}, + toggleDeafen: () => {}, + toggleCamera: () => {}, + toggleScreenShare: () => {}, + disconnect: async () => {}, +}; + +// ─── VoiceSettingsStore ────────────────────────────────────────────────── +export const VoiceSettingsStore = { + inputDeviceId: 'default', + outputDeviceId: 'default', + inputVolume: 100, + outputVolume: 100, + noiseSuppression: true, + echoCancellation: true, + autoGainControl: true, + setInputDevice: (_id: string) => {}, + setOutputDevice: (_id: string) => {}, +}; + +// ─── RoleStore ─────────────────────────────────────────────────────────── +export const RoleStore = { + listRoles: (_serverId: string): Role[] => [], + getRole: (_serverId: string, _id: string): Role | null => null, + getMemberRoles: (_serverId: string, _userId: string): Role[] => [], + getHighestHoistedRole: (_serverId: string, _userId: string): Role | null => null, +}; + +// ─── ReadStateStore ────────────────────────────────────────────────────── +export const ReadStateStore = { + isUnread: (_channelId: string): boolean => false, + getUnreadCount: (_channelId: string): number => 0, + getMentionCount: (_channelId: string): number => 0, + getLastReadId: (_channelId: string): string | null => null, + markRead: async (_channelId: string) => {}, +}; + +// ─── TypingStore ───────────────────────────────────────────────────────── +export const TypingStore = { + getTypingUsers: (_channelId: string): Member[] => [], + startTyping: async (_channelId: string) => {}, +}; + +// ─── FriendStore ───────────────────────────────────────────────────────── +export const FriendStore = { + getFriendChannels: (): Channel[] => [], + getPendingChannels: (): Channel[] => [], + getFriends: (): Member[] => [], + getPending: (): Member[] => [], + getIgnored: (): Member[] => [], + isFriend: (_id: string): boolean => false, + isPending: (_id: string): boolean => false, + isIgnored: (_id: string): boolean => false, +}; + +// ─── PinStore ──────────────────────────────────────────────────────────── +export const PinStore = { + getPinned: (_channelId: string): Message[] => [], + isPinned: (_channelId: string, _messageId: string): boolean => false, +}; + +// ─── SavedMediaStore ───────────────────────────────────────────────────── +export const SavedMediaStore = { + list: (_kind?: SavedMediaKind): SavedMediaItem[] => [], + isSaved: (_url: string): boolean => false, + add: async (_item: SavedMediaItem) => {}, + remove: async (_id: string) => {}, +}; + +// ─── EmojiPackStore ────────────────────────────────────────────────────── +export const EmojiPackStore = { + listPacks: (): EmojiPack[] => [], + getPack: (_id: string): EmojiPack | null => null, + getEmoji: (_id: string): CustomEmoji | null => null, + findByShortcode: (_shortcode: string): CustomEmoji | null => null, +}; + +// ─── PendingAttachmentStore ────────────────────────────────────────────── +export interface PendingAttachment { + id: string; + channelId: string; + file: File; + name: string; + size: number; + mimeType: string; + previewUrl?: string; + progress?: number; + error?: string | null; + spoiler?: boolean; + description?: string; + width?: number; + height?: number; +} + +export const PendingAttachmentStore = { + getAttachments: (_channelId: string): PendingAttachment[] => [], + addAttachment: (_a: PendingAttachment) => {}, + removeAttachment: (_channelId: string, _id: string) => {}, + updateAttachment: (_channelId: string, _id: string, _patch: Partial<PendingAttachment>) => {}, + clearAttachments: (_channelId: string) => {}, +}; + +// ─── PersonalNotesStore ────────────────────────────────────────────────── +export const PersonalNotesStore = { + getRoomId: (): string | null => null, + isPersonalNotesRoom: (_id: string): boolean => false, +}; + +// ─── KeybindStore ──────────────────────────────────────────────────────── +export const KeybindStore = { + getCombo: (_action: string): string => '', + getBinding: (_action: string): string => '', + setCombo: (_action: string, _combo: string) => {}, + listActions: (): string[] => [], +}; + +// ─── SearchStore ───────────────────────────────────────────────────────── +export const SearchStore = { + isOpen: false, + query: '', + results: [] as Message[], + setQuery: (_q: string) => {}, + search: async (_q: string) => {}, + close: () => {}, +}; + +// ─── CryptoStore ───────────────────────────────────────────────────────── +export const CryptoStore = { + isReady: true, + isSetup: true, + needsSetup: false, +}; + +// ─── MatrixConnectionStore ─────────────────────────────────────────────── +export const MatrixConnectionStore = { + isReady: true, + isConnecting: false, + isConnected: true, + error: null as string | null, +}; + +// ─── ThemeStore ────────────────────────────────────────────────────────── +export const ThemeStore = { + theme: 'dark' as 'light' | 'dark', + setTheme: (_t: 'light' | 'dark') => {}, +}; + +// Default exports so `import Foo from '@app/stores/Foo'` also works +export default { + AuthenticationStore, + ChannelStore, + ServerStore, + MessageStore, + UserStore, + SelectionStore, + VoiceStore, + VoiceSettingsStore, + RoleStore, + ReadStateStore, + TypingStore, + FriendStore, + PinStore, + SavedMediaStore, + EmojiPackStore, + PendingAttachmentStore, + PersonalNotesStore, + KeybindStore, + SearchStore, + CryptoStore, + MatrixConnectionStore, + ThemeStore, +}; + +// Workaround for `ok` not being used (keeps TS from erroring on strict mode) +export { ok }; diff --git a/packages/shared/src/components/Avatar.jsx b/packages/shared/src/components/Avatar.jsx deleted file mode 100644 index 85dc375..0000000 --- a/packages/shared/src/components/Avatar.jsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react'; - -const USER_COLORS = ['#5865F2', '#EBA7CD', '#57F287', '#FEE75C', '#EB459E', '#ED4245']; - -function getUserColor(name) { - let hash = 0; - for (let i = 0; i < (name || '').length; i++) { - hash = name.charCodeAt(i) + ((hash << 5) - hash); - } - return USER_COLORS[Math.abs(hash) % USER_COLORS.length]; -} - -const Avatar = ({ username, avatarUrl, size = 40, className = '', style = {}, onClick }) => { - const sizeStr = `${size}px`; - const fontSize = `${Math.max(size * 0.45, 10)}px`; - - if (avatarUrl) { - return ( - <img - className={className} - src={avatarUrl} - alt={username || '?'} - onClick={onClick} - style={{ - width: sizeStr, - height: sizeStr, - borderRadius: '50%', - objectFit: 'cover', - cursor: onClick ? 'pointer' : 'default', - ...style, - }} - /> - ); - } - - return ( - <div - className={className} - onClick={onClick} - style={{ - width: sizeStr, - height: sizeStr, - borderRadius: '50%', - backgroundColor: getUserColor(username || 'U'), - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - color: 'white', - fontWeight: 600, - fontSize, - userSelect: 'none', - flexShrink: 0, - cursor: onClick ? 'pointer' : 'default', - ...style, - }} - > - {(username || '?').substring(0, 1).toUpperCase()} - </div> - ); -}; - -export default Avatar; diff --git a/packages/shared/src/components/AvatarCropModal.jsx b/packages/shared/src/components/AvatarCropModal.jsx deleted file mode 100644 index 4061c48..0000000 --- a/packages/shared/src/components/AvatarCropModal.jsx +++ /dev/null @@ -1,134 +0,0 @@ -import React, { useState, useCallback, useEffect } from 'react'; -import Cropper from 'react-easy-crop'; - -function getCroppedImg(imageSrc, pixelCrop) { - return new Promise((resolve, reject) => { - const image = new Image(); - image.crossOrigin = 'anonymous'; - image.onload = () => { - const canvas = document.createElement('canvas'); - canvas.width = 256; - canvas.height = 256; - const ctx = canvas.getContext('2d'); - ctx.drawImage( - image, - pixelCrop.x, pixelCrop.y, pixelCrop.width, pixelCrop.height, - 0, 0, 256, 256 - ); - canvas.toBlob((blob) => { - if (!blob) return reject(new Error('Canvas toBlob failed')); - resolve(blob); - }, 'image/png'); - }; - image.onerror = reject; - image.src = imageSrc; - }); -} - -const AvatarCropModal = ({ imageUrl, onApply, onCancel, cropShape = 'round' }) => { - const [crop, setCrop] = useState({ x: 0, y: 0 }); - const [zoom, setZoom] = useState(1); - const [croppedAreaPixels, setCroppedAreaPixels] = useState(null); - - const onCropComplete = useCallback((_croppedArea, croppedPixels) => { - setCroppedAreaPixels(croppedPixels); - }, []); - - const handleApply = useCallback(async () => { - if (!croppedAreaPixels) return; - const blob = await getCroppedImg(imageUrl, croppedAreaPixels); - onApply(blob); - }, [imageUrl, croppedAreaPixels, onApply]); - - useEffect(() => { - const handleKey = (e) => { - if (e.key === 'Escape') { - e.stopPropagation(); - onCancel(); - } - }; - window.addEventListener('keydown', handleKey, true); - return () => window.removeEventListener('keydown', handleKey, true); - }, [onCancel]); - - return ( - <div className="avatar-crop-overlay" onMouseDown={(e) => { if (e.target === e.currentTarget) onCancel(); }}> - <div className="avatar-crop-dialog"> - {/* Header */} - <div className="avatar-crop-header"> - <h2 style={{ margin: 0, fontSize: '20px', fontWeight: 700, color: 'var(--header-primary)' }}> - Edit Image - </h2> - <button - onClick={onCancel} - style={{ - background: 'none', border: 'none', color: 'var(--header-secondary)', - fontSize: '24px', cursor: 'pointer', padding: '4px', lineHeight: 1, - }} - > - ✕ - </button> - </div> - - {/* Crop area */} - <div className="avatar-crop-area"> - <Cropper - image={imageUrl} - crop={crop} - zoom={zoom} - aspect={1} - cropShape={cropShape} - showGrid={false} - onCropChange={setCrop} - onZoomChange={setZoom} - onCropComplete={onCropComplete} - /> - </div> - - {/* Zoom slider */} - <div className="avatar-crop-slider-row"> - <svg width="16" height="16" viewBox="0 0 24 24" fill="var(--header-secondary)"> - <path d="M15 3H9v2h6V3zm-4 18h2v-2h-2v2zm8-15.97l-1.41-1.41-1.76 1.76 1.41 1.41L19 5.03zM4.76 4.38L3.34 5.8 5.1 7.56 6.52 6.14 4.76 4.38zM21 11h-2v2h2v-2zM5 11H3v2h2v-2zm7-4a5 5 0 100 10 5 5 0 000-10z"/> - </svg> - <input - type="range" - min={1} - max={3} - step={0.01} - value={zoom} - onChange={(e) => setZoom(Number(e.target.value))} - className="avatar-crop-slider" - /> - <svg width="20" height="20" viewBox="0 0 24 24" fill="var(--header-secondary)"> - <path d="M15 3H9v2h6V3zm-4 18h2v-2h-2v2zm8-15.97l-1.41-1.41-1.76 1.76 1.41 1.41L19 5.03zM4.76 4.38L3.34 5.8 5.1 7.56 6.52 6.14 4.76 4.38zM21 11h-2v2h2v-2zM5 11H3v2h2v-2zm7-4a5 5 0 100 10 5 5 0 000-10z"/> - </svg> - </div> - - {/* Actions */} - <div className="avatar-crop-actions"> - <button - onClick={onCancel} - style={{ - background: 'none', border: 'none', color: 'var(--header-primary)', - cursor: 'pointer', fontSize: '14px', fontWeight: 500, padding: '8px 16px', - }} - > - Cancel - </button> - <button - onClick={handleApply} - style={{ - backgroundColor: 'var(--brand-experiment)', color: 'white', border: 'none', - borderRadius: '4px', padding: '8px 24px', cursor: 'pointer', - fontSize: '14px', fontWeight: 500, - }} - > - Apply - </button> - </div> - </div> - </div> - ); -}; - -export default AvatarCropModal; diff --git a/packages/shared/src/components/ChangeNicknameModal.jsx b/packages/shared/src/components/ChangeNicknameModal.jsx deleted file mode 100644 index ba2b127..0000000 --- a/packages/shared/src/components/ChangeNicknameModal.jsx +++ /dev/null @@ -1,222 +0,0 @@ -import React, { useState, useEffect, useRef } from 'react'; -import ReactDOM from 'react-dom'; -import { useMutation } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; - -const ChangeNicknameModal = ({ targetUserId, targetUsername, currentNickname, actorUserId, onClose }) => { - const [nickname, setNickname] = useState(currentNickname || ''); - const inputRef = useRef(null); - const setNicknameMutation = useMutation(api.auth.setNickname); - const isSelf = targetUserId === actorUserId; - - useEffect(() => { - inputRef.current?.focus(); - inputRef.current?.select(); - }, []); - - const handleSave = async () => { - try { - await setNicknameMutation({ - actorUserId, - targetUserId, - displayName: nickname, - }); - onClose(); - } catch (err) { - console.error('Failed to set nickname:', err); - } - }; - - const handleReset = async () => { - try { - await setNicknameMutation({ - actorUserId, - targetUserId, - displayName: '', - }); - onClose(); - } catch (err) { - console.error('Failed to reset nickname:', err); - } - }; - - const handleKeyDown = (e) => { - if (e.key === 'Enter') { - e.preventDefault(); - handleSave(); - } else if (e.key === 'Escape') { - onClose(); - } - }; - - return ReactDOM.createPortal( - <div - onClick={onClose} - style={{ - position: 'fixed', - top: 0, - left: 0, - right: 0, - bottom: 0, - backgroundColor: 'rgba(0, 0, 0, 0.85)', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - zIndex: 10001, - }} - > - <div - onClick={(e) => e.stopPropagation()} - style={{ - width: '440px', - maxWidth: '90vw', - backgroundColor: 'var(--bg-primary)', - borderRadius: '8px', - boxShadow: '0 4px 20px rgba(0, 0, 0, 0.4)', - overflow: 'hidden', - }} - > - {/* Header */} - <div style={{ padding: '16px 16px 0 16px', position: 'relative' }}> - <h2 style={{ color: 'var(--header-primary)', margin: '0 0 16px 0', fontSize: '20px', fontWeight: 600 }}> - Change Nickname - </h2> - <button - onClick={onClose} - style={{ - position: 'absolute', - top: '12px', - right: '12px', - background: 'none', - border: 'none', - color: 'var(--interactive-normal)', - cursor: 'pointer', - padding: '4px', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - borderRadius: '50%', - }} - > - <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"> - <path d="M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z" /> - </svg> - </button> - </div> - - {/* Content */} - <div style={{ padding: '0 16px 16px 16px' }}> - {/* Notice */} - {!isSelf && ( - <div style={{ - backgroundColor: 'var(--bg-tertiary)', - borderLeft: '4px solid var(--text-warning, #faa61a)', - borderRadius: '4px', - padding: '12px', - marginBottom: '16px', - fontSize: '14px', - color: 'var(--text-normal)', - lineHeight: '1.4', - }}> - Nicknames are visible to everyone on this server. Do not change them unless you are enforcing a naming system or clearing a bad nickname. - </div> - )} - - {/* Label */} - <label style={{ - color: 'var(--header-secondary)', - fontSize: '12px', - fontWeight: 700, - textTransform: 'uppercase', - marginBottom: '8px', - display: 'block', - }}> - Nickname - </label> - - {/* Input */} - <input - ref={inputRef} - type="text" - value={nickname} - onChange={(e) => setNickname(e.target.value.slice(0, 32))} - onKeyDown={handleKeyDown} - placeholder={targetUsername} - maxLength={32} - style={{ - width: '100%', - padding: '10px', - backgroundColor: 'var(--bg-tertiary)', - border: '1px solid var(--border-subtle)', - borderRadius: '4px', - color: 'var(--text-normal)', - fontSize: '14px', - outline: 'none', - boxSizing: 'border-box', - }} - /> - - {/* Reset link */} - <div - onClick={handleReset} - style={{ - color: 'var(--text-link, #00a8fc)', - fontSize: '14px', - cursor: 'pointer', - marginTop: '8px', - marginBottom: '4px', - userSelect: 'none', - }} - > - Reset Nickname - </div> - </div> - - {/* Footer */} - <div style={{ - display: 'flex', - gap: '12px', - padding: '16px', - backgroundColor: 'var(--bg-secondary)', - borderTop: '1px solid var(--border-subtle)', - }}> - <button - onClick={onClose} - style={{ - flex: 1, - padding: '10px 0', - backgroundColor: 'var(--bg-tertiary)', - color: 'var(--text-normal)', - border: 'none', - borderRadius: '3px', - cursor: 'pointer', - fontSize: '14px', - fontWeight: 500, - }} - > - Cancel - </button> - <button - onClick={handleSave} - style={{ - flex: 1, - padding: '10px 0', - backgroundColor: 'var(--brand-experiment)', - color: '#fff', - border: 'none', - borderRadius: '3px', - cursor: 'pointer', - fontSize: '14px', - fontWeight: 500, - }} - > - Save - </button> - </div> - </div> - </div>, - document.body - ); -}; - -export default ChangeNicknameModal; diff --git a/packages/shared/src/components/ChannelSettingsModal.jsx b/packages/shared/src/components/ChannelSettingsModal.jsx deleted file mode 100644 index 9473cac..0000000 --- a/packages/shared/src/components/ChannelSettingsModal.jsx +++ /dev/null @@ -1,213 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useConvex } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; - -const ChannelSettingsModal = ({ channel, onClose, onRename, onDelete }) => { - const [name, setName] = useState(channel.name); - const [activeTab, setActiveTab] = useState('Overview'); - - const convex = useConvex(); - - useEffect(() => { - const handleKey = (e) => { if (e.key === 'Escape') onClose(); }; - window.addEventListener('keydown', handleKey); - return () => window.removeEventListener('keydown', handleKey); - }, [onClose]); - - const handleSave = async () => { - try { - await convex.mutation(api.channels.rename, { id: channel._id, name }); - onRename(channel._id, name); - onClose(); - } catch (err) { - console.error(err); - alert('Failed to update channel: ' + err.message); - } - }; - - const handleDelete = async () => { - if (!confirm('Are you sure you want to delete this channel? This cannot be undone.')) return; - - try { - await convex.mutation(api.channels.remove, { id: channel._id }); - onDelete(channel._id); - onClose(); - } catch (err) { - console.error(err); - alert('Failed to delete channel: ' + err.message); - } - }; - - return ( - <div style={{ - position: 'fixed', - top: 0, - left: 0, - right: 0, - bottom: 0, - backgroundColor: 'var(--bg-primary)', - zIndex: 1000, - display: 'flex', - color: 'var(--text-normal)' - }}> - {/* Sidebar */} - <div style={{ - width: '218px', - backgroundColor: 'var(--bg-secondary)', - display: 'flex', - flexDirection: 'column', - alignItems: 'flex-end', - padding: '60px 6px 60px 20px' - }}> - <div style={{ width: '100%', padding: '0 10px' }}> - <div style={{ - fontSize: '12px', - fontWeight: '700', - color: 'var(--text-muted)', - marginBottom: '6px', - textTransform: 'uppercase' - }}> - {channel.name} Text Channels - </div> - - <div - onClick={() => setActiveTab('Overview')} - style={{ - padding: '6px 10px', - borderRadius: '4px', - backgroundColor: activeTab === 'Overview' ? 'var(--background-modifier-selected)' : 'transparent', - color: activeTab === 'Overview' ? 'var(--header-primary)' : 'var(--header-secondary)', - cursor: 'pointer', - marginBottom: '2px', - fontSize: '15px' - }} - > - Overview - </div> - - <div style={{ height: '1px', backgroundColor: 'var(--border-subtle)', margin: '8px 0' }} /> - - <div - onClick={() => setActiveTab('Delete')} - style={{ - padding: '6px 10px', - borderRadius: '4px', - color: '#ed4245', - cursor: 'pointer', - fontSize: '15px', - display: 'flex', - justifyContent: 'space-between' - }} - > - Delete Channel - <span>🗑️</span> - </div> - </div> - </div> - - {/* Content */} - <div style={{ flex: 1, display: 'flex', justifyContent: 'flex-start', overflowY: 'auto' }}> - <div style={{ flex: 1, maxWidth: '740px', padding: '60px 40px 80px', position: 'relative' }}> - <h2 style={{ color: 'var(--header-primary)', margin: 0, marginBottom: '20px' }}> - {activeTab === 'Delete' ? 'Delete Channel' : 'Overview'} - </h2> - - {activeTab === 'Overview' && ( - <> - <div style={{ marginBottom: '20px' }}> - <label style={{ - display: 'block', - color: 'var(--header-secondary)', - fontSize: '12px', - fontWeight: '700', - textTransform: 'uppercase', - marginBottom: '8px' - }}> - Channel Name - </label> - <input - type="text" - value={name} - onChange={(e) => setName(e.target.value)} - style={{ - width: '100%', - backgroundColor: 'var(--bg-tertiary)', - border: 'none', - borderRadius: '4px', - padding: '10px', - color: 'var(--text-normal)', - fontSize: '16px', - outline: 'none' - }} - /> - </div> - - <div style={{ display: 'flex', gap: '20px' }}> - <button - onClick={handleSave} - style={{ - backgroundColor: '#3ba55c', - color: 'var(--header-primary)', - border: 'none', - borderRadius: '4px', - padding: '10px 24px', - cursor: 'pointer', - fontSize: '14px', - fontWeight: '500' - }} - > - Save Changes - </button> - </div> - </> - )} - - {activeTab === 'Delete' && ( - <div style={{ backgroundColor: 'var(--bg-tertiary)', padding: '16px', borderRadius: '8px', border: '1px solid #ed4245' }}> - <h3 style={{ color: 'var(--header-primary)', marginTop: 0 }}>Are you sure?</h3> - <p style={{ color: 'var(--header-secondary)' }}> - Deleting <b>#{channel.name}</b> cannot be undone. All messages and keys will be lost forever. - </p> - <button - onClick={handleDelete} - style={{ - backgroundColor: '#ed4245', - color: 'var(--header-primary)', - border: 'none', - borderRadius: '4px', - padding: '10px 24px', - cursor: 'pointer', - fontSize: '14px', - fontWeight: '500' - }} - > - Delete Channel - </button> - </div> - )} - - </div> - <div style={{ flex: '0 0 36px', paddingTop: '60px', marginLeft: '8px' }}> - <button - onClick={onClose} - style={{ - width: '36px', height: '36px', borderRadius: '50%', - border: '2px solid var(--header-secondary)', background: 'transparent', - color: 'var(--header-secondary)', cursor: 'pointer', - display: 'flex', alignItems: 'center', justifyContent: 'center', - fontSize: '18px', - }} - > - ✕ - </button> - <div style={{ fontSize: '13px', fontWeight: '600', color: 'var(--header-secondary)', textAlign: 'center', marginTop: '4px' }}> - ESC - </div> - </div> - <div style={{ flex: '0.5' }} /> - </div> - </div> - ); -}; - -export default ChannelSettingsModal; diff --git a/packages/shared/src/components/ChatArea.jsx b/packages/shared/src/components/ChatArea.jsx deleted file mode 100644 index 54e5bc8..0000000 --- a/packages/shared/src/components/ChatArea.jsx +++ /dev/null @@ -1,2651 +0,0 @@ -import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react'; -import ReactDOM from 'react-dom'; -import { Virtuoso } from 'react-virtuoso'; -import { useQuery, usePaginatedQuery, useMutation, useConvex } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; -import { - GifIcon, - StickerIcon, - EmojieIcon, - EmojiesColored, - EmojiesGreyscale, - EditIcon, - ReplyIcon, - DeleteIcon, - PinIcon, - TypingIcon, - AddIcon, - SpoilerIcon -} from '../assets/icons'; -import PingSound from '../assets/sounds/ping.mp3'; -import CategorizedEmojis, { AllEmojis, getEmojiUrl } from '../assets/emojis'; -import GifPicker from './GifPicker'; -import PinnedMessagesPanel from './PinnedMessagesPanel'; -import Tooltip from './Tooltip'; -import UserProfilePopup from './UserProfilePopup'; -import Avatar from './Avatar'; -import MentionMenu from './MentionMenu'; -import SlashCommandMenu from './SlashCommandMenu'; -import MessageItem, { getUserColor, parseAttachment } from './MessageItem'; -import MobileMessageDrawer from './MobileMessageDrawer'; -import ColoredIcon from './ColoredIcon'; -import { usePlatform } from '../platform'; -import { useVoice } from '../contexts/VoiceContext'; -import { useSearch } from '../contexts/SearchContext'; -import { useIsMobile } from '../hooks/useIsMobile'; -import { generateUniqueMessage } from '../utils/floodMessages'; - -const SCROLL_DEBUG = import.meta.env.DEV; -const scrollLog = (...args) => { if (SCROLL_DEBUG) console.log(...args); }; - -const metadataCache = new Map(); -const attachmentCache = new Map(); - -const CONVEX_PUBLIC_URL = 'https://api.brycord.com'; -const rewriteStorageUrl = (url) => { - try { - const u = new URL(url); - const pub = new URL(CONVEX_PUBLIC_URL); - u.hostname = pub.hostname; - u.port = pub.port; - u.protocol = pub.protocol; - return u.toString(); - } catch { return url; } -}; - -// Persistent global decryption cache (survives channel switches) -// Keyed by message _id, stores { content, isVerified, decryptedReply } -const messageDecryptionCache = new Map(); -const MESSAGE_CACHE_MAX = 2000; - -function evictCacheIfNeeded() { - if (messageDecryptionCache.size <= MESSAGE_CACHE_MAX) return; - const keysToDelete = [...messageDecryptionCache.keys()].slice(0, messageDecryptionCache.size - MESSAGE_CACHE_MAX); - for (const key of keysToDelete) { - messageDecryptionCache.delete(key); - } -} - -// Exported for logout clearing -export function clearMessageDecryptionCache() { - messageDecryptionCache.clear(); -} - -const ICON_COLOR_DEFAULT = 'hsl(240, 4.294%, 68.039%)'; -const ICON_COLOR_DANGER = 'hsl(1.353, 82.609%, 68.431%)'; - -const fromHexString = (hexString) => - new Uint8Array(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))); - -const toHexString = (bytes) => - bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), ''); - -const VIDEO_EXT_RE = /\.(mp4|webm|ogg|mov)(\?[^\s]*)?$/i; -const isVideoUrl = (url) => VIDEO_EXT_RE.test(url); - -const DirectVideo = ({ src, marginTop = 8 }) => { - const ref = useRef(null); - const [showControls, setShowControls] = useState(false); - const handlePlay = () => { - setShowControls(true); - if (ref.current) ref.current.play(); - }; - return ( - <div style={{ marginTop, position: 'relative', display: 'inline-block', maxWidth: '100%', aspectRatio: '16 / 9', minHeight: '169px' }}> - <video - ref={ref} - src={src} - controls={showControls} - preload="metadata" - style={{ maxWidth: '100%', maxHeight: '300px', borderRadius: '8px', backgroundColor: 'black', display: 'block', width: '100%', height: '100%', objectFit: 'contain' }} - /> - {!showControls && ( - <div className="play-icon" onClick={handlePlay} style={{ cursor: 'pointer' }}> - ▶ - </div> - )} - </div> - ); -}; - -const getYouTubeId = (link) => { - const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|shorts\/|watch\?v=|&v=)([^#&?]*).*/; - const match = link.match(regExp); - return (match && match[2].length === 11) ? match[2] : null; -}; - -const filterMembersForMention = (members, query) => { - if (!members) return []; - const q = query.toLowerCase(); - if (!q) return members; - const prefix = []; - const substring = []; - for (const m of members) { - const name = m.username.toLowerCase(); - const nick = (m.displayName || '').toLowerCase(); - if (name.startsWith(q) || nick.startsWith(q)) prefix.push(m); - else if (name.includes(q) || nick.includes(q)) substring.push(m); - } - return [...prefix, ...substring]; -}; - -const filterRolesForMention = (roles, query) => { - if (!roles) return []; - const q = query.toLowerCase(); - if (!q) return roles; - const prefix = []; - const substring = []; - for (const r of roles) { - const name = r.name.replace(/^@/, '').toLowerCase(); - if (name.startsWith(q)) prefix.push(r); - else if (name.includes(q)) substring.push(r); - } - return [...prefix, ...substring]; -}; - -const SLASH_COMMANDS = [ - { name: 'ping', description: 'Responds with Pong!', category: 'Built-In' }, - { name: 'flood', description: 'Generate test messages (e.g. /flood 100)', category: 'Testing' }, -]; - -const filterSlashCommands = (commands, query) => { - if (!query) return commands; - const q = query.toLowerCase(); - return commands.filter(c => c.name.toLowerCase().startsWith(q)); -}; - -const isNewDay = (current, previous) => { - if (!previous) return true; - return current.getDate() !== previous.getDate() - || current.getMonth() !== previous.getMonth() - || current.getFullYear() !== previous.getFullYear(); -}; - -const getProviderClass = (url) => { - try { - const hostname = new URL(url).hostname.replace(/^www\./, ''); - if (hostname === 'twitter.com' || hostname === 'x.com') return 'twitter-preview'; - if (hostname === 'open.spotify.com') return 'spotify-preview'; - if (hostname === 'reddit.com') return 'reddit-preview'; - } catch {} - return ''; -}; - -export const LinkPreview = ({ url }) => { - const { links } = usePlatform(); - const [metadata, setMetadata] = useState(metadataCache.get(url) || null); - const [loading, setLoading] = useState(!metadataCache.has(url)); - const [playing, setPlaying] = useState(false); - const [showControls, setShowControls] = useState(false); - const videoRef = useRef(null); - - useEffect(() => { - if (metadataCache.has(url)) { - setMetadata(metadataCache.get(url)); - setLoading(false); - return; - } - - let isMounted = true; - const fetchMeta = async () => { - try { - const data = await links.fetchMetadata(url); - if (isMounted) { - if (data) metadataCache.set(url, data); - setMetadata(data); - setLoading(false); - } - } catch (err) { - console.error("Failed to fetch metadata", err); - if (isMounted) setLoading(false); - } - }; - fetchMeta(); - return () => { isMounted = false; }; - }, [url]); - - const videoId = getYouTubeId(url); - const isYouTube = !!videoId; - const isDirectVideoUrl = isVideoUrl(url); - - if (isDirectVideoUrl) { - return <DirectVideo src={url} />; - } - - if (loading) { - return ( - <div className="link-preview-skeleton" style={{ marginTop: 8, height: 80, borderRadius: '4px' }}> - <div style={{ display: 'flex', flexDirection: 'column', gap: '8px', padding: '12px 16px' }}> - <div className="skeleton-bar" style={{ width: '40%', height: '10px' }} /> - <div className="skeleton-bar" style={{ width: '70%', height: '12px' }} /> - <div className="skeleton-bar" style={{ width: '90%', height: '10px' }} /> - </div> - </div> - ); - } - if (!metadata || (!metadata.title && !metadata.image && !metadata.video)) return null; - - if (metadata.video && !isYouTube) { - const handlePlayClick = () => { - setShowControls(true); - if (videoRef.current) videoRef.current.play(); - }; - - return ( - <div className="preview-video-standalone" style={{ marginTop: 8, position: 'relative', display: 'inline-block', maxWidth: '100%', aspectRatio: '16 / 9', minHeight: '169px' }}> - <video - ref={videoRef} - src={metadata.video} - controls={showControls} - style={{ maxWidth: '100%', maxHeight: '300px', borderRadius: '4px', backgroundColor: 'black', display: 'block', width: '100%', height: '100%', objectFit: 'contain' }} - /> - {!showControls && ( - <div className="play-icon" onClick={handlePlayClick} style={{ cursor: 'pointer' }}> - ▶ - </div> - )} - </div> - ); - } - - if (metadata.description === 'Image File' && metadata.image) { - return ( - <div className="preview-image-standalone" style={{ marginTop: 8, display: 'inline-block', maxWidth: '100%', cursor: 'pointer', minHeight: '200px' }}> - <img src={metadata.image} alt="Preview" draggable="false" style={{ maxWidth: '100%', maxHeight: '350px', borderRadius: '8px', display: 'block' }} /> - </div> - ); - } - - const providerClass = getProviderClass(url); - const isLargeImage = providerClass === 'twitter-preview' || metadata.type === 'article' || metadata.type === 'summary_large_image'; - - return ( - <div className={`link-preview ${isYouTube ? 'youtube-preview' : ''} ${providerClass} ${isLargeImage && !isYouTube ? 'large-image-layout' : ''}`} style={{ borderLeftColor: metadata.themeColor || '#202225' }}> - <div className="preview-content"> - {metadata.siteName && <div className="preview-site-name">{metadata.siteName}</div>} - {metadata.author && <div className="preview-author">{metadata.author}</div>} - {metadata.title && ( - <a href={url} onClick={(e) => { e.preventDefault(); links.openExternal(url); }} className="preview-title"> - {metadata.title} - </a> - )} - {metadata.description && <div className="preview-description">{metadata.description}</div>} - {isYouTube && playing && ( - <div className="youtube-video-wrapper"> - <iframe - src={`https://www.youtube.com/embed/${videoId}?autoplay=1`} - title={metadata.title || "YouTube video player"} - frameBorder="0" - allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" - allowFullScreen - /> - </div> - )} - {isLargeImage && !isYouTube && metadata.image && ( - <div className="preview-image-container large-image"> - <img src={metadata.image} alt="Preview" draggable="false" className="preview-image" /> - </div> - )} - </div> - {!isLargeImage && !isYouTube && metadata.image && ( - <div className="preview-image-container"> - <img src={metadata.image} alt="Preview" draggable="false" className="preview-image" /> - </div> - )} - {isYouTube && metadata.image && !playing && ( - <div className="preview-image-container" onClick={() => setPlaying(true)} style={{ cursor: 'pointer' }}> - <img src={metadata.image} alt="Preview" draggable="false" className="preview-image" /> - <div className="play-icon">▶</div> - </div> - )} - </div> - ); -}; - -const Attachment = ({ metadata, onLoad, onImageClick }) => { - const { crypto } = usePlatform(); - const fetchUrl = rewriteStorageUrl(metadata.url); - const [url, setUrl] = useState(attachmentCache.get(fetchUrl) || null); - const [loading, setLoading] = useState(!attachmentCache.has(fetchUrl)); - const [error, setError] = useState(null); - const [showControls, setShowControls] = useState(false); - const videoRef = useRef(null); - - useEffect(() => { - if (attachmentCache.has(fetchUrl)) { - setUrl(attachmentCache.get(fetchUrl)); - setLoading(false); - return; - } - - let isMounted = true; - const decryptFile = async () => { - try { - const res = await fetch(fetchUrl); - const blob = await res.blob(); - const arrayBuffer = await blob.arrayBuffer(); - const hexInput = toHexString(new Uint8Array(arrayBuffer)); - - if (hexInput.length < 32) throw new Error('Invalid file data'); - - const TAG_HEX_LEN = 32; - const contentHex = hexInput.slice(0, -TAG_HEX_LEN); - const tagHex = hexInput.slice(-TAG_HEX_LEN); - - const decrypted = await crypto.decryptData(contentHex, metadata.key, metadata.iv, tagHex, { encoding: 'buffer' }); - const decryptedBlob = new Blob([decrypted], { type: metadata.mimeType }); - const objectUrl = URL.createObjectURL(decryptedBlob); - - if (isMounted) { - attachmentCache.set(fetchUrl, objectUrl); - setUrl(objectUrl); - setLoading(false); - } - } catch (err) { - console.error('Attachment decrypt error:', err); - if (isMounted) { setError('Failed to decrypt'); setLoading(false); } - } - }; - decryptFile(); - return () => { isMounted = false; }; - }, [metadata, onLoad]); - - if (loading) { - // Skeleton placeholder sized to match final content — prevents layout shift - if (metadata.mimeType.startsWith('image/')) { - const skelW = metadata.width ? Math.min(metadata.width, 400) : 300; - const skelH = metadata.height && metadata.width - ? Math.round(skelW * (metadata.height / metadata.width)) - : 200; - return ( - <div className="attachment-skeleton" style={{ width: skelW, height: skelH, maxWidth: '100%', borderRadius: '4px' }}> - <div className="loading-spinner" style={{ width: '20px', height: '20px', borderWidth: '2px' }} /> - </div> - ); - } - if (metadata.mimeType.startsWith('video/')) { - const skelW = metadata.width ? Math.min(metadata.width, 300) : 300; - const skelH = metadata.height && metadata.width - ? Math.round(skelW * (metadata.height / metadata.width)) - : 169; - return ( - <div className="attachment-skeleton" style={{ width: skelW, height: skelH, maxWidth: '300px', borderRadius: '4px' }}> - <div className="loading-spinner" style={{ width: '20px', height: '20px', borderWidth: '2px' }} /> - </div> - ); - } - // File attachment skeleton — matches final file card layout - return ( - <div className="attachment-skeleton" style={{ width: 300, height: 52, maxWidth: '300px', borderRadius: '4px' }}> - <div className="loading-spinner" style={{ width: '16px', height: '16px', borderWidth: '2px' }} /> - </div> - ); - } - if (error) return <div style={{ color: '#ed4245', fontSize: '12px' }}>{error}</div>; - - if (metadata.mimeType.startsWith('image/')) { - const containerStyle = metadata.width && metadata.height - ? { aspectRatio: `${metadata.width} / ${metadata.height}`, maxWidth: Math.min(metadata.width, 400), maxHeight: '300px' } - : { minHeight: '200px', maxHeight: '300px' }; - return ( - <div style={{ ...containerStyle, display: 'inline-block', maxWidth: '100%' }}> - <img src={url} alt={metadata.filename} draggable="false" style={{ maxWidth: '100%', maxHeight: '300px', borderRadius: '4px', cursor: 'zoom-in', display: 'block' }} onLoad={onLoad} onClick={() => onImageClick(url)} /> - </div> - ); - } - if (metadata.mimeType.startsWith('video/')) { - const handlePlayClick = () => { setShowControls(true); if (videoRef.current) videoRef.current.play(); }; - const videoAspect = metadata.width && metadata.height - ? `${metadata.width} / ${metadata.height}` - : '16 / 9'; - return ( - <div style={{ marginTop: 8, position: 'relative', display: 'inline-block', maxWidth: '300px', aspectRatio: videoAspect, minHeight: '169px' }}> - <video ref={videoRef} src={url} controls={showControls} style={{ maxWidth: '300px', borderRadius: '4px', display: 'block', backgroundColor: 'black', width: '100%', height: '100%', objectFit: 'contain' }} onLoadedData={onLoad} /> - {!showControls && <div className="play-icon" onClick={handlePlayClick} style={{ cursor: 'pointer' }}>▶</div>} - </div> - ); - } - return ( - <div style={{ display: 'flex', alignItems: 'center', backgroundColor: 'var(--embed-background)', padding: '10px', borderRadius: '4px', maxWidth: '300px' }}> - <span style={{ marginRight: '10px', fontSize: '24px' }}>📄</span> - <div style={{ overflow: 'hidden' }}> - <div style={{ color: 'var(--text-link)', fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{metadata.filename}</div> - <div style={{ color: 'var(--header-secondary)', fontSize: '12px' }}>{(metadata.size / 1024).toFixed(1)} KB</div> - <a href={url} download={metadata.filename} style={{ color: 'var(--header-secondary)', fontSize: '12px', textDecoration: 'underline' }}>Download</a> - </div> - </div> - ); -}; - -const PendingFilePreview = ({ file, onRemove }) => { - const [preview, setPreview] = useState(null); - const [isVideo, setIsVideo] = useState(false); - useEffect(() => { - if (file.type.startsWith('image/')) { - const reader = new FileReader(); - reader.onloadend = () => setPreview(reader.result); - reader.readAsDataURL(file); - } else if (file.type.startsWith('video/')) { - const url = URL.createObjectURL(file); - setPreview(url); - setIsVideo(true); - return () => URL.revokeObjectURL(url); - } - }, [file]); - const ActionButton = ({ icon, onClick, bg = 'rgba(0,0,0,0.7)', hoverBg = 'rgba(0,0,0,0.9)' }) => ( - <div onClick={(e) => { e.stopPropagation(); onClick(); }} style={{ width: '28px', height: '28px', borderRadius: '50%', backgroundColor: bg, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', transition: 'background-color 0.15s' }} onMouseEnter={(e) => e.currentTarget.style.backgroundColor = hoverBg} onMouseLeave={(e) => e.currentTarget.style.backgroundColor = bg}> - <ColoredIcon src={icon} color="#fff" size="16px" /> - </div> - ); - - let previewContent; - if (preview && isVideo) { - previewContent = ( - <> - <video src={preview} muted preload="metadata" style={{ width: '100%', height: '100%', objectFit: 'cover' }} /> - <div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', fontSize: '24px', color: 'white', textShadow: '0 1px 4px rgba(0,0,0,0.7)', pointerEvents: 'none' }}>▶</div> - </> - ); - } else if (preview) { - previewContent = <img src={preview} alt="Preview" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />; - } else { - previewContent = ( - <div style={{ textAlign: 'center' }}> - <div style={{ fontSize: '24px' }}>📄</div> - <div style={{ fontSize: '10px', color: 'var(--header-secondary)', marginTop: '4px', maxWidth: '90px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{file.name}</div> - </div> - ); - } - - return ( - <div style={{ display: 'inline-flex', flexDirection: 'column', marginRight: '10px' }}> - <div style={{ position: 'relative', width: '200px', height: '200px', borderRadius: '8px', backgroundColor: 'var(--embed-background)', display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden' }}> - {previewContent} - <div style={{ position: 'absolute', top: '4px', right: '4px', display: 'flex', gap: '4px', padding: '4px' }}> - <ActionButton icon={SpoilerIcon} onClick={() => {}} /> - <ActionButton icon={EditIcon} onClick={() => {}} /> - <ActionButton icon={DeleteIcon} onClick={() => onRemove(file)} bg="#da373c" hoverBg="#a12d31" /> - </div> - </div> - {preview && ( - <div style={{ fontSize: '11px', color: 'var(--header-secondary)', marginTop: '4px', maxWidth: '200px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{file.name}</div> - )} - </div> - ); -}; - -const DragOverlay = () => ( - <div style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(88, 101, 242, 0.9)', zIndex: 2000, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: 'white', pointerEvents: 'none' }}> - <div style={{ backgroundColor: 'white', borderRadius: '50%', padding: '20px', marginBottom: '20px' }}> - <svg width="48" height="48" viewBox="0 0 24 24" fill="#5865F2"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"/></svg> - </div> - <div style={{ fontSize: '24px', fontWeight: 'bold' }}>Upload to #{'channel'}</div> - <div style={{ fontSize: '16px', marginTop: '8px' }}>Hold Shift to upload directly</div> - </div> -); - -const EmojiButton = ({ onClick, active }) => { - const [hovered, setHovered] = useState(false); - const [bgPos, setBgPos] = useState('0px 0px'); - const getRandomPos = () => { - const totalSprites = 77; - const index = Math.floor(Math.random() * totalSprites); - const col = index % 20; - const row = Math.floor(index / 20); - return `-${col * 24}px -${row * 24}px`; - }; - return ( - <Tooltip text="Select Emoji" position="top"> - <div className="chat-input-icon-btn" onClick={(e) => { e.stopPropagation(); if(onClick) onClick(); }} onMouseEnter={() => { setHovered(true); setBgPos(getRandomPos()); }} onMouseLeave={() => { setHovered(false); setBgPos(getRandomPos()); }} style={{ width: '24px', height: '24px', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', marginLeft: '4px' }}> - <div style={{ width: '24px', height: '24px', backgroundImage: `url(${(hovered || active) ? EmojiesColored : EmojiesGreyscale})`, backgroundPosition: bgPos, backgroundSize: '480px 96px', backgroundRepeat: 'no-repeat' }} /> - </div> - </Tooltip> - ); -}; - -const MessageContextMenu = ({ x, y, onInteract, onClose, isOwner, isAttachment, canDelete }) => { - const menuRef = useRef(null); - const [pos, setPos] = useState({ top: y, left: x }); - useEffect(() => { const h = () => onClose(); window.addEventListener('click', h); window.addEventListener('close-context-menus', h); return () => { window.removeEventListener('click', h); window.removeEventListener('close-context-menus', h); }; }, [onClose]); - React.useLayoutEffect(() => { - if (!menuRef.current) return; - const rect = menuRef.current.getBoundingClientRect(); - let newTop = y, newLeft = x; - if (x + rect.width > window.innerWidth) newLeft = x - rect.width; - if (y + rect.height > window.innerHeight) newTop = y - rect.height; - if (newLeft < 0) newLeft = 10; - if (newTop < 0) newTop = 10; - setPos({ top: newTop, left: newLeft }); - }, [x, y]); - - const MenuItem = ({ label, iconSrc, iconColor, onClick, danger }) => ( - <div onClick={(e) => { e.stopPropagation(); onClick(); onClose(); }} className={`context-menu-item ${danger ? 'context-menu-item-danger' : ''}`}> - <span>{label}</span> - <div style={{ marginLeft: '12px' }}><ColoredIcon src={iconSrc} color={iconColor} size="18px" /></div> - </div> - ); - - return ( - <div ref={menuRef} className="context-menu" style={{ top: pos.top, left: pos.left }} onClick={(e) => e.stopPropagation()}> - <MenuItem label="Add Reaction" iconSrc={EmojieIcon} iconColor={ICON_COLOR_DEFAULT} onClick={() => onInteract('reaction')} /> - {isOwner && !isAttachment && <MenuItem label="Edit Message" iconSrc={EditIcon} iconColor={ICON_COLOR_DEFAULT} onClick={() => onInteract('edit')} />} - <MenuItem label="Reply" iconSrc={ReplyIcon} iconColor={ICON_COLOR_DEFAULT} onClick={() => onInteract('reply')} /> - <div className="context-menu-separator" /> - <MenuItem label="Pin Message" iconSrc={PinIcon} iconColor={ICON_COLOR_DEFAULT} onClick={() => onInteract('pin')} /> - <div className="context-menu-separator" /> - {canDelete && <MenuItem label="Delete Message" iconSrc={DeleteIcon} iconColor={ICON_COLOR_DANGER} danger onClick={() => onInteract('delete')} />} - </div> - ); -}; - -const InputContextMenu = ({ x, y, onClose, onPaste }) => { - const menuRef = useRef(null); - const [pos, setPos] = useState({ top: y, left: x }); - useEffect(() => { const h = () => onClose(); window.addEventListener('click', h); window.addEventListener('close-context-menus', h); return () => { window.removeEventListener('click', h); window.removeEventListener('close-context-menus', h); }; }, [onClose]); - React.useLayoutEffect(() => { - if (!menuRef.current) return; - const rect = menuRef.current.getBoundingClientRect(); - let newTop = y, newLeft = x; - if (x + rect.width > window.innerWidth) newLeft = x - rect.width; - if (y + rect.height > window.innerHeight) newTop = y - rect.height; - if (newLeft < 0) newLeft = 10; - if (newTop < 0) newTop = 10; - setPos({ top: newTop, left: newLeft }); - }, [x, y]); - - return ( - <div ref={menuRef} className="context-menu" style={{ top: pos.top, left: pos.left }} onClick={(e) => e.stopPropagation()}> - <div className="context-menu-item" onClick={(e) => { e.stopPropagation(); onPaste(); onClose(); }}> - <span>Paste</span> - </div> - </div> - ); -}; - -const ChatArea = ({ channelId, channelName, channelType, username, channelKey, userId: currentUserId, showMembers, onToggleMembers, onOpenDM, showPinned, onTogglePinned, jumpToMessageId, onClearJumpToMessage }) => { - const platform = usePlatform(); - const { crypto } = platform; - const isMobile = useIsMobile(); - const { isReceivingScreenShareAudio } = useVoice(); - const searchCtx = useSearch(); - const [decryptedMessages, setDecryptedMessages] = useState([]); - const [input, setInput] = useState(''); - const [zoomedImage, setZoomedImage] = useState(null); - const [pickerTab, setPickerTab] = useState(null); - const [isDragging, setIsDragging] = useState(false); - const [pendingFiles, setPendingFiles] = useState([]); - const [hasImages, setHasImages] = useState(false); - const [isMultiline, setIsMultiline] = useState(false); - const [hoveredMessageId, setHoveredMessageId] = useState(null); - const [contextMenu, setContextMenu] = useState(null); - const [inputContextMenu, setInputContextMenu] = useState(null); - const [uploading, setUploading] = useState(false); - const [replyingTo, setReplyingTo] = useState(null); - const [editingMessage, setEditingMessage] = useState(null); - const [editInput, setEditInput] = useState(''); - const [profilePopup, setProfilePopup] = useState(null); - const [mentionQuery, setMentionQuery] = useState(null); - const [mentionIndex, setMentionIndex] = useState(0); - const [slashQuery, setSlashQuery] = useState(null); - const [slashIndex, setSlashIndex] = useState(0); - const [ephemeralMessages, setEphemeralMessages] = useState([]); - const [unreadDividerTimestamp, setUnreadDividerTimestamp] = useState(null); - const [reactionPickerMsgId, setReactionPickerMsgId] = useState(null); - const [mobileDrawer, setMobileDrawer] = useState(null); - - // Focused mode state (for jumping to old messages not in paginated view) - const [focusedMode, setFocusedMode] = useState(false); - const [focusedMessageId, setFocusedMessageId] = useState(null); - const [focusedMessages, setFocusedMessages] = useState([]); - const [focusedHasOlder, setFocusedHasOlder] = useState(false); - const [focusedHasNewer, setFocusedHasNewer] = useState(false); - const [focusedLoading, setFocusedLoading] = useState(false); - const focusedModeRef = useRef(false); - const focusedLoadingMoreRef = useRef(false); - - const inputDivRef = useRef(null); - const savedRangeRef = useRef(null); - const fileInputRef = useRef(null); - const typingTimeoutRef = useRef(null); - const lastTypingEmitRef = useRef(0); - const isInitialLoadRef = useRef(true); - const initialScrollScheduledRef = useRef(false); - const decryptionDoneRef = useRef(false); - const channelLoadIdRef = useRef(0); - const jumpToMessageIdRef = useRef(null); - const pingSeededRef = useRef(false); - const statusRef = useRef(null); - const loadMoreRef = useRef(null); - const userSentMessageRef = useRef(false); - const scrollOnNextDataRef = useRef(false); - const notifiedMessageIdsRef = useRef(new Set()); - const pendingNotificationIdsRef = useRef(new Set()); - const lastPingTimeRef = useRef(0); - - // Virtuoso refs and state - const virtuosoRef = useRef(null); - const scrollerElRef = useRef(null); - const chatInputFormRef = useRef(null); - const INITIAL_FIRST_INDEX = 100000; - const [firstItemIndex, setFirstItemIndex] = useState(INITIAL_FIRST_INDEX); - const prevMessageCountRef = useRef(0); - const prevFirstMsgIdRef = useRef(null); - const isAtBottomRef = useRef(true); - const isLoadingMoreRef = useRef(false); - const loadMoreSettlingRef = useRef(false); - const loadMoreSettlingTimerRef = useRef(null); - const realDistanceFromBottomRef = useRef(0); - const userIsScrolledUpRef = useRef(false); - - const convex = useConvex(); - - const members = useQuery(api.members.getChannelMembers, channelId ? { channelId } : "skip") || []; - const roles = useQuery(api.roles.list, channelType !== 'dm' ? {} : "skip") || []; - const myPermissions = useQuery(api.roles.getMyPermissions, currentUserId ? { userId: currentUserId } : "skip"); - const customEmojis = useQuery(api.customEmojis.list) || []; - - const { results: rawMessages, status, loadMore } = usePaginatedQuery( - api.messages.list, - channelId && !focusedMode ? { channelId, userId: currentUserId || undefined } : "skip", - { initialNumItems: 50 } - ); - - useEffect(() => { - statusRef.current = status; - loadMoreRef.current = loadMore; - if (status !== 'LoadingMore') { - isLoadingMoreRef.current = false; - if (loadMoreSettlingRef.current) { - if (loadMoreSettlingTimerRef.current) clearTimeout(loadMoreSettlingTimerRef.current); - loadMoreSettlingTimerRef.current = setTimeout(() => { - loadMoreSettlingRef.current = false; - loadMoreSettlingTimerRef.current = null; - }, 150); - } - } - }, [status, loadMore]); - - const typingData = useQuery( - api.typing.getTyping, - channelId ? { channelId } : "skip" - ) || []; - - const sendMessageMutation = useMutation(api.messages.send); - const editMessageMutation = useMutation(api.messages.edit); - const pinMessageMutation = useMutation(api.messages.pin); - const deleteMessageMutation = useMutation(api.messages.remove); - const addReaction = useMutation(api.reactions.add); - const removeReaction = useMutation(api.reactions.remove); - const startTypingMutation = useMutation(api.typing.startTyping); - const stopTypingMutation = useMutation(api.typing.stopTyping); - const markReadMutation = useMutation(api.readState.markRead); - const sendBatchMutation = useMutation(api.messages.sendBatch); - const floodInProgressRef = useRef(false); - const floodAbortRef = useRef(false); - - const readState = useQuery( - api.readState.getReadState, - channelId && currentUserId ? { userId: currentUserId, channelId } : "skip" - ); - - const showGifPicker = pickerTab !== null; - - useEffect(() => { - const handleClickOutside = () => { if (showGifPicker) setPickerTab(null); }; - window.addEventListener('click', handleClickOutside); - return () => window.removeEventListener('click', handleClickOutside); - }, [showGifPicker]); - - const TAG_LENGTH = 32; - - useEffect(() => { - if (focusedModeRef.current) return; - if (!rawMessages || rawMessages.length === 0) { - setDecryptedMessages([]); - return; - } - - let cancelled = false; - - // Phase 1: Immediately render from cache (cached = content, uncached = "[Decrypting...]") - const buildFromCache = () => { - return [...rawMessages].reverse().map(msg => { - const cached = messageDecryptionCache.get(msg.id); - return { - ...msg, - content: cached?.content ?? '[Decrypting...]', - isVerified: cached?.isVerified ?? null, - decryptedReply: cached?.decryptedReply ?? null, - }; - }); - }; - - const newMessages = buildFromCache(); - - // Adjust firstItemIndex atomically with data to prevent Virtuoso scroll jump - const prevCount = prevMessageCountRef.current; - const newCount = newMessages.length; - if (newCount > prevCount && prevCount > 0) { - if (prevFirstMsgIdRef.current && newMessages[0]?.id !== prevFirstMsgIdRef.current) { - const prependedCount = newCount - prevCount; - setFirstItemIndex(prev => prev - prependedCount); - } - } - prevMessageCountRef.current = newCount; - prevFirstMsgIdRef.current = newMessages[0]?.id || null; - - scrollLog('[SCROLL:decrypt] Phase 1 — setDecryptedMessages from cache', { count: newMessages.length }); - setDecryptedMessages(newMessages); - - // Phase 2: Batch-decrypt only uncached messages in background - const processUncached = async () => { - if (!channelKey) return; - - // Optimistic: check if any uncached messages match ciphertext from our own sends - for (const msg of rawMessages) { - if (messageDecryptionCache.has(msg.id)) continue; - const plaintext = optimisticMapRef.current.get(msg.ciphertext); - if (plaintext) { - messageDecryptionCache.set(msg.id, { content: plaintext, isVerified: true, decryptedReply: null }); - optimisticMapRef.current.delete(msg.ciphertext); - } - } - - const needsDecryption = rawMessages.filter(msg => { - const cached = messageDecryptionCache.get(msg.id); - if (!cached) return true; - if (msg.replyToNonce && msg.replyToContent && cached.decryptedReply === null) return true; - return false; - }); - - if (needsDecryption.length === 0) { - // Still re-render from cache in case optimistic matches were added - if (!cancelled) { - decryptionDoneRef.current = true; - scrollLog('[SCROLL:decrypt] Phase 2 — all cached, setDecryptedMessages'); - setDecryptedMessages(buildFromCache()); - - if (isInitialLoadRef.current && !initialScrollScheduledRef.current && virtuosoRef.current && !jumpToMessageIdRef.current) { - initialScrollScheduledRef.current = true; - const loadId = channelLoadIdRef.current; - scrollLog('[SCROLL:initialLoad] scheduling scroll chain'); - const scrollEnd = () => { const el = scrollerElRef.current; if (el) { scrollLog('[SCROLL:initialLoad] scrollEnd exec'); el.scrollTop = el.scrollHeight; } }; - requestAnimationFrame(() => requestAnimationFrame(() => { - if (channelLoadIdRef.current === loadId) scrollEnd(); - })); - setTimeout(() => { if (channelLoadIdRef.current === loadId && isInitialLoadRef.current) { scrollEnd(); isInitialLoadRef.current = false; } }, 500); - } - } - return; - } - - // Build batch arrays for decrypt and verify - const decryptItems = []; - const decryptMsgMap = []; // parallel array to track which msg each item belongs to - const replyDecryptItems = []; - const replyMsgMap = []; - const verifyItems = []; - const verifyMsgMap = []; - - for (const msg of needsDecryption) { - if (msg.ciphertext && msg.ciphertext.length >= TAG_LENGTH) { - const tag = msg.ciphertext.slice(-TAG_LENGTH); - const content = msg.ciphertext.slice(0, -TAG_LENGTH); - decryptItems.push({ ciphertext: content, key: channelKey, iv: msg.nonce, tag }); - decryptMsgMap.push(msg); - } - - if (msg.replyToContent && msg.replyToNonce) { - const rTag = msg.replyToContent.slice(-TAG_LENGTH); - const rContent = msg.replyToContent.slice(0, -TAG_LENGTH); - replyDecryptItems.push({ ciphertext: rContent, key: channelKey, iv: msg.replyToNonce, tag: rTag }); - replyMsgMap.push(msg); - } - - if (msg.signature && msg.public_signing_key) { - verifyItems.push({ publicKey: msg.public_signing_key, message: msg.ciphertext, signature: msg.signature }); - verifyMsgMap.push(msg); - } - } - - // Execute batch IPC calls in parallel (2-3 calls instead of 100+) - const [decryptResults, replyResults, verifyResults] = await Promise.all([ - decryptItems.length > 0 - ? crypto.decryptBatch(decryptItems) - : [], - replyDecryptItems.length > 0 - ? crypto.decryptBatch(replyDecryptItems) - : [], - verifyItems.length > 0 - ? crypto.verifyBatch(verifyItems) - : [], - ]); - - if (cancelled) return; - - // Build lookup maps from batch results - const decryptedMap = new Map(); - for (let i = 0; i < decryptResults.length; i++) { - const msg = decryptMsgMap[i]; - const result = decryptResults[i]; - decryptedMap.set(msg.id, result.success ? result.data : '[Decryption Error]'); - } - - const replyMap = new Map(); - for (let i = 0; i < replyResults.length; i++) { - const msg = replyMsgMap[i]; - const result = replyResults[i]; - if (result.success) { - let text = result.data; - if (text.startsWith('{')) text = '[Attachment]'; - else if (text.length > 100) text = text.substring(0, 100) + '...'; - replyMap.set(msg.id, text); - } else { - replyMap.set(msg.id, '[Encrypted]'); - } - } - - const verifyMap = new Map(); - for (let i = 0; i < verifyResults.length; i++) { - const msg = verifyMsgMap[i]; - const verified = verifyResults[i].verified; - verifyMap.set(msg.id, verified === null ? null : (verifyResults[i].success && verified)); - } - - // Populate cache - for (const msg of needsDecryption) { - const content = decryptedMap.get(msg.id) ?? - (msg.ciphertext && msg.ciphertext.length < TAG_LENGTH ? '[Invalid Encrypted Message]' : '[Encrypted Message - Key Missing]'); - const isVerified = verifyMap.has(msg.id) ? verifyMap.get(msg.id) : null; - const decryptedReply = replyMap.get(msg.id) ?? null; - messageDecryptionCache.set(msg.id, { content, isVerified, decryptedReply }); - } - - evictCacheIfNeeded(); - - // Index successfully decrypted messages for search - if (searchCtx?.isReady) { - const toIndex = needsDecryption.map(msg => { - const cached = messageDecryptionCache.get(msg.id); - if (!cached || cached.content.startsWith('[')) return null; - return { - id: msg.id, - channel_id: channelId, - sender_id: msg.sender_id, - username: msg.username, - content: cached.content, - created_at: msg.created_at, - pinned: msg.pinned, - replyToId: msg.replyToId, - }; - }).filter(Boolean); - if (toIndex.length > 0) searchCtx.indexMessages(toIndex); - } - - if (cancelled) return; - - // Phase 3: Re-render with newly decrypted content - decryptionDoneRef.current = true; - scrollLog('[SCROLL:decrypt] Phase 3 — decrypted, setDecryptedMessages', { count: needsDecryption.length }); - setDecryptedMessages(buildFromCache()); - - // After decryption, items may be taller — re-scroll to bottom. - // Double-rAF waits for paint + ResizeObserver cycle; single safety timeout since - // dimension reservation on media prevents most late-sizing shifts. - if (isInitialLoadRef.current && !initialScrollScheduledRef.current && virtuosoRef.current && !jumpToMessageIdRef.current) { - initialScrollScheduledRef.current = true; - const loadId = channelLoadIdRef.current; - scrollLog('[SCROLL:initialLoad] scheduling scroll chain (phase 3)'); - const scrollEnd = () => { const el = scrollerElRef.current; if (el) { scrollLog('[SCROLL:initialLoad] scrollEnd exec (phase 3)'); el.scrollTop = el.scrollHeight; } }; - requestAnimationFrame(() => requestAnimationFrame(() => { - if (channelLoadIdRef.current === loadId) scrollEnd(); - })); - setTimeout(() => { if (channelLoadIdRef.current === loadId && isInitialLoadRef.current) { scrollEnd(); isInitialLoadRef.current = false; } }, 500); - } - }; - - processUncached(); - return () => { cancelled = true; }; - }, [rawMessages, channelKey]); - - // Index cached messages when search DB becomes ready (covers messages decrypted before DB init) - useEffect(() => { - if (!searchCtx?.isReady || !channelId || decryptedMessages.length === 0) return; - const toIndex = decryptedMessages - .filter(m => m.content && !m.content.startsWith('[')) - .map(m => ({ - id: m.id, - channel_id: channelId, - sender_id: m.sender_id, - username: m.username, - content: m.content, - created_at: m.created_at, - pinned: m.pinned, - replyToId: m.replyToId, - })); - if (toIndex.length > 0) searchCtx.indexMessages(toIndex); - }, [searchCtx?.isReady]); - - useEffect(() => { - // Don't clear messageDecryptionCache — it persists across channel switches - channelLoadIdRef.current += 1; - setDecryptedMessages([]); - isInitialLoadRef.current = true; - initialScrollScheduledRef.current = false; - decryptionDoneRef.current = false; - pingSeededRef.current = false; - notifiedMessageIdsRef.current = new Set(); - pendingNotificationIdsRef.current = new Set(); - setReplyingTo(null); - setEditingMessage(null); - setMentionQuery(null); - setUnreadDividerTimestamp(null); - setReactionPickerMsgId(null); - setSlashQuery(null); - setEphemeralMessages([]); - // Reset focused mode - setFocusedMode(false); - focusedModeRef.current = false; - setFocusedMessageId(null); - setFocusedMessages([]); - setFocusedHasOlder(false); - setFocusedHasNewer(false); - setFocusedLoading(false); - focusedLoadingMoreRef.current = false; - floodAbortRef.current = true; - isLoadingMoreRef.current = false; - loadMoreSettlingRef.current = false; - userIsScrolledUpRef.current = false; - realDistanceFromBottomRef.current = 0; - if (loadMoreSettlingTimerRef.current) { - clearTimeout(loadMoreSettlingTimerRef.current); - loadMoreSettlingTimerRef.current = null; - } - setFirstItemIndex(INITIAL_FIRST_INDEX); - prevMessageCountRef.current = 0; - prevFirstMsgIdRef.current = null; - onTogglePinned(); - }, [channelId]); - - // Sync jumpToMessageId prop to ref - useEffect(() => { - jumpToMessageIdRef.current = jumpToMessageId || null; - }, [jumpToMessageId]); - - // Jump to a specific message (from search results or pinned panel) - useEffect(() => { - if (!jumpToMessageId || !decryptedMessages.length || !decryptionDoneRef.current) return; - const idx = decryptedMessages.findIndex(m => m.id === jumpToMessageId); - if (idx !== -1 && virtuosoRef.current) { - scrollLog('[SCROLL:jumpToMessage]', { jumpToMessageId, idx }); - isInitialLoadRef.current = false; - virtuosoRef.current.scrollToIndex({ index: idx, align: 'center', behavior: 'smooth' }); - setTimeout(() => { - const el = document.getElementById(`msg-${jumpToMessageId}`); - if (el) { - el.classList.add('message-highlight'); - setTimeout(() => el.classList.remove('message-highlight'), 2000); - } - }, 300); - onClearJumpToMessage?.(); - } else if (idx === -1 && !focusedModeRef.current) { - // Message not loaded in paginated view — enter focused mode - scrollLog('[SCROLL:jumpToMessage] entering focused mode for', jumpToMessageId); - setFocusedMessageId(jumpToMessageId); - onClearJumpToMessage?.(); - } - }, [jumpToMessageId, decryptedMessages, onClearJumpToMessage]); - - // Safety timeout: clear jumpToMessageId only in normal mode - useEffect(() => { - if (!jumpToMessageId || focusedModeRef.current) return; - const timer = setTimeout(() => onClearJumpToMessage?.(), 5000); - return () => clearTimeout(timer); - }, [jumpToMessageId, onClearJumpToMessage]); - - // Focused mode: fetch messages around target - useEffect(() => { - if (!focusedMessageId || !channelId) return; - let cancelled = false; - setFocusedLoading(true); - setFocusedMode(true); - focusedModeRef.current = true; - setDecryptedMessages([]); - decryptionDoneRef.current = false; - isInitialLoadRef.current = true; - initialScrollScheduledRef.current = false; - setFirstItemIndex(INITIAL_FIRST_INDEX); - prevMessageCountRef.current = 0; - prevFirstMsgIdRef.current = null; - - (async () => { - try { - const result = await convex.query(api.messages.listAround, { - channelId, - messageId: focusedMessageId, - userId: currentUserId || undefined, - }); - if (cancelled) return; - if (!result.targetFound) { - scrollLog('[SCROLL:focusedMode] target not found'); - setFocusedLoading(false); - // Auto-exit focused mode after brief delay - setTimeout(() => { - if (!cancelled) { - setFocusedMode(false); - focusedModeRef.current = false; - setFocusedMessageId(null); - setFocusedMessages([]); - } - }, 2000); - return; - } - setFocusedMessages(result.messages); - setFocusedHasOlder(result.hasOlder); - setFocusedHasNewer(result.hasNewer); - setFocusedLoading(false); - } catch (err) { - console.error('Failed to load messages around target:', err); - if (!cancelled) { - setFocusedLoading(false); - setFocusedMode(false); - focusedModeRef.current = false; - setFocusedMessageId(null); - setFocusedMessages([]); - } - } - })(); - - return () => { cancelled = true; }; - }, [focusedMessageId, channelId]); - - // Focused mode: decrypt focusedMessages (parallel to normal decrypt effect) - useEffect(() => { - if (!focusedMode || focusedMessages.length === 0) return; - - let cancelled = false; - - const buildFromCache = () => { - return focusedMessages.map(msg => { - const cached = messageDecryptionCache.get(msg.id); - return { - ...msg, - content: cached?.content ?? '[Decrypting...]', - isVerified: cached?.isVerified ?? null, - decryptedReply: cached?.decryptedReply ?? null, - }; - }); - }; - - const newMessages = buildFromCache(); - - // Adjust firstItemIndex for prepended messages - const prevCount = prevMessageCountRef.current; - const newCount = newMessages.length; - if (newCount > prevCount && prevCount > 0) { - if (prevFirstMsgIdRef.current && newMessages[0]?.id !== prevFirstMsgIdRef.current) { - const prependedCount = newCount - prevCount; - setFirstItemIndex(prev => prev - prependedCount); - } - } - prevMessageCountRef.current = newCount; - prevFirstMsgIdRef.current = newMessages[0]?.id || null; - - setDecryptedMessages(newMessages); - - const processUncached = async () => { - if (!channelKey) return; - - const needsDecryption = focusedMessages.filter(msg => { - const cached = messageDecryptionCache.get(msg.id); - if (!cached) return true; - if (msg.replyToNonce && msg.replyToContent && cached.decryptedReply === null) return true; - return false; - }); - - if (needsDecryption.length === 0) { - if (!cancelled) { - decryptionDoneRef.current = true; - setDecryptedMessages(buildFromCache()); - } - return; - } - - const decryptItems = []; - const decryptMsgMap = []; - const replyDecryptItems = []; - const replyMsgMap = []; - const verifyItems = []; - const verifyMsgMap = []; - - for (const msg of needsDecryption) { - if (msg.ciphertext && msg.ciphertext.length >= TAG_LENGTH) { - const tag = msg.ciphertext.slice(-TAG_LENGTH); - const content = msg.ciphertext.slice(0, -TAG_LENGTH); - decryptItems.push({ ciphertext: content, key: channelKey, iv: msg.nonce, tag }); - decryptMsgMap.push(msg); - } - if (msg.replyToContent && msg.replyToNonce) { - const rTag = msg.replyToContent.slice(-TAG_LENGTH); - const rContent = msg.replyToContent.slice(0, -TAG_LENGTH); - replyDecryptItems.push({ ciphertext: rContent, key: channelKey, iv: msg.replyToNonce, tag: rTag }); - replyMsgMap.push(msg); - } - if (msg.signature && msg.public_signing_key) { - verifyItems.push({ publicKey: msg.public_signing_key, message: msg.ciphertext, signature: msg.signature }); - verifyMsgMap.push(msg); - } - } - - const [decryptResults, replyResults, verifyResults] = await Promise.all([ - decryptItems.length > 0 ? crypto.decryptBatch(decryptItems) : [], - replyDecryptItems.length > 0 ? crypto.decryptBatch(replyDecryptItems) : [], - verifyItems.length > 0 ? crypto.verifyBatch(verifyItems) : [], - ]); - - if (cancelled) return; - - const decryptedMap = new Map(); - for (let i = 0; i < decryptResults.length; i++) { - const msg = decryptMsgMap[i]; - const result = decryptResults[i]; - decryptedMap.set(msg.id, result.success ? result.data : '[Decryption Error]'); - } - - const replyMap = new Map(); - for (let i = 0; i < replyResults.length; i++) { - const msg = replyMsgMap[i]; - const result = replyResults[i]; - if (result.success) { - let text = result.data; - if (text.startsWith('{')) text = '[Attachment]'; - else if (text.length > 100) text = text.substring(0, 100) + '...'; - replyMap.set(msg.id, text); - } else { - replyMap.set(msg.id, '[Encrypted]'); - } - } - - const verifyMap = new Map(); - for (let i = 0; i < verifyResults.length; i++) { - const msg = verifyMsgMap[i]; - const verified = verifyResults[i].verified; - verifyMap.set(msg.id, verified === null ? null : (verifyResults[i].success && verified)); - } - - for (const msg of needsDecryption) { - const content = decryptedMap.get(msg.id) ?? - (msg.ciphertext && msg.ciphertext.length < TAG_LENGTH ? '[Invalid Encrypted Message]' : '[Encrypted Message - Key Missing]'); - const isVerified = verifyMap.has(msg.id) ? verifyMap.get(msg.id) : null; - const decryptedReply = replyMap.get(msg.id) ?? null; - messageDecryptionCache.set(msg.id, { content, isVerified, decryptedReply }); - } - - evictCacheIfNeeded(); - - if (cancelled) return; - - decryptionDoneRef.current = true; - setDecryptedMessages(buildFromCache()); - }; - - processUncached(); - return () => { cancelled = true; }; - }, [focusedMode, focusedMessages, channelKey]); - - // Focused mode: scroll to target message after decryption completes - useEffect(() => { - if (!focusedMode || !focusedMessageId || !decryptionDoneRef.current || !decryptedMessages.length) return; - const idx = decryptedMessages.findIndex(m => m.id === focusedMessageId); - if (idx !== -1 && virtuosoRef.current) { - scrollLog('[SCROLL:focusedMode] scrolling to target', { focusedMessageId, idx }); - isInitialLoadRef.current = false; - setTimeout(() => { - virtuosoRef.current?.scrollToIndex({ index: idx, align: 'center', behavior: 'smooth' }); - setTimeout(() => { - const el = document.getElementById(`msg-${focusedMessageId}`); - if (el) { - el.classList.add('message-highlight'); - setTimeout(() => el.classList.remove('message-highlight'), 2000); - } - }, 300); - }, 100); - } - }, [focusedMode, focusedMessageId, decryptedMessages]); - - const myRoleNames = members?.find(m => m.username === username)?.roles?.map(r => r.name) || []; - - const isMentionedInContent = useCallback((content) => { - if (!content) return false; - return content.includes(`@${username}`) || - myRoleNames.some(rn => - rn.startsWith('@') ? content.includes(rn) : content.includes(`@role:${rn}`) - ); - }, [username, myRoleNames]); - - const playPingSound = useCallback(() => { - if (isReceivingScreenShareAudio) return; - const now = Date.now(); - if (now - lastPingTimeRef.current < 1000) return; - lastPingTimeRef.current = now; - const audio = new Audio(PingSound); - audio.volume = 0.5; - audio.play().catch(() => {}); - }, [isReceivingScreenShareAudio]); - - // Play ping sound when a new message mentions us (by username or role) - useEffect(() => { - if (!decryptedMessages.length) return; - - // Initial load: seed all IDs, no sound - if (!pingSeededRef.current) { - for (const msg of decryptedMessages) { - if (msg.id) notifiedMessageIdsRef.current.add(msg.id); - } - pingSeededRef.current = true; - return; - } - - let shouldPing = false; - - // Check newest messages (end of array) backwards — stop at first known ID - for (let i = decryptedMessages.length - 1; i >= 0; i--) { - const msg = decryptedMessages[i]; - if (!msg.id) continue; - if (notifiedMessageIdsRef.current.has(msg.id)) break; - - // Skip own messages - if (msg.sender_id === currentUserId) { - notifiedMessageIdsRef.current.add(msg.id); - continue; - } - - // Still decrypting — mark pending - if (msg.content === '[Decrypting...]') { - pendingNotificationIdsRef.current.add(msg.id); - continue; - } - - notifiedMessageIdsRef.current.add(msg.id); - pendingNotificationIdsRef.current.delete(msg.id); - - if (isMentionedInContent(msg.content)) shouldPing = true; - } - - // Re-check previously pending messages now decrypted - if (!shouldPing && pendingNotificationIdsRef.current.size > 0) { - for (const msg of decryptedMessages) { - if (!pendingNotificationIdsRef.current.has(msg.id)) continue; - if (msg.content === '[Decrypting...]') continue; - pendingNotificationIdsRef.current.delete(msg.id); - notifiedMessageIdsRef.current.add(msg.id); - if (isMentionedInContent(msg.content)) shouldPing = true; - } - } - - if (shouldPing) { - playPingSound(); - if (!document.hasFocus()) platform.windowControls?.flashFrame?.(); - } - }, [decryptedMessages, currentUserId, isMentionedInContent, playPingSound, platform]); - - // Capture the unread divider position when read state loads for a channel - const unreadDividerCapturedRef = useRef(null); - useEffect(() => { - if (!channelId) return; - // Reset when channel changes - unreadDividerCapturedRef.current = null; - setUnreadDividerTimestamp(null); - }, [channelId]); - - useEffect(() => { - if (unreadDividerCapturedRef.current === channelId) return; - if (readState === undefined) return; // still loading - if (readState === null) { - // Never read this channel — no divider needed (first visit) - unreadDividerCapturedRef.current = channelId; - return; - } - unreadDividerCapturedRef.current = channelId; - setUnreadDividerTimestamp(readState.lastReadTimestamp); - }, [readState, channelId]); - - // Ref to avoid decryptedMessages in markChannelAsRead deps (prevents handleAtBottomStateChange churn) - const decryptedMessagesRef = useRef(decryptedMessages); - decryptedMessagesRef.current = decryptedMessages; - - // Mark channel as read when scrolled to bottom - const markChannelAsRead = useCallback(() => { - const msgs = decryptedMessagesRef.current; - if (!currentUserId || !channelId || !msgs.length) return; - const lastMsg = msgs[msgs.length - 1]; - if (!lastMsg?.created_at) return; - markReadMutation({ userId: currentUserId, channelId, lastReadTimestamp: new Date(lastMsg.created_at).getTime() }).catch(() => {}); - setUnreadDividerTimestamp(null); - }, [currentUserId, channelId, markReadMutation]); - - const markChannelAsReadRef = useRef(markChannelAsRead); - markChannelAsReadRef.current = markChannelAsRead; - - const typingUsers = typingData.filter(t => t.username !== username); - const mentionableRoles = roles.filter(r => r.name !== 'Owner'); - const filteredMentionRoles = mentionQuery !== null && channelType !== 'dm' - ? filterRolesForMention(mentionableRoles, mentionQuery) : []; - const filteredMentionMembers = mentionQuery !== null - ? filterMembersForMention(members, mentionQuery) : []; - const mentionItems = [ - ...filteredMentionRoles.map(r => ({ type: 'role', ...r })), - ...filteredMentionMembers.map(m => ({ type: 'member', ...m })), - ]; - const scrollToBottom = useCallback((force = false) => { - // Guard: when used as an event handler (e.g. img onLoad), the event - // object is passed as `force`. Coerce to boolean to ignore it. - if (typeof force !== 'boolean') force = false; - scrollLog('[SCROLL:scrollToBottom]', { force, initialLoad: isInitialLoadRef.current, userScrolledUp: userIsScrolledUpRef.current }); - if (isInitialLoadRef.current) { - const el = scrollerElRef.current; - if (el) el.scrollTop = el.scrollHeight; - return; - } - if (force) { - // Direct DOM scroll is more reliable than scrollToIndex for user-sent messages - // Also reset userIsScrolledUpRef since we're explicitly scrolling to bottom - userIsScrolledUpRef.current = false; - const snap = () => { - const el = scrollerElRef.current; - if (el) el.scrollTop = el.scrollHeight; - }; - snap(); - // Single rAF retry — dimension reservation makes multi-retry unnecessary - requestAnimationFrame(snap); - } else if (virtuosoRef.current && !userIsScrolledUpRef.current) { - virtuosoRef.current.scrollToIndex({ - index: 'LAST', - align: 'end', - behavior: 'smooth', - }); - } - }, []); - - // Virtuoso: startReached replaces IntersectionObserver - const handleStartReached = useCallback(() => { - if (focusedModeRef.current) { - if (focusedLoadingMoreRef.current || !focusedHasOlder) return; - const msgs = decryptedMessages; - if (!msgs.length) return; - const oldestTimestamp = new Date(msgs[0].created_at).getTime(); - focusedLoadingMoreRef.current = true; - (async () => { - try { - const result = await convex.query(api.messages.listBefore, { - channelId, - beforeTimestamp: oldestTimestamp, - userId: currentUserId || undefined, - }); - setFocusedMessages(prev => { - const existingIds = new Set(prev.map(m => m.id)); - const newMsgs = result.messages.filter(m => !existingIds.has(m.id)); - return [...newMsgs, ...prev]; - }); - setFocusedHasOlder(result.hasMore); - } catch (err) { - console.error('Failed to load older messages:', err); - } finally { - focusedLoadingMoreRef.current = false; - } - })(); - return; - } - if (isLoadingMoreRef.current) return; - if (statusRef.current === 'CanLoadMore') { - isLoadingMoreRef.current = true; - loadMoreSettlingRef.current = true; - if (loadMoreSettlingTimerRef.current) { - clearTimeout(loadMoreSettlingTimerRef.current); - loadMoreSettlingTimerRef.current = null; - } - loadMoreRef.current(50); - } - }, [focusedHasOlder, decryptedMessages, channelId, currentUserId]); - - const handleEndReached = useCallback(() => { - if (!focusedModeRef.current || !focusedHasNewer || focusedLoadingMoreRef.current) return; - const msgs = decryptedMessages; - if (!msgs.length) return; - const newestTimestamp = new Date(msgs[msgs.length - 1].created_at).getTime(); - focusedLoadingMoreRef.current = true; - (async () => { - try { - const result = await convex.query(api.messages.listAfter, { - channelId, - afterTimestamp: newestTimestamp, - userId: currentUserId || undefined, - }); - setFocusedMessages(prev => { - const existingIds = new Set(prev.map(m => m.id)); - const newMsgs = result.messages.filter(m => !existingIds.has(m.id)); - return [...prev, ...newMsgs]; - }); - setFocusedHasNewer(result.hasMore); - } catch (err) { - console.error('Failed to load newer messages:', err); - } finally { - focusedLoadingMoreRef.current = false; - } - })(); - }, [focusedHasNewer, decryptedMessages, channelId, currentUserId]); - - // Virtuoso: followOutput auto-scrolls on new messages and handles initial load - const followOutput = useCallback((isAtBottom) => { - if (focusedModeRef.current) return false; - - const metrics = { - isAtBottom, - userScrolledUp: userIsScrolledUpRef.current, - realDist: realDistanceFromBottomRef.current, - jumpTo: jumpToMessageIdRef.current, - userSent: userSentMessageRef.current, - initialLoad: isInitialLoadRef.current, - settling: loadMoreSettlingRef.current, - }; - - if (jumpToMessageIdRef.current) { - scrollLog('[SCROLL:followOutput] BLOCKED by jumpToMessage', metrics); - return false; - } - - // If user sent a message, ALWAYS scroll to bottom aggressively - if (userSentMessageRef.current) { - userSentMessageRef.current = false; - scrollLog('[SCROLL:followOutput] USER SENT MSG → auto', metrics); - return 'auto'; - } - - // During initial load, disable followOutput so it doesn't conflict with manual scrollToIndex calls - if (isInitialLoadRef.current) { - scrollLog('[SCROLL:followOutput] BLOCKED by initialLoad', metrics); - return false; - } - - // During load-more settling, don't auto-scroll (prevents snap-to-bottom when header changes) - if (loadMoreSettlingRef.current) { - scrollLog('[SCROLL:followOutput] BLOCKED by settling', metrics); - return false; - } - - // CORE FIX: If user has scrolled >150px from bottom, never auto-scroll - // regardless of what Virtuoso's internal isAtBottom state thinks - if (userIsScrolledUpRef.current) { - scrollLog('[SCROLL:followOutput] BLOCKED by userIsScrolledUp', metrics); - return false; - } - - const decision = isAtBottom ? 'smooth' : false; - scrollLog('[SCROLL:followOutput] decision:', decision, metrics); - return decision; - }, []); - - // Virtuoso: atBottomStateChange replaces manual scroll listener for read state - const handleAtBottomStateChange = useCallback((atBottom) => { - if (focusedModeRef.current) return; - scrollLog('[SCROLL:atBottomStateChange]', { atBottom, settling: loadMoreSettlingRef.current, userScrolledUp: userIsScrolledUpRef.current }); - if (loadMoreSettlingRef.current && atBottom) { - return; - } - isAtBottomRef.current = atBottom; - if (atBottom) { - markChannelAsRead(); - // Delay clearing isInitialLoadRef so self-correction has time for late-loading content - if (isInitialLoadRef.current && decryptionDoneRef.current) { - const loadId = channelLoadIdRef.current; - setTimeout(() => { - if (channelLoadIdRef.current === loadId) { - isInitialLoadRef.current = false; - } - }, 300); - } - } - }, [markChannelAsRead]); - - // Mark as read when component unmounts (e.g., switching to voice channel) - useEffect(() => { - return () => { - markChannelAsReadRef.current(); - }; - }, []); - - // Track input height for synchronous scroll adjustment - const prevInputHeightRef = useRef(0); - - // Use useLayoutEffect to adjust scroll BEFORE paint when React updates (e.g. isMultiline change) - React.useLayoutEffect(() => { - const el = chatInputFormRef.current; - if (!el) return; - const currentHeight = el.clientHeight; - - if (prevInputHeightRef.current > 0 && currentHeight !== prevInputHeightRef.current) { - const heightDiff = currentHeight - prevInputHeightRef.current; - const scroller = scrollerElRef.current; - - if (scroller) { - const scrollTop = scroller.scrollTop; - const scrollHeight = scroller.scrollHeight; - const clientHeight = scroller.clientHeight; - - const currentDistanceFromBottom = scrollHeight - scrollTop - clientHeight; - const previousDistanceFromBottom = currentDistanceFromBottom - heightDiff; - - // If we were at bottom (approx) AND user hasn't scrolled up, force stay at bottom - const willScroll = previousDistanceFromBottom < 50 && !userIsScrolledUpRef.current; - scrollLog('[SCROLL:layoutEffect]', { heightDiff, previousDistanceFromBottom, userScrolledUp: userIsScrolledUpRef.current, willScroll }); - if (willScroll) { - scroller.scrollTop = scrollHeight; - } - } - } - prevInputHeightRef.current = currentHeight; - }); - - useEffect(() => { - const el = chatInputFormRef.current; - if (!el) { - console.error('[ResizeObserver] chatInputFormRef is null!'); - return; - } - console.log('[ResizeObserver] Attaching to form', el); - - const observer = new ResizeObserver(() => { - const newHeight = el.clientHeight; - // We use a separate ref for ResizeObserver to avoid conflict/loop with layout effect if needed, - // but sharing prevInputHeightRef is mostly fine if we are careful. - // Actually, let's just use the ref we have. - if (newHeight !== prevInputHeightRef.current) { - const heightDiff = newHeight - prevInputHeightRef.current; - prevInputHeightRef.current = newHeight; - - const scroller = scrollerElRef.current; - if (!scroller) return; - - const scrollTop = scroller.scrollTop; - const scrollHeight = scroller.scrollHeight; - const clientHeight = scroller.clientHeight; - - const currentDistanceFromBottom = scrollHeight - scrollTop - clientHeight; - const previousDistanceFromBottom = currentDistanceFromBottom - heightDiff; - - const willScroll = previousDistanceFromBottom < 50 && !userIsScrolledUpRef.current; - scrollLog('[SCROLL:resizeObserver]', { - newHeight, - heightDiff, - previousDistanceFromBottom, - userScrolledUp: userIsScrolledUpRef.current, - willScroll, - }); - - if (willScroll) { - scroller.scrollTop = scrollHeight; - } - } - }); - observer.observe(el); - return () => observer.disconnect(); - }, []); - - // Passive scroll listener: track real pixel distance from bottom - // This is the ground-truth for whether the user has scrolled up - useEffect(() => { - const scroller = scrollerElRef.current; - if (!scroller) return; - const onScroll = () => { - const dist = scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight; - realDistanceFromBottomRef.current = dist; - const wasUp = userIsScrolledUpRef.current; - // User is "scrolled up" if >150px from bottom - userIsScrolledUpRef.current = dist > 150; - if (wasUp !== userIsScrolledUpRef.current) { - scrollLog('[SCROLL:userScrollState]', { dist, scrolledUp: userIsScrolledUpRef.current }); - } - }; - scroller.addEventListener('scroll', onScroll, { passive: true }); - return () => scroller.removeEventListener('scroll', onScroll); - }, [scrollerElRef.current]); - - // Keep scroll pinned to bottom when the scroll container resizes (e.g. window resize) - useEffect(() => { - const scroller = scrollerElRef.current; - if (!scroller) return; - const observer = new ResizeObserver(() => { - if (!userIsScrolledUpRef.current && !isInitialLoadRef.current) { - scroller.scrollTop = scroller.scrollHeight; - } - }); - observer.observe(scroller); - return () => observer.disconnect(); - }, [scrollerElRef.current]); - - const saveSelection = () => { - const sel = window.getSelection(); - if (sel.rangeCount > 0) savedRangeRef.current = sel.getRangeAt(0).cloneRange(); - }; - - const insertEmoji = (emoji) => { - if (!inputDivRef.current) return; - const img = document.createElement('img'); - img.src = emoji.src; img.alt = `:${emoji.name}:`; img.className = "inline-emoji"; - img.style.width = "22px"; img.style.height = "22px"; img.style.verticalAlign = "bottom"; img.style.margin = "0 1px"; img.contentEditable = "false"; - const space = document.createTextNode(' '); - inputDivRef.current.focus(); - const sel = window.getSelection(); - if (savedRangeRef.current && inputDivRef.current.contains(savedRangeRef.current.commonAncestorContainer)) { sel.removeAllRanges(); sel.addRange(savedRangeRef.current); } - if (sel.rangeCount > 0 && inputDivRef.current.contains(sel.getRangeAt(0).commonAncestorContainer)) { - const range = sel.getRangeAt(0); range.deleteContents(); range.insertNode(space); range.insertNode(img); range.setStartAfter(space); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); - } else { - inputDivRef.current.appendChild(img); inputDivRef.current.appendChild(space); - const range = document.createRange(); range.setStartAfter(space); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); - } - setInput(inputDivRef.current.textContent); setHasImages(true); - }; - - const checkTypedEmoji = () => { - if (!inputDivRef.current) return; - const selection = window.getSelection(); - if (!selection.rangeCount) return; - const range = selection.getRangeAt(0); - const node = range.startContainer; - if (node.nodeType !== Node.TEXT_NODE) return; - - const content = node.textContent.substring(0, range.startOffset); - const match = content.match(/:([a-zA-Z0-9_]+):$/); - if (!match) return; - - const name = match[1]; - const emoji = customEmojis.find(e => e.name === name) || AllEmojis.find(e => e.name === name); - if (!emoji) return; - - const img = document.createElement('img'); - img.src = emoji.src; img.alt = `:${name}:`; img.className = "inline-emoji"; - img.style.width = "22px"; img.style.height = "22px"; img.style.verticalAlign = "bottom"; img.style.margin = "0 1px"; img.contentEditable = "false"; - const textBefore = node.textContent.substring(0, range.startOffset - match[0].length); - const textAfter = node.textContent.substring(range.startOffset); - node.textContent = textBefore; - const afterNode = document.createTextNode(textAfter); - if (node.nextSibling) { node.parentNode.insertBefore(img, node.nextSibling); node.parentNode.insertBefore(afterNode, img.nextSibling); } - else { node.parentNode.appendChild(img); node.parentNode.appendChild(afterNode); } - const newRange = document.createRange(); newRange.setStart(afterNode, 0); newRange.collapse(true); selection.removeAllRanges(); selection.addRange(newRange); - }; - - const checkMentionTrigger = () => { - if (!inputDivRef.current) return; - const selection = window.getSelection(); - if (!selection.rangeCount) return; - const range = selection.getRangeAt(0); - const node = range.startContainer; - if (node.nodeType !== Node.TEXT_NODE) { setMentionQuery(null); return; } - const content = node.textContent.substring(0, range.startOffset); - const match = content.match(/(?:^|\s)@(\w*)$/); - if (match) { - setMentionQuery(match[1]); - setMentionIndex(0); - } else { - setMentionQuery(null); - } - }; - - const checkSlashTrigger = () => { - if (!inputDivRef.current) return; - const text = inputDivRef.current.textContent; - if (text.startsWith('/')) { - setSlashQuery(text.slice(1)); - setSlashIndex(0); - } else { - setSlashQuery(null); - } - }; - - const handleSlashSelect = (cmd) => { - if (!inputDivRef.current) return; - inputDivRef.current.textContent = `/${cmd.name}`; - setInput(`/${cmd.name}`); - setSlashQuery(null); - // Place cursor at end - const range = document.createRange(); - const sel = window.getSelection(); - range.selectNodeContents(inputDivRef.current); - range.collapse(false); - sel.removeAllRanges(); - sel.addRange(range); - inputDivRef.current.focus(); - }; - - const filteredSlashCommands = slashQuery !== null ? filterSlashCommands(SLASH_COMMANDS, slashQuery) : []; - - const executeSlashCommand = (command, cmdArgs) => { - if (command.name === 'ping') { - setEphemeralMessages(prev => [...prev, { - id: `ephemeral-${Date.now()}`, - type: 'ephemeral', - command: '/ping', - username, - content: 'Pong!', - created_at: Date.now(), - }]); - } else if (command.name === 'flood') { - const count = Math.min(Math.max(parseInt(cmdArgs) || 100, 1), 5000); - if (floodInProgressRef.current) { - setEphemeralMessages(prev => [...prev, { - id: `ephemeral-${Date.now()}`, - type: 'ephemeral', - command: '/flood', - username, - content: 'A flood is already in progress. Please wait for it to finish.', - created_at: Date.now(), - }]); - return; - } - if (!channelKey) { - setEphemeralMessages(prev => [...prev, { - id: `ephemeral-${Date.now()}`, - type: 'ephemeral', - command: '/flood', - username, - content: 'Cannot flood: Missing encryption key for this channel.', - created_at: Date.now(), - }]); - return; - } - const senderId = localStorage.getItem('userId'); - const signingKey = sessionStorage.getItem('signingKey'); - if (!senderId || !signingKey) return; - - floodInProgressRef.current = true; - floodAbortRef.current = false; - const progressId = `ephemeral-flood-${Date.now()}`; - setEphemeralMessages(prev => [...prev, { - id: progressId, - type: 'ephemeral', - command: '/flood', - username, - content: `Generating messages... 0/${count} (0%)`, - created_at: Date.now(), - }]); - - (async () => { - const BATCH_SIZE = 50; - let sent = 0; - try { - for (let i = 0; i < count; i += BATCH_SIZE) { - if (floodAbortRef.current) break; - const batchEnd = Math.min(i + BATCH_SIZE, count); - const batch = []; - for (let j = i; j < batchEnd; j++) { - const text = generateUniqueMessage(j); - const { content: encryptedContent, iv, tag } = await crypto.encryptData(text, channelKey); - const ciphertext = encryptedContent + tag; - const signature = await crypto.signMessage(signingKey, ciphertext); - batch.push({ - channelId, - senderId, - ciphertext, - nonce: iv, - signature, - keyVersion: 1, - }); - } - await sendBatchMutation({ messages: batch }); - sent += batch.length; - const pct = Math.round((sent / count) * 100); - setEphemeralMessages(prev => prev.map(m => - m.id === progressId - ? { ...m, content: `Generating messages... ${sent}/${count} (${pct}%)` } - : m - )); - } - setEphemeralMessages(prev => prev.map(m => - m.id === progressId - ? { ...m, content: floodAbortRef.current ? `Flood stopped. Sent ${sent}/${count} messages.` : `Done! Sent ${sent} test messages.` } - : m - )); - } catch (err) { - console.error('Flood error:', err); - setEphemeralMessages(prev => prev.map(m => - m.id === progressId - ? { ...m, content: `Flood error after ${sent} messages: ${err.message}` } - : m - )); - } finally { - floodInProgressRef.current = false; - } - })(); - } - }; - - const insertMention = (item) => { - if (!inputDivRef.current) return; - const selection = window.getSelection(); - if (!selection.rangeCount) return; - const range = selection.getRangeAt(0); - const node = range.startContainer; - if (node.nodeType !== Node.TEXT_NODE) return; - const content = node.textContent.substring(0, range.startOffset); - const match = content.match(/(?:^|\s)@(\w*)$/); - if (!match) return; - const matchStart = match.index + (match[0].startsWith(' ') ? 1 : 0); - const before = node.textContent.substring(0, matchStart); - const after = node.textContent.substring(range.startOffset); - const insertText = item.type === 'role' - ? (item.name.startsWith('@') ? `${item.name} ` : `@role:${item.name} `) - : `@${item.username} `; - node.textContent = before + insertText + after; - const newOffset = before.length + insertText.length; - const newRange = document.createRange(); - newRange.setStart(node, newOffset); - newRange.collapse(true); - selection.removeAllRanges(); - selection.addRange(newRange); - setMentionQuery(null); - setInput(inputDivRef.current.textContent); - }; - - const MAX_ATTACHMENT_SIZE = 500 * 1024 * 1024; // 500MB - const processFile = (file) => { if (file.size > MAX_ATTACHMENT_SIZE) { alert('File must be under 500MB'); return; } setPendingFiles(prev => [...prev, file]); }; - const handleFileSelect = (e) => { if (e.target.files && e.target.files.length > 0) Array.from(e.target.files).forEach(processFile); }; - const isExternalFileDrag = (e) => { - const types = Array.from(e.dataTransfer.types); - return types.includes('Files') && !types.includes('text/uri-list') && !types.includes('text/html'); - }; - const handleDragOver = (e) => { if (!isExternalFileDrag(e)) return; e.preventDefault(); e.stopPropagation(); if (!isDragging) setIsDragging(true); }; - const handleDragLeave = (e) => { e.preventDefault(); e.stopPropagation(); if (e.currentTarget.contains(e.relatedTarget)) return; setIsDragging(false); }; - const handleDrop = (e) => { if (!isDragging) return; e.preventDefault(); e.stopPropagation(); setIsDragging(false); if (e.dataTransfer.files && e.dataTransfer.files.length > 0) Array.from(e.dataTransfer.files).forEach(processFile); }; - - const getMediaDimensions = (file) => new Promise((resolve) => { - if (file.type.startsWith('image/')) { - const img = new Image(); - const objectUrl = URL.createObjectURL(file); - img.onload = () => { URL.revokeObjectURL(objectUrl); resolve({ width: img.naturalWidth, height: img.naturalHeight }); }; - img.onerror = () => { URL.revokeObjectURL(objectUrl); resolve(null); }; - img.src = objectUrl; - } else if (file.type.startsWith('video/')) { - const video = document.createElement('video'); - const objectUrl = URL.createObjectURL(file); - video.onloadedmetadata = () => { URL.revokeObjectURL(objectUrl); resolve({ width: video.videoWidth, height: video.videoHeight }); }; - video.onerror = () => { URL.revokeObjectURL(objectUrl); resolve(null); }; - video.src = objectUrl; - } else { - resolve(null); - } - }); - - const uploadAndSendFile = async (file) => { - const fileKey = await crypto.randomBytes(32); - const arrayBuffer = await file.arrayBuffer(); - const fileBytes = new Uint8Array(arrayBuffer); - const encrypted = await crypto.encryptData(fileBytes, fileKey); - const encryptedHex = encrypted.content + encrypted.tag; - const encryptedBytes = fromHexString(encryptedHex); - const blob = new Blob([encryptedBytes], { type: 'application/octet-stream' }); - - const uploadUrl = await convex.mutation(api.files.generateUploadUrl, {}); - const uploadRes = await fetch(uploadUrl, { method: 'POST', headers: { 'Content-Type': blob.type }, body: blob }); - const { storageId } = await uploadRes.json(); - - const fileUrl = await convex.mutation(api.files.validateUpload, { storageId }); - - const dims = await getMediaDimensions(file); - - const metadata = { - type: 'attachment', - url: fileUrl, - filename: file.name, - mimeType: file.type, - size: file.size, - key: fileKey, - iv: encrypted.iv, - ...(dims && { width: dims.width, height: dims.height }) - }; - - await sendMessage(JSON.stringify(metadata)); - }; - - // Store ciphertext→plaintext mapping for optimistic display - const optimisticMapRef = useRef(new Map()); - - const sendMessage = async (contentString, replyToId) => { - try { - if (!channelKey) { alert("Cannot send: Missing Encryption Key"); return; } - const { content: encryptedContent, iv, tag } = await crypto.encryptData(contentString, channelKey); - const ciphertext = encryptedContent + tag; - const senderId = localStorage.getItem('userId'); - const signingKey = sessionStorage.getItem('signingKey'); - if (!senderId || !signingKey) return; - - // Optimistic: store ciphertext→plaintext so processMessages can skip decryption - optimisticMapRef.current.set(ciphertext, contentString); - - const args = { - channelId, - senderId, - ciphertext, - nonce: iv, - signature: await crypto.signMessage(signingKey, ciphertext), - keyVersion: 1 - }; - if (replyToId) args.replyTo = replyToId; - - await sendMessageMutation(args); - } catch (err) { - console.error('Send error:', err); - } - }; - - const clearTypingState = () => { - if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current); - if (currentUserId && channelId) { - stopTypingMutation({ channelId, userId: currentUserId }).catch(() => {}); - } - lastTypingEmitRef.current = 0; - }; - - const handleSend = async (e) => { - e.preventDefault(); - let messageContent = ''; - if (inputDivRef.current) { - inputDivRef.current.childNodes.forEach(node => { - if (node.nodeType === Node.TEXT_NODE) messageContent += node.textContent; - else if (node.nodeName === 'IMG' && node.alt) messageContent += node.alt; - else if (node.tagName === 'DIV' || node.tagName === 'BR') messageContent += '\n'; - else messageContent += node.textContent; - }); - messageContent = messageContent.trim(); - } - if (!messageContent && pendingFiles.length === 0) return; - - // Exit focused mode when sending a message - if (focusedModeRef.current) { - setFocusedMode(false); - focusedModeRef.current = false; - setFocusedMessageId(null); - setFocusedMessages([]); - setFocusedHasOlder(false); - setFocusedHasNewer(false); - setFirstItemIndex(INITIAL_FIRST_INDEX); - prevMessageCountRef.current = 0; - prevFirstMsgIdRef.current = null; - } - - // Intercept slash commands - if (messageContent.startsWith('/') && pendingFiles.length === 0) { - const parts = messageContent.slice(1).split(/\s+/); - const cmdName = parts[0]; - const cmdArgs = parts.slice(1).join(' '); - const command = SLASH_COMMANDS.find(c => c.name === cmdName); - if (command) { - executeSlashCommand(command, cmdArgs); - if (inputDivRef.current) inputDivRef.current.innerHTML = ''; - setInput(''); setHasImages(false); - setSlashQuery(null); - clearTypingState(); - userSentMessageRef.current = true; - scrollOnNextDataRef.current = true; - isInitialLoadRef.current = false; - setTimeout(() => scrollToBottom(true), 100); - return; - } - } - - setUploading(true); - userSentMessageRef.current = true; - scrollOnNextDataRef.current = true; - isInitialLoadRef.current = false; - const replyId = replyingTo?.messageId; - try { - for (const file of pendingFiles) await uploadAndSendFile(file); - setPendingFiles([]); - if (messageContent) { - await sendMessage(messageContent, replyId); - if (inputDivRef.current) inputDivRef.current.innerHTML = ''; - setInput(''); setHasImages(false); - clearTypingState(); - } - setReplyingTo(null); - setMentionQuery(null); - markChannelAsRead(); - setTimeout(() => scrollToBottom(true), 100); - } catch (err) { - console.error("Error sending message/files:", err); - alert("Failed to send message/files"); - } finally { - setUploading(false); - if (fileInputRef.current) fileInputRef.current.value = ''; - } - }; - - const handleEditSave = async () => { - if (!editingMessage || !editInput.trim()) { - setEditingMessage(null); - return; - } - try { - const { content: encryptedContent, iv, tag } = await crypto.encryptData(editInput, channelKey); - const ciphertext = encryptedContent + tag; - const signingKey = sessionStorage.getItem('signingKey'); - await editMessageMutation({ - id: editingMessage.id, - ciphertext, - nonce: iv, - signature: await crypto.signMessage(signingKey, ciphertext), - }); - messageDecryptionCache.delete(editingMessage.id); - setEditingMessage(null); - setEditInput(''); - } catch (err) { - console.error('Edit error:', err); - } - }; - - const handleKeyDown = (e) => { - if (slashQuery !== null && filteredSlashCommands.length > 0) { - if (e.key === 'ArrowDown') { e.preventDefault(); setSlashIndex(i => (i + 1) % filteredSlashCommands.length); return; } - if (e.key === 'ArrowUp') { e.preventDefault(); setSlashIndex(i => (i - 1 + filteredSlashCommands.length) % filteredSlashCommands.length); return; } - if (e.key === 'Tab') { e.preventDefault(); handleSlashSelect(filteredSlashCommands[slashIndex]); return; } - if (e.key === 'Escape') { e.preventDefault(); setSlashQuery(null); return; } - } - if (mentionQuery !== null && mentionItems.length > 0) { - if (e.key === 'ArrowDown') { e.preventDefault(); setMentionIndex(i => (i + 1) % mentionItems.length); return; } - if (e.key === 'ArrowUp') { e.preventDefault(); setMentionIndex(i => (i - 1 + mentionItems.length) % mentionItems.length); return; } - if ((e.key === 'Enter' && !e.shiftKey) || e.key === 'Tab') { e.preventDefault(); insertMention(mentionItems[mentionIndex]); return; } - if (e.key === 'Escape') { e.preventDefault(); setMentionQuery(null); return; } - } - if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend(e); } - if (e.key === 'Escape' && replyingTo) { setReplyingTo(null); return; } - if (e.key === 'Backspace' && inputDivRef.current) { - const sel = window.getSelection(); - if (sel.rangeCount > 0 && sel.isCollapsed) { - const range = sel.getRangeAt(0); - if (range.startOffset === 0 && range.startContainer !== inputDivRef.current) { - const prevNode = range.startContainer.previousSibling; - if (prevNode && prevNode.nodeName === 'IMG' && prevNode.classList.contains('inline-emoji')) { e.preventDefault(); prevNode.remove(); setHasImages(inputDivRef.current.querySelectorAll('img').length > 0); return; } - } else if (range.startOffset > 0) { - if (range.startContainer.nodeType === Node.ELEMENT_NODE) { - const nodeBefore = range.startContainer.childNodes[range.startOffset - 1]; - if (nodeBefore && nodeBefore.nodeName === 'IMG' && nodeBefore.classList.contains('inline-emoji')) { e.preventDefault(); nodeBefore.remove(); setHasImages(inputDivRef.current.querySelectorAll('img').length > 0); return; } - } - } - } - } - }; - - const handleEditKeyDown = (e) => { - if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleEditSave(); } - if (e.key === 'Escape') { setEditingMessage(null); setEditInput(''); } - }; - - const handleReactionClick = async (messageId, emoji, hasMyReaction) => { - if (!currentUserId) return; - if (hasMyReaction) { - await removeReaction({ messageId, userId: currentUserId, emoji }); - } else { - await addReaction({ messageId, userId: currentUserId, emoji }); - } - }; - - const togglePicker = (tab) => { - if (pickerTab === tab) { - setPickerTab(null); - } else { - setPickerTab(tab); - } - }; - - const handleContextInteract = (action, messageId) => { - const msg = decryptedMessages.find(m => m.id === messageId); - if (!msg) return; - - switch (action) { - case 'reply': - setReplyingTo({ messageId: msg.id, username: msg.username, content: msg.content?.substring(0, 100) }); - break; - case 'edit': - setEditingMessage({ id: msg.id, content: msg.content }); - setEditInput(msg.content); - break; - case 'pin': - pinMessageMutation({ id: msg.id, pinned: !msg.pinned }); - break; - case 'delete': - deleteMessageMutation({ id: msg.id, userId: currentUserId }); - break; - case 'reaction': - setReactionPickerMsgId(msg.id); - break; - } - setContextMenu(null); - }; - - // Long-press handler factory for mobile (not a hook - called per message in render callback) - const createLongPressHandlers = (callback) => { - let timer = null; - let startX = 0; - let startY = 0; - let triggered = false; - return { - onTouchStart: (e) => { - triggered = false; - startX = e.touches[0].clientX; - startY = e.touches[0].clientY; - timer = setTimeout(() => { - triggered = true; - if (navigator.vibrate) navigator.vibrate(50); - callback(); - }, 500); - }, - onTouchMove: (e) => { - if (!timer) return; - const dx = e.touches[0].clientX - startX; - const dy = e.touches[0].clientY - startY; - if (Math.abs(dx) > 10 || Math.abs(dy) > 10) { - clearTimeout(timer); - timer = null; - } - }, - onTouchEnd: (e) => { - if (timer) { clearTimeout(timer); timer = null; } - if (triggered) { e.preventDefault(); triggered = false; } - }, - }; - }; - - const handleMobileDrawerAction = (action, messageId) => { - const msg = decryptedMessages.find(m => m.id === messageId); - if (!msg) return; - switch (action) { - case 'reply': - setReplyingTo({ messageId: msg.id, username: msg.username, content: msg.content?.substring(0, 100) }); - break; - case 'edit': - setEditingMessage({ id: msg.id, content: msg.content }); - setEditInput(msg.content); - break; - case 'pin': - pinMessageMutation({ id: msg.id, pinned: !msg.pinned }); - break; - case 'delete': - deleteMessageMutation({ id: msg.id, userId: currentUserId }); - break; - case 'reaction': - setReactionPickerMsgId(msg.id); - break; - case 'copy': - if (msg.content) navigator.clipboard.writeText(msg.content).catch(() => {}); - break; - } - }; - - const scrollToMessage = useCallback((messageId) => { - const idx = decryptedMessages.findIndex(m => m.id === messageId); - if (idx !== -1 && virtuosoRef.current) { - virtuosoRef.current.scrollToIndex({ index: idx, align: 'center', behavior: 'smooth' }); - setTimeout(() => { - const el = document.getElementById(`msg-${messageId}`); - if (el) { - el.classList.add('message-highlight'); - setTimeout(() => el.classList.remove('message-highlight'), 2000); - } - }, 300); - } else { - // Message not in current view — enter focused mode - setFocusedMessageId(messageId); - } - }, [decryptedMessages]); - - // Stable callbacks for MessageItem - const handleProfilePopup = useCallback((e, msg) => { - setProfilePopup({ userId: msg.sender_id, username: msg.username, avatarUrl: msg.avatarUrl, position: { x: e.clientX, y: e.clientY } }); - }, []); - - const handleJumpToPresent = useCallback(() => { - setFocusedMode(false); - focusedModeRef.current = false; - setFocusedMessageId(null); - setFocusedMessages([]); - setFocusedHasOlder(false); - setFocusedHasNewer(false); - setFocusedLoading(false); - focusedLoadingMoreRef.current = false; - setDecryptedMessages([]); - decryptionDoneRef.current = false; - isInitialLoadRef.current = true; - initialScrollScheduledRef.current = false; - setFirstItemIndex(INITIAL_FIRST_INDEX); - prevMessageCountRef.current = 0; - prevFirstMsgIdRef.current = null; - userIsScrolledUpRef.current = false; - }, []); - - const isDM = channelType === 'dm'; - const placeholderText = isDM ? `Message @${channelName || 'user'}` : `Message #${channelName || channelId}`; - - // Merge messages + ephemeral for Virtuoso data - const allDisplayMessages = useMemo(() => { - return [...decryptedMessages, ...ephemeralMessages]; - }, [decryptedMessages, ephemeralMessages]); - - // When user sends a message, scroll to bottom once the new message arrives in data - // Virtuoso handles followOutput automatically via the prop - // We don't need manual scrolling here which might conflict - useEffect(() => { - if (scrollOnNextDataRef.current) { - scrollOnNextDataRef.current = false; - scrollLog('[SCROLL:scrollOnNextData] user sent message, forcing scroll to bottom'); - // Reset scrolled-up state since user just sent a message - userIsScrolledUpRef.current = false; - // followOutput already returned 'auto' but it's unreliable — force DOM scroll - requestAnimationFrame(() => { - const el = scrollerElRef.current; - if (el) el.scrollTop = el.scrollHeight; - }); - } - }, [allDisplayMessages]); - - // Header component for Virtuoso — shows skeleton loader or channel beginning - const renderListHeader = useCallback(() => { - return ( - <> - {(status === 'LoadingMore' || (focusedMode && focusedHasOlder)) && ( - <div style={{ display: 'flex', justifyContent: 'center', padding: '8px 0' }}> - <div className="loading-spinner" style={{ width: '20px', height: '20px', borderWidth: '2px' }} /> - </div> - )} - {!focusedMode && status === 'Exhausted' && (decryptedMessages.length > 0 || rawMessages.length === 0) && ( - <div className="channel-beginning"> - <div className="channel-beginning-icon">{isDM ? '@' : '#'}</div> - <h1 className="channel-beginning-title"> - {isDM ? `${channelName}` : `Welcome to #${channelName}`} - </h1> - <p className="channel-beginning-subtitle"> - {isDM - ? `This is the beginning of your direct message history with ${channelName}.` - : `This is the start of the #${channelName} channel.` - } - </p> - </div> - )} - </> - ); - }, [status, decryptedMessages.length, rawMessages.length, isDM, channelName, focusedMode, focusedHasOlder]); - - // Stable Virtuoso components — avoids remounting Header/Footer every render - const virtuosoComponents = useMemo(() => ({ - Header: () => renderListHeader(), - Footer: () => <div style={{ height: '1px' }} />, - }), [renderListHeader]); - - // Render individual message item for Virtuoso - const renderMessageItem = useCallback((item, arrayIndex) => { - // Handle ephemeral messages (they come after decryptedMessages in allDisplayMessages) - if (item.type === 'ephemeral') { - const emsg = item; - return ( - <div className="message-item ephemeral-message"> - <div className="message-reply-context ephemeral-reply-context"> - <div className="reply-spine" /> - <div className="ephemeral-reply-avatar"> - <svg width="16" height="12" viewBox="0 0 28 20"> - <path fill="currentColor" d="M23.0212 1.67671C21.3107 0.879656 19.5079 0.318797 17.6584 0C17.4062 0.461742 17.1749 0.934541 16.9708 1.4184C15.0172 1.11817 13.0907 1.11817 11.1372 1.4184C10.9331 0.934541 10.7018 0.461742 10.4496 0C8.59007 0.318797 6.78726 0.879656 5.0768 1.67671C1.65217 6.84883 0.706797 11.8997 1.166 16.858C3.39578 18.5135 5.56066 19.5165 7.6473 20.1417C8.16912 19.4246 8.63212 18.6713 9.02347 17.8863C8.25707 17.5929 7.52187 17.2415 6.82914 16.8374C7.0147 16.6975 7.19515 16.5518 7.36941 16.402C11.66 18.396 16.4523 18.396 20.7186 16.402C20.8953 16.5518 21.0758 16.6975 21.2613 16.8374C20.5663 17.2435 19.8288 17.5947 19.0624 17.8863C19.4537 18.6713 19.9167 19.4246 20.4385 20.1417C22.5276 19.5165 24.6925 18.5135 26.9223 16.858C27.4684 11.2365 25.9961 6.22055 23.0212 1.67671ZM9.68041 13.6383C8.39754 13.6383 7.34085 12.4456 7.34085 10.994C7.34085 9.5424 8.37555 8.34973 9.68041 8.34973C10.9893 8.34973 12.0395 9.5424 12.0175 10.994C12.0175 12.4456 10.9893 13.6383 9.68041 13.6383ZM18.3161 13.6383C17.0332 13.6383 15.9765 12.4456 15.9765 10.994C15.9765 9.5424 17.0112 8.34973 18.3161 8.34973C19.625 8.34973 20.6752 9.5424 20.6532 10.994C20.6532 12.4456 19.625 13.6383 18.3161 13.6383Z"/> - </svg> - </div> - <span className="reply-author" style={{ color: '#5865f2' }}>System</span> - <span className="reply-text">{emsg.username} used {emsg.command}</span> - </div> - <div className="message-avatar-wrapper"> - <div className="ephemeral-avatar"> - <svg width="28" height="20" viewBox="0 0 28 20"> - <path fill="currentColor" d="M23.0212 1.67671C21.3107 0.879656 19.5079 0.318797 17.6584 0C17.4062 0.461742 17.1749 0.934541 16.9708 1.4184C15.0172 1.11817 13.0907 1.11817 11.1372 1.4184C10.9331 0.934541 10.7018 0.461742 10.4496 0C8.59007 0.318797 6.78726 0.879656 5.0768 1.67671C1.65217 6.84883 0.706797 11.8997 1.166 16.858C3.39578 18.5135 5.56066 19.5165 7.6473 20.1417C8.16912 19.4246 8.63212 18.6713 9.02347 17.8863C8.25707 17.5929 7.52187 17.2415 6.82914 16.8374C7.0147 16.6975 7.19515 16.5518 7.36941 16.402C11.66 18.396 16.4523 18.396 20.7186 16.402C20.8953 16.5518 21.0758 16.6975 21.2613 16.8374C20.5663 17.2435 19.8288 17.5947 19.0624 17.8863C19.4537 18.6713 19.9167 19.4246 20.4385 20.1417C22.5276 19.5165 24.6925 18.5135 26.9223 16.858C27.4684 11.2365 25.9961 6.22055 23.0212 1.67671ZM9.68041 13.6383C8.39754 13.6383 7.34085 12.4456 7.34085 10.994C7.34085 9.5424 8.37555 8.34973 9.68041 8.34973C10.9893 8.34973 12.0395 9.5424 12.0175 10.994C12.0175 12.4456 10.9893 13.6383 9.68041 13.6383ZM18.3161 13.6383C17.0332 13.6383 15.9765 12.4456 15.9765 10.994C15.9765 9.5424 17.0112 8.34973 18.3161 8.34973C19.625 8.34973 20.6752 9.5424 20.6532 10.994C20.6532 12.4456 19.625 13.6383 18.3161 13.6383Z"/> - </svg> - </div> - </div> - <div className="message-body"> - <div className="message-header"> - <span className="username" style={{ color: '#5865f2' }}>System</span> - <span className="ephemeral-bot-badge">BOT</span> - <span className="timestamp">{new Date(emsg.created_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</span> - </div> - <div className="message-content"> - <span>{emsg.content}</span> - </div> - <div className="ephemeral-message-footer"> - <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" style={{ opacity: 0.6 }}> - <path d="M8 3C4.5 3 1.6 5.1.3 8c1.3 2.9 4.2 5 7.7 5s6.4-2.1 7.7-5c-1.3-2.9-4.2-5-7.7-5zm0 8.3c-1.8 0-3.3-1.5-3.3-3.3S6.2 4.7 8 4.7s3.3 1.5 3.3 3.3S9.8 11.3 8 11.3zM8 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/> - </svg> - <span className="ephemeral-message-footer-text">Only you can see this</span> - <span className="ephemeral-message-footer-sep">·</span> - <span - className="ephemeral-message-dismiss" - onClick={() => setEphemeralMessages(prev => prev.filter(m => m.id !== emsg.id))} - > - Dismiss message - </span> - </div> - </div> - </div> - ); - } - - // Regular message - const msg = item; - const idx = arrayIndex; - const currentDate = new Date(msg.created_at); - const previousDate = idx > 0 ? new Date(decryptedMessages[idx - 1]?.created_at) : null; - const isMentioned = isMentionedInContent(msg.content); - const isOwner = msg.username === username; - const canDelete = isOwner || !!myPermissions?.manage_messages; - - const prevMsg = idx > 0 ? decryptedMessages[idx - 1] : null; - const isGrouped = prevMsg - && prevMsg.username === msg.username - && !isNewDay(currentDate, previousDate) - && (currentDate - new Date(prevMsg.created_at)) < 60000 - && !msg.replyToId; - - const showDateDivider = isNewDay(currentDate, previousDate); - const dateLabel = showDateDivider ? currentDate.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' }) : ''; - - const showUnreadDivider = unreadDividerTimestamp != null - && msg.created_at > unreadDividerTimestamp - && (idx === 0 || decryptedMessages[idx - 1]?.created_at <= unreadDividerTimestamp); - - return ( - <MessageItem - msg={msg} - isGrouped={isGrouped} - showDateDivider={showDateDivider} - showUnreadDivider={showUnreadDivider} - dateLabel={dateLabel} - isMentioned={isMentioned} - isOwner={isOwner} - roles={roles} - customEmojis={customEmojis} - isEditing={editingMessage?.id === msg.id} - isHovered={hoveredMessageId === msg.id} - editInput={editInput} - username={username} - onHover={isMobile ? undefined : () => setHoveredMessageId(msg.id)} - onLeave={isMobile ? undefined : () => setHoveredMessageId(null)} - onContextMenu={isMobile ? undefined : (e) => { e.preventDefault(); window.dispatchEvent(new Event('close-context-menus')); setContextMenu({ x: e.clientX, y: e.clientY, messageId: msg.id, isOwner, isAttachment: !!parseAttachment(msg.content), canDelete }); }} - onAddReaction={(emoji) => { if (emoji) { addReaction({ messageId: msg.id, userId: currentUserId, emoji }); } else { setReactionPickerMsgId(reactionPickerMsgId === msg.id ? null : msg.id); } }} - onEdit={() => { setEditingMessage({ id: msg.id, content: msg.content }); setEditInput(msg.content); }} - onReply={() => setReplyingTo({ messageId: msg.id, username: msg.username, content: msg.content?.substring(0, 100) })} - onMore={(e) => { const rect = e.target.getBoundingClientRect(); setContextMenu({ x: rect.left, y: rect.bottom, messageId: msg.id, isOwner, isAttachment: !!parseAttachment(msg.content), canDelete }); }} - onLongPress={isMobile ? createLongPressHandlers(() => setMobileDrawer({ messageId: msg.id, isOwner, isAttachment: !!parseAttachment(msg.content), canDelete, message: msg })) : undefined} - onEditInputChange={(e) => setEditInput(e.target.value)} - onEditKeyDown={handleEditKeyDown} - onEditSave={handleEditSave} - onEditCancel={() => { setEditingMessage(null); setEditInput(''); }} - onReactionClick={handleReactionClick} - onScrollToMessage={scrollToMessage} - onProfilePopup={handleProfilePopup} - onImageClick={setZoomedImage} - scrollToBottom={scrollToBottom} - Attachment={Attachment} - LinkPreview={LinkPreview} - DirectVideo={DirectVideo} - /> - ); - }, [decryptedMessages, username, myPermissions, isMentionedInContent, unreadDividerTimestamp, editingMessage, hoveredMessageId, editInput, roles, customEmojis, reactionPickerMsgId, currentUserId, isMobile, addReaction, handleEditKeyDown, handleEditSave, handleReactionClick, scrollToMessage, handleProfilePopup, scrollToBottom]); - - return ( - <div className="chat-area" onDragOver={handleDragOver} onDragLeave={handleDragLeave} onDrop={handleDrop} style={{ position: 'relative' }}> - {isDragging && <DragOverlay />} - - <PinnedMessagesPanel - channelId={channelId} - visible={showPinned} - onClose={onTogglePinned} - channelKey={channelKey} - onJumpToMessage={scrollToMessage} - userId={currentUserId} - username={username} - roles={roles} - Attachment={Attachment} - LinkPreview={LinkPreview} - DirectVideo={DirectVideo} - onReactionClick={handleReactionClick} - onProfilePopup={handleProfilePopup} - onImageClick={setZoomedImage} - /> - - <div className="messages-list"> - {((!focusedMode && status === 'LoadingFirstPage') || focusedLoading) ? ( - <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', flex: 1, padding: '40px 0' }}> - <div className="loading-spinner" /> - </div> - ) : ( - <Virtuoso - ref={virtuosoRef} - scrollerRef={(el) => { scrollerElRef.current = el; }} - firstItemIndex={firstItemIndex} - {...(!focusedMode ? { initialTopMostItemIndex: { index: 'LAST', align: 'end' } } : {})} - alignToBottom={!focusedMode} - atBottomThreshold={20} - data={allDisplayMessages} - startReached={handleStartReached} - endReached={focusedMode ? handleEndReached : undefined} - followOutput={focusedMode ? false : followOutput} - atBottomStateChange={focusedMode ? undefined : handleAtBottomStateChange} - increaseViewportBy={{ top: 400, bottom: 400 }} - defaultItemHeight={60} - computeItemKey={(index, item) => item.id || `idx-${index}`} - components={virtuosoComponents} - itemContent={(index, item) => renderMessageItem(item, index - firstItemIndex)} - /> - )} - {focusedMode && !focusedLoading && ( - <button className="jump-to-present-btn" onClick={handleJumpToPresent}> - <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" style={{ marginRight: '6px' }}> - <path d="M8 12l-4.5-4.5 1.06-1.06L8 9.88l3.44-3.44 1.06 1.06z"/> - </svg> - Jump to Present - </button> - )} - </div> - {contextMenu && <MessageContextMenu x={contextMenu.x} y={contextMenu.y} isOwner={contextMenu.isOwner} isAttachment={contextMenu.isAttachment} canDelete={contextMenu.canDelete} onClose={() => setContextMenu(null)} onInteract={(action) => handleContextInteract(action, contextMenu.messageId)} />} - {mobileDrawer && ( - <MobileMessageDrawer - message={mobileDrawer.message} - isOwner={mobileDrawer.isOwner} - isAttachment={mobileDrawer.isAttachment} - canDelete={mobileDrawer.canDelete} - onClose={() => setMobileDrawer(null)} - onAction={(action) => handleMobileDrawerAction(action, mobileDrawer.messageId)} - onQuickReaction={(emoji) => { addReaction({ messageId: mobileDrawer.messageId, userId: currentUserId, emoji }); }} - /> - )} - {reactionPickerMsgId && ( - <div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, zIndex: 999 }} onClick={() => setReactionPickerMsgId(null)}> - <div style={{ position: 'absolute', right: '80px', top: '50%', transform: 'translateY(-50%)' }} onClick={(e) => e.stopPropagation()}> - <GifPicker - initialTab="Emoji" - onSelect={(data) => { - if (typeof data !== 'string' && data.name) { - addReaction({ messageId: reactionPickerMsgId, userId: currentUserId, emoji: data.name }); - } - setReactionPickerMsgId(null); - }} - onClose={() => setReactionPickerMsgId(null)} - /> - </div> - </div> - )} - {inputContextMenu && <InputContextMenu x={inputContextMenu.x} y={inputContextMenu.y} onClose={() => setInputContextMenu(null)} onPaste={async () => { - try { - if (inputDivRef.current) inputDivRef.current.focus(); - // Try reading clipboard items for images first - if (navigator.clipboard.read) { - try { - const items = await navigator.clipboard.read(); - for (const item of items) { - const imageType = item.types.find(t => t.startsWith('image/')); - if (imageType) { - const blob = await item.getType(imageType); - const file = new File([blob], `pasted-image.${imageType.split('/')[1] || 'png'}`, { type: imageType }); - processFile(file); - return; - } - } - } catch {} - } - // Fall back to plain text - const text = await navigator.clipboard.readText(); - if (text) { - document.execCommand('insertText', false, text); - // Sync state — onInput may not fire from async execCommand - const el = inputDivRef.current; - if (el) { - setInput(el.textContent); - const inner = el.innerText; - setIsMultiline(inner.includes('\n') || el.scrollHeight > 50); - } - } - } catch {} - }} />} - - <form ref={chatInputFormRef} className="chat-input-form" onSubmit={handleSend} style={{ position: 'relative' }}> - {slashQuery !== null && filteredSlashCommands.length > 0 && ( - <SlashCommandMenu - commands={filteredSlashCommands} - selectedIndex={slashIndex} - onSelect={handleSlashSelect} - onHover={setSlashIndex} - /> - )} - {mentionQuery !== null && mentionItems.length > 0 && ( - <MentionMenu - items={mentionItems} - selectedIndex={mentionIndex} - onSelect={insertMention} - onHover={setMentionIndex} - /> - )} - {typingUsers.length > 0 && ( - <div style={{ position: 'absolute', top: '-24px', left: '0', padding: '0 8px', display: 'flex', alignItems: 'center', gap: '6px', color: '#dbdee1', fontSize: '12px', fontWeight: 'bold', pointerEvents: 'none' }}> - <ColoredIcon src={TypingIcon} size="24px" color="#dbdee1" /> - <span>{typingUsers.map(t => t.displayName || t.username).join(', ')} is typing...</span> - </div> - )} - - {replyingTo && ( - <div className="reply-preview-bar"> - <div className="reply-preview-content"> - Replying to <strong>{replyingTo.username}</strong> - <span className="reply-preview-text">{replyingTo.content}</span> - </div> - <button className="reply-preview-close" onClick={() => setReplyingTo(null)}>×</button> - </div> - )} - - {pendingFiles.length > 0 && ( - <div style={{ display: 'flex', padding: '10px 16px 0', overflowX: 'auto', backgroundColor: 'var(--channeltextarea-background)', borderRadius: '8px 8px 0 0' }}> - {pendingFiles.map((file, idx) => <PendingFilePreview key={idx} file={file} onRemove={(f) => setPendingFiles(prev => prev.filter(item => item !== f))} />)} - </div> - )} - <div className="chat-input-wrapper" style={pendingFiles.length > 0 ? { borderTopLeftRadius: 0, borderTopRightRadius: 0 } : {}}> - <input type="file" ref={fileInputRef} style={{ display: 'none' }} onChange={handleFileSelect} multiple /> - <button type="button" className="chat-input-file-btn" onClick={() => fileInputRef.current.click()} disabled={uploading}> - {uploading ? <div className="spinner" style={{ width: 24, height: 24, borderRadius: '50%', border: '2px solid #b9bbbe', borderTopColor: 'transparent', animation: 'spin 1s linear infinite' }}></div> : <ColoredIcon src={AddIcon} color={ICON_COLOR_DEFAULT} size="24px" />} - </button> - <div ref={inputDivRef} contentEditable className="chat-input-richtext" role="textbox" aria-multiline="true" - onDrop={(e) => e.preventDefault()} - onBlur={saveSelection} - onMouseUp={saveSelection} - onKeyUp={saveSelection} - onContextMenu={(e) => { - e.preventDefault(); - window.dispatchEvent(new Event('close-context-menus')); - setInputContextMenu({ x: e.clientX, y: e.clientY }); - }} - onPaste={(e) => { - const items = e.clipboardData?.items; - if (items) { - for (const item of items) { - if (item.type.startsWith('image/')) { - e.preventDefault(); - const file = item.getAsFile(); - if (file) processFile(file); - return; - } - } - } - e.preventDefault(); - const text = e.clipboardData.getData('text/plain'); - document.execCommand('insertText', false, text); - }} - onInput={(e) => { - const textContent = e.currentTarget.textContent; - setInput(textContent); - setHasImages(e.currentTarget.querySelectorAll('img').length > 0); - - // Clean up browser artifacts (residual <br>) when content is fully erased - if (!textContent && !e.currentTarget.querySelectorAll('img').length) { - e.currentTarget.innerHTML = ''; - setIsMultiline(false); - } else { - const text = e.currentTarget.innerText; - setIsMultiline(text.includes('\n') || e.currentTarget.scrollHeight > 50); - } - checkTypedEmoji(); - checkMentionTrigger(); - checkSlashTrigger(); - const now = Date.now(); - if (now - lastTypingEmitRef.current > 2000 && currentUserId && channelId) { - startTypingMutation({ channelId, userId: currentUserId, username }).catch(() => {}); - lastTypingEmitRef.current = now; - } - if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current); - typingTimeoutRef.current = setTimeout(() => { - if (currentUserId && channelId) stopTypingMutation({ channelId, userId: currentUserId }).catch(() => {}); - lastTypingEmitRef.current = 0; - }, 3000); - }} - onKeyDown={handleKeyDown} - style={{ flex: 1, backgroundColor: 'transparent', border: 'none', color: 'var(--text-normal)', fontSize: '16px', marginTop: '20px', marginLeft: '6px', minHeight: '44px', maxHeight: '200px', overflowY: 'auto', outline: 'none', whiteSpace: 'pre-wrap', wordBreak: 'break-word', lineHeight: '1.375rem', marginBottom: isMultiline ? '20px' : '0px' }} - /> - {!input && !hasImages && <div style={{ position: 'absolute', left: '70px', color: 'var(--text-muted)', pointerEvents: 'none', userSelect: 'none' }}>{placeholderText}</div>} - <div className="chat-input-icons" style={{ position: 'relative' }}> - <Tooltip text="GIF" position="top"> - <button type="button" className="chat-input-icon-btn" onClick={(e) => { e.stopPropagation(); togglePicker('GIFs'); }}> - <ColoredIcon src={GifIcon} color={pickerTab === 'GIFs' ? '#5865f2' : ICON_COLOR_DEFAULT} size="24px" /> - </button> - </Tooltip> - <Tooltip text="Stickers" position="top"> - <button type="button" className="chat-input-icon-btn" onClick={(e) => { e.stopPropagation(); togglePicker('Stickers'); }}> - <ColoredIcon src={StickerIcon} color={pickerTab === 'Stickers' ? '#5865f2' : ICON_COLOR_DEFAULT} size="24px" /> - </button> - </Tooltip> - {showGifPicker && ( - <GifPicker onSelect={(data) => { if (typeof data === 'string') { sendMessage(data); setPickerTab(null); } else { insertEmoji(data); setPickerTab(null); } }} onClose={() => setPickerTab(null)} currentTab={pickerTab} onTabChange={setPickerTab} /> - )} - <EmojiButton active={pickerTab === 'Emoji'} onClick={() => togglePicker('Emoji')} /> - </div> - </div> - </form> - {zoomedImage && ReactDOM.createPortal( - <div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0, 0, 0, 0.85)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'zoom-out' }} onClick={() => setZoomedImage(null)}> - <img src={zoomedImage} alt="Zoomed" style={{ maxWidth: '90%', maxHeight: '90%', boxShadow: '0 8px 16px rgba(0,0,0,0.5)', borderRadius: '4px', cursor: 'default' }} onClick={(e) => e.stopPropagation()} /> - </div>, - document.body - )} - {profilePopup && ( - <UserProfilePopup - userId={profilePopup.userId} - username={profilePopup.username} - avatarUrl={profilePopup.avatarUrl} - status="online" - position={profilePopup.position} - onClose={() => setProfilePopup(null)} - onSendMessage={onOpenDM} - /> - )} - </div> - ); -}; - -export default ChatArea; diff --git a/packages/shared/src/components/ChatHeader.jsx b/packages/shared/src/components/ChatHeader.jsx deleted file mode 100644 index 0a0d5c3..0000000 --- a/packages/shared/src/components/ChatHeader.jsx +++ /dev/null @@ -1,175 +0,0 @@ -import React, { useMemo } from 'react'; -import { useQuery } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; -import { useOnlineUsers } from '../contexts/PresenceContext'; -import Tooltip from './Tooltip'; - -const ChatHeader = ({ - channelName, - channelType, - channelTopic, - channelId, - onToggleMembers, - showMembers, - onTogglePinned, - serverName, - isMobile, - onMobileBack, - onStartCall, - isDMCallActive, - onOpenMembersScreen, - // Search props - searchQuery, - onSearchQueryChange, - onSearchSubmit, - onSearchFocus, - onSearchBlur, - searchInputRef, - searchActive, -}) => { - const isDM = channelType === 'dm'; - const searchPlaceholder = isDM ? 'Search' : `Search ${serverName || 'Server'}`; - - // Query members on mobile text channels only for online count - const shouldQueryMembers = isMobile && !isDM && channelId; - const members = useQuery( - api.members.getChannelMembers, - shouldQueryMembers ? { channelId } : "skip" - ) || []; - const { resolveStatus } = useOnlineUsers(); - const onlineCount = useMemo(() => { - if (!shouldQueryMembers) return 0; - return members.filter(m => resolveStatus(m.status, m.id) !== 'offline').length; - }, [members, resolveStatus, shouldQueryMembers]); - - const handleSearchKeyDown = (e) => { - if (e.key === 'Enter') { - e.preventDefault(); - onSearchSubmit?.(); - } - if (e.key === 'Escape') { - e.preventDefault(); - onSearchBlur?.(); - e.target.blur(); - } - }; - - return ( - <div className="chat-header"> - <div className="chat-header-left"> - {isMobile && onMobileBack && ( - <button className="mobile-back-btn" onClick={onMobileBack}> - <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg> - </button> - )} - {isMobile && !isDM ? ( - <button className="mobile-channel-header-tap" onClick={onOpenMembersScreen}> - <div className="mobile-channel-header-top"> - <span className="chat-header-icon">#</span> - <span className="chat-header-name">{channelName}</span> - <svg className="mobile-channel-chevron" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M9.29 6.71a1 1 0 0 0 0 1.41L13.17 12l-3.88 3.88a1 1 0 1 0 1.42 1.41l4.59-4.59a1 1 0 0 0 0-1.41L10.71 6.7a1 1 0 0 0-1.42 0Z"/> - </svg> - </div> - <div className="mobile-channel-header-bottom"> - <span className="mobile-online-dot" /> - <span className="mobile-online-count">{onlineCount ?? 0} Online</span> - </div> - </button> - ) : ( - <> - <span className="chat-header-icon">{isDM ? '@' : '#'}</span> - <span className="chat-header-name">{channelName}</span> - {channelTopic && !isDM && !isMobile && ( - <> - <div className="chat-header-divider" /> - <span className="chat-header-topic" title={channelTopic}>{channelTopic}</span> - </> - )} - {isDM && <span className="chat-header-status-text"></span>} - </> - )} - </div> - <div className="chat-header-right"> - {!isDM && !isMobile && ( - <Tooltip text="Threads" position="bottom"> - <button className="chat-header-btn"> - <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"> - <path d="M5.43309 21C5.35842 21 5.30189 20.9325 5.31494 20.859L5.99991 17H2.14274C2.06819 17 2.01168 16.9327 2.02453 16.8593L2.33253 15.0993C2.34258 15.0419 2.39244 15 2.45074 15H6.34991L7.14991 10.5H3.29274C3.21819 10.5 3.16168 10.4327 3.17453 10.3593L3.48253 8.59926C3.49258 8.54185 3.54244 8.5 3.60074 8.5H7.49991L8.25674 4.49395C8.26688 4.43665 8.31672 4.395 8.37491 4.395H10.1919C10.2666 4.395 10.3231 4.4625 10.3101 4.536L9.59991 8.5H14.0999L14.8568 4.49395C14.8669 4.43665 14.9167 4.395 14.9749 4.395H16.7919C16.8666 4.395 16.9231 4.4625 16.9101 4.536L16.1999 8.5H20.0571C20.1316 8.5 20.1881 8.56734 20.1753 8.64074L19.8673 10.4007C19.8572 10.4581 19.8074 10.5 19.7491 10.5H15.8499L15.0499 15H18.9071C18.9816 15 19.0381 15.0673 19.0253 15.1407L18.7173 16.9007C18.7072 16.9581 18.6574 17 18.5991 17H14.6999L13.9431 21.006C13.9329 21.0634 13.8831 21.105 13.8249 21.105H12.0079C11.9332 21.105 11.8767 21.0375 11.8897 20.964L12.5999 17H8.09991L7.34309 21.006C7.33295 21.0634 7.28311 21.105 7.22491 21.105H5.43309V21ZM8.44991 15H12.9499L13.7499 10.5H9.24991L8.44991 15Z" /> - </svg> - </button> - </Tooltip> - )} - {isDM && !isMobile && ( - <Tooltip text={isDMCallActive ? "Join Call" : "Start Call"} position="bottom"> - <button - className={`chat-header-btn ${isDMCallActive ? 'active' : ''}`} - onClick={onStartCall} - style={isDMCallActive ? { color: '#3ba55c' } : undefined} - > - <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"> - <path d="M6.62 10.79a15.053 15.053 0 006.59 6.59l2.2-2.2a1.003 1.003 0 011.01-.24c1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1C10.07 21 3 13.93 3 4c0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.1.31.03.66-.25 1.02l-2.2 2.2z"/> - </svg> - </button> - </Tooltip> - )} - <Tooltip text="Pinned Messages" position="bottom"> - <button className="chat-header-btn" onClick={onTogglePinned}> - <svg width="24" height="24" viewBox="0 0 24 24" fill="none"> - <path fill="currentColor" d="M19.38 11.38a3 3 0 0 0 4.24 0l.03-.03a.5.5 0 0 0 0-.7L13.35.35a.5.5 0 0 0-.7 0l-.03.03a3 3 0 0 0 0 4.24L13 5l-2.92 2.92-3.65-.34a2 2 0 0 0-1.6.58l-.62.63a1 1 0 0 0 0 1.42l9.58 9.58a1 1 0 0 0 1.42 0l.63-.63a2 2 0 0 0 .58-1.6l-.34-3.64L19 11zM9.07 17.07a.5.5 0 0 1-.08.77l-5.15 3.43a.5.5 0 0 1-.63-.06l-.42-.42a.5.5 0 0 1-.06-.63L6.16 15a.5.5 0 0 1 .77-.08l2.14 2.14Z"/> - </svg> - </button> - </Tooltip> - {!isDM && !isMobile && ( - <Tooltip text={showMembers ? "Hide Members" : "Show Members"} position="bottom"> - <button - className={`chat-header-btn ${showMembers ? 'active' : ''}`} - onClick={onToggleMembers} - > - <svg width="24" height="24" viewBox="0 0 24 24" fill="none"> - <path fill="currentColor" d="M14.5 8a3 3 0 1 0-2.7-4.3c-.2.4.06.86.44 1.12a5 5 0 0 1 2.14 3.08c.01.06.06.1.12.1ZM18.44 17.27c.15.43.54.73 1 .73h1.06c.83 0 1.5-.67 1.5-1.5a7.5 7.5 0 0 0-6.5-7.43c-.55-.08-.99.38-1.1.92-.06.3-.15.6-.26.87-.23.58-.05 1.3.47 1.63a9.53 9.53 0 0 1 3.83 4.78ZM12.5 9a3 3 0 1 1-6 0 3 3 0 0 1 6 0ZM2 20.5a7.5 7.5 0 0 1 15 0c0 .83-.67 1.5-1.5 1.5a.2.2 0 0 1-.2-.16c-.2-.96-.56-1.87-.88-2.54-.1-.23-.42-.15-.42.1v2.1a.5.5 0 0 1-.5.5h-8a.5.5 0 0 1-.5-.5v-2.1c0-.25-.31-.33-.42-.1-.32.67-.67 1.58-.88 2.54a.2.2 0 0 1-.2.16A1.5 1.5 0 0 1 2 20.5Z"/> - </svg> - </button> - </Tooltip> - )} - {!isMobile && ( - <Tooltip text="Notification Settings" position="bottom"> - <button className="chat-header-btn"> - <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"> - <path d="M18 9V14C18 15.657 19.344 17 21 17V18H3V17C4.656 17 6 15.657 6 14V9C6 5.686 8.686 3 12 3C15.314 3 18 5.686 18 9ZM11.9999 22C10.5239 22 9.24993 20.955 8.99993 19.5H14.9999C14.7499 20.955 13.4759 22 11.9999 22Z" /> - </svg> - </button> - </Tooltip> - )} - {!isMobile && ( - <div className="chat-header-search-wrapper" ref={searchInputRef}> - <svg className="chat-header-search-icon" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M21.71 20.29L18 16.61A9 9 0 1016.61 18l3.68 3.68a1 1 0 001.42 0 1 1 0 000-1.39zM11 18a7 7 0 110-14 7 7 0 010 14z"/> - </svg> - <input - type="text" - placeholder={searchPlaceholder} - className={`chat-header-search ${searchActive ? 'focused' : ''}`} - value={searchQuery || ''} - onChange={(e) => onSearchQueryChange?.(e.target.value)} - onFocus={onSearchFocus} - onKeyDown={handleSearchKeyDown} - /> - {searchQuery && ( - <button - className="chat-header-search-clear" - onClick={() => onSearchQueryChange?.('')} - > - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z"/> - </svg> - </button> - )} - </div> - )} - </div> - </div> - ); -}; - -export default ChatHeader; diff --git a/packages/shared/src/components/ColoredIcon.jsx b/packages/shared/src/components/ColoredIcon.jsx deleted file mode 100644 index 6487e07..0000000 --- a/packages/shared/src/components/ColoredIcon.jsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; - -const ColoredIcon = React.memo(({ src, color, size = '20px', style = {} }) => { - if (!color) { - return ( - <div style={{ width: size, height: size, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, ...style }}> - <img src={src} alt="" style={{ width: size, height: size, objectFit: 'contain' }} /> - </div> - ); - } - return ( - <div style={{ - width: size, height: size, flexShrink: 0, - overflow: 'hidden', - ...style, - }}> - <img - src={src} - alt="" - style={{ - width: size, height: size, - objectFit: 'contain', - filter: `drop-shadow(${size} 0 0 ${color})`, - transform: `translateX(-${size})`, - }} - /> - </div> - ); -}); - -export default ColoredIcon; diff --git a/packages/shared/src/components/DMList.jsx b/packages/shared/src/components/DMList.jsx deleted file mode 100644 index e1130e2..0000000 --- a/packages/shared/src/components/DMList.jsx +++ /dev/null @@ -1,245 +0,0 @@ -import React, { useState, useEffect, useRef } from 'react'; -import { useConvex } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; -import Tooltip from './Tooltip'; -import Avatar from './Avatar'; -import ColoredIcon from './ColoredIcon'; -import { useOnlineUsers } from '../contexts/PresenceContext'; -import friendsIcon from '../assets/icons/friends.svg'; - -const STATUS_COLORS = { - online: '#3ba55c', - idle: '#faa61a', - dnd: '#ed4245', - invisible: '#747f8d', - offline: '#747f8d', -}; - -const STATUS_LABELS = { - online: 'Online', - idle: 'Idle', - dnd: 'Do Not Disturb', - invisible: 'Offline', - offline: 'Offline', -}; - -const getUserColor = (username) => { - if (!username) return '#5865F2'; - const colors = ['#5865F2', '#EBA7CD', '#57F287', '#FEE75C', '#EB459E', '#ED4245']; - let hash = 0; - for (let i = 0; i < username.length; i++) { - hash = username.charCodeAt(i) + ((hash << 5) - hash); - } - return colors[Math.abs(hash) % colors.length]; -}; - -const DMList = ({ dmChannels, activeDMChannel, onSelectDM, onOpenDM, voiceStates }) => { - const [showUserPicker, setShowUserPicker] = useState(false); - const [allUsers, setAllUsers] = useState([]); - const [searchQuery, setSearchQuery] = useState(''); - const [searchFocused, setSearchFocused] = useState(false); - const searchRef = useRef(null); - const searchInputRef = useRef(null); - const { resolveStatus } = useOnlineUsers(); - - const convex = useConvex(); - - const handleOpenUserPicker = async () => { - setShowUserPicker(true); - setSearchQuery(''); - try { - const data = await convex.query(api.auth.getPublicKeys, {}); - const myId = localStorage.getItem('userId'); - setAllUsers(data.filter(u => u.id !== myId)); - } catch (err) { - console.error(err); - } - }; - - const handleSearchFocus = async () => { - setSearchFocused(true); - if (allUsers.length === 0) { - try { - const data = await convex.query(api.auth.getPublicKeys, {}); - const myId = localStorage.getItem('userId'); - setAllUsers(data.filter(u => u.id !== myId)); - } catch (err) { - console.error(err); - } - } - }; - - useEffect(() => { - if (showUserPicker && searchRef.current) { - searchRef.current.focus(); - } - }, [showUserPicker]); - - const filteredUsers = allUsers.filter(u => - u.username?.toLowerCase().includes(searchQuery.toLowerCase()) - ); - - const handleCloseDM = (e, dm) => { - e.stopPropagation(); - // If closing the active DM, switch back to friends - if (activeDMChannel?.channel_id === dm.channel_id) { - onSelectDM('friends'); - } - }; - - return ( - <div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}> - - {/* Search Input */} - <div className="dm-search-wrapper"> - <input - ref={searchInputRef} - type="text" - className="dm-search-input" - placeholder="Find or start a conversation" - value={searchQuery} - onChange={(e) => setSearchQuery(e.target.value)} - onFocus={handleSearchFocus} - onBlur={() => { - setTimeout(() => setSearchFocused(false), 200); - }} - /> - {searchFocused && searchQuery && filteredUsers.length > 0 && ( - <div className="dm-search-dropdown"> - {filteredUsers.slice(0, 8).map(user => ( - <div - key={user.id} - className="dm-search-result" - onMouseDown={(e) => { - e.preventDefault(); - setSearchQuery(''); - setSearchFocused(false); - onOpenDM(user.id, user.username); - }} - > - <Avatar username={user.username} size={24} style={{ marginRight: '8px' }} /> - <span>{user.username}</span> - </div> - ))} - </div> - )} - </div> - - {/* User Picker Modal */} - {showUserPicker && ( - <div style={{ - position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, - backgroundColor: 'rgba(0,0,0,0.7)', zIndex: 100, - display: 'flex', alignItems: 'center', justifyContent: 'center' - }} - onClick={() => setShowUserPicker(false)} - > - <div - style={{ - backgroundColor: 'var(--bg-primary)', borderRadius: '8px', padding: '16px', - width: '400px', maxHeight: '500px', display: 'flex', flexDirection: 'column' - }} - onClick={e => e.stopPropagation()} - > - <h3 style={{ color: 'var(--header-primary)', margin: '0 0 4px 0', fontSize: '16px' }}>Select a User</h3> - <p style={{ color: 'var(--header-secondary)', fontSize: '12px', margin: '0 0 12px 0' }}>Start a new direct message conversation.</p> - <input - ref={searchRef} - type="text" - placeholder="Type a username..." - value={searchQuery} - onChange={e => setSearchQuery(e.target.value)} - style={{ - width: '100%', backgroundColor: 'var(--bg-tertiary)', border: '1px solid var(--border-subtle)', - borderRadius: '4px', color: 'var(--text-normal)', padding: '8px 12px', fontSize: '14px', - outline: 'none', marginBottom: '8px', boxSizing: 'border-box' - }} - /> - <div style={{ flex: 1, overflowY: 'auto', maxHeight: '300px' }}> - {filteredUsers.map(user => ( - <div - key={user.id} - className="dm-picker-user" - onClick={() => { setShowUserPicker(false); onOpenDM(user.id, user.username); }} - > - <Avatar username={user.username} size={32} style={{ marginRight: '12px' }} /> - <span style={{ fontWeight: '500' }}>{user.username}</span> - </div> - ))} - {filteredUsers.length === 0 && ( - <div style={{ color: 'var(--text-muted)', textAlign: 'center', padding: '16px', fontSize: '13px' }}> - No users found. - </div> - )} - </div> - </div> - </div> - )} - - {/* Friends Button */} - <div - className={`dm-friends-btn ${!activeDMChannel ? 'active' : ''}`} - onClick={() => onSelectDM('friends')} - > - <div style={{ marginRight: '12px' }}> - <ColoredIcon src={friendsIcon} color="var(--interactive-normal)" size="24px" /> - </div> - <span style={{ fontWeight: 500 }}>Friends</span> - </div> - - {/* DM List Header */} - <div style={{ fontFamily: 'gg sans', display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '12px 16px 8px', color: '#96989d', fontSize: '11px', fontWeight: 'bold', borderTop: 'solid 1px var(--app-frame-border)'}}> - <span>Direct Messages</span> - </div> - - {/* DM Channel List */} - <div style={{ flex: 1, overflowY: 'auto', padding: '0 8px 8px' }}> - {(dmChannels || []).map(dm => { - const isActive = activeDMChannel?.channel_id === dm.channel_id; - const effectiveStatus = resolveStatus(dm.other_user_status, dm.other_user_id); - return ( - <div - key={dm.channel_id} - className={`dm-item ${isActive ? 'dm-item-active' : ''}`} - onClick={() => onSelectDM({ channel_id: dm.channel_id, other_username: dm.other_username })} - > - <div style={{ display: 'flex', alignItems: 'center', flex: 1, minWidth: 0 }}> - <div style={{ position: 'relative', marginRight: '12px', flexShrink: 0 }}> - <Avatar username={dm.other_username} avatarUrl={dm.other_user_avatar_url} size={32} /> - <div style={{ - position: 'absolute', bottom: -2, right: -2, - width: 10, height: 10, borderRadius: '50%', - backgroundColor: STATUS_COLORS[effectiveStatus] || STATUS_COLORS.offline, - border: '2px solid var(--bg-secondary)' - }} /> - </div> - <div style={{ overflow: 'hidden', flex: 1 }}> - <div style={{ color: isActive ? 'var(--header-primary)' : 'var(--text-normal)', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden', fontWeight: '500' }}> - {dm.other_username} - </div> - {voiceStates && voiceStates[dm.channel_id]?.length > 0 && ( - <div style={{ color: '#3ba55c', fontSize: '11px', fontWeight: '500' }}> - In Call - </div> - )} - </div> - </div> - <div className="dm-close-btn" onClick={(e) => handleCloseDM(e, dm)}> - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z"/> - </svg> - </div> - </div> - ); - })} - {(!dmChannels || dmChannels.length === 0) && ( - <div style={{ color: 'var(--text-muted)', fontSize: '13px', textAlign: 'center', padding: '16px 8px' }}> - No DMs yet. Click + to start a conversation. - </div> - )} - </div> - </div> - ); -}; - -export default DMList; diff --git a/packages/shared/src/components/FloatingStreamPiP.jsx b/packages/shared/src/components/FloatingStreamPiP.jsx deleted file mode 100644 index 78af8f8..0000000 --- a/packages/shared/src/components/FloatingStreamPiP.jsx +++ /dev/null @@ -1,271 +0,0 @@ -import React, { useState, useRef, useEffect, useCallback } from 'react'; -import { useVoice } from '../contexts/VoiceContext'; -import { VideoRenderer, useParticipantTrack } from '../utils/streamUtils.jsx'; -import { getUserPref, setUserPref } from '../utils/userPreferences'; -import { usePlatform } from '../platform'; - -const MIN_WIDTH = 240; -const MIN_HEIGHT = 135; -const MAX_WIDTH_RATIO = 0.75; -const MAX_HEIGHT_RATIO = 0.75; -const DEFAULT_WIDTH = 320; -const DEFAULT_HEIGHT = 180; -const ASPECT_RATIO = 16 / 9; - -const LIVE_BADGE_STYLE = { - backgroundColor: '#ed4245', borderRadius: '4px', padding: '2px 6px', - color: 'white', fontSize: '10px', fontWeight: 'bold', - textTransform: 'uppercase', letterSpacing: '0.5px', -}; - -const FloatingStreamPiP = ({ onGoBackToStream }) => { - const { room, watchingStreamOf, setWatchingStreamOf, voiceStates, activeChannelId } = useVoice(); - const { settings } = usePlatform(); - const pipUserId = localStorage.getItem('userId'); - - const [position, setPosition] = useState(() => getUserPref(pipUserId, 'pipPosition', { x: -1, y: -1 })); - const [size, setSize] = useState(() => getUserPref(pipUserId, 'pipSize', { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT })); - const [hovering, setHovering] = useState(false); - - const isDragging = useRef(false); - const isResizing = useRef(false); - const dragOffset = useRef({ x: 0, y: 0 }); - const resizeStart = useRef({ x: 0, y: 0, width: 0, height: 0 }); - const containerRef = useRef(null); - - // Initialize position to bottom-right on mount (only if no saved position) - useEffect(() => { - if (position.x === -1 && position.y === -1) { - setPosition({ - x: window.innerWidth - size.width - 24, - y: window.innerHeight - size.height - 24, - }); - } - }, []); - - // Find the watched participant from the room - const participant = (() => { - if (!room || !watchingStreamOf) return null; - if (room.localParticipant.identity === watchingStreamOf) return room.localParticipant; - return room.remoteParticipants.get(watchingStreamOf) || null; - })(); - - const screenTrack = useParticipantTrack(participant, 'screenshare'); - - // Resolve streamer username from voiceStates - const streamerUsername = (() => { - if (!watchingStreamOf) return ''; - for (const users of Object.values(voiceStates)) { - const u = users.find(u => u.userId === watchingStreamOf); - if (u) return u.username; - } - return watchingStreamOf; - })(); - - // Drag handlers - const handleDragStart = useCallback((e) => { - if (isResizing.current) return; - e.preventDefault(); - isDragging.current = true; - dragOffset.current = { - x: e.clientX - position.x, - y: e.clientY - position.y, - }; - }, [position]); - - useEffect(() => { - const handleMouseMove = (e) => { - if (isDragging.current) { - let newX = e.clientX - dragOffset.current.x; - let newY = e.clientY - dragOffset.current.y; - - // Constrain to window bounds - newX = Math.max(0, Math.min(newX, window.innerWidth - size.width)); - newY = Math.max(0, Math.min(newY, window.innerHeight - size.height)); - - setPosition({ x: newX, y: newY }); - } - - if (isResizing.current) { - const dx = e.clientX - resizeStart.current.x; - const dy = e.clientY - resizeStart.current.y; - - // Use the larger delta to maintain aspect ratio - const maxW = window.innerWidth * MAX_WIDTH_RATIO; - const maxH = window.innerHeight * MAX_HEIGHT_RATIO; - - let newWidth = resizeStart.current.width + dx; - newWidth = Math.max(MIN_WIDTH, Math.min(maxW, newWidth)); - let newHeight = newWidth / ASPECT_RATIO; - - if (newHeight > maxH) { - newHeight = maxH; - newWidth = newHeight * ASPECT_RATIO; - } - if (newHeight < MIN_HEIGHT) { - newHeight = MIN_HEIGHT; - newWidth = newHeight * ASPECT_RATIO; - } - - setSize({ width: Math.round(newWidth), height: Math.round(newHeight) }); - } - }; - - const handleMouseUp = () => { - isDragging.current = false; - isResizing.current = false; - }; - - window.addEventListener('mousemove', handleMouseMove); - window.addEventListener('mouseup', handleMouseUp); - return () => { - window.removeEventListener('mousemove', handleMouseMove); - window.removeEventListener('mouseup', handleMouseUp); - }; - }, [size]); - - // Resize handler - const handleResizeStart = useCallback((e) => { - e.preventDefault(); - e.stopPropagation(); - isResizing.current = true; - resizeStart.current = { - x: e.clientX, - y: e.clientY, - width: size.width, - height: size.height, - }; - }, [size]); - - // Debounced persist of PiP position and size - useEffect(() => { - if (position.x === -1 && position.y === -1) return; - const timer = setTimeout(() => { - setUserPref(pipUserId, 'pipPosition', position, settings); - setUserPref(pipUserId, 'pipSize', size, settings); - }, 300); - return () => clearTimeout(timer); - }, [position, size, pipUserId]); - - const handleStopWatching = useCallback(() => { - setWatchingStreamOf(null); - }, [setWatchingStreamOf]); - - if (!watchingStreamOf || !participant) return null; - - return ( - <div - ref={containerRef} - onMouseEnter={() => setHovering(true)} - onMouseLeave={() => setHovering(false)} - style={{ - position: 'fixed', - left: position.x, - top: position.y, - width: size.width, - height: size.height, - zIndex: 1000, - borderRadius: '8px', - overflow: 'hidden', - boxShadow: '0 8px 24px rgba(0,0,0,0.5)', - backgroundColor: 'black', - cursor: isDragging.current ? 'grabbing' : 'default', - userSelect: 'none', - }} - onMouseDown={handleDragStart} - > - {/* Video content */} - {screenTrack ? ( - <VideoRenderer track={screenTrack} style={{ objectFit: 'contain', pointerEvents: 'none' }} /> - ) : ( - <div style={{ - width: '100%', height: '100%', - display: 'flex', alignItems: 'center', justifyContent: 'center', - color: '#72767d', fontSize: '13px', - }}> - Loading stream... - </div> - )} - - {/* Hover overlay */} - {hovering && ( - <div style={{ - position: 'absolute', inset: 0, - backgroundColor: 'rgba(0,0,0,0.55)', - display: 'flex', flexDirection: 'column', - justifyContent: 'space-between', - padding: '8px', - transition: 'opacity 0.15s', - }}> - {/* Top row: streamer name + back button */} - <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> - <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}> - <span style={{ color: 'white', fontSize: '12px', fontWeight: '600' }}> - {streamerUsername} - </span> - <span style={LIVE_BADGE_STYLE}>LIVE</span> - </div> - <button - onClick={(e) => { e.stopPropagation(); onGoBackToStream(); }} - title="Back to Stream" - style={{ - width: '28px', height: '28px', borderRadius: '4px', - backgroundColor: 'rgba(255,255,255,0.15)', border: 'none', - color: 'white', fontSize: '16px', cursor: 'pointer', - display: 'flex', alignItems: 'center', justifyContent: 'center', - transition: 'background-color 0.15s', - }} - onMouseEnter={e => e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.25)'} - onMouseLeave={e => e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.15)'} - > - {/* Back arrow icon */} - <svg width="16" height="16" viewBox="0 0 16 16" fill="none"> - <path d="M6.5 12.5L2 8L6.5 3.5M2.5 8H14" stroke="white" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/> - </svg> - </button> - </div> - - {/* Bottom row: stop watching */} - <div style={{ display: 'flex', justifyContent: 'center' }}> - <button - onClick={(e) => { e.stopPropagation(); handleStopWatching(); }} - style={{ - backgroundColor: 'rgba(0,0,0,0.6)', color: 'white', border: 'none', - padding: '6px 14px', borderRadius: '4px', fontWeight: '600', - fontSize: '12px', cursor: 'pointer', - transition: 'background-color 0.15s', - }} - onMouseEnter={e => e.currentTarget.style.backgroundColor = 'rgba(0,0,0,0.8)'} - onMouseLeave={e => e.currentTarget.style.backgroundColor = 'rgba(0,0,0,0.6)'} - > - Stop Watching - </button> - </div> - </div> - )} - - {/* Resize handle (bottom-right corner) */} - <div - onMouseDown={handleResizeStart} - style={{ - position: 'absolute', - bottom: 0, - right: 0, - width: '16px', - height: '16px', - cursor: 'nwse-resize', - zIndex: 2, - }} - > - {/* Diagonal grip lines */} - <svg width="16" height="16" viewBox="0 0 16 16" style={{ opacity: hovering ? 0.6 : 0 , transition: 'opacity 0.15s' }}> - <line x1="14" y1="4" x2="4" y2="14" stroke="white" strokeWidth="1.5"/> - <line x1="14" y1="8" x2="8" y2="14" stroke="white" strokeWidth="1.5"/> - <line x1="14" y1="12" x2="12" y2="14" stroke="white" strokeWidth="1.5"/> - </svg> - </div> - </div> - ); -}; - -export default FloatingStreamPiP; diff --git a/packages/shared/src/components/FriendsView.jsx b/packages/shared/src/components/FriendsView.jsx deleted file mode 100644 index c0008f6..0000000 --- a/packages/shared/src/components/FriendsView.jsx +++ /dev/null @@ -1,149 +0,0 @@ -import React, { useState } from 'react'; -import { useQuery } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; -import Avatar from './Avatar'; -import ColoredIcon from './ColoredIcon'; -import { useOnlineUsers } from '../contexts/PresenceContext'; -import friendsIcon from '../assets/icons/friends.svg'; - -const FriendsView = ({ onOpenDM }) => { - const [activeTab, setActiveTab] = useState('Online'); - const [addFriendSearch, setAddFriendSearch] = useState(''); - - const myId = localStorage.getItem('userId'); - const { resolveStatus } = useOnlineUsers(); - - const allUsers = useQuery(api.auth.getPublicKeys) || []; - const users = allUsers.filter(u => u.id !== myId); - - const getUserColor = (username) => { - if (!username) return '#747f8d'; - const colors = ['#5865F2', '#EBA7CD', '#57F287', '#FEE75C', '#EB459E', '#ED4245']; - let hash = 0; - for (let i = 0; i < username.length; i++) { - hash = username.charCodeAt(i) + ((hash << 5) - hash); - } - return colors[Math.abs(hash) % colors.length]; - }; - - const STATUS_COLORS = { - online: '#3ba55c', - idle: '#faa61a', - dnd: '#ed4245', - invisible: '#747f8d', - offline: '#747f8d', - }; - - const filteredUsers = activeTab === 'Online' - ? users.filter(u => resolveStatus(u.status, u.id) !== 'offline') - : activeTab === 'Add Friend' - ? users.filter(u => u.username?.toLowerCase().includes(addFriendSearch.toLowerCase())) - : users; - - return ( - <div style={{ display: 'flex', flexDirection: 'column', flex: 1, backgroundColor: 'var(--bg-primary)', height: '100vh' }}> - {/* Top Bar */} - <div style={{ - height: '48px', - borderBottom: '1px solid var(--border-subtle)', - display: 'flex', - alignItems: 'center', - padding: '0 16px', - color: '#fff', - fontWeight: 'bold', - flexShrink: 0 - }}> - <div style={{ display: 'flex', alignItems: 'center', marginRight: '16px', paddingRight: '16px', borderRight: '1px solid var(--border-subtle)' }}> - <div style={{ marginRight: '12px' }}> - <ColoredIcon src={friendsIcon} color="var(--interactive-normal)" size="24px" /> - </div> - Friends - </div> - - <div style={{ display: 'flex', gap: '16px', alignItems: 'center' }}> - {['Online', 'All'].map(tab => ( - <div - key={tab} - onClick={() => setActiveTab(tab)} - className="friends-tab" - style={{ - cursor: 'pointer', - color: activeTab === tab ? 'var(--header-primary)' : 'var(--header-secondary)', - backgroundColor: activeTab === tab ? 'rgba(255,255,255,0.06)' : 'transparent', - padding: '2px 8px', - borderRadius: '4px' - }} - > - {tab} - </div> - ))} - </div> - </div> - - {/* List Header */} - <div style={{ padding: '16px 20px 8px' }}> - <div style={{ - fontSize: '12px', - fontWeight: 'bold', - color: 'var(--header-secondary)', - textTransform: 'uppercase' - }}> - {activeTab === 'Add Friend' ? 'USERS' : activeTab} — {filteredUsers.length} - </div> - </div> - - {/* Friends List */} - <div style={{ flex: 1, overflowY: 'auto', padding: '0 20px' }}> - {filteredUsers.map(user => { - const effectiveStatus = resolveStatus(user.status, user.id); - return ( - <div - key={user.id} - className="friend-item" - > - <div style={{ display: 'flex', alignItems: 'center' }}> - <div style={{ position: 'relative', marginRight: '12px' }}> - <Avatar username={user.username} avatarUrl={user.avatarUrl} size={32} /> - <div style={{ - position: 'absolute', bottom: -2, right: -2, - width: 10, height: 10, borderRadius: '50%', - backgroundColor: STATUS_COLORS[effectiveStatus] || STATUS_COLORS.offline, - border: '2px solid var(--bg-primary)' - }} /> - </div> - <div> - <div style={{ color: 'var(--header-primary)', fontWeight: '600' }}> - {user.username ?? 'Unknown'} - </div> - <div style={{ color: 'var(--header-secondary)', fontSize: '12px' }}> - {effectiveStatus === 'dnd' ? 'Do Not Disturb' : effectiveStatus.charAt(0).toUpperCase() + effectiveStatus.slice(1)} - </div> - </div> - </div> - - <div style={{ display: 'flex', gap: '8px' }}> - <div - className="friend-action-btn" - onClick={() => onOpenDM && onOpenDM(user.id, user.username)} - > - <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"> - <path d="M4.79805 3C3.80445 3 2.99805 3.8055 2.99805 4.8V15.6C2.99805 16.5936 3.80445 17.4 4.79805 17.4H8.39805L11.998 21L15.598 17.4H19.198C20.1925 17.4 20.998 16.5936 20.998 15.6V4.8C20.998 3.8055 20.1925 3 19.198 3H4.79805Z" /> - </svg> - </div> - <div className="friend-action-btn"> - <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"> - <path d="M12 16C13.1046 16 14 15.1046 14 14C14 12.8954 13.1046 12 12 12C10.8954 12 10 12.8954 10 14C10 15.1046 10.8954 16 12 16Z" /> - <path d="M12 10C13.1046 10 14 9.10457 14 8C14 6.89543 13.1046 6 12 6C10.8954 6 10 6.89543 10 8C10 9.10457 10.8954 10 12 10Z" /> - <path d="M12 22C13.1046 22 14 21.1046 14 20C14 18.8954 13.1046 18 12 18C10.8954 18 10 18.8954 10 20C10 21.1046 10.8954 22 12 22Z" /> - </svg> - </div> - </div> - </div> - ); - })} - </div> - </div> - ); -}; - -export default FriendsView; diff --git a/packages/shared/src/components/GifPicker.jsx b/packages/shared/src/components/GifPicker.jsx deleted file mode 100644 index 455868c..0000000 --- a/packages/shared/src/components/GifPicker.jsx +++ /dev/null @@ -1,342 +0,0 @@ -import CategorizedEmojis, { AllEmojis } from '../assets/emojis'; -import React, { useState, useEffect, useRef } from 'react'; -import { useConvex, useQuery } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; - -const EmojiItem = ({ emoji, onSelect }) => ( - <div - onMouseDown={(e) => e.preventDefault()} - onClick={() => onSelect && onSelect({ type: 'emoji', ...emoji })} - title={`:${emoji.name}:`} - style={{ cursor: 'pointer', padding: '4px', borderRadius: '4px' }} - onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'var(--background-modifier-hover)'} - onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'transparent'} - > - <img src={emoji.src} alt={emoji.name} style={{ width: '32px', height: '32px' }} loading="lazy" /> - </div> -); - -const emojiGridStyle = { display: 'grid', gridTemplateColumns: 'repeat(8, 1fr)', gap: '4px' }; - -const GifContent = ({ search, results, categories, onSelect, onCategoryClick }) => { - if (search || results.length > 0) { - return ( - <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px' }}> - {results.map(gif => ( - <img - key={gif.id} - src={gif.media_formats?.tinygif?.url || gif.media_formats?.gif?.url} - alt={gif.title} - style={{ width: '100%', borderRadius: '4px', cursor: 'pointer' }} - onMouseDown={(e) => e.preventDefault()} - onClick={() => onSelect(gif.media_formats?.gif?.url || gif.src)} - /> - ))} - {results.length === 0 && <div style={{ color: 'var(--header-secondary)', gridColumn: 'span 2', textAlign: 'center' }}>No GIFs found</div>} - </div> - ); - } - - return ( - <div> - <div style={{ - backgroundImage: 'linear-gradient(to right, #4a90e2, #9013fe)', - borderRadius: '4px', - padding: '20px', - marginBottom: '12px', - color: '#fff', - fontWeight: 'bold', - fontSize: '16px', - textAlign: 'center', - cursor: 'pointer' - }}> - Favorites - </div> - <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px' }}> - {categories.map(cat => ( - <div - key={cat.name} - onClick={() => onCategoryClick(cat.name)} - style={{ - position: 'relative', - height: '100px', - borderRadius: '4px', - overflow: 'hidden', - cursor: 'pointer', - backgroundColor: 'var(--bg-tertiary)' - }} - > - <video - src={cat.src} - autoPlay - loop - muted - style={{ width: '100%', height: '100%', objectFit: 'cover', opacity: 0.6 }} - /> - <div style={{ - position: 'absolute', - top: 0, left: 0, right: 0, bottom: 0, - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - backgroundColor: 'rgba(0,0,0,0.3)', - color: '#fff', - fontWeight: 'bold', - textTransform: 'capitalize' - }}> - {cat.name} - </div> - </div> - ))} - </div> - </div> - ); -}; - -const EmojiContent = ({ search, onSelect, collapsedCategories, toggleCategory, customEmojis = [] }) => { - if (search) { - const q = search.toLowerCase().replace(/:/g, ''); - const customFiltered = customEmojis.filter(e => e.name.toLowerCase().includes(q)); - const builtinFiltered = AllEmojis.filter(e => e.name.toLowerCase().includes(q)); - const filtered = [...customFiltered, ...builtinFiltered].slice(0, 100); - return ( - <div className="emoji-grid" style={{ height: '100%' }}> - <div style={emojiGridStyle}> - {filtered.map((emoji, idx) => ( - <EmojiItem key={idx} emoji={emoji} onSelect={onSelect} /> - ))} - </div> - </div> - ); - } - - const CategoryHeader = ({ name, collapsed }) => ( - <div - onClick={() => toggleCategory(name)} - style={{ - display: 'flex', - alignItems: 'center', - cursor: 'pointer', - marginBottom: '8px', - padding: '4px', - borderRadius: '4px' - }} - onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'var(--background-modifier-hover)'} - onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'transparent'} - > - <svg - xmlns="http://www.w3.org/2000/svg" - width="12" - height="12" - viewBox="0 0 24 24" - fill="none" - stroke="var(--header-secondary)" - strokeWidth="3" - strokeLinecap="round" - strokeLinejoin="round" - style={{ - marginRight: '8px', - transform: collapsed ? 'rotate(-90deg)' : 'rotate(0deg)', - transition: 'transform 0.2s' - }} - > - <polyline points="6 9 12 15 18 9"></polyline> - </svg> - <h3 style={{ - color: 'var(--header-secondary)', - fontSize: '12px', - textTransform: 'uppercase', - fontWeight: 700, - margin: 0 - }}> - {name} - </h3> - </div> - ); - - return ( - <div className="emoji-grid" style={{ height: '100%' }}> - {customEmojis.length > 0 && ( - <div style={{ marginBottom: '8px' }}> - <CategoryHeader name="Custom" collapsed={collapsedCategories['Custom']} /> - {!collapsedCategories['Custom'] && ( - <div style={emojiGridStyle}> - {customEmojis.map((emoji) => ( - <EmojiItem key={emoji._id || emoji.name} emoji={emoji} onSelect={onSelect} /> - ))} - </div> - )} - </div> - )} - {Object.entries(CategorizedEmojis).map(([category, emojis]) => ( - <div key={category} style={{ marginBottom: '8px' }}> - <CategoryHeader name={category} collapsed={collapsedCategories[category]} /> - {!collapsedCategories[category] && ( - <div style={emojiGridStyle}> - {emojis.map((emoji, idx) => ( - <EmojiItem key={idx} emoji={emoji} onSelect={onSelect} /> - ))} - </div> - )} - </div> - ))} - </div> - ); -}; - -const initialCollapsed = Object.fromEntries( - Object.keys(CategorizedEmojis).map(cat => [cat, true]) -); - -const GifPicker = ({ onSelect, onClose, initialTab, currentTab, onTabChange }) => { - const [search, setSearch] = useState(''); - const [categories, setCategories] = useState([]); - const [results, setResults] = useState([]); - const [loading, setLoading] = useState(false); - const [internalActiveTab, setInternalActiveTab] = useState(initialTab || 'GIFs'); - const [collapsedCategories, setCollapsedCategories] = useState(initialCollapsed); - const inputRef = useRef(null); - - const convex = useConvex(); - const customEmojis = useQuery(api.customEmojis.list) || []; - - const activeTab = currentTab !== undefined ? currentTab : internalActiveTab; - const setActiveTab = (tab) => { - if (onTabChange) onTabChange(tab); - if (currentTab === undefined) setInternalActiveTab(tab); - }; - - useEffect(() => { - convex.action(api.gifs.categories, {}) - .then(data => { - if (data.categories) setCategories(data.categories); - }) - .catch(err => console.error('Failed to load categories', err)); - - if (inputRef.current) inputRef.current.focus(); - }, []); - - useEffect(() => { - const fetchResults = async () => { - if (!search || activeTab !== 'GIFs') { - setResults([]); - return; - } - setLoading(true); - try { - const data = await convex.action(api.gifs.search, { q: search }); - setResults(data.results || []); - } catch (err) { - console.error(err); - } finally { - setLoading(false); - } - }; - - const timeout = setTimeout(fetchResults, 500); - return () => clearTimeout(timeout); - }, [search, activeTab]); - - const toggleCategory = (categoryName) => { - setCollapsedCategories(prev => ({ - ...prev, - [categoryName]: !prev[categoryName] - })); - }; - - return ( - <div - className="gif-picker" - style={{ - position: 'absolute', - bottom: '50px', - right: '0', - width: '400px', - height: '450px', - backgroundColor: 'var(--embed-background)', - borderRadius: '8px', - boxShadow: '0 8px 16px rgba(0,0,0,0.24)', - display: 'flex', - flexDirection: 'column', - overflow: 'hidden', - zIndex: 1000 - }} - onClick={(e) => e.stopPropagation()} - > - {/* Header / Tabs */} - <div style={{ padding: '16px 16px 8px 16px', display: 'flex', gap: '16px', borderBottom: '1px solid var(--bg-tertiary)' }}> - {['GIFs', 'Stickers', 'Emoji'].map(tab => ( - <button - key={tab} - onClick={() => setActiveTab(tab)} - style={{ - background: 'none', - border: 'none', - color: activeTab === tab ? 'var(--header-primary)' : 'var(--header-secondary)', - fontWeight: 500, - cursor: 'pointer', - fontSize: '14px', - paddingBottom: '4px', - borderBottom: activeTab === tab ? '2px solid var(--brand-experiment)' : '2px solid transparent' - }} - > - {tab} - </button> - ))} - </div> - - {/* Search Bar */} - <div style={{ padding: '8px 16px' }}> - <div style={{ - backgroundColor: 'var(--bg-tertiary)', - borderRadius: '4px', - display: 'flex', - alignItems: 'center', - padding: '0 8px' - }}> - <input - ref={inputRef} - type="text" - placeholder={activeTab === 'Emoji' ? "Find the perfect emoji" : "Search Tenor"} - value={search} - onChange={(e) => setSearch(e.target.value)} - onKeyDown={(e) => { - if (e.key === 'Enter') { - e.preventDefault(); - e.stopPropagation(); - } - }} - style={{ - flex: 1, - backgroundColor: 'transparent', - border: 'none', - color: 'var(--text-normal)', - padding: '8px', - fontSize: '14px', - outline: 'none' - }} - /> - <div style={{ padding: '4px' }}> - <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--header-secondary)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> - <circle cx="11" cy="11" r="8"></circle> - <line x1="21" y1="21" x2="16.65" y2="16.65"></line> - </svg> - </div> - </div> - </div> - - {/* Content Area */} - <div style={{ flex: 1, overflowY: 'auto', padding: '0 16px 16px 16px' }}> - {loading ? ( - <div style={{ color: 'var(--header-secondary)', textAlign: 'center', padding: '20px' }}>Loading...</div> - ) : activeTab === 'GIFs' ? ( - <GifContent search={search} results={results} categories={categories} onSelect={onSelect} onCategoryClick={setSearch} /> - ) : ( - <EmojiContent search={search} onSelect={onSelect} collapsedCategories={collapsedCategories} toggleCategory={toggleCategory} customEmojis={customEmojis} /> - )} - </div> - </div> - ); -}; - -export default GifPicker; diff --git a/packages/shared/src/components/IncomingCallUI.jsx b/packages/shared/src/components/IncomingCallUI.jsx deleted file mode 100644 index 63ac5b5..0000000 --- a/packages/shared/src/components/IncomingCallUI.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import Avatar from './Avatar'; - -const IncomingCallUI = ({ callerUsername, callerAvatarUrl, onJoin, onReject }) => { - return ( - <div className="incoming-call-ui"> - <div className="incoming-call-avatar-ring"> - <Avatar username={callerUsername} avatarUrl={callerAvatarUrl} size={80} /> - </div> - <div className="incoming-call-username">{callerUsername}</div> - <div className="incoming-call-subtitle">Incoming call...</div> - <div className="incoming-call-buttons"> - <button className="incoming-call-btn join" onClick={onJoin} title="Join Call"> - <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"> - <path d="M6.62 10.79a15.05 15.05 0 0 0 6.59 6.59l2.2-2.2a1 1 0 0 1 1.01-.24c1.12.37 2.33.57 3.58.57a1 1 0 0 1 1 1V20a1 1 0 0 1-1 1A17 17 0 0 1 3 4a1 1 0 0 1 1-1h3.5a1 1 0 0 1 1 1c0 1.25.2 2.46.57 3.58a1 1 0 0 1-.25 1.01l-2.2 2.2z"/> - </svg> - </button> - <button className="incoming-call-btn reject" onClick={onReject} title="Reject"> - <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"> - <path d="M12 9c-1.6 0-3.15.25-4.6.72v3.1c0 .39-.23.74-.56.9-.98.49-1.87 1.12-2.66 1.85-.18.18-.43.28-.7.28a1 1 0 0 1-.71-.3L.29 13.08a1 1 0 0 1 0-1.41C3.57 8.55 7.53 7 12 7s8.43 1.55 11.71 4.67a1 1 0 0 1 0 1.41l-2.48 2.48a1 1 0 0 1-.7.29c-.27 0-.52-.11-.7-.28a11.27 11.27 0 0 0-2.67-1.85.99.99 0 0 1-.56-.9v-3.1A15.9 15.9 0 0 0 12 9z"/> - </svg> - </button> - </div> - </div> - ); -}; - -export default IncomingCallUI; diff --git a/packages/shared/src/components/MembersList.jsx b/packages/shared/src/components/MembersList.jsx deleted file mode 100644 index 11bd470..0000000 --- a/packages/shared/src/components/MembersList.jsx +++ /dev/null @@ -1,256 +0,0 @@ -import React, { useState, useRef, useEffect, useLayoutEffect } from 'react'; -import { useQuery } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; -import { useOnlineUsers } from '../contexts/PresenceContext'; -import { useVoice } from '../contexts/VoiceContext'; -import { CrownIcon, SharingIcon } from '../assets/icons'; -import ColoredIcon from './ColoredIcon'; -import ChangeNicknameModal from './ChangeNicknameModal'; -import avatarDecoStatic from '../assets/avatar_decorations/a_dcfe10bac4a782ffb5eefef7a8003115.png'; -import avatarDecoAnimated from '../assets/avatar_decorations/passthrough/a_dcfe10bac4a782ffb5eefef7a8003115.png'; - -const USER_COLORS = ['#5865F2', '#EBA7CD', '#57F287', '#FEE75C', '#EB459E', '#ED4245']; - -function getUserColor(name) { - let hash = 0; - for (let i = 0; i < name.length; i++) { - hash = name.charCodeAt(i) + ((hash << 5) - hash); - } - return USER_COLORS[Math.abs(hash) % USER_COLORS.length]; -} - -const STATUS_COLORS = { - online: '#3ba55c', - idle: '#faa61a', - dnd: '#ed4245', - invisible: '#747f8d', - offline: '#747f8d', -}; - -const MemberContextMenu = ({ x, y, onClose, member, isSelf, canManageNicknames, onChangeNickname, onMessage, onStartCall }) => { - const menuRef = useRef(null); - const [pos, setPos] = useState({ top: y, left: x }); - - useEffect(() => { - const h = () => onClose(); - window.addEventListener('click', h); - window.addEventListener('close-context-menus', h); - return () => { window.removeEventListener('click', h); window.removeEventListener('close-context-menus', h); }; - }, [onClose]); - - useLayoutEffect(() => { - if (!menuRef.current) return; - const rect = menuRef.current.getBoundingClientRect(); - let newTop = y, newLeft = x; - if (x + rect.width > window.innerWidth) newLeft = x - rect.width; - if (y + rect.height > window.innerHeight) newTop = y - rect.height; - if (newLeft < 0) newLeft = 10; - if (newTop < 0) newTop = 10; - setPos({ top: newTop, left: newLeft }); - }, [x, y]); - - return ( - <div ref={menuRef} className="context-menu" style={{ top: pos.top, left: pos.left }} onClick={(e) => e.stopPropagation()}> - {(isSelf || canManageNicknames) && ( - <div - className="context-menu-item" - onClick={(e) => { e.stopPropagation(); onChangeNickname(); onClose(); }} - > - <span>Change Nickname</span> - </div> - )} - {(isSelf || canManageNicknames) && (!isSelf) && ( - <div className="context-menu-separator" /> - )} - {!isSelf && ( - <> - <div - className="context-menu-item" - onClick={(e) => { e.stopPropagation(); onMessage(); onClose(); }} - > - <span>Message</span> - </div> - <div - className="context-menu-item" - onClick={(e) => { e.stopPropagation(); onStartCall(); onClose(); }} - > - <span>Start a Call</span> - </div> - </> - )} - </div> - ); -}; - -const MembersList = ({ channelId, visible, onMemberClick, userId, myPermissions, onOpenDM, onStartCallWithUser }) => { - const members = useQuery( - api.members.getChannelMembers, - channelId ? { channelId } : "skip" - ) || []; - const { resolveStatus } = useOnlineUsers(); - const { voiceStates } = useVoice(); - const [contextMenu, setContextMenu] = useState(null); - const [nicknameModal, setNicknameModal] = useState(null); - - const usersInVoice = new Set(); - const usersScreenSharing = new Set(); - Object.values(voiceStates).forEach(users => { - users.forEach(u => { - usersInVoice.add(u.userId); - if (u.isScreenSharing) usersScreenSharing.add(u.userId); - }); - }); - - if (!visible) return null; - - const onlineMembers = members.filter(m => resolveStatus(m.status, m.id) !== 'offline'); - const offlineMembers = members.filter(m => resolveStatus(m.status, m.id) === 'offline'); - - // Group online members by highest hoisted role - const roleGroups = {}; - const ungrouped = []; - - onlineMembers.forEach(member => { - const hoistedRole = member.roles.find(r => r.isHoist && r.name !== '@everyone' && r.name !== 'Owner'); - if (hoistedRole) { - const key = `${hoistedRole.position}_${hoistedRole.name}`; - if (!roleGroups[key]) { - roleGroups[key] = { role: hoistedRole, members: [] }; - } - roleGroups[key].members.push(member); - } else { - ungrouped.push(member); - } - }); - - // Sort groups by position descending - const sortedGroups = Object.values(roleGroups).sort((a, b) => b.role.position - a.role.position); - - const handleContextMenu = (e, member) => { - e.preventDefault(); - e.stopPropagation(); - window.dispatchEvent(new Event('close-context-menus')); - setContextMenu({ x: e.clientX, y: e.clientY, member }); - }; - - const renderMember = (member) => { - const displayRole = member.roles.find(r => r.name !== '@everyone' && r.name !== 'Owner') || null; - const nameColor = displayRole ? displayRole.color : '#fff'; - const isOwner = member.roles.some(r => r.name === 'Owner'); - const effectiveStatus = resolveStatus(member.status, member.id); - - return ( - <div - key={member.id} - className="member-item" - onClick={() => onMemberClick && onMemberClick(member)} - onContextMenu={(e) => handleContextMenu(e, member)} - style={effectiveStatus === 'offline' ? { opacity: 0.3 } : {}} - > - <div className="member-avatar-wrapper"> - {member.avatarUrl ? ( - <img - className="member-avatar" - src={member.avatarUrl} - alt={member.username} - style={{ objectFit: 'cover' }} - /> - ) : ( - <div - className="member-avatar" - style={{ backgroundColor: getUserColor(member.username) }} - > - {(member.displayName || member.username).substring(0, 1).toUpperCase()} - </div> - )} - {/* <img className="avatar-decoration avatar-decoration-static" src={avatarDecoStatic} alt="" aria-hidden="true" /> */} - {/* <img className="avatar-decoration avatar-decoration-animated" src={avatarDecoAnimated} alt="" aria-hidden="true" /> */} - <div - className="member-status-dot" - style={{ backgroundColor: STATUS_COLORS[effectiveStatus] || STATUS_COLORS.offline }} - /> - </div> - <div className="member-info"> - <span className="member-name" style={{ color: nameColor, display: 'flex', alignItems: 'center', gap: '4px' }}> - {member.displayName || member.username} - {isOwner && <ColoredIcon src={CrownIcon} color="var(--text-feedback-warning)" size="14px" />} - </span> - {usersScreenSharing.has(member.id) ? ( - <div className="member-screen-sharing-indicator"> - <img src={SharingIcon} alt="" /> - Sharing their screen - </div> - ) : usersInVoice.has(member.id) ? ( - <div className="member-voice-indicator"> - <svg viewBox="0 0 24 24" fill="#3ba55c"> - <path d="M12 3a1 1 0 0 0-1-1h-.06a1 1 0 0 0-.74.32L5.92 7H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.92l4.28 4.68a1 1 0 0 0 .74.32H11a1 1 0 0 0 1-1zm3.1 17.75c-.58.14-1.1-.33-1.1-.92v-.03c0-.5.37-.92.85-1.05a7 7 0 0 0 0-13.5A1.11 1.11 0 0 1 14 4.2v-.03c0-.6.52-1.06 1.1-.92a9 9 0 0 1 0 17.5"/> - <path d="M15.16 16.51c-.57.28-1.16-.2-1.16-.83v-.14c0-.43.28-.8.63-1.02a3 3 0 0 0 0-5.04c-.35-.23-.63-.6-.63-1.02v-.14c0-.63.59-1.1 1.16-.83a5 5 0 0 1 0 9.02"/> - </svg> - In Voice - </div> - ) : member.customStatus ? ( - <div style={{ fontSize: '12px', color: 'var(--text-muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}> - {member.customStatus} - </div> - ) : null} - </div> - </div> - ); - }; - - return ( - <div className="members-list"> - {/* <img src={avatarDecoAnimated} alt="" style={{ display: 'none' }} aria-hidden="true" /> */} - {sortedGroups.map(group => ( - <React.Fragment key={group.role.name}> - <div className="members-role-header"> - {group.role.name} — {group.members.length} - </div> - {group.members.map(renderMember)} - </React.Fragment> - ))} - {ungrouped.length > 0 && ( - <> - <div className="members-role-header"> - ONLINE — {ungrouped.length} - </div> - {ungrouped.map(renderMember)} - </> - )} - {offlineMembers.length > 0 && ( - <> - <div className="members-role-header"> - OFFLINE — {offlineMembers.length} - </div> - {offlineMembers.map(renderMember)} - </> - )} - - {contextMenu && ( - <MemberContextMenu - x={contextMenu.x} - y={contextMenu.y} - member={contextMenu.member} - isSelf={contextMenu.member.id === userId} - canManageNicknames={!!myPermissions?.manage_nicknames} - onClose={() => setContextMenu(null)} - onChangeNickname={() => setNicknameModal(contextMenu.member)} - onMessage={() => onOpenDM && onOpenDM(contextMenu.member.id, contextMenu.member.displayName || contextMenu.member.username)} - onStartCall={() => onStartCallWithUser && onStartCallWithUser(contextMenu.member.id, contextMenu.member.displayName || contextMenu.member.username)} - /> - )} - - {nicknameModal && ( - <ChangeNicknameModal - targetUserId={nicknameModal.id} - targetUsername={nicknameModal.username} - currentNickname={nicknameModal.displayName || ''} - actorUserId={userId} - onClose={() => setNicknameModal(null)} - /> - )} - </div> - ); -}; - -export default MembersList; diff --git a/packages/shared/src/components/MentionMenu.jsx b/packages/shared/src/components/MentionMenu.jsx deleted file mode 100644 index a706612..0000000 --- a/packages/shared/src/components/MentionMenu.jsx +++ /dev/null @@ -1,84 +0,0 @@ -import React, { useEffect, useRef } from 'react'; -import Avatar from './Avatar'; - -const MentionMenu = ({ items, selectedIndex, onSelect, onHover }) => { - const scrollerRef = useRef(null); - - useEffect(() => { - if (!scrollerRef.current) return; - const selected = scrollerRef.current.querySelector('.mention-menu-row.selected'); - if (selected) selected.scrollIntoView({ block: 'nearest' }); - }, [selectedIndex]); - - if (!items || items.length === 0) return null; - - const roleItems = items.filter(i => i.type === 'role'); - const memberItems = items.filter(i => i.type === 'member'); - - let globalIndex = 0; - - return ( - <div className="mention-menu"> - <div className="mention-menu-scroller" ref={scrollerRef}> - {roleItems.length > 0 && ( - <> - <div className="mention-menu-section-header">Roles</div> - {roleItems.map((role) => { - const idx = globalIndex++; - const displayName = role.name.startsWith('@') ? role.name : `@${role.name}`; - return ( - <div - key={`role-${role._id}`} - className={`mention-menu-row${idx === selectedIndex ? ' selected' : ''}`} - onMouseDown={(e) => e.preventDefault()} - onClick={() => onSelect(role)} - onMouseEnter={() => onHover(idx)} - > - <div - className="mention-menu-role-icon" - style={{ backgroundColor: role.color || '#99aab5' }} - > - @ - </div> - <span - className="mention-menu-row-primary" - style={role.color ? { color: role.color } : undefined} - > - {displayName} - </span> - </div> - ); - })} - </> - )} - {memberItems.length > 0 && ( - <> - <div className="mention-menu-section-header">Members</div> - {memberItems.map((member) => { - const idx = globalIndex++; - const topRole = member.roles && member.roles.length > 0 ? member.roles[0] : null; - const nameColor = topRole?.color || undefined; - return ( - <div - key={member.id} - className={`mention-menu-row${idx === selectedIndex ? ' selected' : ''}`} - onMouseDown={(e) => e.preventDefault()} - onClick={() => onSelect(member)} - onMouseEnter={() => onHover(idx)} - > - <Avatar username={member.username} avatarUrl={member.avatarUrl} size={24} /> - <span className="mention-menu-row-primary" style={nameColor ? { color: nameColor } : undefined}> - {member.displayName || member.username} - </span> - <span className="mention-menu-row-secondary">{member.username}</span> - </div> - ); - })} - </> - )} - </div> - </div> - ); -}; - -export default MentionMenu; diff --git a/packages/shared/src/components/MessageItem.jsx b/packages/shared/src/components/MessageItem.jsx deleted file mode 100644 index 77523e0..0000000 --- a/packages/shared/src/components/MessageItem.jsx +++ /dev/null @@ -1,408 +0,0 @@ -import React, { useState } from 'react'; -import ReactMarkdown from 'react-markdown'; -import remarkGfm from 'remark-gfm'; -import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; -import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'; -import { - EmojieIcon, - EditIcon, - ReplyIcon, - MoreIcon, - DeleteIcon, - PinIcon, -} from '../assets/icons'; -import { getEmojiUrl, AllEmojis } from '../assets/emojis'; -import Tooltip from './Tooltip'; -import Avatar from './Avatar'; -import ColoredIcon from './ColoredIcon'; -import { usePlatform } from '../platform'; - -const fireIcon = getEmojiUrl('nature', 'fire'); -const heartIcon = getEmojiUrl('symbols', 'heart'); -const thumbsupIcon = getEmojiUrl('people', 'thumbsup'); - -const ICON_COLOR_DEFAULT = 'hsl(240, 4.294%, 68.039%)'; -const USER_COLORS = ['#5865F2', '#EBA7CD', '#57F287', '#FEE75C', '#EB459E', '#ED4245']; - -export const getUserColor = (name) => { - let hash = 0; - for (let i = 0; i < name.length; i++) { hash = name.charCodeAt(i) + ((hash << 5) - hash); } - return USER_COLORS[Math.abs(hash) % USER_COLORS.length]; -}; - -export const extractUrls = (text) => { - const urlRegex = /(https?:\/\/[^\s]+)/g; - return text.match(urlRegex) || []; -}; - -export const formatMentions = (text, roles) => { - if (!text) return ''; - // First pass: replace @role:Name with role mention links - let result = text.replace(/@role:([^\s]+)/g, (match, name) => { - const role = roles?.find(r => r.name === name); - const color = role?.color || '#99aab5'; - const displayName = name.startsWith('@') ? name : `@${name}`; - return `[${displayName}](rolemention://${encodeURIComponent(name)}?color=${encodeURIComponent(color)})`; - }); - // Second pass: replace @-prefixed role names (like @everyone) directly - if (roles) { - for (const role of roles) { - if (role.name.startsWith('@')) { - const escaped = role.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - const re = new RegExp(`(?<!\\[)${escaped}\\b`, 'g'); - const color = role.color || '#99aab5'; - result = result.replace(re, `[${role.name}](rolemention://${encodeURIComponent(role.name)}?color=${encodeURIComponent(color)})`); - } - } - } - // Third pass: replace @username with user mention links (skip already-linked @) - result = result.replace(/(?<!\[)@(\w+)/g, '[@$1](mention://$1)'); - return result; -}; - -export const formatEmojis = (text, customEmojis = []) => { - if (!text) return ''; - return text.replace(/:([a-zA-Z0-9_]+):/g, (match, name) => { - const custom = customEmojis.find(e => e.name === name); - if (custom) return `![${match}](${custom.src})`; - const emoji = AllEmojis.find(e => e.name === name); - return emoji ? `![${match}](${emoji.src})` : match; - }); -}; - -export const parseAttachment = (content) => { - if (!content || !content.startsWith('{')) return null; - try { - const parsed = JSON.parse(content); - return parsed.type === 'attachment' ? parsed : null; - } catch (e) { - return null; - } -}; - -export const parseSystemMessage = (content) => { - if (!content || !content.startsWith('{')) return null; - try { - const parsed = JSON.parse(content); - return parsed.type === 'system' ? parsed : null; - } catch (e) { - return null; - } -}; - -const VIDEO_EXT_RE = /\.(mp4|webm|ogg|mov)(\?[^\s]*)?$/i; -const isVideoUrl = (url) => VIDEO_EXT_RE.test(url); - -const getReactionIcon = (name, customEmojis = []) => { - const custom = customEmojis.find(e => e.name === name); - if (custom) return custom.src; - switch (name) { - case 'thumbsup': return thumbsupIcon; - case 'heart': return heartIcon; - case 'fire': return fireIcon; - default: { - const builtin = AllEmojis.find(e => e.name === name); - return builtin ? builtin.src : heartIcon; - } - } -}; - -const isNewDay = (current, previous) => { - if (!previous) return true; - return current.getDate() !== previous.getDate() - || current.getMonth() !== previous.getMonth() - || current.getFullYear() !== previous.getFullYear(); -}; - -const createMarkdownComponents = (openExternal) => ({ - a: ({ node, ...props }) => { - if (props.href && props.href.startsWith('rolemention://')) { - try { - const url = new URL(props.href); - const color = url.searchParams.get('color') || '#99aab5'; - return <span style={{ background: `${color}26`, borderRadius: '3px', color, fontWeight: 500, padding: '0 2px', cursor: 'default' }} title={props.children}>{props.children}</span>; - } catch { - return <span>{props.children}</span>; - } - } - if (props.href && props.href.startsWith('mention://')) return <span style={{ background: 'hsla(234.935, 85.556%, 64.706%, 0.239)', borderRadius: '3px', color: 'hsl(228.14, 100%, 83.137%)', fontWeight: 500, padding: '0 2px', cursor: 'default' }} title={props.children}>{props.children}</span>; - return <a {...props} onClick={(e) => { e.preventDefault(); openExternal(props.href); }} style={{ color: '#00b0f4', cursor: 'pointer', textDecoration: 'none' }} onMouseOver={(e) => e.target.style.textDecoration = 'underline'} onMouseOut={(e) => e.target.style.textDecoration = 'none'} />; - }, - code({ node, inline, className, children, ...props }) { - const match = /language-(\w+)/.exec(className || ''); - return !inline && match ? <SyntaxHighlighter style={oneDark} language={match[1]} PreTag="div" {...props}>{String(children).replace(/\n$/, '')}</SyntaxHighlighter> : <code className={className} {...props}>{children}</code>; - }, - p: ({ node, ...props }) => <p style={{ margin: '0 0 6px 0' }} {...props} />, - h1: ({ node, ...props }) => <h1 style={{ fontSize: '1.5rem', fontWeight: 700, margin: '12px 0 6px 0', lineHeight: 1.1 }} {...props} />, - h2: ({ node, ...props }) => <h2 style={{ fontSize: '1.25rem', fontWeight: 700, margin: '10px 0 6px 0', lineHeight: 1.1 }} {...props} />, - h3: ({ node, ...props }) => <h3 style={{ fontSize: '1.1rem', fontWeight: 700, margin: '8px 0 6px 0', lineHeight: 1.1 }} {...props} />, - ul: ({ node, ...props }) => <ul style={{ margin: '0 0 6px 0', paddingLeft: '20px' }} {...props} />, - ol: ({ node, ...props }) => <ol style={{ margin: '0 0 6px 0', paddingLeft: '20px' }} {...props} />, - li: ({ node, ...props }) => <li style={{ margin: '0' }} {...props} />, - hr: ({ node, ...props }) => <hr style={{ border: 'none', borderTop: '1px solid #4f545c', margin: '12px 0' }} {...props} />, - img: ({ node, alt, src, ...props }) => { - if (alt && alt.startsWith(':') && alt.endsWith(':')) { - return <img src={src} alt={alt} style={{ width: '48px', height: '48px', verticalAlign: 'bottom', margin: '0 1px', display: 'inline' }} />; - } - return <img alt={alt} src={src} {...props} />; - }, -}); - -const IconButton = ({ onClick, emoji }) => ( - <div onClick={(e) => { e.stopPropagation(); onClick(e); }} className="icon-button" style={{ cursor: 'pointer', padding: '6px', fontSize: '16px', lineHeight: 1, color: 'var(--header-secondary)', transition: 'background-color 0.1s' }}> - {emoji} - </div> -); - -const MessageToolbar = ({ onAddReaction, onEdit, onReply, onMore, isOwner, isAttachment }) => ( - <div className="message-toolbar"> - <Tooltip text="Thumbs Up" position="top"> - <IconButton onClick={() => onAddReaction('thumbsup')} emoji={<ColoredIcon src={thumbsupIcon} size="20px" />} /> - </Tooltip> - <Tooltip text="Heart" position="top"> - <IconButton onClick={() => onAddReaction('heart')} emoji={<ColoredIcon src={heartIcon} size="20px" />} /> - </Tooltip> - <Tooltip text="Fire" position="top"> - <IconButton onClick={() => onAddReaction('fire')} emoji={<ColoredIcon src={fireIcon} size="20px" />} /> - </Tooltip> - <div style={{ width: '1px', height: '24px', margin: '2px 4px', backgroundColor: 'hsla(240, 4%, 60.784%, 0.122)' }}></div> - <Tooltip text="Add Reaction" position="top"> - <IconButton onClick={() => onAddReaction(null)} emoji={<ColoredIcon src={EmojieIcon} color={ICON_COLOR_DEFAULT} size="20px" />} /> - </Tooltip> - {isOwner && !isAttachment && ( - <Tooltip text="Edit" position="top"> - <IconButton onClick={onEdit} emoji={<ColoredIcon src={EditIcon} color={ICON_COLOR_DEFAULT} size="20px" />} /> - </Tooltip> - )} - <Tooltip text="Reply" position="top"> - <IconButton onClick={onReply} emoji={<ColoredIcon src={ReplyIcon} color={ICON_COLOR_DEFAULT} size="20px" />} /> - </Tooltip> - <Tooltip text="More" position="top"> - <IconButton onClick={onMore} emoji={<ColoredIcon src={MoreIcon} color={ICON_COLOR_DEFAULT} size="20px" />} /> - </Tooltip> - </div> -); - -const MessageItem = React.memo(({ - msg, - isGrouped, - showDateDivider, - showUnreadDivider, - dateLabel, - isMentioned, - isOwner, - isEditing, - isHovered, - editInput, - username, - roles, - customEmojis, - onHover, - onLeave, - onContextMenu, - onAddReaction, - onEdit, - onReply, - onMore, - onEditInputChange, - onEditKeyDown, - onEditSave, - onEditCancel, - onReactionClick, - onScrollToMessage, - onProfilePopup, - onImageClick, - onLongPress, - scrollToBottom, - Attachment, - LinkPreview, - DirectVideo, -}) => { - const { links } = usePlatform(); - const markdownComponents = createMarkdownComponents(links.openExternal); - const currentDate = new Date(msg.created_at); - const userColor = getUserColor(msg.username || 'Unknown'); - - const systemMsg = parseSystemMessage(msg.content); - - const renderMessageContent = () => { - if (systemMsg) return null; - - const attachmentMetadata = parseAttachment(msg.content); - if (attachmentMetadata) { - return <Attachment metadata={attachmentMetadata} onLoad={scrollToBottom} onImageClick={onImageClick} />; - } - - const urls = extractUrls(msg.content); - const isOnlyUrl = urls.length === 1 && msg.content.trim() === urls[0]; - const isGif = isOnlyUrl && (urls[0].includes('tenor.com') || urls[0].includes('giphy.com') || urls[0].endsWith('.gif')); - const isDirectVideo = isOnlyUrl && isVideoUrl(urls[0]); - - return ( - <> - {!isGif && !isDirectVideo && ( - <ReactMarkdown remarkPlugins={[remarkGfm]} urlTransform={(url) => url} components={markdownComponents}> - {formatEmojis(formatMentions(msg.content, roles), customEmojis)} - </ReactMarkdown> - )} - {isDirectVideo && <DirectVideo src={urls[0]} marginTop={4} />} - {urls.filter(u => !(isDirectVideo && u === urls[0])).map((url, i) => ( - <LinkPreview key={i} url={url} /> - ))} - </> - ); - }; - - const renderReactions = () => { - if (!msg.reactions || Object.keys(msg.reactions).length === 0) return null; - return ( - <div style={{ display: 'flex', gap: '4px', marginTop: '4px', flexWrap: 'wrap' }}> - {Object.entries(msg.reactions).map(([emojiName, data]) => ( - <div key={emojiName} onClick={() => onReactionClick(msg.id, emojiName, data.me)} style={{ display: 'flex', alignItems: 'center', backgroundColor: data.me ? 'rgba(88, 101, 242, 0.15)' : 'hsla(240, 4%, 60.784%, 0.078)', border: data.me ? '1px solid var(--brand-experiment)' : '1px solid transparent', borderRadius: '8px', padding: '2px 6px', cursor: 'pointer', gap: '4px' }}> - <ColoredIcon src={getReactionIcon(emojiName, customEmojis)} size="16px" color={null} /> - <span style={{ fontSize: '12px', color: data.me ? '#dee0fc' : 'var(--header-secondary)', fontWeight: 600 }}>{data.count}</span> - </div> - ))} - </div> - ); - }; - - return ( - <React.Fragment> - {showUnreadDivider && ( - <div className="unread-divider" role="separator"> - <span className="unread-pill"> - <svg className="unread-pill-cap" width="8" height="13" viewBox="0 0 8 13"> - <path stroke="currentColor" fill="transparent" d="M8.16639 0.5H9C10.933 0.5 12.5 2.067 12.5 4V9C12.5 10.933 10.933 12.5 9 12.5H8.16639C7.23921 12.5 6.34992 12.1321 5.69373 11.4771L0.707739 6.5L5.69373 1.52292C6.34992 0.86789 7.23921 0.5 8.16639 0.5Z" /> - </svg> - NEW - </span> - </div> - )} - {showDateDivider && <div className="date-divider"><span>{dateLabel}</span></div>} - {systemMsg ? ( - <div id={`msg-${msg.id}`} className="system-message-row"> - <svg width="18" height="18" viewBox="0 0 24 24" fill="#23a559" style={{ flexShrink: 0 }}> - <path d="M2 7.4A5.4 5.4 0 0 1 7.4 2c.36 0 .7.22.83.55l1.93 4.64a1 1 0 0 1-.43 1.25L7 10a8.52 8.52 0 0 0 7 7l1.12-2.24a1 1 0 0 1 1.19-.51l5.06 1.56c.38.11.63.46.63.85C22 19.6 19.6 22 16.66 22h-.37C8.39 22 2 15.6 2 7.71V7.4ZM13 3a1 1 0 0 1 1-1 8 8 0 0 1 8 8 1 1 0 1 1-2 0 6 6 0 0 0-6-6 1 1 0 0 1-1-1Z"/> - <path d="M13 7a1 1 0 0 1 1-1 4 4 0 0 1 4 4 1 1 0 1 1-2 0 2 2 0 0 0-2-2 1 1 0 0 1-1-1Z"/> - </svg> - <span className="system-message-text"> - {systemMsg.text || 'System event'} - </span> - <span className="system-message-time"> - {currentDate.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })} - </span> - </div> - ) : ( - <div - id={`msg-${msg.id}`} - className={`message-item${isGrouped ? ' message-grouped' : ''}`} - style={isMentioned ? { background: 'hsla(36.894, 100%, 31.569%, 0.078)', position: 'relative' } : { position: 'relative' }} - onMouseEnter={onHover} - onMouseLeave={onLeave} - onContextMenu={onContextMenu} - onTouchStart={onLongPress?.onTouchStart} - onTouchMove={onLongPress?.onTouchMove} - onTouchEnd={onLongPress?.onTouchEnd} - > - {isMentioned && <div style={{ background: 'hsl(34, 50.847%, 53.725%)', bottom: 0, display: 'block', left: 0, pointerEvents: 'none', position: 'absolute', top: 0, width: '2px' }} />} - - {msg.replyToId && msg.replyToUsername && ( - <div className="message-reply-context" onClick={() => onScrollToMessage(msg.replyToId)}> - <div className="reply-spine" /> - <Avatar username={msg.replyToUsername} avatarUrl={msg.replyToAvatarUrl} size={16} className="reply-avatar" /> - <span className="reply-author" style={{ color: getUserColor(msg.replyToUsername) }}> - @{msg.replyToDisplayName || msg.replyToUsername} - </span> - <span className="reply-text">{msg.decryptedReply || '[Encrypted]'}</span> - </div> - )} - - {isGrouped ? ( - <div className="message-avatar-wrapper"> - </div> - ) : ( - <div className="message-avatar-wrapper"> - <Avatar - username={msg.username} - avatarUrl={msg.avatarUrl} - size={40} - className="message-avatar" - style={{ cursor: 'pointer' }} - onClick={(e) => onProfilePopup(e, msg)} - /> - </div> - )} - <div className="message-body"> - {!isGrouped && ( - <div className="message-header"> - <span - className="username" - style={{ color: userColor, cursor: 'pointer' }} - onClick={(e) => onProfilePopup(e, msg)} - > - {msg.displayName || msg.username || 'Unknown'} - </span> - {msg.isVerified === false && <span className="verification-failed" title="Signature Verification Failed!"><svg aria-hidden="true" role="img" width="16" height="16" viewBox="0 0 24 24" fill="var(--danger)"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"></path></svg></span>} - <span className="timestamp">{currentDate.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}</span> - </div> - )} - <div style={{ position: 'relative' }}> - {isEditing ? ( - <div className="message-editing"> - <textarea - className="message-edit-textarea" - value={editInput} - onChange={onEditInputChange} - onKeyDown={onEditKeyDown} - autoFocus - /> - <div className="message-edit-hint"> - escape to <span onClick={onEditCancel}>cancel</span> · enter to <span onClick={onEditSave}>save</span> - </div> - </div> - ) : ( - <div className="message-content"> - {renderMessageContent()} - {msg.editedAt && <span className="edited-indicator">(edited)</span>} - {renderReactions()} - </div> - )} - {isHovered && !isEditing && ( - <MessageToolbar isOwner={isOwner} - isAttachment={!!parseAttachment(msg.content)} - onAddReaction={onAddReaction} - onEdit={onEdit} - onReply={onReply} - onMore={onMore} - /> - )} - </div> - </div> - </div> - )} - </React.Fragment> - ); -}, (prevProps, nextProps) => { - return ( - prevProps.msg.id === nextProps.msg.id && - prevProps.msg.content === nextProps.msg.content && - prevProps.msg.editedAt === nextProps.msg.editedAt && - prevProps.msg.reactions === nextProps.msg.reactions && - prevProps.msg.decryptedReply === nextProps.msg.decryptedReply && - prevProps.msg.isVerified === nextProps.msg.isVerified && - prevProps.isHovered === nextProps.isHovered && - prevProps.isEditing === nextProps.isEditing && - prevProps.editInput === nextProps.editInput && - prevProps.isGrouped === nextProps.isGrouped && - prevProps.showDateDivider === nextProps.showDateDivider && - prevProps.showUnreadDivider === nextProps.showUnreadDivider && - prevProps.isMentioned === nextProps.isMentioned && - prevProps.roles === nextProps.roles && - prevProps.customEmojis === nextProps.customEmojis - ); -}); - -MessageItem.displayName = 'MessageItem'; - -export default MessageItem; diff --git a/packages/shared/src/components/MobileChannelDrawer.jsx b/packages/shared/src/components/MobileChannelDrawer.jsx deleted file mode 100644 index 8ded760..0000000 --- a/packages/shared/src/components/MobileChannelDrawer.jsx +++ /dev/null @@ -1,126 +0,0 @@ -import React, { useState, useRef, useCallback } from 'react'; -import ReactDOM from 'react-dom'; -import ColoredIcon from './ColoredIcon'; -import settingsIcon from '../assets/icons/settings.svg'; - -const ICON_COLOR_DEFAULT = 'hsl(240, 4.294%, 68.039%)'; - -const MobileChannelDrawer = ({ channel, isUnread, onMarkAsRead, onEditChannel, onClose }) => { - const [closing, setClosing] = useState(false); - const drawerRef = useRef(null); - const dragStartY = useRef(null); - const dragCurrentY = useRef(null); - const dragStartTime = useRef(null); - - const dismiss = useCallback(() => { - setClosing(true); - setTimeout(onClose, 200); - }, [onClose]); - - const handleAction = useCallback((cb) => { - dismiss(); - setTimeout(cb, 220); - }, [dismiss]); - - // Swipe-to-dismiss - const handleTouchStart = useCallback((e) => { - dragStartY.current = e.touches[0].clientY; - dragCurrentY.current = e.touches[0].clientY; - dragStartTime.current = Date.now(); - if (drawerRef.current) { - drawerRef.current.style.transition = 'none'; - } - }, []); - - const handleTouchMove = useCallback((e) => { - if (dragStartY.current === null) return; - dragCurrentY.current = e.touches[0].clientY; - const dy = dragCurrentY.current - dragStartY.current; - if (dy > 0 && drawerRef.current) { - drawerRef.current.style.transform = `translateY(${dy}px)`; - } - }, []); - - const handleTouchEnd = useCallback(() => { - if (dragStartY.current === null || !drawerRef.current) return; - const dy = dragCurrentY.current - dragStartY.current; - const dt = (Date.now() - dragStartTime.current) / 1000; - const velocity = dt > 0 ? dy / dt : 0; - const drawerHeight = drawerRef.current.offsetHeight; - const threshold = drawerHeight * 0.3; - - if (dy > threshold || velocity > 500) { - dismiss(); - } else { - drawerRef.current.style.transition = 'transform 0.2s ease-out'; - drawerRef.current.style.transform = 'translateY(0)'; - } - dragStartY.current = null; - }, [dismiss]); - - const isVoice = channel?.type === 'voice'; - - return ReactDOM.createPortal( - <> - <div className="mobile-drawer-overlay" onClick={dismiss} /> - <div - ref={drawerRef} - className={`mobile-drawer${closing ? ' mobile-drawer-closing' : ''}`} - onTouchStart={handleTouchStart} - onTouchMove={handleTouchMove} - onTouchEnd={handleTouchEnd} - > - <div className="mobile-drawer-handle"> - <div className="mobile-drawer-handle-bar" /> - </div> - - {/* Channel header */} - <div style={{ - padding: '4px 16px 12px', - display: 'flex', - alignItems: 'center', - gap: 8, - }}> - {isVoice ? ( - <svg width="20" height="20" viewBox="0 0 24 24" fill="var(--interactive-normal)"> - <path d="M11.383 3.07904C11.009 2.92504 10.579 3.01004 10.293 3.29604L6.586 7.00304H3C2.45 7.00304 2 7.45304 2 8.00304V16.003C2 16.553 2.45 17.003 3 17.003H6.586L10.293 20.71C10.579 20.996 11.009 21.082 11.383 20.927C11.757 20.772 12 20.407 12 20.003V4.00304C12 3.59904 11.757 3.23404 11.383 3.07904ZM14 5.00304V7.00304C16.757 7.00304 19 9.24604 19 12.003C19 14.76 16.757 17.003 14 17.003V19.003C17.86 19.003 21 15.863 21 12.003C21 8.14304 17.86 5.00304 14 5.00304ZM14 9.00304V15.003C15.654 15.003 17 13.657 17 12.003C17 10.349 15.654 9.00304 14 9.00304Z" /> - </svg> - ) : ( - <span style={{ color: 'var(--interactive-normal)', fontSize: 20, fontWeight: 500 }}>#</span> - )} - <span style={{ - color: 'var(--text-normal)', - fontSize: 16, - fontWeight: 600, - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - }}> - {channel?.name} - </span> - </div> - - {/* Actions */} - <div className="mobile-drawer-card"> - <button - className={`mobile-drawer-action${!isUnread ? ' mobile-drawer-action-disabled' : ''}`} - onClick={isUnread ? () => handleAction(onMarkAsRead) : undefined} - disabled={!isUnread} - > - <svg width="20" height="20" viewBox="0 0 24 24" fill={isUnread ? ICON_COLOR_DEFAULT : 'var(--text-muted)'}> - <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" /> - </svg> - <span>Mark As Read</span> - </button> - <button className="mobile-drawer-action" onClick={() => handleAction(onEditChannel)}> - <ColoredIcon src={settingsIcon} color={ICON_COLOR_DEFAULT} size="20px" /> - <span>Edit Channel</span> - </button> - </div> - </div> - </>, - document.body - ); -}; - -export default MobileChannelDrawer; diff --git a/packages/shared/src/components/MobileChannelSettingsScreen.jsx b/packages/shared/src/components/MobileChannelSettingsScreen.jsx deleted file mode 100644 index 5749b1b..0000000 --- a/packages/shared/src/components/MobileChannelSettingsScreen.jsx +++ /dev/null @@ -1,227 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import ReactDOM from 'react-dom'; -import { useConvex } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; - -const MobileChannelSettingsScreen = ({ channel, categories, onClose, onDelete }) => { - const [visible, setVisible] = useState(false); - const [channelName, setChannelName] = useState(channel.name); - const [channelTopic, setChannelTopic] = useState(channel.topic || ''); - const [selectedCategoryId, setSelectedCategoryId] = useState(channel.categoryId || null); - const [showCategoryPicker, setShowCategoryPicker] = useState(false); - const [confirmDelete, setConfirmDelete] = useState(false); - const [saving, setSaving] = useState(false); - const convex = useConvex(); - - useEffect(() => { - requestAnimationFrame(() => setVisible(true)); - }, []); - - const handleClose = () => { - setVisible(false); - setTimeout(onClose, 250); - }; - - const handleNameChange = (e) => { - setChannelName(e.target.value.toLowerCase().replace(/\s+/g, '-')); - }; - - const hasChanges = - channelName.trim() !== channel.name || - channelTopic.trim() !== (channel.topic || '') || - selectedCategoryId !== (channel.categoryId || null); - - const handleSave = async () => { - if (!hasChanges || saving) return; - setSaving(true); - try { - const trimmedName = channelName.trim(); - if (trimmedName && trimmedName !== channel.name) { - await convex.mutation(api.channels.rename, { id: channel._id, name: trimmedName }); - } - const trimmedTopic = channelTopic.trim(); - if (trimmedTopic !== (channel.topic || '')) { - await convex.mutation(api.channels.updateTopic, { id: channel._id, topic: trimmedTopic }); - } - if (selectedCategoryId !== (channel.categoryId || null)) { - await convex.mutation(api.channels.moveChannel, { - id: channel._id, - categoryId: selectedCategoryId || undefined, - position: 0, - }); - } - handleClose(); - } catch (err) { - console.error('Failed to save channel settings:', err); - alert('Failed to save: ' + err.message); - } finally { - setSaving(false); - } - }; - - const handleDelete = async () => { - try { - await convex.mutation(api.channels.remove, { id: channel._id }); - if (onDelete) onDelete(channel._id); - handleClose(); - } catch (err) { - console.error('Failed to delete channel:', err); - alert('Failed to delete: ' + err.message); - } - }; - - const currentCategoryName = selectedCategoryId - ? (categories || []).find(c => c._id === selectedCategoryId)?.name || 'Unknown' - : 'None'; - - return ReactDOM.createPortal( - <div className={`mobile-create-screen${visible ? ' visible' : ''}`}> - {/* Header */} - <div className="mobile-create-header"> - <button className="mobile-create-close-btn" onClick={handleClose}> - <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"> - <path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" /> - </svg> - </button> - <span className="mobile-create-title">Channel Settings</span> - <button - className={`mobile-create-submit-btn${!hasChanges || saving ? ' disabled' : ''}`} - onClick={handleSave} - disabled={!hasChanges || saving} - > - {saving ? 'Saving...' : 'Save'} - </button> - </div> - - {/* Body */} - <div className="mobile-create-body"> - {/* Channel Name */} - <div className="mobile-create-section"> - <label className="mobile-create-section-label">Channel Name</label> - <div className="mobile-create-input-wrapper"> - <span className="mobile-create-input-prefix"> - {channel.type === 'voice' ? ( - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M12 3a1 1 0 0 0-1-1h-.06a1 1 0 0 0-.74.32L5.92 7H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.92l4.28 4.68a1 1 0 0 0 .74.32H11a1 1 0 0 0 1-1z" /> - </svg> - ) : '#'} - </span> - <input - className="mobile-create-input" - type="text" - placeholder="channel-name" - value={channelName} - onChange={handleNameChange} - /> - </div> - </div> - - {/* Channel Topic */} - <div className="mobile-create-section"> - <label className="mobile-create-section-label"> - Channel Topic - <span style={{ float: 'right', fontWeight: 400, textTransform: 'none' }}> - {channelTopic.length}/1024 - </span> - </label> - <div className="mobile-create-input-wrapper" style={{ alignItems: 'flex-start' }}> - <textarea - className="mobile-create-input" - placeholder="Set a topic for this channel" - value={channelTopic} - onChange={(e) => { - if (e.target.value.length <= 1024) setChannelTopic(e.target.value); - }} - rows={3} - style={{ - resize: 'none', - fontFamily: 'inherit', - lineHeight: '1.4', - }} - /> - </div> - </div> - - {/* Category */} - <div className="mobile-create-section"> - <label className="mobile-create-section-label">Category</label> - <div - className="mobile-create-input-wrapper" - style={{ cursor: 'pointer' }} - onClick={() => setShowCategoryPicker(!showCategoryPicker)} - > - <span className="mobile-create-input" style={{ cursor: 'pointer', userSelect: 'none' }}> - {currentCategoryName} - </span> - <svg - width="20" height="20" viewBox="0 0 24 24" - fill="var(--interactive-normal)" - style={{ - marginRight: 8, - flexShrink: 0, - transform: showCategoryPicker ? 'rotate(180deg)' : 'none', - transition: 'transform 0.15s', - }} - > - <path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6z" /> - </svg> - </div> - {showCategoryPicker && ( - <div className="mobile-channel-settings-category-list"> - <div - className={`mobile-channel-settings-category-option${!selectedCategoryId ? ' selected' : ''}`} - onClick={() => { setSelectedCategoryId(null); setShowCategoryPicker(false); }} - > - None - </div> - {(categories || []).map(cat => ( - <div - key={cat._id} - className={`mobile-channel-settings-category-option${selectedCategoryId === cat._id ? ' selected' : ''}`} - onClick={() => { setSelectedCategoryId(cat._id); setShowCategoryPicker(false); }} - > - {cat.name} - </div> - ))} - </div> - )} - </div> - - {/* Delete Channel */} - <div className="mobile-create-section" style={{ marginTop: 16 }}> - {!confirmDelete ? ( - <button - className="mobile-channel-settings-delete-btn" - onClick={() => setConfirmDelete(true)} - > - Delete Channel - </button> - ) : ( - <div className="mobile-channel-settings-delete-confirm"> - <p style={{ color: '#ed4245', fontSize: 14, margin: '0 0 12px' }}> - Are you sure you want to delete <strong>#{channel.name}</strong>? This cannot be undone. - </p> - <div style={{ display: 'flex', gap: 8 }}> - <button - className="mobile-channel-settings-cancel-btn" - onClick={() => setConfirmDelete(false)} - > - Cancel - </button> - <button - className="mobile-channel-settings-delete-btn" - onClick={handleDelete} - > - Delete - </button> - </div> - </div> - )} - </div> - </div> - </div>, - document.body - ); -}; - -export default MobileChannelSettingsScreen; diff --git a/packages/shared/src/components/MobileCreateCategoryScreen.jsx b/packages/shared/src/components/MobileCreateCategoryScreen.jsx deleted file mode 100644 index 78e87de..0000000 --- a/packages/shared/src/components/MobileCreateCategoryScreen.jsx +++ /dev/null @@ -1,82 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import ReactDOM from 'react-dom'; - -const MobileCreateCategoryScreen = ({ onClose, onSubmit }) => { - const [visible, setVisible] = useState(false); - const [categoryName, setCategoryName] = useState(''); - - useEffect(() => { - requestAnimationFrame(() => setVisible(true)); - }, []); - - const handleClose = () => { - setVisible(false); - setTimeout(onClose, 250); - }; - - const handleCreate = () => { - if (!categoryName.trim()) return; - onSubmit(categoryName.trim()); - handleClose(); - }; - - return ReactDOM.createPortal( - <div className={`mobile-create-screen${visible ? ' visible' : ''}`}> - {/* Header */} - <div className="mobile-create-header"> - <button className="mobile-create-close-btn" onClick={handleClose}> - <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"> - <path d="M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z" /> - </svg> - </button> - <span className="mobile-create-title">Create Category</span> - <button - className={`mobile-create-submit-btn${!categoryName.trim() ? ' disabled' : ''}`} - onClick={handleCreate} - disabled={!categoryName.trim()} - > - Create - </button> - </div> - - {/* Body */} - <div className="mobile-create-body"> - {/* Category Name */} - <div className="mobile-create-section"> - <label className="mobile-create-section-label">Category Name</label> - <div className="mobile-create-input-wrapper"> - <input - className="mobile-create-input" - type="text" - placeholder="New Category" - value={categoryName} - onChange={(e) => setCategoryName(e.target.value)} - autoFocus - /> - </div> - </div> - - {/* Private Category Toggle */} - <div className="mobile-create-section"> - <p className="mobile-create-private-desc"> - By making a category private, only selected members and roles will be able to view this category. - </p> - <div className="mobile-create-toggle-row"> - <div className="mobile-create-toggle-left"> - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style={{ color: 'var(--interactive-normal)' }}> - <path d="M17 11V7C17 4.243 14.757 2 12 2C9.243 2 7 4.243 7 7V11C5.897 11 5 11.896 5 13V20C5 21.103 5.897 22 7 22H17C18.103 22 19 21.103 19 20V13C19 11.896 18.103 11 17 11ZM12 18C11.172 18 10.5 17.328 10.5 16.5C10.5 15.672 11.172 15 12 15C12.828 15 13.5 15.672 13.5 16.5C13.5 17.328 12.828 18 12 18ZM15 11H9V7C9 5.346 10.346 4 12 4C13.654 4 15 5.346 15 7V11Z" /> - </svg> - <span className="mobile-create-toggle-label">Private Category</span> - </div> - <div className="category-toggle-switch"> - <div className="category-toggle-knob" /> - </div> - </div> - </div> - </div> - </div>, - document.body - ); -}; - -export default MobileCreateCategoryScreen; diff --git a/packages/shared/src/components/MobileCreateChannelScreen.jsx b/packages/shared/src/components/MobileCreateChannelScreen.jsx deleted file mode 100644 index d4906cf..0000000 --- a/packages/shared/src/components/MobileCreateChannelScreen.jsx +++ /dev/null @@ -1,128 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import ReactDOM from 'react-dom'; - -const MobileCreateChannelScreen = ({ onClose, onSubmit, categoryId }) => { - const [visible, setVisible] = useState(false); - const [channelName, setChannelName] = useState(''); - const [channelType, setChannelType] = useState('text'); - - useEffect(() => { - requestAnimationFrame(() => setVisible(true)); - }, []); - - const handleClose = () => { - setVisible(false); - setTimeout(onClose, 250); - }; - - const handleCreate = () => { - if (!channelName.trim()) return; - onSubmit(channelName.trim(), channelType, categoryId); - handleClose(); - }; - - const handleNameChange = (e) => { - setChannelName(e.target.value.toLowerCase().replace(/\s+/g, '-')); - }; - - return ReactDOM.createPortal( - <div className={`mobile-create-screen${visible ? ' visible' : ''}`}> - {/* Header */} - <div className="mobile-create-header"> - <button className="mobile-create-close-btn" onClick={handleClose}> - <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"> - <path d="M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z" /> - </svg> - </button> - <span className="mobile-create-title">Create Channel</span> - <button - className={`mobile-create-submit-btn${!channelName.trim() ? ' disabled' : ''}`} - onClick={handleCreate} - disabled={!channelName.trim()} - > - Create - </button> - </div> - - {/* Body */} - <div className="mobile-create-body"> - {/* Channel Name */} - <div className="mobile-create-section"> - <label className="mobile-create-section-label">Channel Name</label> - <div className="mobile-create-input-wrapper"> - <span className="mobile-create-input-prefix"> - {channelType === 'text' ? '#' : ( - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M12 3a1 1 0 0 0-1-1h-.06a1 1 0 0 0-.74.32L5.92 7H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.92l4.28 4.68a1 1 0 0 0 .74.32H11a1 1 0 0 0 1-1z" /> - </svg> - )} - </span> - <input - className="mobile-create-input" - type="text" - placeholder="new-channel" - value={channelName} - onChange={handleNameChange} - autoFocus - /> - </div> - </div> - - {/* Channel Type */} - <div className="mobile-create-section"> - <label className="mobile-create-section-label">Channel Type</label> - <div className="mobile-create-type-list"> - <div - className={`mobile-create-type-option${channelType === 'text' ? ' selected' : ''}`} - onClick={() => setChannelType('text')} - > - <span className="mobile-create-type-icon">#</span> - <div className="mobile-create-type-info"> - <div className="mobile-create-type-name">Text</div> - <div className="mobile-create-type-desc">Send messages, images, GIFs, emoji, opinions, and puns</div> - </div> - <div className={`mobile-create-radio${channelType === 'text' ? ' selected' : ''}`}> - {channelType === 'text' && <div className="mobile-create-radio-dot" />} - </div> - </div> - <div - className={`mobile-create-type-option${channelType === 'voice' ? ' selected' : ''}`} - onClick={() => setChannelType('voice')} - > - <span className="mobile-create-type-icon"> - <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"> - <path d="M11.383 3.07904C11.009 2.92504 10.579 3.01004 10.293 3.29604L6.586 7.00304H3C2.45 7.00304 2 7.45304 2 8.00304V16.003C2 16.553 2.45 17.003 3 17.003H6.586L10.293 20.71C10.579 20.996 11.009 21.082 11.383 20.927C11.757 20.772 12 20.407 12 20.003V4.00304C12 3.59904 11.757 3.23404 11.383 3.07904ZM14 5.00304V7.00304C16.757 7.00304 19 9.24604 19 12.003C19 14.76 16.757 17.003 14 17.003V19.003C17.86 19.003 21 15.863 21 12.003C21 8.14304 17.86 5.00304 14 5.00304ZM14 9.00304V15.003C15.654 15.003 17 13.657 17 12.003C17 10.349 15.654 9.00304 14 9.00304Z" /> - </svg> - </span> - <div className="mobile-create-type-info"> - <div className="mobile-create-type-name">Voice</div> - <div className="mobile-create-type-desc">Hang out together with voice, video, and screen share</div> - </div> - <div className={`mobile-create-radio${channelType === 'voice' ? ' selected' : ''}`}> - {channelType === 'voice' && <div className="mobile-create-radio-dot" />} - </div> - </div> - </div> - </div> - - {/* Private Channel Toggle */} - <div className="mobile-create-section"> - <div className="mobile-create-toggle-row"> - <div className="mobile-create-toggle-left"> - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style={{ color: 'var(--interactive-normal)' }}> - <path d="M17 11V7C17 4.243 14.757 2 12 2C9.243 2 7 4.243 7 7V11C5.897 11 5 11.896 5 13V20C5 21.103 5.897 22 7 22H17C18.103 22 19 21.103 19 20V13C19 11.896 18.103 11 17 11ZM12 18C11.172 18 10.5 17.328 10.5 16.5C10.5 15.672 11.172 15 12 15C12.828 15 13.5 15.672 13.5 16.5C13.5 17.328 12.828 18 12 18ZM15 11H9V7C9 5.346 10.346 4 12 4C13.654 4 15 5.346 15 7V11Z" /> - </svg> - <span className="mobile-create-toggle-label">Private Channel</span> - </div> - <div className="category-toggle-switch"> - <div className="category-toggle-knob" /> - </div> - </div> - </div> - </div> - </div>, - document.body - ); -}; - -export default MobileCreateChannelScreen; diff --git a/packages/shared/src/components/MobileMembersScreen.jsx b/packages/shared/src/components/MobileMembersScreen.jsx deleted file mode 100644 index 6c70d77..0000000 --- a/packages/shared/src/components/MobileMembersScreen.jsx +++ /dev/null @@ -1,208 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import ReactDOM from 'react-dom'; -import { useQuery } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; -import { useOnlineUsers } from '../contexts/PresenceContext'; -import { useVoice } from '../contexts/VoiceContext'; -import { CrownIcon, SharingIcon } from '../assets/icons'; -import ColoredIcon from './ColoredIcon'; - -const USER_COLORS = ['#5865F2', '#EBA7CD', '#57F287', '#FEE75C', '#EB459E', '#ED4245']; - -function getUserColor(name) { - let hash = 0; - for (let i = 0; i < name.length; i++) { - hash = name.charCodeAt(i) + ((hash << 5) - hash); - } - return USER_COLORS[Math.abs(hash) % USER_COLORS.length]; -} - -const STATUS_COLORS = { - online: '#3ba55c', - idle: '#faa61a', - dnd: '#ed4245', - invisible: '#747f8d', - offline: '#747f8d', -}; - -const TABS = ['Members', 'Media', 'Pins', 'Threads', 'Links', 'Files']; - -const MobileMembersScreen = ({ channelId, channelName, onClose }) => { - const [activeTab, setActiveTab] = useState('Members'); - const [visible, setVisible] = useState(false); - const members = useQuery( - api.members.getChannelMembers, - channelId ? { channelId } : "skip" - ) || []; - const { resolveStatus } = useOnlineUsers(); - const { voiceStates } = useVoice(); - - const usersInVoice = new Set(); - const usersScreenSharing = new Set(); - Object.values(voiceStates).forEach(users => { - users.forEach(u => { - usersInVoice.add(u.userId); - if (u.isScreenSharing) usersScreenSharing.add(u.userId); - }); - }); - - useEffect(() => { - requestAnimationFrame(() => setVisible(true)); - }, []); - - const handleClose = () => { - setVisible(false); - setTimeout(onClose, 250); - }; - - const onlineMembers = members.filter(m => resolveStatus(m.status, m.id) !== 'offline'); - const offlineMembers = members.filter(m => resolveStatus(m.status, m.id) === 'offline'); - - const roleGroups = {}; - const ungrouped = []; - - onlineMembers.forEach(member => { - const hoistedRole = member.roles.find(r => r.isHoist && r.name !== '@everyone' && r.name !== 'Owner'); - if (hoistedRole) { - const key = `${hoistedRole.position}_${hoistedRole.name}`; - if (!roleGroups[key]) { - roleGroups[key] = { role: hoistedRole, members: [] }; - } - roleGroups[key].members.push(member); - } else { - ungrouped.push(member); - } - }); - - const sortedGroups = Object.values(roleGroups).sort((a, b) => b.role.position - a.role.position); - - const renderMember = (member) => { - const displayRole = member.roles.find(r => r.name !== '@everyone' && r.name !== 'Owner') || null; - const nameColor = displayRole ? displayRole.color : '#fff'; - const isOwner = member.roles.some(r => r.name === 'Owner'); - const effectiveStatus = resolveStatus(member.status, member.id); - - return ( - <div - key={member.id} - className="mobile-member-item" - style={effectiveStatus === 'offline' ? { opacity: 0.3 } : {}} - > - <div className="member-avatar-wrapper"> - {member.avatarUrl ? ( - <img - className="member-avatar" - src={member.avatarUrl} - alt={member.username} - style={{ objectFit: 'cover' }} - /> - ) : ( - <div - className="member-avatar" - style={{ backgroundColor: getUserColor(member.username) }} - > - {(member.displayName || member.username).substring(0, 1).toUpperCase()} - </div> - )} - <div - className="member-status-dot" - style={{ backgroundColor: STATUS_COLORS[effectiveStatus] || STATUS_COLORS.offline }} - /> - </div> - <div className="member-info"> - <span className="member-name" style={{ color: nameColor, display: 'flex', alignItems: 'center', gap: '4px' }}> - {member.displayName || member.username} - {isOwner && <ColoredIcon src={CrownIcon} color="var(--text-feedback-warning)" size="14px" />} - </span> - {usersScreenSharing.has(member.id) ? ( - <div className="member-screen-sharing-indicator"> - <img src={SharingIcon} alt="" /> - Sharing their screen - </div> - ) : usersInVoice.has(member.id) ? ( - <div className="member-voice-indicator"> - <svg viewBox="0 0 24 24" fill="#3ba55c"> - <path d="M12 3a1 1 0 0 0-1-1h-.06a1 1 0 0 0-.74.32L5.92 7H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.92l4.28 4.68a1 1 0 0 0 .74.32H11a1 1 0 0 0 1-1zm3.1 17.75c-.58.14-1.1-.33-1.1-.92v-.03c0-.5.37-.92.85-1.05a7 7 0 0 0 0-13.5A1.11 1.11 0 0 1 14 4.2v-.03c0-.6.52-1.06 1.1-.92a9 9 0 0 1 0 17.5" /> - <path d="M15.16 16.51c-.57.28-1.16-.2-1.16-.83v-.14c0-.43.28-.8.63-1.02a3 3 0 0 0 0-5.04c-.35-.23-.63-.6-.63-1.02v-.14c0-.63.59-1.1 1.16-.83a5 5 0 0 1 0 9.02" /> - </svg> - In Voice - </div> - ) : member.customStatus ? ( - <div style={{ fontSize: '12px', color: 'var(--text-muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}> - {member.customStatus} - </div> - ) : null} - </div> - </div> - ); - }; - - return ReactDOM.createPortal( - <div className={`mobile-members-screen${visible ? ' visible' : ''}`}> - <div className="mobile-members-header"> - <button className="mobile-members-back" onClick={handleClose}> - <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"> - <path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" /> - </svg> - </button> - <div className="mobile-members-header-info"> - <div className="mobile-members-header-name"> - <span style={{ color: 'var(--text-muted)', marginRight: 4 }}>#</span> - {channelName} - </div> - <div className="mobile-members-header-subtitle">Text Channel</div> - </div> - </div> - <div className="mobile-members-tabs"> - {TABS.map(tab => ( - <button - key={tab} - className={`mobile-members-tab${activeTab === tab ? ' active' : ''}`} - onClick={() => setActiveTab(tab)} - > - {tab} - </button> - ))} - </div> - <div className="mobile-members-content"> - {activeTab === 'Members' ? ( - <> - {sortedGroups.map(group => ( - <React.Fragment key={group.role.name}> - <div className="members-role-header"> - {group.role.name} — {group.members.length} - </div> - {group.members.map(renderMember)} - </React.Fragment> - ))} - {ungrouped.length > 0 && ( - <> - <div className="members-role-header"> - ONLINE — {ungrouped.length} - </div> - {ungrouped.map(renderMember)} - </> - )} - {offlineMembers.length > 0 && ( - <> - <div className="members-role-header"> - OFFLINE — {offlineMembers.length} - </div> - {offlineMembers.map(renderMember)} - </> - )} - </> - ) : ( - <div className="mobile-members-placeholder"> - <span style={{ color: 'var(--text-muted)', fontSize: 14 }}> - {activeTab} coming soon - </span> - </div> - )} - </div> - </div>, - document.body - ); -}; - -export default MobileMembersScreen; diff --git a/packages/shared/src/components/MobileMessageDrawer.jsx b/packages/shared/src/components/MobileMessageDrawer.jsx deleted file mode 100644 index e030006..0000000 --- a/packages/shared/src/components/MobileMessageDrawer.jsx +++ /dev/null @@ -1,158 +0,0 @@ -import React, { useState, useRef, useCallback } from 'react'; -import ReactDOM from 'react-dom'; -import { EmojieIcon, EditIcon, ReplyIcon, DeleteIcon, PinIcon } from '../assets/icons'; -import { getEmojiUrl } from '../assets/emojis'; -import ColoredIcon from './ColoredIcon'; - -const ICON_COLOR_DEFAULT = 'hsl(240, 4.294%, 68.039%)'; - -const QUICK_REACTIONS = [ - { name: 'thumbsup', category: 'people' }, - { name: 'fire', category: 'nature' }, - { name: 'heart', category: 'symbols' }, - { name: 'joy', category: 'people' }, - { name: 'sob', category: 'people' }, - { name: 'eyes', category: 'people' }, -]; - -const CopyIcon = () => ( - <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"> - <path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/> - </svg> -); - -const MobileMessageDrawer = ({ message, isOwner, isAttachment, canDelete, onClose, onAction, onQuickReaction }) => { - const [closing, setClosing] = useState(false); - const drawerRef = useRef(null); - const dragStartY = useRef(null); - const dragCurrentY = useRef(null); - const dragStartTime = useRef(null); - - const dismiss = useCallback(() => { - setClosing(true); - setTimeout(onClose, 200); - }, [onClose]); - - const handleAction = useCallback((action) => { - onAction(action); - dismiss(); - }, [onAction, dismiss]); - - const handleQuickReaction = useCallback((name) => { - onQuickReaction(name); - dismiss(); - }, [onQuickReaction, dismiss]); - - // Swipe-to-dismiss - const handleTouchStart = useCallback((e) => { - dragStartY.current = e.touches[0].clientY; - dragCurrentY.current = e.touches[0].clientY; - dragStartTime.current = Date.now(); - if (drawerRef.current) { - drawerRef.current.style.transition = 'none'; - } - }, []); - - const handleTouchMove = useCallback((e) => { - if (dragStartY.current === null) return; - dragCurrentY.current = e.touches[0].clientY; - const dy = dragCurrentY.current - dragStartY.current; - if (dy > 0 && drawerRef.current) { - drawerRef.current.style.transform = `translateY(${dy}px)`; - } - }, []); - - const handleTouchEnd = useCallback(() => { - if (dragStartY.current === null || !drawerRef.current) return; - const dy = dragCurrentY.current - dragStartY.current; - const dt = (Date.now() - dragStartTime.current) / 1000; - const velocity = dt > 0 ? dy / dt : 0; - const drawerHeight = drawerRef.current.offsetHeight; - const threshold = drawerHeight * 0.3; - - if (dy > threshold || velocity > 500) { - dismiss(); - } else { - drawerRef.current.style.transition = 'transform 0.2s ease-out'; - drawerRef.current.style.transform = 'translateY(0)'; - } - dragStartY.current = null; - }, [dismiss]); - - const isPinned = message?.pinned; - - return ReactDOM.createPortal( - <> - <div className="mobile-drawer-overlay" onClick={dismiss} /> - <div - ref={drawerRef} - className={`mobile-drawer${closing ? ' mobile-drawer-closing' : ''}`} - onTouchStart={handleTouchStart} - onTouchMove={handleTouchMove} - onTouchEnd={handleTouchEnd} - > - <div className="mobile-drawer-handle"> - <div className="mobile-drawer-handle-bar" /> - </div> - - {/* Quick reactions */} - <div className="mobile-drawer-reactions"> - {QUICK_REACTIONS.map(({ name, category }) => ( - <button - key={name} - className="mobile-drawer-reaction-btn" - onClick={() => handleQuickReaction(name)} - > - <ColoredIcon src={getEmojiUrl(category, name)} size="24px" color={null} /> - </button> - ))} - <button - className="mobile-drawer-reaction-btn" - onClick={() => handleAction('reaction')} - > - <ColoredIcon src={EmojieIcon} color={ICON_COLOR_DEFAULT} size="22px" /> - </button> - </div> - - <div className="mobile-drawer-separator" /> - - {/* Primary actions */} - <div className="mobile-drawer-card"> - {isOwner && !isAttachment && ( - <button className="mobile-drawer-action" onClick={() => handleAction('edit')}> - <ColoredIcon src={EditIcon} color={ICON_COLOR_DEFAULT} size="20px" /> - <span>Edit Message</span> - </button> - )} - <button className="mobile-drawer-action" onClick={() => handleAction('reply')}> - <ColoredIcon src={ReplyIcon} color={ICON_COLOR_DEFAULT} size="20px" /> - <span>Reply</span> - </button> - {!isAttachment && ( - <button className="mobile-drawer-action" onClick={() => handleAction('copy')}> - <CopyIcon /> - <span>Copy Text</span> - </button> - )} - <button className="mobile-drawer-action" onClick={() => handleAction('pin')}> - <ColoredIcon src={PinIcon} color={ICON_COLOR_DEFAULT} size="20px" /> - <span>{isPinned ? 'Unpin Message' : 'Pin Message'}</span> - </button> - </div> - - {/* Danger actions */} - {canDelete && ( - <div className="mobile-drawer-card"> - <button className="mobile-drawer-action mobile-drawer-action-danger" onClick={() => handleAction('delete')}> - <ColoredIcon src={DeleteIcon} color="#ed4245" size="20px" /> - <span>Delete Message</span> - </button> - </div> - )} - </div> - </>, - document.body - ); -}; - -export default MobileMessageDrawer; diff --git a/packages/shared/src/components/MobileSearchScreen.jsx b/packages/shared/src/components/MobileSearchScreen.jsx deleted file mode 100644 index a5c59ab..0000000 --- a/packages/shared/src/components/MobileSearchScreen.jsx +++ /dev/null @@ -1,464 +0,0 @@ -import React, { useState, useEffect, useMemo, useRef, useCallback } from 'react'; -import ReactDOM from 'react-dom'; -import { useQuery } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; -import { useOnlineUsers } from '../contexts/PresenceContext'; -import { useSearch } from '../contexts/SearchContext'; -import { usePlatform } from '../platform'; -import { LinkPreview } from './ChatArea'; -import { extractUrls } from './MessageItem'; -import Avatar from './Avatar'; -import { - formatTime, escapeHtml, linkifyHtml, formatEmojisHtml, getAvatarColor, - SearchResultImage, SearchResultVideo, SearchResultFile -} from '../utils/searchRendering'; - -const USER_COLORS = ['#5865F2', '#EBA7CD', '#57F287', '#FEE75C', '#EB459E', '#ED4245']; - -function getUserColor(name) { - let hash = 0; - for (let i = 0; i < name.length; i++) { - hash = name.charCodeAt(i) + ((hash << 5) - hash); - } - return USER_COLORS[Math.abs(hash) % USER_COLORS.length]; -} - -const STATUS_COLORS = { - online: '#3ba55c', - idle: '#faa61a', - dnd: '#ed4245', - invisible: '#747f8d', - offline: '#747f8d', -}; - -const BROWSE_TABS = ['Recent', 'Members', 'Channels']; -const SEARCH_TABS = ['Messages', 'Media', 'Links', 'Files']; - -function formatTimeAgo(timestamp) { - if (!timestamp) return ''; - const now = Date.now(); - const diff = now - timestamp; - const minutes = Math.floor(diff / 60000); - if (minutes < 1) return 'Active just now'; - if (minutes < 60) return `Active ${minutes}m ago`; - const hours = Math.floor(minutes / 60); - if (hours < 24) return `Active ${hours}h ago`; - const days = Math.floor(hours / 24); - if (days === 1) return 'Active 1d ago'; - return `Active ${days}d ago`; -} - -const MobileSearchScreen = ({ channels, allMembers, serverName, onClose, onSelectChannel, onJumpToMessage }) => { - const [activeTab, setActiveTab] = useState('Recent'); - const [visible, setVisible] = useState(false); - const [searchText, setSearchText] = useState(''); - const { resolveStatus } = useOnlineUsers(); - const { search, isReady } = useSearch() || {}; - const { links } = usePlatform(); - const customEmojis = useQuery(api.customEmojis.list) || []; - - // Search result state - const [messageResults, setMessageResults] = useState([]); - const [mediaResults, setMediaResults] = useState([]); - const [linkResults, setLinkResults] = useState([]); - const [fileResults, setFileResults] = useState([]); - const [searching, setSearching] = useState(false); - const searchTimerRef = useRef(null); - - const channelIds = useMemo(() => channels.map(c => c._id), [channels]); - const latestTimestampsRaw = useQuery( - api.readState.getLatestMessageTimestamps, - channelIds.length > 0 ? { channelIds } : "skip" - ) || []; - const latestTimestamps = useMemo(() => { - const map = {}; - for (const item of latestTimestampsRaw) { - map[item.channelId] = item.latestTimestamp; - } - return map; - }, [latestTimestampsRaw]); - - const serverChannelIds = useMemo(() => new Set(channels.map(c => c._id)), [channels]); - - const channelMap = useMemo(() => { - const map = {}; - for (const c of channels) map[c._id] = c.name; - return map; - }, [channels]); - - // Determine mode based on search text - const hasQuery = searchText.trim().length > 0; - - useEffect(() => { - requestAnimationFrame(() => setVisible(true)); - }, []); - - // Debounced search execution - useEffect(() => { - if (searchTimerRef.current) clearTimeout(searchTimerRef.current); - - if (!hasQuery || !search || !isReady) { - setMessageResults([]); - setMediaResults([]); - setLinkResults([]); - setFileResults([]); - setSearching(false); - return; - } - - setSearching(true); - searchTimerRef.current = setTimeout(() => { - const q = searchText.trim(); - const filterToServer = (results) => results.filter(r => serverChannelIds.has(r.channel_id)); - - // Messages search - const msgs = filterToServer(search({ query: q, limit: 50 })); - msgs.sort((a, b) => b.created_at - a.created_at); - setMessageResults(msgs); - - // Media search (images + videos, deduped) - const images = filterToServer(search({ query: q, hasImage: true, limit: 50 })); - const videos = filterToServer(search({ query: q, hasVideo: true, limit: 50 })); - const mediaMap = new Map(); - for (const r of [...images, ...videos]) mediaMap.set(r.id, r); - const media = Array.from(mediaMap.values()); - media.sort((a, b) => b.created_at - a.created_at); - setMediaResults(media); - - // Links search - const lnks = filterToServer(search({ query: q, hasLink: true, limit: 50 })); - lnks.sort((a, b) => b.created_at - a.created_at); - setLinkResults(lnks); - - // Files search - const files = filterToServer(search({ query: q, hasFile: true, limit: 50 })); - files.sort((a, b) => b.created_at - a.created_at); - setFileResults(files); - - setSearching(false); - }, 300); - - return () => { if (searchTimerRef.current) clearTimeout(searchTimerRef.current); }; - }, [searchText, hasQuery, search, isReady, serverChannelIds]); - - // Reset to first search tab when entering search mode - useEffect(() => { - if (hasQuery) { - setActiveTab('Messages'); - } else { - setActiveTab('Recent'); - } - }, [hasQuery]); - - const handleClose = () => { - setVisible(false); - setTimeout(onClose, 250); - }; - - const handleSelectChannel = (channelId) => { - setVisible(false); - setTimeout(() => { - onSelectChannel(channelId); - onClose(); - }, 250); - }; - - const handleResultClick = useCallback((result) => { - if (onJumpToMessage) { - setVisible(false); - setTimeout(() => { - onJumpToMessage(result.channel_id, result.id); - }, 250); - } - }, [onJumpToMessage]); - - const query = searchText.toLowerCase().trim(); - - // Browse mode data - const recentChannels = useMemo(() => { - const textChannels = channels.filter(c => c.type === 'text'); - return textChannels - .map(c => ({ ...c, lastActivity: latestTimestamps[c._id] || 0 })) - .sort((a, b) => b.lastActivity - a.lastActivity) - .filter(c => !query || c.name.toLowerCase().includes(query)); - }, [channels, latestTimestamps, query]); - - const filteredMembers = useMemo(() => { - if (!query) return allMembers; - return allMembers.filter(m => - m.username.toLowerCase().includes(query) || - (m.displayName && m.displayName.toLowerCase().includes(query)) - ); - }, [allMembers, query]); - - const filteredChannels = useMemo(() => { - if (!query) return channels; - return channels.filter(c => c.name.toLowerCase().includes(query)); - }, [channels, query]); - - // Group results by channel - const groupByChannel = useCallback((results) => { - const grouped = {}; - for (const r of results) { - const chName = channelMap[r.channel_id] || 'Unknown'; - if (!grouped[chName]) grouped[chName] = []; - grouped[chName].push(r); - } - return grouped; - }, [channelMap]); - - const renderSearchResult = useCallback((r) => ( - <div - key={r.id} - className="mobile-search-result-item" - onClick={() => handleResultClick(r)} - > - <div - className="mobile-search-result-avatar" - style={{ backgroundColor: getAvatarColor(r.username) }} - > - {r.username?.[0]?.toUpperCase()} - </div> - <div className="mobile-search-result-body"> - <div className="mobile-search-result-header"> - <span className="mobile-search-result-username" style={{ color: getAvatarColor(r.username) }}> - {r.username} - </span> - <span className="mobile-search-result-time">{formatTime(r.created_at)}</span> - </div> - {!(r.has_attachment && r.attachment_meta) && ( - <div - className="mobile-search-result-content" - dangerouslySetInnerHTML={{ __html: formatEmojisHtml(linkifyHtml(r.snippet || escapeHtml(r.content)), customEmojis) }} - onClick={(e) => { - if (e.target.tagName === 'A' && e.target.href) { - e.preventDefault(); - e.stopPropagation(); - links.openExternal(e.target.href); - } - }} - /> - )} - {r.has_attachment && r.attachment_meta ? (() => { - try { - const meta = JSON.parse(r.attachment_meta); - if (r.attachment_type?.startsWith('image/')) return <SearchResultImage metadata={meta} />; - if (r.attachment_type?.startsWith('video/')) return <SearchResultVideo metadata={meta} />; - return <SearchResultFile metadata={meta} />; - } catch { return <span className="search-result-badge">File</span>; } - })() : r.has_attachment ? <span className="search-result-badge">File</span> : null} - {r.has_link && r.content && (() => { - const urls = extractUrls(r.content); - return urls.map((url, i) => <LinkPreview key={i} url={url} />); - })()} - </div> - </div> - ), [handleResultClick, customEmojis, links]); - - const renderGroupedResults = useCallback((results) => { - if (searching) { - return <div className="mobile-search-empty">Searching...</div>; - } - if (!isReady) { - return <div className="mobile-search-empty">Search database is loading...</div>; - } - if (results.length === 0) { - return ( - <div className="mobile-search-empty"> - <svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor" style={{ opacity: 0.3, marginBottom: 8 }}> - <path d="M21.71 20.29L18 16.61A9 9 0 1016.61 18l3.68 3.68a1 1 0 001.42 0 1 1 0 000-1.39zM11 18a7 7 0 110-14 7 7 0 010 14z"/> - </svg> - <div>No results found</div> - </div> - ); - } - const grouped = groupByChannel(results); - return ( - <div className="mobile-search-results"> - {Object.entries(grouped).map(([chName, msgs]) => ( - <div key={chName} className="mobile-search-channel-group"> - <div className="mobile-search-channel-group-header">#{chName}</div> - {msgs.map(renderSearchResult)} - </div> - ))} - </div> - ); - }, [searching, isReady, groupByChannel, renderSearchResult]); - - const renderContent = () => { - // Search mode - if (hasQuery) { - switch (activeTab) { - case 'Messages': return renderGroupedResults(messageResults); - case 'Media': return renderGroupedResults(mediaResults); - case 'Links': return renderGroupedResults(linkResults); - case 'Files': return renderGroupedResults(fileResults); - default: return renderGroupedResults(messageResults); - } - } - - // Browse mode - switch (activeTab) { - case 'Recent': - return ( - <div className="mobile-search-section"> - <div className="mobile-search-section-title">Suggested</div> - {recentChannels.length === 0 ? ( - <div className="mobile-search-empty">No channels found</div> - ) : ( - recentChannels.map(channel => ( - <button - key={channel._id} - className="mobile-search-channel-item" - onClick={() => handleSelectChannel(channel._id)} - > - <span className="mobile-search-channel-hash">#</span> - <div className="mobile-search-channel-info"> - <span className="mobile-search-channel-name">{channel.name}</span> - <span className="mobile-search-channel-activity"> - {formatTimeAgo(channel.lastActivity)} - </span> - </div> - </button> - )) - )} - </div> - ); - - case 'Members': - return ( - <div className="mobile-search-section"> - {filteredMembers.length === 0 ? ( - <div className="mobile-search-empty">No members found</div> - ) : ( - filteredMembers.map(member => { - const effectiveStatus = resolveStatus(member.status, member.id); - return ( - <div key={member.id} className="mobile-search-member-item"> - <div className="member-avatar-wrapper"> - {member.avatarUrl ? ( - <img - className="member-avatar" - src={member.avatarUrl} - alt={member.username} - style={{ objectFit: 'cover' }} - /> - ) : ( - <div - className="member-avatar" - style={{ backgroundColor: getUserColor(member.username) }} - > - {(member.displayName || member.username).substring(0, 1).toUpperCase()} - </div> - )} - <div - className="member-status-dot" - style={{ backgroundColor: STATUS_COLORS[effectiveStatus] || STATUS_COLORS.offline }} - /> - </div> - <span className="mobile-search-member-name"> - {member.displayName || member.username} - </span> - </div> - ); - }) - )} - </div> - ); - - case 'Channels': - return ( - <div className="mobile-search-section"> - {filteredChannels.length === 0 ? ( - <div className="mobile-search-empty">No channels found</div> - ) : ( - filteredChannels.map(channel => ( - <button - key={channel._id} - className="mobile-search-channel-item" - onClick={() => handleSelectChannel(channel._id)} - > - <span className="mobile-search-channel-hash"> - {channel.type === 'voice' ? ( - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M12 3a1 1 0 0 0-1-1h-.06a1 1 0 0 0-.74.32L5.92 7H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.92l4.28 4.68a1 1 0 0 0 .74.32H11a1 1 0 0 0 1-1z" /> - </svg> - ) : '#'} - </span> - <span className="mobile-search-channel-name">{channel.name}</span> - </button> - )) - )} - </div> - ); - - default: - return ( - <div className="mobile-search-empty"> - {activeTab} coming soon - </div> - ); - } - }; - - const currentTabs = hasQuery ? SEARCH_TABS : BROWSE_TABS; - const getTabLabel = (tab) => { - if (!hasQuery) return tab; - switch (tab) { - case 'Messages': return `Messages${!searching ? ` (${messageResults.length})` : ''}`; - case 'Media': return `Media${!searching ? ` (${mediaResults.length})` : ''}`; - case 'Links': return `Links${!searching ? ` (${linkResults.length})` : ''}`; - case 'Files': return `Files${!searching ? ` (${fileResults.length})` : ''}`; - default: return tab; - } - }; - - return ReactDOM.createPortal( - <div className={`mobile-search-screen${visible ? ' visible' : ''}`}> - <div className="mobile-search-header"> - <button className="mobile-search-back" onClick={handleClose}> - <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"> - <path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" /> - </svg> - </button> - <div className="mobile-search-input-wrapper"> - <svg className="mobile-search-input-icon" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" /> - </svg> - <input - className="mobile-search-input" - type="text" - placeholder="Search" - value={searchText} - onChange={(e) => setSearchText(e.target.value)} - autoFocus - /> - {searchText && ( - <button className="mobile-search-clear" onClick={() => setSearchText('')}> - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" /> - </svg> - </button> - )} - </div> - </div> - <div className="mobile-search-tabs"> - {currentTabs.map(tab => ( - <button - key={tab} - className={`mobile-search-tab${activeTab === tab ? ' active' : ''}`} - onClick={() => setActiveTab(tab)} - > - {getTabLabel(tab)} - </button> - ))} - </div> - <div className="mobile-search-content"> - {renderContent()} - </div> - </div>, - document.body - ); -}; - -export default MobileSearchScreen; diff --git a/packages/shared/src/components/MobileServerDrawer.jsx b/packages/shared/src/components/MobileServerDrawer.jsx deleted file mode 100644 index b79b0a7..0000000 --- a/packages/shared/src/components/MobileServerDrawer.jsx +++ /dev/null @@ -1,124 +0,0 @@ -import React, { useState, useRef, useCallback } from 'react'; -import ReactDOM from 'react-dom'; -import ColoredIcon from './ColoredIcon'; -import inviteUserIcon from '../assets/icons/invite_user.svg'; -import settingsIcon from '../assets/icons/settings.svg'; -import createIcon from '../assets/icons/create.svg'; -import createCategoryIcon from '../assets/icons/create_category.svg'; - -const ICON_COLOR_DEFAULT = 'hsl(240, 4.294%, 68.039%)'; - -const MobileServerDrawer = ({ serverName, serverIconUrl, memberCount, onInvite, onSettings, onCreateChannel, onCreateCategory, onClose }) => { - const [closing, setClosing] = useState(false); - const drawerRef = useRef(null); - const dragStartY = useRef(null); - const dragCurrentY = useRef(null); - const dragStartTime = useRef(null); - - const dismiss = useCallback(() => { - setClosing(true); - setTimeout(onClose, 200); - }, [onClose]); - - const handleAction = useCallback((cb) => { - dismiss(); - setTimeout(cb, 220); - }, [dismiss]); - - // Swipe-to-dismiss - const handleTouchStart = useCallback((e) => { - dragStartY.current = e.touches[0].clientY; - dragCurrentY.current = e.touches[0].clientY; - dragStartTime.current = Date.now(); - if (drawerRef.current) { - drawerRef.current.style.transition = 'none'; - } - }, []); - - const handleTouchMove = useCallback((e) => { - if (dragStartY.current === null) return; - dragCurrentY.current = e.touches[0].clientY; - const dy = dragCurrentY.current - dragStartY.current; - if (dy > 0 && drawerRef.current) { - drawerRef.current.style.transform = `translateY(${dy}px)`; - } - }, []); - - const handleTouchEnd = useCallback(() => { - if (dragStartY.current === null || !drawerRef.current) return; - const dy = dragCurrentY.current - dragStartY.current; - const dt = (Date.now() - dragStartTime.current) / 1000; - const velocity = dt > 0 ? dy / dt : 0; - const drawerHeight = drawerRef.current.offsetHeight; - const threshold = drawerHeight * 0.3; - - if (dy > threshold || velocity > 500) { - dismiss(); - } else { - drawerRef.current.style.transition = 'transform 0.2s ease-out'; - drawerRef.current.style.transform = 'translateY(0)'; - } - dragStartY.current = null; - }, [dismiss]); - - const initials = serverName ? serverName.split(/\s+/).map(w => w[0]).join('').slice(0, 2).toUpperCase() : '?'; - - return ReactDOM.createPortal( - <> - <div className="mobile-drawer-overlay" onClick={dismiss} /> - <div - ref={drawerRef} - className={`mobile-drawer${closing ? ' mobile-drawer-closing' : ''}`} - onTouchStart={handleTouchStart} - onTouchMove={handleTouchMove} - onTouchEnd={handleTouchEnd} - > - <div className="mobile-drawer-handle"> - <div className="mobile-drawer-handle-bar" /> - </div> - - {/* Server header */} - <div className="mobile-server-drawer-header"> - <div className="mobile-server-drawer-icon"> - {serverIconUrl ? ( - <img src={serverIconUrl} alt={serverName} /> - ) : ( - <span>{initials}</span> - )} - </div> - <div> - <div className="mobile-server-drawer-name">{serverName}</div> - <div className="mobile-server-drawer-members">{memberCount} {memberCount === 1 ? 'member' : 'members'}</div> - </div> - </div> - - {/* Actions card 1 */} - <div className="mobile-drawer-card"> - <button className="mobile-drawer-action" onClick={() => handleAction(onInvite)}> - <ColoredIcon src={inviteUserIcon} color={ICON_COLOR_DEFAULT} size="20px" /> - <span>Invite People</span> - </button> - <button className="mobile-drawer-action" onClick={() => handleAction(onSettings)}> - <ColoredIcon src={settingsIcon} color={ICON_COLOR_DEFAULT} size="20px" /> - <span>Server Settings</span> - </button> - </div> - - {/* Actions card 2 */} - <div className="mobile-drawer-card"> - <button className="mobile-drawer-action" onClick={() => handleAction(onCreateChannel)}> - <ColoredIcon src={createIcon} color={ICON_COLOR_DEFAULT} size="20px" /> - <span>Create Channel</span> - </button> - <button className="mobile-drawer-action" onClick={() => handleAction(onCreateCategory)}> - <ColoredIcon src={createCategoryIcon} color={ICON_COLOR_DEFAULT} size="20px" /> - <span>Create Category</span> - </button> - </div> - </div> - </>, - document.body - ); -}; - -export default MobileServerDrawer; diff --git a/packages/shared/src/components/PinnedMessagesPanel.jsx b/packages/shared/src/components/PinnedMessagesPanel.jsx deleted file mode 100644 index aea386d..0000000 --- a/packages/shared/src/components/PinnedMessagesPanel.jsx +++ /dev/null @@ -1,199 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useQuery, useMutation } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; -import MessageItem from './MessageItem'; -import { usePlatform } from '../platform'; - -const TAG_LENGTH = 32; -const EMPTY = []; - -const PinnedMessagesPanel = ({ - channelId, - visible, - onClose, - channelKey, - onJumpToMessage, - userId, - username, - roles, - Attachment, - LinkPreview, - DirectVideo, - onReactionClick, - onProfilePopup, - onImageClick, -}) => { - const { crypto } = usePlatform(); - const [decryptedPins, setDecryptedPins] = useState([]); - - const pinnedMessages = useQuery( - api.messages.listPinned, - channelId ? { channelId, userId: userId || undefined } : "skip" - ) || EMPTY; - - const unpinMutation = useMutation(api.messages.pin); - - useEffect(() => { - if (!pinnedMessages.length || !channelKey) { - setDecryptedPins(prev => prev.length === 0 ? prev : []); - return; - } - - let cancelled = false; - - const decryptAll = async () => { - // Build batch arrays for message decryption - const decryptItems = []; - const decryptMsgMap = []; - const replyDecryptItems = []; - const replyMsgMap = []; - const verifyItems = []; - const verifyMsgMap = []; - - for (const msg of pinnedMessages) { - if (msg.ciphertext && msg.ciphertext.length >= TAG_LENGTH) { - const tag = msg.ciphertext.slice(-TAG_LENGTH); - const content = msg.ciphertext.slice(0, -TAG_LENGTH); - decryptItems.push({ ciphertext: content, key: channelKey, iv: msg.nonce, tag }); - decryptMsgMap.push(msg); - } - - if (msg.replyToContent && msg.replyToNonce) { - const rTag = msg.replyToContent.slice(-TAG_LENGTH); - const rContent = msg.replyToContent.slice(0, -TAG_LENGTH); - replyDecryptItems.push({ ciphertext: rContent, key: channelKey, iv: msg.replyToNonce, tag: rTag }); - replyMsgMap.push(msg); - } - - if (msg.signature && msg.public_signing_key) { - verifyItems.push({ publicKey: msg.public_signing_key, message: msg.ciphertext, signature: msg.signature }); - verifyMsgMap.push(msg); - } - } - - const [decryptResults, replyResults, verifyResults] = await Promise.all([ - decryptItems.length > 0 ? crypto.decryptBatch(decryptItems) : [], - replyDecryptItems.length > 0 ? crypto.decryptBatch(replyDecryptItems) : [], - verifyItems.length > 0 ? crypto.verifyBatch(verifyItems) : [], - ]); - - if (cancelled) return; - - const decryptedMap = new Map(); - for (let i = 0; i < decryptResults.length; i++) { - const msg = decryptMsgMap[i]; - const result = decryptResults[i]; - decryptedMap.set(msg.id, result.success ? result.data : '[Decryption Error]'); - } - - const replyMap = new Map(); - for (let i = 0; i < replyResults.length; i++) { - const msg = replyMsgMap[i]; - const result = replyResults[i]; - if (result.success) { - let text = result.data; - if (text.startsWith('{')) text = '[Attachment]'; - else if (text.length > 100) text = text.substring(0, 100) + '...'; - replyMap.set(msg.id, text); - } else { - replyMap.set(msg.id, '[Encrypted]'); - } - } - - const verifyMap = new Map(); - for (let i = 0; i < verifyResults.length; i++) { - const msg = verifyMsgMap[i]; - verifyMap.set(msg.id, verifyResults[i].success && verifyResults[i].verified); - } - - const results = pinnedMessages.map(msg => ({ - ...msg, - content: decryptedMap.get(msg.id) ?? '[Encrypted Message]', - isVerified: verifyMap.get(msg.id) ?? false, - decryptedReply: replyMap.get(msg.id) ?? null, - })); - - if (!cancelled) setDecryptedPins(results); - }; - - decryptAll(); - return () => { cancelled = true; }; - }, [pinnedMessages, channelKey]); - - if (!visible) return null; - - const noop = () => {}; - - return ( - <div className="pinned-panel"> - <div className="pinned-panel-header"> - <h3>Pinned Messages</h3> - <button className="pinned-panel-close" onClick={onClose}>×</button> - </div> - <div className="pinned-panel-content"> - {decryptedPins.length === 0 ? ( - <div className="pinned-panel-empty"> - No pinned messages in this channel yet. - </div> - ) : ( - decryptedPins.map(msg => { - const isOwner = msg.username === username; - return ( - <div key={msg.id} className="pinned-message-card"> - <MessageItem - msg={msg} - isGrouped={false} - showDateDivider={false} - showUnreadDivider={false} - dateLabel="" - isMentioned={false} - isOwner={isOwner} - isEditing={false} - isHovered={false} - editInput="" - username={username} - roles={roles} - onHover={noop} - onLeave={noop} - onContextMenu={(e) => e.preventDefault()} - onAddReaction={noop} - onEdit={noop} - onReply={noop} - onMore={noop} - onEditInputChange={noop} - onEditKeyDown={noop} - onEditSave={noop} - onEditCancel={noop} - onReactionClick={onReactionClick} - onScrollToMessage={onJumpToMessage} - onProfilePopup={onProfilePopup} - onImageClick={onImageClick} - scrollToBottom={noop} - Attachment={Attachment} - LinkPreview={LinkPreview} - DirectVideo={DirectVideo} - /> - <div className="pinned-message-actions"> - <button - className="pinned-action-btn" - onClick={() => onJumpToMessage && onJumpToMessage(msg.id)} - > - Jump - </button> - <button - className="pinned-action-btn pinned-action-danger" - onClick={() => unpinMutation({ id: msg.id, pinned: false })} - > - Unpin - </button> - </div> - </div> - ); - }) - )} - </div> - </div> - ); -}; - -export default PinnedMessagesPanel; diff --git a/packages/shared/src/components/ScreenShareModal.jsx b/packages/shared/src/components/ScreenShareModal.jsx deleted file mode 100644 index 9a568d5..0000000 --- a/packages/shared/src/components/ScreenShareModal.jsx +++ /dev/null @@ -1,283 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { usePlatform } from '../platform'; - -const ScreenShareModal = ({ onClose, onSelectSource }) => { - const { screenCapture } = usePlatform(); - const [activeTab, setActiveTab] = useState('applications'); // applications | screens | devices - const [sources, setSources] = useState([]); - const [loading, setLoading] = useState(true); - const [shareAudio, setShareAudio] = useState(true); - - useEffect(() => { - loadSources(); - }, []); - - const [isWebFallback, setIsWebFallback] = useState(false); - - const loadSources = async () => { - setLoading(true); - try { - // Get screen/window sources from Electron - const desktopSources = await screenCapture.getScreenSources(); - - // If no desktop sources (web platform), use getDisplayMedia fallback - if (!desktopSources || desktopSources.length === 0) { - setIsWebFallback(true); - setLoading(false); - return; - } - - // Get video input devices (webcams) - const devices = await navigator.mediaDevices.enumerateDevices(); - const videoDevices = devices.filter(d => d.kind === 'videoinput'); - - // Categorize - const apps = desktopSources.filter(s => s.id.startsWith('window')); - const screens = desktopSources.filter(s => s.id.startsWith('screen')); - - const formattedDevices = videoDevices.map(d => ({ - id: d.deviceId, - name: d.label || `Camera ${d.deviceId.substring(0, 4)}...`, - isDevice: true, - thumbnail: null // Devices don't have static thumbnails easily referencable without opening stream - })); - - setSources({ - applications: apps, - screens: screens, - devices: formattedDevices - }); - } catch (err) { - console.error("Failed to load sources:", err); - setIsWebFallback(true); - } finally { - setLoading(false); - } - }; - - const handleWebFallback = async () => { - try { - const stream = await navigator.mediaDevices.getDisplayMedia({ - video: { frameRate: { ideal: 60, max: 60 } }, - audio: shareAudio, - }); - onSelectSource({ type: 'web_stream', stream, shareAudio }); - onClose(); - } catch (err) { - if (err.name !== 'NotAllowedError') { - console.error('getDisplayMedia failed:', err); - } - onClose(); - } - }; - - // Auto-trigger the browser picker on web - useEffect(() => { - if (isWebFallback) { - handleWebFallback(); - } - }, [isWebFallback]); - - const handleSelect = (source) => { - // If device, pass constraints differently (webcams don't have loopback audio) - if (source.isDevice) { - onSelectSource({ deviceId: source.id, type: 'device', shareAudio: false }); - } else { - onSelectSource({ sourceId: source.id, type: 'screen', shareAudio }); - } - onClose(); - }; - - const renderGrid = (items) => { - if (!items || items.length === 0) return <div style={{ color: '#b9bbbe', padding: 20, textAlign: 'center' }}>No sources found.</div>; - - return ( - <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '16px', padding: '16px' }}> - {items.map(item => ( - <div - key={item.id} - onClick={() => handleSelect(item)} - style={{ - cursor: 'pointer', - borderRadius: '4px', - padding: '8px', - }} - > - <div style={{ position: 'relative', width: '100%', height: '250px', width: '450px', borderRadius: '8px', overflow: 'hidden', marginBottom: '8px' }}> - {/* Thumbnail/Placeholder */} - {item.thumbnail ? ( - <img src={item.thumbnail} alt={item.name} style={{ width: '100%', height: '100%', borderRadius: '8px', objectFit: 'cover', display: 'block' }} /> - ) : ( - <div style={{ width: '100%', height: '100%', backgroundColor: '#202225', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#b9bbbe' }}> - 📷 - </div> - )} - - {/* Hover Overlay */} - <div - className="share-overlay" - style={{ - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - backgroundColor: 'rgba(0,0,0,0.5)', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - opacity: 0, - transition: 'opacity 0.2s' - }} - onMouseEnter={(e) => e.currentTarget.style.opacity = 1} - onMouseLeave={(e) => e.currentTarget.style.opacity = 0} - > - <button style={{ - backgroundColor: '#5865F2', - color: 'white', - border: 'none', - padding: '8px 16px', - borderRadius: '4px', - fontWeight: 'bold', - cursor: 'pointer', - fontSize: '14px', - boxShadow: '0 4px 6px rgba(0,0,0,0.3)' - }}> - Share Screen - </button> - </div> - </div> - - <div style={{ display: 'flex', alignItems: 'center', marginTop: '4px', maxWidth: '450px' }}> - {item.appIcon && ( - <img src={item.appIcon} alt="" style={{ width: '20px', height: '20px', marginRight: '8px' }} /> - )} - <div style={{ color: '#dcddde', fontSize: '14px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', fontWeight: '500' }}> - {item.name} - </div> - </div> - </div> - ))} - </div> - ); - }; - - return ( - <div style={{ - position: 'fixed', - top: 0, - left: 0, - width: '100%', - height: '100%', - backgroundColor: 'rgba(0,0,0,0.7)', - zIndex: 10000, - display: 'flex', - alignItems: 'center', - justifyContent: 'center' - }} onClick={onClose}> - <style>{` - .no-scrollbar::-webkit-scrollbar { - display: none; - } - .no-scrollbar { - -ms-overflow-style: none; - scrollbar-width: none; - } - `}</style> - <div style={{ - backgroundColor: '#242429', - borderRadius: '8px', - width: '965px', - height: '740px', - maxWidth: '95vw', - maxHeight: '95vh', - display: 'flex', - flexDirection: 'column', - boxShadow: '0 8px 16px rgba(0,0,0,0.24)' - }} onClick={e => e.stopPropagation()}> - - {/* Header/Tabs */} - <div style={{ display: 'flex', borderBottom: '1px solid #2f3136', padding: '16px 16px 0 16px', flexShrink: 0 }}> - <div - onClick={() => setActiveTab('applications')} - style={{ - padding: '8px 16px', - cursor: 'pointer', - borderBottom: activeTab === 'applications' ? '2px solid white' : '2px solid transparent', - color: activeTab === 'applications' ? 'white' : '#b9bbbe', - fontWeight: '500' - }} - > - Applications - </div> - <div - onClick={() => setActiveTab('screens')} - style={{ - padding: '8px 16px', - cursor: 'pointer', - borderBottom: activeTab === 'screens' ? '2px solid white' : '2px solid transparent', - color: activeTab === 'screens' ? 'white' : '#b9bbbe', - fontWeight: '500' - }} - > - Screens - </div> - <div - onClick={() => setActiveTab('devices')} - style={{ - padding: '8px 16px', - cursor: 'pointer', - borderBottom: activeTab === 'devices' ? '2px solid white' : '2px solid transparent', - color: activeTab === 'devices' ? 'white' : '#b9bbbe', - fontWeight: '500' - }} - > - Devices - </div> - </div> - - {/* Content */} - <div className="no-scrollbar" style={{ flex: 1, overflowY: 'auto' }}> - {loading ? ( - <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%', color: '#b9bbbe' }}> - Loading sources... - </div> - ) : ( - renderGrid(sources[activeTab]) - )} - </div> - - {/* Audio sharing footer — hidden for device sources (webcams) */} - {activeTab !== 'devices' && ( - <div style={{ - borderTop: '1px solid #2f3136', - padding: '12px 16px', - display: 'flex', - alignItems: 'center', - flexShrink: 0, - }}> - <label style={{ - display: 'flex', - alignItems: 'center', - gap: '8px', - cursor: 'pointer', - color: '#dcddde', - fontSize: '14px', - userSelect: 'none', - }}> - <input - type="checkbox" - checked={shareAudio} - onChange={(e) => setShareAudio(e.target.checked)} - style={{ accentColor: '#5865F2', width: '16px', height: '16px', cursor: 'pointer' }} - /> - Also share computer audio - </label> - </div> - )} - </div> - </div> - ); -}; - -export default ScreenShareModal; diff --git a/packages/shared/src/components/SearchDropdown.jsx b/packages/shared/src/components/SearchDropdown.jsx deleted file mode 100644 index 8141640..0000000 --- a/packages/shared/src/components/SearchDropdown.jsx +++ /dev/null @@ -1,244 +0,0 @@ -import React, { useEffect, useRef, useState, useCallback } from 'react'; -import ReactDOM from 'react-dom'; -import { detectActivePrefix } from '../utils/searchUtils'; - -const FILTER_SUGGESTIONS = [ - { prefix: 'from:', label: 'from:', description: 'user', icon: 'user' }, - { prefix: 'mentions:', label: 'mentions:', description: 'user', icon: 'at' }, - { prefix: 'has:', label: 'has:', description: 'link, file, image, or video', icon: 'has' }, - { prefix: 'in:', label: 'in:', description: 'channel', icon: 'channel' }, - { prefix: 'before:', label: 'before:', description: 'date', icon: 'date' }, - { prefix: 'after:', label: 'after:', description: 'date', icon: 'date' }, - { prefix: 'pinned:', label: 'pinned:', description: 'true or false', icon: 'pin' }, -]; - -const HAS_OPTIONS = [ - { value: 'link', label: 'link' }, - { value: 'file', label: 'file' }, - { value: 'image', label: 'image' }, - { value: 'video', label: 'video' }, -]; - -function getAvatarColor(name) { - const colors = ['#5865F2', '#57F287', '#FEE75C', '#EB459E', '#ED4245', '#F47B67', '#E67E22', '#3498DB']; - let hash = 0; - for (let i = 0; i < (name || '').length; i++) hash = name.charCodeAt(i) + ((hash << 5) - hash); - return colors[Math.abs(hash) % colors.length]; -} - -function FilterIcon({ type }) { - switch (type) { - case 'user': - return ( - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/> - </svg> - ); - case 'at': - return ( - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10h5v-2h-5c-4.34 0-8-3.66-8-8s3.66-8 8-8 8 3.66 8 8v1.43c0 .79-.71 1.57-1.5 1.57s-1.5-.78-1.5-1.57V12c0-2.76-2.24-5-5-5s-5 2.24-5 5 2.24 5 5 5c1.38 0 2.64-.56 3.54-1.47.65.89 1.77 1.47 2.96 1.47 1.97 0 3.5-1.6 3.5-3.57V12c0-5.52-4.48-10-10-10zm0 13c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3z"/> - </svg> - ); - case 'has': - return ( - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/> - </svg> - ); - case 'channel': - return <span style={{ fontSize: 16, fontWeight: 700, opacity: 0.7 }}>#</span>; - case 'date': - return ( - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11z"/> - </svg> - ); - case 'pin': - return ( - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M19.3 5.3a1 1 0 00-1.4-1.4L14.6 7.2l-1.5-.8a2 2 0 00-2.2.2L8.5 9a1 1 0 000 1.5l1.8 1.8-4.6 4.6a1 1 0 001.4 1.4l4.6-4.6 1.8 1.8a1 1 0 001.5 0l2.4-2.4a2 2 0 00.2-2.2l-.8-1.5 3.3-3.3z"/> - </svg> - ); - default: - return null; - } -} - -const SearchDropdown = ({ - visible, - searchText, - channels, - members, - searchHistory, - onSelectFilter, - onSelectHistoryItem, - onClearHistory, - onClearHistoryItem, - anchorRef, - onClose, -}) => { - const dropdownRef = useRef(null); - const [pos, setPos] = useState({ top: 0, left: 0, width: 420 }); - - // Position dropdown below anchor - useEffect(() => { - if (!visible || !anchorRef?.current) return; - const rect = anchorRef.current.getBoundingClientRect(); - setPos({ - top: rect.bottom + 4, - left: Math.max(rect.right - 420, 8), - width: 420, - }); - }, [visible, anchorRef, searchText]); - - // Click outside to close - useEffect(() => { - if (!visible) return; - const handler = (e) => { - if ( - dropdownRef.current && !dropdownRef.current.contains(e.target) && - anchorRef?.current && !anchorRef.current.contains(e.target) - ) { - onClose(); - } - }; - document.addEventListener('mousedown', handler); - return () => document.removeEventListener('mousedown', handler); - }, [visible, onClose, anchorRef]); - - if (!visible) return null; - - const activePrefix = detectActivePrefix(searchText); - - let content; - - if (activePrefix?.prefix === 'from' || activePrefix?.prefix === 'mentions') { - const filtered = (members || []).filter(m => - m.username.toLowerCase().includes(activePrefix.partial) - ); - const headerText = activePrefix.prefix === 'from' ? 'FROM USER' : 'MENTIONS USER'; - content = ( - <div className="search-dropdown-scrollable"> - <div className="search-dropdown-section-header">{headerText}</div> - {filtered.length === 0 && ( - <div className="search-dropdown-empty">No matching users</div> - )} - {filtered.map(m => ( - <div - key={m.id} - className="search-dropdown-member" - onClick={() => onSelectFilter(activePrefix.prefix, m.username)} - > - {m.avatarUrl ? ( - <img src={m.avatarUrl} className="search-dropdown-avatar" alt="" /> - ) : ( - <div className="search-dropdown-avatar" style={{ backgroundColor: getAvatarColor(m.username) }}> - {m.username[0]?.toUpperCase()} - </div> - )} - <span className="search-dropdown-member-name">{m.username}</span> - </div> - ))} - </div> - ); - } else if (activePrefix?.prefix === 'in') { - const filtered = (channels || []).filter(c => - c.name?.toLowerCase().includes(activePrefix.partial) && c.type === 'text' - ); - content = ( - <div className="search-dropdown-scrollable"> - <div className="search-dropdown-section-header">IN CHANNEL</div> - {filtered.length === 0 && ( - <div className="search-dropdown-empty">No matching channels</div> - )} - {filtered.map(c => ( - <div - key={c._id} - className="search-dropdown-channel" - onClick={() => onSelectFilter('in', c.name)} - > - <span className="search-dropdown-channel-hash">#</span> - <span>{c.name}</span> - </div> - ))} - </div> - ); - } else if (activePrefix?.prefix === 'has') { - const filtered = HAS_OPTIONS.filter(o => - o.value.includes(activePrefix.partial) - ); - content = ( - <div className="search-dropdown-scrollable"> - <div className="search-dropdown-section-header">MESSAGE CONTAINS</div> - {filtered.map(o => ( - <div - key={o.value} - className="search-dropdown-item" - onClick={() => onSelectFilter('has', o.value)} - > - <FilterIcon type="has" /> - <span>{o.label}</span> - </div> - ))} - </div> - ); - } else { - // Default: show filter suggestions + search history - content = ( - <div className="search-dropdown-scrollable"> - <div className="search-dropdown-section-header">SEARCH OPTIONS</div> - {FILTER_SUGGESTIONS.map(f => ( - <div - key={f.prefix} - className="search-dropdown-item" - onClick={() => onSelectFilter(f.prefix.replace(':', ''), null)} - > - <span className="search-dropdown-item-icon"><FilterIcon type={f.icon} /></span> - <span className="search-dropdown-item-label">{f.label}</span> - <span className="search-dropdown-item-desc">{f.description}</span> - </div> - ))} - {searchHistory && searchHistory.length > 0 && ( - <> - <div className="search-dropdown-section-header search-dropdown-history-header"> - <span>SEARCH HISTORY</span> - <button className="search-dropdown-clear-all" onClick={onClearHistory}>Clear</button> - </div> - {searchHistory.map((item, i) => ( - <div - key={i} - className="search-dropdown-history-item" - onClick={() => onSelectHistoryItem(item)} - > - <svg className="search-dropdown-history-icon" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M13 3a9 9 0 00-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.954 8.954 0 0013 21a9 9 0 000-18zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"/> - </svg> - <span className="search-dropdown-history-text">{item}</span> - <button - className="search-dropdown-history-delete" - onClick={(e) => { e.stopPropagation(); onClearHistoryItem(i); }} - > - × - </button> - </div> - ))} - </> - )} - </div> - ); - } - - return ReactDOM.createPortal( - <div - ref={dropdownRef} - className="search-dropdown" - style={{ top: pos.top, left: pos.left, width: pos.width }} - > - {content} - </div>, - document.body - ); -}; - -export default SearchDropdown; diff --git a/packages/shared/src/components/SearchPanel.jsx b/packages/shared/src/components/SearchPanel.jsx deleted file mode 100644 index 782fe51..0000000 --- a/packages/shared/src/components/SearchPanel.jsx +++ /dev/null @@ -1,235 +0,0 @@ -import React, { useState, useCallback, useEffect } from 'react'; -import { useQuery } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; -import { useSearch } from '../contexts/SearchContext'; -import { parseFilters } from '../utils/searchUtils'; -import { usePlatform } from '../platform'; -import { LinkPreview } from './ChatArea'; -import { extractUrls } from './MessageItem'; -import { - formatTime, escapeHtml, linkifyHtml, formatEmojisHtml, getAvatarColor, - SearchResultImage, SearchResultVideo, SearchResultFile -} from '../utils/searchRendering'; - -const SearchPanel = ({ visible, onClose, channels, isDM, dmChannelId, onJumpToMessage, query, sortOrder, onSortChange }) => { - const { search, isReady } = useSearch() || {}; - const { links } = usePlatform(); - const customEmojis = useQuery(api.customEmojis.list) || []; - const [results, setResults] = useState([]); - const [searching, setSearching] = useState(false); - const [showSortMenu, setShowSortMenu] = useState(false); - - // Execute search when query changes - useEffect(() => { - if (!visible || !query?.trim() || !search || !isReady) { - if (!query?.trim()) setResults([]); - return; - } - - setSearching(true); - const { textQuery, filters } = parseFilters(query); - - let channelId; - if (isDM) { - // In DM view — always scope to the DM channel - channelId = dmChannelId; - } else { - channelId = filters.channelName - ? channels?.find(c => c.name?.toLowerCase() === filters.channelName.toLowerCase())?._id - : undefined; - } - - const params = { - query: textQuery || undefined, - channelId, - senderName: filters.senderName, - hasLink: filters.hasLink, - hasImage: filters.hasImage, - hasVideo: filters.hasVideo, - hasFile: filters.hasFile, - hasMention: filters.hasMention, - before: filters.before, - after: filters.after, - pinned: filters.pinned, - limit: 25, - }; - - const res = search(params); - - let filtered; - if (isDM) { - // In DM view — results are already scoped to dmChannelId - filtered = res; - } else { - // In server view — filter out DM messages - const serverChannelIds = new Set(channels?.map(c => c._id) || []); - filtered = res.filter(r => serverChannelIds.has(r.channel_id)); - } - - // Sort results - let sorted = [...filtered]; - if (sortOrder === 'oldest') { - sorted.sort((a, b) => a.created_at - b.created_at); - } else { - // newest first (default) - sorted.sort((a, b) => b.created_at - a.created_at); - } - - setResults(sorted); - setSearching(false); - }, [visible, query, sortOrder, search, isReady, channels, isDM, dmChannelId]); - - const handleResultClick = useCallback((result) => { - onJumpToMessage(result.channel_id, result.id); - }, [onJumpToMessage]); - - if (!visible) return null; - - const channelMap = {}; - if (channels) { - for (const c of channels) channelMap[c._id] = c.name; - } - - // Group results by channel - const grouped = {}; - for (const r of results) { - const chName = channelMap[r.channel_id] || 'Unknown'; - if (!grouped[chName]) grouped[chName] = []; - grouped[chName].push(r); - } - - const { filters: activeFilters } = query?.trim() ? parseFilters(query) : { filters: {} }; - const filterChips = []; - if (activeFilters.senderName) filterChips.push({ label: `from: ${activeFilters.senderName}`, key: 'from' }); - if (activeFilters.hasLink) filterChips.push({ label: 'has: link', key: 'hasLink' }); - if (activeFilters.hasImage) filterChips.push({ label: 'has: image', key: 'hasImage' }); - if (activeFilters.hasVideo) filterChips.push({ label: 'has: video', key: 'hasVideo' }); - if (activeFilters.hasFile) filterChips.push({ label: 'has: file', key: 'hasFile' }); - if (activeFilters.hasMention) filterChips.push({ label: 'has: mention', key: 'hasMention' }); - if (activeFilters.before) filterChips.push({ label: `before: ${activeFilters.before}`, key: 'before' }); - if (activeFilters.after) filterChips.push({ label: `after: ${activeFilters.after}`, key: 'after' }); - if (activeFilters.pinned) filterChips.push({ label: 'pinned: true', key: 'pinned' }); - if (activeFilters.channelName) filterChips.push({ label: `in: ${activeFilters.channelName}`, key: 'in' }); - - const sortLabel = sortOrder === 'oldest' ? 'Oldest' : 'Newest'; - - return ( - <div className="search-panel"> - <div className="search-panel-header"> - <div className="search-panel-header-left"> - <span className="search-result-count"> - {results.length} result{results.length !== 1 ? 's' : ''} - </span> - </div> - <div className="search-panel-header-right"> - <div className="search-panel-sort-wrapper"> - <button - className="search-panel-sort-btn" - onClick={() => setShowSortMenu(prev => !prev)} - > - {sortLabel} - <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" style={{ marginLeft: 4 }}> - <path d="M7 10l5 5 5-5z"/> - </svg> - </button> - {showSortMenu && ( - <div className="search-panel-sort-menu"> - <div - className={`search-panel-sort-option ${sortOrder === 'newest' ? 'active' : ''}`} - onClick={() => { onSortChange('newest'); setShowSortMenu(false); }} - > - Newest - </div> - <div - className={`search-panel-sort-option ${sortOrder === 'oldest' ? 'active' : ''}`} - onClick={() => { onSortChange('oldest'); setShowSortMenu(false); }} - > - Oldest - </div> - </div> - )} - </div> - <button className="search-panel-close" onClick={onClose}>×</button> - </div> - </div> - - {filterChips.length > 0 && ( - <div className="search-filter-chips"> - {filterChips.map(chip => ( - <span key={chip.key} className="search-filter-chip"> - {chip.label} - </span> - ))} - </div> - )} - - <div className="search-panel-results"> - {!isReady && ( - <div className="search-panel-empty">Search database is loading...</div> - )} - {isReady && searching && <div className="search-panel-empty">Searching...</div>} - {isReady && !searching && results.length === 0 && ( - <div className="search-panel-empty"> - <svg width="40" height="40" viewBox="0 0 24 24" fill="currentColor" style={{ opacity: 0.3, marginBottom: 8 }}> - <path d="M21.71 20.29L18 16.61A9 9 0 1016.61 18l3.68 3.68a1 1 0 001.42 0 1 1 0 000-1.39zM11 18a7 7 0 110-14 7 7 0 010 14z"/> - </svg> - <div>No results found</div> - </div> - )} - {Object.entries(grouped).map(([chName, msgs]) => ( - <div key={chName}> - <div className="search-channel-header">{isDM ? chName : `#${chName}`}</div> - {msgs.map(r => ( - <div - key={r.id} - className="search-result" - onClick={() => handleResultClick(r)} - > - <div - className="search-result-avatar" - style={{ backgroundColor: getAvatarColor(r.username) }} - > - {r.username?.[0]?.toUpperCase()} - </div> - <div className="search-result-body"> - <div className="search-result-header"> - <span className="search-result-username">{r.username}</span> - <span className="search-result-time">{formatTime(r.created_at)}</span> - </div> - {!(r.has_attachment && r.attachment_meta) && ( - <div - className="search-result-content" - dangerouslySetInnerHTML={{ __html: formatEmojisHtml(linkifyHtml(r.snippet || escapeHtml(r.content)), customEmojis) }} - onClick={(e) => { - if (e.target.tagName === 'A' && e.target.href) { - e.preventDefault(); - e.stopPropagation(); - links.openExternal(e.target.href); - } - }} - /> - )} - {r.has_attachment && r.attachment_meta ? (() => { - try { - const meta = JSON.parse(r.attachment_meta); - if (r.attachment_type?.startsWith('image/')) return <SearchResultImage metadata={meta} />; - if (r.attachment_type?.startsWith('video/')) return <SearchResultVideo metadata={meta} />; - return <SearchResultFile metadata={meta} />; - } catch { return <span className="search-result-badge">File</span>; } - })() : r.has_attachment ? <span className="search-result-badge">File</span> : null} - {r.has_link && r.content && (() => { - const urls = extractUrls(r.content); - return urls.map((url, i) => <LinkPreview key={i} url={url} />); - })()} - {r.pinned && <span className="search-result-badge">Pinned</span>} - </div> - </div> - ))} - </div> - ))} - </div> - </div> - ); -}; - -export default SearchPanel; diff --git a/packages/shared/src/components/ServerSettingsModal.jsx b/packages/shared/src/components/ServerSettingsModal.jsx deleted file mode 100644 index 7e29852..0000000 --- a/packages/shared/src/components/ServerSettingsModal.jsx +++ /dev/null @@ -1,1345 +0,0 @@ -import React, { useState, useEffect, useRef, useCallback } from 'react'; -import ReactDOM from 'react-dom'; -import { useQuery, useConvex } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; -import { AllEmojis } from '../assets/emojis'; -import AvatarCropModal from './AvatarCropModal'; -import Cropper from 'react-easy-crop'; -import { useIsMobile } from '../hooks/useIsMobile'; - -function getCroppedEmojiImg(imageSrc, pixelCrop, rotation, flipH, flipV) { - return new Promise((resolve, reject) => { - const image = new Image(); - image.crossOrigin = 'anonymous'; - image.onload = () => { - const canvas = document.createElement('canvas'); - canvas.width = 128; - canvas.height = 128; - const ctx = canvas.getContext('2d'); - - ctx.translate(64, 64); - ctx.rotate((rotation * Math.PI) / 180); - ctx.scale(flipH ? -1 : 1, flipV ? -1 : 1); - ctx.translate(-64, -64); - - ctx.drawImage( - image, - pixelCrop.x, pixelCrop.y, pixelCrop.width, pixelCrop.height, - 0, 0, 128, 128 - ); - canvas.toBlob((blob) => { - if (!blob) return reject(new Error('Canvas toBlob failed')); - resolve(blob); - }, 'image/png'); - }; - image.onerror = reject; - image.src = imageSrc; - }); -} - -const TIMEOUT_OPTIONS = [ - { value: 60, label: '1 min' }, - { value: 300, label: '5 min' }, - { value: 900, label: '15 min' }, - { value: 1800, label: '30 min' }, - { value: 3600, label: '1 hour' }, -]; - -const ServerSettingsModal = ({ onClose }) => { - const [activeTab, setActiveTab] = useState('Overview'); - const [selectedRole, setSelectedRole] = useState(null); - - const userId = localStorage.getItem('userId'); - const convex = useConvex(); - - // Reactive queries from Convex - const roles = useQuery(api.roles.list) || []; - const members = useQuery(api.roles.listMembers) || []; - const myPermissions = useQuery( - api.roles.getMyPermissions, - userId ? { userId } : "skip" - ) || {}; - - // Custom emojis - const customEmojis = useQuery(api.customEmojis.list) || []; - const [showEmojiModal, setShowEmojiModal] = useState(false); - const [emojiPreviewUrl, setEmojiPreviewUrl] = useState(null); - const [emojiName, setEmojiName] = useState(''); - const [emojiFile, setEmojiFile] = useState(null); - const [emojiUploading, setEmojiUploading] = useState(false); - const [emojiError, setEmojiError] = useState(''); - const emojiFileInputRef = useRef(null); - const [emojiCrop, setEmojiCrop] = useState({ x: 0, y: 0 }); - const [emojiZoom, setEmojiZoom] = useState(1); - const [emojiRotation, setEmojiRotation] = useState(0); - const [emojiFlipH, setEmojiFlipH] = useState(false); - const [emojiFlipV, setEmojiFlipV] = useState(false); - const [emojiCroppedAreaPixels, setEmojiCroppedAreaPixels] = useState(null); - - const onEmojiCropComplete = useCallback((_croppedArea, croppedPixels) => { - setEmojiCroppedAreaPixels(croppedPixels); - }, []); - - // Server settings - const serverSettings = useQuery(api.serverSettings.get); - const channels = useQuery(api.channels.list) || []; - const voiceChannels = channels.filter(c => c.type === 'voice'); - const [serverName, setServerName] = useState('Secure Chat'); - const [serverNameDirty, setServerNameDirty] = useState(false); - const [afkChannelId, setAfkChannelId] = useState(''); - const [afkTimeout, setAfkTimeout] = useState(300); - const [afkDirty, setAfkDirty] = useState(false); - const [iconFile, setIconFile] = useState(null); - const [iconPreview, setIconPreview] = useState(null); - const [rawIconUrl, setRawIconUrl] = useState(null); - const [showIconCropModal, setShowIconCropModal] = useState(false); - const [iconDirty, setIconDirty] = useState(false); - const [savingIcon, setSavingIcon] = useState(false); - const iconInputRef = useRef(null); - - // Mobile state - const isMobile = useIsMobile(); - const [mobileScreen, setMobileScreen] = useState('menu'); - - const mobileGoBack = () => { - if (mobileScreen === 'role-edit') setMobileScreen('roles'); - else setMobileScreen('menu'); - }; - - const mobileSelectRole = (role) => { - setSelectedRole(role); - setMobileScreen('role-edit'); - }; - - // Auto-navigate to overview on icon crop (so user sees save button) - useEffect(() => { - if (isMobile && iconDirty && mobileScreen === 'menu') { - setMobileScreen('overview'); - } - }, [isMobile, iconDirty, mobileScreen]); - - useEffect(() => { - const handleKey = (e) => { - if (e.key === 'Escape') { - if (showEmojiModal) { - handleEmojiModalClose(); - } else if (!showIconCropModal) { - if (isMobile && mobileScreen !== 'menu') { - mobileGoBack(); - } else { - onClose(); - } - } - } - }; - window.addEventListener('keydown', handleKey); - return () => window.removeEventListener('keydown', handleKey); - }, [onClose, showEmojiModal, showIconCropModal, isMobile, mobileScreen]); - - React.useEffect(() => { - if (serverSettings) { - setServerName(serverSettings.serverName || 'Secure Chat'); - setServerNameDirty(false); - setAfkChannelId(serverSettings.afkChannelId || ''); - setAfkTimeout(serverSettings.afkTimeout || 300); - setAfkDirty(false); - } - }, [serverSettings]); - - const handleSaveServerName = async () => { - if (!userId) return; - try { - await convex.mutation(api.serverSettings.updateName, { - userId, - serverName, - }); - setServerNameDirty(false); - } catch (e) { - console.error('Failed to update server name:', e); - alert('Failed to save server name: ' + e.message); - } - }; - - const handleSaveAfkSettings = async () => { - if (!userId) return; - try { - await convex.mutation(api.serverSettings.update, { - userId, - afkChannelId: afkChannelId || undefined, - afkTimeout, - }); - setAfkDirty(false); - } catch (e) { - console.error('Failed to update server settings:', e); - alert('Failed to save settings: ' + e.message); - } - }; - - const handleIconFileChange = (e) => { - const file = e.target.files?.[0]; - if (!file) return; - const url = URL.createObjectURL(file); - setRawIconUrl(url); - setShowIconCropModal(true); - e.target.value = ''; - }; - - const handleIconCropApply = (blob) => { - const file = new File([blob], 'server-icon.png', { type: 'image/png' }); - setIconFile(file); - const previewUrl = URL.createObjectURL(blob); - setIconPreview(previewUrl); - if (rawIconUrl) URL.revokeObjectURL(rawIconUrl); - setRawIconUrl(null); - setShowIconCropModal(false); - setIconDirty(true); - }; - - const handleIconCropCancel = () => { - if (rawIconUrl) URL.revokeObjectURL(rawIconUrl); - setRawIconUrl(null); - setShowIconCropModal(false); - }; - - const handleSaveIcon = async () => { - if (!userId || savingIcon) return; - setSavingIcon(true); - try { - let iconStorageId; - if (iconFile) { - const uploadUrl = await convex.mutation(api.files.generateUploadUrl); - const res = await fetch(uploadUrl, { - method: 'POST', - headers: { 'Content-Type': iconFile.type }, - body: iconFile, - }); - const { storageId } = await res.json(); - iconStorageId = storageId; - } - await convex.mutation(api.serverSettings.updateIcon, { - userId, - iconStorageId, - }); - setIconFile(null); - setIconDirty(false); - if (iconPreview) { - URL.revokeObjectURL(iconPreview); - setIconPreview(null); - } - } catch (e) { - console.error('Failed to update server icon:', e); - alert('Failed to save server icon: ' + e.message); - } finally { - setSavingIcon(false); - } - }; - - const handleRemoveIcon = async () => { - if (!userId) return; - setSavingIcon(true); - try { - await convex.mutation(api.serverSettings.updateIcon, { - userId, - iconStorageId: undefined, - }); - setIconFile(null); - setIconDirty(false); - if (iconPreview) { - URL.revokeObjectURL(iconPreview); - setIconPreview(null); - } - } catch (e) { - console.error('Failed to remove server icon:', e); - alert('Failed to remove server icon: ' + e.message); - } finally { - setSavingIcon(false); - } - }; - - const currentIconUrl = iconPreview || serverSettings?.iconUrl; - - const handleEmojiFileSelect = (e) => { - const file = e.target.files?.[0]; - if (!file) return; - const name = file.name.replace(/\.[^.]+$/, '').replace(/[^a-zA-Z0-9_]/g, '_').replace(/^_+|_+$/g, '').substring(0, 32); - setEmojiFile(file); - setEmojiName(name || 'emoji'); - setEmojiPreviewUrl(URL.createObjectURL(file)); - setEmojiError(''); - setShowEmojiModal(true); - e.target.value = ''; - }; - - const handleEmojiModalClose = () => { - setShowEmojiModal(false); - if (emojiPreviewUrl) URL.revokeObjectURL(emojiPreviewUrl); - setEmojiPreviewUrl(null); - setEmojiFile(null); - setEmojiName(''); - setEmojiError(''); - setEmojiCrop({ x: 0, y: 0 }); - setEmojiZoom(1); - setEmojiRotation(0); - setEmojiFlipH(false); - setEmojiFlipV(false); - setEmojiCroppedAreaPixels(null); - }; - - const handleEmojiUpload = async () => { - if (!userId || !emojiFile || !emojiName.trim()) return; - setEmojiError(''); - const name = emojiName.trim(); - - if (!/^[a-zA-Z0-9_]+$/.test(name)) { - setEmojiError('Name can only contain letters, numbers, and underscores'); - return; - } - if (name.length < 2 || name.length > 32) { - setEmojiError('Name must be between 2 and 32 characters'); - return; - } - if (AllEmojis.find(e => e.name === name)) { - setEmojiError(`"${name}" conflicts with a built-in emoji`); - return; - } - if (customEmojis.find(e => e.name === name)) { - setEmojiError(`"${name}" already exists as a custom emoji`); - return; - } - - setEmojiUploading(true); - try { - let fileToUpload = emojiFile; - if (emojiCroppedAreaPixels && emojiPreviewUrl) { - const blob = await getCroppedEmojiImg(emojiPreviewUrl, emojiCroppedAreaPixels, emojiRotation, emojiFlipH, emojiFlipV); - fileToUpload = new File([blob], 'emoji.png', { type: 'image/png' }); - } - const uploadUrl = await convex.mutation(api.files.generateUploadUrl); - const res = await fetch(uploadUrl, { - method: 'POST', - headers: { 'Content-Type': fileToUpload.type }, - body: fileToUpload, - }); - const { storageId } = await res.json(); - await convex.mutation(api.customEmojis.upload, { userId, name, storageId }); - handleEmojiModalClose(); - } catch (e) { - console.error('Failed to upload emoji:', e); - setEmojiError(e.message || 'Failed to upload emoji'); - } finally { - setEmojiUploading(false); - } - }; - - const handleEmojiDelete = async (emojiId) => { - if (!userId) return; - try { - await convex.mutation(api.customEmojis.remove, { userId, emojiId }); - } catch (e) { - console.error('Failed to delete emoji:', e); - } - }; - - const handleCreateRole = async () => { - try { - const newRole = await convex.mutation(api.roles.create, { - name: 'new role', - color: '#99aab5' - }); - setSelectedRole(newRole); - } catch (e) { - console.error('Failed to create role:', e); - } - }; - - const handleUpdateRole = async (id, updates) => { - try { - const updated = await convex.mutation(api.roles.update, { id, ...updates }); - if (selectedRole && selectedRole._id === id) { - setSelectedRole(updated); - } - } catch (e) { - console.error('Failed to update role:', e); - } - }; - - const handleDeleteRole = async (id) => { - if (!confirm('Delete this role?')) return; - try { - await convex.mutation(api.roles.remove, { id }); - if (selectedRole && selectedRole._id === id) setSelectedRole(null); - } catch (e) { - console.error('Failed to delete role:', e); - } - }; - - const handleAssignRole = async (roleId, targetUserId, isAdding) => { - const action = isAdding ? api.roles.assign : api.roles.unassign; - try { - await convex.mutation(action, { roleId, userId: targetUserId }); - } catch (e) { - console.error('Failed to assign/unassign role:', e); - } - }; - - // Render Tabs - const renderSidebar = () => ( - <div style={{ - width: '218px', - backgroundColor: 'var(--bg-secondary)', - display: 'flex', flexDirection: 'column', alignItems: 'flex-end', padding: '60px 6px 60px 20px' - }}> - <div style={{ width: '100%', padding: '0 10px' }}> - <div style={{ fontSize: '12px', fontWeight: '700', color: 'var(--text-muted)', marginBottom: '6px', textTransform: 'uppercase' }}> - Server Settings - </div> - {['Overview', 'Emoji', 'Roles', 'Members'].map(tab => ( - <div - key={tab} - onClick={() => setActiveTab(tab)} - style={{ - padding: '6px 10px', borderRadius: '4px', - backgroundColor: activeTab === tab ? 'var(--background-modifier-selected)' : 'transparent', - color: activeTab === tab ? 'var(--header-primary)' : 'var(--header-secondary)', - cursor: 'pointer', marginBottom: '2px', fontSize: '15px' - }} - > - {tab} - </div> - ))} - </div> - </div> - ); - - const canManageRoles = myPermissions.manage_roles; - const disabledOpacity = canManageRoles ? 1 : 0.5; - const labelStyle = { display: 'block', color: 'var(--header-secondary)', fontSize: '12px', fontWeight: '700', marginBottom: 8 }; - const editableRoles = roles.filter(r => r.name !== 'Owner'); - - const renderRolesTab = () => ( - <div style={{ display: 'flex', height: '100%' }}> - <div style={{ width: '200px', borderRight: '1px solid var(--border-subtle)', marginRight: '20px' }}> - <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '10px' }}> - <h3 style={{ color: 'var(--header-secondary)', fontSize: '12px' }}>ROLES</h3> - {canManageRoles && ( - <button onClick={handleCreateRole} style={{ background: 'transparent', border: 'none', color: 'var(--header-secondary)', cursor: 'pointer' }}>+</button> - )} - </div> - {editableRoles.map(r => ( - <div - key={r._id} - onClick={() => setSelectedRole(r)} - style={{ - padding: '6px', - backgroundColor: selectedRole?._id === r._id ? 'var(--background-modifier-selected)' : 'transparent', - borderRadius: '4px', cursor: 'pointer', color: r.color || '#b9bbbe', - display: 'flex', alignItems: 'center' - }} - > - <div style={{ width: 12, height: 12, borderRadius: '50%', background: r.color, marginRight: 8 }}></div> - {r.name} - </div> - ))} - </div> - - {selectedRole ? ( - <div className="no-scrollbar" style={{ flex: 1, overflowY: 'auto' }}> - <h2 style={{ color: 'var(--header-primary)', marginTop: 0 }}>Edit Role - {selectedRole.name}</h2> - - <label style={labelStyle}>ROLE NAME</label> - <input - value={selectedRole.name} - onChange={(e) => handleUpdateRole(selectedRole._id, { name: e.target.value })} - disabled={!canManageRoles} - style={{ width: '100%', padding: 10, background: 'var(--bg-tertiary)', border: 'none', borderRadius: 4, color: 'var(--header-primary)', marginBottom: 20, opacity: disabledOpacity }} - /> - - <label style={labelStyle}>ROLE COLOR</label> - <input - type="color" - value={selectedRole.color} - onChange={(e) => handleUpdateRole(selectedRole._id, { color: e.target.value })} - disabled={!canManageRoles} - style={{ width: '100%', height: 40, border: 'none', padding: 0, marginBottom: 20, opacity: disabledOpacity }} - /> - - <label style={labelStyle}>PERMISSIONS</label> - {['manage_channels', 'manage_roles', 'manage_nicknames', 'create_invite', 'embed_links', 'attach_files', 'move_members', 'mute_members'].map(perm => ( - <div key={perm} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10, paddingBottom: 10, borderBottom: '1px solid var(--border-subtle)' }}> - <span style={{ color: 'var(--header-primary)', textTransform: 'capitalize' }}>{perm.replace('_', ' ')}</span> - <input - type="checkbox" - checked={selectedRole.permissions?.[perm] || false} - onChange={(e) => { - handleUpdateRole(selectedRole._id, { - permissions: { ...selectedRole.permissions, [perm]: e.target.checked } - }); - }} - disabled={!canManageRoles} - style={{ transform: 'scale(1.5)', opacity: disabledOpacity }} - /> - </div> - ))} - - {canManageRoles && selectedRole.name !== 'Owner' && selectedRole.name !== '@everyone' && ( - <button onClick={() => handleDeleteRole(selectedRole._id)} style={{ color: '#ed4245', background: 'transparent', border: '1px solid #ed4245', padding: '6px 12px', borderRadius: 4, marginTop: 20, cursor: 'pointer' }}> - Delete Role - </button> - )} - </div> - ) : ( - <div style={{ color: 'var(--header-secondary)' }}>Select a role to edit</div> - )} - </div> - ); - - const isCurrentUserAdmin = members.find(m => m.id === userId)?.roles?.some(r => r.name === 'Owner'); - - const handleDeleteUser = async (targetUserId, targetUsername) => { - if (!confirm(`Are you sure you want to delete "${targetUsername}" and ALL their messages? This cannot be undone.`)) return; - try { - const result = await convex.mutation(api.auth.deleteUser, { - requestingUserId: userId, - targetUserId, - }); - if (!result.success) { - alert(result.error || 'Failed to delete user.'); - } - } catch (e) { - console.error('Delete user error:', e); - alert('Failed to delete user. See console.'); - } - }; - - const renderMembersTab = () => ( - <div> - <h2 style={{ color: 'var(--header-primary)' }}>Members</h2> - {members.map(m => { - const isOwner = m.roles?.some(r => r.name === 'Owner'); - const isSelf = m.id === userId; - return ( - <div key={m.id} style={{ display: 'flex', alignItems: 'center', padding: '10px', borderBottom: '1px solid var(--border-subtle)' }}> - <div style={{ width: 32, height: 32, borderRadius: '50%', background: '#5865F2', marginRight: 10, display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--header-primary)' }}> - {m.username[0].toUpperCase()} - </div> - <div style={{ flex: 1 }}> - <div style={{ color: 'var(--header-primary)', fontWeight: 'bold' }}>{m.username}</div> - <div style={{ display: 'flex', gap: 4 }}> - {m.roles?.map(r => ( - <span key={r._id} style={{ fontSize: 10, background: r.color, color: 'var(--header-primary)', padding: '2px 4px', borderRadius: 4 }}> - {r.name} - </span> - ))} - </div> - </div> - <div style={{ display: 'flex', gap: 4, alignItems: 'center' }}> - {canManageRoles && editableRoles.map(r => { - const hasRole = m.roles?.some(ur => ur._id === r._id); - return ( - <button - key={r._id} - onClick={() => handleAssignRole(r._id, m.id, !hasRole)} - style={{ - width: 16, height: 16, borderRadius: '50%', - border: `2px solid ${r.color}`, - background: hasRole ? r.color : 'transparent', - cursor: 'pointer' - }} - title={hasRole ? `Remove ${r.name}` : `Add ${r.name}`} - /> - ); - })} - {isCurrentUserAdmin && !isSelf && !isOwner && ( - <button - onClick={() => handleDeleteUser(m.id, m.username)} - style={{ - background: 'transparent', border: 'none', color: 'var(--header-secondary)', - cursor: 'pointer', fontSize: 14, padding: '4px 8px', marginLeft: 8, - borderRadius: 4, opacity: 0.5, transition: 'opacity 0.15s, color 0.15s', - }} - onMouseEnter={(e) => { e.currentTarget.style.opacity = '1'; e.currentTarget.style.color = '#ed4245'; }} - onMouseLeave={(e) => { e.currentTarget.style.opacity = '0.5'; e.currentTarget.style.color = 'var(--header-secondary)'; }} - title={`Delete ${m.username}`} - > - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M15 3.999V2H9V3.999H3V5.999H21V3.999H15Z" /> - <path d="M5 6.99902V18.999C5 20.101 5.897 20.999 7 20.999H17C18.103 20.999 19 20.101 19 18.999V6.99902H5ZM11 17H9V11H11V17ZM15 17H13V11H15V17Z" /> - </svg> - </button> - )} - </div> - </div> - ); - })} - </div> - ); - - const renderEmojiTab = () => ( - <div> - <div style={{ marginBottom: 20 }}> - <p style={{ color: 'var(--header-secondary)', fontSize: 14, margin: '0 0 16px' }}> - Add custom emoji that anyone can use in this server. - </p> - {myPermissions.manage_channels && ( - <> - <button - onClick={() => emojiFileInputRef.current?.click()} - style={{ - backgroundColor: '#5865F2', color: '#fff', border: 'none', - borderRadius: 3, padding: '8px 16px', cursor: 'pointer', - fontWeight: 600, fontSize: 14, - }} - > - Upload Emoji - </button> - <input - ref={emojiFileInputRef} - type="file" - accept="image/*,.gif" - onChange={handleEmojiFileSelect} - style={{ display: 'none' }} - /> - </> - )} - </div> - - {/* Emoji table */} - <div style={{ borderTop: '1px solid var(--border-subtle)' }}> - {/* Table header */} - <div style={{ - display: 'grid', gridTemplateColumns: '48px 1fr 1fr 40px', - padding: '8px 12px', alignItems: 'center', - borderBottom: '1px solid var(--border-subtle)', - }}> - <span style={{ color: 'var(--header-secondary)', fontSize: 12, fontWeight: 700, textTransform: 'uppercase' }}>Image</span> - <span style={{ color: 'var(--header-secondary)', fontSize: 12, fontWeight: 700, textTransform: 'uppercase' }}>Name</span> - <span style={{ color: 'var(--header-secondary)', fontSize: 12, fontWeight: 700, textTransform: 'uppercase' }}>Uploaded By</span> - <span /> - </div> - - {customEmojis.length === 0 ? ( - <div style={{ color: 'var(--header-secondary)', textAlign: 'center', padding: '40px 0' }}> - No custom emojis yet - </div> - ) : ( - customEmojis.map(emoji => ( - <div - key={emoji._id} - className="emoji-table-row" - style={{ - display: 'grid', gridTemplateColumns: '48px 1fr 1fr 40px', - padding: '8px 12px', alignItems: 'center', - borderBottom: '1px solid var(--border-subtle)', - }} - > - <img src={emoji.src} alt={emoji.name} style={{ width: 32, height: 32, objectFit: 'contain' }} /> - <span style={{ color: 'var(--header-primary)', fontSize: 15 }}>:{emoji.name}:</span> - <span style={{ color: 'var(--header-secondary)', fontSize: 14 }}>{emoji.uploadedByUsername}</span> - <div style={{ display: 'flex', justifyContent: 'center' }}> - {myPermissions.manage_channels && ( - <button - onClick={() => handleEmojiDelete(emoji._id)} - style={{ - background: 'transparent', border: 'none', color: 'var(--header-secondary)', - cursor: 'pointer', fontSize: 16, padding: '4px 8px', - borderRadius: 4, opacity: 0.5, transition: 'opacity 0.15s, color 0.15s', - }} - onMouseEnter={(e) => { e.currentTarget.style.opacity = '1'; e.currentTarget.style.color = '#ed4245'; }} - onMouseLeave={(e) => { e.currentTarget.style.opacity = '0.5'; e.currentTarget.style.color = 'var(--header-secondary)'; }} - title="Delete emoji" - > - ✕ - </button> - )} - </div> - </div> - )) - )} - </div> - </div> - ); - - const renderTabContent = () => { - switch (activeTab) { - case 'Emoji': return renderEmojiTab(); - case 'Roles': return renderRolesTab(); - case 'Members': return renderMembersTab(); - default: return ( - <div> - <label style={labelStyle}>SERVER ICON</label> - <div style={{ display: 'flex', alignItems: 'center', gap: '16px', marginBottom: 20 }}> - <div - className="server-icon-upload-wrapper" - onClick={() => myPermissions.manage_channels && iconInputRef.current?.click()} - style={{ cursor: myPermissions.manage_channels ? 'pointer' : 'default', opacity: myPermissions.manage_channels ? 1 : 0.5 }} - > - {currentIconUrl ? ( - <img src={currentIconUrl} alt="Server Icon" style={{ width: 80, height: 80, objectFit: 'cover', borderRadius: '16px' }} /> - ) : ( - <div style={{ - width: 80, height: 80, borderRadius: '16px', - backgroundColor: 'var(--bg-tertiary)', - display: 'flex', alignItems: 'center', justifyContent: 'center', - color: 'var(--text-normal)', fontWeight: 600, fontSize: '24px', - }}> - {serverName.substring(0, 2)} - </div> - )} - {myPermissions.manage_channels && ( - <div className="server-icon-upload-overlay"> - CHANGE<br/>ICON - </div> - )} - <input - ref={iconInputRef} - type="file" - accept="image/*" - onChange={handleIconFileChange} - style={{ display: 'none' }} - /> - </div> - <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}> - {iconDirty && myPermissions.manage_channels && ( - <button - onClick={handleSaveIcon} - disabled={savingIcon} - style={{ - backgroundColor: '#5865F2', color: '#fff', border: 'none', - borderRadius: 4, padding: '8px 16px', cursor: savingIcon ? 'not-allowed' : 'pointer', - fontWeight: 600, fontSize: 14, opacity: savingIcon ? 0.5 : 1, - }} - > - {savingIcon ? 'Saving...' : 'Upload Icon'} - </button> - )} - {currentIconUrl && !iconDirty && myPermissions.manage_channels && ( - <button - onClick={handleRemoveIcon} - disabled={savingIcon} - style={{ - backgroundColor: 'transparent', color: '#ed4245', border: '1px solid #ed4245', - borderRadius: 4, padding: '6px 12px', cursor: 'pointer', - fontWeight: 600, fontSize: 13, - }} - > - Remove Icon - </button> - )} - </div> - </div> - - <label style={labelStyle}>SERVER NAME</label> - <input - value={serverName} - onChange={(e) => { setServerName(e.target.value); setServerNameDirty(true); }} - disabled={!myPermissions.manage_channels} - maxLength={100} - style={{ - width: '100%', padding: 10, background: 'var(--bg-tertiary)', border: 'none', - borderRadius: 4, color: 'var(--header-primary)', marginBottom: 8, - opacity: myPermissions.manage_channels ? 1 : 0.5, - }} - /> - {serverNameDirty && myPermissions.manage_channels && ( - <button - onClick={handleSaveServerName} - disabled={!serverName.trim()} - style={{ - backgroundColor: '#5865F2', color: '#fff', border: 'none', - borderRadius: 4, padding: '8px 16px', cursor: 'pointer', - fontWeight: 600, fontSize: 14, marginBottom: 20, - opacity: serverName.trim() ? 1 : 0.5, - }} - > - Save Changes - </button> - )} - {!serverNameDirty && <div style={{ marginBottom: 12 }} />} - - <div style={{ color: 'var(--header-secondary)', marginBottom: 20, fontSize: '14px' }}>Region: US-East</div> - - <label style={labelStyle}>INACTIVE CHANNEL</label> - <select - value={afkChannelId} - onChange={(e) => { setAfkChannelId(e.target.value); setAfkDirty(true); }} - disabled={!myPermissions.manage_channels} - style={{ - width: '100%', padding: 10, background: 'var(--bg-tertiary)', border: 'none', - borderRadius: 4, color: 'var(--header-primary)', marginBottom: 20, - opacity: myPermissions.manage_channels ? 1 : 0.5, cursor: 'pointer', - appearance: 'auto', - }} - > - <option value="">No Inactive Channel</option> - {voiceChannels.map(ch => ( - <option key={ch._id} value={ch._id}>{ch.name}</option> - ))} - </select> - - <label style={labelStyle}>INACTIVE TIMEOUT</label> - <select - value={afkTimeout} - onChange={(e) => { setAfkTimeout(Number(e.target.value)); setAfkDirty(true); }} - disabled={!myPermissions.manage_channels} - style={{ - width: '100%', padding: 10, background: 'var(--bg-tertiary)', border: 'none', - borderRadius: 4, color: 'var(--header-primary)', marginBottom: 20, - opacity: myPermissions.manage_channels ? 1 : 0.5, cursor: 'pointer', - appearance: 'auto', - }} - > - {TIMEOUT_OPTIONS.map(opt => ( - <option key={opt.value} value={opt.value}>{opt.label}</option> - ))} - </select> - - {afkDirty && myPermissions.manage_channels && ( - <button - onClick={handleSaveAfkSettings} - style={{ - backgroundColor: '#5865F2', color: '#fff', border: 'none', - borderRadius: 4, padding: '8px 16px', cursor: 'pointer', - fontWeight: 600, fontSize: 14, - }} - > - Save Changes - </button> - )} - </div> - ); - } - }; - - // ─── Mobile render functions ─── - - const roleMemberCounts = React.useMemo(() => { - const counts = {}; - for (const m of members) { - for (const r of (m.roles || [])) { - counts[r._id] = (counts[r._id] || 0) + 1; - } - } - return counts; - }, [members]); - - const renderMobileHeader = (title, onBack, rightAction) => ( - <div className="msm-header"> - <button className="msm-back-btn" onClick={onBack}> - <svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg> - </button> - <div className="msm-header-title">{title}</div> - {rightAction || <div style={{ width: 32 }} />} - </div> - ); - - const renderMobileMenu = () => ( - <div className="msm-screen"> - <div className="msm-header"> - <div style={{ flex: 1 }} /> - <button className="msm-back-btn" onClick={onClose}> - <svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg> - </button> - </div> - <div className="msm-content"> - {/* Server icon + name */} - <div className="msm-icon-section"> - <div className="msm-icon-wrapper" onClick={() => myPermissions.manage_channels && iconInputRef.current?.click()}> - {currentIconUrl ? ( - <img src={currentIconUrl} alt="Server Icon" className="msm-icon-img" /> - ) : ( - <div className="msm-icon-placeholder">{serverName.substring(0, 2)}</div> - )} - {myPermissions.manage_channels && ( - <div className="msm-icon-badge"> - <svg width="14" height="14" viewBox="0 0 24 24" fill="#fff"><path d="M19 7v2.99s-1.99.01-2 0V7h-3s.01-1.99 0-2h3V2h2v3h3v2h-3zm-3 4V8h-3V5H5c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2v-8h-3zM5 19l3-4 2 3 3-4 4 5H5z"/></svg> - </div> - )} - <input ref={iconInputRef} type="file" accept="image/*" onChange={handleIconFileChange} style={{ display: 'none' }} /> - </div> - <div className="msm-icon-name">{serverName}</div> - </div> - - {/* Settings menu */} - <div className="msm-section-label">Settings</div> - <div className="msm-card"> - {[ - { key: 'overview', label: 'Overview', icon: <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg> }, - { key: 'emoji', label: 'Emoji', icon: <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/></svg> }, - { key: 'roles', label: 'Roles', icon: <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4z"/></svg> }, - { key: 'members', label: 'Members', icon: <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></svg> }, - ].map(item => ( - <div key={item.key} className="msm-card-item" onClick={() => setMobileScreen(item.key)}> - <span className="msm-card-item-icon">{item.icon}</span> - <span style={{ flex: 1, fontSize: 15, color: 'var(--header-primary)' }}>{item.label}</span> - <span className="msm-card-item-chevron"> - <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> - </span> - </div> - ))} - </div> - </div> - </div> - ); - - const renderMobileOverview = () => ( - <div className="msm-screen"> - {renderMobileHeader('Overview', mobileGoBack)} - <div className="msm-content"> - {/* Server icon (small) */} - <div className="msm-section-label">Server Icon</div> - <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 16 }}> - <div className="msm-icon-wrapper" style={{ width: 56, height: 56 }} onClick={() => myPermissions.manage_channels && iconInputRef.current?.click()}> - {currentIconUrl ? ( - <img src={currentIconUrl} alt="Icon" style={{ width: 56, height: 56, objectFit: 'cover', borderRadius: 16 }} /> - ) : ( - <div className="msm-icon-placeholder" style={{ width: 56, height: 56, fontSize: 18, borderRadius: 16 }}>{serverName.substring(0, 2)}</div> - )} - <input ref={iconInputRef} type="file" accept="image/*" onChange={handleIconFileChange} style={{ display: 'none' }} /> - </div> - <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}> - {iconDirty && myPermissions.manage_channels && ( - <button className="msm-btn-primary" onClick={handleSaveIcon} disabled={savingIcon} style={{ fontSize: 13, padding: '6px 14px' }}> - {savingIcon ? 'Saving...' : 'Save Icon'} - </button> - )} - {currentIconUrl && !iconDirty && myPermissions.manage_channels && ( - <button className="msm-btn-danger-outline" onClick={handleRemoveIcon} disabled={savingIcon} style={{ fontSize: 13, padding: '5px 12px' }}> - Remove - </button> - )} - </div> - </div> - - {/* Server name */} - <div className="msm-section-label">Server Name</div> - <input - className="msm-input" - value={serverName} - onChange={(e) => { setServerName(e.target.value); setServerNameDirty(true); }} - disabled={!myPermissions.manage_channels} - maxLength={100} - style={{ opacity: myPermissions.manage_channels ? 1 : 0.5 }} - /> - {serverNameDirty && myPermissions.manage_channels && ( - <button className="msm-btn-primary msm-btn-full" onClick={handleSaveServerName} disabled={!serverName.trim()} style={{ marginTop: 8 }}> - Save Changes - </button> - )} - - {/* AFK settings */} - <div className="msm-section-label">Inactive Channel</div> - <select - className="msm-select" - value={afkChannelId} - onChange={(e) => { setAfkChannelId(e.target.value); setAfkDirty(true); }} - disabled={!myPermissions.manage_channels} - style={{ opacity: myPermissions.manage_channels ? 1 : 0.5 }} - > - <option value="">No Inactive Channel</option> - {voiceChannels.map(ch => ( - <option key={ch._id} value={ch._id}>{ch.name}</option> - ))} - </select> - - <div className="msm-section-label">Inactive Timeout</div> - <select - className="msm-select" - value={afkTimeout} - onChange={(e) => { setAfkTimeout(Number(e.target.value)); setAfkDirty(true); }} - disabled={!myPermissions.manage_channels} - style={{ opacity: myPermissions.manage_channels ? 1 : 0.5 }} - > - {TIMEOUT_OPTIONS.map(opt => ( - <option key={opt.value} value={opt.value}>{opt.label}</option> - ))} - </select> - - {afkDirty && myPermissions.manage_channels && ( - <button className="msm-btn-primary msm-btn-full" onClick={handleSaveAfkSettings} style={{ marginTop: 8 }}> - Save Changes - </button> - )} - </div> - </div> - ); - - const renderMobileEmoji = () => ( - <div className="msm-screen"> - {renderMobileHeader('Emoji', mobileGoBack)} - <div className="msm-content"> - {myPermissions.manage_channels && ( - <> - <button className="msm-btn-primary msm-btn-full" onClick={() => emojiFileInputRef.current?.click()} style={{ marginBottom: 12 }}> - Upload Emoji - </button> - <input ref={emojiFileInputRef} type="file" accept="image/*,.gif" onChange={handleEmojiFileSelect} style={{ display: 'none' }} /> - </> - )} - <p className="msm-description">Add custom emoji that anyone can use in this server.</p> - - {customEmojis.length === 0 ? ( - <div className="msm-empty">No custom emojis yet</div> - ) : ( - <div className="msm-card"> - {customEmojis.map(emoji => ( - <div key={emoji._id} className="msm-emoji-row"> - <img src={emoji.src} alt={emoji.name} className="msm-emoji-img" /> - <div className="msm-emoji-info"> - <span className="msm-emoji-name">:{emoji.name}:</span> - <span className="msm-emoji-uploader">{emoji.uploadedByUsername}</span> - </div> - {myPermissions.manage_channels && ( - <button className="msm-emoji-delete" onClick={() => handleEmojiDelete(emoji._id)}> - <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg> - </button> - )} - </div> - ))} - </div> - )} - </div> - </div> - ); - - const renderMobileRoles = () => ( - <div className="msm-screen"> - {renderMobileHeader('Roles', mobileGoBack, canManageRoles ? ( - <button className="msm-header-action" onClick={handleCreateRole}> - <svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg> - </button> - ) : null)} - <div className="msm-content"> - <p className="msm-description">Roles let you organize members and customize permissions.</p> - - {/* @everyone */} - <div className="msm-card" style={{ marginBottom: 16 }}> - <div className="msm-card-item" onClick={() => mobileSelectRole(editableRoles.find(r => r.name === '@everyone') || editableRoles[0])}> - <span className="msm-role-dot" style={{ backgroundColor: '#99aab5' }} /> - <span style={{ flex: 1, color: 'var(--header-primary)', fontSize: 15 }}>@everyone</span> - <span className="msm-role-count">{members.length}</span> - <span className="msm-card-item-chevron"> - <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> - </span> - </div> - </div> - - {/* Other roles */} - {editableRoles.filter(r => r.name !== '@everyone').length > 0 && ( - <> - <div className="msm-section-label">Roles - {editableRoles.filter(r => r.name !== '@everyone').length}</div> - <div className="msm-card"> - {editableRoles.filter(r => r.name !== '@everyone').map(r => ( - <div key={r._id} className="msm-card-item" onClick={() => mobileSelectRole(r)}> - <span className="msm-role-dot" style={{ backgroundColor: r.color || '#99aab5' }} /> - <span style={{ flex: 1, color: 'var(--header-primary)', fontSize: 15 }}>{r.name}</span> - <span className="msm-role-count">{roleMemberCounts[r._id] || 0}</span> - <span className="msm-card-item-chevron"> - <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> - </span> - </div> - ))} - </div> - </> - )} - </div> - </div> - ); - - const renderMobileRoleEdit = () => { - if (!selectedRole) return renderMobileRoles(); - const permList = ['manage_channels', 'manage_roles', 'manage_nicknames', 'create_invite', 'embed_links', 'attach_files', 'move_members', 'mute_members']; - - return ( - <div className="msm-screen"> - {renderMobileHeader(`Edit Role`, () => setMobileScreen('roles'))} - <div className="msm-content"> - {/* Role name */} - <div className="msm-section-label">Role Name</div> - <input - className="msm-input" - value={selectedRole.name} - onChange={(e) => handleUpdateRole(selectedRole._id, { name: e.target.value })} - disabled={!canManageRoles} - style={{ opacity: canManageRoles ? 1 : 0.5 }} - /> - - {/* Role color */} - <div className="msm-section-label">Role Color</div> - <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 8 }}> - <input - type="color" - className="msm-color-input" - value={selectedRole.color} - onChange={(e) => handleUpdateRole(selectedRole._id, { color: e.target.value })} - disabled={!canManageRoles} - /> - <span style={{ color: 'var(--header-secondary)', fontSize: 14 }}>{selectedRole.color}</span> - </div> - - {/* Display separately toggle (isHoist) */} - <div className="msm-section-label">Display</div> - <div className="msm-card"> - <div className="msm-card-item" onClick={() => canManageRoles && handleUpdateRole(selectedRole._id, { isHoist: !selectedRole.isHoist })}> - <span style={{ flex: 1, color: 'var(--header-primary)', fontSize: 15 }}>Display separately</span> - <div className={`msm-toggle ${selectedRole.isHoist ? 'msm-toggle-on' : ''}`}> - <div className="msm-toggle-knob" /> - </div> - </div> - </div> - - {/* Permissions */} - <div className="msm-section-label">Permissions</div> - <div className="msm-card"> - {permList.map(perm => ( - <div key={perm} className="msm-card-item" onClick={() => canManageRoles && handleUpdateRole(selectedRole._id, { permissions: { ...selectedRole.permissions, [perm]: !selectedRole.permissions?.[perm] } })}> - <span style={{ flex: 1, color: 'var(--header-primary)', fontSize: 15, textTransform: 'capitalize' }}>{perm.replace(/_/g, ' ')}</span> - <div className={`msm-toggle ${selectedRole.permissions?.[perm] ? 'msm-toggle-on' : ''}`}> - <div className="msm-toggle-knob" /> - </div> - </div> - ))} - </div> - - {/* Delete */} - {canManageRoles && selectedRole.name !== 'Owner' && selectedRole.name !== '@everyone' && ( - <div className="msm-card" style={{ marginTop: 24 }}> - <div className="msm-card-item msm-card-item-danger" onClick={() => handleDeleteRole(selectedRole._id)}> - Delete Role - </div> - </div> - )} - </div> - </div> - ); - }; - - const renderMobileMembers = () => ( - <div className="msm-screen"> - {renderMobileHeader('Members', mobileGoBack)} - <div className="msm-content"> - {members.length === 0 ? ( - <div className="msm-empty">No members found</div> - ) : ( - <div className="msm-card"> - {members.map(m => ( - <div key={m.id} className="msm-member-row"> - <div className="msm-member-avatar"> - {m.avatarUrl ? ( - <img src={m.avatarUrl} alt="" style={{ width: 36, height: 36, borderRadius: '50%' }} /> - ) : ( - m.username[0].toUpperCase() - )} - </div> - <div className="msm-member-info"> - <div className="msm-member-name">{m.username}</div> - <div className="msm-member-roles"> - {m.roles?.map(r => ( - <span key={r._id} className="msm-member-role-pill" style={{ backgroundColor: r.color + '33', color: r.color, borderColor: r.color + '66' }}> - {r.name} - </span> - ))} - </div> - </div> - {canManageRoles && ( - <div style={{ display: 'flex', gap: 6, flexShrink: 0 }}> - {editableRoles.map(r => { - const hasRole = m.roles?.some(ur => ur._id === r._id); - return ( - <button - key={r._id} - className="msm-member-role-toggle" - onClick={() => handleAssignRole(r._id, m.id, !hasRole)} - style={{ - borderColor: r.color, - backgroundColor: hasRole ? r.color : 'transparent', - }} - title={hasRole ? `Remove ${r.name}` : `Add ${r.name}`} - /> - ); - })} - </div> - )} - </div> - ))} - </div> - )} - </div> - </div> - ); - - const renderMobileContent = () => { - switch (mobileScreen) { - case 'overview': return renderMobileOverview(); - case 'emoji': return renderMobileEmoji(); - case 'roles': return renderMobileRoles(); - case 'role-edit': return renderMobileRoleEdit(); - case 'members': return renderMobileMembers(); - default: return renderMobileMenu(); - } - }; - - // ─── Shared modals ─── - const sharedModals = ( - <> - {showIconCropModal && rawIconUrl && ( - <AvatarCropModal - imageUrl={rawIconUrl} - onApply={handleIconCropApply} - onCancel={handleIconCropCancel} - cropShape="rect" - /> - )} - {showEmojiModal && emojiPreviewUrl && ( - <div - onClick={handleEmojiModalClose} - style={{ - position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, - backgroundColor: 'rgba(0,0,0,0.7)', zIndex: 2000, - display: 'flex', alignItems: 'center', justifyContent: 'center', - }} - > - <div - onClick={(e) => e.stopPropagation()} - style={{ - backgroundColor: 'var(--bg-secondary)', borderRadius: 8, - width: 580, maxWidth: '90vw', overflow: 'hidden', - }} - > - <div style={{ - display: 'flex', alignItems: 'center', justifyContent: 'space-between', - padding: '16px 16px 0', - }}> - <h2 style={{ color: 'var(--header-primary)', margin: 0, fontSize: 20, fontWeight: 600 }}>Add Emoji</h2> - <button - onClick={handleEmojiModalClose} - style={{ - background: 'transparent', border: 'none', color: 'var(--header-secondary)', - cursor: 'pointer', fontSize: 20, padding: '4px 8px', - }} - > - ✕ - </button> - </div> - <div style={{ display: 'flex', padding: '20px 16px 16px', gap: 24 }}> - <div style={{ width: 240, minWidth: 240, display: 'flex', flexDirection: 'column', gap: 10 }}> - <div style={{ - width: 240, height: 240, position: 'relative', - backgroundColor: 'var(--bg-tertiary)', borderRadius: 8, - overflow: 'hidden', - }}> - <Cropper - image={emojiPreviewUrl} - crop={emojiCrop} - zoom={emojiZoom} - rotation={emojiRotation} - aspect={1} - cropShape="rect" - showGrid={false} - onCropChange={setEmojiCrop} - onZoomChange={setEmojiZoom} - onCropComplete={onEmojiCropComplete} - style={{ - containerStyle: { width: 240, height: 240 }, - mediaStyle: { - transform: `${emojiFlipH ? 'scaleX(-1)' : ''} ${emojiFlipV ? 'scaleY(-1)' : ''}`.trim() || undefined, - }, - }} - /> - </div> - <div style={{ display: 'flex', justifyContent: 'center', gap: 4 }}> - <button onClick={() => setEmojiRotation((r) => (r - 90 + 360) % 360)} title="Rotate left" style={{ width: 36, height: 36, borderRadius: 4, background: 'var(--bg-tertiary)', border: 'none', color: 'var(--header-secondary)', cursor: 'pointer', fontSize: 16, display: 'flex', alignItems: 'center', justifyContent: 'center' }}> - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M7.11 8.53L5.7 7.11C4.8 8.27 4.24 9.61 4.07 11h2.02c.14-.87.49-1.72 1.02-2.47zM6.09 13H4.07c.17 1.39.72 2.73 1.62 3.89l1.41-1.42c-.52-.75-.87-1.59-1.01-2.47zm1.01 5.32c1.16.9 2.51 1.44 3.9 1.61V17.9c-.87-.15-1.71-.49-2.46-1.03L7.1 18.32zM13 4.07V1L8.45 5.55 13 10V6.09c2.84.48 5 2.94 5 5.91s-2.16 5.43-5 5.91v2.02c3.95-.49 7-3.85 7-7.93s-3.05-7.44-7-7.93z"/></svg> - </button> - <button onClick={() => setEmojiRotation((r) => (r + 90) % 360)} title="Rotate right" style={{ width: 36, height: 36, borderRadius: 4, background: 'var(--bg-tertiary)', border: 'none', color: 'var(--header-secondary)', cursor: 'pointer', fontSize: 16, display: 'flex', alignItems: 'center', justifyContent: 'center' }}> - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style={{ transform: 'scaleX(-1)' }}><path d="M7.11 8.53L5.7 7.11C4.8 8.27 4.24 9.61 4.07 11h2.02c.14-.87.49-1.72 1.02-2.47zM6.09 13H4.07c.17 1.39.72 2.73 1.62 3.89l1.41-1.42c-.52-.75-.87-1.59-1.01-2.47zm1.01 5.32c1.16.9 2.51 1.44 3.9 1.61V17.9c-.87-.15-1.71-.49-2.46-1.03L7.1 18.32zM13 4.07V1L8.45 5.55 13 10V6.09c2.84.48 5 2.94 5 5.91s-2.16 5.43-5 5.91v2.02c3.95-.49 7-3.85 7-7.93s-3.05-7.44-7-7.93z"/></svg> - </button> - <button onClick={() => setEmojiFlipH((f) => !f)} title="Flip horizontal" style={{ width: 36, height: 36, borderRadius: 4, background: emojiFlipH ? 'rgba(88, 101, 242, 0.3)' : 'var(--bg-tertiary)', border: emojiFlipH ? '1px solid #5865F2' : 'none', color: 'var(--header-secondary)', cursor: 'pointer', fontSize: 16, display: 'flex', alignItems: 'center', justifyContent: 'center' }}> - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M15 21h2v-2h-2v2zm4-12h2V7h-2v2zM3 5v14c0 1.1.9 2 2 2h4v-2H5V5h4V3H5c-1.1 0-2 .9-2 2zm16-2v2h2c0-1.1-.9-2-2-2zm-8-2h2v24h-2V1zm8 8h2v2h-2v-2zm0 8c1.1 0 2-.9 2-2h-2v2zm0-4h2v-2h-2v2zm-4 8h2v-2h-2v2zm4-16h2v-2h-2v2z"/></svg> - </button> - <button onClick={() => setEmojiFlipV((f) => !f)} title="Flip vertical" style={{ width: 36, height: 36, borderRadius: 4, background: emojiFlipV ? 'rgba(88, 101, 242, 0.3)' : 'var(--bg-tertiary)', border: emojiFlipV ? '1px solid #5865F2' : 'none', color: 'var(--header-secondary)', cursor: 'pointer', fontSize: 16, display: 'flex', alignItems: 'center', justifyContent: 'center' }}> - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style={{ transform: 'rotate(90deg)' }}><path d="M15 21h2v-2h-2v2zm4-12h2V7h-2v2zM3 5v14c0 1.1.9 2 2 2h4v-2H5V5h4V3H5c-1.1 0-2 .9-2 2zm16-2v2h2c0-1.1-.9-2-2-2zm-8-2h2v24h-2V1zm8 8h2v2h-2v-2zm0 8c1.1 0 2-.9 2-2h-2v2zm0-4h2v-2h-2v2zm-4 8h2v-2h-2v2zm4-16h2v-2h-2v2z"/></svg> - </button> - </div> - <div className="avatar-crop-slider-row" style={{ padding: 0, margin: 0 }}> - <svg width="16" height="16" viewBox="0 0 24 24" fill="var(--header-secondary)"><path d="M15 3H9v2h6V3zm-4 18h2v-2h-2v2zm8-15.97l-1.41-1.41-1.76 1.76 1.41 1.41L19 5.03zM4.76 4.38L3.34 5.8 5.1 7.56 6.52 6.14 4.76 4.38zM21 11h-2v2h2v-2zM5 11H3v2h2v-2zm7-4a5 5 0 100 10 5 5 0 000-10z"/></svg> - <input type="range" min={1} max={3} step={0.01} value={emojiZoom} onChange={(e) => setEmojiZoom(Number(e.target.value))} className="avatar-crop-slider" /> - <svg width="20" height="20" viewBox="0 0 24 24" fill="var(--header-secondary)"><path d="M15 3H9v2h6V3zm-4 18h2v-2h-2v2zm8-15.97l-1.41-1.41-1.76 1.76 1.41 1.41L19 5.03zM4.76 4.38L3.34 5.8 5.1 7.56 6.52 6.14 4.76 4.38zM21 11h-2v2h2v-2zM5 11H3v2h2v-2zm7-4a5 5 0 100 10 5 5 0 000-10z"/></svg> - </div> - </div> - <div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}> - <div style={{ marginBottom: 16 }}> - <span style={{ color: 'var(--header-secondary)', fontSize: 12, fontWeight: 700, textTransform: 'uppercase', display: 'block', marginBottom: 8 }}>Preview</span> - <div style={{ display: 'inline-flex', alignItems: 'center', gap: 4, backgroundColor: 'rgba(88, 101, 242, 0.15)', border: '1px solid var(--brand-experiment, #5865F2)', borderRadius: 8, padding: '2px 6px', cursor: 'default' }}> - <img src={emojiPreviewUrl} alt="" style={{ width: 16, height: 16, objectFit: 'contain', transform: `${emojiFlipH ? 'scaleX(-1)' : ''} ${emojiFlipV ? 'scaleY(-1)' : ''}`.trim() || undefined }} /> - <span style={{ color: 'var(--text-normal)', fontSize: 14, marginLeft: 2 }}>1</span> - </div> - </div> - <div style={{ marginBottom: 16 }}> - <label style={{ color: 'var(--header-secondary)', fontSize: 12, fontWeight: 700, textTransform: 'uppercase', display: 'block', marginBottom: 8 }}> - Emoji name <span style={{ color: '#ed4245' }}>*</span> - </label> - <div style={{ position: 'relative' }}> - <input type="text" value={emojiName} onChange={(e) => { setEmojiName(e.target.value); setEmojiError(''); }} maxLength={32} style={{ width: '100%', padding: '10px 32px 10px 10px', background: 'var(--bg-tertiary)', border: 'none', borderRadius: 4, color: 'var(--header-primary)', fontSize: 14, boxSizing: 'border-box' }} /> - {emojiName && ( - <button onClick={() => setEmojiName('')} style={{ position: 'absolute', right: 8, top: '50%', transform: 'translateY(-50%)', background: 'transparent', border: 'none', color: 'var(--header-secondary)', cursor: 'pointer', fontSize: 14, padding: '2px 4px' }}>✕</button> - )} - </div> - {emojiError && <div style={{ color: '#ed4245', fontSize: 13, marginTop: 6 }}>{emojiError}</div>} - </div> - <div style={{ flex: 1 }} /> - <button onClick={handleEmojiUpload} disabled={emojiUploading || !emojiName.trim()} style={{ backgroundColor: '#5865F2', color: '#fff', border: 'none', borderRadius: 3, padding: '10px 0', cursor: emojiUploading ? 'not-allowed' : 'pointer', fontWeight: 600, fontSize: 14, width: '100%', opacity: (emojiUploading || !emojiName.trim()) ? 0.5 : 1 }}> - {emojiUploading ? 'Uploading...' : 'Finish'} - </button> - </div> - </div> - </div> - </div> - )} - </> - ); - - // ─── Main return ─── - - if (isMobile) { - return ReactDOM.createPortal( - <div className="msm-root"> - {renderMobileContent()} - {sharedModals} - </div>, - document.body - ); - } - - return ( - <div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'var(--bg-primary)', zIndex: 1000, display: 'flex', color: 'var(--text-normal)' }}> - {renderSidebar()} - <div style={{ flex: 1, display: 'flex', justifyContent: 'flex-start', overflowY: 'auto' }}> - <div style={{ flex: 1, maxWidth: '740px', padding: '60px 40px 80px', position: 'relative' }}> - <h2 style={{ color: 'var(--header-primary)', margin: 0, marginBottom: 20 }}>{activeTab}</h2> - {renderTabContent()} - </div> - <div style={{ flex: '0 0 36px', paddingTop: '60px', marginLeft: '8px' }}> - <button - onClick={onClose} - style={{ - width: '36px', height: '36px', borderRadius: '50%', - border: '2px solid var(--header-secondary)', background: 'transparent', - color: 'var(--header-secondary)', cursor: 'pointer', - display: 'flex', alignItems: 'center', justifyContent: 'center', - fontSize: '18px', - }} - > - ✕ - </button> - <div style={{ fontSize: '13px', fontWeight: '600', color: 'var(--header-secondary)', textAlign: 'center', marginTop: '4px' }}> - ESC - </div> - </div> - <div style={{ flex: '0.5' }} /> - </div> - {sharedModals} - </div> - ); -}; - -export default ServerSettingsModal; diff --git a/packages/shared/src/components/Sidebar.jsx b/packages/shared/src/components/Sidebar.jsx deleted file mode 100644 index bafe111..0000000 --- a/packages/shared/src/components/Sidebar.jsx +++ /dev/null @@ -1,2663 +0,0 @@ -import React, { useState, useRef, useEffect, useLayoutEffect, useCallback } from "react"; -import { useNavigate } from "react-router-dom"; -import { useConvex, useMutation, useQuery } from "convex/react"; -import { api } from "../../../../convex/_generated/api"; -import Tooltip from "./Tooltip"; -import { useVoice } from "../contexts/VoiceContext"; -import ChannelSettingsModal from "./ChannelSettingsModal"; -import ServerSettingsModal from "./ServerSettingsModal"; -import ScreenShareModal from "./ScreenShareModal"; -import MobileServerDrawer from "./MobileServerDrawer"; -import MobileCreateChannelScreen from "./MobileCreateChannelScreen"; -import MobileCreateCategoryScreen from "./MobileCreateCategoryScreen"; -import MobileChannelDrawer from "./MobileChannelDrawer"; -import MobileChannelSettingsScreen from "./MobileChannelSettingsScreen"; -import DMList from "./DMList"; -import Avatar from "./Avatar"; -import UserSettings from "./UserSettings"; -import ChangeNicknameModal from "./ChangeNicknameModal"; -import { Track } from "livekit-client"; -import { - DndContext, - closestCenter, - PointerSensor, - useSensor, - useSensors, - DragOverlay, - useDraggable, -} from "@dnd-kit/core"; -import { SortableContext, verticalListSortingStrategy, useSortable } from "@dnd-kit/sortable"; -import { CSS } from "@dnd-kit/utilities"; -import muteIcon from "../assets/icons/mute.svg"; -import mutedIcon from "../assets/icons/muted.svg"; -import defeanIcon from "../assets/icons/defean.svg"; -import defeanedIcon from "../assets/icons/defeaned.svg"; -import settingsIcon from "../assets/icons/settings.svg"; -import voiceIcon from "../assets/icons/voice.svg"; -import disconnectIcon from "../assets/icons/disconnect.svg"; -import cameraIcon from "../assets/icons/camera.svg"; -import screenIcon from "../assets/icons/screen.svg"; -import inviteUserIcon from "../assets/icons/invite_user.svg"; -import personalMuteIcon from "../assets/icons/personal_mute.svg"; -import serverMuteIcon from "../assets/icons/server_mute.svg"; -import categoryCollapsedIcon from "../assets/icons/category_collapsed_icon.svg"; -import PingSound from "../assets/sounds/ping.mp3"; -import screenShareStartSound from "../assets/sounds/screenshare_start.mp3"; -import screenShareStopSound from "../assets/sounds/screenshare_stop.mp3"; -import { getUserPref, setUserPref } from "../utils/userPreferences"; -import { usePlatform } from "../platform"; -import ColoredIcon from "./ColoredIcon"; - -const USER_COLORS = ["#5865F2", "#EBA7CD", "#57F287", "#FEE75C", "#EB459E", "#ED4245"]; - -const ICON_COLOR_DEFAULT = "hsl(240, 4.294%, 68.039%)"; -const ICON_COLOR_ACTIVE = "hsl(357.692, 67.826%, 54.902%)"; -const SERVER_MUTE_RED = "hsl(1.343, 84.81%, 69.02%)"; - -const controlButtonStyle = { - background: "transparent", - border: "none", - cursor: "pointer", - padding: "6px", - borderRadius: "4px", - display: "flex", - alignItems: "center", - justifyContent: "center", -}; - -function getUserColor(name) { - let hash = 0; - for (let i = 0; i < name.length; i++) { - hash = name.charCodeAt(i) + ((hash << 5) - hash); - } - return USER_COLORS[Math.abs(hash) % USER_COLORS.length]; -} - -function bytesToHex(bytes) { - return Array.from(bytes) - .map((b) => b.toString(16).padStart(2, "0")) - .join(""); -} - -function randomHex(length) { - const bytes = new Uint8Array(length); - crypto.getRandomValues(bytes); - return bytesToHex(bytes); -} - -const VoiceTimer = () => { - const [elapsed, setElapsed] = React.useState(0); - React.useEffect(() => { - const start = Date.now(); - const interval = setInterval(() => setElapsed(Math.floor((Date.now() - start) / 1000)), 1000); - return () => clearInterval(interval); - }, []); - const hours = Math.floor(elapsed / 3600); - const mins = Math.floor((elapsed % 3600) / 60); - const secs = elapsed % 60; - const time = - hours > 0 - ? `${hours}:${String(mins).padStart(2, "0")}:${String(secs).padStart(2, "0")}` - : `${mins}:${String(secs).padStart(2, "0")}`; - return <span className="voice-timer">{time}</span>; -}; - -const STATUS_OPTIONS = [ - { value: "online", label: "Online", color: "#3ba55c" }, - { value: "idle", label: "Idle", color: "#faa61a" }, - { value: "dnd", label: "Do Not Disturb", color: "#ed4245" }, - { value: "invisible", label: "Invisible", color: "#747f8d" }, -]; - -const UserControlPanel = React.memo(({ username, userId }) => { - const { session, idle, searchDB } = usePlatform(); - const { isMuted, isDeafened, toggleMute, toggleDeafen, connectionState, disconnectVoice } = - useVoice(); - const [showStatusMenu, setShowStatusMenu] = useState(false); - const [showUserSettings, setShowUserSettings] = useState(false); - const [currentStatus, setCurrentStatus] = useState("online"); - const updateStatusMutation = useMutation(api.auth.updateStatus); - const navigate = useNavigate(); - const manualStatusRef = useRef(false); - const preIdleStatusRef = useRef("online"); - const hasInitializedRef = useRef(false); - const currentStatusRef = useRef(currentStatus); - currentStatusRef.current = currentStatus; - - // Fetch stored status preference from server and sync local state - const allUsers = useQuery(api.auth.getPublicKeys) || []; - const myUser = allUsers.find((u) => u.id === userId); - React.useEffect(() => { - if (myUser) { - const isInitial = !hasInitializedRef.current; - if (isInitial) hasInitializedRef.current = true; - - // 'idle' is auto-set by the idle detector, not a user preference — - // on a fresh app launch, reset it to 'online' just like 'offline' - const shouldReset = - !myUser.status || myUser.status === "offline" || (isInitial && myUser.status === "idle"); - - if (shouldReset) { - setCurrentStatus("online"); - manualStatusRef.current = false; - if (userId) { - updateStatusMutation({ userId, status: "online" }).catch(() => {}); - } - } else if (myUser.status) { - setCurrentStatus(myUser.status); - manualStatusRef.current = myUser.status === "dnd" || myUser.status === "invisible"; - } - } - }, [myUser?.status]); - - const handleLogout = async () => { - // Disconnect voice if connected - if (connectionState === "connected") { - try { - disconnectVoice(); - } catch {} - } - // Save and close search DB - if (searchDB?.isOpen()) { - try { - await searchDB.save(); - searchDB.close(); - } catch {} - } - // Clear persisted session - if (session) { - try { - await session.clear(); - } catch {} - } - // Clear storage (preserve theme and user preferences) - const theme = localStorage.getItem("theme"); - const savedPrefs = {}; - for (let i = 0; i < localStorage.length; i++) { - const key = localStorage.key(i); - if (key.startsWith("userPrefs_")) { - savedPrefs[key] = localStorage.getItem(key); - } - } - localStorage.clear(); - if (theme) localStorage.setItem("theme", theme); - for (const [key, value] of Object.entries(savedPrefs)) { - localStorage.setItem(key, value); - } - sessionStorage.clear(); - navigate("/"); - }; - - const effectiveMute = isMuted || isDeafened; - const statusColor = STATUS_OPTIONS.find((s) => s.value === currentStatus)?.color || "#3ba55c"; - - const handleStatusChange = async (status) => { - manualStatusRef.current = status !== "online"; - setCurrentStatus(status); - setShowStatusMenu(false); - if (userId) { - try { - await updateStatusMutation({ userId, status }); - } catch (e) { - console.error("Failed to update status:", e); - } - } - }; - - // Auto-idle detection via platform idle API - // On Capacitor (Android), skip this entirely — presence disconnect handles - // offline when not in voice, and VoiceContext AFK polling handles idle - // after 5 min of not talking when in voice. - useEffect(() => { - if (!idle || !userId) return; - if (window.Capacitor?.isNativePlatform?.()) return; - - const handleIdleChange = (data) => { - if (manualStatusRef.current) return; - if (data.isIdle) { - preIdleStatusRef.current = currentStatusRef.current; - setCurrentStatus("idle"); - updateStatusMutation({ userId, status: "idle" }).catch(() => {}); - } else { - const restoreTo = preIdleStatusRef.current || "online"; - setCurrentStatus(restoreTo); - updateStatusMutation({ userId, status: restoreTo }).catch(() => {}); - } - }; - idle.onIdleStateChanged(handleIdleChange); - return () => idle.removeIdleStateListener(); - }, [userId]); - - return ( - <div - style={{ - height: "64px", - margin: "0 8px 8px 8px", - borderRadius: connectionState === "connected" ? "0px 0px 8px 8px" : "8px", - backgroundColor: "var(--panel-bg)", - display: "flex", - alignItems: "center", - padding: "0 8px", - flexShrink: 0, - position: "relative", - }} - > - {showStatusMenu && ( - <div className="status-menu"> - {STATUS_OPTIONS.map((opt) => ( - <div - key={opt.value} - className="status-menu-item" - onClick={() => handleStatusChange(opt.value)} - > - <div className="status-menu-dot" style={{ backgroundColor: opt.color }} /> - <span>{opt.label}</span> - </div> - ))} - </div> - )} - <div className="user-control-info" onClick={() => setShowStatusMenu(!showStatusMenu)}> - <div style={{ position: "relative", marginRight: "8px" }}> - <Avatar username={username} avatarUrl={myUser?.avatarUrl} size={32} /> - <div - style={{ - position: "absolute", - bottom: "-2px", - right: "-2px", - width: "10px", - height: "10px", - borderRadius: "50%", - backgroundColor: statusColor, - border: "2px solid var(--panel-bg)", - cursor: "pointer", - }} - /> - </div> - <div style={{ display: "flex", flexDirection: "column", overflow: "hidden" }}> - <div - style={{ - color: "var(--header-primary)", - fontWeight: "600", - fontSize: "14px", - lineHeight: "18px", - whiteSpace: "nowrap", - overflow: "hidden", - textOverflow: "ellipsis", - }} - > - {username || "Unknown"} - </div> - <div style={{ color: "var(--header-secondary)", fontSize: "12px", lineHeight: "13px" }}> - {STATUS_OPTIONS.find((s) => s.value === currentStatus)?.label || "Online"} - </div> - </div> - </div> - - <div style={{ display: "flex" }}> - <Tooltip text={effectiveMute ? "Unmute" : "Mute"} position="top"> - <button onClick={toggleMute} style={controlButtonStyle}> - <ColoredIcon - src={effectiveMute ? mutedIcon : muteIcon} - color={effectiveMute ? ICON_COLOR_ACTIVE : ICON_COLOR_DEFAULT} - /> - </button> - </Tooltip> - <Tooltip text={isDeafened ? "Undeafen" : "Deafen"} position="top"> - <button onClick={toggleDeafen} style={controlButtonStyle}> - <ColoredIcon - src={isDeafened ? defeanedIcon : defeanIcon} - color={isDeafened ? ICON_COLOR_ACTIVE : ICON_COLOR_DEFAULT} - /> - </button> - </Tooltip> - <Tooltip text="User Settings" position="top"> - <button style={controlButtonStyle} onClick={() => setShowUserSettings(true)}> - <ColoredIcon src={settingsIcon} color={ICON_COLOR_DEFAULT} /> - </button> - </Tooltip> - </div> - {showUserSettings && ( - <UserSettings - onClose={() => setShowUserSettings(false)} - userId={userId} - username={username} - onLogout={handleLogout} - /> - )} - </div> - ); -}); - -const headerButtonStyle = { - background: "transparent", - border: "none", - color: "var(--header-secondary)", - cursor: "pointer", - fontSize: "18px", - padding: "0 4px", -}; - -const voicePanelButtonStyle = { - flex: 1, - alignItems: "center", - minHeight: "32px", - background: "hsla(240, 4%, 60.784%, 0.078)", - border: "hsla(0, 0%, 100%, 0.078)", - borderColor: "hsla(240, 4%, 60.784%, 0.039)", - borderRadius: "8px", - cursor: "pointer", - padding: "4px", - display: "flex", - justifyContent: "center", -}; - -const liveBadgeStyle = { - backgroundColor: "#ed4245", - borderRadius: "8px", - padding: "0 6px", - textOverflow: "ellipsis", - whiteSpace: "nowrap", - overflow: "hidden", - textAlign: "center", - height: "16px", - minHeight: "16px", - minWidth: "16px", - color: "hsl(0, 0%, 100%)", - fontSize: "12px", - fontWeight: "700", - letterSpacing: ".02em", - lineHeight: "1.3333333333333333", - textTransform: "uppercase", - display: "flex", - alignItems: "center", - marginRight: "4px", -}; - -const ACTIVE_SPEAKER_SHADOW = - "rgb(67, 162, 90) 0px 0px 0px 2px, rgb(67, 162, 90) 0px 0px 0px 20px inset, rgb(26, 26, 30) 0px 0px 0px 20px inset"; -const VOICE_ACTIVE_COLOR = "hsl(132.809, 34.902%, 50%)"; - -async function encryptKeyForUsers(convex, channelId, keyHex, crypto) { - const users = await convex.query(api.auth.getPublicKeys, {}); - const batchKeys = []; - - for (const u of users) { - if (!u.public_identity_key) continue; - try { - const payload = JSON.stringify({ [channelId]: keyHex }); - const encryptedKeyHex = await crypto.publicEncrypt(u.public_identity_key, payload); - batchKeys.push({ - channelId, - userId: u.id, - encryptedKeyBundle: encryptedKeyHex, - keyVersion: 1, - }); - } catch (e) { - console.error("Failed to encrypt for user", u.id, e); - } - } - - await convex.mutation(api.channelKeys.uploadKeys, { keys: batchKeys }); -} - -function getScreenCaptureConstraints(selection) { - if (selection.type === "device") { - return { video: { deviceId: { exact: selection.deviceId } }, audio: false }; - } - return { - audio: selection.shareAudio - ? { - mandatory: { - chromeMediaSource: "desktop", - chromeMediaSourceId: selection.sourceId, - }, - } - : false, - video: { - mandatory: { - chromeMediaSource: "desktop", - chromeMediaSourceId: selection.sourceId, - }, - }, - }; -} - -const VoiceUserContextMenu = ({ - x, - y, - onClose, - user, - onMute, - isMuted, - onServerMute, - isServerMuted, - hasPermission, - onDisconnect, - hasDisconnectPermission, - onMessage, - isSelf, - userVolume, - onVolumeChange, - onChangeNickname, - showNicknameOption, - onStartCall, -}) => { - const menuRef = useRef(null); - const [pos, setPos] = useState({ top: y, left: x }); - - useEffect(() => { - const h = () => onClose(); - window.addEventListener("click", h); - window.addEventListener("close-context-menus", h); - return () => { - window.removeEventListener("click", h); - window.removeEventListener("close-context-menus", h); - }; - }, [onClose]); - - useLayoutEffect(() => { - if (!menuRef.current) return; - const rect = menuRef.current.getBoundingClientRect(); - let newTop = y, - newLeft = x; - if (x + rect.width > window.innerWidth) newLeft = x - rect.width; - if (y + rect.height > window.innerHeight) newTop = y - rect.height; - if (newLeft < 0) newLeft = 10; - if (newTop < 0) newTop = 10; - setPos({ top: newTop, left: newLeft }); - }, [x, y]); - - const sliderPercent = (userVolume / 200) * 100; - - return ( - <div - ref={menuRef} - className="context-menu" - style={{ top: pos.top, left: pos.left }} - onClick={(e) => e.stopPropagation()} - > - {!isSelf && ( - <> - <div - className="context-menu-volume" - onMouseDown={(e) => e.stopPropagation()} - onClick={(e) => e.stopPropagation()} - > - <div className="context-menu-volume-label"> - <span>User Volume</span> - <span>{userVolume}%</span> - </div> - <input - type="range" - min="0" - max="200" - value={userVolume} - onChange={(e) => onVolumeChange(Number(e.target.value))} - className="context-menu-volume-slider" - style={{ - background: `linear-gradient(to right, hsl(235, 86%, 65%) ${sliderPercent}%, var(--bg-tertiary) ${sliderPercent}%)`, - }} - /> - </div> - <div className="context-menu-separator" /> - </> - )} - <div - className="context-menu-item context-menu-checkbox-item" - role="menuitemcheckbox" - aria-checked={isMuted} - onClick={(e) => { - e.stopPropagation(); - onMute(); - }} - > - <span>Mute</span> - <div className="context-menu-checkbox"> - <div className={`context-menu-checkbox-indicator ${isMuted ? "checked" : ""}`}> - {isMuted ? ( - <svg width="18" height="18" viewBox="0 0 24 24"> - <path - fill="white" - d="M8.99991 16.17L4.82991 12L3.40991 13.41L8.99991 19L20.9999 7L19.5899 5.59L8.99991 16.17Z" - /> - </svg> - ) : ( - <svg width="10" height="10" viewBox="0 0 10 10"></svg> - )} - </div> - </div> - </div> - {hasPermission && ( - <div - className="context-menu-item context-menu-checkbox-item" - role="menuitemcheckbox" - aria-checked={isServerMuted} - onClick={(e) => { - e.stopPropagation(); - onServerMute(); - }} - > - <span style={{ color: SERVER_MUTE_RED }}>Server Mute</span> - <div className="context-menu-checkbox"> - <div - className={`context-menu-checkbox-indicator ${isServerMuted ? "checked" : ""}`} - style={ - isServerMuted - ? { backgroundColor: SERVER_MUTE_RED, borderColor: SERVER_MUTE_RED } - : {} - } - > - {isServerMuted ? ( - <svg width="18" height="18" viewBox="0 0 24 24"> - <path - fill="white" - d="M8.99991 16.17L4.82991 12L3.40991 13.41L8.99991 19L20.9999 7L19.5899 5.59L8.99991 16.17Z" - /> - </svg> - ) : ( - <svg width="10" height="10" viewBox="0 0 10 10"></svg> - )} - </div> - </div> - </div> - )} - {!isSelf && hasDisconnectPermission && ( - <div - className="context-menu-item" - onClick={(e) => { - e.stopPropagation(); - onDisconnect(); - onClose(); - }} - > - <span style={{ color: SERVER_MUTE_RED }}>Disconnect</span> - </div> - )} - <div className="context-menu-separator" /> - {showNicknameOption && ( - <div - className="context-menu-item" - onClick={(e) => { - e.stopPropagation(); - onChangeNickname(); - onClose(); - }} - > - <span>Change Nickname</span> - </div> - )} - {!isSelf && ( - <div - className="context-menu-item" - onClick={(e) => { - e.stopPropagation(); - onMessage(); - onClose(); - }} - > - <span>Message</span> - </div> - )} - {!isSelf && ( - <div - className="context-menu-item" - onClick={(e) => { - e.stopPropagation(); - onStartCall(); - onClose(); - }} - > - <span>Start a Call</span> - </div> - )} - </div> - ); -}; - -const ChannelListContextMenu = ({ x, y, onClose, onCreateChannel, onCreateCategory }) => { - const menuRef = useRef(null); - const [pos, setPos] = useState({ top: y, left: x }); - - useEffect(() => { - const h = () => onClose(); - window.addEventListener("click", h); - window.addEventListener("close-context-menus", h); - return () => { - window.removeEventListener("click", h); - window.removeEventListener("close-context-menus", h); - }; - }, [onClose]); - - useLayoutEffect(() => { - if (!menuRef.current) return; - const rect = menuRef.current.getBoundingClientRect(); - let newTop = y, - newLeft = x; - if (x + rect.width > window.innerWidth) newLeft = x - rect.width; - if (y + rect.height > window.innerHeight) newTop = y - rect.height; - if (newLeft < 0) newLeft = 10; - if (newTop < 0) newTop = 10; - setPos({ top: newTop, left: newLeft }); - }, [x, y]); - - return ( - <div - ref={menuRef} - className="context-menu" - style={{ top: pos.top, left: pos.left }} - onClick={(e) => e.stopPropagation()} - > - <div - className="context-menu-item" - onClick={(e) => { - e.stopPropagation(); - onCreateChannel(); - onClose(); - }} - > - <span>Create Channel</span> - </div> - <div - className="context-menu-item" - onClick={(e) => { - e.stopPropagation(); - onCreateCategory(); - onClose(); - }} - > - <span>Create Category</span> - </div> - </div> - ); -}; - -const CategoryContextMenu = ({ x, y, onClose, categoryName, onEdit, onDelete }) => { - const menuRef = useRef(null); - const [pos, setPos] = useState({ top: y, left: x }); - - useEffect(() => { - const h = () => onClose(); - window.addEventListener("click", h); - window.addEventListener("close-context-menus", h); - return () => { - window.removeEventListener("click", h); - window.removeEventListener("close-context-menus", h); - }; - }, [onClose]); - - useLayoutEffect(() => { - if (!menuRef.current) return; - const rect = menuRef.current.getBoundingClientRect(); - let newTop = y, - newLeft = x; - if (x + rect.width > window.innerWidth) newLeft = x - rect.width; - if (y + rect.height > window.innerHeight) newTop = y - rect.height; - if (newLeft < 0) newLeft = 10; - if (newTop < 0) newTop = 10; - setPos({ top: newTop, left: newLeft }); - }, [x, y]); - - return ( - <div - ref={menuRef} - className="context-menu" - style={{ top: pos.top, left: pos.left }} - onClick={(e) => e.stopPropagation()} - > - <div - className="context-menu-item" - onClick={(e) => { - e.stopPropagation(); - onEdit(); - }} - > - <span>Edit Category</span> - </div> - <div className="context-menu-separator" /> - <div - className="context-menu-item" - style={{ color: "#ed4245" }} - onClick={(e) => { - e.stopPropagation(); - onDelete(); - }} - > - <span>Delete Category</span> - </div> - </div> - ); -}; - -const CreateChannelModal = ({ onClose, onSubmit, categoryId }) => { - const [channelType, setChannelType] = useState("text"); - const [channelName, setChannelName] = useState(""); - - const handleSubmit = () => { - if (!channelName.trim()) return; - onSubmit(channelName.trim(), channelType, categoryId); - onClose(); - }; - - return ( - <div className="create-channel-modal-overlay" onClick={onClose}> - <div className="create-channel-modal" onClick={(e) => e.stopPropagation()}> - <div className="create-channel-modal-header"> - <div> - <h2 - style={{ - margin: 0, - color: "var(--header-primary)", - fontSize: "20px", - fontWeight: 700, - }} - > - Create Channel - </h2> - <p style={{ margin: "4px 0 0", color: "var(--header-secondary)", fontSize: "12px" }}> - in Text Channels - </p> - </div> - <button className="create-channel-modal-close" onClick={onClose}> - <svg width="24" height="24" viewBox="0 0 24 24"> - <path - fill="currentColor" - d="M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z" - /> - </svg> - </button> - </div> - - <div style={{ padding: "0 16px" }}> - <div style={{ marginBottom: "16px" }}> - <label - style={{ - display: "block", - color: "var(--header-secondary)", - fontSize: "12px", - fontWeight: 700, - textTransform: "uppercase", - marginBottom: "8px", - }} - > - Channel Type - </label> - - <div - className={`channel-type-option ${channelType === "text" ? "selected" : ""}`} - onClick={() => setChannelType("text")} - > - <div style={{ display: "flex", alignItems: "center", gap: "12px", flex: 1 }}> - <span - style={{ - fontSize: "24px", - color: "var(--interactive-normal)", - width: "24px", - textAlign: "center", - }} - > - # - </span> - <div> - <div - style={{ color: "var(--header-primary)", fontWeight: 500, fontSize: "16px" }} - > - Text - </div> - <div - style={{ color: "var(--header-secondary)", fontSize: "12px", marginTop: "2px" }} - > - Send messages, images, GIFs, emoji, opinions, and puns - </div> - </div> - </div> - <div className={`channel-type-radio ${channelType === "text" ? "selected" : ""}`}> - {channelType === "text" && <div className="channel-type-radio-dot" />} - </div> - </div> - - <div - className={`channel-type-option ${channelType === "voice" ? "selected" : ""}`} - onClick={() => setChannelType("voice")} - > - <div style={{ display: "flex", alignItems: "center", gap: "12px", flex: 1 }}> - <svg - width="24" - height="24" - viewBox="0 0 24 24" - style={{ flexShrink: 0, color: "var(--interactive-normal)" }} - > - <path - fill="currentColor" - d="M11.383 3.07904C11.009 2.92504 10.579 3.01004 10.293 3.29604L6.586 7.00304H3C2.45 7.00304 2 7.45304 2 8.00304V16.003C2 16.553 2.45 17.003 3 17.003H6.586L10.293 20.71C10.579 20.996 11.009 21.082 11.383 20.927C11.757 20.772 12 20.407 12 20.003V4.00304C12 3.59904 11.757 3.23404 11.383 3.07904ZM14 5.00304V7.00304C16.757 7.00304 19 9.24604 19 12.003C19 14.76 16.757 17.003 14 17.003V19.003C17.86 19.003 21 15.863 21 12.003C21 8.14304 17.86 5.00304 14 5.00304ZM14 9.00304V15.003C15.654 15.003 17 13.657 17 12.003C17 10.349 15.654 9.00304 14 9.00304Z" - /> - </svg> - <div> - <div - style={{ color: "var(--header-primary)", fontWeight: 500, fontSize: "16px" }} - > - Voice - </div> - <div - style={{ color: "var(--header-secondary)", fontSize: "12px", marginTop: "2px" }} - > - Hang out together with voice, video, and screen share - </div> - </div> - </div> - <div className={`channel-type-radio ${channelType === "voice" ? "selected" : ""}`}> - {channelType === "voice" && <div className="channel-type-radio-dot" />} - </div> - </div> - </div> - - <div style={{ marginBottom: "16px" }}> - <label - style={{ - display: "block", - color: "var(--header-secondary)", - fontSize: "12px", - fontWeight: 700, - textTransform: "uppercase", - marginBottom: "8px", - }} - > - Channel Name - </label> - <div className="create-channel-name-input-wrapper"> - <span - style={{ color: "var(--interactive-normal)", fontSize: "16px", marginRight: "4px" }} - > - {channelType === "text" ? "#" : "🔊"} - </span> - <input - autoFocus - type="text" - placeholder="new-channel" - value={channelName} - onChange={(e) => setChannelName(e.target.value.toLowerCase().replace(/\s+/g, "-"))} - onKeyDown={(e) => { - if (e.key === "Enter") handleSubmit(); - }} - className="create-channel-name-input" - /> - </div> - </div> - </div> - - <div className="create-channel-modal-footer"> - <button className="create-channel-cancel-btn" onClick={onClose}> - Cancel - </button> - <button - className="create-channel-submit-btn" - onClick={handleSubmit} - disabled={!channelName.trim()} - > - Create Channel - </button> - </div> - </div> - </div> - ); -}; - -const CreateCategoryModal = ({ onClose, onSubmit }) => { - const [categoryName, setCategoryName] = useState(""); - - const handleSubmit = () => { - if (!categoryName.trim()) return; - onSubmit(categoryName.trim()); - onClose(); - }; - - return ( - <div className="create-channel-modal-overlay" onClick={onClose}> - <div className="create-channel-modal" onClick={(e) => e.stopPropagation()}> - <div className="create-channel-modal-header"> - <h2 - style={{ margin: 0, color: "var(--header-primary)", fontSize: "20px", fontWeight: 700 }} - > - Create Category - </h2> - <button className="create-channel-modal-close" onClick={onClose}> - <svg width="24" height="24" viewBox="0 0 24 24"> - <path - fill="currentColor" - d="M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z" - /> - </svg> - </button> - </div> - - <div style={{ padding: "0 16px" }}> - <div style={{ marginBottom: "16px" }}> - <label - style={{ - display: "block", - color: "var(--header-secondary)", - fontSize: "12px", - fontWeight: 700, - textTransform: "uppercase", - marginBottom: "8px", - }} - > - Category Name - </label> - <div className="create-channel-name-input-wrapper"> - <input - autoFocus - type="text" - placeholder="New Category" - value={categoryName} - onChange={(e) => setCategoryName(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Enter") handleSubmit(); - }} - className="create-channel-name-input" - /> - </div> - </div> - - <div - style={{ - display: "flex", - alignItems: "center", - justifyContent: "space-between", - marginBottom: "8px", - }} - > - <div style={{ display: "flex", alignItems: "center", gap: "8px" }}> - <svg - width="16" - height="16" - viewBox="0 0 24 24" - style={{ color: "var(--interactive-normal)" }} - > - <path - fill="currentColor" - d="M17 11V7C17 4.243 14.757 2 12 2C9.243 2 7 4.243 7 7V11C5.897 11 5 11.896 5 13V20C5 21.103 5.897 22 7 22H17C18.103 22 19 21.103 19 20V13C19 11.896 18.103 11 17 11ZM12 18C11.172 18 10.5 17.328 10.5 16.5C10.5 15.672 11.172 15 12 15C12.828 15 13.5 15.672 13.5 16.5C13.5 17.328 12.828 18 12 18ZM15 11H9V7C9 5.346 10.346 4 12 4C13.654 4 15 5.346 15 7V11Z" - /> - </svg> - <span style={{ color: "var(--header-primary)", fontSize: "14px", fontWeight: 500 }}> - Private Category - </span> - </div> - <div className="category-toggle-switch"> - <div className="category-toggle-knob" /> - </div> - </div> - <p - style={{ - color: "var(--header-secondary)", - fontSize: "12px", - margin: "0 0 16px", - lineHeight: "16px", - }} - > - By making a category private, only selected members and roles will be able to view this - category. Synced channels will automatically match this category's permissions. - </p> - </div> - - <div className="create-channel-modal-footer"> - <button className="create-channel-cancel-btn" onClick={onClose}> - Cancel - </button> - <button - className="create-channel-submit-btn" - onClick={handleSubmit} - disabled={!categoryName.trim()} - > - Create Category - </button> - </div> - </div> - </div> - ); -}; - -// --- DnD wrapper components --- - -const SortableCategory = ({ id, children }) => { - const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ - id, - data: { type: "category" }, - }); - - const style = { - transform: CSS.Transform.toString(transform), - transition, - opacity: isDragging ? 0.5 : 1, - }; - - return ( - <div ref={setNodeRef} style={style} {...attributes}> - {React.Children.map(children, (child, i) => { - // First child is the category header — attach drag listeners to it - if (i === 0 && React.isValidElement(child)) { - return React.cloneElement(child, { dragListeners: listeners }); - } - return child; - })} - </div> - ); -}; - -const SortableChannel = ({ id, children }) => { - const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ - id, - data: { type: "channel" }, - }); - - const style = { - transform: CSS.Transform.toString(transform), - transition, - opacity: isDragging ? 0.5 : 1, - }; - - return ( - <div ref={setNodeRef} style={style} {...attributes}> - {typeof children === "function" ? children(listeners) : children} - </div> - ); -}; - -const DraggableVoiceUser = ({ userId, channelId, disabled, children }) => { - const { attributes, listeners, setNodeRef, isDragging } = useDraggable({ - id: `voice-user-${userId}`, - data: { type: "voice-user", userId, channelId }, - disabled, - }); - - return ( - <div - ref={setNodeRef} - {...attributes} - {...listeners} - style={{ - opacity: isDragging ? 0.4 : 1, - cursor: disabled ? "default" : "grab", - }} - > - {children} - </div> - ); -}; - -const Sidebar = ({ - channels, - categories, - activeChannel, - onSelectChannel, - username, - channelKeys, - view, - onViewChange, - onOpenDM, - activeDMChannel, - setActiveDMChannel, - dmChannels, - userId, - serverName = "Secure Chat", - serverIconUrl, - isMobile, - onStartCallWithUser, - onOpenMobileSearch, -}) => { - const { crypto, settings } = usePlatform(); - const [isCreating, setIsCreating] = useState(false); - const [isServerSettingsOpen, setIsServerSettingsOpen] = useState(false); - const [newChannelName, setNewChannelName] = useState(""); - const [newChannelType, setNewChannelType] = useState("text"); - const [editingChannel, setEditingChannel] = useState(null); - const [isScreenShareModalOpen, setIsScreenShareModalOpen] = useState(false); - const [collapsedCategories, setCollapsedCategories] = useState(() => { - const effectiveUserId = userId || localStorage.getItem("userId"); - return getUserPref(effectiveUserId, "collapsedCategories", {}); - }); - useEffect(() => { - if (userId) { - setCollapsedCategories(getUserPref(userId, "collapsedCategories", {})); - } - }, [userId]); - const [channelListContextMenu, setChannelListContextMenu] = useState(null); - const [voiceUserMenu, setVoiceUserMenu] = useState(null); - const [categoryContextMenu, setCategoryContextMenu] = useState(null); - const [editingCategoryId, setEditingCategoryId] = useState(null); - const [showCreateChannelModal, setShowCreateChannelModal] = useState(false); - const [showCreateCategoryModal, setShowCreateCategoryModal] = useState(false); - const [createChannelCategoryId, setCreateChannelCategoryId] = useState(null); - const [activeDragItem, setActiveDragItem] = useState(null); - const [dragOverChannelId, setDragOverChannelId] = useState(null); - const [voiceNicknameModal, setVoiceNicknameModal] = useState(null); - const [showMobileServerDrawer, setShowMobileServerDrawer] = useState(false); - const [showMobileCreateChannel, setShowMobileCreateChannel] = useState(false); - const [showMobileCreateCategory, setShowMobileCreateCategory] = useState(false); - const [mobileChannelDrawer, setMobileChannelDrawer] = useState(null); - const [showMobileChannelSettings, setShowMobileChannelSettings] = useState(null); - - const convex = useConvex(); - - // Permissions for move_members gating - const myPermissions = useQuery(api.roles.getMyPermissions, userId ? { userId } : "skip") || {}; - - // Member count for mobile server drawer - const allUsersForDrawer = useQuery(api.auth.getPublicKeys) || []; - - // DnD sensors - const sensors = useSensors( - useSensor(PointerSensor, { activationConstraint: { distance: isMobile ? Infinity : 5 } }), - ); - - // Unread tracking - const channelIds = React.useMemo( - () => [...channels.map((c) => c._id), ...dmChannels.map((dm) => dm.channel_id)], - [channels, dmChannels], - ); - const rawAllReadStates = useQuery(api.readState.getAllReadStates, userId ? { userId } : "skip"); - const rawLatestTimestamps = useQuery( - api.readState.getLatestMessageTimestamps, - channelIds.length > 0 ? { channelIds } : "skip", - ); - const allReadStates = rawAllReadStates || []; - const latestTimestamps = rawLatestTimestamps || []; - const unreadQueriesLoaded = rawAllReadStates !== undefined && rawLatestTimestamps !== undefined; - - const unreadChannels = React.useMemo(() => { - const set = new Set(); - const readMap = new Map(); - for (const rs of allReadStates) { - readMap.set(rs.channelId, rs.lastReadTimestamp); - } - for (const lt of latestTimestamps) { - const lastRead = readMap.get(lt.channelId); - if (lastRead === undefined || lt.latestTimestamp > lastRead) { - set.add(lt.channelId); - } - } - return set; - }, [allReadStates, latestTimestamps]); - - const unreadDMs = React.useMemo( - () => - dmChannels.filter( - (dm) => - unreadChannels.has(dm.channel_id) && - !(view === "me" && activeDMChannel?.channel_id === dm.channel_id), - ), - [dmChannels, unreadChannels, view, activeDMChannel], - ); - - const { - connectToVoice, - activeChannelId: voiceChannelId, - connectionState, - disconnectVoice, - activeChannelName: voiceChannelName, - voiceStates, - room, - activeSpeakers, - setScreenSharing, - isPersonallyMuted, - togglePersonalMute, - isMuted: selfMuted, - toggleMute, - serverMute, - disconnectUser, - isServerMuted, - serverSettings, - getUserVolume, - setUserVolume, - isReceivingScreenShareAudio, - } = useVoice(); - - const prevUnreadDMsRef = useRef(null); - - useEffect(() => { - if (!unreadQueriesLoaded) return; - - const currentIds = new Set( - dmChannels.filter((dm) => unreadChannels.has(dm.channel_id)).map((dm) => dm.channel_id), - ); - - if (prevUnreadDMsRef.current === null) { - prevUnreadDMsRef.current = currentIds; - return; - } - - for (const id of currentIds) { - if (!prevUnreadDMsRef.current.has(id)) { - if (!isReceivingScreenShareAudio) { - const audio = new Audio(PingSound); - audio.volume = 0.5; - audio.play().catch(() => {}); - } - break; - } - } - - prevUnreadDMsRef.current = currentIds; - }, [dmChannels, unreadChannels, unreadQueriesLoaded, isReceivingScreenShareAudio]); - - const onRenameChannel = () => {}; - - const onDeleteChannel = (id) => { - if (activeChannel === id) onSelectChannel(null); - }; - - const handleStartCreate = () => { - setIsCreating(true); - setNewChannelName(""); - setNewChannelType("text"); - }; - - const handleSubmitCreate = async (e) => { - if (e) e.preventDefault(); - - if (!newChannelName.trim()) { - setIsCreating(false); - return; - } - - const name = newChannelName.trim(); - const userId = localStorage.getItem("userId"); - - if (!userId) { - alert("Please login first."); - setIsCreating(false); - return; - } - - try { - const { id: channelId } = await convex.mutation(api.channels.create, { - name, - type: newChannelType, - }); - const keyHex = randomHex(32); - - try { - await encryptKeyForUsers(convex, channelId, keyHex, crypto); - } catch (keyErr) { - console.error("Critical: Failed to distribute keys", keyErr); - alert("Channel created but key distribution failed."); - } - } catch (err) { - console.error(err); - alert("Failed to create channel: " + err.message); - } finally { - setIsCreating(false); - } - }; - - const handleCreateInvite = async () => { - const userId = localStorage.getItem("userId"); - if (!userId) { - alert("Error: No User ID found. Please login again."); - return; - } - - // Bundle all server channel keys (not DM keys) so new users get access to everything - const serverChannelIds = new Set(channels.map((c) => c._id)); - const allServerKeys = {}; - for (const [chId, key] of Object.entries(channelKeys || {})) { - if (serverChannelIds.has(chId)) { - allServerKeys[chId] = key; - } - } - - if (Object.keys(allServerKeys).length === 0) { - alert("Error: You don't have any channel keys to share."); - return; - } - - try { - const inviteCode = globalThis.crypto.randomUUID(); - const inviteSecret = randomHex(32); - - const payload = JSON.stringify(allServerKeys); - const encrypted = await crypto.encryptData(payload, inviteSecret); - const blob = JSON.stringify({ c: encrypted.content, t: encrypted.tag, iv: encrypted.iv }); - - await convex.mutation(api.invites.create, { - code: inviteCode, - encryptedPayload: blob, - createdBy: userId, - keyVersion: 1, - }); - - const baseUrl = import.meta.env.VITE_APP_URL || window.location.origin; - const link = `${baseUrl}/#/register?code=${inviteCode}&key=${inviteSecret}`; - navigator.clipboard.writeText(link); - alert(`Invite Link Copied to Clipboard!\n\n${link}`); - } catch (e) { - console.error("Invite Error:", e); - alert("Failed to create invite. See console."); - } - }; - - const handleScreenShareSelect = async (selection) => { - if (!room) return; - - try { - if (room.localParticipant.isScreenShareEnabled) { - await room.localParticipant.setScreenShareEnabled(false); - } - - let stream; - try { - stream = await navigator.mediaDevices.getUserMedia(getScreenCaptureConstraints(selection)); - } catch (audioErr) { - // Audio capture may fail (e.g. macOS/Linux) — retry video-only - if (selection.shareAudio) { - console.warn("Audio capture failed, falling back to video-only:", audioErr.message); - stream = await navigator.mediaDevices.getUserMedia( - getScreenCaptureConstraints({ ...selection, shareAudio: false }), - ); - } else { - throw audioErr; - } - } - - const track = stream.getVideoTracks()[0]; - if (!track) return; - - await room.localParticipant.publishTrack(track, { - name: "screen_share", - source: Track.Source.ScreenShare, - }); - - // Publish audio track if present (system audio from desktop capture) - const audioTrack = stream.getAudioTracks()[0]; - if (audioTrack) { - await room.localParticipant.publishTrack(audioTrack, { - name: "screen_share_audio", - source: Track.Source.ScreenShareAudio, - }); - } - - if (!isReceivingScreenShareAudio) new Audio(screenShareStartSound).play(); - setScreenSharing(true); - - track.onended = () => { - // Clean up audio track when video track ends - if (audioTrack) { - audioTrack.stop(); - room.localParticipant.unpublishTrack(audioTrack); - } - setScreenSharing(false); - room.localParticipant.setScreenShareEnabled(false).catch(console.error); - }; - } catch (err) { - console.error("Error sharing screen:", err); - alert("Failed to share screen: " + err.message); - } - }; - - const handleScreenShareClick = () => { - if (room?.localParticipant.isScreenShareEnabled) { - // Clean up any screen share audio tracks before stopping - for (const pub of room.localParticipant.trackPublications.values()) { - const source = pub.source ? pub.source.toString().toLowerCase() : ""; - const name = pub.trackName ? pub.trackName.toLowerCase() : ""; - if (source === "screen_share_audio" || name === "screen_share_audio") { - if (pub.track) pub.track.stop(); - room.localParticipant.unpublishTrack(pub.track); - } - } - room.localParticipant.setScreenShareEnabled(false); - if (!isReceivingScreenShareAudio) new Audio(screenShareStopSound).play(); - setScreenSharing(false); - } else { - setIsScreenShareModalOpen(true); - } - }; - - const handleChannelClick = (channel) => { - if (channel.type === "voice") { - if (voiceChannelId !== channel._id) { - connectToVoice(channel._id, channel.name, localStorage.getItem("userId")); - } - onSelectChannel(channel._id); - } else { - onSelectChannel(channel._id); - } - }; - - // Long-press handler factory for mobile channel items - const createLongPressHandlers = (callback) => { - let timer = null; - let startX = 0; - let startY = 0; - let triggered = false; - return { - onTouchStart: (e) => { - triggered = false; - startX = e.touches[0].clientX; - startY = e.touches[0].clientY; - timer = setTimeout(() => { - triggered = true; - if (navigator.vibrate) navigator.vibrate(50); - callback(); - }, 500); - }, - onTouchMove: (e) => { - if (!timer) return; - const dx = e.touches[0].clientX - startX; - const dy = e.touches[0].clientY - startY; - if (Math.abs(dx) > 10 || Math.abs(dy) > 10) { - clearTimeout(timer); - timer = null; - } - }, - onTouchEnd: (e) => { - if (timer) { - clearTimeout(timer); - timer = null; - } - if (triggered) { - e.preventDefault(); - triggered = false; - } - }, - }; - }; - - const handleMarkAsRead = async (channelId) => { - if (!userId) return; - try { - await convex.mutation(api.readState.markRead, { - userId, - channelId, - lastReadTimestamp: Date.now(), - }); - } catch (e) { - console.error("Failed to mark as read:", e); - } - }; - - const renderDMView = () => ( - <div - className="channel-list" - style={{ - flex: 1, - overflowY: "auto", - display: "flex", - flexDirection: "column", - backgroundColor: "var(--bg-tertiary)", - borderLeft: "1px solid var(--app-frame-border)", - borderTop: "1px solid var(--app-frame-border)", - borderRadius: "8px 0 0 0", - }} - > - <DMList - dmChannels={dmChannels} - activeDMChannel={activeDMChannel} - onSelectDM={(dm) => setActiveDMChannel(dm === "friends" ? null : dm)} - onOpenDM={onOpenDM} - voiceStates={voiceStates} - /> - </div> - ); - - const renderVoiceUsers = (channel) => { - const users = voiceStates[channel._id]; - if (channel.type !== "voice" || !users?.length) return null; - - return ( - <div style={{ marginLeft: 32, marginBottom: 8 }}> - {users.map((user) => ( - <DraggableVoiceUser - key={user.userId} - userId={user.userId} - channelId={channel._id} - disabled={!myPermissions.move_members} - > - <div - className="voice-user-item" - style={{ display: "flex", alignItems: "center", marginBottom: 2 }} - onContextMenu={(e) => { - e.preventDefault(); - e.stopPropagation(); - window.dispatchEvent(new Event("close-context-menus")); - setVoiceUserMenu({ x: e.clientX, y: e.clientY, user }); - }} - > - <Avatar - username={user.username} - avatarUrl={user.avatarUrl} - size={24} - style={{ - marginRight: 8, - boxShadow: activeSpeakers.has(user.userId) ? ACTIVE_SPEAKER_SHADOW : "none", - transition: "box-shadow 0.15s ease", - }} - /> - <span style={{ color: "var(--header-secondary)", fontSize: 14 }}> - {user.displayName || user.username} - </span> - <div - style={{ - display: "flex", - marginLeft: "auto", - gap: 4, - alignItems: "center", - marginRight: "16px", - }} - > - {user.isScreenSharing && <div style={liveBadgeStyle}>Live</div>} - {user.isServerMuted ? ( - <ColoredIcon src={serverMuteIcon} color={SERVER_MUTE_RED} size="14px" /> - ) : isPersonallyMuted(user.userId) ? ( - <ColoredIcon src={personalMuteIcon} color="var(--header-secondary)" size="14px" /> - ) : user.isMuted || user.isDeafened ? ( - <ColoredIcon src={mutedIcon} color="var(--header-secondary)" size="14px" /> - ) : null} - {user.isDeafened && ( - <ColoredIcon src={defeanedIcon} color="var(--header-secondary)" size="14px" /> - )} - </div> - </div> - </DraggableVoiceUser> - ))} - </div> - ); - }; - - const renderCollapsedVoiceUsers = (channel) => { - const users = voiceStates[channel._id]; - if (channel.type !== "voice" || !users?.length) return null; - - return ( - <div - className={`channel-item ${voiceChannelId === channel._id ? "voice-active" : ""}`} - onClick={() => handleChannelClick(channel)} - style={{ position: "relative", display: "flex", alignItems: "center", paddingRight: "8px" }} - > - <div style={{ marginRight: 6 }}> - <ColoredIcon src={voiceIcon} size="16px" color={VOICE_ACTIVE_COLOR} /> - </div> - <div style={{ display: "flex", alignItems: "center" }}> - {users.map((user) => ( - <div key={user.userId} style={{ marginRight: -6, position: "relative", zIndex: 1 }}> - <Avatar - username={user.username} - avatarUrl={user.avatarUrl} - size={24} - style={{ - boxShadow: activeSpeakers.has(user.userId) ? ACTIVE_SPEAKER_SHADOW : "none", - borderRadius: "50%", - transition: "box-shadow 0.15s ease", - }} - /> - </div> - ))} - </div> - </div> - ); - }; - - const toggleCategory = useCallback( - (cat) => { - setCollapsedCategories((prev) => { - const next = { ...prev, [cat]: !prev[cat] }; - setUserPref(userId, "collapsedCategories", next, settings); - return next; - }); - }, - [userId, settings], - ); - - const handleAddChannelToCategory = useCallback((groupId) => { - setCreateChannelCategoryId(groupId === "__uncategorized__" ? null : groupId); - setShowCreateChannelModal(true); - }, []); - - // Group channels by categoryId - const groupedChannels = React.useMemo(() => { - const groups = []; - const channelsByCategory = new Map(); - - channels.forEach((ch) => { - const catId = ch.categoryId || "__uncategorized__"; - if (!channelsByCategory.has(catId)) channelsByCategory.set(catId, []); - channelsByCategory.get(catId).push(ch); - }); - - // Sort channels within each category by position - for (const [, list] of channelsByCategory) { - list.sort((a, b) => (a.position ?? 0) - (b.position ?? 0)); - } - - // Add uncategorized at top - const uncategorized = channelsByCategory.get("__uncategorized__"); - if (uncategorized?.length) { - groups.push({ id: "__uncategorized__", name: "Channels", channels: uncategorized }); - } - - // Add categories in position order - for (const cat of categories || []) { - groups.push({ id: cat._id, name: cat.name, channels: channelsByCategory.get(cat._id) || [] }); - } - - return groups; - }, [channels, categories]); - - // DnD items - const categoryDndIds = React.useMemo( - () => groupedChannels.map((g) => `category-${g.id}`), - [groupedChannels], - ); - - const handleDragStart = (event) => { - const { active } = event; - const activeType = active.data.current?.type; - if (activeType === "category") { - const catId = active.id.replace("category-", ""); - const group = groupedChannels.find((g) => g.id === catId); - setActiveDragItem({ type: "category", name: group?.name || "" }); - } else if (activeType === "channel") { - const chId = active.id.replace("channel-", ""); - const ch = channels.find((c) => c._id === chId); - setActiveDragItem({ type: "channel", channel: ch }); - } else if (activeType === "voice-user") { - const targetUserId = active.data.current.userId; - const sourceChannelId = active.data.current.channelId; - const users = voiceStates[sourceChannelId]; - const user = users?.find((u) => u.userId === targetUserId); - setActiveDragItem({ type: "voice-user", user, sourceChannelId }); - } - }; - - const handleDragOver = (event) => { - const { active, over } = event; - if (!active?.data.current || active.data.current.type !== "voice-user") { - setDragOverChannelId(null); - return; - } - if (over) { - // Check if hovering over a voice channel (channel item or its DnD wrapper) - const overType = over.data.current?.type; - if (overType === "channel") { - const chId = over.id.replace("channel-", ""); - const ch = channels.find((c) => c._id === chId); - if (ch?.type === "voice") { - setDragOverChannelId(ch._id); - return; - } - } - } - setDragOverChannelId(null); - }; - - const handleDragEnd = async (event) => { - setActiveDragItem(null); - setDragOverChannelId(null); - const { active, over } = event; - if (!over || active.id === over.id) return; - - const activeType = active.data.current?.type; - const overType = over.data.current?.type; - - // Handle voice-user drag - if (activeType === "voice-user") { - if (overType !== "channel") return; - const targetChId = over.id.replace("channel-", ""); - const targetChannel = channels.find((c) => c._id === targetChId); - if (!targetChannel || targetChannel.type !== "voice") return; - const sourceChannelId = active.data.current.channelId; - if (sourceChannelId === targetChId) return; - try { - await convex.mutation(api.voiceState.moveUser, { - actorUserId: userId, - targetUserId: active.data.current.userId, - targetChannelId: targetChId, - }); - } catch (e) { - console.error("Failed to move voice user:", e); - } - return; - } - - if (activeType === "category" && overType === "category") { - // Reorder categories - const oldIndex = groupedChannels.findIndex((g) => `category-${g.id}` === active.id); - const newIndex = groupedChannels.findIndex((g) => `category-${g.id}` === over.id); - if (oldIndex === -1 || newIndex === -1) return; - - // Build reordered array (only real categories, skip uncategorized) - const reordered = [...groupedChannels]; - const [moved] = reordered.splice(oldIndex, 1); - reordered.splice(newIndex, 0, moved); - - const updates = reordered - .filter((g) => g.id !== "__uncategorized__") - .map((g, i) => ({ id: g.id, position: i * 1000 })); - - if (updates.length > 0) { - try { - await convex.mutation(api.categories.reorder, { updates }); - } catch (e) { - console.error("Failed to reorder categories:", e); - } - } - } else if (activeType === "channel") { - const activeChId = active.id.replace("channel-", ""); - - if (overType === "channel") { - const overChId = over.id.replace("channel-", ""); - const activeChannel = channels.find((c) => c._id === activeChId); - const overChannel = channels.find((c) => c._id === overChId); - if (!activeChannel || !overChannel) return; - - const targetCategoryId = overChannel.categoryId; - const targetGroup = groupedChannels.find( - (g) => g.id === (targetCategoryId || "__uncategorized__"), - ); - if (!targetGroup) return; - - // Build new order for the target category - const targetChannels = [...targetGroup.channels]; - - // Remove active channel if it's already in this category - const existingIdx = targetChannels.findIndex((c) => c._id === activeChId); - if (existingIdx !== -1) targetChannels.splice(existingIdx, 1); - - // Insert at the position of the over channel - const overIdx = targetChannels.findIndex((c) => c._id === overChId); - targetChannels.splice(overIdx, 0, activeChannel); - - const updates = targetChannels.map((ch, i) => ({ - id: ch._id, - categoryId: targetCategoryId, - position: i * 1000, - })); - - try { - await convex.mutation(api.channels.reorderChannels, { updates }); - } catch (e) { - console.error("Failed to reorder channels:", e); - } - } else if (overType === "category") { - // Drop channel onto a category header — move it to end of that category - const targetCatId = over.id.replace("category-", ""); - const targetCategoryId = targetCatId === "__uncategorized__" ? undefined : targetCatId; - const targetGroup = groupedChannels.find((g) => g.id === targetCatId); - const maxPos = (targetGroup?.channels || []).reduce( - (max, c) => Math.max(max, c.position ?? 0), - -1000, - ); - - try { - await convex.mutation(api.channels.moveChannel, { - id: activeChId, - categoryId: targetCategoryId, - position: maxPos + 1000, - }); - } catch (e) { - console.error("Failed to move channel:", e); - } - } - } - }; - - const renderServerView = () => ( - <div - className="channel-panel" - style={{ - flex: 1, - overflowY: "auto", - display: "flex", - flexDirection: "column", - backgroundColor: "var(--bg-tertiary)", - borderLeft: "1px solid var(--app-frame-border)", - borderTop: "1px solid var(--app-frame-border)", - borderRadius: "8px 0 0 0", - }} - > - <div - className="server-header" - style={{ borderBottom: isMobile ? "none" : "1px solid var(--app-frame-border)" }} - > - <span - className="server-header-name" - onClick={() => - isMobile ? setShowMobileServerDrawer(true) : setIsServerSettingsOpen(true) - } - > - {serverName} - {isMobile && ( - <svg - className="mobile-server-chevron" - width="16" - height="16" - viewBox="0 0 24 24" - fill="currentColor" - > - <path d="M9.29 6.71a1 1 0 0 0 0 1.41L13.17 12l-3.88 3.88a1 1 0 1 0 1.42 1.41l4.59-4.59a1 1 0 0 0 0-1.41L10.71 6.7a1 1 0 0 0-1.42 0Z" /> - </svg> - )} - </span> - {!isMobile && ( - <button - className="server-header-invite" - onClick={handleCreateInvite} - title="Invite People" - > - <img src={inviteUserIcon} alt="Invite" /> - </button> - )} - </div> - {isMobile && ( - <div className="mobile-search-invite-row"> - <button className="mobile-search-bar-btn" onClick={onOpenMobileSearch}> - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> - <path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" /> - </svg> - Search - </button> - <button - className="mobile-search-invite-btn" - onClick={handleCreateInvite} - title="Invite People" - > - <img src={inviteUserIcon} alt="Invite" /> - </button> - </div> - )} - - <div - className="channel-list" - style={{ flex: 1, overflowY: "auto", backgroundColor: "var(--bg-tertiary)" }} - onContextMenu={ - isMobile - ? undefined - : (e) => { - if ( - !e.target.closest(".channel-item") && - !e.target.closest(".channel-category-header") - ) { - e.preventDefault(); - window.dispatchEvent(new Event("close-context-menus")); - setChannelListContextMenu({ x: e.clientX, y: e.clientY }); - } - } - } - > - {isCreating && ( - <div style={{ padding: "0 8px", marginBottom: "4px" }}> - <form onSubmit={handleSubmitCreate}> - <div style={{ display: "flex", gap: 4, marginBottom: 4 }}> - <label - style={{ - color: newChannelType === "text" ? "white" : "var(--interactive-normal)", - fontSize: 10, - cursor: "pointer", - }} - onClick={() => setNewChannelType("text")} - > - Text - </label> - <label - style={{ - color: newChannelType === "voice" ? "white" : "var(--interactive-normal)", - fontSize: 10, - cursor: "pointer", - }} - onClick={() => setNewChannelType("voice")} - > - Voice - </label> - </div> - <input - autoFocus - type="text" - placeholder={`new-${newChannelType}-channel`} - value={newChannelName} - onChange={(e) => setNewChannelName(e.target.value)} - style={{ - width: "100%", - background: "var(--bg-tertiary)", - border: "1px solid var(--brand-experiment)", - borderRadius: "4px", - color: "var(--text-normal)", - padding: "4px 8px", - fontSize: "14px", - outline: "none", - }} - /> - </form> - <div - style={{ fontSize: 10, color: "var(--text-muted)", marginTop: 2, textAlign: "right" }} - > - Press Enter to Create {newChannelType === "voice" && "(Voice)"} - </div> - </div> - )} - - <DndContext - sensors={sensors} - collisionDetection={closestCenter} - onDragStart={handleDragStart} - onDragOver={handleDragOver} - onDragEnd={handleDragEnd} - > - <SortableContext items={categoryDndIds} strategy={verticalListSortingStrategy}> - {groupedChannels.map((group) => { - const channelDndIds = group.channels.map((ch) => `channel-${ch._id}`); - return ( - <SortableCategory key={group.id} id={`category-${group.id}`}> - <CategoryHeader - group={group} - groupId={group.id} - collapsed={collapsedCategories[group.id]} - onToggle={toggleCategory} - onAddChannel={handleAddChannelToCategory} - onContextMenu={ - group.id !== "__uncategorized__" && !isMobile - ? (e) => { - e.preventDefault(); - e.stopPropagation(); - window.dispatchEvent(new Event("close-context-menus")); - setCategoryContextMenu({ - x: e.clientX, - y: e.clientY, - categoryId: group.id, - categoryName: group.name, - }); - } - : undefined - } - isEditing={editingCategoryId === group.id} - onRenameSubmit={async (newName) => { - if (newName && newName !== group.name) { - await convex.mutation(api.categories.rename, { - id: group.id, - name: newName, - }); - } - setEditingCategoryId(null); - }} - onRenameCancel={() => setEditingCategoryId(null)} - /> - {(() => { - const isCollapsed = collapsedCategories[group.id]; - const visibleChannels = isCollapsed - ? group.channels.filter( - (ch) => - ch._id === activeChannel || - (ch.type === "voice" && voiceStates[ch._id]?.length > 0), - ) - : group.channels; - if (visibleChannels.length === 0) return null; - const visibleDndIds = visibleChannels.map((ch) => `channel-${ch._id}`); - return ( - <SortableContext items={visibleDndIds} strategy={verticalListSortingStrategy}> - {visibleChannels.map((channel) => { - const isUnread = - activeChannel !== channel._id && unreadChannels.has(channel._id); - return ( - <SortableChannel key={channel._id} id={`channel-${channel._id}`}> - {(channelDragListeners) => ( - <React.Fragment> - {!( - isCollapsed && - channel.type === "voice" && - voiceStates[channel._id]?.length > 0 - ) && ( - <div - className={`channel-item ${activeChannel === channel._id ? "active" : ""} ${voiceChannelId === channel._id ? "voice-active" : ""} ${dragOverChannelId === channel._id ? "voice-drop-target" : ""}`} - onClick={() => handleChannelClick(channel)} - {...channelDragListeners} - {...(isMobile - ? createLongPressHandlers(() => - setMobileChannelDrawer(channel), - ) - : {})} - style={{ - position: "relative", - display: "flex", - justifyContent: "space-between", - alignItems: "center", - paddingRight: "8px", - }} - > - {isUnread && <div className="channel-unread-indicator" />} - <div - style={{ - display: "flex", - alignItems: "center", - overflow: "hidden", - flex: 1, - }} - > - {channel.type === "voice" ? ( - <div style={{ marginRight: 6 }}> - <ColoredIcon - src={voiceIcon} - size="16px" - color={ - voiceStates[channel._id]?.length > 0 - ? VOICE_ACTIVE_COLOR - : "var(--interactive-normal)" - } - /> - </div> - ) : ( - <span - style={{ - color: "var(--interactive-normal)", - marginRight: "6px", - flexShrink: 0, - }} - > - # - </span> - )} - <span - style={{ - whiteSpace: "nowrap", - overflow: "hidden", - textOverflow: "ellipsis", - ...(isUnread - ? { color: "var(--header-primary)", fontWeight: 600 } - : {}), - }} - > - {channel.name} - {serverSettings?.afkChannelId === channel._id - ? " (AFK)" - : ""} - </span> - </div> - - {!isMobile && ( - <button - className="channel-settings-icon" - onClick={(e) => { - e.stopPropagation(); - setEditingChannel(channel); - }} - style={{ - background: "transparent", - border: "none", - cursor: "pointer", - padding: "2px 4px", - display: "flex", - alignItems: "center", - }} - > - <ColoredIcon - src={settingsIcon} - color="var(--interactive-normal)" - size="14px" - /> - </button> - )} - </div> - )} - {isCollapsed - ? renderCollapsedVoiceUsers(channel) - : renderVoiceUsers(channel)} - </React.Fragment> - )} - </SortableChannel> - ); - })} - </SortableContext> - ); - })()} - </SortableCategory> - ); - })} - </SortableContext> - - <DragOverlay> - {activeDragItem?.type === "channel" && activeDragItem.channel && ( - <div className="drag-overlay-channel"> - {activeDragItem.channel.type === "voice" ? ( - <ColoredIcon src={voiceIcon} size="16px" color="var(--interactive-normal)" /> - ) : ( - <span style={{ color: "var(--interactive-normal)", marginRight: "6px" }}>#</span> - )} - <span>{activeDragItem.channel.name}</span> - </div> - )} - {activeDragItem?.type === "category" && ( - <div className="drag-overlay-category">{activeDragItem.name}</div> - )} - {activeDragItem?.type === "voice-user" && activeDragItem.user && ( - <div className="drag-overlay-voice-user"> - <Avatar - username={activeDragItem.user.username} - avatarUrl={activeDragItem.user.avatarUrl} - size={24} - style={{ marginRight: 8 }} - /> - <span>{activeDragItem.user.username}</span> - </div> - )} - </DragOverlay> - </DndContext> - </div> - </div> - ); - - return ( - <div className="sidebar" style={{ display: "flex", flexDirection: "column" }}> - <div style={{ display: "flex", flex: 1, minHeight: 0 }}> - <div className="server-list"> - <div className="server-item-wrapper"> - <div className={`server-pill ${view === "me" ? "active" : ""}`} /> - <Tooltip text="Direct Messages" position="right"> - <div - className={`server-icon ${view === "me" ? "active" : ""}`} - onClick={() => onViewChange("me")} - style={{ - backgroundColor: view === "me" ? "var(--brand-experiment)" : "var(--bg-primary)", - color: view === "me" ? "#fff" : "var(--text-normal)", - cursor: "pointer", - }} - > - <svg width="28" height="20" viewBox="0 0 28 20"> - <path - fill="currentColor" - d="M23.0212 1.67671C21.3107 0.879656 19.5079 0.318797 17.6584 0C17.4062 0.461742 17.1749 0.934541 16.9708 1.4184C15.0172 1.11817 13.0907 1.11817 11.1372 1.4184C10.9331 0.934541 10.7018 0.461742 10.4496 0C8.59007 0.318797 6.78726 0.879656 5.0768 1.67671C1.65217 6.84883 0.706797 11.8997 1.166 16.858C3.39578 18.5135 5.56066 19.5165 7.6473 20.1417C8.16912 19.4246 8.63212 18.6713 9.02347 17.8863C8.25707 17.5929 7.52187 17.2415 6.82914 16.8374C7.0147 16.6975 7.19515 16.5518 7.36941 16.402C11.66 18.396 16.4523 18.396 20.7186 16.402C20.8953 16.5518 21.0758 16.6975 21.2613 16.8374C20.5663 17.2435 19.8288 17.5947 19.0624 17.8863C19.4537 18.6713 19.9167 19.4246 20.4385 20.1417C22.5276 19.5165 24.6925 18.5135 26.9223 16.858C27.4684 11.2365 25.9961 6.22055 23.0212 1.67671ZM9.68041 13.6383C8.39754 13.6383 7.34085 12.4456 7.34085 10.994C7.34085 9.5424 8.37555 8.34973 9.68041 8.34973C10.9893 8.34973 12.0395 9.5424 12.0175 10.994C12.0175 12.4456 10.9893 13.6383 9.68041 13.6383ZM18.3161 13.6383C17.0332 13.6383 15.9765 12.4456 15.9765 10.994C15.9765 9.5424 17.0112 8.34973 18.3161 8.34973C19.625 8.34973 20.6752 9.5424 20.6532 10.994C20.6532 12.4456 19.625 13.6383 18.3161 13.6383Z" - /> - </svg> - </div> - </Tooltip> - </div> - - {unreadDMs.map((dm) => ( - <div key={dm.channel_id} className="server-item-wrapper"> - <div - className={`server-pill ${ - view === "me" && activeDMChannel?.channel_id === dm.channel_id ? "active" : "" - }`} - /> - <Tooltip text={dm.other_username} position="right"> - <div - className={`server-icon dm-server-icon ${ - view === "me" && activeDMChannel?.channel_id === dm.channel_id ? "active" : "" - }`} - onClick={() => { - setActiveDMChannel(dm); - onViewChange("me"); - }} - > - <Avatar - username={dm.other_username} - avatarUrl={dm.other_user_avatar_url} - size={48} - /> - <div className="dm-notification-dot" /> - </div> - </Tooltip> - </div> - ))} - - <div className="server-separator" /> - - <div className="server-item-wrapper"> - <div className={`server-pill ${view === "server" ? "active" : ""}`} /> - <Tooltip text={serverName} position="right"> - <div - className={`server-icon ${view === "server" ? "active" : ""}`} - onClick={() => onViewChange("server")} - style={{ cursor: "pointer" }} - > - {serverIconUrl ? ( - <img - src={serverIconUrl} - alt={serverName} - style={{ width: 48, height: 48, objectFit: "cover", borderRadius: "inherit" }} - /> - ) : ( - serverName.substring(0, 2) - )} - </div> - </Tooltip> - </div> - </div> - - {view === "me" ? renderDMView() : renderServerView()} - </div> - - {(connectionState === "connected" || connectionState === "connecting") && ( - <div - style={{ - backgroundColor: "var(--panel-bg)", - borderRadius: "8px 8px 0px 0px", - padding: "calc(16px - 8px + 4px)", - margin: "8px 8px 0px 8px", - display: "flex", - flexDirection: "column", - borderBottom: "1px solid hsla(240, 4%, 60.784%, 0.039)", - }} - > - <div - style={{ - display: "flex", - alignItems: "center", - justifyContent: "space-between", - marginBottom: 4, - }} - > - <div style={{ display: "flex", alignItems: "center", gap: 6 }}> - <svg - className={connectionState === "connecting" ? "voice-connecting-icon" : ""} - width="18" - height="18" - viewBox="0 0 24 24" - fill="none" - > - <rect - x="2" - y="16" - width="4" - height="6" - rx="1" - fill={connectionState === "connected" ? "#43b581" : "#faa61a"} - /> - <rect - x="8" - y="12" - width="4" - height="10" - rx="1" - fill={connectionState === "connected" ? "#43b581" : "#faa61a"} - /> - <rect - x="14" - y="7" - width="4" - height="15" - rx="1" - fill={connectionState === "connected" ? "#43b581" : "#faa61a"} - opacity={connectionState === "connecting" ? 0.4 : 1} - /> - <rect - x="20" - y="2" - width="4" - height="20" - rx="1" - fill={connectionState === "connected" ? "#43b581" : "#faa61a"} - opacity={connectionState === "connecting" ? 0.4 : 1} - /> - </svg> - <div - style={{ - color: connectionState === "connected" ? "#43b581" : "#faa61a", - fontWeight: "bold", - fontSize: 13, - }} - > - {connectionState === "connected" ? "Voice Connected" : "Voice Connecting"} - </div> - </div> - <button - onClick={disconnectVoice} - title="Disconnect" - style={{ - background: "transparent", - border: "none", - cursor: "pointer", - padding: "0", - display: "flex", - justifyContent: "center", - }} - > - <ColoredIcon src={disconnectIcon} color="var(--header-secondary)" size="20px" /> - </button> - </div> - <div style={{ color: "var(--text-normal)", fontSize: 12, marginBottom: 4 }}> - {dmChannels?.some((dm) => dm.channel_id === voiceChannelId) - ? `Call with ${voiceChannelName}` - : `${voiceChannelName} / ${serverName}`} - </div> - {connectionState === "connected" && ( - <> - <div style={{ marginBottom: 8 }}> - <VoiceTimer /> - </div> - <div style={{ display: "flex", gap: 4 }}> - <button - onClick={() => - room?.localParticipant.setCameraEnabled(!room.localParticipant.isCameraEnabled) - } - title="Turn On Camera" - style={voicePanelButtonStyle} - > - <ColoredIcon src={cameraIcon} color="var(--header-secondary)" size="20px" /> - </button> - <button - onClick={handleScreenShareClick} - title="Share Screen" - style={voicePanelButtonStyle} - > - <ColoredIcon src={screenIcon} color="var(--header-secondary)" size="20px" /> - </button> - </div> - </> - )} - </div> - )} - - <UserControlPanel username={username} userId={userId} /> - - {editingChannel && !isMobile && ( - <ChannelSettingsModal - channel={editingChannel} - onClose={() => setEditingChannel(null)} - onRename={onRenameChannel} - onDelete={onDeleteChannel} - /> - )} - {isServerSettingsOpen && ( - <ServerSettingsModal onClose={() => setIsServerSettingsOpen(false)} /> - )} - {showMobileServerDrawer && ( - <MobileServerDrawer - serverName={serverName} - serverIconUrl={serverIconUrl} - memberCount={allUsersForDrawer.length} - onInvite={handleCreateInvite} - onSettings={() => setIsServerSettingsOpen(true)} - onCreateChannel={() => { - setCreateChannelCategoryId(null); - setShowMobileCreateChannel(true); - }} - onCreateCategory={() => setShowMobileCreateCategory(true)} - onClose={() => setShowMobileServerDrawer(false)} - /> - )} - {isScreenShareModalOpen && ( - <ScreenShareModal - onClose={() => setIsScreenShareModalOpen(false)} - onSelectSource={handleScreenShareSelect} - /> - )} - {channelListContextMenu && ( - <ChannelListContextMenu - x={channelListContextMenu.x} - y={channelListContextMenu.y} - onClose={() => setChannelListContextMenu(null)} - onCreateChannel={() => { - setCreateChannelCategoryId(null); - setShowCreateChannelModal(true); - }} - onCreateCategory={() => setShowCreateCategoryModal(true)} - /> - )} - {categoryContextMenu && ( - <CategoryContextMenu - x={categoryContextMenu.x} - y={categoryContextMenu.y} - categoryName={categoryContextMenu.categoryName} - onClose={() => setCategoryContextMenu(null)} - onEdit={() => { - setEditingCategoryId(categoryContextMenu.categoryId); - setCategoryContextMenu(null); - }} - onDelete={async () => { - const categoryId = categoryContextMenu.categoryId; - const categoryName = categoryContextMenu.categoryName; - setCategoryContextMenu(null); - if ( - window.confirm( - `Are you sure you want to delete "${categoryName}"? Channels in this category will become uncategorized.`, - ) - ) { - await convex.mutation(api.categories.remove, { id: categoryId }); - } - }} - /> - )} - {voiceUserMenu && ( - <VoiceUserContextMenu - x={voiceUserMenu.x} - y={voiceUserMenu.y} - user={voiceUserMenu.user} - onClose={() => setVoiceUserMenu(null)} - isSelf={voiceUserMenu.user.userId === userId} - isMuted={ - voiceUserMenu.user.userId === userId - ? selfMuted - : isPersonallyMuted(voiceUserMenu.user.userId) - } - onMute={() => - voiceUserMenu.user.userId === userId - ? toggleMute() - : togglePersonalMute(voiceUserMenu.user.userId) - } - isServerMuted={isServerMuted(voiceUserMenu.user.userId)} - onServerMute={() => - serverMute(voiceUserMenu.user.userId, !isServerMuted(voiceUserMenu.user.userId)) - } - hasPermission={!!myPermissions.mute_members} - onDisconnect={() => disconnectUser(voiceUserMenu.user.userId)} - hasDisconnectPermission={!!myPermissions.move_members} - onMessage={() => { - onOpenDM( - voiceUserMenu.user.userId, - voiceUserMenu.user.displayName || voiceUserMenu.user.username, - ); - onViewChange("me"); - }} - userVolume={getUserVolume(voiceUserMenu.user.userId)} - onVolumeChange={(vol) => setUserVolume(voiceUserMenu.user.userId, vol)} - showNicknameOption={ - voiceUserMenu.user.userId === userId || !!myPermissions.manage_nicknames - } - onChangeNickname={() => setVoiceNicknameModal(voiceUserMenu.user)} - onStartCall={() => { - if (onStartCallWithUser) - onStartCallWithUser( - voiceUserMenu.user.userId, - voiceUserMenu.user.displayName || voiceUserMenu.user.username, - ); - }} - /> - )} - {voiceNicknameModal && ( - <ChangeNicknameModal - targetUserId={voiceNicknameModal.userId} - targetUsername={voiceNicknameModal.username} - currentNickname={voiceNicknameModal.displayName || ""} - actorUserId={userId} - onClose={() => setVoiceNicknameModal(null)} - /> - )} - {showCreateChannelModal && ( - <CreateChannelModal - categoryId={createChannelCategoryId} - onClose={() => setShowCreateChannelModal(false)} - onSubmit={async (name, type, catId) => { - const userId = localStorage.getItem("userId"); - if (!userId) { - alert("Please login first."); - return; - } - try { - const createArgs = { name, type }; - if (catId) createArgs.categoryId = catId; - const { id: channelId } = await convex.mutation(api.channels.create, createArgs); - const keyHex = randomHex(32); - try { - await encryptKeyForUsers(convex, channelId, keyHex, crypto); - } catch (keyErr) { - console.error("Critical: Failed to distribute keys", keyErr); - alert("Channel created but key distribution failed."); - } - } catch (err) { - console.error(err); - alert("Failed to create channel: " + err.message); - } - }} - /> - )} - {showCreateCategoryModal && ( - <CreateCategoryModal - onClose={() => setShowCreateCategoryModal(false)} - onSubmit={async (name) => { - try { - await convex.mutation(api.categories.create, { name }); - } catch (err) { - console.error(err); - alert("Failed to create category: " + err.message); - } - }} - /> - )} - {showMobileCreateChannel && ( - <MobileCreateChannelScreen - categoryId={createChannelCategoryId} - onClose={() => setShowMobileCreateChannel(false)} - onSubmit={async (name, type, catId) => { - const userId = localStorage.getItem("userId"); - if (!userId) { - alert("Please login first."); - return; - } - try { - const createArgs = { name, type }; - if (catId) createArgs.categoryId = catId; - const { id: channelId } = await convex.mutation(api.channels.create, createArgs); - const keyHex = randomHex(32); - try { - await encryptKeyForUsers(convex, channelId, keyHex, crypto); - } catch (keyErr) { - console.error("Critical: Failed to distribute keys", keyErr); - alert("Channel created but key distribution failed."); - } - } catch (err) { - console.error(err); - alert("Failed to create channel: " + err.message); - } - }} - /> - )} - {showMobileCreateCategory && ( - <MobileCreateCategoryScreen - onClose={() => setShowMobileCreateCategory(false)} - onSubmit={async (name) => { - try { - await convex.mutation(api.categories.create, { name }); - } catch (err) { - console.error(err); - alert("Failed to create category: " + err.message); - } - }} - /> - )} - {mobileChannelDrawer && ( - <MobileChannelDrawer - channel={mobileChannelDrawer} - isUnread={unreadChannels.has(mobileChannelDrawer._id)} - onMarkAsRead={() => handleMarkAsRead(mobileChannelDrawer._id)} - onEditChannel={() => setShowMobileChannelSettings(mobileChannelDrawer)} - onClose={() => setMobileChannelDrawer(null)} - /> - )} - {showMobileChannelSettings && ( - <MobileChannelSettingsScreen - channel={showMobileChannelSettings} - categories={categories} - onClose={() => setShowMobileChannelSettings(null)} - onDelete={onDeleteChannel} - /> - )} - </div> - ); -}; - -// Category header component (extracted for DnD drag handle) -const CategoryHeader = React.memo( - ({ - group, - groupId, - collapsed, - onToggle, - onAddChannel, - dragListeners, - onContextMenu, - isEditing, - onRenameSubmit, - onRenameCancel, - }) => { - const [editName, setEditName] = useState(group.name); - const inputRef = useRef(null); - - useEffect(() => { - if (isEditing && inputRef.current) { - inputRef.current.focus(); - inputRef.current.select(); - } - }, [isEditing]); - - useEffect(() => { - setEditName(group.name); - }, [group.name, isEditing]); - - return ( - <div - className="channel-category-header" - onClick={() => !isEditing && onToggle(groupId)} - onContextMenu={onContextMenu} - {...(dragListeners || {})} - > - {isEditing ? ( - <input - ref={inputRef} - type="text" - value={editName} - onChange={(e) => setEditName(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Enter") { - e.preventDefault(); - onRenameSubmit(editName.trim()); - } - if (e.key === "Escape") { - e.preventDefault(); - onRenameCancel(); - } - }} - onBlur={() => onRenameCancel()} - onClick={(e) => e.stopPropagation()} - style={{ - background: "var(--bg-tertiary)", - border: "1px solid var(--brand-experiment)", - borderRadius: "2px", - color: "var(--text-normal)", - fontSize: "12px", - fontWeight: 600, - textTransform: "uppercase", - padding: "1px 4px", - outline: "none", - width: "100%", - letterSpacing: ".02em", - }} - /> - ) : ( - <span className="category-label">{group.name}</span> - )} - <div className={`category-chevron ${collapsed ? "collapsed" : ""}`}> - <ColoredIcon src={categoryCollapsedIcon} color="currentColor" size="12px" /> - </div> - <button - className="category-add-btn" - onClick={(e) => { - e.stopPropagation(); - onAddChannel(groupId); - }} - title="Create Channel" - > - + - </button> - </div> - ); - }, -); - -export default Sidebar; diff --git a/packages/shared/src/components/SlashCommandMenu.jsx b/packages/shared/src/components/SlashCommandMenu.jsx deleted file mode 100644 index b4a5bf8..0000000 --- a/packages/shared/src/components/SlashCommandMenu.jsx +++ /dev/null @@ -1,51 +0,0 @@ -import React, { useEffect, useRef } from 'react'; - -const SlashCommandMenu = ({ commands, selectedIndex, onSelect, onHover }) => { - const scrollerRef = useRef(null); - - useEffect(() => { - if (!scrollerRef.current) return; - const selected = scrollerRef.current.querySelector('.slash-command-row.selected'); - if (selected) selected.scrollIntoView({ block: 'nearest' }); - }, [selectedIndex]); - - if (!commands || commands.length === 0) return null; - - const grouped = {}; - for (const cmd of commands) { - const cat = cmd.category || 'Built-In'; - if (!grouped[cat]) grouped[cat] = []; - grouped[cat].push(cmd); - } - - let globalIndex = 0; - - return ( - <div className="slash-command-menu"> - <div className="slash-command-scroller" ref={scrollerRef}> - {Object.entries(grouped).map(([category, cmds]) => ( - <React.Fragment key={category}> - <div className="slash-command-section-header">{category}</div> - {cmds.map((cmd) => { - const idx = globalIndex++; - return ( - <div - key={cmd.name} - className={`slash-command-row${idx === selectedIndex ? ' selected' : ''}`} - onMouseDown={(e) => e.preventDefault()} - onClick={() => onSelect(cmd)} - onMouseEnter={() => onHover(idx)} - > - <span className="slash-command-name">/{cmd.name}</span> - <span className="slash-command-description">{cmd.description}</span> - </div> - ); - })} - </React.Fragment> - ))} - </div> - </div> - ); -}; - -export default SlashCommandMenu; diff --git a/packages/shared/src/components/ThemeSelector.jsx b/packages/shared/src/components/ThemeSelector.jsx deleted file mode 100644 index cf9228f..0000000 --- a/packages/shared/src/components/ThemeSelector.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; -import { useTheme, THEMES, THEME_LABELS } from '../contexts/ThemeContext'; - -const THEME_PREVIEWS = { - [THEMES.LIGHT]: { bg: '#ffffff', sidebar: '#f2f3f5', tertiary: '#e3e5e8', text: '#313338' }, - [THEMES.DARK]: { bg: '#313338', sidebar: '#2b2d31', tertiary: '#1e1f22', text: '#f2f3f5' }, - [THEMES.ASH]: { bg: '#202225', sidebar: '#1a1b1e', tertiary: '#111214', text: '#f0f1f3' }, - [THEMES.ONYX]: { bg: '#0c0c14', sidebar: '#080810', tertiary: '#000000', text: '#e0def0' }, -}; - -const ThemeSelector = ({ onClose }) => { - const { theme, setTheme } = useTheme(); - - return ( - <div className="theme-selector-overlay" onClick={onClose}> - <div className="theme-selector-modal" onClick={(e) => e.stopPropagation()}> - <div className="theme-selector-header"> - <h2>Appearance</h2> - <button className="theme-selector-close" onClick={onClose}>✕</button> - </div> - <div className="theme-selector-grid"> - {Object.values(THEMES).map((themeKey) => { - const preview = THEME_PREVIEWS[themeKey]; - const isActive = theme === themeKey; - return ( - <div - key={themeKey} - className={`theme-card ${isActive ? 'active' : ''}`} - onClick={() => setTheme(themeKey)} - > - <div className="theme-preview" style={{ backgroundColor: preview.bg }}> - <div className="theme-preview-sidebar" style={{ backgroundColor: preview.sidebar }}> - <div className="theme-preview-channel" style={{ backgroundColor: preview.tertiary }} /> - <div className="theme-preview-channel" style={{ backgroundColor: preview.tertiary, width: '60%' }} /> - </div> - <div className="theme-preview-chat"> - <div className="theme-preview-message" style={{ backgroundColor: preview.sidebar, opacity: 0.6 }} /> - <div className="theme-preview-message" style={{ backgroundColor: preview.sidebar, opacity: 0.4, width: '70%' }} /> - </div> - </div> - <div className="theme-card-label"> - <div className={`theme-radio ${isActive ? 'active' : ''}`}> - {isActive && <div className="theme-radio-dot" />} - </div> - <span>{THEME_LABELS[themeKey]}</span> - </div> - </div> - ); - })} - </div> - </div> - </div> - ); -}; - -export default ThemeSelector; diff --git a/packages/shared/src/components/TitleBar.jsx b/packages/shared/src/components/TitleBar.jsx deleted file mode 100644 index 85982d6..0000000 --- a/packages/shared/src/components/TitleBar.jsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import { usePlatform } from '../platform'; -import { TitleBarUpdateIcon } from './UpdateBanner'; - -const TitleBar = () => { - const { windowControls, features } = usePlatform(); - - if (!features.hasWindowControls) return null; - - return ( - <div className="titlebar"> - <div className="titlebar-drag-region" /> - <div className="titlebar-title">Brycord</div> - <div className="titlebar-buttons"> - <TitleBarUpdateIcon /> - <button - className="titlebar-btn titlebar-minimize" - onClick={() => windowControls.minimize()} - aria-label="Minimize" - > - <svg width="12" height="12" viewBox="0 0 12 12"> - <rect fill="currentColor" width="10" height="1" x="1" y="6" /> - </svg> - </button> - <button - className="titlebar-btn titlebar-maximize" - onClick={() => windowControls.maximize()} - aria-label="Maximize" - > - <svg width="12" height="12" viewBox="0 0 12 12"> - <rect fill="none" stroke="currentColor" strokeWidth="1" width="8" height="8" x="2" y="2" /> - </svg> - </button> - <button - className="titlebar-btn titlebar-close" - onClick={() => windowControls.close()} - aria-label="Close" - > - <svg width="12" height="12" viewBox="0 0 12 12"> - <polygon fill="currentColor" points="11,1.576 10.424,1 6,5.424 1.576,1 1,1.576 5.424,6 1,10.424 1.576,11 6,6.576 10.424,11 11,10.424 6.576,6" /> - </svg> - </button> - </div> - </div> - ); -}; - -export default TitleBar; diff --git a/packages/shared/src/components/Toast.jsx b/packages/shared/src/components/Toast.jsx deleted file mode 100644 index ea4572b..0000000 --- a/packages/shared/src/components/Toast.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import React, { useState, useEffect, useCallback } from 'react'; -import ReactDOM from 'react-dom'; - -const USER_COLORS = ['#5865F2', '#EBA7CD', '#57F287', '#FEE75C', '#EB459E', '#ED4245']; - -function getUserColor(name) { - let hash = 0; - for (let i = 0; i < name.length; i++) { - hash = name.charCodeAt(i) + ((hash << 5) - hash); - } - return USER_COLORS[Math.abs(hash) % USER_COLORS.length]; -} - -const ToastContainer = ({ toasts, removeToast }) => { - if (toasts.length === 0) return null; - - return ReactDOM.createPortal( - <div className="toast-container"> - {toasts.map(toast => ( - <ToastItem key={toast.id} toast={toast} onDismiss={() => removeToast(toast.id)} /> - ))} - </div>, - document.body - ); -}; - -const ToastItem = ({ toast, onDismiss }) => { - const [exiting, setExiting] = useState(false); - - useEffect(() => { - const timer = setTimeout(() => { - setExiting(true); - setTimeout(onDismiss, 300); - }, 5000); - return () => clearTimeout(timer); - }, [onDismiss]); - - return ( - <div className={`toast ${exiting ? 'toast-exit' : 'toast-enter'}`}> - <div className="toast-avatar" style={{ backgroundColor: getUserColor(toast.username || '') }}> - {(toast.username || '?').substring(0, 1).toUpperCase()} - </div> - <div className="toast-body"> - <div className="toast-title">New message from <strong>{toast.username}</strong></div> - <div className="toast-preview">{toast.preview}</div> - </div> - <button className="toast-close" onClick={() => { setExiting(true); setTimeout(onDismiss, 300); }}> - × - </button> - </div> - ); -}; - -export function useToasts() { - const [toasts, setToasts] = useState([]); - - const addToast = useCallback((toast) => { - const id = Date.now() + Math.random(); - setToasts(prev => [...prev.slice(-4), { ...toast, id }]); - }, []); - - const removeToast = useCallback((id) => { - setToasts(prev => prev.filter(t => t.id !== id)); - }, []); - - return { toasts, addToast, removeToast, ToastContainer: () => <ToastContainer toasts={toasts} removeToast={removeToast} /> }; -} - -export default ToastContainer; diff --git a/packages/shared/src/components/Tooltip.jsx b/packages/shared/src/components/Tooltip.jsx deleted file mode 100644 index 7aa3ccc..0000000 --- a/packages/shared/src/components/Tooltip.jsx +++ /dev/null @@ -1,90 +0,0 @@ -import React, { useState, useRef, useEffect } from 'react'; -import ReactDOM from 'react-dom'; - -const Tooltip = ({ children, text, position = 'top' }) => { - const [visible, setVisible] = useState(false); - const [coords, setCoords] = useState({ top: 0, left: 0 }); - const triggerRef = useRef(null); - const timeoutRef = useRef(null); - - const showTooltip = () => { - timeoutRef.current = setTimeout(() => { - if (triggerRef.current) { - const rect = triggerRef.current.getBoundingClientRect(); - let top, left; - - switch (position) { - case 'bottom': - top = rect.bottom + 8; - left = rect.left + rect.width / 2; - break; - case 'left': - top = rect.top + rect.height / 2; - left = rect.left - 8; - break; - case 'right': - top = rect.top + rect.height / 2; - left = rect.right + 8; - break; - default: // top - top = rect.top - 8; - left = rect.left + rect.width / 2; - break; - } - setCoords({ top, left }); - setVisible(true); - } - }, 200); - }; - - const hideTooltip = () => { - if (timeoutRef.current) clearTimeout(timeoutRef.current); - setVisible(false); - }; - - useEffect(() => { - return () => { if (timeoutRef.current) clearTimeout(timeoutRef.current); }; - }, []); - - const getTransformStyle = () => { - switch (position) { - case 'bottom': return 'translate(-50%, 0)'; - case 'left': return 'translate(-100%, -50%)'; - case 'right': return 'translate(0, -50%)'; - default: return 'translate(-50%, -100%)'; - } - }; - - const getArrowClass = () => `tooltip-arrow tooltip-arrow-${position}`; - - return ( - <> - <div - ref={triggerRef} - onMouseEnter={showTooltip} - onMouseLeave={hideTooltip} - style={{ display: 'inline-flex' }} - > - {children} - </div> - {visible && ReactDOM.createPortal( - <div - className="tooltip" - style={{ - position: 'fixed', - top: coords.top, - left: coords.left, - transform: getTransformStyle(), - zIndex: 10001, - }} - > - {text} - <div className={getArrowClass()} /> - </div>, - document.body - )} - </> - ); -}; - -export default Tooltip; diff --git a/packages/shared/src/components/UpdateBanner.jsx b/packages/shared/src/components/UpdateBanner.jsx deleted file mode 100644 index 9aaa915..0000000 --- a/packages/shared/src/components/UpdateBanner.jsx +++ /dev/null @@ -1,143 +0,0 @@ -import React, { useState, useEffect, createContext, useContext } from 'react'; -import { usePlatform } from '../platform'; -import ColoredIcon from './ColoredIcon'; -import updateIcon from '../assets/icons/update.svg'; - -const RELEASE_URL = 'https://gitea.moyettes.com/Moyettes/DiscordClone/releases/tag/latest'; - -const UpdateContext = createContext(null); - -export function useUpdateCheck() { - return useContext(UpdateContext); -} - -export function UpdateProvider({ children }) { - const { updates, features } = usePlatform(); - const [state, setState] = useState(null); - - useEffect(() => { - if (!features.hasNativeUpdates || !updates) return; - - updates.checkUpdate().then((result) => { - if (!result?.updateAvailable) return; - setState(result); - }).catch(() => {}); - }, []); - - return ( - <UpdateContext.Provider value={state}> - {children} - {state && (state.updateType === 'major' || state.updateType === 'minor' || !features.hasWindowControls) && ( - <ForcedUpdateModal updateType={state.updateType} latestVersion={state.latestVersion} /> - )} - </UpdateContext.Provider> - ); -} - -function ForcedUpdateModal({ updateType, latestVersion }) { - const { links, updates } = usePlatform(); - const [downloading, setDownloading] = useState(false); - const [progress, setProgress] = useState(0); - const [error, setError] = useState(null); - - useEffect(() => { - if (!downloading || !updates?.onDownloadProgress) return; - updates.onDownloadProgress(({ percent }) => { - setProgress(percent); - }); - }, [downloading]); - - const handleDownload = async () => { - if (updates?.installUpdate) { - setDownloading(true); - setError(null); - try { - await updates.installUpdate(); - } catch (e) { - setError('Download failed. Please try again.'); - setDownloading(false); - } - } else { - links.openExternal(RELEASE_URL); - } - }; - - const isPatch = updateType === 'patch'; - - return ( - <div className="forced-update-overlay"> - <div className="forced-update-modal"> - <h2>{isPatch ? 'Update Available' : 'Update Required'}</h2> - <p> - {isPatch - ? `A new version (v${latestVersion}) is available.` - : `A new version (v${latestVersion}) is available. This update is required to continue using the app.` - } - </p> - {downloading ? ( - <div className="update-progress"> - <div className="update-progress-bar-bg"> - <div - className="update-progress-bar-fill" - style={{ width: `${progress}%` }} - /> - </div> - <span className="update-progress-text">Downloading... {progress}%</span> - </div> - ) : ( - <button className="forced-update-btn" onClick={handleDownload}> - Download Update - </button> - )} - {error && <p className="update-error-text">{error}</p>} - </div> - </div> - ); -} - -export function TitleBarUpdateIcon() { - const { links, updates } = usePlatform(); - const update = useUpdateCheck(); - const [downloading, setDownloading] = useState(false); - const [progress, setProgress] = useState(0); - - useEffect(() => { - if (!downloading || !updates?.onDownloadProgress) return; - updates.onDownloadProgress(({ percent }) => { - setProgress(percent); - }); - }, [downloading]); - - if (!update) return null; - - const handleClick = async () => { - if (updates?.installUpdate && !downloading) { - setDownloading(true); - try { - await updates.installUpdate(); - } catch { - setDownloading(false); - } - } else if (!downloading) { - links.openExternal(RELEASE_URL); - } - }; - - const label = downloading - ? `Downloading update... ${progress}%` - : `Update available: v${update.latestVersion}`; - - return ( - <button - className="titlebar-btn" - onClick={handleClick} - aria-label={label} - title={label} - style={{ borderRight: '1px solid var(--app-frame-border)' }} - > - <div style={{ marginRight: '12px' }}> - <ColoredIcon src={updateIcon} color="#3ba55c" size="20px" /> - </div> - </button> - ); -} diff --git a/packages/shared/src/components/UserProfilePopup.jsx b/packages/shared/src/components/UserProfilePopup.jsx deleted file mode 100644 index 52384c5..0000000 --- a/packages/shared/src/components/UserProfilePopup.jsx +++ /dev/null @@ -1,138 +0,0 @@ -import React, { useRef, useEffect, useState } from 'react'; -import ReactDOM from 'react-dom'; -import { useQuery } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; -import Avatar from './Avatar'; - -const STATUS_LABELS = { - online: 'Online', - idle: 'Idle', - dnd: 'Do Not Disturb', - invisible: 'Invisible', - offline: 'Offline', -}; - -const STATUS_COLORS = { - online: '#3ba55c', - idle: '#faa61a', - dnd: '#ed4245', - invisible: '#747f8d', - offline: '#747f8d', -}; - -const USER_COLORS = ['#5865F2', '#EBA7CD', '#57F287', '#FEE75C', '#EB459E', '#ED4245']; - -function getUserColor(name) { - let hash = 0; - for (let i = 0; i < name.length; i++) { - hash = name.charCodeAt(i) + ((hash << 5) - hash); - } - return USER_COLORS[Math.abs(hash) % USER_COLORS.length]; -} - -const UserProfilePopup = ({ userId, username, avatarUrl, status, position, onClose, onSendMessage }) => { - const popupRef = useRef(null); - const [note, setNote] = useState(''); - - // Fetch member data (roles, aboutMe) for this user - const allUsers = useQuery(api.auth.getPublicKeys) || []; - const userData = allUsers.find(u => u.id === userId); - - useEffect(() => { - const handleClick = (e) => { - if (popupRef.current && !popupRef.current.contains(e.target)) { - onClose(); - } - }; - document.addEventListener('mousedown', handleClick); - return () => document.removeEventListener('mousedown', handleClick); - }, [onClose]); - - // Load note from localStorage - useEffect(() => { - if (userId) { - const saved = localStorage.getItem(`note_${userId}`); - if (saved) setNote(saved); - } - }, [userId]); - - const handleNoteChange = (e) => { - const val = e.target.value; - setNote(val); - if (userId) { - localStorage.setItem(`note_${userId}`, val); - } - }; - - const userColor = getUserColor(username || 'Unknown'); - const userStatus = status || 'online'; - const resolvedAvatarUrl = avatarUrl || userData?.avatarUrl; - const aboutMe = userData?.aboutMe; - - const style = { - position: 'fixed', - top: Math.min(position.y, window.innerHeight - 420), - left: Math.min(position.x, window.innerWidth - 320), - zIndex: 10000, - }; - - return ReactDOM.createPortal( - <div ref={popupRef} className="user-profile-popup" style={style}> - <div className="user-profile-banner" style={{ backgroundColor: userColor }} /> - <div className="user-profile-body"> - <div className="user-profile-avatar-wrapper"> - <Avatar - username={username} - avatarUrl={resolvedAvatarUrl} - size={64} - className="user-profile-avatar" - style={{ border: '4px solid var(--background-base-lowest)' }} - /> - <div - className="user-profile-status-dot" - style={{ backgroundColor: STATUS_COLORS[userStatus] }} - /> - </div> - <div className="user-profile-name">{userData?.displayName || username}</div> - {userData?.displayName && ( - <div style={{ color: 'var(--header-secondary)', fontSize: '12px', marginTop: '-4px', marginBottom: '4px', paddingLeft: '12px' }}> - {username} - </div> - )} - <div className="user-profile-status-text"> - {userData?.customStatus || STATUS_LABELS[userStatus] || 'Online'} - </div> - <div className="user-profile-divider" /> - - <div className="user-profile-section-header">ABOUT ME</div> - <div className="user-profile-about"> - {aboutMe || 'No information set.'} - </div> - - <div className="user-profile-divider" /> - - <div className="user-profile-section-header">NOTE</div> - <textarea - className="user-profile-note-input" - placeholder="Click to add a note" - value={note} - onChange={handleNoteChange} - rows={2} - /> - - {onSendMessage && ( - <button - className="user-profile-message-btn" - onClick={() => { onSendMessage(userId, username); onClose(); }} - style={{ marginTop: '8px' }} - > - Send Message - </button> - )} - </div> - </div>, - document.body - ); -}; - -export default UserProfilePopup; diff --git a/packages/shared/src/components/UserSettings.jsx b/packages/shared/src/components/UserSettings.jsx deleted file mode 100644 index e77e403..0000000 --- a/packages/shared/src/components/UserSettings.jsx +++ /dev/null @@ -1,1477 +0,0 @@ -import React, { useState, useEffect, useRef, useCallback } from 'react'; -import ReactDOM from 'react-dom'; -import { useQuery, useConvex } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; -import Avatar from './Avatar'; -import AvatarCropModal from './AvatarCropModal'; -import { useTheme, THEMES, THEME_LABELS } from '../contexts/ThemeContext'; -import { useVoice } from '../contexts/VoiceContext'; -import { useSearch } from '../contexts/SearchContext'; -import { usePlatform } from '../platform'; -import { useIsMobile } from '../hooks/useIsMobile'; - -const THEME_PREVIEWS = { - [THEMES.LIGHT]: { bg: '#ffffff', sidebar: '#f2f3f5', tertiary: '#e3e5e8', text: '#313338' }, - [THEMES.DARK]: { bg: '#313338', sidebar: '#2b2d31', tertiary: '#1e1f22', text: '#f2f3f5' }, - [THEMES.ASH]: { bg: '#202225', sidebar: '#1a1b1e', tertiary: '#111214', text: '#f0f1f3' }, - [THEMES.ONYX]: { bg: '#0c0c14', sidebar: '#080810', tertiary: '#000000', text: '#e0def0' }, -}; - -const TABS = [ - { id: 'account', label: 'My Account', section: 'USER SETTINGS' }, - { id: 'security', label: 'Security', section: 'USER SETTINGS' }, - { id: 'appearance', label: 'Appearance', section: 'USER SETTINGS' }, - { id: 'voice', label: 'Voice & Video', section: 'APP SETTINGS' }, - { id: 'keybinds', label: 'Keybinds', section: 'APP SETTINGS' }, - { id: 'search', label: 'Search', section: 'APP SETTINGS' }, -]; - -const UserSettings = ({ onClose, userId, username, onLogout }) => { - const [activeTab, setActiveTab] = useState('account'); - const isMobile = useIsMobile(); - const [mobileScreen, setMobileScreen] = useState('menu'); - - useEffect(() => { - const handleKey = (e) => { - if (e.key === 'Escape') { - if (isMobile && mobileScreen !== 'menu') { - setMobileScreen('menu'); - } else { - onClose(); - } - } - }; - window.addEventListener('keydown', handleKey); - return () => window.removeEventListener('keydown', handleKey); - }, [onClose, isMobile, mobileScreen]); - - const renderSidebar = () => { - let lastSection = null; - const items = []; - - TABS.forEach((tab, i) => { - if (tab.section !== lastSection) { - if (lastSection !== null) { - items.push(<div key={`sep-${i}`} style={{ height: '1px', backgroundColor: 'var(--border-subtle)', margin: '8px 10px' }} />); - } - items.push( - <div key={`hdr-${tab.section}`} style={{ - fontSize: '12px', fontWeight: '700', color: 'var(--text-muted)', - marginBottom: '6px', textTransform: 'uppercase', padding: '0 10px' - }}> - {tab.section} - </div> - ); - lastSection = tab.section; - } - items.push( - <div - key={tab.id} - onClick={() => setActiveTab(tab.id)} - style={{ - padding: '6px 10px', borderRadius: '4px', cursor: 'pointer', marginBottom: '2px', fontSize: '15px', - backgroundColor: activeTab === tab.id ? 'var(--background-modifier-selected)' : 'transparent', - color: activeTab === tab.id ? 'var(--header-primary)' : 'var(--header-secondary)', - }} - > - {tab.label} - </div> - ); - }); - - items.push(<div key="sep-logout" style={{ height: '1px', backgroundColor: 'var(--border-subtle)', margin: '8px 10px' }} />); - items.push( - <div - key="logout" - onClick={onLogout} - style={{ - padding: '6px 10px', borderRadius: '4px', cursor: 'pointer', fontSize: '15px', - color: '#ed4245', display: 'flex', justifyContent: 'space-between', alignItems: 'center', - }} - > - Log Out - <svg width="16" height="16" viewBox="0 0 24 24" fill="none"> - <path d="M16 17L21 12L16 7" stroke="#ed4245" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> - <path d="M21 12H9" stroke="#ed4245" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> - <path d="M9 21H5C4.46957 21 3.96086 20.7893 3.58579 20.4142C3.21071 20.0391 3 19.5304 3 19V5C3 4.46957 3.21071 3.96086 3.58579 3.58579C3.96086 3.21071 4.46957 3 5 3H9" stroke="#ed4245" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> - </svg> - </div> - ); - - return items; - }; - - // ─── Mobile render functions ─── - - const renderMobileHeader = (title, onBack) => ( - <div className="msm-header"> - <button className="msm-back-btn" onClick={onBack}> - <svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg> - </button> - <div className="msm-header-title">{title}</div> - <div style={{ width: 32 }} /> - </div> - ); - - const renderMobileMenu = () => ( - <div className="msm-screen"> - <div className="msm-header"> - <div style={{ flex: 1 }} /> - <button className="msm-back-btn" onClick={onClose}> - <svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg> - </button> - </div> - <div className="msm-content"> - <div className="msm-section-label">User Settings</div> - <div className="msm-card"> - <div className="msm-card-item" onClick={() => setMobileScreen('account')}> - <span className="msm-card-item-icon"> - <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg> - </span> - <span style={{ flex: 1, fontSize: 15, color: 'var(--header-primary)' }}>Account</span> - <span className="msm-card-item-chevron"> - <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> - </span> - </div> - <div className="msm-card-item" onClick={() => setMobileScreen('security')}> - <span className="msm-card-item-icon"> - <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4z"/></svg> - </span> - <span style={{ flex: 1, fontSize: 15, color: 'var(--header-primary)' }}>Security</span> - <span className="msm-card-item-chevron"> - <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> - </span> - </div> - </div> - - <div className="msm-section-label">App Settings</div> - <div className="msm-card"> - <div className="msm-card-item" onClick={() => setMobileScreen('appearance')}> - <span className="msm-card-item-icon"> - <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-1 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></svg> - </span> - <span style={{ flex: 1, fontSize: 15, color: 'var(--header-primary)' }}>Appearance</span> - <span className="msm-card-item-chevron"> - <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> - </span> - </div> - <div className="msm-card-item" onClick={() => setMobileScreen('voice')}> - <span className="msm-card-item-icon"> - <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"/></svg> - </span> - <span style={{ flex: 1, fontSize: 15, color: 'var(--header-primary)' }}>Voice & Video</span> - <span className="msm-card-item-chevron"> - <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> - </span> - </div> - <div className="msm-card-item" onClick={() => setMobileScreen('keybinds')}> - <span className="msm-card-item-icon"> - <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M20 5H4c-1.1 0-1.99.9-1.99 2L2 17c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-9 3h2v2h-2V8zm0 3h2v2h-2v-2zM8 8h2v2H8V8zm0 3h2v2H8v-2zm-1 2H5v-2h2v2zm0-3H5V8h2v2zm9 7H8v-2h8v2zm0-4h-2v-2h2v2zm0-3h-2V8h2v2zm3 3h-2v-2h2v2zm0-3h-2V8h2v2z"/></svg> - </span> - <span style={{ flex: 1, fontSize: 15, color: 'var(--header-primary)' }}>Keybinds</span> - <span className="msm-card-item-chevron"> - <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> - </span> - </div> - <div className="msm-card-item" onClick={() => setMobileScreen('search')}> - <span className="msm-card-item-icon"> - <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg> - </span> - <span style={{ flex: 1, fontSize: 15, color: 'var(--header-primary)' }}>Search</span> - <span className="msm-card-item-chevron"> - <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> - </span> - </div> - </div> - - {/* Log Out */} - <div className="msm-card" style={{ marginTop: 24 }}> - <div className="msm-card-item msm-card-item-danger" onClick={onLogout}> - <span className="msm-card-item-icon" style={{ color: '#ed4245' }}> - <svg width="20" height="20" viewBox="0 0 24 24" fill="none"> - <path d="M16 17L21 12L16 7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> - <path d="M21 12H9" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> - <path d="M9 21H5C4.46957 21 3.96086 20.7893 3.58579 20.4142C3.21071 20.0391 3 19.5304 3 19V5C3 4.46957 3.21071 3.96086 3.58579 3.58579C3.96086 3.21071 4.46957 3 5 3H9" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> - </svg> - </span> - Log Out - </div> - </div> - </div> - </div> - ); - - const renderMobileContent = () => { - switch (mobileScreen) { - case 'account': - return ( - <div className="msm-screen"> - {renderMobileHeader('Account', () => setMobileScreen('menu'))} - <div className="msm-content"> - <MyAccountTab userId={userId} username={username} /> - </div> - </div> - ); - case 'security': - return ( - <div className="msm-screen"> - {renderMobileHeader('Security', () => setMobileScreen('menu'))} - <div className="msm-content"> - <SecurityTab /> - </div> - </div> - ); - case 'appearance': - return ( - <div className="msm-screen"> - {renderMobileHeader('Appearance', () => setMobileScreen('menu'))} - <div className="msm-content"> - <AppearanceTab /> - </div> - </div> - ); - case 'voice': - return ( - <div className="msm-screen"> - {renderMobileHeader('Voice & Video', () => setMobileScreen('menu'))} - <div className="msm-content"> - <VoiceVideoTab /> - </div> - </div> - ); - case 'keybinds': - return ( - <div className="msm-screen"> - {renderMobileHeader('Keybinds', () => setMobileScreen('menu'))} - <div className="msm-content"> - <KeybindsTab /> - </div> - </div> - ); - case 'search': - return ( - <div className="msm-screen"> - {renderMobileHeader('Search', () => setMobileScreen('menu'))} - <div className="msm-content"> - <SearchTab userId={userId} /> - </div> - </div> - ); - default: - return renderMobileMenu(); - } - }; - - if (isMobile) { - return ReactDOM.createPortal( - <div className="msm-root">{renderMobileContent()}</div>, - document.body - ); - } - - return ( - <div style={{ - position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, - backgroundColor: 'var(--bg-primary)', zIndex: 1000, - display: 'flex', color: 'var(--text-normal)', - }}> - {/* Sidebar */} - <div style={{ - width: '218px', backgroundColor: 'var(--bg-secondary)', - display: 'flex', flexDirection: 'column', alignItems: 'flex-end', - padding: '60px 6px 60px 20px', overflowY: 'auto', - }}> - <div style={{ width: '100%' }}> - {renderSidebar()} - </div> - </div> - - {/* Content */} - <div style={{ flex: 1, display: 'flex', justifyContent: 'flex-start', overflowY: 'auto' }}> - <div style={{ flex: 1, maxWidth: '740px', padding: '60px 40px 80px', position: 'relative' }}> - {activeTab === 'account' && <MyAccountTab userId={userId} username={username} />} - {activeTab === 'security' && <SecurityTab />} - {activeTab === 'appearance' && <AppearanceTab />} - {activeTab === 'voice' && <VoiceVideoTab />} - {activeTab === 'keybinds' && <KeybindsTab />} - {activeTab === 'search' && <SearchTab userId={userId} />} - </div> - - {/* Right spacer with close button */} - <div style={{ flex: '0 0 36px', paddingTop: '60px', marginLeft: '8px' }}> - <button - onClick={onClose} - style={{ - width: '36px', height: '36px', borderRadius: '50%', - border: '2px solid var(--header-secondary)', background: 'transparent', - color: 'var(--header-secondary)', cursor: 'pointer', - display: 'flex', alignItems: 'center', justifyContent: 'center', - fontSize: '18px', - }} - > - ✕ - </button> - <div style={{ fontSize: '13px', fontWeight: '600', color: 'var(--header-secondary)', textAlign: 'center', marginTop: '4px' }}> - ESC - </div> - </div> - <div style={{ flex: '0.5' }} /> - </div> - </div> - ); -}; - -/* ========================================= - MY ACCOUNT TAB - ========================================= */ -const MyAccountTab = ({ userId, username }) => { - const allUsers = useQuery(api.auth.getPublicKeys); - const convex = useConvex(); - - const currentUser = allUsers?.find(u => u.id === userId); - - const [displayName, setDisplayName] = useState(''); - const [aboutMe, setAboutMe] = useState(''); - const [customStatus, setCustomStatus] = useState(''); - const [avatarFile, setAvatarFile] = useState(null); - const [avatarPreview, setAvatarPreview] = useState(null); - const [saving, setSaving] = useState(false); - const [hasChanges, setHasChanges] = useState(false); - const [showCropModal, setShowCropModal] = useState(false); - const [rawImageUrl, setRawImageUrl] = useState(null); - const fileInputRef = useRef(null); - const [joinSoundFile, setJoinSoundFile] = useState(null); - const [joinSoundPreviewName, setJoinSoundPreviewName] = useState(null); - const [removeJoinSound, setRemoveJoinSound] = useState(false); - const joinSoundInputRef = useRef(null); - const joinSoundAudioRef = useRef(null); - - useEffect(() => { - if (currentUser) { - setDisplayName(currentUser.displayName || ''); - setAboutMe(currentUser.aboutMe || ''); - setCustomStatus(currentUser.customStatus || ''); - } - }, [currentUser]); - - useEffect(() => { - if (!currentUser) return; - const changed = - displayName !== (currentUser.displayName || '') || - aboutMe !== (currentUser.aboutMe || '') || - customStatus !== (currentUser.customStatus || '') || - avatarFile !== null || - joinSoundFile !== null || - removeJoinSound; - setHasChanges(changed); - }, [displayName, aboutMe, customStatus, avatarFile, joinSoundFile, removeJoinSound, currentUser]); - - const handleAvatarChange = (e) => { - const file = e.target.files?.[0]; - if (!file) return; - const url = URL.createObjectURL(file); - setRawImageUrl(url); - setShowCropModal(true); - e.target.value = ''; - }; - - const handleCropApply = (blob) => { - const file = new File([blob], 'avatar.png', { type: 'image/png' }); - setAvatarFile(file); - const previewUrl = URL.createObjectURL(blob); - setAvatarPreview(previewUrl); - if (rawImageUrl) URL.revokeObjectURL(rawImageUrl); - setRawImageUrl(null); - setShowCropModal(false); - }; - - const handleCropCancel = () => { - if (rawImageUrl) URL.revokeObjectURL(rawImageUrl); - setRawImageUrl(null); - setShowCropModal(false); - }; - - const handleJoinSoundChange = (e) => { - const file = e.target.files?.[0]; - if (!file) return; - if (file.size > 10 * 1024 * 1024) { - alert('Join sound must be under 10MB'); - e.target.value = ''; - return; - } - setJoinSoundFile(file); - setJoinSoundPreviewName(file.name); - setRemoveJoinSound(false); - e.target.value = ''; - }; - - const handleJoinSoundPreview = () => { - if (joinSoundAudioRef.current) { - joinSoundAudioRef.current.pause(); - joinSoundAudioRef.current = null; - } - let src; - if (joinSoundFile) { - src = URL.createObjectURL(joinSoundFile); - } else if (currentUser?.joinSoundUrl) { - src = currentUser.joinSoundUrl; - } - if (src) { - const audio = new Audio(src); - audio.volume = 0.5; - audio.play().catch(e => console.error('Preview failed', e)); - joinSoundAudioRef.current = audio; - } - }; - - const handleRemoveJoinSound = () => { - setJoinSoundFile(null); - setJoinSoundPreviewName(null); - setRemoveJoinSound(true); - }; - - const handleSave = async () => { - if (!userId || saving) return; - setSaving(true); - try { - let avatarStorageId; - if (avatarFile) { - const uploadUrl = await convex.mutation(api.files.generateUploadUrl); - const res = await fetch(uploadUrl, { - method: 'POST', - headers: { 'Content-Type': avatarFile.type }, - body: avatarFile, - }); - const { storageId } = await res.json(); - avatarStorageId = storageId; - } - let joinSoundStorageId; - if (joinSoundFile) { - const jsUploadUrl = await convex.mutation(api.files.generateUploadUrl); - const jsRes = await fetch(jsUploadUrl, { - method: 'POST', - headers: { 'Content-Type': joinSoundFile.type }, - body: joinSoundFile, - }); - const jsData = await jsRes.json(); - joinSoundStorageId = jsData.storageId; - } - const args = { userId, displayName, aboutMe, customStatus }; - if (avatarStorageId) args.avatarStorageId = avatarStorageId; - if (joinSoundStorageId) args.joinSoundStorageId = joinSoundStorageId; - if (removeJoinSound) args.removeJoinSound = true; - await convex.mutation(api.auth.updateProfile, args); - setAvatarFile(null); - setJoinSoundFile(null); - setJoinSoundPreviewName(null); - setRemoveJoinSound(false); - if (avatarPreview) { - URL.revokeObjectURL(avatarPreview); - setAvatarPreview(null); - } - } catch (err) { - console.error('Failed to save profile:', err); - alert('Failed to save profile: ' + err.message); - } finally { - setSaving(false); - } - }; - - const handleReset = () => { - if (currentUser) { - setDisplayName(currentUser.displayName || ''); - setAboutMe(currentUser.aboutMe || ''); - setCustomStatus(currentUser.customStatus || ''); - } - setAvatarFile(null); - setJoinSoundFile(null); - setJoinSoundPreviewName(null); - setRemoveJoinSound(false); - if (avatarPreview) { - URL.revokeObjectURL(avatarPreview); - setAvatarPreview(null); - } - if (rawImageUrl) { - URL.revokeObjectURL(rawImageUrl); - setRawImageUrl(null); - } - setShowCropModal(false); - }; - - const avatarUrl = avatarPreview || currentUser?.avatarUrl; - - return ( - <div> - <h2 style={{ color: 'var(--header-primary)', margin: '0 0 20px', fontSize: '20px' }}>My Account</h2> - - {/* Profile card */} - <div style={{ backgroundColor: 'var(--bg-secondary)', borderRadius: '8px', overflow: 'hidden' }}> - {/* Banner */} - <div style={{ height: '100px', backgroundColor: 'var(--brand-experiment)' }} /> - - {/* Profile body */} - <div style={{ padding: '0 16px 16px', position: 'relative' }}> - {/* Avatar */} - <div - className="user-settings-avatar-wrapper" - onClick={() => fileInputRef.current?.click()} - style={{ marginTop: '-40px', marginBottom: '12px', width: 'fit-content', cursor: 'pointer', position: 'relative' }} - > - <Avatar username={username} avatarUrl={avatarUrl} size={80} /> - <div className="user-settings-avatar-overlay"> - CHANGE<br/>AVATAR - </div> - <input - ref={fileInputRef} - type="file" - accept="image/*" - onChange={handleAvatarChange} - style={{ display: 'none' }} - /> - </div> - - {/* Fields */} - <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}> - {/* Username (read-only) */} - <div> - <label style={{ - display: 'block', color: 'var(--header-secondary)', fontSize: '12px', - fontWeight: '700', textTransform: 'uppercase', marginBottom: '8px', - }}> - Username - </label> - <div style={{ - backgroundColor: 'var(--bg-tertiary)', borderRadius: '4px', padding: '10px', - color: 'var(--text-muted)', fontSize: '16px', - }}> - {username} - </div> - </div> - - {/* Display Name */} - <div> - <label style={{ - display: 'block', color: 'var(--header-secondary)', fontSize: '12px', - fontWeight: '700', textTransform: 'uppercase', marginBottom: '8px', - }}> - Display Name - </label> - <input - type="text" - value={displayName} - onChange={(e) => setDisplayName(e.target.value)} - placeholder="How others see you in chat" - style={{ - width: '100%', backgroundColor: 'var(--bg-tertiary)', border: 'none', - borderRadius: '4px', padding: '10px', color: 'var(--text-normal)', - fontSize: '16px', outline: 'none', boxSizing: 'border-box', - }} - /> - </div> - - {/* About Me */} - <div> - <label style={{ - display: 'block', color: 'var(--header-secondary)', fontSize: '12px', - fontWeight: '700', textTransform: 'uppercase', marginBottom: '8px', - }}> - About Me - </label> - <textarea - value={aboutMe} - onChange={(e) => setAboutMe(e.target.value.slice(0, 190))} - placeholder="Tell others about yourself" - rows={3} - style={{ - width: '100%', backgroundColor: 'var(--bg-tertiary)', border: 'none', - borderRadius: '4px', padding: '10px', color: 'var(--text-normal)', - fontSize: '16px', outline: 'none', resize: 'none', fontFamily: 'inherit', - boxSizing: 'border-box', - }} - /> - <div style={{ fontSize: '12px', color: 'var(--text-muted)', textAlign: 'right' }}> - {aboutMe.length}/190 - </div> - </div> - - {/* Custom Status */} - <div> - <label style={{ - display: 'block', color: 'var(--header-secondary)', fontSize: '12px', - fontWeight: '700', textTransform: 'uppercase', marginBottom: '8px', - }}> - Custom Status - </label> - <input - type="text" - value={customStatus} - onChange={(e) => setCustomStatus(e.target.value)} - placeholder="Set a custom status" - style={{ - width: '100%', backgroundColor: 'var(--bg-tertiary)', border: 'none', - borderRadius: '4px', padding: '10px', color: 'var(--text-normal)', - fontSize: '16px', outline: 'none', boxSizing: 'border-box', - }} - /> - </div> - - {/* Voice Channel Join Sound */} - <div> - <label style={{ - display: 'block', color: 'var(--header-secondary)', fontSize: '12px', - fontWeight: '700', textTransform: 'uppercase', marginBottom: '8px', - }}> - Voice Channel Join Sound - </label> - <div style={{ display: 'flex', alignItems: 'center', gap: '8px', flexWrap: 'wrap' }}> - <button - onClick={() => joinSoundInputRef.current?.click()} - style={{ - backgroundColor: 'var(--brand-experiment)', color: 'white', border: 'none', - borderRadius: '4px', padding: '8px 16px', cursor: 'pointer', - fontSize: '14px', fontWeight: '500', - }} - > - Upload Sound - </button> - {(joinSoundPreviewName || (!removeJoinSound && currentUser?.joinSoundUrl)) && ( - <button - onClick={handleJoinSoundPreview} - title="Preview join sound" - style={{ - backgroundColor: 'var(--bg-tertiary)', color: 'var(--text-normal)', border: 'none', - borderRadius: '4px', padding: '8px 12px', cursor: 'pointer', fontSize: '14px', - display: 'flex', alignItems: 'center', gap: '4px', - }} - > - <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"> - <polygon points="5,3 19,12 5,21" /> - </svg> - Preview - </button> - )} - {(joinSoundPreviewName || (!removeJoinSound && currentUser?.joinSoundUrl)) && ( - <button - onClick={handleRemoveJoinSound} - style={{ - backgroundColor: 'transparent', color: '#ed4245', border: '1px solid #ed4245', - borderRadius: '4px', padding: '7px 12px', cursor: 'pointer', fontSize: '14px', - }} - > - Remove - </button> - )} - <input - ref={joinSoundInputRef} - type="file" - accept="audio/mpeg,audio/wav,audio/ogg,audio/webm" - onChange={handleJoinSoundChange} - style={{ display: 'none' }} - /> - </div> - <div style={{ fontSize: '12px', color: 'var(--text-muted)', marginTop: '6px' }}> - {joinSoundPreviewName - ? `Selected: ${joinSoundPreviewName}` - : removeJoinSound - ? 'Join sound will be removed on save' - : currentUser?.joinSoundUrl - ? 'Custom sound set — plays when you join a voice channel' - : 'Upload a short audio file (max 10MB) — plays for everyone when you join a voice channel' - } - </div> - </div> - </div> - </div> - </div> - - {/* Save bar */} - {hasChanges && ( - <div style={{ - position: 'sticky', bottom: '0', left: 0, right: 0, - backgroundColor: 'var(--bg-tertiary)', borderRadius: '4px', - padding: '10px 16px', marginTop: '16px', - display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: '12px', - boxShadow: '0 -2px 10px rgba(0,0,0,0.2)', - }}> - <span style={{ color: 'var(--text-muted)', fontSize: '14px', marginRight: 'auto' }}> - Careful — you have unsaved changes! - </span> - <button - onClick={handleReset} - style={{ - background: 'transparent', border: 'none', color: 'var(--header-primary)', - cursor: 'pointer', fontSize: '14px', fontWeight: '500', padding: '8px 16px', - }} - > - Reset - </button> - <button - onClick={handleSave} - disabled={saving} - style={{ - backgroundColor: '#3ba55c', color: 'white', border: 'none', - borderRadius: '4px', padding: '8px 24px', cursor: saving ? 'not-allowed' : 'pointer', - fontSize: '14px', fontWeight: '500', opacity: saving ? 0.7 : 1, - }} - > - {saving ? 'Saving...' : 'Save Changes'} - </button> - </div> - )} - - {showCropModal && rawImageUrl && ( - <AvatarCropModal - imageUrl={rawImageUrl} - onApply={handleCropApply} - onCancel={handleCropCancel} - /> - )} - </div> - ); -}; - -/* ========================================= - SECURITY TAB - ========================================= */ -const SecurityTab = () => { - const [masterKey, setMasterKey] = useState(null); - const [revealed, setRevealed] = useState(false); - const [confirmed, setConfirmed] = useState(false); - const [copied, setCopied] = useState(false); - - useEffect(() => { - const mk = sessionStorage.getItem('masterKey'); - setMasterKey(mk); - }, []); - - const formatKey = (hex) => { - if (!hex) return ''; - return hex.match(/.{1,4}/g).join(' '); - }; - - const handleCopy = () => { - if (!masterKey) return; - navigator.clipboard.writeText(masterKey).then(() => { - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }); - }; - - const handleDownload = () => { - if (!masterKey) return; - const content = [ - '=== RECOVERY KEY ===', - '', - 'This is your Recovery Key for your encrypted account.', - 'Store it in a safe place. If you lose your password, this key is the ONLY way to recover your account.', - '', - 'DO NOT share this key with anyone.', - '', - `Recovery Key: ${masterKey}`, - '', - `Exported: ${new Date().toISOString()}`, - ].join('\n'); - const blob = new Blob([content], { type: 'text/plain' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = 'recovery-key.txt'; - a.click(); - URL.revokeObjectURL(url); - }; - - const labelStyle = { - display: 'block', color: 'var(--header-secondary)', fontSize: '12px', - fontWeight: '700', textTransform: 'uppercase', marginBottom: '8px', - }; - - return ( - <div> - <h2 style={{ color: 'var(--header-primary)', margin: '0 0 20px', fontSize: '20px' }}>Security</h2> - - <div style={{ backgroundColor: 'var(--bg-secondary)', borderRadius: '8px', padding: '20px' }}> - <h3 style={{ color: 'var(--header-primary)', margin: '0 0 8px', fontSize: '16px', fontWeight: '600' }}> - Recovery Key - </h3> - <p style={{ color: 'var(--text-muted)', fontSize: '14px', margin: '0 0 16px', lineHeight: '1.4' }}> - Your Recovery Key allows you to reset your password without losing access to your encrypted messages. - Store it somewhere safe — if you forget your password, this is the <strong style={{ color: 'var(--text-normal)' }}>only way</strong> to recover your account. - </p> - - {!masterKey ? ( - <div style={{ - backgroundColor: 'var(--bg-tertiary)', borderRadius: '4px', padding: '16px', - color: 'var(--text-muted)', fontSize: '14px', - }}> - Recovery Key is not available in this session. Please log out and log back in to access it. - </div> - ) : !revealed ? ( - <div> - {/* Warning box */} - <div style={{ - backgroundColor: 'rgba(250, 166, 26, 0.1)', border: '1px solid rgba(250, 166, 26, 0.4)', - borderRadius: '4px', padding: '12px', marginBottom: '16px', - }}> - <div style={{ color: '#faa61a', fontSize: '14px', fontWeight: '600', marginBottom: '4px' }}> - Warning - </div> - <div style={{ color: 'var(--text-normal)', fontSize: '13px', lineHeight: '1.4' }}> - Anyone with your Recovery Key can reset your password and take control of your account. - Only reveal it in a private, secure environment. - </div> - </div> - - <label style={{ - display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer', - color: 'var(--text-normal)', fontSize: '14px', marginBottom: '16px', - }}> - <input - type="checkbox" - checked={confirmed} - onChange={(e) => setConfirmed(e.target.checked)} - style={{ width: '16px', height: '16px', accentColor: 'var(--brand-experiment)' }} - /> - I understand and want to reveal my Recovery Key - </label> - - <button - onClick={() => setRevealed(true)} - disabled={!confirmed} - style={{ - backgroundColor: 'var(--brand-experiment)', color: 'white', border: 'none', - borderRadius: '4px', padding: '10px 20px', cursor: confirmed ? 'pointer' : 'not-allowed', - fontSize: '14px', fontWeight: '500', opacity: confirmed ? 1 : 0.5, - }} - > - Reveal Recovery Key - </button> - </div> - ) : ( - <div> - <label style={labelStyle}>Your Recovery Key</label> - <div style={{ - backgroundColor: 'var(--bg-tertiary)', borderRadius: '4px', padding: '16px', - fontFamily: 'Consolas, "Courier New", monospace', fontSize: '15px', - color: 'var(--text-normal)', wordBreak: 'break-all', lineHeight: '1.6', - letterSpacing: '1px', marginBottom: '12px', userSelect: 'all', - }}> - {formatKey(masterKey)} - </div> - - <div style={{ display: 'flex', gap: '8px', marginBottom: '16px' }}> - <button - onClick={handleCopy} - style={{ - backgroundColor: 'var(--brand-experiment)', color: 'white', border: 'none', - borderRadius: '4px', padding: '8px 16px', cursor: 'pointer', - fontSize: '14px', fontWeight: '500', - }} - > - {copied ? 'Copied!' : 'Copy'} - </button> - <button - onClick={handleDownload} - style={{ - backgroundColor: 'var(--bg-tertiary)', color: 'var(--text-normal)', border: 'none', - borderRadius: '4px', padding: '8px 16px', cursor: 'pointer', - fontSize: '14px', fontWeight: '500', - }} - > - Download - </button> - <button - onClick={() => { setRevealed(false); setConfirmed(false); }} - style={{ - backgroundColor: 'transparent', color: 'var(--text-muted)', border: '1px solid var(--border-subtle)', - borderRadius: '4px', padding: '8px 16px', cursor: 'pointer', - fontSize: '14px', fontWeight: '500', - }} - > - Hide - </button> - </div> - </div> - )} - </div> - </div> - ); -}; - -/* ========================================= - APPEARANCE TAB - ========================================= */ -const AppearanceTab = () => { - const { theme, setTheme } = useTheme(); - - return ( - <div> - <h2 style={{ color: 'var(--header-primary)', margin: '0 0 20px', fontSize: '20px' }}>Appearance</h2> - <div style={{ marginBottom: '16px' }}> - <label style={{ - display: 'block', color: 'var(--header-secondary)', fontSize: '12px', - fontWeight: '700', textTransform: 'uppercase', marginBottom: '12px', - }}> - Theme - </label> - <div className="theme-selector-grid"> - {Object.values(THEMES).map((themeKey) => { - const preview = THEME_PREVIEWS[themeKey]; - const isActive = theme === themeKey; - return ( - <div - key={themeKey} - className={`theme-card ${isActive ? 'active' : ''}`} - onClick={() => setTheme(themeKey)} - > - <div className="theme-preview" style={{ backgroundColor: preview.bg }}> - <div className="theme-preview-sidebar" style={{ backgroundColor: preview.sidebar }}> - <div className="theme-preview-channel" style={{ backgroundColor: preview.tertiary }} /> - <div className="theme-preview-channel" style={{ backgroundColor: preview.tertiary, width: '60%' }} /> - </div> - <div className="theme-preview-chat"> - <div className="theme-preview-message" style={{ backgroundColor: preview.sidebar, opacity: 0.6 }} /> - <div className="theme-preview-message" style={{ backgroundColor: preview.sidebar, opacity: 0.4, width: '70%' }} /> - </div> - </div> - <div className="theme-card-label"> - <div className={`theme-radio ${isActive ? 'active' : ''}`}> - {isActive && <div className="theme-radio-dot" />} - </div> - <span>{THEME_LABELS[themeKey]}</span> - </div> - </div> - ); - })} - </div> - </div> - </div> - ); -}; - -/* ========================================= - VOICE & VIDEO TAB - ========================================= */ -const VoiceVideoTab = () => { - const { switchDevice, setGlobalOutputVolume } = useVoice(); - const [inputDevices, setInputDevices] = useState([]); - const [outputDevices, setOutputDevices] = useState([]); - const [selectedInput, setSelectedInput] = useState(() => localStorage.getItem('voiceInputDevice') || 'default'); - const [selectedOutput, setSelectedOutput] = useState(() => localStorage.getItem('voiceOutputDevice') || 'default'); - const [inputVolume, setInputVolume] = useState(() => parseInt(localStorage.getItem('voiceInputVolume') || '100')); - const [outputVolume, setOutputVolume] = useState(() => parseInt(localStorage.getItem('voiceOutputVolume') || '100')); - const [micTesting, setMicTesting] = useState(false); - const [micLevel, setMicLevel] = useState(0); - const micStreamRef = useRef(null); - const animFrameRef = useRef(null); - const analyserRef = useRef(null); - - useEffect(() => { - const enumerate = async () => { - try { - // Request permission to get labels - const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); - stream.getTracks().forEach(t => t.stop()); - - const devices = await navigator.mediaDevices.enumerateDevices(); - setInputDevices(devices.filter(d => d.kind === 'audioinput')); - setOutputDevices(devices.filter(d => d.kind === 'audiooutput')); - } catch (err) { - console.error('Failed to enumerate devices:', err); - } - }; - enumerate(); - }, []); - - useEffect(() => { - localStorage.setItem('voiceInputDevice', selectedInput); - switchDevice('audioinput', selectedInput); - }, [selectedInput]); - - useEffect(() => { - localStorage.setItem('voiceOutputDevice', selectedOutput); - switchDevice('audiooutput', selectedOutput); - }, [selectedOutput]); - - useEffect(() => { - localStorage.setItem('voiceInputVolume', String(inputVolume)); - }, [inputVolume]); - - useEffect(() => { - localStorage.setItem('voiceOutputVolume', String(outputVolume)); - setGlobalOutputVolume(outputVolume); - }, [outputVolume]); - - const startMicTest = async () => { - try { - const constraints = { audio: selectedInput !== 'default' ? { deviceId: { exact: selectedInput } } : true }; - const stream = await navigator.mediaDevices.getUserMedia(constraints); - micStreamRef.current = stream; - - const audioCtx = new AudioContext(); - const source = audioCtx.createMediaStreamSource(stream); - const analyser = audioCtx.createAnalyser(); - analyser.fftSize = 256; - source.connect(analyser); - analyserRef.current = analyser; - - setMicTesting(true); - - const dataArray = new Uint8Array(analyser.frequencyBinCount); - const tick = () => { - analyser.getByteFrequencyData(dataArray); - const avg = dataArray.reduce((sum, v) => sum + v, 0) / dataArray.length; - setMicLevel(Math.min(100, (avg / 128) * 100)); - animFrameRef.current = requestAnimationFrame(tick); - }; - tick(); - } catch (err) { - console.error('Mic test failed:', err); - } - }; - - const stopMicTest = useCallback(() => { - if (micStreamRef.current) { - micStreamRef.current.getTracks().forEach(t => t.stop()); - micStreamRef.current = null; - } - if (animFrameRef.current) { - cancelAnimationFrame(animFrameRef.current); - animFrameRef.current = null; - } - setMicTesting(false); - setMicLevel(0); - }, []); - - useEffect(() => { - return () => stopMicTest(); - }, [stopMicTest]); - - const selectStyle = { - width: '100%', backgroundColor: 'var(--bg-tertiary)', border: 'none', - borderRadius: '4px', padding: '10px', color: 'var(--text-normal)', - fontSize: '14px', outline: 'none', boxSizing: 'border-box', - }; - - const labelStyle = { - display: 'block', color: 'var(--header-secondary)', fontSize: '12px', - fontWeight: '700', textTransform: 'uppercase', marginBottom: '8px', - }; - - return ( - <div> - <h2 style={{ color: 'var(--header-primary)', margin: '0 0 20px', fontSize: '20px' }}>Voice & Video</h2> - - <div style={{ display: 'flex', gap: '16px', marginBottom: '24px' }}> - {/* Input Device */} - <div style={{ flex: 1 }}> - <label style={labelStyle}>Input Device</label> - <select - value={selectedInput} - onChange={(e) => setSelectedInput(e.target.value)} - style={selectStyle} - > - <option value="default">Default</option> - {inputDevices.map(d => ( - <option key={d.deviceId} value={d.deviceId}> - {d.label || `Microphone ${d.deviceId.slice(0, 8)}`} - </option> - ))} - </select> - </div> - - {/* Output Device */} - <div style={{ flex: 1 }}> - <label style={labelStyle}>Output Device</label> - <select - value={selectedOutput} - onChange={(e) => setSelectedOutput(e.target.value)} - style={selectStyle} - > - <option value="default">Default</option> - {outputDevices.map(d => ( - <option key={d.deviceId} value={d.deviceId}> - {d.label || `Speaker ${d.deviceId.slice(0, 8)}`} - </option> - ))} - </select> - </div> - </div> - - {/* Input Volume */} - <div style={{ marginBottom: '20px' }}> - <label style={labelStyle}>Input Volume</label> - <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}> - <input - type="range" - min="0" - max="100" - value={inputVolume} - onChange={(e) => setInputVolume(parseInt(e.target.value))} - className="voice-slider" - /> - <span style={{ color: 'var(--text-normal)', fontSize: '14px', minWidth: '36px' }}>{inputVolume}%</span> - </div> - </div> - - {/* Output Volume */} - <div style={{ marginBottom: '24px' }}> - <label style={labelStyle}>Output Volume</label> - <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}> - <input - type="range" - min="0" - max="100" - value={outputVolume} - onChange={(e) => setOutputVolume(parseInt(e.target.value))} - className="voice-slider" - /> - <span style={{ color: 'var(--text-normal)', fontSize: '14px', minWidth: '36px' }}>{outputVolume}%</span> - </div> - </div> - - {/* Mic Test */} - <div style={{ marginBottom: '24px' }}> - <label style={labelStyle}>Mic Test</label> - <div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}> - <button - onClick={micTesting ? stopMicTest : startMicTest} - style={{ - backgroundColor: micTesting ? '#ed4245' : 'var(--brand-experiment)', - color: 'white', border: 'none', borderRadius: '4px', - padding: '8px 16px', cursor: 'pointer', fontSize: '14px', fontWeight: '500', - flexShrink: 0, - }} - > - {micTesting ? 'Stop Testing' : 'Let\'s Check'} - </button> - <div className="mic-level-bar"> - <div className="mic-level-fill" style={{ width: `${micLevel}%` }} /> - </div> - </div> - </div> - - {/* Noise Suppression */} - <div style={{ marginBottom: '24px' }}> - <label style={labelStyle}>Voice Processing</label> - <label style={{ - display: 'flex', alignItems: 'center', gap: '10px', - cursor: 'pointer', color: 'var(--text-normal)', fontSize: '14px', - userSelect: 'none', - }}> - <input - type="checkbox" - checked={localStorage.getItem('voiceNoiseSuppression') !== 'false'} - onChange={(e) => { - localStorage.setItem('voiceNoiseSuppression', String(e.target.checked)); - }} - style={{ accentColor: 'var(--brand-experiment)', width: '18px', height: '18px', cursor: 'pointer' }} - /> - Noise Suppression - <span style={{ color: 'var(--header-secondary)', fontSize: '12px' }}> - — Reduces background noise like keyboard clicks and fans. Takes effect on next voice connect. - </span> - </label> - </div> - </div> - ); -}; - -/* ========================================= - KEYBINDS TAB - ========================================= */ -const KeybindsTab = () => { - const keybinds = [ - { action: 'Quick Switcher', keys: 'Ctrl+K' }, - { action: 'Toggle Mute', keys: 'Ctrl+Shift+M' }, - ]; - - return ( - <div> - <h2 style={{ color: 'var(--header-primary)', margin: '0 0 20px', fontSize: '20px' }}>Keybinds</h2> - - <div style={{ - backgroundColor: 'var(--bg-secondary)', borderRadius: '8px', padding: '16px', - marginBottom: '16px', - }}> - <p style={{ color: 'var(--text-muted)', fontSize: '14px', margin: '0 0 16px' }}> - Keybind configuration coming soon. Current keybinds are shown below. - </p> - - <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}> - {keybinds.map(kb => ( - <div key={kb.action} style={{ - display: 'flex', alignItems: 'center', justifyContent: 'space-between', - padding: '10px 12px', backgroundColor: 'var(--bg-tertiary)', borderRadius: '4px', - }}> - <span style={{ color: 'var(--text-normal)', fontSize: '14px' }}>{kb.action}</span> - <kbd style={{ - backgroundColor: 'var(--bg-primary)', padding: '4px 8px', borderRadius: '4px', - fontSize: '13px', color: 'var(--header-primary)', fontFamily: 'inherit', - border: '1px solid var(--border-subtle)', - }}> - {kb.keys} - </kbd> - </div> - ))} - </div> - </div> - </div> - ); -}; - -/* ========================================= - SEARCH TAB - ========================================= */ -const TAG_HEX_LEN = 32; - -const SearchTab = ({ userId }) => { - const convex = useConvex(); - const { crypto } = usePlatform(); - const searchCtx = useSearch(); - - const [status, setStatus] = useState('idle'); // idle | rebuilding | done | error - const [progress, setProgress] = useState({ currentChannel: '', channelIndex: 0, totalChannels: 0, messagesIndexed: 0 }); - const [errorMsg, setErrorMsg] = useState(''); - const cancelledRef = useRef(false); - - const handleRebuild = async () => { - if (!userId || !crypto || !searchCtx?.isReady) return; - - cancelledRef.current = false; - setStatus('rebuilding'); - setProgress({ currentChannel: '', channelIndex: 0, totalChannels: 0, messagesIndexed: 0 }); - setErrorMsg(''); - - try { - // 1. Gather channels + DMs - const [channels, dmChannels, rawKeys] = await Promise.all([ - convex.query(api.channels.list, {}), - convex.query(api.dms.listDMs, { userId }), - convex.query(api.channelKeys.getKeysForUser, { userId }), - ]); - - // 2. Decrypt channel keys - const privateKey = sessionStorage.getItem('privateKey'); - if (!privateKey) throw new Error('Private key not found in session. Please re-login.'); - - const decryptedKeys = {}; - for (const item of rawKeys) { - try { - const bundleJson = await crypto.privateDecrypt(privateKey, item.encrypted_key_bundle); - Object.assign(decryptedKeys, JSON.parse(bundleJson)); - } catch (e) { - // Skip channels we can't decrypt - } - } - - // 3. Build channel list: text channels + DMs that have keys - const textChannels = channels - .filter(c => c.type === 'text' && decryptedKeys[c._id]) - .map(c => ({ id: c._id, name: '#' + c.name, key: decryptedKeys[c._id] })); - - const dmItems = (dmChannels || []) - .filter(dm => decryptedKeys[dm.channel_id]) - .map(dm => ({ id: dm.channel_id, name: '@' + dm.other_username, key: decryptedKeys[dm.channel_id] })); - - const allChannels = [...textChannels, ...dmItems]; - - if (allChannels.length === 0) { - setStatus('done'); - setProgress(p => ({ ...p, totalChannels: 0 })); - return; - } - - setProgress(p => ({ ...p, totalChannels: allChannels.length })); - - let totalIndexed = 0; - - // 4. For each channel, paginate and decrypt - for (let i = 0; i < allChannels.length; i++) { - if (cancelledRef.current) break; - - const ch = allChannels[i]; - setProgress(p => ({ ...p, currentChannel: ch.name, channelIndex: i + 1 })); - - let cursor = null; - let isDone = false; - - while (!isDone) { - if (cancelledRef.current) break; - - const paginationOpts = { numItems: 100, cursor }; - const result = await convex.query(api.messages.fetchBulkPage, { - channelId: ch.id, - paginationOpts, - }); - - if (result.page.length > 0) { - // Build decrypt batch - const decryptItems = []; - const msgMap = []; - - for (const msg of result.page) { - if (msg.ciphertext && msg.ciphertext.length >= TAG_HEX_LEN) { - const tag = msg.ciphertext.slice(-TAG_HEX_LEN); - const content = msg.ciphertext.slice(0, -TAG_HEX_LEN); - decryptItems.push({ ciphertext: content, key: ch.key, iv: msg.nonce, tag }); - msgMap.push(msg); - } - } - - if (decryptItems.length > 0) { - const decryptResults = await crypto.decryptBatch(decryptItems); - - const indexItems = []; - for (let j = 0; j < decryptResults.length; j++) { - const plaintext = decryptResults[j]; - if (plaintext && plaintext !== '[Decryption Error]') { - indexItems.push({ - id: msgMap[j].id, - channel_id: msgMap[j].channel_id, - sender_id: msgMap[j].sender_id, - username: msgMap[j].username, - content: plaintext, - created_at: msgMap[j].created_at, - pinned: msgMap[j].pinned, - replyToId: msgMap[j].replyToId, - }); - } - } - - if (indexItems.length > 0) { - searchCtx.indexMessages(indexItems); - totalIndexed += indexItems.length; - setProgress(p => ({ ...p, messagesIndexed: totalIndexed })); - } - } - } - - isDone = result.isDone; - cursor = result.continueCursor; - - // Yield to UI between pages - await new Promise(r => setTimeout(r, 10)); - } - } - - // 5. Save - await searchCtx.save(); - setStatus(cancelledRef.current ? 'idle' : 'done'); - setProgress(p => ({ ...p, messagesIndexed: totalIndexed })); - } catch (err) { - console.error('Search index rebuild failed:', err); - setErrorMsg(err.message || 'Unknown error'); - setStatus('error'); - } - }; - - const handleCancel = () => { - cancelledRef.current = true; - }; - - const formatNumber = (n) => n.toLocaleString(); - - return ( - <div> - <h2 style={{ color: 'var(--header-primary)', margin: '0 0 20px', fontSize: '20px' }}>Search</h2> - - <div style={{ backgroundColor: 'var(--bg-secondary)', borderRadius: '8px', padding: '20px' }}> - <h3 style={{ color: 'var(--header-primary)', margin: '0 0 8px', fontSize: '16px', fontWeight: '600' }}> - Search Index - </h3> - <p style={{ color: 'var(--text-muted)', fontSize: '14px', margin: '0 0 16px', lineHeight: '1.4' }}> - Rebuild your local search index by downloading and decrypting all messages from the server. This may take a while for large servers. - </p> - - {status === 'idle' && ( - <button - onClick={handleRebuild} - disabled={!searchCtx?.isReady} - style={{ - backgroundColor: 'var(--brand-experiment)', color: 'white', border: 'none', - borderRadius: '4px', padding: '10px 20px', cursor: searchCtx?.isReady ? 'pointer' : 'not-allowed', - fontSize: '14px', fontWeight: '500', opacity: searchCtx?.isReady ? 1 : 0.5, - }} - > - Rebuild Search Index - </button> - )} - - {status === 'rebuilding' && ( - <div> - {/* Progress bar */} - <div style={{ - backgroundColor: 'var(--bg-tertiary)', borderRadius: '4px', height: '8px', - overflow: 'hidden', marginBottom: '12px', - }}> - <div style={{ - height: '100%', borderRadius: '4px', - backgroundColor: 'var(--brand-experiment)', - width: progress.totalChannels > 0 - ? `${(progress.channelIndex / progress.totalChannels) * 100}%` - : '0%', - transition: 'width 0.3s ease', - }} /> - </div> - - <div style={{ color: 'var(--text-normal)', fontSize: '14px', marginBottom: '4px' }}> - Indexing {progress.currentChannel}... ({progress.channelIndex} of {progress.totalChannels} channels) - </div> - <div style={{ color: 'var(--text-muted)', fontSize: '13px', marginBottom: '12px' }}> - {formatNumber(progress.messagesIndexed)} messages indexed - </div> - - <button - onClick={handleCancel} - style={{ - backgroundColor: 'transparent', color: '#ed4245', border: '1px solid #ed4245', - borderRadius: '4px', padding: '8px 16px', cursor: 'pointer', - fontSize: '14px', fontWeight: '500', - }} - > - Cancel - </button> - </div> - )} - - {status === 'done' && ( - <div> - <div style={{ color: '#3ba55c', fontSize: '14px', marginBottom: '12px', fontWeight: '500' }}> - Complete! {formatNumber(progress.messagesIndexed)} messages indexed across {progress.totalChannels} channels. - </div> - <button - onClick={() => setStatus('idle')} - style={{ - backgroundColor: 'var(--brand-experiment)', color: 'white', border: 'none', - borderRadius: '4px', padding: '10px 20px', cursor: 'pointer', - fontSize: '14px', fontWeight: '500', - }} - > - Rebuild Again - </button> - </div> - )} - - {status === 'error' && ( - <div> - <div style={{ color: '#ed4245', fontSize: '14px', marginBottom: '12px' }}> - Error: {errorMsg} - </div> - <button - onClick={() => setStatus('idle')} - style={{ - backgroundColor: 'var(--brand-experiment)', color: 'white', border: 'none', - borderRadius: '4px', padding: '10px 20px', cursor: 'pointer', - fontSize: '14px', fontWeight: '500', - }} - > - Retry - </button> - </div> - )} - </div> - </div> - ); -}; - -export default UserSettings; diff --git a/packages/shared/src/components/VoiceStage.jsx b/packages/shared/src/components/VoiceStage.jsx deleted file mode 100644 index 46601e0..0000000 --- a/packages/shared/src/components/VoiceStage.jsx +++ /dev/null @@ -1,1135 +0,0 @@ -import React, { useState, useEffect, useRef, useCallback } from 'react'; -import { Track, RoomEvent, ConnectionQuality } from 'livekit-client'; -import { useVoice } from '../contexts/VoiceContext'; -import { usePlatform } from '../platform/PlatformProvider'; -import ScreenShareModal from './ScreenShareModal'; -import Avatar from './Avatar'; -import { VideoRenderer, findTrackPubs, useParticipantTrack } from '../utils/streamUtils.jsx'; - -// Icons -import muteIcon from '../assets/icons/mute.svg'; -import mutedIcon from '../assets/icons/muted.svg'; -import cameraIcon from '../assets/icons/camera.svg'; -import screenIcon from '../assets/icons/screen.svg'; -import disconnectIcon from '../assets/icons/disconnect.svg'; -import personalMuteIcon from '../assets/icons/personal_mute.svg'; -import serverMuteIcon from '../assets/icons/server_mute.svg'; -import screenShareStartSound from '../assets/sounds/screenshare_start.mp3'; -import screenShareStopSound from '../assets/sounds/screenshare_stop.mp3'; -import ColoredIcon from './ColoredIcon'; - -const SERVER_MUTE_RED = 'hsl(1.343, 84.81%, 69.02%)'; - -const getInitials = (name) => (name || '?').substring(0, 1).toUpperCase(); - -const getUserColor = (username) => { - const colors = ['#5865F2', '#EDA843', '#3BA55D', '#FAA81A', '#EB459E']; - let hash = 0; - for (let i = 0; i < username.length; i++) { - hash = username.charCodeAt(i) + ((hash << 5) - hash); - } - return colors[Math.abs(hash) % colors.length]; -}; - -// Style constants -const ACTIVE_SPEAKER_SHADOW = 'rgb(67, 162, 90) 0px 0px 0px 2px, rgb(67, 162, 90) 0px 0px 0px 20px inset, rgb(26, 26, 30) 0px 0px 0px 20px inset'; - -const LIVE_BADGE_STYLE = { - backgroundColor: '#ed4245', borderRadius: '4px', padding: '2px 6px', - color: 'white', fontSize: '11px', fontWeight: 'bold', - textTransform: 'uppercase', letterSpacing: '0.5px', -}; -const WATCH_STREAM_BUTTON_STYLE = { - backgroundColor: 'rgba(0,0,0,0.6)', color: 'white', border: 'none', - padding: '10px 20px', borderRadius: '4px', fontWeight: 'bold', - fontSize: '14px', cursor: 'pointer', -}; -const THUMBNAIL_SIZE = { width: 120, height: 68 }; -const BOTTOM_BAR_HEIGHT = 140; - -const ConnectionQualityIcon = ({ quality }) => { - const getColor = () => { - switch (quality) { - case ConnectionQuality.Excellent: return '#3ba55d'; - case ConnectionQuality.Good: return '#3ba55d'; - case ConnectionQuality.Poor: return '#faa61a'; - case ConnectionQuality.Lost: return '#ed4245'; - default: return '#72767d'; - } - }; - const getBars = () => { - switch (quality) { - case ConnectionQuality.Excellent: return 4; - case ConnectionQuality.Good: return 3; - case ConnectionQuality.Poor: return 2; - case ConnectionQuality.Lost: return 1; - default: return 0; - } - }; - const color = getColor(); - const bars = getBars(); - return ( - <svg width="16" height="14" viewBox="0 0 16 14" style={{ flexShrink: 0 }}> - {[0, 1, 2, 3].map(i => ( - <rect - key={i} - x={i * 4} - y={10 - i * 3} - width="3" - height={4 + i * 3} - rx="0.5" - fill={i < bars ? color : 'rgba(255,255,255,0.2)'} - /> - ))} - </svg> - ); -}; - -// --- Components --- - -const ParticipantTile = ({ participant, username, avatarUrl }) => { - const cameraTrack = useParticipantTrack(participant, 'camera'); - const { isPersonallyMuted, voiceStates, connectionQualities, activeSpeakers } = useVoice(); - const isMicEnabled = participant.isMicrophoneEnabled; - const isPersonalMuted = isPersonallyMuted(participant.identity); - const displayName = username || participant.identity; - - // Look up server mute from voiceStates - let isServerMutedUser = false; - for (const users of Object.values(voiceStates)) { - const u = users.find(u => u.userId === participant.identity); - if (u) { isServerMutedUser = !!u.isServerMuted; break; } - } - - return ( - <div style={{ - backgroundColor: '#202225', - borderRadius: '8px', - overflow: 'hidden', - position: 'relative', - display: 'flex', - width: '100%', - height: '100%', - aspectRatio: '16/9', - boxShadow: activeSpeakers.has(participant.identity) ? ACTIVE_SPEAKER_SHADOW : 'none', - transition: 'box-shadow 0.15s ease', - }}> - {cameraTrack ? ( - <VideoRenderer track={cameraTrack} /> - ) : ( - <div style={{ - width: '100%', - height: '100%', - backgroundColor: '#2f3136', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'column' - }}> - <Avatar - username={displayName} - avatarUrl={avatarUrl} - size={80} - style={{ boxShadow: '0 4px 10px rgba(0,0,0,0.3)' }} - /> - </div> - )} - - <div style={{ - position: 'absolute', - bottom: '10px', - left: '10px', - backgroundColor: 'rgba(0, 0, 0, 0.6)', - padding: '4px 8px', - borderRadius: '4px', - color: 'white', - fontSize: '14px', - display: 'flex', - alignItems: 'center', - gap: '6px' - }}> - {isServerMutedUser ? ( - <ColoredIcon src={serverMuteIcon} color={SERVER_MUTE_RED} size="16px" /> - ) : isPersonalMuted ? ( - <ColoredIcon src={personalMuteIcon} color="white" size="16px" /> - ) : isMicEnabled ? '\u{1F3A4}' : '\u{1F507}'} - {displayName} - <ConnectionQualityIcon quality={connectionQualities[participant.identity]} /> - </div> - </div> - ); -}; - -const StreamPreviewTile = ({ participant, username, onWatchStream }) => { - const displayName = username || participant.identity; - const [hover, setHover] = useState(false); - - return ( - <div - style={{ - backgroundColor: '#202225', - borderRadius: '8px', - overflow: 'hidden', - position: 'relative', - display: 'flex', - width: '100%', - height: '100%', - aspectRatio: '16/9', - cursor: 'pointer', - }} - onClick={onWatchStream} - onMouseEnter={() => setHover(true)} - onMouseLeave={() => setHover(false)} - > - {/* Static preview — no video subscription */} - <div style={{ - width: '100%', height: '100%', backgroundColor: '#2f3136', - display: 'flex', alignItems: 'center', justifyContent: 'center', - }}> - <ColoredIcon src={screenIcon} color="#72767d" size="48px" /> - </div> - - {/* Overlay */} - <div style={{ - position: 'absolute', inset: 0, - backgroundColor: hover ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.5)', - display: 'flex', alignItems: 'center', justifyContent: 'center', - flexDirection: 'column', gap: '8px', - transition: 'background-color 0.15s', - }}> - <button - style={{ - ...WATCH_STREAM_BUTTON_STYLE, - transform: hover ? 'scale(1.05)' : 'scale(1)', - transition: 'transform 0.15s', - }} - onClick={(e) => { e.stopPropagation(); onWatchStream(); }} - > - Watch Stream - </button> - </div> - - {/* Bottom label */} - <div style={{ - position: 'absolute', bottom: '10px', left: '10px', - display: 'flex', alignItems: 'center', gap: '6px', zIndex: 2, - }}> - <div style={{ - backgroundColor: 'rgba(0,0,0,0.6)', padding: '4px 8px', - borderRadius: '4px', color: 'white', fontSize: '14px', - display: 'flex', alignItems: 'center', gap: '6px', - }}> - {displayName} - <span style={LIVE_BADGE_STYLE}>LIVE</span> - </div> - </div> - </div> - ); -}; - -const ParticipantThumbnail = ({ participant, username, avatarUrl, isStreamer, isMuted }) => { - const cameraTrack = useParticipantTrack(participant, 'camera'); - const { isPersonallyMuted, voiceStates } = useVoice(); - const isPersonalMuted = isPersonallyMuted(participant.identity); - const displayName = username || participant.identity; - - // Look up server mute from voiceStates - let isServerMutedUser = false; - for (const users of Object.values(voiceStates)) { - const u = users.find(u => u.userId === participant.identity); - if (u) { isServerMutedUser = !!u.isServerMuted; break; } - } - - return ( - <div style={{ - width: THUMBNAIL_SIZE.width, - height: THUMBNAIL_SIZE.height, - borderRadius: '6px', - overflow: 'hidden', - position: 'relative', - backgroundColor: '#2f3136', - flexShrink: 0, - }}> - {cameraTrack ? ( - <VideoRenderer track={cameraTrack} style={{ objectFit: 'cover' }} /> - ) : ( - <div style={{ - width: '100%', height: '100%', - display: 'flex', alignItems: 'center', justifyContent: 'center', - }}> - <Avatar username={displayName} avatarUrl={avatarUrl} size={32} /> - </div> - )} - - {/* Bottom label */} - <div style={{ - position: 'absolute', bottom: '2px', left: '2px', right: '2px', - display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '3px', - }}> - <span style={{ - backgroundColor: 'rgba(0,0,0,0.7)', padding: '1px 4px', - borderRadius: '3px', color: 'white', fontSize: '10px', - maxWidth: '100%', overflow: 'hidden', textOverflow: 'ellipsis', - whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', gap: '3px', - }}> - {isServerMutedUser ? ( - <ColoredIcon src={serverMuteIcon} color={SERVER_MUTE_RED} size="12px" /> - ) : isPersonalMuted ? ( - <ColoredIcon src={personalMuteIcon} color="white" size="12px" /> - ) : isMuted ? ( - <span style={{ fontSize: '9px' }}>{'\u{1F507}'}</span> - ) : null} - {displayName} - {isStreamer && <span style={{ ...LIVE_BADGE_STYLE, fontSize: '8px', padding: '1px 3px' }}>LIVE</span>} - </span> - </div> - </div> - ); -}; - -// Inline SVG icons for volume control -const SpeakerIcon = ({ volume, muted }) => { - if (muted || volume === 0) { - return ( - <svg width="20" height="20" viewBox="0 0 24 24" fill="white"> - <path d="M11 5L6 9H2v6h4l5 4V5z" /> - <line x1="23" y1="9" x2="17" y2="15" stroke="white" strokeWidth="2" strokeLinecap="round" /> - <line x1="17" y1="9" x2="23" y2="15" stroke="white" strokeWidth="2" strokeLinecap="round" /> - </svg> - ); - } - if (volume < 50) { - return ( - <svg width="20" height="20" viewBox="0 0 24 24" fill="white"> - <path d="M11 5L6 9H2v6h4l5 4V5z" /> - <path d="M15.54 8.46a5 5 0 010 7.07" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" /> - </svg> - ); - } - return ( - <svg width="20" height="20" viewBox="0 0 24 24" fill="white"> - <path d="M11 5L6 9H2v6h4l5 4V5z" /> - <path d="M15.54 8.46a5 5 0 010 7.07" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" /> - <path d="M19.07 4.93a10 10 0 010 14.14" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" /> - </svg> - ); -}; - -// Inline SVG icons for fullscreen -const ExpandIcon = () => ( - <svg width="18" height="18" viewBox="0 0 24 24" fill="white"> - <path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" /> - </svg> -); - -const CompressIcon = () => ( - <svg width="18" height="18" viewBox="0 0 24 24" fill="white"> - <path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z" /> - </svg> -); - -const FocusedStreamView = ({ - streamParticipant, - streamerUsername, - allParticipants, - getUsername, - getAvatarUrl, - participantsCollapsed, - onToggleCollapse, - onStopWatching, - streamingIdentities, - voiceUsers, - isTabVisible, - isFullscreen, - onToggleFullscreen, - localIdentity, -}) => { - const screenTrack = useParticipantTrack(streamParticipant, 'screenshare'); - const [barHover, setBarHover] = useState(false); - const [bottomEdgeHover, setBottomEdgeHover] = useState(false); - - // Volume control state - const { getUserVolume, setUserVolume, togglePersonalMute, isPersonallyMuted } = useVoice(); - const streamerId = streamParticipant.identity; - const isSelf = streamerId === localIdentity; - const isMutedByMe = isPersonallyMuted(streamerId); - const userVolume = getUserVolume(streamerId); - const [volumeExpanded, setVolumeExpanded] = useState(false); - const volumeHideTimeout = useRef(null); - - const handleVolumeMouseEnter = () => { - if (volumeHideTimeout.current) clearTimeout(volumeHideTimeout.current); - setVolumeExpanded(true); - }; - const handleVolumeMouseLeave = () => { - volumeHideTimeout.current = setTimeout(() => setVolumeExpanded(false), 1500); - }; - useEffect(() => () => { if (volumeHideTimeout.current) clearTimeout(volumeHideTimeout.current); }, []); - - const sliderPercent = (userVolume / 200) * 100; - - // Auto-exit if stream track disappears - useEffect(() => { - if (!streamParticipant) { - onStopWatching(); - return; - } - - const checkTrack = () => { - const { screenSharePub } = findTrackPubs(streamParticipant); - if (!screenSharePub || !screenSharePub.track) { - onStopWatching(); - } - }; - - // Give a brief grace period for track to appear - const timeout = setTimeout(checkTrack, 3000); - - streamParticipant.on(RoomEvent.TrackUnpublished, checkTrack); - streamParticipant.on('localTrackUnpublished', checkTrack); - - return () => { - clearTimeout(timeout); - streamParticipant.off(RoomEvent.TrackUnpublished, checkTrack); - streamParticipant.off('localTrackUnpublished', checkTrack); - }; - }, [streamParticipant, onStopWatching]); - - return ( - <div style={{ display: 'flex', flexDirection: 'column', flex: 1, position: 'relative' }}> - {/* Stream area */} - <div style={{ - flex: 1, - position: 'relative', - backgroundColor: 'black', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - overflow: 'hidden', - }}> - {screenTrack && isTabVisible ? ( - <VideoRenderer track={screenTrack} style={{ objectFit: 'contain' }} /> - ) : screenTrack && !isTabVisible ? ( - <div style={{ - display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px', - color: '#72767d', - }}> - <ColoredIcon src={screenIcon} color="#72767d" size="48px" /> - <span style={{ fontSize: '14px' }}>Stream paused</span> - </div> - ) : ( - <div style={{ - display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px', - color: '#72767d', - }}> - <div style={{ - width: '48px', height: '48px', borderRadius: '50%', - border: '3px solid #72767d', display: 'flex', - alignItems: 'center', justifyContent: 'center', - animation: 'spin 1s linear infinite', - }}> - <div style={{ width: '6px', height: '6px', borderRadius: '50%', backgroundColor: '#72767d' }} /> - </div> - <span style={{ fontSize: '14px' }}>Loading stream...</span> - </div> - )} - - {/* Top-left: streamer info */} - <div style={{ - position: 'absolute', top: '12px', left: '12px', - display: 'flex', alignItems: 'center', gap: '8px', - backgroundColor: 'rgba(0,0,0,0.6)', padding: '6px 10px', - borderRadius: '6px', - }}> - <span style={{ color: 'white', fontSize: '14px', fontWeight: '500' }}> - {streamerUsername} - </span> - <span style={LIVE_BADGE_STYLE}>LIVE</span> - </div> - - {/* Top-right: button group (fullscreen + close) */} - <div style={{ - position: 'absolute', top: '12px', right: '12px', - display: 'flex', gap: '8px', - }}> - <button - onClick={onToggleFullscreen} - title={isFullscreen ? "Exit Fullscreen" : "Fullscreen"} - style={{ - width: '32px', height: '32px', borderRadius: '50%', - backgroundColor: 'rgba(0,0,0,0.6)', border: 'none', - color: 'white', cursor: 'pointer', - display: 'flex', alignItems: 'center', justifyContent: 'center', - transition: 'background-color 0.15s', - }} - onMouseEnter={e => e.currentTarget.style.backgroundColor = 'rgba(0,0,0,0.8)'} - onMouseLeave={e => e.currentTarget.style.backgroundColor = 'rgba(0,0,0,0.6)'} - > - {isFullscreen ? <CompressIcon /> : <ExpandIcon />} - </button> - <button - onClick={onStopWatching} - title="Stop Watching" - style={{ - width: '32px', height: '32px', borderRadius: '50%', - backgroundColor: 'rgba(0,0,0,0.6)', border: 'none', - color: 'white', fontSize: '18px', cursor: 'pointer', - display: 'flex', alignItems: 'center', justifyContent: 'center', - transition: 'background-color 0.15s', - }} - onMouseEnter={e => e.currentTarget.style.backgroundColor = 'rgba(0,0,0,0.8)'} - onMouseLeave={e => e.currentTarget.style.backgroundColor = 'rgba(0,0,0,0.6)'} - > - ✕ - </button> - </div> - - {/* Bottom-left: volume control (hidden when watching own stream) */} - {!isSelf && ( - <div - onMouseEnter={handleVolumeMouseEnter} - onMouseLeave={handleVolumeMouseLeave} - style={{ - position: 'absolute', bottom: '12px', left: '12px', - display: 'flex', alignItems: 'center', gap: '8px', - backgroundColor: 'rgba(0,0,0,0.6)', - padding: '6px 10px', - borderRadius: '6px', - transition: 'width 0.2s ease', - }} - > - <button - onClick={() => togglePersonalMute(streamerId)} - title={isMutedByMe ? "Unmute" : "Mute"} - style={{ - background: 'none', border: 'none', cursor: 'pointer', - padding: 0, display: 'flex', alignItems: 'center', - opacity: isMutedByMe ? 0.6 : 1, - }} - > - <SpeakerIcon volume={userVolume} muted={isMutedByMe} /> - </button> - {volumeExpanded && ( - <> - <input - type="range" - min="0" - max="200" - value={isMutedByMe ? 0 : userVolume} - onChange={(e) => setUserVolume(streamerId, Number(e.target.value))} - onMouseDown={(e) => e.stopPropagation()} - className="context-menu-volume-slider" - style={{ - width: '100px', - background: `linear-gradient(to right, hsl(235, 86%, 65%) ${isMutedByMe ? 0 : sliderPercent}%, rgba(255,255,255,0.2) ${isMutedByMe ? 0 : sliderPercent}%)`, - }} - /> - <span style={{ color: 'white', fontSize: '12px', minWidth: '32px', textAlign: 'right' }}> - {isMutedByMe ? 0 : userVolume}% - </span> - </> - )} - </div> - )} - </div> - - {/* Bottom participants bar */} - <div - style={{ position: 'relative' }} - onMouseEnter={() => setBarHover(true)} - onMouseLeave={() => setBarHover(false)} - > - {/* Collapse/expand toggle */} - {!participantsCollapsed && barHover && ( - <button - onClick={onToggleCollapse} - title="Collapse participants" - style={{ - position: 'absolute', top: '-16px', left: '50%', - transform: 'translateX(-50%)', zIndex: 10, - width: '32px', height: '16px', borderRadius: '8px 8px 0 0', - backgroundColor: 'rgba(47,49,54,0.9)', border: 'none', - color: '#b9bbbe', fontSize: '12px', cursor: 'pointer', - display: 'flex', alignItems: 'center', justifyContent: 'center', - }} - > - ▼ - </button> - )} - - <div style={{ - height: participantsCollapsed ? 0 : BOTTOM_BAR_HEIGHT, - overflow: 'hidden', - transition: 'height 0.25s ease', - }}> - <div style={{ - height: BOTTOM_BAR_HEIGHT, - display: 'flex', - alignItems: 'center', - padding: '0 16px', - gap: '8px', - overflowX: 'auto', - overflowY: 'hidden', - }}> - {allParticipants.map(p => { - const uname = getUsername(p.identity); - const user = voiceUsers?.find(u => u.userId === p.identity); - return ( - <ParticipantThumbnail - key={p.identity} - participant={p} - username={uname} - avatarUrl={getAvatarUrl(p.identity)} - isStreamer={streamingIdentities.has(p.identity)} - isMuted={user ? user.isMuted : !p.isMicrophoneEnabled} - /> - ); - })} - </div> - </div> - </div> - - {/* Expand trigger when collapsed */} - {participantsCollapsed && ( - <div - onMouseEnter={() => setBottomEdgeHover(true)} - onMouseLeave={() => setBottomEdgeHover(false)} - style={{ - position: 'absolute', bottom: 0, left: 0, right: 0, - height: '24px', zIndex: 10, - display: 'flex', alignItems: 'flex-end', justifyContent: 'center', - }} - > - {bottomEdgeHover && ( - <button - onClick={onToggleCollapse} - title="Show participants" - style={{ - width: '32px', height: '16px', borderRadius: '8px 8px 0 0', - backgroundColor: 'rgba(47,49,54,0.9)', border: 'none', - color: '#b9bbbe', fontSize: '12px', cursor: 'pointer', - display: 'flex', alignItems: 'center', justifyContent: 'center', - marginBottom: '0', - }} - > - ▲ - </button> - )} - </div> - )} - </div> - ); -}; - -const PreviewParticipantTile = ({ username, displayName, avatarUrl }) => { - const name = displayName || username; - return ( - <div style={{ - width: '88px', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - gap: '8px', - }}> - <Avatar username={name} avatarUrl={avatarUrl} size={56} /> - <span style={{ - color: '#b9bbbe', - fontSize: '12px', - maxWidth: '88px', - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - textAlign: 'center', - }}> - {name} - </span> - </div> - ); -}; - -// --- Main Component --- - -const VoiceStage = ({ room, channelId, voiceStates, channelName }) => { - const [participants, setParticipants] = useState([]); - const { isMuted, toggleMute, disconnectVoice, setScreenSharing, connectToVoice, watchingStreamOf, setWatchingStreamOf, isReceivingScreenShareAudio, isReconnecting } = useVoice(); - const { features } = usePlatform(); - const [isScreenShareModalOpen, setIsScreenShareModalOpen] = useState(false); - const [isScreenShareActive, setIsScreenShareActive] = useState(false); - const screenShareAudioTrackRef = useRef(null); - - const [participantsCollapsed, setParticipantsCollapsed] = useState(false); - - // Fullscreen support - const stageContainerRef = useRef(null); - const [isFullscreen, setIsFullscreen] = useState(false); - - useEffect(() => { - const handleFullscreenChange = () => { - setIsFullscreen(!!document.fullscreenElement); - }; - document.addEventListener('fullscreenchange', handleFullscreenChange); - return () => document.removeEventListener('fullscreenchange', handleFullscreenChange); - }, []); - - const toggleFullscreen = useCallback(() => { - if (!stageContainerRef.current) return; - if (document.fullscreenElement) { - document.exitFullscreen().catch(console.error); - } else { - stageContainerRef.current.requestFullscreen().catch(console.error); - } - }, []); - - const isReceivingScreenShareAudioRef = useRef(false); - useEffect(() => { isReceivingScreenShareAudioRef.current = isReceivingScreenShareAudio; }, [isReceivingScreenShareAudio]); - - useEffect(() => { - if (!room) return; - - const updateParticipants = () => { - const remote = Array.from(room.remoteParticipants.values()); - const local = [room.localParticipant]; - setParticipants([...local, ...remote]); - setIsScreenShareActive(room.localParticipant.isScreenShareEnabled); - }; - - updateParticipants(); - - room.on(RoomEvent.ParticipantConnected, updateParticipants); - room.on(RoomEvent.ParticipantDisconnected, updateParticipants); - room.localParticipant.on('localTrackPublished', updateParticipants); - room.localParticipant.on('localTrackUnpublished', (pub) => { - if ((pub.source === Track.Source.ScreenShare || pub.source === 'screen_share') && !isReceivingScreenShareAudioRef.current) { - new Audio(screenShareStopSound).play(); - } - updateParticipants(); - }); - - return () => { - room.off(RoomEvent.ParticipantConnected, updateParticipants); - room.off(RoomEvent.ParticipantDisconnected, updateParticipants); - room.localParticipant.off('localTrackPublished', updateParticipants); - room.localParticipant.off('localTrackUnpublished', updateParticipants); - }; - }, [room]); - - // Reset collapsed state and exit fullscreen when room disconnects - useEffect(() => { - if (!room) { - setParticipantsCollapsed(false); - if (document.fullscreenElement) document.exitFullscreen().catch(console.error); - } - }, [room]); - - // Derive streaming identities from voiceStates - const voiceUsers = voiceStates?.[channelId] || []; - const streamingIdentities = new Set( - voiceUsers.filter(u => u.isScreenSharing).map(u => u.userId) - ); - - const handleStopWatching = useCallback(() => { - if (document.fullscreenElement) document.exitFullscreen().catch(console.error); - setWatchingStreamOf(null); - setParticipantsCollapsed(false); - }, []); - - // Screen Share Handler - const handleScreenShareSelect = async (selection) => { - if (!room) return; - try { - if (room.localParticipant.isScreenShareEnabled) { - await room.localParticipant.setScreenShareEnabled(false); - } - let stream; - if (selection.type === 'web_stream') { - // Web fallback: stream already obtained via getDisplayMedia - stream = selection.stream; - } else if (selection.type === 'device') { - stream = await navigator.mediaDevices.getUserMedia({ - video: { deviceId: { exact: selection.deviceId } }, - audio: false - }); - } else { - // Try with audio if requested, fall back to video-only if it fails - const audioConstraint = selection.shareAudio ? { - mandatory: { - chromeMediaSource: 'desktop', - chromeMediaSourceId: selection.sourceId - } - } : false; - try { - stream = await navigator.mediaDevices.getUserMedia({ - audio: audioConstraint, - video: { - mandatory: { - chromeMediaSource: 'desktop', - chromeMediaSourceId: selection.sourceId, - minWidth: 1280, - maxWidth: 1920, - minHeight: 720, - maxHeight: 1080, - maxFrameRate: 60 - } - } - }); - } catch (audioErr) { - // Audio capture failed (e.g. macOS/Linux) — retry video-only - if (selection.shareAudio) { - console.warn("Audio capture failed, falling back to video-only:", audioErr.message); - stream = await navigator.mediaDevices.getUserMedia({ - audio: false, - video: { - mandatory: { - chromeMediaSource: 'desktop', - chromeMediaSourceId: selection.sourceId, - minWidth: 1280, - maxWidth: 1920, - minHeight: 720, - maxHeight: 1080, - maxFrameRate: 60 - } - } - }); - } else { - throw audioErr; - } - } - } - const track = stream.getVideoTracks()[0]; - if (track) { - if (!isReceivingScreenShareAudio) new Audio(screenShareStartSound).play(); - await room.localParticipant.publishTrack(track, { - name: 'screen_share', - source: Track.Source.ScreenShare - }); - - // Publish audio track if present (system audio from desktop capture) - const audioTrack = stream.getAudioTracks()[0]; - if (audioTrack) { - await room.localParticipant.publishTrack(audioTrack, { - name: 'screen_share_audio', - source: Track.Source.ScreenShareAudio - }); - screenShareAudioTrackRef.current = audioTrack; - } - - setScreenSharing(true); - - track.onended = () => { - // Clean up audio track when video track ends - if (screenShareAudioTrackRef.current) { - screenShareAudioTrackRef.current.stop(); - room.localParticipant.unpublishTrack(screenShareAudioTrackRef.current); - screenShareAudioTrackRef.current = null; - } - setScreenSharing(false); - room.localParticipant.setScreenShareEnabled(false).catch(console.error); - }; - } - } catch (err) { - console.error("Error sharing screen:", err); - alert("Failed to share screen: " + err.message); - } - }; - - const handleScreenShareClick = () => { - if (isScreenShareActive) { - // Clean up audio track before stopping screen share - if (screenShareAudioTrackRef.current) { - screenShareAudioTrackRef.current.stop(); - room.localParticipant.unpublishTrack(screenShareAudioTrackRef.current); - screenShareAudioTrackRef.current = null; - } - room.localParticipant.setScreenShareEnabled(false); - setScreenSharing(false); - } else { - setIsScreenShareModalOpen(true); - } - }; - - const getUsername = (identity) => { - const user = voiceUsers.find(u => u.userId === identity); - return user ? (user.displayName || user.username) : identity; - }; - - const getAvatarUrl = (identity) => { - const user = voiceUsers.find(u => u.userId === identity); - return user?.avatarUrl || null; - }; - - // Pause local stream preview when tab is not visible to save CPU/GPU - const [isTabVisible, setIsTabVisible] = useState(!document.hidden); - useEffect(() => { - const handler = () => setIsTabVisible(!document.hidden); - document.addEventListener('visibilitychange', handler); - return () => document.removeEventListener('visibilitychange', handler); - }, []); - - // F key shortcut to toggle fullscreen when watching a stream - useEffect(() => { - if (!watchingStreamOf) return; - const handleKeyDown = (e) => { - if (e.key === 'f' || e.key === 'F') { - const tag = e.target.tagName; - if (tag === 'INPUT' || tag === 'TEXTAREA' || e.target.isContentEditable) return; - e.preventDefault(); - toggleFullscreen(); - } - }; - document.addEventListener('keydown', handleKeyDown); - return () => document.removeEventListener('keydown', handleKeyDown); - }, [watchingStreamOf, toggleFullscreen]); - - if (!room) { - return ( - <div style={{ - flex: 1, - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - backgroundColor: '#1a1b1e', - width: '100%', - height: '100%', - position: 'relative', - overflow: 'hidden' - }}> - <div style={{ - position: 'absolute', - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - width: '100%', - height: '100%', - background: 'radial-gradient(circle at center, #2f3136 0%, #000000 100%)', - opacity: 0.8, - zIndex: 0 - }} /> - - <div style={{ zIndex: 1, textAlign: 'center', display: 'flex', flexDirection: 'column', alignItems: 'center' }}> - <h2 style={{ - color: 'white', - fontSize: '24px', - fontWeight: 'bold', - marginBottom: '8px' - }}> - {channelName || 'Voice Channel'} - </h2> - <p style={{ - color: '#b9bbbe', - fontSize: '14px', - marginBottom: '24px' - }}> - {voiceUsers.length === 0 - ? 'No one is currently in voice' - : voiceUsers.length === 1 - ? '1 person is in voice' - : `${voiceUsers.length} people are in voice`} - </p> - {voiceUsers.length > 0 && ( - <div style={{ - display: 'flex', - flexWrap: 'wrap', - justifyContent: 'center', - gap: '16px', - maxWidth: '440px', - marginBottom: '24px', - }}> - {voiceUsers.map(u => ( - <PreviewParticipantTile - key={u.userId} - username={u.username} - displayName={u.displayName} - avatarUrl={u.avatarUrl} - /> - ))} - </div> - )} - <button - onClick={() => connectToVoice(channelId, channelName, localStorage.getItem('userId'))} - style={{ - backgroundColor: 'white', - color: 'black', - border: 'none', - borderRadius: '24px', - padding: '10px 24px', - fontSize: '14px', - fontWeight: '600', - cursor: 'pointer', - transition: 'transform 0.1s', - boxShadow: '0 2px 4px rgba(0,0,0,0.2)' - }} - onMouseEnter={e => e.target.style.transform = 'scale(1.05)'} - onMouseLeave={e => e.target.style.transform = 'scale(1)'} - > - Join Voice - </button> - </div> - </div> - ); - } - - const isCameraOn = room.localParticipant.isCameraEnabled; - const isScreenShareOn = isScreenShareActive; - - // Find the participant being watched - const watchedParticipant = watchingStreamOf - ? participants.find(p => p.identity === watchingStreamOf) - : null; - - return ( - <div ref={stageContainerRef} style={{ display: 'flex', flexDirection: 'column', height: '100%', flex: 1, backgroundColor: 'black', width: '100%' }}> - {watchingStreamOf && watchedParticipant ? ( - /* Focused/Fullscreen View */ - <FocusedStreamView - streamParticipant={watchedParticipant} - streamerUsername={getUsername(watchingStreamOf)} - allParticipants={participants} - getUsername={getUsername} - getAvatarUrl={getAvatarUrl} - participantsCollapsed={participantsCollapsed} - onToggleCollapse={() => setParticipantsCollapsed(c => !c)} - onStopWatching={handleStopWatching} - streamingIdentities={streamingIdentities} - voiceUsers={voiceUsers} - isTabVisible={isTabVisible} - isFullscreen={isFullscreen} - onToggleFullscreen={toggleFullscreen} - localIdentity={room.localParticipant.identity} - /> - ) : ( - /* Grid View */ - <div style={{ - flex: 1, - padding: '16px', - display: 'grid', - gridTemplateColumns: 'repeat(auto-fit, minmax(320px, 1fr))', - gap: '16px', - overflowY: 'auto', - alignContent: 'start' - }}> - {participants.map(p => { - const isStreaming = streamingIdentities.has(p.identity); - return ( - <React.Fragment key={p.identity}> - <ParticipantTile - participant={p} - username={getUsername(p.identity)} - avatarUrl={getAvatarUrl(p.identity)} - /> - {isStreaming && ( - <StreamPreviewTile - key={`stream-${p.identity}`} - participant={p} - username={getUsername(p.identity)} - onWatchStream={() => setWatchingStreamOf(p.identity)} - /> - )} - </React.Fragment> - ); - })} - </div> - )} - - {/* Reconnection Banner */} - {isReconnecting && ( - <div style={{ - backgroundColor: '#faa61a', - color: '#000', - textAlign: 'center', - padding: '6px 12px', - fontSize: '13px', - fontWeight: '600', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - gap: '8px', - }}> - <div style={{ - width: '14px', height: '14px', borderRadius: '50%', - border: '2px solid #000', borderTopColor: 'transparent', - animation: 'spin 0.8s linear infinite', - }} /> - Reconnecting... - </div> - )} - - {/* Controls */} - <div style={{ - height: '80px', - backgroundColor: 'black', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - paddingBottom: '20px' - }}> - <div style={{ - display: 'flex', - gap: '16px', - padding: '8px 16px', - backgroundColor: '#2f3136', - borderRadius: '32px' - }}> - <button - onClick={toggleMute} - title={isMuted ? "Unmute" : "Mute"} - style={{ - width: '56px', height: '56px', borderRadius: '50%', - backgroundColor: isMuted ? 'white' : '#202225', - border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' - }} - > - <ColoredIcon src={isMuted ? mutedIcon : muteIcon} color={isMuted ? 'black' : 'white'} size="24px" /> - </button> - - <button - onClick={() => room.localParticipant.setCameraEnabled(!isCameraOn)} - title="Toggle Camera" - style={{ - width: '56px', height: '56px', borderRadius: '50%', - backgroundColor: isCameraOn ? 'white' : '#202225', - border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' - }} - > - <ColoredIcon src={cameraIcon} color={isCameraOn ? 'black' : 'white'} size="24px" /> - </button> - - {features.hasScreenCapture && ( - <button - onClick={handleScreenShareClick} - title="Share Screen" - style={{ - width: '56px', height: '56px', borderRadius: '50%', - backgroundColor: isScreenShareOn ? 'white' : '#202225', - border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' - }} - > - <ColoredIcon src={screenIcon} color={isScreenShareOn ? 'black' : 'white'} size="24px" /> - </button> - )} - - <button - onClick={disconnectVoice} - title="Disconnect" - style={{ - width: '56px', height: '56px', borderRadius: '50%', - backgroundColor: '#ED4245', - border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' - }} - > - <ColoredIcon src={disconnectIcon} color="white" size="24px" /> - </button> - </div> - </div> - - {isScreenShareModalOpen && ( - <ScreenShareModal - onClose={() => setIsScreenShareModalOpen(false)} - onSelectSource={handleScreenShareSelect} - /> - )} - </div> - ); -}; - -export default VoiceStage; diff --git a/packages/shared/src/components/auth/AuthLayout.module.css b/packages/shared/src/components/auth/AuthLayout.module.css new file mode 100644 index 0000000..ce0c40c --- /dev/null +++ b/packages/shared/src/components/auth/AuthLayout.module.css @@ -0,0 +1,165 @@ +.container { + position: relative; + min-height: 100vh; + min-height: 100dvh; + width: 100%; + background-color: var(--brand-primary); +} + +/* Mobile-only logo banner inside the form column. Hidden by + default — only revealed when the desktop side-column is + collapsed in the mobile media query below. */ +.mobileLogo { + display: none; +} + +.pattern { + position: fixed; + inset: 0; + opacity: 0.06; + pointer-events: none; + z-index: 0; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260' viewBox='0 0 260 260'%3E%3Cg fill='%23ffffff' fill-opacity='0.4'%3E%3Ccircle cx='30' cy='30' r='8'/%3E%3Ccircle cx='130' cy='30' r='6'/%3E%3Ccircle cx='230' cy='30' r='8'/%3E%3Ccircle cx='80' cy='80' r='10'/%3E%3Ccircle cx='180' cy='80' r='6'/%3E%3Ccircle cx='30' cy='130' r='6'/%3E%3Ccircle cx='130' cy='130' r='8'/%3E%3Ccircle cx='230' cy='130' r='10'/%3E%3Ccircle cx='80' cy='180' r='6'/%3E%3Ccircle cx='180' cy='180' r='8'/%3E%3Ccircle cx='30' cy='230' r='10'/%3E%3Ccircle cx='130' cy='230' r='6'/%3E%3Ccircle cx='230' cy='230' r='8'/%3E%3C/g%3E%3C/svg%3E"); + background-repeat: repeat; + background-size: 260px 260px; +} + +.cardContainer { + position: relative; + z-index: 10; + display: flex; + min-height: 100vh; + width: 100%; + align-items: center; + justify-content: center; + padding: clamp(2rem, 6vw, 4rem); + box-sizing: border-box; +} + +.card { + display: flex; + height: auto; + min-height: 500px; + width: 100%; + max-width: 56rem; + overflow: hidden; + border-radius: 1rem; + background-color: var(--background-secondary); + box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25); +} + +.logoSide { + display: flex; + width: 33.333%; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 3rem 2rem; + border-right: 1px solid var(--background-modifier-accent); + background-color: var(--background-secondary); +} + +.logoIcon { + width: 6rem; + height: 6rem; + margin-bottom: 1.5rem; + border-radius: 50%; + background-color: var(--brand-primary); + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 2.5rem; + font-weight: 700; +} + +.wordmark { + font-size: 1.75rem; + font-weight: 700; + color: var(--text-primary); + letter-spacing: -0.01em; +} + +.formSide { + display: flex; + width: 66.667%; + flex-direction: column; + justify-content: center; + padding: 3rem; + background: var(--background-secondary); +} + +@media (max-width: 768px) { + /* Strip the purple branded backdrop on mobile — Fluxer's + reference is just a flat dark surface with no card chrome. + Uses `--background-secondary` so the inputs (which use the + darker `--background-tertiary`) visibly recess into the page. */ + .container { + background-color: var(--background-secondary); + } + + .pattern { + display: none; + } + + .cardContainer { + padding: 0; + min-height: 100vh; + min-height: 100dvh; + align-items: stretch; + } + + /* The card collapses into a borderless full-screen surface and + the desktop side column is hidden — its content gets + re-rendered inside the form column via the `.mobileLogo` + banner so the layout becomes a single vertical stack. */ + .card { + flex-direction: column; + min-height: 100vh; + min-height: 100dvh; + max-width: none; + border-radius: 0; + box-shadow: none; + background-color: var(--background-secondary); + } + + .logoSide { + display: none; + } + + .formSide { + width: 100%; + padding: 32px 24px 24px; + background-color: var(--background-secondary); + justify-content: flex-start; + } + + /* Reveal the in-form-column logo banner. */ + .mobileLogo { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + margin: 8px 0 28px; + } + + .mobileLogoIcon { + width: 36px; + height: 36px; + border-radius: 50%; + background-color: var(--brand-primary); + display: flex; + align-items: center; + justify-content: center; + color: #fff; + font-size: 18px; + font-weight: 700; + } + + .mobileWordmark { + font-size: 22px; + font-weight: 800; + color: var(--text-primary); + letter-spacing: -0.01em; + } +} diff --git a/packages/shared/src/components/auth/AuthLayout.tsx b/packages/shared/src/components/auth/AuthLayout.tsx new file mode 100644 index 0000000..32ffe51 --- /dev/null +++ b/packages/shared/src/components/auth/AuthLayout.tsx @@ -0,0 +1,45 @@ +import styles from './AuthLayout.module.css'; + +interface AuthLayoutProps { + children: React.ReactNode; +} + +/** + * AuthLayout — frame for the login / register pages. + * + * Desktop: keeps the existing side-by-side "card" — purple branded + * background, centred dialog with a logo column on the left and + * the form on the right. + * + * Mobile (≤768px): collapses into a full-screen `--background-primary` + * surface with the logo + wordmark stacked at the top of the form + * column, matching the Fluxer mobile reference. The card chrome, + * purple background, and pattern are all hidden via the media + * query in `AuthLayout.module.css`. + */ +export function AuthLayout({ children }: AuthLayoutProps) { + return ( + <div className={styles.container}> + <div className={styles.pattern} /> + <div className={styles.cardContainer}> + <div className={styles.card}> + <div className={styles.logoSide}> + <div className={styles.logoIcon}>B</div> + <span className={styles.wordmark}>Brycord</span> + </div> + <div className={styles.formSide}> + {/* Mobile-only logo banner — appears above the form + when the side column is hidden. The + `data-mobile-logo` attribute is what the CSS + media query targets to switch its display. */} + <div className={styles.mobileLogo} data-mobile-logo> + <div className={styles.mobileLogoIcon}>B</div> + <span className={styles.mobileWordmark}>Brycord</span> + </div> + {children} + </div> + </div> + </div> + </div> + ); +} diff --git a/packages/shared/src/components/auth/InviteAcceptPage.tsx b/packages/shared/src/components/auth/InviteAcceptPage.tsx new file mode 100644 index 0000000..fa10f44 --- /dev/null +++ b/packages/shared/src/components/auth/InviteAcceptPage.tsx @@ -0,0 +1,136 @@ +/** + * InviteAcceptPage — handles `/invite/:code#key=<secret>`. + * + * Pulls the encrypted payload from `api.invites.use`, decrypts it + * with the URL-fragment secret, stashes the decoded channel-key + * map in sessionStorage under `pendingInviteKeys` / `pendingInviteCode`, + * then forwards the user to the register page (or to `/channels/@me` + * if they're already logged in). + * + * The fragment secret never hits the server — browsers don't send + * `#…` to origin servers — so knowledge of it is the capability that + * unlocks the invite payload. + */ +import { useEffect, useState } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { useConvex } from 'convex/react'; +import { api } from '../../../../../convex/_generated/api'; +import { usePlatform } from '../../platform'; +import { AuthLayout } from './AuthLayout'; + +export function InviteAcceptPage() { + const { code } = useParams<{ code: string }>(); + const navigate = useNavigate(); + const convex = useConvex(); + const { crypto } = usePlatform(); + const [status, setStatus] = useState<'working' | 'error'>('working'); + const [error, setError] = useState<string | null>(null); + + useEffect(() => { + let cancelled = false; + + const run = async () => { + if (!code) { + setStatus('error'); + setError('Missing invite code.'); + return; + } + + const hash = + typeof window !== 'undefined' ? window.location.hash : ''; + // Fragment looks like "#key=abcd…" + const match = hash.match(/[#&]key=([^&]+)/); + const secret = match ? match[1] : null; + if (!secret) { + setStatus('error'); + setError( + 'Invite link is missing its secret. Ask the sender for a fresh link.', + ); + return; + } + + try { + const result = await convex.query(api.invites.use, { code }); + if (cancelled) return; + if ('error' in result) { + setStatus('error'); + setError(result.error); + return; + } + + // Older invites used to send a placeholder empty payload. + // Treat that as "no channel keys to import" and continue + // so the user can still register with the code. + let keys: Record<string, string> = {}; + if (result.encryptedPayload) { + try { + const blob = JSON.parse(result.encryptedPayload) as { + c: string; + i: string; + t: string; + }; + const plaintext = await crypto.decryptData( + blob.c, + secret, + blob.i, + blob.t, + ); + keys = JSON.parse(plaintext) as Record<string, string>; + } catch { + setStatus('error'); + setError( + 'Failed to decrypt invite payload. The secret may be wrong or the invite corrupted.', + ); + return; + } + } + + try { + sessionStorage.setItem('pendingInviteCode', code); + sessionStorage.setItem('pendingInviteKeys', JSON.stringify(keys)); + } catch { + /* storage blocked — flow still works if same tab */ + } + + // If the user is already signed in, just take them home — + // the invite keys are already theirs in that case. + const loggedIn = !!sessionStorage.getItem('privateKey'); + if (loggedIn) { + navigate('/channels/@me', { replace: true }); + } else { + navigate('/register', { replace: true }); + } + } catch (err: any) { + if (cancelled) return; + setStatus('error'); + setError(err?.message ?? 'Failed to accept invite.'); + } + }; + + void run(); + return () => { + cancelled = true; + }; + }, [code, convex, crypto, navigate]); + + return ( + <AuthLayout> + <h1 style={{ color: 'var(--text-primary)' }}> + {status === 'working' ? 'Accepting invite…' : 'Invite Error'} + </h1> + {error && ( + <p + style={{ + color: 'var(--status-danger, #ed4245)', + marginTop: 12, + fontSize: 14, + }} + > + {error} + </p> + )} + </AuthLayout> + ); +} + +export default InviteAcceptPage; diff --git a/packages/shared/src/components/auth/LoginPage.module.css b/packages/shared/src/components/auth/LoginPage.module.css new file mode 100644 index 0000000..4624a45 --- /dev/null +++ b/packages/shared/src/components/auth/LoginPage.module.css @@ -0,0 +1,169 @@ +.title { + margin: 0 0 1.5rem; + text-align: center; + font-size: 1.5rem; + font-weight: 700; + color: var(--text-primary); +} + +.form { + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +.field { + display: flex; + flex-direction: column; + gap: 8px; +} + +.label { + font-size: 0.9375rem; + font-weight: 700; + color: var(--text-primary); + letter-spacing: 0; + text-transform: none; +} + +/* Wrapper for inputs that have a trailing icon (eye toggle on + password fields). The `position: relative` lets the icon be + absolutely positioned inside the input area. */ +.inputWrap { + position: relative; +} + +.input { + width: 100%; + height: 48px; + padding: 0 14px; + border-radius: 8px; + border: 1px solid transparent; + background-color: var(--form-surface-background); + color: var(--text-primary); + font-size: 1rem; + font-family: inherit; + outline: none; + box-shadow: none; + transition: border-color 0.15s, background-color 0.15s; + box-sizing: border-box; +} + +.inputWithTrailing { + padding-right: 44px; +} + +.input:focus, +.input:focus-visible { + border-color: var(--brand-primary); + outline: none; +} + +.input::placeholder { + color: var(--text-tertiary-muted, var(--text-tertiary)); +} + +/* Trailing icon button (e.g. password visibility toggle). Sits + inside `.inputWrap` and overlaps the right edge of `.input`. */ +.trailingIconButton { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border: none; + border-radius: 50%; + background: transparent; + color: var(--text-primary-muted, #a0a3a8); + cursor: pointer; + transition: background-color 0.15s, color 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.trailingIconButton:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +.error { + color: hsl(0, calc(80% * var(--saturation-factor)), 70%); + font-size: 0.875rem; + background-color: hsl(0, calc(60% * var(--saturation-factor)), 22%); + padding: 10px 14px; + border-radius: 8px; +} + +.submitButton { + width: 100%; + height: 48px; + padding: 0 1rem; + border-radius: 10px; + border: none; + background-color: var(--brand-primary); + color: #fff; + font-weight: 700; + font-size: 1rem; + font-family: inherit; + cursor: pointer; + transition: filter 120ms ease; + -webkit-tap-highlight-color: transparent; +} + +.submitButton:disabled { + opacity: 0.55; + cursor: not-allowed; +} + +.submitButton:hover:not(:disabled), +.submitButton:active:not(:disabled) { + filter: brightness(0.92); +} + +.divider { + display: flex; + align-items: center; + gap: 1rem; + margin: 0.25rem 0; +} + +.dividerLine { + flex: 1; + border: none; + border-top: 1px solid var(--background-header-secondary); +} + +.dividerText { + font-size: 0.8125rem; + font-weight: 600; + color: var(--text-primary-muted); + letter-spacing: 0.04em; +} + +.footer { + margin-top: 0.5rem; + font-size: 0.9375rem; +} + +.footerText { + color: var(--text-primary-muted); +} + +.footerLink { + color: var(--brand-primary-light); + text-decoration: none; + cursor: pointer; + background: none; + border: none; + padding: 0; + font-family: inherit; + font-weight: 700; + font-size: inherit; +} + +.footerLink:hover { + text-decoration: underline; +} diff --git a/packages/shared/src/components/auth/LoginPage.tsx b/packages/shared/src/components/auth/LoginPage.tsx new file mode 100644 index 0000000..956bd84 --- /dev/null +++ b/packages/shared/src/components/auth/LoginPage.tsx @@ -0,0 +1,158 @@ +import { useState, type FormEvent } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Eye, EyeSlash } from '@phosphor-icons/react'; +import { useConvex } from 'convex/react'; +import { usePlatform } from '../../platform'; +import { useSearch } from '../../contexts/SearchContext'; +import { api } from '../../../../../convex/_generated/api'; +import { AuthLayout } from './AuthLayout'; +import styles from './LoginPage.module.css'; + +export function LoginPage() { + const navigate = useNavigate(); + const convex = useConvex(); + const { crypto, session } = usePlatform(); + const searchCtx = useSearch(); + + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); + const [error, setError] = useState<string | null>(null); + const [loading, setLoading] = useState(false); + + async function decryptEncryptedField(encryptedJson: string, keyHex: string): Promise<string> { + const obj = JSON.parse(encryptedJson); + return crypto.decryptData(obj.content, keyHex, obj.iv, obj.tag); + } + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); + setError(null); + setLoading(true); + + try { + const { salt } = await convex.query(api.auth.getSalt, { username }); + const { dek, dak } = await crypto.deriveAuthKeys(password, salt); + + const searchKeys = await crypto.deriveAuthKeys(password, 'searchdb-' + username); + sessionStorage.setItem('searchDbKey', searchKeys.dak); + + const verifyData = await convex.mutation(api.auth.verifyUser, { username, dak }); + if (verifyData.error) throw new Error(verifyData.error); + + if (verifyData.userId) localStorage.setItem('userId', verifyData.userId); + + const mkHex = await decryptEncryptedField(verifyData.encryptedMK, dek); + sessionStorage.setItem('masterKey', mkHex); + + const encryptedPrivateKeysObj = JSON.parse(verifyData.encryptedPrivateKeys); + const signingKey = await decryptEncryptedField(JSON.stringify(encryptedPrivateKeysObj.ed), mkHex); + const rsaPriv = await decryptEncryptedField(JSON.stringify(encryptedPrivateKeysObj.rsa), mkHex); + + sessionStorage.setItem('signingKey', signingKey); + sessionStorage.setItem('privateKey', rsaPriv); + localStorage.setItem('username', username); + if (verifyData.publicKey) localStorage.setItem('publicKey', verifyData.publicKey); + + if (session) { + try { + await session.save({ + userId: verifyData.userId, + username, + publicKey: verifyData.publicKey || '', + signingKey, + privateKey: rsaPriv, + masterKey: mkHex, + searchDbKey: searchKeys.dak, + savedAt: Date.now(), + }); + } catch (err) { + console.warn('Session persistence unavailable:', err); + } + } + + searchCtx?.initialize(); + navigate('/channels/@me'); + } catch (err: any) { + setError(err?.message ?? 'Login failed'); + } finally { + setLoading(false); + } + }; + + return ( + <AuthLayout> + <h1 className={styles.title}>Welcome back</h1> + + <form onSubmit={handleSubmit} className={styles.form}> + <div className={styles.field}> + <label className={styles.label}>Username</label> + <input + type="text" + className={styles.input} + value={username} + onChange={(e) => setUsername(e.target.value)} + placeholder="Enter your username" + required + autoFocus + disabled={loading} + /> + </div> + + <div className={styles.field}> + <label className={styles.label}>Password</label> + <div className={styles.inputWrap}> + <input + type={showPassword ? 'text' : 'password'} + className={`${styles.input} ${styles.inputWithTrailing}`} + value={password} + onChange={(e) => setPassword(e.target.value)} + placeholder="Enter your password" + required + disabled={loading} + /> + <button + type="button" + className={styles.trailingIconButton} + onClick={() => setShowPassword((v) => !v)} + aria-label={showPassword ? 'Hide password' : 'Show password'} + tabIndex={-1} + > + {showPassword ? <EyeSlash size={18} weight="regular" /> : <Eye size={18} weight="regular" />} + </button> + </div> + </div> + + {error && <div className={styles.error}>{error}</div>} + + <button type="submit" className={styles.submitButton} disabled={loading}> + {loading ? 'Logging in…' : 'Log in'} + </button> + + <div className={styles.divider}> + <hr className={styles.dividerLine} /> + <span className={styles.dividerText}>OR</span> + <hr className={styles.dividerLine} /> + </div> + + <div className={styles.footer}> + <span className={styles.footerText}>Need an account? </span> + <button type="button" className={styles.footerLink} onClick={() => navigate('/register')}> + Register + </button> + </div> + <div style={{ textAlign: 'center', marginTop: 8 }}> + <button + type="button" + className={styles.footerLink} + onClick={() => navigate('/recovery')} + > + Forgot your password? + </button> + </div> + </form> + </AuthLayout> + ); +} + +export default LoginPage; diff --git a/packages/shared/src/components/auth/RegisterPage.tsx b/packages/shared/src/components/auth/RegisterPage.tsx new file mode 100644 index 0000000..fb13156 --- /dev/null +++ b/packages/shared/src/components/auth/RegisterPage.tsx @@ -0,0 +1,500 @@ +import { useEffect, useRef, useState, type FormEvent } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { CheckCircle, Eye, EyeSlash, Warning } from '@phosphor-icons/react'; +import { useConvex, useQuery } from 'convex/react'; +import { usePlatform } from '../../platform'; +import { useSearch } from '../../contexts/SearchContext'; +import { api } from '../../../../../convex/_generated/api'; +import { AuthLayout } from './AuthLayout'; +import styles from './LoginPage.module.css'; + +const MIN_PASSWORD_LENGTH = 8; +const MIN_USERNAME_LENGTH = 2; +const USERNAME_PATTERN = /^[a-zA-Z0-9._-]+$/; + +type InviteState = + | { status: 'idle' } + | { status: 'checking' } + | { + status: 'valid'; + code: string; + // Decoded channel-key map, if the pasted link carried a secret. + // When the user only pastes a bare code, the map stays null and + // the new account just won't have any existing channel keys. + keys: Record<string, string> | null; + } + | { status: 'invalid'; message: string }; + +/** + * Parse the invite-code field. Accepts either a bare code or a full + * invite URL of the form `${origin}/invite/CODE#key=SECRET`. + */ +function parseInviteInput(raw: string): { code: string; secret: string | null } | null { + const trimmed = raw.trim(); + if (!trimmed) return null; + // Full URL (possibly with a fragment) + try { + const url = new URL(trimmed); + const match = url.pathname.match(/\/invite\/([^/]+)/); + if (match) { + const code = match[1]; + const hash = url.hash; // "#key=abcd" + const keyMatch = hash.match(/[#&]key=([^&]+)/); + return { code, secret: keyMatch ? keyMatch[1] : null }; + } + } catch { + /* not a URL — fall through to bare-code path */ + } + // Bare code (treat whitespace-delimited first token as the code) + const code = trimmed.split(/\s+/)[0]; + return { code, secret: null }; +} + +export function RegisterPage() { + const navigate = useNavigate(); + const convex = useConvex(); + const { crypto, session } = usePlatform(); + const searchCtx = useSearch(); + + // Used to detect the first-user bootstrap case — before any user + // exists, the backend allows an empty invite code. This query is + // cheap and public, so it's fine to run during register. + const existingUsers = useQuery(api.auth.getPublicKeys) ?? []; + const isFirstUser = existingUsers.length === 0; + + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [inviteInput, setInviteInput] = useState(''); + const [invite, setInvite] = useState<InviteState>({ status: 'idle' }); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const [error, setError] = useState<string | null>(null); + const [isRegistering, setIsRegistering] = useState(false); + + const debounceRef = useRef<number | null>(null); + + // Pick up an invite already accepted by /invite/:code — if + // InviteAcceptPage stashed keys in sessionStorage, we skip the + // validation UI and mark the invite as already valid. + useEffect(() => { + try { + const pendingCode = sessionStorage.getItem('pendingInviteCode'); + const pendingRaw = sessionStorage.getItem('pendingInviteKeys'); + if (pendingCode) { + setInviteInput(pendingCode); + if (pendingRaw) { + try { + const parsed = JSON.parse(pendingRaw) as Record<string, string>; + setInvite({ status: 'valid', code: pendingCode, keys: parsed }); + return; + } catch { + /* fall through to re-validation */ + } + } + } + } catch { + /* ignore */ + } + }, []); + + // Live validation of whatever the user is typing. A 500 ms debounce + // keeps us from hammering Convex on every keystroke. + useEffect(() => { + if (debounceRef.current !== null) { + window.clearTimeout(debounceRef.current); + debounceRef.current = null; + } + const trimmed = inviteInput.trim(); + if (!trimmed) { + setInvite({ status: 'idle' }); + return; + } + // If this is the code we already validated on mount via + // sessionStorage, don't re-fetch. + if (invite.status === 'valid' && invite.code === trimmed) return; + + setInvite({ status: 'checking' }); + debounceRef.current = window.setTimeout(async () => { + const parsed = parseInviteInput(trimmed); + if (!parsed) { + setInvite({ status: 'invalid', message: 'Empty invite.' }); + return; + } + try { + const result = await convex.query(api.invites.use, { code: parsed.code }); + if ('error' in result) { + setInvite({ status: 'invalid', message: result.error }); + return; + } + + // If a secret was supplied, try to decrypt the payload + // right now so we can (a) verify the secret actually + // works, and (b) stash the decoded key map for the + // submit handler to upload after account creation. + let keys: Record<string, string> | null = null; + if (parsed.secret && result.encryptedPayload) { + try { + const blob = JSON.parse(result.encryptedPayload) as { + c: string; + i: string; + t: string; + }; + const plaintext = await crypto.decryptData( + blob.c, + parsed.secret, + blob.i, + blob.t, + ); + keys = JSON.parse(plaintext) as Record<string, string>; + } catch { + setInvite({ + status: 'invalid', + message: + 'Invite secret does not match. Make sure you pasted the whole link.', + }); + return; + } + } + setInvite({ status: 'valid', code: parsed.code, keys }); + } catch (err: any) { + setInvite({ + status: 'invalid', + message: err?.message ?? 'Unable to verify invite.', + }); + } + }, 500); + + return () => { + if (debounceRef.current !== null) { + window.clearTimeout(debounceRef.current); + } + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [inviteInput, convex, crypto]); + + const canSubmit = + !isRegistering && + (isFirstUser || invite.status === 'valid') && + username.trim().length >= MIN_USERNAME_LENGTH && + password.length >= MIN_PASSWORD_LENGTH && + password === confirmPassword; + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); + setError(null); + + const trimmedUsername = username.trim(); + if (trimmedUsername.length < MIN_USERNAME_LENGTH) { + setError(`Username must be at least ${MIN_USERNAME_LENGTH} characters`); + return; + } + if (!USERNAME_PATTERN.test(trimmedUsername)) { + setError( + 'Username may only contain letters, numbers, dots, underscores, or hyphens', + ); + return; + } + if (password.length < MIN_PASSWORD_LENGTH) { + setError(`Password must be at least ${MIN_PASSWORD_LENGTH} characters`); + return; + } + if (password !== confirmPassword) { + setError('Passwords do not match'); + return; + } + if (!isFirstUser && invite.status !== 'valid') { + setError('You need a valid invite to register.'); + return; + } + + setIsRegistering(true); + + try { + const keys = await crypto.generateKeys(); + const salt = await crypto.randomBytes(16); + const masterKeyHex = await crypto.randomBytes(32); + const { dek, dak } = await crypto.deriveAuthKeys(password, salt); + const hak = await crypto.sha256(dak); + const encryptedMK = JSON.stringify(await crypto.encryptData(masterKeyHex, dek)); + const encryptedEd = JSON.stringify( + await crypto.encryptData(keys.edPriv, masterKeyHex), + ); + const encryptedRsa = JSON.stringify( + await crypto.encryptData(keys.rsaPriv, masterKeyHex), + ); + const encryptedPrivateKeys = JSON.stringify({ + ed: JSON.parse(encryptedEd), + rsa: JSON.parse(encryptedRsa), + }); + + const result = await convex.mutation(api.auth.createUserWithProfile, { + username: trimmedUsername, + salt, + encryptedMK, + hak, + publicKey: keys.rsaPub, + signingKey: keys.edPub, + encryptedPrivateKeys, + inviteCode: invite.status === 'valid' ? invite.code : undefined, + }); + + if ('error' in result) { + throw new Error(result.error); + } + + const searchKeys = await crypto.deriveAuthKeys( + password, + 'searchdb-' + trimmedUsername, + ); + sessionStorage.setItem('searchDbKey', searchKeys.dak); + sessionStorage.setItem('masterKey', masterKeyHex); + sessionStorage.setItem('signingKey', keys.edPriv); + sessionStorage.setItem('privateKey', keys.rsaPriv); + localStorage.setItem('userId', result.userId); + localStorage.setItem('username', trimmedUsername); + localStorage.setItem('publicKey', keys.rsaPub); + + if (session) { + try { + await session.save({ + userId: result.userId, + username: trimmedUsername, + publicKey: keys.rsaPub, + signingKey: keys.edPriv, + privateKey: keys.rsaPriv, + masterKey: masterKeyHex, + searchDbKey: searchKeys.dak, + savedAt: Date.now(), + }); + } catch (err) { + console.warn('Session persistence unavailable:', err); + } + } + + // Upload the decoded invite channel keys. The keys map comes + // from the validated invite state (either from direct input + // or from InviteAcceptPage via sessionStorage). + const inviteKeys = + invite.status === 'valid' ? invite.keys : null; + try { + if (inviteKeys && Object.keys(inviteKeys).length > 0) { + const batch: Array<{ + channelId: string; + userId: string; + encryptedKeyBundle: string; + keyVersion: number; + }> = []; + for (const [channelId, keyHex] of Object.entries(inviteKeys)) { + if (!keyHex) continue; + try { + const payload = JSON.stringify({ [channelId]: keyHex }); + const encryptedKeyBundle = await crypto.publicEncrypt( + keys.rsaPub, + payload, + ); + batch.push({ + channelId, + userId: result.userId, + encryptedKeyBundle, + keyVersion: 1, + }); + } catch (err) { + console.error( + 'Failed to encrypt invite key for channel', + channelId, + err, + ); + } + } + if (batch.length > 0) { + await convex.mutation(api.channelKeys.uploadKeys, { + keys: batch as any, + }); + } + } + } catch (err) { + console.error('Failed to import invite keys:', err); + } finally { + sessionStorage.removeItem('pendingInviteKeys'); + sessionStorage.removeItem('pendingInviteCode'); + } + + searchCtx?.initialize(); + navigate('/channels/@me'); + } catch (err: any) { + setError(err?.message || 'Registration failed'); + } finally { + setIsRegistering(false); + } + }; + + return ( + <AuthLayout> + <h1 className={styles.title}>Create an account</h1> + + <form onSubmit={handleSubmit} className={styles.form}> + <div className={styles.field}> + <label className={styles.label}>Invite Link or Code</label> + <input + type="text" + className={styles.input} + value={inviteInput} + onChange={(e) => setInviteInput(e.target.value)} + placeholder="Paste your invite link or code" + required={!isFirstUser} + disabled={isRegistering} + /> + <div + style={{ + marginTop: 6, + fontSize: 12, + minHeight: 18, + display: 'flex', + alignItems: 'center', + gap: 6, + }} + > + {isFirstUser && invite.status === 'idle' && ( + <span style={{ color: 'var(--text-tertiary)' }}> + No users yet — you'll be the first and become admin. + </span> + )} + {invite.status === 'checking' && ( + <span style={{ color: 'var(--text-tertiary)' }}> + Checking invite… + </span> + )} + {invite.status === 'valid' && ( + <> + <CheckCircle + size={14} + weight="fill" + style={{ color: '#3ba55d' }} + /> + <span style={{ color: '#3ba55d' }}> + Invite valid + {invite.keys && Object.keys(invite.keys).length > 0 + ? ` — ${Object.keys(invite.keys).length} channel key(s) will be imported` + : ''} + </span> + </> + )} + {invite.status === 'invalid' && ( + <> + <Warning + size={14} + weight="fill" + style={{ color: 'var(--status-danger, #ed4245)' }} + /> + <span style={{ color: 'var(--status-danger, #ed4245)' }}> + {invite.message} + </span> + </> + )} + </div> + </div> + + <div className={styles.field}> + <label className={styles.label}>Username</label> + <input + type="text" + className={styles.input} + value={username} + onChange={(e) => setUsername(e.target.value)} + placeholder="Choose a username" + required + disabled={isRegistering} + /> + </div> + + <div className={styles.field}> + <label className={styles.label}>Password</label> + <div className={styles.inputWrap}> + <input + type={showPassword ? 'text' : 'password'} + className={`${styles.input} ${styles.inputWithTrailing}`} + value={password} + onChange={(e) => setPassword(e.target.value)} + placeholder={`At least ${MIN_PASSWORD_LENGTH} characters`} + required + disabled={isRegistering} + /> + <button + type="button" + className={styles.trailingIconButton} + onClick={() => setShowPassword((v) => !v)} + aria-label={showPassword ? 'Hide password' : 'Show password'} + tabIndex={-1} + > + {showPassword ? ( + <EyeSlash size={18} weight="regular" /> + ) : ( + <Eye size={18} weight="regular" /> + )} + </button> + </div> + </div> + + <div className={styles.field}> + <label className={styles.label}>Confirm Password</label> + <div className={styles.inputWrap}> + <input + type={showConfirmPassword ? 'text' : 'password'} + className={`${styles.input} ${styles.inputWithTrailing}`} + value={confirmPassword} + onChange={(e) => setConfirmPassword(e.target.value)} + placeholder="Confirm your password" + required + disabled={isRegistering} + /> + <button + type="button" + className={styles.trailingIconButton} + onClick={() => setShowConfirmPassword((v) => !v)} + aria-label={ + showConfirmPassword ? 'Hide password' : 'Show password' + } + tabIndex={-1} + > + {showConfirmPassword ? ( + <EyeSlash size={18} weight="regular" /> + ) : ( + <Eye size={18} weight="regular" /> + )} + </button> + </div> + </div> + + {error && <div className={styles.error}>{error}</div>} + + <button + type="submit" + className={styles.submitButton} + disabled={!canSubmit} + > + {isRegistering ? 'Registering…' : 'Register'} + </button> + + <div className={styles.divider}> + <hr className={styles.dividerLine} /> + <span className={styles.dividerText}>OR</span> + <hr className={styles.dividerLine} /> + </div> + + <div className={styles.footer}> + <span className={styles.footerText}>Already have an account? </span> + <button + type="button" + className={styles.footerLink} + onClick={() => navigate('/login')} + > + Log in + </button> + </div> + </form> + </AuthLayout> + ); +} + +export default RegisterPage; diff --git a/packages/shared/src/components/auth/SetupEncryptionPage.module.css b/packages/shared/src/components/auth/SetupEncryptionPage.module.css new file mode 100644 index 0000000..e0376d2 --- /dev/null +++ b/packages/shared/src/components/auth/SetupEncryptionPage.module.css @@ -0,0 +1,199 @@ +.header { + text-align: center; + margin-bottom: 24px; +} + +.icon { + font-size: 2.5rem; + margin-bottom: 12px; +} + +.title { + font-size: 1.5rem; + font-weight: 700; + color: var(--text-primary); + margin: 0 0 8px; +} + +.subtitle { + font-size: 0.9375rem; + color: var(--text-secondary); + margin: 0; + line-height: 1.4; +} + +.content { + display: flex; + flex-direction: column; + gap: 16px; +} + +.field { + display: flex; + flex-direction: column; + gap: 8px; +} + +.label { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + color: var(--text-secondary); + letter-spacing: 0.02em; +} + +.recoveryKeyInput { + padding: 12px; + border-radius: var(--radius-md); + border: none; + background-color: var(--background-tertiary); + color: var(--text-primary); + font-size: 0.875rem; + font-family: monospace; + outline: none; + resize: none; +} + +.recoveryKeyInput:focus { + outline: 2px solid var(--brand-primary); + outline-offset: -2px; +} + +.recoveryKeyInput::placeholder { + color: var(--text-muted); + font-family: inherit; +} + +.recoveryKeyDisplay { + background-color: var(--background-tertiary); + border-radius: var(--radius-md); + padding: 16px; + position: relative; +} + +.recoveryKeyText { + font-size: 0.8125rem; + line-height: 1.6; + color: var(--text-primary); + word-break: break-all; + display: block; + margin-bottom: 8px; +} + +.copyButton { + background: var(--brand-primary); + color: white; + border: none; + border-radius: var(--radius-sm); + padding: 4px 12px; + font-size: 0.75rem; + font-weight: 600; + cursor: pointer; + transition: background 0.15s; +} + +.copyButton:hover { + background: var(--brand-primary-hover); +} + +.infoBox { + background-color: var(--background-secondary); + border-radius: var(--radius-md); + padding: 12px 16px; + font-size: 0.875rem; + color: var(--text-secondary); + line-height: 1.4; +} + +.infoList { + margin: 8px 0 0; + padding-left: 20px; +} + +.infoList li { + margin-top: 4px; +} + +.warningBox { + background-color: rgba(250, 166, 26, 0.1); + border: 1px solid rgba(250, 166, 26, 0.3); + border-radius: var(--radius-md); + padding: 12px 16px; + font-size: 0.8125rem; + color: var(--status-warning); + line-height: 1.4; +} + +.divider { + display: flex; + align-items: center; + gap: 12px; +} + +.divider::before, +.divider::after { + content: ''; + flex: 1; + height: 1px; + background-color: var(--background-modifier-accent); +} + +.dividerText { + font-size: 0.75rem; + color: var(--text-muted); + text-transform: uppercase; + font-weight: 600; +} + +.input { + height: 40px; + padding: 0 12px; + border-radius: var(--radius-md); + border: none; + background-color: var(--background-tertiary); + color: var(--text-primary); + font-size: 1rem; + outline: none; + width: 100%; + box-sizing: border-box; +} + +.input:focus { + outline: 2px solid var(--brand-primary); + outline-offset: -2px; +} + +.input::placeholder { + color: var(--text-muted); +} + +.passphraseForm { + display: flex; + flex-direction: column; + gap: 12px; +} + +.error { + color: var(--status-danger); + font-size: 0.875rem; +} + +.skipButton { + background: none; + border: none; + color: var(--text-muted); + font-size: 0.8125rem; + cursor: pointer; + padding: 8px; + text-align: center; + transition: color 0.15s; +} + +.skipButton:hover { + color: var(--text-secondary); +} + +.skipButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} diff --git a/packages/shared/src/components/auth/SetupEncryptionPage.tsx b/packages/shared/src/components/auth/SetupEncryptionPage.tsx new file mode 100644 index 0000000..ff9765b --- /dev/null +++ b/packages/shared/src/components/auth/SetupEncryptionPage.tsx @@ -0,0 +1,27 @@ +/** + * SetupEncryptionPage — carried over from the new UI for API parity, but + * Discord Clone's Convex auth flow generates + encrypts private keys at + * registration time (RegisterPage.tsx) and there is no separate recovery- + * key / cross-signing setup. This page is therefore a no-op that simply + * continues into the app. It remains exported so any linked route keeps + * compiling; nothing currently routes here. + */ +import { useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { AuthLayout } from './AuthLayout'; + +export function SetupEncryptionPage() { + const navigate = useNavigate(); + + useEffect(() => { + navigate('/channels/@me', { replace: true }); + }, [navigate]); + + return ( + <AuthLayout> + <h1 style={{ color: 'var(--text-primary, #fff)' }}>Continuing…</h1> + </AuthLayout> + ); +} + +export default SetupEncryptionPage; diff --git a/packages/shared/src/components/auth/SplashScreen.module.css b/packages/shared/src/components/auth/SplashScreen.module.css new file mode 100644 index 0000000..8dcf256 --- /dev/null +++ b/packages/shared/src/components/auth/SplashScreen.module.css @@ -0,0 +1,28 @@ +.container { + display: flex; + align-items: center; + justify-content: center; + height: 100vh; + height: 100dvh; + background-color: var(--background-secondary); +} + +.content { + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; +} + +.logo { + font-size: 2rem; + font-weight: 800; + color: var(--text-primary); + margin: 0; +} + +.text { + font-size: 0.875rem; + color: var(--text-secondary); + margin: 0; +} diff --git a/packages/shared/src/components/auth/SplashScreen.tsx b/packages/shared/src/components/auth/SplashScreen.tsx new file mode 100644 index 0000000..3ecaf8f --- /dev/null +++ b/packages/shared/src/components/auth/SplashScreen.tsx @@ -0,0 +1,14 @@ +import { Spinner } from '@brycord/ui'; +import styles from './SplashScreen.module.css'; + +export function SplashScreen() { + return ( + <div className={styles.container}> + <div className={styles.content}> + <h1 className={styles.logo}>Brycord</h1> + <Spinner size={32} /> + <p className={styles.text}>Connecting securely...</p> + </div> + </div> + ); +} diff --git a/packages/shared/src/components/auth/index.ts b/packages/shared/src/components/auth/index.ts new file mode 100644 index 0000000..775cc5b --- /dev/null +++ b/packages/shared/src/components/auth/index.ts @@ -0,0 +1,4 @@ +export { LoginPage } from './LoginPage'; +export { RegisterPage } from './RegisterPage'; +export { SplashScreen } from './SplashScreen'; +export { AuthLayout } from './AuthLayout'; diff --git a/packages/shared/src/components/channel/AccessPicker.module.css b/packages/shared/src/components/channel/AccessPicker.module.css new file mode 100644 index 0000000..ef608ca --- /dev/null +++ b/packages/shared/src/components/channel/AccessPicker.module.css @@ -0,0 +1,135 @@ +.root { + display: flex; + flex-direction: column; + gap: 8px; +} + +.sectionLabel { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.02em; + color: var(--text-tertiary); +} + +.sectionDescription { + font-size: 0.8125rem; + color: var(--text-secondary); + line-height: 1.4; + margin: 0; +} + +.customNotice { + font-size: 0.8125rem; + color: var(--status-warning, #faa61a); + padding: 8px 12px; + background-color: color-mix(in srgb, var(--status-warning, #faa61a) 10%, transparent); + border-radius: 8px; + margin: 0; +} + +.optionList { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 4px; +} + +.option { + display: flex; + align-items: flex-start; + gap: 12px; + padding: 14px; + background-color: var(--background-tertiary, rgba(255, 255, 255, 0.03)); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); + border-radius: 10px; + color: var(--text-primary); + font: inherit; + text-align: left; + cursor: pointer; + transition: background-color 0.15s ease, border-color 0.15s ease; +} + +.option:hover:not(:disabled) { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.05)); +} + +.option:disabled { + opacity: 0.55; + cursor: not-allowed; +} + +.optionSelected { + border-color: var(--brand-primary, #5865f2); + background-color: color-mix(in srgb, var(--brand-primary, #5865f2) 10%, transparent); +} + +.optionSelected:hover:not(:disabled) { + background-color: color-mix(in srgb, var(--brand-primary, #5865f2) 15%, transparent); +} + +.optionIcon { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + flex-shrink: 0; + border-radius: 50%; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.05)); + color: var(--text-secondary); +} + +.optionSelected .optionIcon { + background-color: var(--brand-primary, #5865f2); + color: #ffffff; +} + +.optionBody { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 4px; +} + +.optionTitle { + font-size: 0.9375rem; + font-weight: 600; + color: var(--text-primary); +} + +.optionDescription { + font-size: 0.8125rem; + line-height: 1.35; + color: var(--text-secondary); +} + +.optionCheck { + display: flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + flex-shrink: 0; + border-radius: 50%; + background-color: var(--brand-primary, #5865f2); + color: #ffffff; + align-self: center; +} + +.disabledHint { + font-size: 0.75rem; + color: var(--text-tertiary); + margin: 4px 0 0; +} + +.status { + font-size: 0.8125rem; + margin: 6px 0 0; + line-height: 1.4; +} + +.statusError { + color: var(--status-danger, #da373c); +} diff --git a/packages/shared/src/components/channel/AccessPicker.tsx b/packages/shared/src/components/channel/AccessPicker.tsx new file mode 100644 index 0000000..b5da34f --- /dev/null +++ b/packages/shared/src/components/channel/AccessPicker.tsx @@ -0,0 +1,136 @@ +import { RoomManager } from '@brycord/matrix-client'; +import { Check, GlobeHemisphereWest, Lock, UsersThree } from '@phosphor-icons/react'; +/** + * AccessPicker — three-option "who can join" picker for a channel + * or category. Mirrors Element's "Invite only / Space members / + * Anyone" control and writes the corresponding `m.room.join_rules` + * state event via `RoomManager.setChannelAccess`. + * + * Reads the current access on mount. Writes are optimistic — + * selection updates immediately; on server rejection we roll back + * and show an inline error. Disabled when the caller passes + * `disabled` (e.g., the user lacks `state_default` PL in the + * target room). + */ +import { useEffect, useState } from 'react'; +import styles from './AccessPicker.module.css'; + +export type AccessMode = 'invite' | 'space_members' | 'public'; + +interface AccessPickerProps { + /** The channel or category whose access we're editing. */ + roomId: string; + /** Parent space — used when writing the `restricted` allow list. */ + spaceId: string; + /** When true, every option is non-interactive. */ + disabled?: boolean; +} + +interface OptionDescriptor { + value: AccessMode; + icon: React.ReactNode; + title: string; + description: string; +} + +const OPTIONS: OptionDescriptor[] = [ + { + value: 'invite', + icon: <Lock size={20} weight="fill" />, + title: 'Invite Only', + description: 'Only invited members can see and join this channel.', + }, + { + value: 'space_members', + icon: <UsersThree size={20} weight="fill" />, + title: 'Space Members', + description: 'Anyone in the server can join. Recommended.', + }, + { + value: 'public', + icon: <GlobeHemisphereWest size={20} weight="fill" />, + title: 'Anyone', + description: 'Anyone with a link can join, even without a server invite.', + }, +]; + +export function AccessPicker({ roomId, spaceId, disabled = false }: AccessPickerProps) { + const [current, setCurrent] = useState<AccessMode | 'other' | null>(null); + const [saving, setSaving] = useState(false); + const [error, setError] = useState<string | null>(null); + + // Read the current access on mount and whenever the target + // room changes. Synchronous — reads from matrix-js-sdk's + // in-memory room state. + useEffect(() => { + const mode = RoomManager.getInstance().getChannelAccess(roomId, spaceId); + setCurrent(mode); + }, [roomId, spaceId]); + + const handleSelect = async (next: AccessMode) => { + if (disabled || saving || next === current) return; + const previous = current; + // Optimistic — flip the UI immediately, roll back on error. + setCurrent(next); + setSaving(true); + setError(null); + try { + await RoomManager.getInstance().setChannelAccess(roomId, next, spaceId); + } catch (err: any) { + setCurrent(previous); + setError(err?.message || 'Failed to update access.'); + } finally { + setSaving(false); + } + }; + + // If the room has join rules we don't recognise (e.g. `knock` + // or a `restricted` allow list pointing at a different room), + // surface that rather than silently overwriting the config. + const isCustom = current === 'other'; + + return ( + <div className={styles.root}> + <div className={styles.sectionLabel}>Channel Access</div> + <p className={styles.sectionDescription}> + Control who can join this channel. Existing members always keep their access — this only affects new joins. + </p> + + {isCustom && ( + <p className={styles.customNotice}> + Custom join rules are set on this channel. Pick an option below to replace them. + </p> + )} + + <div className={styles.optionList}> + {OPTIONS.map((opt) => { + const isSelected = current === opt.value; + return ( + <button + key={opt.value} + type="button" + className={`${styles.option} ${isSelected ? styles.optionSelected : ''}`} + onClick={() => handleSelect(opt.value)} + disabled={disabled || saving} + > + <span className={styles.optionIcon}>{opt.icon}</span> + <span className={styles.optionBody}> + <span className={styles.optionTitle}>{opt.title}</span> + <span className={styles.optionDescription}>{opt.description}</span> + </span> + {isSelected && ( + <span className={styles.optionCheck}> + <Check size={14} weight="bold" /> + </span> + )} + </button> + ); + })} + </div> + + {disabled && <p className={styles.disabledHint}>You need a higher role to change access.</p>} + + {error && <p className={`${styles.status} ${styles.statusError}`}>{error}</p>} + </div> + ); +} diff --git a/packages/shared/src/components/channel/AttachmentAudio.module.css b/packages/shared/src/components/channel/AttachmentAudio.module.css new file mode 100644 index 0000000..47d10e5 --- /dev/null +++ b/packages/shared/src/components/channel/AttachmentAudio.module.css @@ -0,0 +1,360 @@ +/* ── Audio player card ─────────────────────────────────────── + Fluxer-style audio attachment surface. Rounded rectangle with + a subtle background, play button on the left, filename + seek + bar + time on the right, control row underneath. Width is + constrained so the card feels like a compact inline element + in a chat message, not a full-width banner. */ + +.card { + display: flex; + flex-direction: column; + gap: 12px; + width: 100%; + max-width: 480px; + padding: 14px 16px; + background-color: var(--background-secondary); + border: 1px solid var(--background-modifier-accent); + border-radius: 0.625rem; + box-sizing: border-box; +} + +/* ── Top row: play button + filename on the same line ──────── */ +.topRow { + display: flex; + align-items: center; + gap: 12px; + min-width: 0; +} + +.playButton { + display: inline-flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + padding: 0; + background-color: var(--brand-primary); + border: none; + border-radius: 50%; + color: var(--text-on-brand-primary, #fff); + cursor: pointer; + flex-shrink: 0; + transition: filter 0.12s, transform 0.12s; + -webkit-tap-highlight-color: transparent; +} + +.playButton:hover:not(:disabled) { + filter: brightness(1.08); +} + +.playButton:active:not(:disabled) { + transform: scale(0.96); +} + +.playButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* ── Filename row ─────────────────────────────────────────── + The stem is a single-line ellipsis'd span and the extension + is a separate right-flush span, mirroring the Fluxer pattern + where the extension always stays visible while the middle of + a long filename gets clipped. Lives inline with the play + button in the top row; progress bar sits on its own row + below so it can span the full card width. */ +.filenameRow { + flex: 1; + display: flex; + align-items: baseline; + gap: 2px; + min-width: 0; + font-size: 0.9375rem; + font-weight: 700; + color: var(--text-primary); +} + +.filenameStem { + min-width: 0; + flex-shrink: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.filenameExt { + flex-shrink: 0; +} + +/* ── Progress row: seek bar + time label ───────────────────── */ +.progressRow { + display: flex; + align-items: center; + gap: 10px; + min-width: 0; +} + +.progressTrack { + position: relative; + flex: 1; + height: 4px; + border-radius: 999px; + background-color: var(--background-modifier-accent); + cursor: pointer; +} + +.progressFill { + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: var(--progress, 0%); + border-radius: 999px; + background-color: var(--brand-primary); + pointer-events: none; +} + +/* Invisible native range input sits on top of the visual track + so the user gets native keyboard + drag support for free. + The real styling comes from .progressTrack / .progressFill; we + just override the thumb to be clickable. */ +.progressInput { + position: absolute; + inset: -8px 0; + width: 100%; + height: calc(100% + 16px); + margin: 0; + padding: 0; + background: transparent; + border: none; + outline: none; + cursor: pointer; + opacity: 0; + -webkit-appearance: none; + appearance: none; +} + +.progressInput:disabled { + cursor: not-allowed; +} + +.progressInput::-webkit-slider-thumb { + -webkit-appearance: none; + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--brand-primary); + cursor: pointer; +} + +.progressInput::-moz-range-thumb { + width: 14px; + height: 14px; + border: none; + border-radius: 50%; + background: var(--brand-primary); + cursor: pointer; +} + +.timeLabel { + flex-shrink: 0; + font-size: 0.75rem; + font-variant-numeric: tabular-nums; + color: var(--text-primary-muted, #a0a3a8); +} + +/* ── Bottom row: volume • speed + favorite + download ──────── */ +.bottomRow { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.bottomRowRight { + display: flex; + align-items: center; + gap: 4px; +} + +.iconButton { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + padding: 0; + background: transparent; + border: none; + border-radius: 6px; + color: var(--text-primary-muted, #a0a3a8); + cursor: pointer; + transition: background-color 0.12s, color 0.12s; + -webkit-tap-highlight-color: transparent; +} + +.iconButton:hover:not(:disabled) { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +.iconButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* Favorited state — brand-primary fill to match the lightbox + star treatment. */ +.iconButtonActive { + color: var(--brand-primary); +} + +.iconButtonActive:hover:not(:disabled) { + color: var(--brand-primary); + background-color: var(--background-modifier-hover); + filter: brightness(1.08); +} + +.speedButton { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 32px; + height: 32px; + padding: 0 10px; + background: transparent; + border: none; + border-radius: 6px; + color: var(--text-primary-muted, #a0a3a8); + font: inherit; + font-size: 0.75rem; + font-weight: 700; + cursor: pointer; + transition: background-color 0.12s, color 0.12s; + -webkit-tap-highlight-color: transparent; + font-variant-numeric: tabular-nums; +} + +.speedButton:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +.hiddenAudio { + display: none; +} + +/* ── Volume slider popover ───────────────────────────────── + Speaker button + a hover-revealed vertical slider above it. + The popover sits on top of the bottom row, anchored to the + button, and fades in when the user hovers either the icon + or the popover itself — the shared `.volumeWrap` wrapper + keeps the hover state alive across both. */ + +.volumeWrap { + position: relative; + display: inline-flex; + align-items: center; +} + +.volumePopover { + position: absolute; + bottom: calc(100% + 6px); + left: 50%; + transform: translateX(-50%); + width: 44px; + padding: 10px 0 14px; + background-color: var(--background-floating, #18191c); + border: 1px solid var(--background-modifier-accent); + border-radius: 0.5rem; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35); + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + visibility: hidden; + transition: opacity 0.12s ease, visibility 0.12s ease; + z-index: 5; +} + +/* Invisible bridge below the popover so the mouse can move + from the speaker icon into the slider without briefly + leaving the hover chain and closing the popover. */ +.volumePopover::after { + content: ''; + position: absolute; + top: 100%; + left: 0; + right: 0; + height: 6px; + pointer-events: auto; +} + +.volumeWrap:hover .volumePopover, +.volumeWrap:focus-within .volumePopover { + opacity: 1; + visibility: visible; +} + +/* Vertical track. 4px wide, 90px tall — same fill-via-CSS-var + technique the seek bar uses, just with the axis flipped. */ +.volumeTrack { + position: relative; + width: 4px; + height: 90px; + border-radius: 999px; + background-color: var(--background-modifier-accent); +} + +.volumeFill { + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: var(--volume, 0%); + border-radius: 999px; + background-color: var(--brand-primary); + pointer-events: none; +} + +/* Rotated native range input layered on top of the visual + track. The -90deg rotation makes it drag vertically while + keeping all the native keyboard + pointer behaviour. The + hit area is deliberately larger than the visible track so + it's easy to grab without pixel-perfect aim. */ +.volumeInput { + position: absolute; + width: 90px; + height: 28px; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) rotate(-90deg); + transform-origin: center; + margin: 0; + padding: 0; + background: transparent; + border: none; + outline: none; + cursor: pointer; + opacity: 0; + -webkit-appearance: none; + appearance: none; +} + +.volumeInput::-webkit-slider-thumb { + -webkit-appearance: none; + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--brand-primary); + cursor: pointer; +} + +.volumeInput::-moz-range-thumb { + width: 14px; + height: 14px; + border: none; + border-radius: 50%; + background: var(--brand-primary); + cursor: pointer; +} diff --git a/packages/shared/src/components/channel/AttachmentAudio.tsx b/packages/shared/src/components/channel/AttachmentAudio.tsx new file mode 100644 index 0000000..fb8beab --- /dev/null +++ b/packages/shared/src/components/channel/AttachmentAudio.tsx @@ -0,0 +1,335 @@ +/** + * AttachmentAudio — custom audio player for `audio/*` attachments. + * Ported from the new UI's Fluxer-style layout but simplified for + * our Convex pipeline: takes the already-decrypted blob URL from + * `EncryptedAttachment` instead of resolving an MXC URL itself. + * + * ┌──────────────────────────────────────────────────────────┐ + * │ ┌───┐ filename-truncated… .mp3 │ + * │ │ ▶ │ ────────────────────────●─── 0:12/3:04│ + * │ └───┘ │ + * │ ◀) 1x ↓ │ + * └──────────────────────────────────────────────────────────┘ + */ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { + Download, + Pause, + Play, + SpeakerHigh, + SpeakerSlash, + Star, +} from '@phosphor-icons/react'; +import { useMutation, useQuery } from 'convex/react'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import type { AttachmentMetadata } from './EncryptedAttachment'; +import styles from './AttachmentAudio.module.css'; + +interface AttachmentAudioProps { + src: string; + filename: string; + attachment?: AttachmentMetadata; +} + +const PLAYBACK_SPEEDS = [1, 1.25, 1.5, 2, 0.5, 0.75]; + +function splitFilename(filename: string): { stem: string; ext: string } { + const dot = filename.lastIndexOf('.'); + if (dot <= 0 || dot === filename.length - 1) { + return { stem: filename, ext: '' }; + } + return { stem: filename.slice(0, dot), ext: filename.slice(dot) }; +} + +function formatTime(seconds: number): string { + if (!Number.isFinite(seconds) || seconds < 0) return '0:00'; + const total = Math.floor(seconds); + const m = Math.floor(total / 60); + const s = total % 60; + return `${m}:${s.toString().padStart(2, '0')}`; +} + +export function AttachmentAudio({ src, filename, attachment }: AttachmentAudioProps) { + const audioRef = useRef<HTMLAudioElement>(null); + const [isPlaying, setIsPlaying] = useState(false); + const [currentTime, setCurrentTime] = useState(0); + const [duration, setDuration] = useState(0); + const [volume, setVolume] = useState(1); + const [isMuted, setIsMuted] = useState(false); + const [playbackRateIndex, setPlaybackRateIndex] = useState(0); + + const { stem, ext } = useMemo(() => splitFilename(filename), [filename]); + + // Saved-media wiring — mirrors the ImageLightbox star button so + // audio attachments can be bookmarked into the Media tab. + const myUserId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const savedList = useQuery( + api.savedMedia.list, + myUserId && attachment + ? { userId: myUserId as Id<'userProfiles'> } + : 'skip', + ); + const isSaved = !!( + attachment && savedList?.some((m) => m.url === attachment.url) + ); + const saveMutation = useMutation(api.savedMedia.save); + const removeMutation = useMutation(api.savedMedia.remove); + + const handleToggleSaved = useCallback(async () => { + if (!attachment || !myUserId) return; + try { + if (isSaved) { + await removeMutation({ + userId: myUserId as Id<'userProfiles'>, + url: attachment.url, + }); + } else { + await saveMutation({ + userId: myUserId as Id<'userProfiles'>, + url: attachment.url, + kind: attachment.mimeType.split('/')[0], + filename: attachment.filename, + mimeType: attachment.mimeType, + width: attachment.width, + height: attachment.height, + size: attachment.size, + encryptionKey: attachment.key, + encryptionIv: attachment.iv, + }); + } + } catch (err) { + console.warn('Failed to toggle saved audio:', err); + } + }, [attachment, isSaved, myUserId, removeMutation, saveMutation]); + + useEffect(() => { + const el = audioRef.current; + if (!el) return; + const handleTime = () => setCurrentTime(el.currentTime); + const handleDuration = () => setDuration(el.duration); + const handlePlay = () => setIsPlaying(true); + const handlePause = () => setIsPlaying(false); + const handleEnded = () => setIsPlaying(false); + const handleVolume = () => { + setVolume(el.volume); + setIsMuted(el.muted); + }; + el.addEventListener('timeupdate', handleTime); + el.addEventListener('loadedmetadata', handleDuration); + el.addEventListener('durationchange', handleDuration); + el.addEventListener('play', handlePlay); + el.addEventListener('pause', handlePause); + el.addEventListener('ended', handleEnded); + el.addEventListener('volumechange', handleVolume); + return () => { + el.removeEventListener('timeupdate', handleTime); + el.removeEventListener('loadedmetadata', handleDuration); + el.removeEventListener('durationchange', handleDuration); + el.removeEventListener('play', handlePlay); + el.removeEventListener('pause', handlePause); + el.removeEventListener('ended', handleEnded); + el.removeEventListener('volumechange', handleVolume); + }; + }, []); + + useEffect(() => { + const el = audioRef.current; + if (!el) return; + el.playbackRate = PLAYBACK_SPEEDS[playbackRateIndex]; + }, [playbackRateIndex]); + + const handleTogglePlay = useCallback(() => { + const el = audioRef.current; + if (!el || !src) return; + if (el.paused) { + void el.play().catch(() => {}); + } else { + el.pause(); + } + }, [src]); + + const handleSeek = useCallback( + (e: React.ChangeEvent<HTMLInputElement>) => { + const el = audioRef.current; + if (!el) return; + const next = Number(e.target.value); + el.currentTime = next; + setCurrentTime(next); + }, + [], + ); + + const handleToggleMute = useCallback(() => { + const el = audioRef.current; + if (!el) return; + el.muted = !el.muted; + }, []); + + const handleVolumeSlider = useCallback( + (e: React.ChangeEvent<HTMLInputElement>) => { + const el = audioRef.current; + if (!el) return; + const next = Number(e.target.value); + el.volume = next; + if (next > 0 && el.muted) el.muted = false; + else if (next === 0 && !el.muted) el.muted = true; + }, + [], + ); + + const handleCycleSpeed = useCallback(() => { + setPlaybackRateIndex((i) => (i + 1) % PLAYBACK_SPEEDS.length); + }, []); + + const handleDownload = useCallback(() => { + if (!src) return; + const a = document.createElement('a'); + a.href = src; + a.download = filename; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + }, [src, filename]); + + const progressRatio = + duration > 0 ? Math.max(0, Math.min(1, currentTime / duration)) : 0; + const progressPercent = `${(progressRatio * 100).toFixed(2)}%`; + const playbackSpeedLabel = `${PLAYBACK_SPEEDS[playbackRateIndex]}x`; + + return ( + <div className={styles.card}> + <div className={styles.topRow}> + <button + type="button" + className={styles.playButton} + onClick={handleTogglePlay} + disabled={!src} + aria-label={isPlaying ? 'Pause' : 'Play'} + title={isPlaying ? 'Pause' : 'Play'} + > + {isPlaying ? ( + <Pause size={18} weight="fill" /> + ) : ( + <Play size={18} weight="fill" /> + )} + </button> + + <div className={styles.filenameRow}> + <span className={styles.filenameStem} title={filename}> + {stem} + </span> + {ext && <span className={styles.filenameExt}>{ext}</span>} + </div> + </div> + + <div className={styles.progressRow}> + <div + className={styles.progressTrack} + style={{ ['--progress' as string]: progressPercent }} + > + <div className={styles.progressFill} /> + <input + type="range" + className={styles.progressInput} + min={0} + max={duration || 0} + step={0.01} + value={currentTime} + onChange={handleSeek} + disabled={!duration} + aria-label="Seek audio" + /> + </div> + <span className={styles.timeLabel}> + {formatTime(currentTime)} / {formatTime(duration)} + </span> + </div> + + <div className={styles.bottomRow}> + <div className={styles.volumeWrap}> + <div className={styles.volumePopover}> + <div + className={styles.volumeTrack} + style={{ + ['--volume' as string]: `${(isMuted ? 0 : volume) * 100}%`, + }} + > + <div className={styles.volumeFill} /> + <input + type="range" + min={0} + max={1} + step={0.01} + value={isMuted ? 0 : volume} + onChange={handleVolumeSlider} + className={styles.volumeInput} + aria-label="Volume" + /> + </div> + </div> + + <button + type="button" + className={styles.iconButton} + onClick={handleToggleMute} + aria-label={isMuted || volume === 0 ? 'Unmute' : 'Mute'} + title={isMuted || volume === 0 ? 'Unmute' : 'Mute'} + > + {isMuted || volume === 0 ? ( + <SpeakerSlash size={18} weight="fill" /> + ) : ( + <SpeakerHigh size={18} weight="fill" /> + )} + </button> + </div> + + <div className={styles.bottomRowRight}> + <button + type="button" + className={styles.speedButton} + onClick={handleCycleSpeed} + aria-label={`Playback speed: ${playbackSpeedLabel}`} + title="Playback speed" + > + {playbackSpeedLabel} + </button> + {attachment && ( + <button + type="button" + className={styles.iconButton} + onClick={() => void handleToggleSaved()} + aria-label={isSaved ? 'Unfavorite' : 'Favorite'} + title={isSaved ? 'Unfavorite' : 'Favorite'} + style={ + isSaved + ? { color: 'var(--brand-primary, #5865f2)' } + : undefined + } + > + <Star size={18} weight={isSaved ? 'fill' : 'regular'} /> + </button> + )} + <button + type="button" + className={styles.iconButton} + onClick={handleDownload} + disabled={!src} + aria-label="Download" + title="Download" + > + <Download size={18} weight="regular" /> + </button> + </div> + </div> + + <audio + ref={audioRef} + src={src || undefined} + preload="metadata" + className={styles.hiddenAudio} + /> + </div> + ); +} diff --git a/packages/shared/src/components/channel/AttachmentVideo.module.css b/packages/shared/src/components/channel/AttachmentVideo.module.css new file mode 100644 index 0000000..5947cdc --- /dev/null +++ b/packages/shared/src/components/channel/AttachmentVideo.module.css @@ -0,0 +1,289 @@ +/* ── Video attachment card ──────────────────────────────── + Inline video renderer for `video/*` message attachments. + Poster → click → inline playback with a custom control + overlay (top hover bar + bottom bar with seek + play + + volume + time + fullscreen). */ + +.wrapper { + position: relative; + display: inline-block; + max-width: 100%; + border-radius: var(--radius-lg, 12px); + overflow: hidden; + background-color: var(--background-secondary); +} + +.video { + display: block; + width: 100%; + height: 100%; + max-width: 100%; + max-height: inherit; + border-radius: var(--radius-lg, 12px); + /* `object-fit: contain` keeps portrait videos centered inside + the 400 × 300 cap instead of being cropped. */ + object-fit: contain; + background-color: #000; + cursor: pointer; +} + +/* ── Center play overlay (poster state) ─────────────────── + Shown before the user first clicks play. Fades away once + hasStarted is true so the native video surface + custom + control bar take over. */ +.playOverlay { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; + padding: 0; + cursor: pointer; + background-color: rgba(0, 0, 0, 0.25); + transition: background-color 0.12s; + z-index: 2; +} + +.playOverlay:hover { + background-color: rgba(0, 0, 0, 0.35); +} + +.playBadge { + display: inline-flex; + align-items: center; + justify-content: center; + width: 48px; + height: 48px; + border-radius: 50%; + background-color: rgba(0, 0, 0, 0.6); + border: none; + color: #fff; + /* Nudge the play triangle 2px right so the visual weight + sits centered inside the circle — the triangle's + geometric centroid is left of its bounding box. */ + padding-left: 4px; + box-sizing: border-box; + transition: transform 0.12s; +} + +.playOverlay:hover .playBadge { + transform: scale(1.05); +} + +/* ── Top hover bar (Trash / Download / Favorite) ────────── + Pinned to the top-right of the card. Hidden at rest, + fades in on wrapper hover or focus-within. */ +.topBar { + position: absolute; + top: 8px; + right: 8px; + display: flex; + align-items: center; + gap: 6px; + opacity: 0; + visibility: hidden; + transition: opacity 0.15s ease, visibility 0.15s ease; + z-index: 3; +} + +.wrapper:hover .topBar, +.wrapper:focus-within .topBar { + opacity: 1; + visibility: visible; +} + +.topBarButton { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + padding: 0; + background-color: rgba(0, 0, 0, 0.6); + border: none; + border-radius: 6px; + color: #fff; + cursor: pointer; + transition: background-color 0.12s, color 0.12s; +} + +.topBarButton:hover:not(:disabled) { + background-color: rgba(0, 0, 0, 0.8); +} + +.topBarButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* Favorited state — brand-primary fill matches the image + lightbox's star treatment so the visual language stays + consistent across all three attachment viewers. */ +.topBarButtonActive { + background-color: var(--brand-primary); + color: var(--text-on-brand-primary, #fff); +} + +.topBarButtonActive:hover:not(:disabled) { + background-color: var(--brand-primary); + filter: brightness(1.08); +} + +.topBarButtonDanger:hover:not(:disabled) { + background-color: hsl(0, calc(70% * var(--saturation-factor, 1)), 55%); +} + +/* ── Bottom control bar (after hasStarted) ──────────────── + Seek bar + play + volume + time + fullscreen. Fades in on + wrapper hover so it doesn't clutter a video the user is + actively watching at rest. Permanently visible inside + fullscreen because the wrapper receives the :hover state + from the system cursor whenever it moves. */ +.bottomBar { + position: absolute; + left: 0; + right: 0; + bottom: 0; + padding: 8px 12px 10px; + /* Gradient so the white controls stay readable against + bright video content without a hard edge. */ + background: linear-gradient( + to top, + rgba(0, 0, 0, 0.75) 0%, + rgba(0, 0, 0, 0) 100% + ); + display: flex; + flex-direction: column; + gap: 6px; + opacity: 0; + visibility: hidden; + transition: opacity 0.15s ease, visibility 0.15s ease; + z-index: 3; +} + +.wrapperPlaying:hover .bottomBar, +.wrapperPlaying:focus-within .bottomBar { + opacity: 1; + visibility: visible; +} + +/* ── Seek track ────────────────────────────────────────── + Native range input layered over a visual track+fill so we + get keyboard + drag support for free while keeping full + visual control. Same pattern as AttachmentAudio. */ +.seekTrack { + position: relative; + height: 4px; + border-radius: 999px; + background-color: rgba(255, 255, 255, 0.25); + cursor: pointer; +} + +.seekFill { + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: var(--progress, 0%); + border-radius: 999px; + background-color: var(--brand-primary); + pointer-events: none; +} + +.seekInput { + position: absolute; + inset: -8px 0; + width: 100%; + height: calc(100% + 16px); + margin: 0; + padding: 0; + background: transparent; + border: none; + outline: none; + cursor: pointer; + opacity: 0; + -webkit-appearance: none; + appearance: none; +} + +.seekInput::-webkit-slider-thumb { + -webkit-appearance: none; + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--brand-primary); + cursor: pointer; +} + +.seekInput::-moz-range-thumb { + width: 14px; + height: 14px; + border: none; + border-radius: 50%; + background: var(--brand-primary); + cursor: pointer; +} + +/* ── Control row (play / volume / time / fullscreen) ─────── */ +.controlsRow { + display: flex; + align-items: center; + gap: 8px; +} + +.controlButton { + display: inline-flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + padding: 0; + background: transparent; + border: none; + border-radius: 4px; + color: #fff; + cursor: pointer; + transition: background-color 0.12s; +} + +.controlButton:hover { + background-color: rgba(255, 255, 255, 0.14); +} + +.timeLabel { + font-size: 0.75rem; + font-weight: 600; + color: #fff; + font-variant-numeric: tabular-nums; + margin-left: 2px; +} + +.controlsSpacer { + flex: 1; +} + +/* ── Spoiler state ──────────────────────────────────────── + Blurs the poster and swaps the play badge for a SPOILER + label. First click reveals, a second click plays — same + two-step reveal the image attachment uses. */ +.wrapperBlurred .video { + filter: blur(44px); + clip-path: inset(0 round var(--radius-lg, 12px)); +} + +.wrapperBlurred .playOverlay { + background-color: rgba(0, 0, 0, 0.45); +} + +.spoilerCoverLabel { + padding: 8px 16px; + background-color: rgba(0, 0, 0, 0.75); + border-radius: 999px; + color: #fff; + font-size: 0.8125rem; + font-weight: 800; + letter-spacing: 0.08em; + pointer-events: none; +} diff --git a/packages/shared/src/components/channel/AttachmentVideo.tsx b/packages/shared/src/components/channel/AttachmentVideo.tsx new file mode 100644 index 0000000..c7fd470 --- /dev/null +++ b/packages/shared/src/components/channel/AttachmentVideo.tsx @@ -0,0 +1,348 @@ +/** + * AttachmentVideo — custom video player with Fluxer-style overlay + * controls. Ported from the new UI and adapted for our pipeline: + * takes the already-decrypted blob URL from `EncryptedAttachment`. + * + * Two playback states: + * 1. Not started → poster + center play button. + * 2. Playing → bottom seek bar, play/pause, mute, time, fullscreen. + * + * Top-right hover bar surfaces Download regardless of playback state. + */ +import { useCallback, useEffect, useRef, useState } from 'react'; +import { + CornersIn, + CornersOut, + Download, + Pause, + Play, + SpeakerHigh, + SpeakerSlash, + Star, +} from '@phosphor-icons/react'; +import { useMutation, useQuery } from 'convex/react'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import type { AttachmentMetadata } from './EncryptedAttachment'; +import styles from './AttachmentVideo.module.css'; + +interface AttachmentVideoProps { + src: string; + filename: string; + width?: number; + height?: number; + attachment?: AttachmentMetadata; +} + +function formatTime(seconds: number): string { + if (!Number.isFinite(seconds) || seconds < 0) return '0:00'; + const total = Math.floor(seconds); + const m = Math.floor(total / 60); + const s = total % 60; + return `${m}:${s.toString().padStart(2, '0')}`; +} + +export function AttachmentVideo({ + src, + filename, + width, + height, + attachment, +}: AttachmentVideoProps) { + const wrapperRef = useRef<HTMLDivElement>(null); + const videoRef = useRef<HTMLVideoElement>(null); + + const [hasStarted, setHasStarted] = useState(false); + const [isPlaying, setIsPlaying] = useState(false); + const [currentTime, setCurrentTime] = useState(0); + const [duration, setDuration] = useState(0); + const [volume, setVolume] = useState(1); + const [isMuted, setIsMuted] = useState(false); + const [isFullscreen, setIsFullscreen] = useState(false); + + // Saved-media wiring — mirrors ImageLightbox / AttachmentAudio + // so video attachments can be bookmarked into the Media tab. + const myUserId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const savedList = useQuery( + api.savedMedia.list, + myUserId && attachment + ? { userId: myUserId as Id<'userProfiles'> } + : 'skip', + ); + const isSaved = !!( + attachment && savedList?.some((m) => m.url === attachment.url) + ); + const saveMutation = useMutation(api.savedMedia.save); + const removeMutation = useMutation(api.savedMedia.remove); + + const handleToggleSaved = useCallback(async () => { + if (!attachment || !myUserId) return; + try { + if (isSaved) { + await removeMutation({ + userId: myUserId as Id<'userProfiles'>, + url: attachment.url, + }); + } else { + await saveMutation({ + userId: myUserId as Id<'userProfiles'>, + url: attachment.url, + kind: attachment.mimeType.split('/')[0], + filename: attachment.filename, + mimeType: attachment.mimeType, + width: attachment.width, + height: attachment.height, + size: attachment.size, + encryptionKey: attachment.key, + encryptionIv: attachment.iv, + }); + } + } catch (err) { + console.warn('Failed to toggle saved video:', err); + } + }, [attachment, isSaved, myUserId, removeMutation, saveMutation]); + + const widthCap = Math.min(width || 400, 400); + const heightCap = Math.min(height || 300, 300); + + useEffect(() => { + const el = videoRef.current; + if (!el) return; + const onTime = () => setCurrentTime(el.currentTime); + const onDur = () => setDuration(el.duration); + const onPlay = () => setIsPlaying(true); + const onPause = () => setIsPlaying(false); + const onEnded = () => setIsPlaying(false); + const onVol = () => { + setVolume(el.volume); + setIsMuted(el.muted); + }; + el.addEventListener('timeupdate', onTime); + el.addEventListener('loadedmetadata', onDur); + el.addEventListener('durationchange', onDur); + el.addEventListener('play', onPlay); + el.addEventListener('pause', onPause); + el.addEventListener('ended', onEnded); + el.addEventListener('volumechange', onVol); + return () => { + el.removeEventListener('timeupdate', onTime); + el.removeEventListener('loadedmetadata', onDur); + el.removeEventListener('durationchange', onDur); + el.removeEventListener('play', onPlay); + el.removeEventListener('pause', onPause); + el.removeEventListener('ended', onEnded); + el.removeEventListener('volumechange', onVol); + }; + }, []); + + useEffect(() => { + const handler = () => { + setIsFullscreen(document.fullscreenElement === wrapperRef.current); + }; + document.addEventListener('fullscreenchange', handler); + return () => document.removeEventListener('fullscreenchange', handler); + }, []); + + const handleStartPlay = useCallback(() => { + const el = videoRef.current; + if (!el) return; + setHasStarted(true); + void el.play().catch(() => {}); + }, []); + + const handleTogglePlay = useCallback(() => { + const el = videoRef.current; + if (!el) return; + if (el.paused) void el.play().catch(() => {}); + else el.pause(); + }, []); + + const handleSeek = useCallback( + (e: React.ChangeEvent<HTMLInputElement>) => { + const el = videoRef.current; + if (!el) return; + const next = Number(e.target.value); + el.currentTime = next; + setCurrentTime(next); + }, + [], + ); + + const handleToggleMute = useCallback(() => { + const el = videoRef.current; + if (!el) return; + if (el.muted && el.volume === 0) el.volume = 1; + el.muted = !el.muted; + }, []); + + const handleToggleFullscreen = useCallback(() => { + const wrapper = wrapperRef.current; + if (!wrapper) return; + if (document.fullscreenElement) { + void document.exitFullscreen().catch(() => {}); + } else { + void wrapper.requestFullscreen().catch(() => {}); + } + }, []); + + const handleDownload = useCallback(() => { + if (!src) return; + const a = document.createElement('a'); + a.href = src; + a.download = filename; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + }, [src, filename]); + + const stop = + (fn: () => void) => + (e: React.MouseEvent) => { + e.stopPropagation(); + fn(); + }; + + const progressRatio = duration > 0 ? currentTime / duration : 0; + const progressPercent = `${Math.max(0, Math.min(100, progressRatio * 100)).toFixed(2)}%`; + + return ( + <div + ref={wrapperRef} + className={`${styles.wrapper} ${hasStarted ? styles.wrapperPlaying : ''}`} + style={{ + maxWidth: `min(${widthCap}px, 100%)`, + maxHeight: `${heightCap}px`, + }} + > + <video + ref={videoRef} + src={src || undefined} + className={styles.video} + preload="metadata" + playsInline + muted={!hasStarted} + onClick={hasStarted ? handleTogglePlay : undefined} + /> + + <div className={styles.topBar}> + {attachment && ( + <button + type="button" + className={styles.topBarButton} + onClick={stop(() => void handleToggleSaved())} + aria-label={isSaved ? 'Unfavorite' : 'Favorite'} + title={isSaved ? 'Unfavorite' : 'Favorite'} + style={ + isSaved + ? { color: 'var(--brand-primary, #5865f2)' } + : undefined + } + > + <Star size={18} weight={isSaved ? 'fill' : 'regular'} /> + </button> + )} + <button + type="button" + className={styles.topBarButton} + onClick={stop(handleDownload)} + disabled={!src} + aria-label="Download" + title="Download" + > + <Download size={18} weight="regular" /> + </button> + </div> + + {!hasStarted && ( + <button + type="button" + className={styles.playOverlay} + onClick={handleStartPlay} + aria-label="Play video" + > + <span className={styles.playBadge}> + <Play size={22} weight="fill" /> + </span> + </button> + )} + + {hasStarted && ( + <div + className={styles.bottomBar} + onClick={(e) => e.stopPropagation()} + > + <div + className={styles.seekTrack} + style={{ ['--progress' as string]: progressPercent }} + > + <div className={styles.seekFill} /> + <input + type="range" + className={styles.seekInput} + min={0} + max={duration || 0} + step={0.01} + value={currentTime} + onChange={handleSeek} + disabled={!duration} + aria-label="Seek video" + /> + </div> + + <div className={styles.controlsRow}> + <button + type="button" + className={styles.controlButton} + onClick={handleTogglePlay} + aria-label={isPlaying ? 'Pause' : 'Play'} + title={isPlaying ? 'Pause' : 'Play'} + > + {isPlaying ? ( + <Pause size={18} weight="fill" /> + ) : ( + <Play size={18} weight="fill" /> + )} + </button> + + <button + type="button" + className={styles.controlButton} + onClick={handleToggleMute} + aria-label={isMuted || volume === 0 ? 'Unmute' : 'Mute'} + title={isMuted || volume === 0 ? 'Unmute' : 'Mute'} + > + {isMuted || volume === 0 ? ( + <SpeakerSlash size={18} weight="fill" /> + ) : ( + <SpeakerHigh size={18} weight="fill" /> + )} + </button> + + <span className={styles.timeLabel}> + {formatTime(currentTime)} / {formatTime(duration)} + </span> + + <div className={styles.controlsSpacer} /> + + <button + type="button" + className={styles.controlButton} + onClick={handleToggleFullscreen} + aria-label={ + isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen' + } + title={isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'} + > + {isFullscreen ? ( + <CornersIn size={18} weight="bold" /> + ) : ( + <CornersOut size={18} weight="bold" /> + )} + </button> + </div> + </div> + )} + </div> + ); +} diff --git a/packages/shared/src/components/channel/ChannelChatLayout.module.css b/packages/shared/src/components/channel/ChannelChatLayout.module.css new file mode 100644 index 0000000..d260854 --- /dev/null +++ b/packages/shared/src/components/channel/ChannelChatLayout.module.css @@ -0,0 +1,18 @@ +.container { + display: flex; + flex-direction: column; + flex: 1; + min-width: 0; + min-height: 0; + background-color: var(--background-secondary-lighter, var(--background-primary)); +} + +.messagesWrapper { + flex: 1; + min-height: 0; + overflow: hidden; +} + +.inputArea { + flex-shrink: 0; +} diff --git a/packages/shared/src/components/channel/ChannelChatLayout.tsx b/packages/shared/src/components/channel/ChannelChatLayout.tsx new file mode 100644 index 0000000..3a193d9 --- /dev/null +++ b/packages/shared/src/components/channel/ChannelChatLayout.tsx @@ -0,0 +1,38 @@ +import { useCallback, useState } from 'react'; +import { ChannelTextarea } from './ChannelTextarea'; +import { Messages } from './Messages'; +import { TypingUsers } from './TypingUsers'; +import styles from './ChannelChatLayout.module.css'; + +interface ChannelChatLayoutProps { + channelId: string; +} + +interface ReplyState { + eventId: string; + username: string; +} + +export function ChannelChatLayout({ channelId }: ChannelChatLayoutProps) { + const [replyTo, setReplyTo] = useState<ReplyState | null>(null); + + const handleReply = useCallback((eventId: string, username: string) => { + setReplyTo({ eventId, username }); + }, []); + + const handleCancelReply = useCallback(() => { + setReplyTo(null); + }, []); + + return ( + <div className={styles.container}> + <div className={styles.messagesWrapper}> + <Messages channelId={channelId} onReply={handleReply} /> + </div> + <div className={styles.inputArea}> + <TypingUsers channelId={channelId} /> + <ChannelTextarea channelId={channelId} replyTo={replyTo} onCancelReply={handleCancelReply} /> + </div> + </div> + ); +} diff --git a/packages/shared/src/components/channel/ChannelDetailsDrawer.module.css b/packages/shared/src/components/channel/ChannelDetailsDrawer.module.css new file mode 100644 index 0000000..a89ace4 --- /dev/null +++ b/packages/shared/src/components/channel/ChannelDetailsDrawer.module.css @@ -0,0 +1,255 @@ +/* ── Channel details drawer — header ─────────────────────────────────── */ + +.header { + display: flex; + align-items: center; + gap: 12px; + padding: 16px 20px 12px; + border-bottom: 1px solid var(--user-area-divider-color, rgba(255, 255, 255, 0.06)); +} + +.headerMain { + display: flex; + align-items: center; + gap: 12px; + flex: 1; + min-width: 0; +} + +.headerIcon { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + background-color: var(--background-tertiary, rgba(255, 255, 255, 0.05)); + color: var(--text-secondary); + flex-shrink: 0; +} + +.headerTextBlock { + display: flex; + flex-direction: column; + min-width: 0; + flex: 1; +} + +.headerTitleRow { + display: flex; + align-items: center; + gap: 6px; + min-width: 0; +} + +.headerTitleIcon { + color: var(--text-secondary); + flex-shrink: 0; +} + +.headerTitle { + font-size: 1rem; + font-weight: 600; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; +} + +.headerSubtitle { + font-size: 0.8125rem; + color: var(--text-tertiary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-top: 2px; +} + +.headerCloseButton { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + flex-shrink: 0; + background: transparent; + border: none; + color: var(--text-secondary); + border-radius: 50%; + cursor: pointer; + transition: background-color 0.15s, color 0.15s; +} + +.headerCloseButton:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.04)); + color: var(--text-primary); +} + +/* ── Quick actions row (Mute / Search / More) ────────────────────────── */ + +.quickActions { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; + padding: 16px 20px; +} + +.quickAction { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 6px; + padding: 12px 8px; + background-color: var(--background-tertiary, rgba(255, 255, 255, 0.04)); + border: none; + border-radius: 12px; + color: var(--text-primary); + cursor: pointer; + font: inherit; + font-size: 0.75rem; + font-weight: 500; + transition: background-color 0.15s; +} + +.quickAction:hover:not(:disabled) { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.08)); +} + +.quickAction:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.quickActionIcon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + color: var(--text-secondary); +} + +.quickActionLabel { + line-height: 1; +} + +/* ── Tab bar (Members / Pins) ──────────────────────────────────────── + Flex row of two equal-width tab buttons. Each button is a column + layout: icon + label side by side, padded to ~12px tall. The + active tab swaps the text + icon colour to --brand-primary-light + and draws a 2px underline via an absolutely-positioned ::after + that hangs off the bottom edge of the button and overlaps the + row's 1px divider. Inactive tabs stay muted. */ + +.tabBar { + position: relative; + display: flex; + align-items: stretch; + padding: 0; + border-bottom: 1px solid var(--user-area-divider-color, rgba(255, 255, 255, 0.06)); +} + +.tabButton { + position: relative; + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 14px 8px; + background: none; + border: none; + color: var(--text-secondary, var(--text-primary-muted)); + cursor: pointer; + font: inherit; + font-size: 0.9375rem; + font-weight: 600; + transition: color 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.tabButton svg { + flex-shrink: 0; + color: var(--text-secondary, var(--text-primary-muted)); + transition: color 0.15s; +} + +.tabButtonActive { + color: var(--brand-primary-light); +} + +.tabButtonActive svg { + color: var(--brand-primary-light); +} + +/* Active tab underline — a 2px bar drawn at the bottom of the button, + translated down 1px so it overlaps and visually replaces the tab + row's divider underneath that tab. */ +.tabButtonActive::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: -1px; + height: 2px; + background-color: var(--brand-primary-light); + border-radius: 2px 2px 0 0; +} + +.membersWrapper { + padding: 1rem; +} + +.emptyState { + padding: 16px 20px; + font-size: 0.875rem; + color: var(--text-tertiary); + text-align: center; +} + +/* ── Pins tab ───────────────────────────────────────────────────────*/ + +.pinsList { + display: flex; + flex-direction: column; + padding: 4px 0 16px; +} + +.pinsLoading { + padding: 16px 20px; + text-align: center; + font-size: 0.8125rem; + color: var(--text-primary-muted); +} + +/* Fluxer-style empty state: flag icon + "You've reached the end" + title + explanatory body. Matches the reference screenshot. */ +.pinsEmpty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + padding: 48px 32px 32px; + text-align: center; +} + +.pinsEmptyIcon { + color: var(--text-primary-muted); + margin-bottom: 4px; +} + +.pinsEmptyTitle { + font-size: 1.125rem; + font-weight: 800; + color: var(--text-primary); +} + +.pinsEmptyBody { + font-size: 0.875rem; + color: var(--text-primary-muted); + line-height: 1.4; + max-width: 280px; +} diff --git a/packages/shared/src/components/channel/ChannelDetailsDrawer.tsx b/packages/shared/src/components/channel/ChannelDetailsDrawer.tsx new file mode 100644 index 0000000..c2b41dc --- /dev/null +++ b/packages/shared/src/components/channel/ChannelDetailsDrawer.tsx @@ -0,0 +1,301 @@ +/** + * ChannelDetailsDrawer — mobile bottom sheet opened by tapping the + * channel name in the ChannelHeader. Shows a Members/Pins tab switcher + * with the channel's member list and pinned messages. + */ +import { useEffect, useMemo, useState } from 'react'; +import { Users, PushPin } from '@phosphor-icons/react'; +import { useQuery } from 'convex/react'; +import { BottomSheet } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import { usePlatform } from '../../platform'; +import { MemberListContainer } from '../member/MemberListContainer'; +import { PinnedMessageRow, ReachedEndNotice, type PinnedMessage } from './PinnedMessageRow'; +import type { AttachmentMetadata } from './EncryptedAttachment'; + +type DrawerTab = 'members' | 'pins'; + +interface ChannelDetailsDrawerProps { + isOpen: boolean; + onClose: () => void; + channelId: string; + channelName?: string; + channelType?: string; +} + +const tabBarStyle: React.CSSProperties = { + display: 'flex', + gap: 8, + padding: '4px 12px 12px', + borderBottom: '1px solid var(--border-subtle, rgba(255,255,255,0.06))', +}; + +const tabButtonStyle = (active: boolean): React.CSSProperties => ({ + flex: 1, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + gap: 8, + padding: '10px 12px', + borderRadius: 10, + border: 'none', + background: active ? 'var(--bg-hover, rgba(255,255,255,0.08))' : 'transparent', + color: active ? 'var(--text-primary, #fff)' : 'var(--text-muted, #a0a0a8)', + fontSize: 14, + fontWeight: 600, + cursor: 'pointer', +}); + +const tabBodyStyle: React.CSSProperties = { + padding: '12px', + minHeight: 240, +}; + +const emptyStateStyle: React.CSSProperties = { + padding: '24px 12px', + textAlign: 'center', + color: 'var(--text-muted, #a0a0a8)', + fontSize: 14, +}; + +export function ChannelDetailsDrawer({ + isOpen, + onClose, + channelId, + channelName, + channelType, +}: ChannelDetailsDrawerProps) { + const [activeTab, setActiveTab] = useState<DrawerTab>('members'); + + // Reset to Members whenever the drawer re-opens so a previously + // selected Pins tab doesn't follow across channel changes. + useEffect(() => { + if (isOpen) setActiveTab('members'); + }, [isOpen, channelId]); + + const title = channelName ?? 'Channel'; + + return ( + <BottomSheet isOpen={isOpen} onClose={onClose} title={title}> + <div style={tabBarStyle}> + <button + type="button" + style={tabButtonStyle(activeTab === 'members')} + onClick={() => setActiveTab('members')} + role="tab" + aria-selected={activeTab === 'members'} + > + <Users size={18} weight="fill" /> + <span>Members</span> + </button> + <button + type="button" + style={tabButtonStyle(activeTab === 'pins')} + onClick={() => setActiveTab('pins')} + role="tab" + aria-selected={activeTab === 'pins'} + > + <PushPin size={18} weight="fill" /> + <span>Pins</span> + </button> + </div> + + <div style={tabBodyStyle}> + {activeTab === 'members' ? ( + <MemberListContainer channelId={channelId} variant="drawer" /> + ) : ( + <PinsTabContent + channelId={channelId} + isOpen={isOpen && activeTab === 'pins'} + onClose={onClose} + /> + )} + {/* channelType currently unused; reserved for future voice-specific UI */} + <span style={{ display: 'none' }}>{channelType}</span> + </div> + </BottomSheet> + ); +} + +// ── Pins tab ──────────────────────────────────────────────────────── + +const TAG_LENGTH = 32; + +interface PinsTabContentProps { + channelId: string; + isOpen: boolean; + onClose: () => void; +} + +function PinsTabContent({ channelId, isOpen, onClose }: PinsTabContentProps) { + const { crypto } = usePlatform(); + + const userId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const privateKeyPem = + typeof sessionStorage !== 'undefined' + ? sessionStorage.getItem('privateKey') + : null; + + const allKeys = useQuery( + api.channelKeys.getKeysForUser, + userId && isOpen ? ({ userId: userId as any } as any) : 'skip', + ); + + // Merge all encrypted key bundles → { channelId: keyHex } + const [channelKey, setChannelKey] = useState<string | null>(null); + useEffect(() => { + let cancelled = false; + if (!allKeys || !privateKeyPem) { + setChannelKey(null); + return; + } + (async () => { + const merged: Record<string, string> = {}; + for (const item of allKeys) { + try { + const bundleJson = await crypto.privateDecrypt( + privateKeyPem, + (item as any).encrypted_key_bundle, + ); + Object.assign(merged, JSON.parse(bundleJson)); + } catch (err) { + console.error('Failed to decrypt key bundle:', err); + } + } + if (cancelled) return; + setChannelKey(merged[channelId] ?? null); + })(); + return () => { + cancelled = true; + }; + }, [allKeys, privateKeyPem, channelId, crypto]); + + const pinnedRaw = useQuery( + api.messages.listPinned, + isOpen + ? ({ + channelId: channelId as any, + userId: (userId as any) ?? undefined, + } as any) + : 'skip', + ); + + const [decryptedMap, setDecryptedMap] = useState<Map<string, string>>(new Map()); + + useEffect(() => { + if (!channelKey || !pinnedRaw) return; + let cancelled = false; + (async () => { + const next = new Map(decryptedMap); + let changed = false; + for (const msg of pinnedRaw as any[]) { + const id = msg.id as string; + if (next.has(id)) continue; + if (!msg.ciphertext || msg.ciphertext.length < TAG_LENGTH) { + next.set(id, '[Invalid Encrypted Message]'); + changed = true; + continue; + } + const tag = msg.ciphertext.slice(-TAG_LENGTH); + const contentHex = msg.ciphertext.slice(0, -TAG_LENGTH); + try { + const plaintext = await crypto.decryptData( + contentHex, + channelKey, + msg.nonce, + tag, + ); + if (cancelled) return; + next.set(id, plaintext); + changed = true; + } catch { + next.set(id, '[Unable to decrypt]'); + changed = true; + } + } + if (changed && !cancelled) setDecryptedMap(next); + })(); + return () => { + cancelled = true; + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [pinnedRaw, channelKey]); + + const pinned: PinnedMessage[] = useMemo(() => { + if (!pinnedRaw) return []; + return (pinnedRaw as any[]) + .slice() + .sort((a, b) => { + const at = a.created_at ? new Date(a.created_at).getTime() : 0; + const bt = b.created_at ? new Date(b.created_at).getTime() : 0; + return bt - at; + }) + .map((msg) => { + const id = msg.id as string; + const rawContent = decryptedMap.get(id) ?? ''; + let text = rawContent; + const attachments: AttachmentMetadata[] = []; + try { + const parsed = JSON.parse(rawContent); + if (parsed && typeof parsed === 'object') { + if (Array.isArray(parsed)) { + for (const item of parsed) { + if (item?.type === 'attachment' && item.url && item.key && item.iv) { + attachments.push(item as AttachmentMetadata); + } + } + text = ''; + } else if (parsed.type === 'attachment' && parsed.url && parsed.key && parsed.iv) { + attachments.push(parsed as AttachmentMetadata); + text = ''; + } else if (parsed.text !== undefined) { + text = String(parsed.text); + } + } + } catch { + // plain text — leave as-is + } + return { + id, + authorName: msg.displayName || msg.username || 'User', + authorAvatarUrl: msg.avatarUrl ?? null, + content: text, + timestamp: msg.created_at + ? new Date(msg.created_at).getTime() + : Date.now(), + attachments, + } as PinnedMessage; + }); + }, [pinnedRaw, decryptedMap]); + + const handleJumpTo = (messageId: string) => { + window.dispatchEvent( + new CustomEvent('brycord:scroll-to-message', { + detail: { channelId, messageId }, + }), + ); + onClose(); + }; + + if (pinnedRaw === undefined) { + return <div style={emptyStateStyle}>Loading pinned messages…</div>; + } + + if (pinned.length === 0) { + return <div style={emptyStateStyle}>No pinned messages yet.</div>; + } + + return ( + <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}> + {pinned.map((msg) => ( + <PinnedMessageRow + key={msg.id} + message={msg} + onClick={() => handleJumpTo(msg.id)} + /> + ))} + <ReachedEndNotice /> + </div> + ); +} diff --git a/packages/shared/src/components/channel/ChannelHeader.module.css b/packages/shared/src/components/channel/ChannelHeader.module.css new file mode 100644 index 0000000..6c71613 --- /dev/null +++ b/packages/shared/src/components/channel/ChannelHeader.module.css @@ -0,0 +1,405 @@ +.container { + display: grid; + grid-template-columns: 1fr auto; + align-items: center; + height: var(--layout-header-height, 3.5rem); + min-height: var(--layout-header-height, 3.5rem); + padding: 0 16px; + gap: 16px; + border-bottom: 1px solid var(--user-area-divider-color); + background-color: var(--background-secondary-lighter, var(--background-primary)); + z-index: var(--z-index-elevated-3, 30); + flex-shrink: 0; +} + +.channelInfo { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; + overflow: hidden; +} + +/* Desktop 1:1 DM — converts the channelInfo row into a tappable + button that opens the other user's profile modal. Resets the + native button chrome and adds a subtle hover background so the + click target reads as interactive. */ +.channelInfoClickable { + padding: 4px 8px; + margin-left: -8px; + background: transparent; + border: none; + border-radius: var(--radius-md, 6px); + color: inherit; + font: inherit; + text-align: left; + cursor: pointer; + transition: background-color 0.15s ease; +} + +.channelInfoClickable:hover { + background-color: color-mix(in srgb, var(--interactive-normal) 10%, transparent); +} + +.icon { + color: var(--channel-icon, var(--text-muted)); + flex-shrink: 0; +} + +/* Wrapper for the small user-avatar rendered in place of the + channel icon when viewing a 1:1 DM. Zeroes the layout chrome + around the Avatar so it sits flush in the header row like the + Hash icon it replaces. */ +.dmAvatar { + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + margin-right: 2px; +} + +/* Mobile-only back button — appears on the left of the channel title + when SelectionStore.isMobileViewport is true. Navigates to the parent + space's channel list (or the DM list). */ +.backButton { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + margin-right: 4px; + margin-left: -8px; + background: transparent; + border: none; + border-radius: var(--radius-md, 6px); + color: var(--interactive-normal); + cursor: pointer; + flex-shrink: 0; + transition: color 0.15s ease, background-color 0.15s ease; +} + +.backButton:hover { + background-color: color-mix(in srgb, var(--interactive-normal) 10%, transparent); + color: var(--interactive-hover); +} + +/* Mobile-only channel info wrapper — holds the back button + the + tappable channel-name-with-caret button side by side. */ +.channelInfoMobile { + display: flex; + align-items: center; + gap: 4px; + min-width: 0; + overflow: hidden; +} + +.channelInfoButton { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; + padding: 6px 8px; + border-radius: var(--radius-md, 6px); + background: transparent; + border: none; + color: inherit; + font: inherit; + text-align: left; + cursor: pointer; + transition: background-color 0.15s ease; +} + +.channelInfoButton:hover { + background-color: color-mix(in srgb, var(--interactive-normal) 10%, transparent); +} + +.channelInfoButton .name { + font-weight: 600; + font-size: 1rem; + color: var(--text-primary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; +} + +.caretRight { + color: var(--text-tertiary); + flex-shrink: 0; +} + +.name { + font-weight: 600; + font-size: 1rem; + color: var(--text-primary); + margin: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + line-height: 1.5rem; + max-height: 1.5rem; +} + +.divider { + width: 1px; + height: 24px; + background-color: var(--background-modifier-accent); + flex-shrink: 0; +} + +.topic { + font-size: 0.875rem; + color: var(--text-secondary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; +} + +.headerButtons { + display: flex; + align-items: center; + gap: 4px; + flex-shrink: 0; +} + +.headerButton { + position: relative; + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; + border-radius: var(--radius-md); + color: var(--interactive-normal); + cursor: pointer; + transition: color 0.15s ease, background-color 0.15s ease; +} + +.headerButton:hover { + background-color: color-mix(in srgb, var(--interactive-normal) 10%, transparent); + color: var(--interactive-hover); +} + +.headerButtonActive { + color: var(--interactive-active); +} + +/* Circular-background variant used for the DM Phone / Video call + buttons. Matches the Fluxer reference image where the icons sit + inside a solid dark circle. Uses --guild-list-foreground (same + shade as the empty server icon and the DM sidebar action pills) + for visual consistency with the rest of the DM chrome. */ +.headerButtonCircle { + width: 36px; + height: 36px; + border-radius: 50%; + background-color: var(--guild-list-foreground); + color: var(--text-primary); +} + +.headerButtonCircle:hover { + background-color: var(--guild-list-foreground); + filter: brightness(1.15); + color: var(--text-primary); +} + +/* Disabled appearance for the members-list toggle at narrow viewports. + We use aria-disabled rather than the native disabled attribute so the + Tooltip wrapper can still fire on hover and explain WHY the button is + unavailable. The class below overrides the hover state so it doesn't + lie about being interactive. */ +.headerButtonDisabled { + opacity: 0.5; + cursor: not-allowed; + color: var(--interactive-normal); +} + +.headerButtonDisabled:hover { + background: transparent; + color: var(--interactive-normal); +} + +/* ── Search bar (always visible) ─────────────────────────────── */ + +.searchBarWrapper { + position: relative; +} + +.searchBar { + display: flex; + align-items: center; + gap: 6px; + width: 244px; + height: 36px; + padding: 0 8px; + border-radius: var(--radius-xl, 12px); + border: 1px solid var(--background-modifier-accent); + background-color: var(--background-tertiary); + transition: border-color 0.1s; +} + +.searchBar:focus-within { + border-color: var(--background-modifier-accent); +} + +.searchBarIcon { + flex-shrink: 0; + color: var(--text-tertiary); +} + +.searchBarInput { + flex: 1; + border: none; + background: none; + color: var(--text-primary); + font-size: 0.875rem; + font-family: inherit; + padding: 0; + outline: none; + min-width: 0; +} + +.searchBarInput:focus, +.searchBarInput:focus-visible { + outline: none; + box-shadow: none; +} + +.searchBarInput::placeholder { + color: var(--text-tertiary); +} + +.searchBarClear { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + flex-shrink: 0; + border: none; + border-radius: var(--radius-md); + background: transparent; + color: var(--text-tertiary); + cursor: pointer; +} + +.searchBarClear:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +/* ── Filter dropdown ─────────────────────────────────────────── */ + +.filterDropdown { + position: absolute; + top: calc(100% + 8px); + right: 0; + width: 380px; + border-radius: var(--radius-xl, 12px); + border: 1px solid var(--background-modifier-accent); + background-color: var(--background-floating, var(--background-tertiary)); + box-shadow: 0 2px 0 0 rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.28); + z-index: 1000; + padding: 4px; +} + +.filterSection { + display: flex; + flex-direction: column; +} + +.filterSectionHeader { + display: flex; + align-items: center; + gap: 6px; + padding: 6px 8px; + font-size: 0.75rem; + font-weight: 600; + color: var(--text-tertiary); + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.filterSectionIcon { + display: flex; + align-items: center; + color: var(--text-tertiary); +} + +.filterOption { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + padding: 4px 8px; + border-radius: var(--radius-md); + background: none; + border: none; + cursor: pointer; + text-align: left; + font-family: inherit; + font-size: 0.875rem; + color: var(--text-primary); + transition: background-color 0.1s; +} + +.filterOption:hover { + background-color: var(--background-modifier-hover); +} + +.filterBadge { + display: inline-block; + padding: 2px 6px; + border-radius: 4px; + background-color: var(--background-secondary); + color: var(--text-primary); + font-size: 0.8125rem; + font-weight: 500; + border: 1px solid var(--background-modifier-accent); + flex-shrink: 0; +} + +.filterDesc { + flex: 1; + color: var(--text-tertiary); + font-size: 0.8125rem; +} + +.filterPlus { + flex-shrink: 0; + color: var(--text-tertiary); +} + +.filterOption:hover .filterPlus { + color: var(--text-primary); +} + +.dmHeaderButton { + display: flex; + align-items: center; + gap: 10px; + padding: 4px 10px 4px 4px; + border-radius: 6px; + background: transparent; + border: none; + color: inherit; + font: inherit; + cursor: pointer; + min-width: 0; +} + +.dmHeaderButton:hover { + background: var(--background-modifier-hover, rgba(255, 255, 255, 0.06)); +} + +.dmHeaderName { + font-size: 16px; + font-weight: 600; + color: var(--text-primary, #fff); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/packages/shared/src/components/channel/ChannelHeader.tsx b/packages/shared/src/components/channel/ChannelHeader.tsx new file mode 100644 index 0000000..1981c7c --- /dev/null +++ b/packages/shared/src/components/channel/ChannelHeader.tsx @@ -0,0 +1,497 @@ +import { + ArrowLeft, + CaretRight, + Funnel, + Hash, + MagnifyingGlass, + Phone, + Plus, + PushPin, + SpeakerHigh, + Users, + VideoCamera, + X, +} from '@phosphor-icons/react'; +import { Avatar, Tooltip } from '@discord-clone/ui'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import { useMutation, useQuery } from 'convex/react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { useIsMobile } from '../../hooks/useIsMobile'; +import { usePlatform } from '../../platform'; +import { useOnlineUsers } from '../../contexts/PresenceContext'; +import { api } from '../../../../../convex/_generated/api'; +import { MemberProfileModal } from '../member/MemberProfileModal'; +import { MobileMemberProfileSheet } from '../member/MobileMemberProfileSheet'; +import { useKeybinds } from '../../contexts/KeybindContext'; +import { ChannelHeaderPinsPopover } from './ChannelHeaderPinsPopover'; +import styles from './ChannelHeader.module.css'; + +const SEARCH_FILTERS = [ + { key: 'from:', desc: 'user' }, + { key: 'mentions:', desc: 'user' }, + { key: 'has:', desc: 'link, embed or file' }, + { key: 'before:', desc: 'specific date' }, + { key: 'during:', desc: 'specific date' }, + { key: 'after:', desc: 'specific date' }, + { key: 'pinned:', desc: 'true or false' }, +]; + +interface ChannelLike { + _id?: string; + name?: string; + type?: string; + topic?: string; +} + +interface ChannelHeaderProps { + channel?: ChannelLike | null; + serverId?: string; + onOpenChannelDetails?: () => void; + onOpenSearchDrawer?: () => void; + membersVisible?: boolean; + onToggleMembers?: () => void; + /** Hide the members button entirely. DMs use this since a 1:1 + * conversation doesn't need a sidebar member list. */ + hideMembersButton?: boolean; + /** When true, the viewport is below the members-list breakpoint. The + * Users button becomes a no-op and gets disabled styling. */ + isNarrow?: boolean; + /** Controlled search input. State lives in ChannelView so the sibling + * SearchPanel can read the same query. */ + searchQuery?: string; + onSearchChange?: (next: string) => void; + onSearchClear?: () => void; +} + +export function ChannelHeader({ + channel, + onOpenChannelDetails, + membersVisible = true, + onToggleMembers, + hideMembersButton = false, + isNarrow = false, + searchQuery = '', + onSearchChange, + onSearchClear, +}: ChannelHeaderProps) { + const isVoice = channel?.type === 'voice'; + const isDM = channel?.type === 'dm'; + + const isMobile = useIsMobile(); + const navigate = useNavigate(); + const location = useLocation(); + const { crypto } = usePlatform(); + const { resolveStatus } = useOnlineUsers(); + const keybinds = useKeybinds(); + + const pinsButtonRef = useRef<HTMLButtonElement>(null); + const dmHeaderButtonRef = useRef<HTMLButtonElement>(null); + const [pinsAnchor, setPinsAnchor] = useState<DOMRect | null>(null); + const [profileOpen, setProfileOpen] = useState(false); + const [showFilters, setShowFilters] = useState(false); + const searchInputRef = useRef<HTMLInputElement>(null); + + // ── DM participant lookup ────────────────────────────────────── + // When this channel is a DM we pull the other participant out of + // `api.dms.listDMs` and resolve their profile via getPublicKeys. + const myUserId = + typeof localStorage !== 'undefined' + ? localStorage.getItem('userId') + : null; + const dmRows = useQuery( + api.dms.listDMs, + isDM && myUserId ? { userId: myUserId as any } : 'skip', + ); + const allUsers = useQuery( + api.auth.getPublicKeys, + isDM ? {} : 'skip', + ) ?? []; + + const otherParticipant = useMemo(() => { + if (!isDM || !dmRows || !channel?._id) return null; + const row = (dmRows as any[]).find((r) => r.channel_id === channel._id); + if (!row) return null; + const profile = allUsers.find((u) => u.id === row.other_user_id); + const storedStatus = + (profile?.status as string | undefined) || + (row.other_user_status as string | undefined) || + 'offline'; + const liveStatus = resolveStatus(storedStatus, row.other_user_id); + // DM header uses the raw username (not the server display + // name) so people always know who they're actually talking to. + const username = + (profile?.username as string | undefined) || + (row.other_username as string | undefined) || + 'user'; + return { + userId: row.other_user_id as string, + displayName: username, + username, + avatarUrl: profile?.avatarUrl ?? row.other_user_avatar_url ?? null, + status: liveStatus, + }; + }, [isDM, dmRows, allUsers, channel?._id, resolveStatus]); + + const rotateDMKey = useMutation(api.channelKeys.rotateDMKey); + + const handleRotateDMKey = async () => { + if (!channel?._id || !otherParticipant || !myUserId) { + throw new Error("Can't rotate — missing DM context."); + } + const privateKey = + typeof sessionStorage !== 'undefined' + ? sessionStorage.getItem('privateKey') + : null; + if (!privateKey) { + throw new Error('No session key available.'); + } + const me = allUsers.find((u) => u.id === myUserId); + const other = allUsers.find((u) => u.id === otherParticipant.userId); + if (!me?.public_identity_key || !other?.public_identity_key) { + throw new Error("One participant's public key is missing."); + } + const newKeyHex = await crypto.randomBytes(32); + const plaintext = JSON.stringify({ [channel._id]: newKeyHex }); + const [myBundle, otherBundle] = await Promise.all([ + crypto.publicEncrypt(me.public_identity_key, plaintext), + crypto.publicEncrypt(other.public_identity_key, plaintext), + ]); + await rotateDMKey({ + channelId: channel._id as any, + initiatorUserId: myUserId as any, + entries: [ + { userId: myUserId as any, encryptedKeyBundle: myBundle }, + { + userId: otherParticipant.userId as any, + encryptedKeyBundle: otherBundle, + }, + ], + }); + }; + + const handleOpenProfile = () => { + setProfileOpen(true); + }; + + // Wire keybinds that affect this header: toggle pins popover, + // toggle member list. The KeybindProvider dispatches + // `brycord:keybind:<id>` events on the window. + useEffect(() => { + const onTogglePins = () => handleTogglePins(); + const onToggleMembers = () => { + if (hideMembersButton) return; + handleToggleMembers(); + }; + window.addEventListener('brycord:keybind:popouts.openPins', onTogglePins); + window.addEventListener( + 'brycord:keybind:popouts.toggleMembers', + onToggleMembers, + ); + return () => { + window.removeEventListener('brycord:keybind:popouts.openPins', onTogglePins); + window.removeEventListener( + 'brycord:keybind:popouts.toggleMembers', + onToggleMembers, + ); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [hideMembersButton]); + + const setSearchQuery = (next: string) => { + onSearchChange?.(next); + }; + const clearSearchQuery = () => { + if (onSearchClear) onSearchClear(); + else onSearchChange?.(''); + }; + + const onInputBlur = () => { + // 200ms delay — long enough for the chip click to register + setTimeout(() => setShowFilters(false), 200); + }; + + const handleFilterClick = (key: string) => { + setSearchQuery(searchQuery ? `${searchQuery} ${key}` : key); + requestAnimationFrame(() => searchInputRef.current?.focus()); + }; + + /** + * On mobile, the back button strips the trailing `/channelId` segment + * from the current path so we return to the channel list view. For + * `/channels/home/:id` this yields `/channels/home`; for + * `/channels/@me/:id` → `/channels/@me`. Falls back to `/channels/@me` + * if the path doesn't look like a channel URL. + */ + const handleMobileBack = () => { + const path = location.pathname; + const match = path.match(/^(\/channels\/(?:home|@me|[^/]+))(?:\/[^/]+)?$/); + navigate(match ? match[1] : '/channels/@me'); + }; + + const handleTogglePins = () => { + if (pinsAnchor) { + setPinsAnchor(null); + return; + } + const rect = pinsButtonRef.current?.getBoundingClientRect(); + if (rect) setPinsAnchor(rect); + }; + + const handleToggleMembers = () => { + if (isNarrow) return; + if (onToggleMembers) { + onToggleMembers(); + } else { + window.dispatchEvent(new CustomEvent('brycord:toggle-members')); + } + }; + + if (isMobile) { + return ( + <div className={styles.container}> + <div className={styles.channelInfoMobile}> + <button + type="button" + className={styles.backButton} + onClick={handleMobileBack} + aria-label="Back to channels" + > + <ArrowLeft size={20} weight="bold" /> + </button> + {isDM && otherParticipant ? ( + <button + ref={dmHeaderButtonRef} + type="button" + className={styles.channelInfoButton} + onClick={handleOpenProfile} + > + <Avatar + src={otherParticipant.avatarUrl} + size={26} + fallback={otherParticipant.displayName} + status={otherParticipant.status as any} + /> + <span className={styles.name}> + {otherParticipant.displayName} + </span> + <CaretRight size={14} weight="bold" className={styles.caretRight} /> + </button> + ) : ( + <button + type="button" + className={styles.channelInfoButton} + onClick={onOpenChannelDetails} + > + {isVoice ? ( + <SpeakerHigh size={22} className={styles.icon} /> + ) : ( + <Hash size={22} weight="bold" className={styles.icon} /> + )} + <span className={styles.name}>{channel?.name || 'Channel'}</span> + <CaretRight size={14} weight="bold" className={styles.caretRight} /> + </button> + )} + </div> + {otherParticipant && ( + <MemberProfileModal + isOpen={profileOpen} + onClose={() => setProfileOpen(false)} + member={{ userId: otherParticipant.userId }} + onRotateKey={handleRotateDMKey} + /> + )} + </div> + ); + } + + return ( + <div className={styles.container}> + {isDM && otherParticipant ? ( + <button + ref={dmHeaderButtonRef} + type="button" + className={styles.dmHeaderButton} + onClick={handleOpenProfile} + aria-label={`Open ${otherParticipant.displayName}'s profile`} + > + <Avatar + src={otherParticipant.avatarUrl} + size={28} + fallback={otherParticipant.displayName} + status={otherParticipant.status as any} + /> + <span className={styles.dmHeaderName}> + {otherParticipant.displayName} + </span> + </button> + ) : ( + <div className={styles.channelInfo}> + {isVoice ? ( + <SpeakerHigh size={20} className={styles.icon} /> + ) : ( + <Hash size={20} weight="bold" className={styles.icon} /> + )} + <span className={styles.name}>{channel?.name || 'Channel'}</span> + {channel?.topic && ( + <> + <div className={styles.divider} /> + <span className={styles.topic}>{channel.topic}</span> + </> + )} + </div> + )} + <div className={styles.headerButtons}> + {isDM && ( + <> + <Tooltip content="Start Voice Call" placement="bottom"> + <button + type="button" + className={styles.headerButton} + aria-label="Start Voice Call" + disabled + title="Voice calls coming soon" + > + <Phone size={20} /> + </button> + </Tooltip> + <Tooltip content="Start Video Call" placement="bottom"> + <button + type="button" + className={styles.headerButton} + aria-label="Start Video Call" + disabled + title="Video calls coming soon" + > + <VideoCamera size={20} /> + </button> + </Tooltip> + </> + )} + <Tooltip + content="Pinned Messages" + shortcut={keybinds.getCombo('popouts.openPins')} + placement="bottom" + > + <button + ref={pinsButtonRef} + type="button" + className={`${styles.headerButton} ${pinsAnchor ? styles.headerButtonActive : ''}`} + aria-label="Pinned" + onClick={handleTogglePins} + > + <PushPin size={20} /> + </button> + </Tooltip> + {!hideMembersButton && ( + <Tooltip + content={ + isNarrow + ? 'Window too narrow for member list' + : 'Member List' + } + shortcut={ + isNarrow ? undefined : keybinds.getCombo('popouts.toggleMembers') + } + placement="bottom" + > + <button + type="button" + className={`${styles.headerButton} ${ + membersVisible && !isNarrow ? styles.headerButtonActive : '' + } ${isNarrow ? styles.headerButtonDisabled : ''}`} + aria-label="Members" + aria-disabled={isNarrow || undefined} + onClick={handleToggleMembers} + > + <Users size={20} /> + </button> + </Tooltip> + )} + + <div className={styles.searchBarWrapper}> + <div className={styles.searchBar}> + <MagnifyingGlass + size={16} + weight="regular" + className={styles.searchBarIcon} + /> + <input + ref={searchInputRef} + className={styles.searchBarInput} + placeholder="Search messages" + value={searchQuery} + onChange={(e) => setSearchQuery(e.target.value)} + onFocus={() => setShowFilters(true)} + onBlur={onInputBlur} + /> + {searchQuery && ( + <button + type="button" + className={styles.searchBarClear} + onClick={clearSearchQuery} + aria-label="Clear search" + > + <X size={14} /> + </button> + )} + </div> + {showFilters && !searchQuery && ( + <div className={styles.filterDropdown}> + <div className={styles.filterSection}> + <div className={styles.filterSectionHeader}> + <span className={styles.filterSectionIcon}> + <Funnel size={12} /> + </span> + <span>Search Filters</span> + </div> + {SEARCH_FILTERS.map((f) => ( + <button + key={f.key} + type="button" + className={styles.filterOption} + onMouseDown={(e) => { + // Use mousedown not click so it fires before the input blur + e.preventDefault(); + handleFilterClick(f.key); + }} + > + <span className={styles.filterBadge}>{f.key}</span> + <span className={styles.filterDesc}>— {f.desc}</span> + <Plus size={14} className={styles.filterPlus} /> + </button> + ))} + </div> + </div> + )} + </div> + </div> + + {channel?._id && ( + <ChannelHeaderPinsPopover + isOpen={pinsAnchor !== null} + anchorRect={pinsAnchor} + channelId={channel._id} + onClose={() => setPinsAnchor(null)} + /> + )} + + {otherParticipant && ( + isMobile ? ( + <MobileMemberProfileSheet + isOpen={profileOpen} + onClose={() => setProfileOpen(false)} + member={{ userId: otherParticipant.userId }} + onRotateKey={handleRotateDMKey} + /> + ) : ( + <MemberProfileModal + isOpen={profileOpen} + onClose={() => setProfileOpen(false)} + member={{ userId: otherParticipant.userId }} + onRotateKey={handleRotateDMKey} + /> + ) + )} + </div> + ); +} diff --git a/packages/shared/src/components/channel/ChannelHeaderPinsPopover.module.css b/packages/shared/src/components/channel/ChannelHeaderPinsPopover.module.css new file mode 100644 index 0000000..02d0262 --- /dev/null +++ b/packages/shared/src/components/channel/ChannelHeaderPinsPopover.module.css @@ -0,0 +1,165 @@ +/* ── Pinned Messages popover ───────────────────────────────────────── + Portal-rendered dropdown anchored to the ChannelHeader pin icon + button. Shows the channel's pinned messages in reverse + chronological order, same style as Discord / Fluxer. */ + +.overlay { + position: fixed; + inset: 0; + z-index: var(--z-index-modal, 10000); + pointer-events: none; +} + +.popover { + /* Scoped custom properties for this component's sizing + constants, so they live next to the component that uses them + instead of cluttering global.css. The track-size + thumb + formula follow Fluxer's auto-hide scroller pattern: an 8px + track with a 4px visible thumb (4px = 8px track - 2px border + on each side, drawn via background-clip: padding-box). The + scoped --scrollbar-thumb-bg override resets the global + color-mix value to the literal rgba Fluxer uses here. */ + --pins-popout-header-height: 68px; + --scroller-track-size: 8px; + --scrollbar-thumb-bg: rgba(121, 122, 124, 0.4); + + position: absolute; + pointer-events: auto; + background-color: var(--background-primary); + border: 1px solid var(--background-header-secondary); + border-radius: 12px; + box-shadow: + 0 0 0 1px rgba(0, 0, 0, 0.2), + 0 8px 24px -4px rgba(0, 0, 0, 0.45), + 0 20px 48px -8px rgba(0, 0, 0, 0.3); + width: 480px; + max-width: calc(100vw - 24px); + max-height: min(calc(100vh - 120px), 720px); + display: flex; + flex-direction: column; + overflow: hidden; +} + +.header { + display: flex; + align-items: center; + gap: 10px; + padding: 14px 16px; + min-height: var(--pins-popout-header-height); + flex-shrink: 0; + box-sizing: border-box; +} + +.headerIcon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + color: var(--text-primary); + flex-shrink: 0; +} + +.title { + font-weight: 600; + font-size: 1rem; + color: var(--text-primary); + line-height: 1.25; + margin: 0; +} + +.body { + flex: 1; + min-height: 0; + overflow-y: auto; + padding: 4px 0; + + /* Firefox — thin scrollbar, transparent thumb by default, track + transparent always. Flips to the real thumb color on hover. */ + scrollbar-width: thin; + scrollbar-color: transparent transparent; + transition: scrollbar-color 0.15s ease; +} + +.body:hover { + scrollbar-color: var(--scrollbar-thumb-bg) transparent; +} + +/* WebKit — auto-hide scrollbar. Track stays transparent; thumb + background is transparent by default so the bar is invisible, + and fades in only on hover. The transparent border + padding-box + background-clip collapses the visible thumb width to + max(2px, track-size - 4px) — 4px visible thumb inside an 8px + track. */ +.body::-webkit-scrollbar { + width: var(--scroller-track-size); + height: var(--scroller-track-size); +} + +.body::-webkit-scrollbar-track { + background-color: transparent; +} + +.body::-webkit-scrollbar-thumb { + background-color: transparent; + border: 2px solid transparent; + border-radius: 999px; + background-clip: padding-box; + min-height: 40px; + transition: background-color 0.15s ease; +} + +.body:hover::-webkit-scrollbar-thumb { + background-color: var(--scrollbar-thumb-bg); +} + +.body::-webkit-scrollbar-corner { + background-color: transparent; +} + +/* Hide the up/down arrow buttons some platforms (notably macOS + Chrome with "Show scroll arrows" enabled) render at the ends + of the track. Fluxer and Discord both suppress these for a + cleaner look. */ +.body::-webkit-scrollbar-button { + display: none; + width: 0; + height: 0; +} + +.loading { + padding: 16px 20px; + text-align: center; + font-size: 0.8125rem; + color: var(--text-primary-muted); +} + +/* Fluxer-style empty state — mirrors the ChannelDetailsDrawer pin + empty state so mobile and desktop feel consistent. */ +.empty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + padding: 40px 24px; + text-align: center; +} + +.emptyIcon { + color: var(--text-primary-muted); + margin-bottom: 4px; +} + +.emptyTitle { + font-size: 1rem; + font-weight: 800; + color: var(--text-primary); +} + +.emptyBody { + font-size: 0.8125rem; + color: var(--text-primary-muted); + line-height: 1.4; + max-width: 280px; +} diff --git a/packages/shared/src/components/channel/ChannelHeaderPinsPopover.tsx b/packages/shared/src/components/channel/ChannelHeaderPinsPopover.tsx new file mode 100644 index 0000000..49f298b --- /dev/null +++ b/packages/shared/src/components/channel/ChannelHeaderPinsPopover.tsx @@ -0,0 +1,274 @@ +/** + * ChannelHeaderPinsPopover — desktop popover anchored to the + * ChannelHeader pin button. Fetches pinned messages from Convex, + * decrypts them using the channel key, and renders a scrollable + * list. Dismisses on outside click or Escape. + */ +import { useEffect, useMemo, useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { PushPin } from '@phosphor-icons/react'; +import { useQuery } from 'convex/react'; +import { api } from '../../../../../convex/_generated/api'; +import { usePlatform } from '../../platform'; +import { PinnedMessageRow, ReachedEndNotice, type PinnedMessage } from './PinnedMessageRow'; +import { PinConfirmationModal } from './PinConfirmationModal'; +import type { AttachmentMetadata } from './EncryptedAttachment'; +import styles from './ChannelHeaderPinsPopover.module.css'; + +interface ChannelHeaderPinsPopoverProps { + isOpen: boolean; + channelId: string; + anchorRect: DOMRect | null; + onClose: () => void; +} + +// Ciphertext format matches Messages.tsx: content + 32-hex-char GCM tag. +const TAG_LENGTH = 32; + +export function ChannelHeaderPinsPopover({ + isOpen, + channelId, + anchorRect, + onClose, +}: ChannelHeaderPinsPopoverProps) { + const { crypto } = usePlatform(); + const ref = useRef<HTMLDivElement>(null); + + // Outside click + Escape dismiss. + useEffect(() => { + if (!isOpen) return; + const handleClick = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) { + onClose(); + } + }; + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + document.addEventListener('mousedown', handleClick); + document.addEventListener('keydown', handleEscape); + return () => { + document.removeEventListener('mousedown', handleClick); + document.removeEventListener('keydown', handleEscape); + }; + }, [isOpen, onClose]); + + // Position the popover below and right-aligned to the pin button, + // clamped to the viewport. + const positionStyle = useMemo<React.CSSProperties>(() => { + if (!anchorRect) return { top: 0, left: 0 }; + const POPOVER_WIDTH = 480; + const POPOVER_MAX_HEIGHT = Math.min(window.innerHeight - 120, 720); + const GAP = 8; + const MARGIN = 12; + + const rawLeft = anchorRect.right - POPOVER_WIDTH; + const left = Math.max( + MARGIN, + Math.min(rawLeft, window.innerWidth - POPOVER_WIDTH - MARGIN), + ); + const rawTop = anchorRect.bottom + GAP; + const top = Math.max( + MARGIN, + Math.min(rawTop, window.innerHeight - POPOVER_MAX_HEIGHT - MARGIN), + ); + return { top, left }; + }, [anchorRect]); + + // Channel key decryption — mirrors Messages.tsx. + const userId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const privateKeyPem = + typeof sessionStorage !== 'undefined' + ? sessionStorage.getItem('privateKey') + : null; + + const allKeys = useQuery( + api.channelKeys.getKeysForUser, + userId && isOpen ? ({ userId: userId as any } as any) : 'skip', + ); + + // Each encrypted_key_bundle decrypts to a JSON object mapping + // channelId → keyHex. Decrypt once per bundle and merge. + const [channelKey, setChannelKey] = useState<string | null>(null); + useEffect(() => { + let cancelled = false; + if (!allKeys || !privateKeyPem) { + setChannelKey(null); + return; + } + (async () => { + const merged: Record<string, string> = {}; + for (const item of allKeys) { + try { + const bundleJson = await crypto.privateDecrypt( + privateKeyPem, + (item as any).encrypted_key_bundle, + ); + Object.assign(merged, JSON.parse(bundleJson)); + } catch (err) { + console.error('Failed to decrypt key bundle:', err); + } + } + if (cancelled) return; + setChannelKey(merged[channelId] ?? null); + })(); + return () => { + cancelled = true; + }; + }, [allKeys, privateKeyPem, channelId, crypto]); + + // Fetch pinned messages. + const pinnedRaw = useQuery( + api.messages.listPinned, + isOpen + ? ({ + channelId: channelId as any, + userId: (userId as any) ?? undefined, + } as any) + : 'skip', + ); + + // Decrypt pinned message content. + const [decryptedMap, setDecryptedMap] = useState<Map<string, string>>(new Map()); + + useEffect(() => { + if (!channelKey || !pinnedRaw) return; + let cancelled = false; + (async () => { + const next = new Map(decryptedMap); + let changed = false; + for (const msg of pinnedRaw as any[]) { + const id = msg.id as string; + if (next.has(id)) continue; + if (!msg.ciphertext || msg.ciphertext.length < TAG_LENGTH) { + next.set(id, '[Invalid Encrypted Message]'); + changed = true; + continue; + } + const tag = msg.ciphertext.slice(-TAG_LENGTH); + const contentHex = msg.ciphertext.slice(0, -TAG_LENGTH); + try { + const plaintext = await crypto.decryptData( + contentHex, + channelKey, + msg.nonce, + tag, + ); + if (cancelled) return; + next.set(id, plaintext); + changed = true; + } catch { + next.set(id, '[Unable to decrypt]'); + changed = true; + } + } + if (changed && !cancelled) setDecryptedMap(next); + })(); + return () => { + cancelled = true; + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [pinnedRaw, channelKey]); + + const pinned: PinnedMessage[] = useMemo(() => { + if (!pinnedRaw) return []; + return (pinnedRaw as any[]) + .slice() + .sort((a, b) => { + const at = a.created_at ? new Date(a.created_at).getTime() : 0; + const bt = b.created_at ? new Date(b.created_at).getTime() : 0; + return bt - at; + }) + .map((msg) => { + const id = msg.id as string; + const rawContent = decryptedMap.get(id) ?? ''; + let text = rawContent; + const attachments: AttachmentMetadata[] = []; + try { + const parsed = JSON.parse(rawContent); + if (parsed && typeof parsed === 'object') { + if (Array.isArray(parsed)) { + for (const item of parsed) { + if (item?.type === 'attachment' && item.url && item.key && item.iv) { + attachments.push(item as AttachmentMetadata); + } + } + text = ''; + } else if (parsed.type === 'attachment' && parsed.url && parsed.key && parsed.iv) { + attachments.push(parsed as AttachmentMetadata); + text = ''; + } else if (parsed.text !== undefined) { + text = String(parsed.text); + } + } + } catch { + // plain text — leave as-is + } + return { + id, + authorName: msg.displayName || msg.username || 'User', + authorAvatarUrl: msg.avatarUrl ?? null, + content: text, + timestamp: msg.created_at + ? new Date(msg.created_at).getTime() + : Date.now(), + attachments, + } as PinnedMessage; + }); + }, [pinnedRaw, decryptedMap]); + + const handleJumpTo = (messageId: string) => { + window.dispatchEvent( + new CustomEvent('brycord:scroll-to-message', { + detail: { channelId, messageId }, + }), + ); + onClose(); + }; + + const [unpinTarget, setUnpinTarget] = useState<PinnedMessage | null>(null); + + if (!isOpen || !anchorRect) return null; + + return createPortal( + <> + <div className={styles.overlay}> + <div ref={ref} className={styles.popover} style={positionStyle}> + <div className={styles.header}> + <span className={styles.headerIcon}> + <PushPin size={20} weight="fill" /> + </span> + <h2 className={styles.title}>Pinned Messages</h2> + </div> + <div className={styles.body}> + {pinned.map((msg) => ( + <PinnedMessageRow + key={msg.id} + message={msg} + onJumpTo={() => handleJumpTo(msg.id)} + onUnpin={() => setUnpinTarget(msg)} + showHoverActions + canUnpin + /> + ))} + {pinnedRaw === undefined && ( + <div className={styles.loading}>Loading pinned messages…</div> + )} + <ReachedEndNotice /> + </div> + </div> + </div> + + <PinConfirmationModal + isOpen={!!unpinTarget} + onClose={() => setUnpinTarget(null)} + channelId={channelId} + messageId={unpinTarget?.id ?? null} + message={unpinTarget} + variant="unpin" + /> + </>, + document.body, + ); +} diff --git a/packages/shared/src/components/channel/ChannelSettingsModal.module.css b/packages/shared/src/components/channel/ChannelSettingsModal.module.css new file mode 100644 index 0000000..90ff041 --- /dev/null +++ b/packages/shared/src/components/channel/ChannelSettingsModal.module.css @@ -0,0 +1,192 @@ +.body { + display: flex; + flex-direction: column; + gap: 1.25rem; + padding-top: 20px; +} + +.permissionWarning { + padding: 10px 14px; + background-color: color-mix(in srgb, var(--status-warning, #faa61a) 15%, transparent); + color: var(--status-warning, #faa61a); + border-radius: 8px; + font-size: 0.8125rem; + font-weight: 500; +} + +.channelHeader { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 14px; + background-color: var(--background-tertiary, rgba(255, 255, 255, 0.03)); + border-radius: 10px; +} + +.channelHeaderIcon { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: 50%; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.05)); + color: var(--text-secondary); + flex-shrink: 0; +} + +.channelHeaderText { + display: flex; + flex-direction: column; + min-width: 0; + flex: 1; +} + +.channelHeaderName { + font-size: 0.9375rem; + font-weight: 600; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.channelHeaderSubtitle { + font-size: 0.75rem; + color: var(--text-tertiary); + margin-top: 2px; +} + +/* ── Form fields ─────────────────────────────────────────────────────── */ + +.field { + display: flex; + flex-direction: column; + gap: 0.375rem; + margin: 0; + padding: 0; + border: 0; +} + +.field:disabled { + opacity: 0.6; +} + +.label { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.02em; + color: var(--text-tertiary); + margin: 0; +} + +.input, +.textarea { + width: 100%; + padding: 10px 12px; + background-color: var(--input-background, var(--background-tertiary)); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.08)); + border-radius: 8px; + color: var(--text-primary); + font: inherit; + font-size: 0.9375rem; + outline: none; + box-sizing: border-box; + transition: border-color 150ms ease; +} + +.textarea { + resize: vertical; + min-height: 88px; + line-height: 1.4; +} + +.input::placeholder, +.textarea::placeholder { + color: var(--text-tertiary); +} + +.input:focus, +.textarea:focus { + border-color: var(--background-modifier-accent-focus, var(--brand-primary, #5865f2)); +} + +.fieldFooter { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; +} + +.fieldHint { + font-size: 0.75rem; + color: var(--text-tertiary); +} + +.counter { + font-size: 0.75rem; + font-variant-numeric: tabular-nums; + color: var(--text-tertiary); +} + +.counterFull { + font-size: 0.75rem; + font-variant-numeric: tabular-nums; + color: var(--status-danger, #da373c); +} + +.statusMessage { + font-size: 0.8125rem; + margin: 0; + line-height: 1.4; +} + +.statusSuccess { + color: var(--status-positive, #23a55a); +} + +.statusError { + color: var(--status-danger, #da373c); +} + +/* ── Danger zone ─────────────────────────────────────────────────────── */ + +.dangerZone { + display: flex; + flex-direction: column; + gap: 10px; + padding: 14px; + margin-top: 8px; + border-radius: 10px; + border: 1px solid color-mix(in srgb, var(--status-danger, #da373c) 35%, transparent); + background-color: color-mix(in srgb, var(--status-danger, #da373c) 8%, transparent); +} + +.dangerHeader { + font-size: 0.875rem; + font-weight: 700; + color: var(--status-danger, #da373c); +} + +.dangerDescription { + font-size: 0.8125rem; + line-height: 1.4; + color: var(--text-secondary); + margin: 0; +} + +.dangerConfirmRow { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; +} + +.dangerConfirmText { + font-size: 0.8125rem; + color: var(--text-primary); + font-weight: 500; + flex: 1; + min-width: 120px; +} diff --git a/packages/shared/src/components/channel/ChannelSettingsModal.tsx b/packages/shared/src/components/channel/ChannelSettingsModal.tsx new file mode 100644 index 0000000..40e1688 --- /dev/null +++ b/packages/shared/src/components/channel/ChannelSettingsModal.tsx @@ -0,0 +1,277 @@ +/** + * ChannelSettingsModal — opens from the hover gear icon on a channel + * row (via the `brycord:open-channel-settings` event, dispatched by + * GuildNavbar). Lets the user edit the channel name + topic and delete + * the channel. Gated by the `manage_channels` permission. + */ +import { useEffect, useState } from 'react'; +import { useMutation, useQuery } from 'convex/react'; +import { Hash, SpeakerHigh, Trash } from '@phosphor-icons/react'; +import { Button, Modal } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import styles from './ChannelSettingsModal.module.css'; + +interface ChannelSettingsModalProps { + isOpen: boolean; + onClose: () => void; + channelId: string | null; +} + +const NAME_MAX = 100; +const TOPIC_MAX = 1024; + +export function ChannelSettingsModal({ + isOpen, + onClose, + channelId, +}: ChannelSettingsModalProps) { + const channel = useQuery( + api.channels.get, + isOpen && channelId ? { id: channelId as Id<'channels'> } : 'skip', + ); + + const myUserId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const myPerms = useQuery( + api.roles.getMyPermissions, + myUserId ? { userId: myUserId as Id<'userProfiles'> } : 'skip', + ); + const canModify = !!myPerms?.manage_channels; + + const renameChannel = useMutation(api.channels.rename); + const updateTopic = useMutation(api.channels.updateTopic); + const removeChannel = useMutation(api.channels.remove); + + const [name, setName] = useState(''); + const [topic, setTopic] = useState(''); + const [saving, setSaving] = useState(false); + const [deleting, setDeleting] = useState(false); + const [status, setStatus] = useState< + { type: 'success' | 'error'; message: string } | null + >(null); + const [confirmDelete, setConfirmDelete] = useState(false); + + useEffect(() => { + if (isOpen && channel) { + setName(channel.name || ''); + setTopic(channel.topic || ''); + setStatus(null); + setConfirmDelete(false); + } + }, [isOpen, channel?._id, channel?.name, channel?.topic]); + + if (!channel) return null; + + const trimmedName = name.trim(); + const trimmedTopic = topic.trim(); + const originalName = (channel.name || '').trim(); + const originalTopic = (channel.topic || '').trim(); + const isDirty = trimmedName !== originalName || trimmedTopic !== originalTopic; + const nameValid = trimmedName.length > 0 && trimmedName.length <= NAME_MAX; + const topicValid = trimmedTopic.length <= TOPIC_MAX; + + const handleSave = async () => { + if (!canModify || !isDirty || !nameValid || !topicValid) return; + setSaving(true); + setStatus(null); + try { + if (trimmedName !== originalName) { + await renameChannel({ + id: channel._id as Id<'channels'>, + name: trimmedName, + }); + } + if (trimmedTopic !== originalTopic) { + await updateTopic({ + id: channel._id as Id<'channels'>, + topic: trimmedTopic, + }); + } + setStatus({ type: 'success', message: 'Channel updated.' }); + } catch (err: any) { + setStatus({ + type: 'error', + message: err?.message || 'Failed to update channel.', + }); + } finally { + setSaving(false); + } + }; + + const handleDelete = async () => { + if (!canModify) return; + setDeleting(true); + try { + await removeChannel({ id: channel._id as Id<'channels'> }); + onClose(); + } catch (err: any) { + setStatus({ + type: 'error', + message: err?.message || 'Failed to delete channel.', + }); + setDeleting(false); + } + }; + + const isVoice = channel.type === 'voice'; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="medium"> + <Modal.Header + title={`Edit ${isVoice ? 'Voice Channel' : 'Channel'}`} + onClose={onClose} + /> + <Modal.Content> + <div className={styles.body}> + {!canModify && ( + <div className={styles.permissionWarning}> + You don't have permission to edit this channel. + </div> + )} + + <div className={styles.channelHeader}> + <div className={styles.channelHeaderIcon}> + {isVoice ? ( + <SpeakerHigh size={18} /> + ) : ( + <Hash size={18} weight="bold" /> + )} + </div> + <div className={styles.channelHeaderText}> + <div className={styles.channelHeaderName}>{channel.name}</div> + <div className={styles.channelHeaderSubtitle}> + {isVoice ? 'Voice Channel' : 'Text Channel'} + </div> + </div> + </div> + + <fieldset className={styles.field} disabled={!canModify}> + <label className={styles.label} htmlFor="channel-settings-name"> + Channel Name + </label> + <input + id="channel-settings-name" + type="text" + className={styles.input} + value={name} + maxLength={NAME_MAX} + onChange={(e) => setName(e.target.value)} + placeholder="channel-name" + /> + <div className={styles.fieldFooter}> + <span className={styles.fieldHint}> + {isVoice + ? 'Displayed in the voice channel list.' + : 'Displayed in the channel list and at the top of the chat.'} + </span> + <span + className={ + name.length >= NAME_MAX ? styles.counterFull : styles.counter + } + > + {name.length}/{NAME_MAX} + </span> + </div> + </fieldset> + + <fieldset className={styles.field} disabled={!canModify}> + <label className={styles.label} htmlFor="channel-settings-topic"> + Channel Topic + </label> + <textarea + id="channel-settings-topic" + className={styles.textarea} + value={topic} + maxLength={TOPIC_MAX} + rows={4} + onChange={(e) => setTopic(e.target.value)} + placeholder="Let people know what this channel is about." + /> + <div className={styles.fieldFooter}> + <span className={styles.fieldHint}> + Shown next to the channel name in the header. + </span> + <span + className={ + topic.length >= TOPIC_MAX ? styles.counterFull : styles.counter + } + > + {topic.length}/{TOPIC_MAX} + </span> + </div> + </fieldset> + + {status && ( + <p + className={`${styles.statusMessage} ${ + status.type === 'success' + ? styles.statusSuccess + : styles.statusError + }`} + > + {status.message} + </p> + )} + + {canModify && ( + <div className={styles.dangerZone}> + <div className={styles.dangerHeader}>Delete Channel</div> + <p className={styles.dangerDescription}> + Removes this channel for everyone. All messages in it will be + permanently deleted. + </p> + {confirmDelete ? ( + <div className={styles.dangerConfirmRow}> + <span className={styles.dangerConfirmText}>Are you sure?</span> + <Button + variant="secondary" + size="sm" + onClick={() => setConfirmDelete(false)} + disabled={deleting} + > + Cancel + </Button> + <Button + variant="danger" + size="sm" + icon={<Trash size={16} />} + loading={deleting} + onClick={handleDelete} + > + Delete Channel + </Button> + </div> + ) : ( + <Button + variant="danger" + size="sm" + icon={<Trash size={16} />} + onClick={() => setConfirmDelete(true)} + > + Delete Channel + </Button> + )} + </div> + )} + </div> + </Modal.Content> + <Modal.Footer> + <Button variant="secondary" size="sm" onClick={onClose}> + Cancel + </Button> + <Button + variant="primary" + size="sm" + disabled={ + !canModify || !isDirty || !nameValid || !topicValid || saving + } + loading={saving} + onClick={handleSave} + > + Save Changes + </Button> + </Modal.Footer> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/channel/ChannelTextarea.module.css b/packages/shared/src/components/channel/ChannelTextarea.module.css new file mode 100644 index 0000000..ca08e78 --- /dev/null +++ b/packages/shared/src/components/channel/ChannelTextarea.module.css @@ -0,0 +1,469 @@ +/* ── Channel Textarea — matches Fluxer's dense 3-column grid ────────── */ + +/* Variables */ +:root { + --textarea-button-height: var(--user-area-content-height); + --textarea-button-icon-size: 26px; + --textarea-min-height: var(--input-container-min-height); + --textarea-horizontal-padding: var(--chat-horizontal-padding, var(--spacing-4)); + --textarea-content-offset: calc((var(--user-area-content-height) - var(--textarea-line-height)) / 2); + --textarea-upload-gap: var(--message-gutter, 16px); + --textarea-side-button-padding: max(0px, calc((var(--message-avatar-size, 40px) - var(--textarea-button-height)) / 2)); +} + +/* Outer container — sits inside ChannelChatLayout inputArea */ +.outer { + outline: none; + padding-left: var(--textarea-horizontal-padding); + padding-right: var(--textarea-horizontal-padding); + box-sizing: border-box; + box-shadow: inset 0 1px 0 var(--user-area-divider-color); + position: relative; + width: 100%; + max-width: 100%; + min-width: 0; + contain: inline-size; + overflow: hidden; +} + +/* Reply / Edit bars — stacked above textarea */ +.replyBar, +.editBar { + display: grid; + align-items: center; + grid-template-columns: minmax(0, 1fr) auto; + padding-left: 16px; + padding-right: 16px; + width: 100%; + max-width: 100%; + box-sizing: border-box; + min-height: 40px; + border-bottom: 1px solid var(--user-area-divider-color); +} + +.replyText { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 0.875rem; + color: var(--text-primary); +} + +.replyText strong { + font-weight: 600; +} + +.editLabel { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 0.875rem; + color: var(--text-primary); +} + +.replyClose, +.editCancel { + cursor: pointer; + flex-shrink: 0; + border: none; + background-color: transparent; + padding: 8px 0 8px 16px; + color: var(--text-primary-muted); + line-height: 0; + transition: color 200ms; +} + +.replyClose:hover, +.editCancel:hover { + color: var(--text-primary); +} + +.editCancel { + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + line-height: 1; + color: var(--text-link); +} + +.editCancel:hover { + text-decoration: underline; + color: var(--text-link); +} + +/* 3-column dense grid: [plus button] [textarea] [action buttons] */ +.mainWrapper { + display: grid; + grid-template-columns: minmax(0, auto) minmax(0, 1fr) minmax(0, auto); + align-items: flex-start; + position: relative; + min-height: var(--textarea-min-height); + box-sizing: border-box; + padding: var(--user-area-padding-y, 18px) 0; + column-gap: var(--textarea-upload-gap); + min-width: 0; + width: 100%; + max-width: 100%; +} + +/* Top divider — drawn via a ::before so it can bleed beyond the + mainWrapper's own box and span the full width of .outer, + including the horizontal padding area. An `inset` box-shadow + on mainWrapper itself would stop at the padding edge and + leave gaps on both sides when a PendingAttachmentRow or a + reply/edit bar sits above it. + `.outer`'s own `overflow: hidden` clips the pseudo to the + outer's edge, so nothing leaks past. */ +.mainWrapper::before { + content: ""; + position: absolute; + top: 0; + left: calc(var(--textarea-horizontal-padding) * -1); + right: calc(var(--textarea-horizontal-padding) * -1); + height: 1px; + background-color: var(--user-area-divider-color); + pointer-events: none; +} + +/* Left column: upload / plus button */ +.uploadColumn { + grid-column: 1; + display: flex; + align-items: flex-start; + min-height: var(--user-area-content-height); + padding-top: var(--textarea-side-button-padding); +} + +/* Center column: textarea content */ +.contentArea { + grid-column: 2; + display: flex; + flex-direction: column; + min-height: var(--user-area-content-height); + min-width: 0; + padding-top: var(--textarea-content-offset); + position: relative; +} + +/* Right column: buttons (emoji, gif, send) */ +.buttonContainer { + grid-column: 3; + display: flex; + align-items: flex-start; + gap: 10px; + min-height: var(--user-area-content-height); + min-width: 0; + flex-shrink: 1; +} + +/* The textarea itself */ +.textarea { + display: block; + width: 100%; + min-height: var(--textarea-line-height); + max-height: 300px; + overflow-y: auto; + white-space: pre-wrap; + word-break: break-word; + background-color: transparent; + color: var(--text-chat, var(--text-primary)); + line-height: var(--textarea-line-height); + caret-color: var(--text-chat, var(--text-primary)); + padding: 0; + margin: 0; + border: none; + outline: none; + font-family: inherit; + font-size: inherit; + max-height: 50svh; + box-sizing: border-box; +} + +.textarea:focus, +.textarea:focus-visible { + outline: none; + box-shadow: none; +} + +.placeholder { + position: absolute; + top: var(--textarea-content-offset); + left: 0; + right: 0; + color: var(--text-primary-muted); + pointer-events: none; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + line-height: var(--textarea-line-height); +} + +/* Mention pill rendered inside the contenteditable — matches the + received-message mention style so sending looks the same as reading. + contentEditable=false on the element itself so it's atomic. */ +.mentionPill { + display: inline; + padding: 0 2px; + border-radius: var(--radius-sm, 4px); + background-color: rgba(88, 101, 242, 0.3); + color: var(--brand-primary, #5865f2); + font-weight: 500; + cursor: default; + user-select: all; +} + +/* Textarea buttons (icons for plus, emoji, gif, send) */ +.textareaButton { + display: flex; + align-items: center; + justify-content: center; + align-self: flex-start; + min-width: var(--textarea-button-height); + height: var(--textarea-button-height); + padding: 0; + color: var(--text-primary-muted); + transition: color var(--transition-normal); + cursor: pointer; + background: transparent; + border: none; + outline: none; + flex-shrink: 0; +} + +.textareaButton:hover:not(:disabled) { + color: var(--text-primary); +} + +.textareaButton:disabled { + cursor: not-allowed; + opacity: 0.7; +} + +.textareaButtonIcon { + width: var(--textarea-button-icon-size); + height: var(--textarea-button-icon-size); + flex-shrink: 0; +} + +/* Send button — brand color when active */ +.sendActive { + color: var(--brand-primary); +} + +.sendActive:hover { + color: var(--brand-secondary, var(--brand-primary)); +} + +.emojiPickerPopout { + position: absolute; + bottom: 100%; + right: 8px; + margin-bottom: 8px; + z-index: var(--z-index-popout, 100); +} + +/* Mobile send button — hidden on desktop. On mobile it sits outside + the input pill to the right as a round brand-primary button that + commits the message. Desktop still uses Enter-to-send. */ +.sendColumn { + display: none; +} + +.sendButton { + display: inline-flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + min-width: 36px; + border-radius: 50%; + background-color: var(--brand-primary, #5865f2); + color: #ffffff; + border: none; + padding: 0; + cursor: pointer; + transition: filter 0.15s ease, opacity 0.15s ease; +} + +.sendButton:hover:not(:disabled) { + filter: brightness(1.1); +} + +.sendButton:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +/* Body wrapper inside the mobile emoji bottom sheet — stretches the + picker so its mobile layout can fill the entire drawer height. The + picker itself (in mobile mode) uses width/height: 100% and handles + its own internal layout. */ +.emojiPickerSheetBody { + display: flex; + flex-direction: column; + flex: 1 1 auto; + width: 100%; + height: 100%; + min-height: 0; +} + +.emojiPickerSheetBody > * { + flex: 1 1 auto; + min-height: 0; +} + +/* ── Mobile layout ≤768px ────────────────────────────────────────────── */ + +@media (max-width: 768px) { + .outer { + padding-left: 8px; + padding-right: 8px; + } + + /* Drop the 3-column grid to a single row of pill components. */ + .mainWrapper { + display: flex; + align-items: center; + column-gap: 8px; + padding: 8px 0; + min-height: 52px; + width: 100%; + } + + /* Left: plus becomes a round button outside the pill. */ + .uploadColumn { + padding-top: 0; + align-items: center; + min-height: 0; + flex: 0 0 auto; + grid-column: unset; + } + + .uploadColumn .textareaButton { + width: 36px; + height: 36px; + min-width: 36px; + border-radius: 50%; + background-color: var(--background-tertiary, rgba(255, 255, 255, 0.04)); + color: var(--text-primary); + } + + .uploadColumn .textareaButtonIcon { + width: 22px; + height: 22px; + } + + /* Center + right: visually merge into one rounded pill — expand to + fill all the space between the plus button and the right edge. */ + .contentArea { + flex: 1 1 auto; + min-width: 0; + grid-column: unset; + background-color: var(--background-tertiary, rgba(255, 255, 255, 0.04)); + border-radius: 18px; + padding: 10px 44px 10px 16px; + min-height: 40px; + position: relative; + align-self: stretch; + justify-content: center; + padding-top: 10px; + } + + .placeholder { + top: 50%; + left: 14px; + right: 44px; + transform: translateY(-50%); + } + + .textarea { + max-height: 140px; + } + + /* Right column collapses into the pill: GIF/Image/Sticker hidden, + emoji button absolute-positioned at the pill's right edge. + The offset is measured from .mainWrapper's right edge and needs + to clear both the send button (36px) and the column gap (8px) + before adding the 6px inset inside the pill. */ + .buttonContainer { + grid-column: unset; + position: absolute; + right: calc(36px + 8px + 6px); + top: 50%; + transform: translateY(-50%); + min-height: 0; + gap: 0; + background: transparent; + } + + /* Hide the GIF / Image / Sticker buttons on mobile — the pill only + needs the emoji toggle. */ + .buttonContainer > .textareaButton:nth-child(1), + .buttonContainer > .textareaButton:nth-child(2), + .buttonContainer > .textareaButton:nth-child(3) { + display: none; + } + + .buttonContainer > .textareaButton:last-child { + width: 32px; + height: 32px; + min-width: 32px; + } + + .buttonContainer > .textareaButton:last-child .textareaButtonIcon { + width: 22px; + height: 22px; + } + + /* The parent ChannelView re-nests the textarea inside a flex column; + keep the input sized appropriately on mobile. */ + .replyBar, + .editBar { + padding-left: 12px; + padding-right: 12px; + } + + /* Show the mobile send button — sits in a fixed-width column next to + the input pill. */ + .sendColumn { + display: flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; + } +} + +/* ── Plus-button popup menu ─────────────────────────────────────────── */ + +.plusMenu { + min-width: 188px; + padding: 6px 8px; + background-color: var(--background-floating, var(--background-primary)); + border-radius: 8px; + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.24); + display: flex; + flex-direction: column; +} + +.plusMenuItem { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + padding: 8px 8px; + border-radius: 4px; + background: none; + border: none; + cursor: pointer; + color: var(--text-secondary); + font-size: 0.875rem; + font-weight: 500; + font-family: inherit; + text-align: left; + transition: background-color 0.1s, color 0.1s; +} + +.plusMenuItem:hover { + background-color: var(--brand-primary); + color: #fff; +} diff --git a/packages/shared/src/components/channel/ChannelTextarea.tsx b/packages/shared/src/components/channel/ChannelTextarea.tsx new file mode 100644 index 0000000..05034fe --- /dev/null +++ b/packages/shared/src/components/channel/ChannelTextarea.tsx @@ -0,0 +1,1037 @@ +import { useConvex, useMutation, useQuery } from 'convex/react'; +import { + ArrowUp, + ChartBar, + Gif, + ImageSquare, + Paperclip, + PlusCircle, + Smiley, + Sticker, + X, +} from '@phosphor-icons/react'; +import { useEffect, useMemo, useRef, useState, type KeyboardEvent as ReactKeyboardEvent } from 'react'; +import { createPortal } from 'react-dom'; +import { api } from '../../../../../convex/_generated/api'; +import { usePlatform } from '../../platform'; +import { useIsMobile } from '../../hooks/useIsMobile'; +import { getTwemojiUrl } from '../../utils/twemoji'; +import { EmojiPicker, type EmojiPickerValue } from './EmojiPicker'; +import { MobileExpressionPickerSheet } from './MobileExpressionPickerSheet'; +import { CreatePollModal } from './CreatePollModal'; +import { + MentionAutocomplete, + type MentionAutocompleteHandle, + type MentionItem, +} from './MentionAutocomplete'; +import { + PendingAttachmentRow, + type PendingAttachment, +} from './PendingAttachmentRow'; +import { Tooltip } from '@discord-clone/ui'; +import { useKeybinds } from '../../contexts/KeybindContext'; +import styles from './ChannelTextarea.module.css'; + +interface ReplyState { + eventId: string; + username: string; +} + +interface ChannelTextareaProps { + channelId: string; + replyTo?: ReplyState | null; + onCancelReply?: () => void; + editingEventId?: string; + editingContent?: string; + onCancelEdit?: () => void; +} + +/** + * Composer: encrypts plaintext with the channel key and sends via + * api.messages.send. Structure mirrors the original contenteditable-based + * composer so the CSS module classes (.outer / .mainWrapper / .uploadColumn + * / .contentArea / .buttonContainer / .sendColumn) resolve correctly. + * + * Omitted from the original: autocomplete, rich formatting, file upload, + * emoji picker, GIF picker, stickers, saved media. The buttons render + * visually but do nothing yet. + */ +export function ChannelTextarea({ + channelId, + replyTo, + onCancelReply, + editingEventId, + onCancelEdit, +}: ChannelTextareaProps) { + const { crypto } = usePlatform(); + const isMobile = useIsMobile(); + const convex = useConvex(); + void convex; + const editorRef = useRef<HTMLDivElement>(null); + const emojiButtonRef = useRef<HTMLButtonElement>(null); + const plusButtonRef = useRef<HTMLButtonElement>(null); + const fileInputRef = useRef<HTMLInputElement>(null); + // Wraps all four expression-trigger buttons (GIFs / Media / + // Stickers / Emoji) so the document-level outside-click handler + // can ignore clicks that land on any of them — the buttons + // already toggle the picker themselves and we don't want the + // outside-click to race with the toggle. + const expressionButtonsRef = useRef<HTMLDivElement>(null); + const pickerWrapperRef = useRef<HTMLDivElement>(null); + const [isEmpty, setIsEmpty] = useState(true); + // channelKey holds the LATEST key for this channel + its version. + // We only ever send new messages under the latest version; for + // decrypting existing messages Messages.tsx has its own version + // lookup table. + const [channelKey, setChannelKey] = useState<string | null>(null); + const [channelKeyVersion, setChannelKeyVersion] = useState<number>(1); + const [isUploading, setIsUploading] = useState(false); + const [pendingAttachments, setPendingAttachments] = useState<PendingAttachment[]>([]); + const [showPlusMenu, setShowPlusMenu] = useState(false); + const [showCreatePoll, setShowCreatePoll] = useState(false); + const [showEmojiPicker, setShowEmojiPicker] = useState(false); + // `pickerInitialTab` is the source-of-truth for which tab the + // picker should display — both as the initial tab on open and + // for imperative tab swaps while it's open. The picker syncs + // to changes via a useEffect, and reports its own internal tab + // switches back through `onTabChange` so the composer toggle + // behaviour stays in sync with what the user actually sees. + const [pickerInitialTab, setPickerInitialTab] = useState< + 'emojis' | 'gifs' | 'media' | 'stickers' + >('emojis'); + const [pickerPos, setPickerPos] = useState<{ bottom: number; right: number }>({ + bottom: 80, + right: 20, + }); + + // Mention autocomplete state. `mentionQuery` is null when the + // popup is closed; a string (possibly empty) when it's open and + // tracking the characters the user typed after `@`. + const [mentionQuery, setMentionQuery] = useState<string | null>(null); + const mentionRef = useRef<MentionAutocompleteHandle>(null); + + const userId = typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const keybinds = useKeybinds(); + const username = + typeof localStorage !== 'undefined' ? localStorage.getItem('username') : null; + const privateKeyPem = + typeof sessionStorage !== 'undefined' ? sessionStorage.getItem('privateKey') : null; + const signingKey = + typeof sessionStorage !== 'undefined' ? sessionStorage.getItem('signingKey') : null; + + const startTyping = useMutation(api.typing.startTyping); + const stopTyping = useMutation(api.typing.stopTyping); + const typingHeartbeatRef = useRef<number>(0); + + const channel = useQuery(api.channels.get, channelId ? { id: channelId as any } : 'skip'); + const allKeys = useQuery( + api.channelKeys.getKeysForUser, + userId ? { userId: userId as any } : 'skip', + ); + const keyBundle = allKeys?.find((k) => k.channel_id === channelId) ?? null; + + const sendMessage = useMutation(api.messages.send); + const generateUploadUrl = useMutation(api.files.generateUploadUrl); + const validateUpload = useMutation(api.files.validateUpload); + + // Walk every bundle we have, decrypt the ones that carry a key + // for the currently-open channel, and pick the highest-version + // entry. That's the key new messages get sent under. The version + // number rides along with each outgoing message so the receiver + // knows which key to decrypt with. + useEffect(() => { + let cancelled = false; + if (!allKeys || !privateKeyPem) { + setChannelKey(null); + setChannelKeyVersion(1); + return; + } + (async () => { + let bestVersion = -Infinity; + let bestKey: string | null = null; + for (const item of allKeys) { + try { + const bundleJson = await crypto.privateDecrypt( + privateKeyPem, + item.encrypted_key_bundle, + ); + const parsed = JSON.parse(bundleJson) as Record<string, string>; + const keyForThisChannel = parsed[channelId]; + if (!keyForThisChannel) continue; + const version = item.key_version ?? 1; + if (version > bestVersion) { + bestVersion = version; + bestKey = keyForThisChannel; + } + } catch (err) { + console.error( + `Failed to decrypt key bundle for ${item.channel_id}`, + err, + ); + } + } + if (cancelled) return; + setChannelKey(bestKey); + setChannelKeyVersion(bestVersion === -Infinity ? 1 : bestVersion); + })(); + return () => { + cancelled = true; + }; + }, [allKeys, privateKeyPem, channelId]); + + // Reset the composer when channel changes + useEffect(() => { + if (editorRef.current) editorRef.current.textContent = ''; + setIsEmpty(true); + setMentionQuery(null); + typingHeartbeatRef.current = 0; + }, [channelId]); + + // Listen for saved-media reposts dispatched from the Media tab in + // EmojiPicker. The detail carries the same shape we'd embed in + // the message body, so we just forward it through the existing + // `sendAttachmentMessage` path. + useEffect(() => { + const onRepost = (e: Event) => { + const detail = (e as CustomEvent<{ + url: string; + filename: string; + mimeType?: string; + width?: number; + height?: number; + size?: number; + encryptionKey: string; + encryptionIv: string; + }>).detail; + if (!detail?.url || !detail?.encryptionKey) return; + void sendAttachmentMessage({ + type: 'attachment', + url: detail.url, + filename: detail.filename, + mimeType: detail.mimeType ?? 'application/octet-stream', + size: detail.size ?? 0, + key: detail.encryptionKey, + iv: detail.encryptionIv, + width: detail.width, + height: detail.height, + }); + }; + window.addEventListener('brycord:repost-saved-media', onRepost); + return () => + window.removeEventListener('brycord:repost-saved-media', onRepost); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [channelKey, signingKey, userId, channelId, replyTo]); + + // Composer-level keybinds: open the emoji / gif picker and focus the + // editor. Dispatched by KeybindProvider as window CustomEvents. + useEffect(() => { + const positionPicker = () => { + const btn = emojiButtonRef.current; + if (!btn) return; + const rect = btn.getBoundingClientRect(); + setPickerPos({ + bottom: window.innerHeight - rect.top + 8, + right: window.innerWidth - rect.right, + }); + }; + const openEmoji = () => { + positionPicker(); + setPickerInitialTab('emojis'); + setShowEmojiPicker((v) => !v); + }; + const openGif = () => { + positionPicker(); + setPickerInitialTab('gifs'); + setShowEmojiPicker(true); + }; + const focusComposer = () => editorRef.current?.focus(); + window.addEventListener('brycord:keybind:popouts.openEmojiPicker', openEmoji); + window.addEventListener('brycord:keybind:popouts.openGifPicker', openGif); + window.addEventListener( + 'brycord:keybind:messaging.focusComposer', + focusComposer, + ); + return () => { + window.removeEventListener( + 'brycord:keybind:popouts.openEmojiPicker', + openEmoji, + ); + window.removeEventListener( + 'brycord:keybind:popouts.openGifPicker', + openGif, + ); + window.removeEventListener( + 'brycord:keybind:messaging.focusComposer', + focusComposer, + ); + }; + }, []); + + const handleInput = () => { + const editor = editorRef.current; + const text = editor ? serializeEditor(editor) : ''; + setIsEmpty(text.trim().length === 0); + + // Typing heartbeat — refresh the server TTL at most once every 3s. + if (userId && username && channelId && text.trim().length > 0) { + const now = Date.now(); + if (now - typingHeartbeatRef.current > 3000) { + typingHeartbeatRef.current = now; + void startTyping({ + channelId: channelId as any, + userId: userId as any, + username, + }); + } + } + + // Detect `@query` at the cursor. We look at the text up to the + // current caret position and match a trailing `@` token that + // isn't glued to another word (must be start-of-text or after + // whitespace). Matches `@`, `@f`, `@foo`, etc. + const beforeCaret = getTextBeforeCaret(editor); + const match = beforeCaret.match(/(?:^|\s)@([^\s@]*)$/); + if (match) { + setMentionQuery(match[1] ?? ''); + } else { + setMentionQuery(null); + } + }; + + // Walk the editor's DOM tree and produce the plain-text string we + // actually send. <img data-emoji> → the unicode char; <img + // data-custom-emoji> → `:shortcode:`; <br> → newline; text nodes + // pass through (with zero-width spaces stripped). + const serializeEditor = (editor: HTMLElement): string => { + let out = ''; + const walk = (node: Node) => { + if (node.nodeType === Node.TEXT_NODE) { + out += (node.textContent ?? '').replace(/\u200B/g, ''); + return; + } + if (node.nodeType === Node.ELEMENT_NODE) { + const el = node as HTMLElement; + if (el.tagName === 'IMG') { + if (el.dataset.customEmoji === 'true' && el.dataset.shortcode) { + out += `:${el.dataset.shortcode}:`; + } else if (el.dataset.emoji) { + out += el.dataset.emoji; + } + return; + } + if (el.tagName === 'BR') { + out += '\n'; + return; + } + for (const child of Array.from(el.childNodes)) walk(child); + } + }; + for (const child of Array.from(editor.childNodes)) walk(child); + return out; + }; + + // Read the text from the start of the contenteditable up to the + // current caret position. Used to drive the `@mention` matcher. + const getTextBeforeCaret = (editor: HTMLElement | null): string => { + if (!editor) return ''; + const selection = typeof window !== 'undefined' ? window.getSelection() : null; + if (!selection || selection.rangeCount === 0) return editor.textContent ?? ''; + const range = selection.getRangeAt(0); + if (!editor.contains(range.endContainer)) return editor.textContent ?? ''; + const pre = range.cloneRange(); + pre.selectNodeContents(editor); + pre.setEnd(range.endContainer, range.endOffset); + return pre.toString(); + }; + + // Commit the mention: replace the trailing `@query` in the + // contenteditable with `@DisplayName ` plain text. Plain-text only + // for this pass — no mention pills. + const handleMentionSelect = (item: MentionItem) => { + const editor = editorRef.current; + if (!editor) return; + const label = + item.kind === 'everyone' ? '@everyone ' : `@${item.displayName} `; + const full = editor.textContent ?? ''; + // Find the last `@…` token — same heuristic as the matcher. + const m = full.match(/(?:^|\s)@([^\s@]*)$/); + if (!m) { + editor.textContent = full + label; + } else { + const start = full.length - (m[0].startsWith('@') ? m[0].length : m[0].length - 1); + const head = full.slice(0, start); + editor.textContent = head + label; + } + // Restore focus and move caret to the end. + editor.focus(); + const selection = window.getSelection(); + if (selection) { + const range = document.createRange(); + range.selectNodeContents(editor); + range.collapse(false); + selection.removeAllRanges(); + selection.addRange(range); + } + setMentionQuery(null); + setIsEmpty((editor.textContent ?? '').trim().length === 0); + }; + + const doSend = async () => { + const text = editorRef.current ? serializeEditor(editorRef.current).trim() : ''; + const hasAttachments = pendingAttachments.length > 0; + if (!hasAttachments && !text) return; + if (!channelKey || !signingKey || !userId) return; + + // Snapshot staged attachments so the state can clear immediately + // after send. The upload loop uses this local copy. + const stagedFiles = pendingAttachments.map((a) => a.file); + const stagedPreviews = pendingAttachments + .map((a) => a.previewUrl) + .filter((u): u is string => !!u); + + try { + // 1. Text message first (if any). Matches the old client's + // send-then-attach order so the reply context lands on + // the text message. + if (text) { + const { content, iv, tag } = await crypto.encryptData(text, channelKey); + const ciphertext = content + tag; + const signature = await crypto.signMessage(signingKey, ciphertext); + await sendMessage({ + channelId: channelId as any, + senderId: userId as any, + ciphertext, + nonce: iv, + signature, + keyVersion: channelKeyVersion, + replyTo: replyTo ? (replyTo.eventId as any) : undefined, + }); + } + + // 2. One message per attachment. Clear the staging row up + // front so the cards disappear even if a later upload + // fails — failed uploads are logged to the console. + if (hasAttachments) { + setPendingAttachments([]); + setIsUploading(true); + try { + for (const file of stagedFiles) { + try { + await uploadOneFile(file); + } catch (err) { + console.error('Failed to upload file:', err); + } + } + } finally { + setIsUploading(false); + // Revoke the preview blob URLs now that the cards + // are gone — keeping them leaks memory. + for (const url of stagedPreviews) URL.revokeObjectURL(url); + } + } + + if (editorRef.current) editorRef.current.textContent = ''; + setIsEmpty(true); + setMentionQuery(null); + if (userId && channelId) { + void stopTyping({ + channelId: channelId as any, + userId: userId as any, + }); + typingHeartbeatRef.current = 0; + } + onCancelReply?.(); + } catch (err) { + console.error('Failed to send message:', err); + } + }; + + // Send a plain text message (used by GIF picks — the URL travels + // through the channel-key encryption like any other text and the + // receiver's MessageContent + LinkEmbed renders the GIF inline). + const sendPlainTextMessage = async (text: string) => { + if (!channelKey || !signingKey || !userId) return; + const payload = JSON.stringify({ text }); + const { content, iv, tag } = await crypto.encryptData(payload, channelKey); + const ciphertext = content + tag; + const signature = await crypto.signMessage(signingKey, ciphertext); + await sendMessage({ + channelId: channelId as any, + senderId: userId as any, + ciphertext, + nonce: iv, + signature, + keyVersion: channelKeyVersion, + replyTo: replyTo ? (replyTo.eventId as any) : undefined, + }); + }; + + // Send a message whose content is a single attachment metadata JSON. + // Matches the original Brycord on-the-wire format — the decrypt side + // recognises `{ type: 'attachment', url, key, iv, ... }` as a plain + // object and renders it via <EncryptedAttachment>. + const sendAttachmentMessage = async (metadata: Record<string, unknown>) => { + if (!channelKey || !signingKey || !userId) return; + const payload = JSON.stringify(metadata); + const { content, iv, tag } = await crypto.encryptData(payload, channelKey); + const ciphertext = content + tag; + const signature = await crypto.signMessage(signingKey, ciphertext); + await sendMessage({ + channelId: channelId as any, + senderId: userId as any, + ciphertext, + nonce: iv, + signature, + keyVersion: keyBundle?.key_version ?? 1, + replyTo: replyTo ? (replyTo.eventId as any) : undefined, + }); + }; + + const getMediaDimensions = ( + file: File, + ): Promise<{ width: number; height: number } | null> => { + return new Promise((resolve) => { + if (file.type.startsWith('image/')) { + const img = new Image(); + const objectUrl = URL.createObjectURL(file); + img.onload = () => { + URL.revokeObjectURL(objectUrl); + resolve({ width: img.naturalWidth, height: img.naturalHeight }); + }; + img.onerror = () => { + URL.revokeObjectURL(objectUrl); + resolve(null); + }; + img.src = objectUrl; + } else if (file.type.startsWith('video/')) { + const video = document.createElement('video'); + const objectUrl = URL.createObjectURL(file); + video.onloadedmetadata = () => { + URL.revokeObjectURL(objectUrl); + resolve({ width: video.videoWidth, height: video.videoHeight }); + }; + video.onerror = () => { + URL.revokeObjectURL(objectUrl); + resolve(null); + }; + video.src = objectUrl; + } else { + resolve(null); + } + }); + }; + + const fromHexString = (hex: string): Uint8Array => { + const matches = hex.match(/.{1,2}/g) ?? []; + return new Uint8Array(matches.map((b) => parseInt(b, 16))); + }; + + const uploadOneFile = async (file: File) => { + // 1. Encrypt the file with a fresh per-file AES key. + const fileKey = await crypto.randomBytes(32); + const buf = new Uint8Array(await file.arrayBuffer()); + const encrypted = await crypto.encryptData(buf, fileKey); + const encryptedHex = encrypted.content + encrypted.tag; + const encryptedBytes = fromHexString(encryptedHex); + + // 2. Upload the ciphertext as an opaque blob. + const blob = new Blob([encryptedBytes], { type: 'application/octet-stream' }); + const uploadUrl = await generateUploadUrl(); + const res = await fetch(uploadUrl, { + method: 'POST', + headers: { 'Content-Type': blob.type }, + body: blob, + }); + if (!res.ok) throw new Error(`Upload failed: ${res.status}`); + const { storageId } = (await res.json()) as { storageId: string }; + + // 3. Validate — server checks size cap + returns the permanent URL. + const fileUrl = await validateUpload({ storageId: storageId as any }); + if (!fileUrl) throw new Error('Failed to resolve file URL'); + + // 4. Collect media dimensions for preview sizing. + const dims = await getMediaDimensions(file); + + // 5. Send the message with the attachment metadata. + const metadata: Record<string, unknown> = { + type: 'attachment', + url: fileUrl, + filename: file.name, + mimeType: file.type || 'application/octet-stream', + size: file.size, + key: fileKey, + iv: encrypted.iv, + ...(dims && { width: dims.width, height: dims.height }), + }; + await sendAttachmentMessage(metadata); + }; + + // Stage files as pending attachments above the composer instead of + // uploading immediately. The actual encrypt+upload runs in doSend() + // when the user presses Send. + const stageFiles = (files: File[]) => { + if (files.length === 0) return; + const staged: PendingAttachment[] = files.map((file) => { + const kind = file.type.split('/')[0]; + const previewUrl = + kind === 'image' || kind === 'video' ? URL.createObjectURL(file) : null; + return { + id: `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`, + file, + previewUrl, + }; + }); + setPendingAttachments((prev) => [...prev, ...staged]); + }; + + const removePendingAttachment = (id: string) => { + setPendingAttachments((prev) => { + const victim = prev.find((a) => a.id === id); + if (victim?.previewUrl) URL.revokeObjectURL(victim.previewUrl); + return prev.filter((a) => a.id !== id); + }); + }; + + const handleFilesSelected = (e: React.ChangeEvent<HTMLInputElement>) => { + const files = Array.from(e.target.files ?? []); + e.target.value = ''; + stageFiles(files); + }; + + // Listen for drag-and-drop file events from ChannelView's FileDropZone. + useEffect(() => { + const handler = (e: Event) => { + const detail = (e as CustomEvent<{ channelId: string; files: File[] }>).detail; + if (!detail || detail.channelId !== channelId) return; + stageFiles(detail.files); + }; + window.addEventListener('brycord:drop-files', handler); + return () => window.removeEventListener('brycord:drop-files', handler); + }, [channelId]); + + // Revoke any preview URLs still around when the channel switches or + // the component unmounts — the cards are channel-scoped and don't + // survive navigation. + useEffect(() => { + return () => { + for (const att of pendingAttachments) { + if (att.previewUrl) URL.revokeObjectURL(att.previewUrl); + } + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [channelId]); + + const handleKeyDown = (e: ReactKeyboardEvent<HTMLDivElement>) => { + // Route arrow keys + Enter + Escape to the mention popup when + // it's open. This way the textarea's own keybindings stay out + // of the way while the user is picking a mention. + if (mentionQuery !== null && mentionRef.current) { + if (e.key === 'ArrowDown') { + if (mentionRef.current.moveSelection(1)) { + e.preventDefault(); + return; + } + } else if (e.key === 'ArrowUp') { + if (mentionRef.current.moveSelection(-1)) { + e.preventDefault(); + return; + } + } else if (e.key === 'Enter' || e.key === 'Tab') { + if (mentionRef.current.commit()) { + e.preventDefault(); + return; + } + } else if (e.key === 'Escape') { + setMentionQuery(null); + e.preventDefault(); + return; + } + } + + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + void doSend(); + } + }; + + // Click an expression button: + // - picker closed → open on this tab + // - picker open on the same tab → close + // - picker open on a different tab → switch tabs (stay open) + const openPickerOnTab = (tab: 'emojis' | 'gifs' | 'media' | 'stickers') => { + if (showEmojiPicker) { + if (pickerInitialTab === tab) { + setShowEmojiPicker(false); + return; + } + setPickerInitialTab(tab); + return; + } + const btn = emojiButtonRef.current; + if (btn) { + const rect = btn.getBoundingClientRect(); + setPickerPos({ + bottom: window.innerHeight - rect.top + 8, + right: window.innerWidth - rect.right, + }); + } + setPickerInitialTab(tab); + setShowEmojiPicker(true); + }; + + const toggleEmojiPicker = () => openPickerOnTab('emojis'); + + // Outside-click closes the desktop picker. Skips clicks on the + // picker portal itself and on any of the expression trigger + // buttons (those handle their own toggle). Only attached while + // the picker is open and on desktop — mobile uses a sheet. + useEffect(() => { + if (!showEmojiPicker || isMobile) return; + const onDocMouseDown = (e: MouseEvent) => { + const target = e.target as Node | null; + if (!target) return; + if (pickerWrapperRef.current?.contains(target)) return; + if (expressionButtonsRef.current?.contains(target)) return; + setShowEmojiPicker(false); + }; + // `mousedown` (not `click`) so we beat any selection logic + // that the textarea below might run on click. + document.addEventListener('mousedown', onDocMouseDown); + return () => document.removeEventListener('mousedown', onDocMouseDown); + }, [showEmojiPicker, isMobile]); + + const insertEmoji = (value: EmojiPickerValue) => { + // GIF picks bypass the contentEditable image-insertion path + // entirely — they're posted as a plain-text URL message so + // the existing link-embed renderer can show them inline. + if (value.kind === 'gif') { + void sendPlainTextMessage(value.url); + return; + } + + const editor = editorRef.current; + if (!editor) return; + editor.focus(); + + // Build the <img> node — unicode emoji render as a Twemoji SVG, + // custom server emoji render as the uploaded image. Both are + // marked contentEditable={false} so the caret skips over them. + const img = document.createElement('img'); + if (value.kind === 'custom') { + img.className = 'customEmoji'; + img.src = value.url; + img.alt = `:${value.shortcode}:`; + img.dataset.customEmoji = 'true'; + img.dataset.shortcode = value.shortcode; + } else { + img.className = 'twemoji-inline'; + img.src = getTwemojiUrl(value.surrogates); + img.alt = value.surrogates; + img.dataset.emoji = value.surrogates; + } + img.contentEditable = 'false'; + + // Insert at the current selection when it lives inside the + // editor, otherwise append at the end. A trailing zero-width + // space gives the caret somewhere to sit past the image so the + // user can keep typing. + const selection = typeof window !== 'undefined' ? window.getSelection() : null; + let range: Range | null = null; + if (selection && selection.rangeCount > 0) { + const r = selection.getRangeAt(0); + if (editor.contains(r.startContainer) && editor.contains(r.endContainer)) { + range = r; + } + } + + const zwsp = document.createTextNode('\u200B'); + if (range) { + range.deleteContents(); + range.insertNode(img); + img.after(zwsp); + } else { + editor.appendChild(img); + editor.appendChild(zwsp); + } + + // Move caret to after the zero-width space. + if (selection) { + const newRange = document.createRange(); + newRange.setStartAfter(zwsp); + newRange.collapse(true); + selection.removeAllRanges(); + selection.addRange(newRange); + } + + setIsEmpty(serializeEditor(editor).trim().length === 0); + }; + + // For DMs we fetch the other participant so the placeholder reads + // "Message @username" instead of the internal `dm-xxx-yyy` channel + // name. For text/voice channels we still prefix with `#`. + const isDM = channel?.type === 'dm'; + const dmRows = useQuery( + api.dms.listDMs, + isDM && userId ? { userId: userId as any } : 'skip', + ); + const dmOtherUsername = useMemo(() => { + if (!isDM || !dmRows) return null; + const row = (dmRows as any[]).find((r) => r.channel_id === channelId); + if (!row) return null; + // Always use the real `username` for DM placeholder so it never + // shows a server-side nickname. The display name still shows in + // the header pill — it's only the composer hint that's strict. + return row.other_username as string; + }, [isDM, dmRows, channelId]); + + const channelName = channel?.name || 'channel'; + const placeholder = isDM + ? `Message @${dmOtherUsername || 'user'}` + : `Message #${channelName}`; + + return ( + <div className={styles.outer}> + {replyTo && onCancelReply && ( + <div className={styles.replyBar}> + <span className={styles.replyText}> + Replying to <strong>{replyTo.username}</strong> + </span> + <button + type="button" + className={styles.replyClose} + onClick={onCancelReply} + aria-label="Cancel reply" + > + <X size={20} weight="bold" /> + </button> + </div> + )} + + {editingEventId && onCancelEdit && ( + <div className={styles.editBar}> + <span className={styles.editLabel}>Editing message</span> + <button + type="button" + className={styles.editCancel} + onClick={onCancelEdit} + > + cancel + </button> + </div> + )} + + <PendingAttachmentRow + attachments={pendingAttachments} + onRemove={removePendingAttachment} + /> + + <div className={styles.mainWrapper}> + <div className={styles.uploadColumn}> + <input + ref={fileInputRef} + type="file" + multiple + style={{ display: 'none' }} + onChange={handleFilesSelected} + /> + <Tooltip content="Upload a File" placement="top"> + <button + ref={plusButtonRef} + type="button" + className={styles.textareaButton} + aria-label="Upload a file or create a poll" + onClick={() => setShowPlusMenu((v) => !v)} + disabled={isUploading || !channelKey} + > + <PlusCircle size={26} weight="fill" className={styles.textareaButtonIcon} /> + </button> + </Tooltip> + </div> + + <div className={styles.contentArea}> + <div + ref={editorRef} + className={styles.textarea} + contentEditable + role="textbox" + aria-multiline + onInput={handleInput} + onKeyDown={handleKeyDown} + suppressContentEditableWarning + /> + {isEmpty && <div className={styles.placeholder}>{placeholder}</div>} + </div> + + <div className={styles.buttonContainer} ref={expressionButtonsRef}> + <Tooltip + content="GIFs" + shortcut={keybinds.getCombo('popouts.openGifPicker')} + placement="top" + > + <button + type="button" + className={styles.textareaButton} + onClick={() => openPickerOnTab('gifs')} + aria-label="GIFs" + > + <Gif size={26} className={styles.textareaButtonIcon} /> + </button> + </Tooltip> + <Tooltip content="Saved Media" placement="top"> + <button + type="button" + className={styles.textareaButton} + onClick={() => openPickerOnTab('media')} + aria-label="Saved Media" + > + <ImageSquare size={26} className={styles.textareaButtonIcon} /> + </button> + </Tooltip> + <Tooltip content="Stickers" placement="top"> + <button + type="button" + className={styles.textareaButton} + onClick={() => openPickerOnTab('stickers')} + aria-label="Stickers" + > + <Sticker size={26} className={styles.textareaButtonIcon} /> + </button> + </Tooltip> + <Tooltip + content="Emoji" + shortcut={keybinds.getCombo('popouts.openEmojiPicker')} + placement="top" + > + <button + ref={emojiButtonRef} + type="button" + className={styles.textareaButton} + onClick={toggleEmojiPicker} + aria-label="Emoji" + > + <Smiley size={26} className={styles.textareaButtonIcon} /> + </button> + </Tooltip> + </div> + + <div className={styles.sendColumn}> + <button + type="button" + className={styles.sendButton} + onClick={doSend} + disabled={(isEmpty && pendingAttachments.length === 0) || !channelKey || isUploading} + aria-label="Send message" + > + <ArrowUp size={22} weight="bold" /> + </button> + </div> + </div> + + {mentionQuery !== null && ( + <MentionAutocomplete + ref={mentionRef} + channelId={channelId} + query={mentionQuery} + anchorEl={editorRef.current} + onSelect={handleMentionSelect} + onClose={() => setMentionQuery(null)} + /> + )} + + {isMobile ? ( + <MobileExpressionPickerSheet + isOpen={showEmojiPicker} + initialTab={pickerInitialTab} + onClose={() => setShowEmojiPicker(false)} + onSelect={(value) => { + insertEmoji(value); + setShowEmojiPicker(false); + }} + /> + ) : ( + showEmojiPicker && + createPortal( + <div + ref={pickerWrapperRef} + style={{ + position: 'fixed', + bottom: pickerPos.bottom, + right: pickerPos.right, + zIndex: 15000, + }} + > + <EmojiPicker + initialTab={pickerInitialTab} + onTabChange={(tab) => setPickerInitialTab(tab)} + onSelect={(value) => { + insertEmoji(value); + setShowEmojiPicker(false); + }} + onClose={() => setShowEmojiPicker(false)} + /> + </div>, + document.body, + ) + )} + + {showPlusMenu && + plusButtonRef.current && + createPortal( + (() => { + const rect = plusButtonRef.current.getBoundingClientRect(); + return ( + <> + <div + style={{ + position: 'fixed', + inset: 0, + zIndex: 14999, + }} + onClick={() => setShowPlusMenu(false)} + /> + <div + className={styles.plusMenu} + style={{ + position: 'fixed', + bottom: window.innerHeight - rect.top + 8, + left: rect.left, + zIndex: 15000, + }} + onClick={(e) => e.stopPropagation()} + > + <button + type="button" + className={styles.plusMenuItem} + onClick={() => { + setShowPlusMenu(false); + fileInputRef.current?.click(); + }} + > + <Paperclip size={20} /> + <span>Upload File</span> + </button> + <button + type="button" + className={styles.plusMenuItem} + onClick={() => { + setShowPlusMenu(false); + setShowCreatePoll(true); + }} + > + <ChartBar size={20} /> + <span>Create Poll</span> + </button> + </div> + </> + ); + })(), + document.body, + )} + <CreatePollModal + isOpen={showCreatePoll} + onClose={() => setShowCreatePoll(false)} + channelId={channelId} + /> + </div> + ); +} diff --git a/packages/shared/src/components/channel/ChannelView.module.css b/packages/shared/src/components/channel/ChannelView.module.css new file mode 100644 index 0000000..b403147 --- /dev/null +++ b/packages/shared/src/components/channel/ChannelView.module.css @@ -0,0 +1,98 @@ +.container { + display: flex; + flex-direction: column; + flex: 1; + min-width: 0; + min-height: 0; +} + +.body { + display: flex; + flex: 1; + min-width: 0; + min-height: 0; + position: relative; + contain: layout style; + overflow: hidden; +} + +.memberListDivider { + position: absolute; + top: 0; + bottom: 0; + right: var(--layout-member-list-width, 240px); + width: 1px; + background: var(--user-area-divider-color); + pointer-events: none; + z-index: 5; +} + +/* Hide the members-panel divider alongside the panel on narrow + viewports — the useIsNarrowScreen hook in ChannelView.tsx skips + rendering the panel, so this divider would otherwise be left + hanging over the edge of the chat column. */ +@media (max-width: 1024px) { + .memberListDivider { + display: none; + } +} + +/* ── Voice channel join prompt ─────────────────────────────── */ + +.voiceJoinPrompt { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 32px; + background-color: var(--background-secondary-lighter, var(--background-primary)); + gap: 12px; +} + +.voiceJoinIcon { + color: var(--text-tertiary); + margin-bottom: 8px; +} + +.voiceJoinTitle { + font-size: 1.5rem; + font-weight: 700; + color: var(--text-primary); + margin: 0; +} + +.voiceJoinDescription { + font-size: 0.9375rem; + color: var(--text-secondary); + margin: 0 0 16px; + text-align: center; + max-width: 360px; +} + +.voiceJoinButton { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 160px; + height: 44px; + padding: 0 24px; + border-radius: var(--radius-md, 8px); + border: none; + background-color: var(--brand-primary); + color: #fff; + font-size: 0.9375rem; + font-weight: 600; + font-family: inherit; + cursor: pointer; + transition: filter 150ms; +} + +.voiceJoinButton:hover:not(:disabled) { + filter: brightness(1.1); +} + +.voiceJoinButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} diff --git a/packages/shared/src/components/channel/ChannelView.tsx b/packages/shared/src/components/channel/ChannelView.tsx new file mode 100644 index 0000000..87924f6 --- /dev/null +++ b/packages/shared/src/components/channel/ChannelView.tsx @@ -0,0 +1,125 @@ +import { useQuery } from 'convex/react'; +import { SpeakerHigh } from '@phosphor-icons/react'; +import { useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { api } from '../../../../../convex/_generated/api'; +import { useVoice } from '../../contexts/VoiceContext'; +import { useIsNarrowScreen } from '../../hooks/useIsNarrowScreen'; +import { ChannelChatLayout } from './ChannelChatLayout'; +import { ChannelDetailsDrawer } from './ChannelDetailsDrawer'; +import { ChannelHeader } from './ChannelHeader'; +import { MemberListContainer } from '../member/MemberListContainer'; +import { SearchPanel } from './SearchPanel'; +import { VoiceCallView } from '../voice/VoiceCallView'; +import styles from './ChannelView.module.css'; + +/** + * ChannelView — the main content column for a selected channel. Shows + * either the voice-join prompt, an active voice call, or a text chat + * (header + messages + textarea + members panel). + */ +export function ChannelView() { + const { channelId } = useParams<{ channelId: string }>(); + const voice = useVoice(); + const [membersVisible, setMembersVisible] = useState(true); + const [detailsOpen, setDetailsOpen] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + const isNarrow = useIsNarrowScreen(); + const showMembers = membersVisible && !isNarrow; + const searchOpen = searchQuery.trim().length > 0; + + const channel = useQuery( + api.channels.get, + channelId ? { id: channelId as any } : 'skip', + ); + + if (!channelId) return null; + + const isVoiceChannel = channel?.type === 'voice'; + const isDM = channel?.type === 'dm'; + const isInThisVoiceCall = voice?.activeChannelId === channelId; + + // DMs never show a members panel — there are only two participants + // and they're already represented by the DM header + yourself. + const showMembersPanel = showMembers && !isDM; + + const handleJoinVoice = () => { + if (!voice || !channel) return; + const myId = typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + if (!myId) return; + void voice.connectToVoice?.(channelId, channel.name, myId, false); + }; + + return ( + <div className={styles.container}> + {/* Voice channels we're actively in skip the chat-style header + entirely — the search bar, pins button, members toggle, + and topic string all belong to text channels. A voice call + owns the whole surface via VoiceCallView so there's no + room for that chrome anyway. */} + {(!isVoiceChannel || !isInThisVoiceCall) && ( + <ChannelHeader + channel={channel as any} + serverId={undefined} + onOpenChannelDetails={() => setDetailsOpen(true)} + onOpenSearchDrawer={() => {}} + membersVisible={showMembersPanel} + onToggleMembers={() => setMembersVisible((v) => !v)} + hideMembersButton={isDM} + isNarrow={isNarrow} + searchQuery={searchQuery} + onSearchChange={setSearchQuery} + onSearchClear={() => setSearchQuery('')} + /> + )} + <div className={styles.body}> + {isVoiceChannel ? ( + isInThisVoiceCall ? ( + <VoiceCallView channelId={channelId} /> + ) : ( + <div className={styles.voiceJoinPrompt}> + <SpeakerHigh size={64} weight="fill" className={styles.voiceJoinIcon} /> + <h2 className={styles.voiceJoinTitle}>{channel?.name || 'Voice Channel'}</h2> + <p className={styles.voiceJoinDescription}>Click below to join the voice channel.</p> + <button className={styles.voiceJoinButton} onClick={handleJoinVoice}> + Join Voice + </button> + </div> + ) + ) : ( + <> + <ChannelChatLayout channelId={channelId} /> + {searchOpen ? ( + <> + <div className={styles.memberListDivider} /> + <SearchPanel + channelId={channelId} + query={searchQuery} + onClose={() => setSearchQuery('')} + /> + </> + ) : ( + showMembersPanel && ( + <> + <div className={styles.memberListDivider} /> + <MemberListContainer + members={[]} + channelId={channelId} + serverId={undefined} + /> + </> + ) + )} + </> + )} + </div> + <ChannelDetailsDrawer + isOpen={detailsOpen} + onClose={() => setDetailsOpen(false)} + channelId={channelId} + channelName={channel?.name} + channelType={channel?.type} + /> + </div> + ); +} diff --git a/packages/shared/src/components/channel/ChannelWelcomeSection.module.css b/packages/shared/src/components/channel/ChannelWelcomeSection.module.css new file mode 100644 index 0000000..0a7ddd9 --- /dev/null +++ b/packages/shared/src/components/channel/ChannelWelcomeSection.module.css @@ -0,0 +1,173 @@ +.container { + padding: 16px 16px 0; + margin-bottom: 8px; +} + +.iconWrapper { + width: 68px; + height: 68px; + border-radius: 50%; + background-color: var(--background-modifier-accent); + display: flex; + align-items: center; + justify-content: center; + color: var(--text-primary); + margin-bottom: 8px; +} + +.title { + font-size: 2rem; + font-weight: 800; + color: var(--text-primary); + margin: 0 0 8px; +} + +.description { + font-size: 0.9375rem; + color: var(--text-secondary); + margin: 0; + line-height: 1.4; +} + +/* ── Personal Notes variant ───────────────────────────────────────── + When the channel is the Personal Notes room we render a + centred empty state instead of the left-aligned channel welcome: + a big brand-primary square with the NotePencil icon, the + "Personal Notes" title centred below it, a thin divider, and + the Fluxer-style tagline. Matches the reference screenshot. */ + +.personalNotesContainer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 48px 32px; + text-align: center; +} + +.personalNotesIconWrapper { + width: 72px; + height: 72px; + border-radius: 50%; + background-color: var(--brand-primary); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 16px; +} + +.personalNotesTitle { + font-size: 1.75rem; + font-weight: 800; + color: var(--text-primary); + margin: 0 0 12px; +} + +.personalNotesDivider { + width: 40px; + height: 2px; + background-color: var(--background-modifier-accent, rgba(255, 255, 255, 0.1)); + border-radius: 1px; + margin: 0 0 12px; +} + +.personalNotesDescription { + font-size: 0.9375rem; + color: var(--text-primary-muted, var(--text-secondary)); + margin: 0; + max-width: 280px; + line-height: 1.4; +} + +/* ── 1:1 DM variant ────────────────────────────────────────────────── + Rendered when the channel is a 1:1 DM. Matches the Fluxer mobile + reference: centred layout, large user avatar, username + handle + line, single-line welcome text, and a "Remove Friend" action + button. */ + +.dmContainer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 48px 32px 24px; + text-align: center; +} + +.dmAvatar { + margin-bottom: 18px; +} + +.dmNameRow { + display: flex; + align-items: baseline; + gap: 4px; + margin-bottom: 10px; + flex-wrap: wrap; + justify-content: center; +} + +.dmName { + font-size: 1.75rem; + font-weight: 800; + color: var(--text-primary); + margin: 0; +} + +.dmHandle { + font-size: 1.125rem; + font-weight: 600; + color: var(--text-primary-muted, var(--text-secondary)); + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; +} + +.dmDescription { + font-size: 0.9375rem; + color: var(--text-primary-muted, var(--text-secondary)); + margin: 0 0 18px; + line-height: 1.5; + max-width: 320px; +} + +.dmDescription strong { + color: var(--text-primary); + font-weight: 700; +} + +.removeFriendButton { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 10px 18px; + background-color: var(--background-secondary-alt); + border: none; + border-radius: 0.5rem; + color: var(--text-primary); + font: inherit; + font-size: 14px; + font-weight: 700; + cursor: pointer; + transition: background-color 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.removeFriendButton:hover, +.removeFriendButton:active { + background-color: var(--background-modifier-hover); +} + +.removeFriendButton:disabled { + opacity: 0.55; + cursor: default; +} + +.dmStatus { + margin-top: 10px; + font-size: 12px; + color: var(--text-primary-muted, var(--text-secondary)); +} + +.dmStatusError { + color: hsl(0, calc(80% * var(--saturation-factor)), 70%); +} diff --git a/packages/shared/src/components/channel/ChannelWelcomeSection.tsx b/packages/shared/src/components/channel/ChannelWelcomeSection.tsx new file mode 100644 index 0000000..95d6b6f --- /dev/null +++ b/packages/shared/src/components/channel/ChannelWelcomeSection.tsx @@ -0,0 +1,124 @@ +/** + * ChannelWelcomeSection — the "start of channel" header rendered above + * the first message in any channel view. Two variants: + * + * - DM (1:1) → centred avatar + display name + "@username" handle + + * "This is the beginning of your direct message history with Name." + * - Text channel → "Welcome to #channel-name!" with the Hash icon + * and a short description. + * + * Voice channels never reach the message list so they never need this. + */ +import { useMemo } from 'react'; +import { useQuery } from 'convex/react'; +import { Hash } from '@phosphor-icons/react'; +import { Avatar } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import { useOnlineUsers } from '../../contexts/PresenceContext'; +import styles from './ChannelWelcomeSection.module.css'; + +interface ChannelWelcomeSectionProps { + channelId: string; + channelName?: string; + channelType?: string; +} + +function mapPresence( + status: string | undefined, +): 'online' | 'idle' | 'dnd' | 'offline' { + switch (status) { + case 'online': + return 'online'; + case 'idle': + return 'idle'; + case 'dnd': + return 'dnd'; + default: + return 'offline'; + } +} + +export function ChannelWelcomeSection({ + channelId, + channelName, + channelType, +}: ChannelWelcomeSectionProps) { + if (channelType === 'dm') { + return <DmWelcome channelId={channelId} channelName={channelName} />; + } + + const name = channelName || 'channel'; + return ( + <div className={styles.container}> + <div className={styles.iconWrapper}> + <Hash size={42} weight="bold" /> + </div> + <h1 className={styles.title}>Welcome to #{name}!</h1> + <p className={styles.description}> + This is the start of the #{name} channel. All messages are + end-to-end encrypted. + </p> + </div> + ); +} + +interface DmWelcomeProps { + channelId: string; + channelName?: string; +} + +function DmWelcome({ channelId, channelName }: DmWelcomeProps) { + const { resolveStatus } = useOnlineUsers(); + const myUserId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + + const dmRows = useQuery( + api.dms.listDMs, + myUserId ? { userId: myUserId as any } : 'skip', + ); + const allUsers = useQuery(api.auth.getPublicKeys) ?? []; + + const other = useMemo(() => { + if (!dmRows) return null; + const row = (dmRows as any[]).find((r) => r.channel_id === channelId); + if (!row) return null; + const profile = allUsers.find((u) => u.id === row.other_user_id); + const username = profile?.username || row.other_username || ''; + return { + userId: row.other_user_id as string, + displayName: username || channelName || 'this user', + username, + avatarUrl: profile?.avatarUrl ?? row.other_user_avatar_url ?? null, + status: + (profile?.status as string | undefined) || + (row.other_user_status as string | undefined) || + 'offline', + }; + }, [dmRows, allUsers, channelId, channelName]); + + const presence = mapPresence( + other ? resolveStatus(other.status, other.userId) : 'offline', + ); + + const displayName = other?.displayName ?? channelName ?? 'this user'; + + return ( + <div className={styles.dmContainer}> + <div className={styles.dmAvatar}> + <Avatar + src={other?.avatarUrl ?? null} + fallback={displayName} + size={80} + status={presence} + /> + </div> + <div className={styles.dmNameRow}> + <h1 className={styles.dmName}>{displayName}</h1> + </div> + <p className={styles.dmDescription}> + This is the beginning of your direct message history with{' '} + <strong>{displayName}</strong>. + </p> + </div> + ); +} diff --git a/packages/shared/src/components/channel/CreatePollModal.module.css b/packages/shared/src/components/channel/CreatePollModal.module.css new file mode 100644 index 0000000..71cedaa --- /dev/null +++ b/packages/shared/src/components/channel/CreatePollModal.module.css @@ -0,0 +1,154 @@ +.body { + display: flex; + flex-direction: column; + gap: 1rem; + padding: 0.5rem 0.25rem; +} + +.field { + display: flex; + flex-direction: column; + gap: 0.375rem; +} + +.label { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--text-primary-muted, var(--text-muted)); +} + +.input, +.textarea { + width: 100%; + padding: 0.625rem 0.75rem; + border: 1px solid var(--background-modifier-accent); + border-radius: 0.375rem; + background-color: var(--background-primary); + color: var(--text-primary); + font: inherit; + font-size: 0.875rem; + outline: none; + transition: border-color 0.12s; +} + +.input:focus, +.textarea:focus { + border-color: var(--brand-primary, #4641d9); +} + +.answerList { + display: flex; + flex-direction: column; + gap: 0.375rem; +} + +.answerRow { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.answerRow .input { + flex: 1; +} + +.removeButton { + flex-shrink: 0; + width: 2rem; + height: 2rem; + display: inline-flex; + align-items: center; + justify-content: center; + border: 1px solid var(--background-modifier-accent); + border-radius: 0.375rem; + background-color: transparent; + color: var(--text-tertiary); + cursor: pointer; + transition: color 0.12s, border-color 0.12s; +} + +.removeButton:hover:not(:disabled) { + color: var(--status-danger); + border-color: var(--status-danger); +} + +.removeButton:disabled { + opacity: 0.35; + cursor: not-allowed; +} + +.addButton { + align-self: flex-start; + margin-top: 0.125rem; + padding: 0.375rem 0.75rem; + border: 1px dashed var(--background-modifier-accent); + border-radius: 0.375rem; + background-color: transparent; + color: var(--text-tertiary); + font: inherit; + font-size: 0.8125rem; + font-weight: 600; + cursor: pointer; + transition: color 0.12s, border-color 0.12s; +} + +.addButton:hover:not(:disabled) { + color: var(--text-primary); + border-color: var(--brand-primary, #4641d9); +} + +.addButton:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.toggles { + display: flex; + flex-direction: column; + gap: 0.625rem; +} + +.toggleRow { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; +} + +.toggleLabel { + display: flex; + flex-direction: column; + gap: 0.125rem; +} + +.toggleName { + font-size: 0.875rem; + font-weight: 600; + color: var(--text-primary); +} + +.toggleHint { + font-size: 0.75rem; + color: var(--text-tertiary); +} + +.toggleInput { + width: 1.125rem; + height: 1.125rem; + accent-color: var(--brand-primary, #4641d9); + cursor: pointer; +} + +.actions { + display: flex; + justify-content: flex-end; + gap: 0.5rem; + margin-top: 0.25rem; +} + +.error { + color: var(--status-danger); + font-size: 0.8125rem; +} diff --git a/packages/shared/src/components/channel/CreatePollModal.tsx b/packages/shared/src/components/channel/CreatePollModal.tsx new file mode 100644 index 0000000..92f5b24 --- /dev/null +++ b/packages/shared/src/components/channel/CreatePollModal.tsx @@ -0,0 +1,237 @@ +/** + * CreatePollModal — compose a new MSC3381 poll. + * + * Lives next to ChannelTextarea; the composer's poll button opens + * it for the active channel. Keeps its own local form state and + * hands the final payload off to `MessageActionCreators.sendPollStart` + * on submit. Closes itself on success, surfaces errors inline. + */ +import { useEffect, useMemo, useState } from 'react'; +import { useMutation } from 'convex/react'; +import { Plus, X } from '@phosphor-icons/react'; +import { Button, Modal } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import styles from './CreatePollModal.module.css'; + +interface PollAnswer { + id: string; + text: string; +} + +interface CreatePollModalProps { + isOpen: boolean; + onClose: () => void; + channelId: string; +} + +const MIN_ANSWERS = 2; +const MAX_ANSWERS = 20; +const MAX_QUESTION_LEN = 340; +const MAX_ANSWER_LEN = 340; + +/** Short random string for answer ids. Matches Element's format. */ +function makeAnswerId(): string { + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let out = ''; + for (let i = 0; i < 16; i++) { + out += chars[Math.floor(Math.random() * chars.length)]; + } + return out; +} + +export function CreatePollModal({ isOpen, onClose, channelId }: CreatePollModalProps) { + const createPoll = useMutation(api.polls.create); + const [question, setQuestion] = useState(''); + // Each draft answer carries a stable id even before submit so + // React doesn't reshuffle focus between rows when the list + // mutates. The ids flow straight into the outgoing poll.start. + const [answers, setAnswers] = useState<PollAnswer[]>(() => [ + { id: makeAnswerId(), text: '' }, + { id: makeAnswerId(), text: '' }, + ]); + const [allowMultiple, setAllowMultiple] = useState(false); + const [disclosed, setDisclosed] = useState(true); + const [submitting, setSubmitting] = useState(false); + const [error, setError] = useState<string | null>(null); + + // Reset the form when the modal opens so previous attempts don't + // linger. Running on `isOpen === true` only avoids wiping the + // form while the user's submit is in flight. + useEffect(() => { + if (!isOpen) return; + setQuestion(''); + setAnswers([ + { id: makeAnswerId(), text: '' }, + { id: makeAnswerId(), text: '' }, + ]); + setAllowMultiple(false); + setDisclosed(true); + setSubmitting(false); + setError(null); + }, [isOpen]); + + const trimmedQuestion = question.trim(); + const validAnswers = useMemo( + () => answers.map((a) => ({ ...a, text: a.text.trim() })).filter((a) => a.text.length > 0), + [answers], + ); + const canSubmit = + trimmedQuestion.length > 0 && + validAnswers.length >= MIN_ANSWERS && + !submitting; + + const setAnswerText = (id: string, text: string) => { + setAnswers((prev) => + prev.map((a) => (a.id === id ? { ...a, text: text.slice(0, MAX_ANSWER_LEN) } : a)), + ); + }; + + const addAnswer = () => { + setAnswers((prev) => + prev.length >= MAX_ANSWERS ? prev : [...prev, { id: makeAnswerId(), text: '' }], + ); + }; + + const removeAnswer = (id: string) => { + setAnswers((prev) => + prev.length <= MIN_ANSWERS ? prev : prev.filter((a) => a.id !== id), + ); + }; + + const handleSubmit = async () => { + if (!canSubmit) return; + const myUserId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + if (!myUserId) { + setError('You must be signed in to create a poll.'); + return; + } + setSubmitting(true); + setError(null); + try { + await createPoll({ + channelId: channelId as Id<'channels'>, + createdBy: myUserId as Id<'userProfiles'>, + question: trimmedQuestion, + options: validAnswers.map((a) => ({ id: a.id, text: a.text })), + allowMultiple, + disclosed, + }); + onClose(); + } catch (err: any) { + setError(err?.message || 'Failed to create poll.'); + } finally { + setSubmitting(false); + } + }; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="small"> + <Modal.Header title="Create Poll" onClose={onClose} /> + <Modal.Content> + <div className={styles.body}> + <div className={styles.field}> + <label className={styles.label} htmlFor="poll-question"> + Question + </label> + <input + id="poll-question" + className={styles.input} + value={question} + placeholder="What should we have for lunch?" + maxLength={MAX_QUESTION_LEN} + onChange={(e) => setQuestion(e.target.value)} + disabled={submitting} + /> + </div> + + <div className={styles.field}> + <div className={styles.label}>Answers</div> + <div className={styles.answerList}> + {answers.map((answer, i) => ( + <div key={answer.id} className={styles.answerRow}> + <input + className={styles.input} + value={answer.text} + placeholder={`Answer ${i + 1}`} + onChange={(e) => setAnswerText(answer.id, e.target.value)} + disabled={submitting} + /> + <button + type="button" + className={styles.removeButton} + onClick={() => removeAnswer(answer.id)} + disabled={submitting || answers.length <= MIN_ANSWERS} + aria-label={`Remove answer ${i + 1}`} + > + <X size={14} weight="bold" /> + </button> + </div> + ))} + </div> + <button + type="button" + className={styles.addButton} + onClick={addAnswer} + disabled={submitting || answers.length >= MAX_ANSWERS} + > + <Plus size={14} weight="bold" style={{ marginRight: 4, verticalAlign: 'middle' }} /> + Add answer + </button> + </div> + + <div className={styles.toggles}> + <label className={styles.toggleRow}> + <span className={styles.toggleLabel}> + <span className={styles.toggleName}>Allow multiple answers</span> + <span className={styles.toggleHint}> + Voters can select more than one option. + </span> + </span> + <input + type="checkbox" + className={styles.toggleInput} + checked={allowMultiple} + onChange={(e) => setAllowMultiple(e.target.checked)} + disabled={submitting} + /> + </label> + <label className={styles.toggleRow}> + <span className={styles.toggleLabel}> + <span className={styles.toggleName}>Show results in real time</span> + <span className={styles.toggleHint}> + If off, vote counts stay hidden until you end the poll. + </span> + </span> + <input + type="checkbox" + className={styles.toggleInput} + checked={disclosed} + onChange={(e) => setDisclosed(e.target.checked)} + disabled={submitting} + /> + </label> + </div> + + {error && <div className={styles.error}>{error}</div>} + + <div className={styles.actions}> + <Button variant="secondary" size="sm" onClick={onClose} disabled={submitting}> + Cancel + </Button> + <Button + variant="primary" + size="sm" + onClick={handleSubmit} + disabled={!canSubmit} + loading={submitting} + > + Create + </Button> + </div> + </div> + </Modal.Content> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/channel/CustomEmojiImage.tsx b/packages/shared/src/components/channel/CustomEmojiImage.tsx new file mode 100644 index 0000000..dae8326 --- /dev/null +++ b/packages/shared/src/components/channel/CustomEmojiImage.tsx @@ -0,0 +1,25 @@ +/** + * CustomEmojiImage — renders a custom emoji image given an `mxc://` + * URL (or a raw http URL), resolving through the authenticated media + * endpoint via `useMxcUrl`. While the blob is being fetched the img + * has no src, so the browser draws nothing; once resolved the image + * pops in. Cached across the tab lifetime so repeat renders are + * instant. + * + * Forwards all other img props (className, alt, title, draggable, + * width/height, etc.), so callers can style the element normally. + */ +import { useMxcUrl } from '@app/hooks/useMxcUrl'; + +interface CustomEmojiImageProps + extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, 'src'> { + /** Either an mxc:// URL (will be resolved via auth) or a direct http URL. */ + mxc: string; +} + +export function CustomEmojiImage({ mxc, alt = '', ...rest }: CustomEmojiImageProps) { + const src = useMxcUrl(mxc); + // Don't set src until it resolves — otherwise the browser eagerly + // fetches an empty string and logs an error. + return <img src={src || undefined} alt={alt} {...rest} />; +} diff --git a/packages/shared/src/components/channel/EditAttachmentModal.module.css b/packages/shared/src/components/channel/EditAttachmentModal.module.css new file mode 100644 index 0000000..849f18b --- /dev/null +++ b/packages/shared/src/components/channel/EditAttachmentModal.module.css @@ -0,0 +1,180 @@ +/* ── Edit Attachment modal body ────────────────────────────── + Chrome (backdrop, centring, header X) comes from Modal.Root. + This module styles the form controls inside Modal.Content. */ + +.body { + display: flex; + flex-direction: column; + gap: 16px; + padding: 0; +} + +.field { + display: flex; + flex-direction: column; + gap: 6px; +} + +.labelRow { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.label { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--text-primary-muted, #a0a3a8); +} + +.counter { + font-size: 0.6875rem; + font-weight: 500; + color: var(--text-tertiary, #a0a3a8); + font-variant-numeric: tabular-nums; +} + +.input { + height: 40px; + padding: 0 12px; + background-color: var(--form-surface-background, #1e2024); + border: 1px solid var(--background-modifier-accent); + border-radius: 6px; + color: var(--text-primary); + font: inherit; + font-size: 0.9375rem; + outline: none; + transition: border-color 0.12s; +} + +.input:focus, +.input:focus-visible { + outline: none; + border-color: var(--brand-primary); +} + +.textarea { + min-height: 80px; + padding: 10px 12px; + background-color: var(--form-surface-background, #1e2024); + border: 1px solid var(--background-modifier-accent); + border-radius: 6px; + color: var(--text-primary); + font: inherit; + font-size: 0.9375rem; + resize: vertical; + outline: none; + transition: border-color 0.12s; +} + +.textarea:focus, +.textarea:focus-visible { + outline: none; + border-color: var(--brand-primary); +} + +.textarea::placeholder { + color: var(--text-tertiary); +} + +/* ── Spoiler toggle row ─────────────────────────────────────── + Fluxer-style iOS switch. Left label + right track-and-thumb. */ +.spoilerRow { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 8px 0; +} + +.spoilerLabel { + font-size: 0.9375rem; + font-weight: 600; + color: var(--text-primary); + cursor: pointer; +} + +.toggle { + position: relative; + width: 36px; + height: 20px; + padding: 0; + background-color: var(--background-modifier-accent); + border: none; + border-radius: 999px; + cursor: pointer; + transition: background-color 0.18s ease; + flex-shrink: 0; +} + +.toggleOn { + background-color: var(--brand-primary); +} + +.toggleThumb { + position: absolute; + top: 2px; + left: 2px; + width: 16px; + height: 16px; + border-radius: 50%; + background-color: #fff; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35); + transition: transform 0.18s ease; +} + +.toggleOn .toggleThumb { + transform: translateX(16px); +} + +/* ── Footer actions ───────────────────────────────────────── */ +.actions { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 8px; + margin-top: 4px; +} + +.secondaryButton { + height: 40px; + padding: 0 18px; + background: var(--background-secondary-alt); + border: none; + border-radius: 6px; + color: var(--text-primary); + font: inherit; + font-size: 0.875rem; + font-weight: 700; + cursor: pointer; + transition: background-color 0.12s; +} + +.secondaryButton:hover { + background-color: var(--background-modifier-hover); +} + +.primaryButton { + height: 40px; + padding: 0 20px; + background-color: var(--brand-primary); + border: none; + border-radius: 6px; + color: var(--text-on-brand-primary, #fff); + font: inherit; + font-size: 0.875rem; + font-weight: 700; + cursor: pointer; + transition: filter 0.12s; +} + +.primaryButton:hover { + filter: brightness(1.08); +} + +.primaryButton:active { + filter: brightness(0.92); +} diff --git a/packages/shared/src/components/channel/EditAttachmentModal.tsx b/packages/shared/src/components/channel/EditAttachmentModal.tsx new file mode 100644 index 0000000..c739b8a --- /dev/null +++ b/packages/shared/src/components/channel/EditAttachmentModal.tsx @@ -0,0 +1,144 @@ +/** + * EditAttachmentModal — opened from a PendingAttachmentCard's + * "Edit" hover button. Lets the user rename the outgoing file, + * add an alt-text description, and toggle the Mark-as-Spoiler + * flag before sending. + * + * The Alt Text Description field is hidden for audio attachments + * per the user's spec — audio doesn't carry a visual payload so + * the accessibility angle doesn't apply. + * + * Saving writes the edits back to PendingAttachmentStore via + * `updateAttachment`, which re-renders the affected card + * reactively without any prop wiring. + */ +import { useEffect, useState } from 'react'; +import { Modal } from '@brycord/ui'; +import PendingAttachmentStore, { + type PendingAttachment, +} from '@app/stores/PendingAttachmentStore'; +import styles from './EditAttachmentModal.module.css'; + +const ALT_TEXT_MAX = 4096; + +interface EditAttachmentModalProps { + isOpen: boolean; + onClose: () => void; + channelId: string; + attachment: PendingAttachment | null; +} + +export function EditAttachmentModal({ + isOpen, + onClose, + channelId, + attachment, +}: EditAttachmentModalProps) { + const [filename, setFilename] = useState(''); + const [altText, setAltText] = useState(''); + const [isSpoiler, setIsSpoiler] = useState(false); + + // Hydrate the form every time the modal opens on a (possibly + // different) attachment. Without this re-sync, the user's last + // edit on a previous card would leak into the next one. + useEffect(() => { + if (!isOpen || !attachment) return; + setFilename(attachment.filename); + setAltText(attachment.altText); + setIsSpoiler(attachment.isSpoiler); + }, [isOpen, attachment]); + + if (!attachment) return null; + + const hideAltText = attachment.kind === 'audio'; + + const handleSave = () => { + PendingAttachmentStore.updateAttachment(channelId, attachment.id, { + filename: filename.trim() || attachment.file.name, + altText: altText.trim(), + isSpoiler, + }); + onClose(); + }; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="small"> + <Modal.Header title="Edit Attachment" onClose={onClose} /> + <Modal.Content> + <div className={styles.body}> + <div className={styles.field}> + <label className={styles.label} htmlFor="edit-att-filename"> + Filename + </label> + <input + id="edit-att-filename" + type="text" + className={styles.input} + value={filename} + onChange={(e) => setFilename(e.target.value)} + autoFocus + /> + </div> + + {!hideAltText && ( + <div className={styles.field}> + <div className={styles.labelRow}> + <label className={styles.label} htmlFor="edit-att-alt"> + Alt Text Description + </label> + <span className={styles.counter}> + {altText.length}/{ALT_TEXT_MAX} + </span> + </div> + <textarea + id="edit-att-alt" + className={styles.textarea} + placeholder="Describe this media for screen readers" + value={altText} + onChange={(e) => + setAltText(e.target.value.slice(0, ALT_TEXT_MAX)) + } + rows={3} + /> + </div> + )} + + <div className={styles.spoilerRow}> + <label className={styles.spoilerLabel} htmlFor="edit-att-spoiler"> + Mark as Spoiler + </label> + <button + id="edit-att-spoiler" + type="button" + className={`${styles.toggle} ${ + isSpoiler ? styles.toggleOn : '' + }`} + onClick={() => setIsSpoiler((v) => !v)} + aria-pressed={isSpoiler} + aria-label="Mark as spoiler" + > + <span className={styles.toggleThumb} /> + </button> + </div> + + <div className={styles.actions}> + <button + type="button" + className={styles.secondaryButton} + onClick={onClose} + > + Cancel + </button> + <button + type="button" + className={styles.primaryButton} + onClick={handleSave} + > + Save + </button> + </div> + </div> + </Modal.Content> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/channel/EmojiPicker.module.css b/packages/shared/src/components/channel/EmojiPicker.module.css new file mode 100644 index 0000000..d33a02e --- /dev/null +++ b/packages/shared/src/components/channel/EmojiPicker.module.css @@ -0,0 +1,581 @@ +/* ── Desktop picker shell ───────────────────────────────────────── + Fluxer-style expression picker. Top stripe of tabs (GIFs / Media + / Stickers / Emojis), search bar below, main row with a vertical + category sidebar on the left and a collapsible emoji grid on the + right, hover inspector pinned at the bottom. */ +.picker { + width: 480px; + height: 460px; + display: flex; + flex-direction: column; + background-color: var(--background-tertiary); + border: 1px solid var(--background-modifier-accent); + border-radius: 10px; + box-shadow: + 0 8px 24px rgba(0, 0, 0, 0.35), + 0 0 0 1px rgba(0, 0, 0, 0.3); + overflow: hidden; +} + +/* Mobile variant: fill the parent (a BottomSheet body), no fixed + dimensions, no rounding/shadow — the sheet owns those. */ +.pickerMobile { + width: 100%; + height: 100%; + max-width: 100%; + max-height: 100%; + border: none; + border-radius: 0; + box-shadow: none; + background-color: transparent; +} + +/* ── Top tab bar (GIFs / Media / Stickers / Emojis) ───────────── */ +.tabBar { + display: flex; + align-items: center; + gap: 4px; + padding: 10px 12px 0; + flex-shrink: 0; +} + +.tabButton { + padding: 6px 12px; + border: none; + border-radius: 6px; + background: transparent; + color: var(--text-primary-muted, #a0a3a8); + font: inherit; + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + transition: background-color 0.12s, color 0.12s; +} + +.tabButton:hover { + color: var(--text-primary); + background-color: var(--background-modifier-hover); +} + +.tabButtonActive { + color: var(--text-primary); + background-color: var(--background-modifier-selected, var(--background-modifier-hover)); +} + +/* ── Search row ───────────────────────────────────────────────── */ +.searchRow { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 12px; + flex-shrink: 0; + border-bottom: 1px solid var(--background-modifier-hover); +} + +/* On the Media tab the divider moves down one level to the filter + chip row (which sits between the search bar and the grid), so + the search row itself drops its bottom border to avoid a double + line. */ +.searchRowFlush { + border-bottom: none; +} + +.searchBar { + flex: 1; + display: flex; + align-items: center; + gap: 8px; + min-height: 44px; + padding: 0 12px; + border-radius: 8px; + border: 1px solid var(--background-modifier-accent); + background-color: color-mix(in srgb, var(--form-surface-background) 85%, black); +} + +.searchIcon { + flex-shrink: 0; + color: var(--text-primary-muted, #a0a3a8); +} + +.searchInput { + flex: 1; + height: 100%; + background: transparent; + border: none; + padding: 0; + font-size: 0.875rem; + color: var(--text-primary); + outline: none; + font-family: inherit; +} + +/* Kill the browser's default focus ring — the search bar's own + border already signals the interactive state, a blue outline + on top of it reads as a bug. */ +.searchInput:focus, +.searchInput:focus-visible { + outline: none; + box-shadow: none; + border: none; +} + +.searchInput::placeholder { + color: var(--text-tertiary); +} + +.searchInput:disabled { + cursor: not-allowed; + opacity: 0.6; +} + +.searchClear { + display: flex; + align-items: center; + justify-content: center; + background: none; + border: none; + cursor: pointer; + color: var(--text-tertiary); + padding: 2px; +} + +.searchClear:hover { + color: var(--text-primary); +} + +/* ── Main area (sidebar + grid) ───────────────────────────────── */ +.main { + flex: 1; + min-height: 0; + display: flex; + align-items: stretch; +} + +/* Vertical category sidebar — narrow column of icon buttons on + the left edge. The active category pill is a rounded square + matching Fluxer. */ +.sideBar { + display: flex; + flex-direction: column; + gap: 4px; + padding: 4px 6px; + width: 48px; + flex-shrink: 0; + overflow-y: auto; + scrollbar-width: none; + background-color: var(--background-primary); + box-shadow: inset -1px 0 0 var(--background-modifier-accent); +} + +.sideBar::-webkit-scrollbar { + display: none; +} + +.sideTab { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border: none; + border-radius: 8px; + background: transparent; + color: var(--text-primary-muted, #a0a3a8); + cursor: pointer; + flex-shrink: 0; + transition: background-color 0.12s, color 0.12s; +} + +.sideTab:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +.sideTabActive { + background-color: var(--background-modifier-selected, var(--background-modifier-hover)); + color: var(--text-primary); +} + +/* ── Scrollable emoji grid ────────────────────────────────────── */ +.grid { + flex: 1; + min-height: 0; + overflow-y: auto; + padding: 4px 12px 12px; + -webkit-overflow-scrolling: touch; +} + +.pickerMobile .grid { + padding: 4px 12px; +} + +/* ── Collapsible section (desktop only) ───────────────────────── */ +.section { + margin-top: 4px; +} + +.sectionHeader { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + padding: 8px 4px; + background: transparent; + border: none; + cursor: pointer; + color: var(--text-primary); + font: inherit; + font-size: 0.9375rem; + font-weight: 700; + text-align: left; +} + +.sectionHeaderLabel { + flex-shrink: 0; +} + +.sectionHeaderCaret { + color: var(--text-primary-muted, #a0a3a8); + transition: transform 0.15s ease; +} + +/* Caret points DOWN when expanded (default), rotated to RIGHT when + the section is collapsed. Matches the Fluxer reference where + expanded sections show a `v` and collapsed sections show `>`. */ +.sectionHeaderCaretCollapsed { + transform: rotate(-90deg); +} + +/* Mobile sticky headers (legacy flat layout) reuse a subset of + the desktop header styles; they don't need the caret. */ +.categoryHeader { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + color: var(--text-tertiary); + letter-spacing: 0.02em; + padding: 8px 4px 4px; + position: sticky; + top: 0; + background-color: var(--background-secondary-lighter, var(--background-primary)); + z-index: 1; +} + +.emojiGrid { + display: grid; + grid-template-columns: repeat(9, 1fr); + gap: 0; +} + +.pickerMobile .emojiGrid { + grid-template-columns: repeat(auto-fill, minmax(40px, 1fr)); +} + +.emojiButton { + display: flex; + align-items: center; + justify-content: center; + aspect-ratio: 1; + font-size: 1.75rem; + line-height: 1; + border: none; + background: none; + border-radius: 6px; + cursor: pointer; + padding: 0; + transition: background-color 0.1s; +} + +.emojiButton:hover { + background-color: var(--background-modifier-hover); +} + +.noResults { + text-align: center; + color: var(--text-tertiary); + font-size: 0.875rem; + padding: 2rem; +} + +/* Placeholder rendered when the user clicks an unimplemented + top-level tab (GIFs / Media / Stickers). */ +.comingSoon { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + padding: 2rem; + text-align: center; + color: var(--text-primary-muted, #a0a3a8); + font-size: 0.9375rem; +} + +/* ── Mobile-only bottom category bar ──────────────────────────── */ +.categoryBar { + display: none; +} + +.pickerMobile .categoryBar { + display: flex; + border-top: 1px solid var(--background-modifier-accent); + padding: 8px 12px; + gap: 4px; + justify-content: space-between; + overflow-x: auto; + flex-shrink: 0; +} + +.pickerMobile .categoryTab { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: 6px; + background: none; + border: none; + cursor: pointer; + color: var(--text-tertiary); + flex-shrink: 0; + transition: color 0.15s, background-color 0.15s; +} + +.pickerMobile .categoryTab:hover { + color: var(--text-primary); + background-color: var(--background-modifier-hover); +} + +.pickerMobile .categoryTabActive { + color: var(--text-primary); + background-color: var(--background-modifier-selected); +} + +/* ── Media tab ──────────────────────────────────────────────── + Body content rendered when the user selects the top-level + "Media" tab. Saved-item grid + hover-delete X. The filter + chip row is rendered OUTSIDE this container (at the EmojiPicker + top level, directly under the search row), so `.mediaTab` only + owns the grid and its empty state. */ + +.mediaTab { + display: flex; + flex-direction: column; + gap: 12px; + padding: 8px 0; +} + +/* Filter chip row — sits between the search row and the main + content when `activeTab === 'media'`. Rendered at the picker + top level so it stays pinned while the grid scrolls. */ +.filterChips { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; + padding: 8px 12px; + flex-shrink: 0; + border-bottom: 1px solid var(--background-modifier-hover); +} + +/* Mobile: the sheet already owns its own horizontal padding and the + bottom sheet surface doesn't need a secondary divider under the + chips, so drop both so the chip row sits flush inside the sheet. */ +@media (max-width: 768px) { + .filterChips { + padding: 0; + border-bottom: none; + } +} + +.filterChip { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 10px; + background: transparent; + border: none; + border-radius: var(--radius-md, 0.375rem); + color: var(--text-primary-muted, #a0a3a8); + font: inherit; + font-size: 0.75rem; + font-weight: 600; + line-height: 1.25rem; + cursor: pointer; + transition: background-color 0.12s, color 0.12s; +} + +.filterChip:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +.filterChipActive { + background-color: var(--background-modifier-selected); + color: var(--text-primary); +} + +.filterChipActive:hover { + background-color: var(--background-modifier-selected); + color: var(--text-primary); +} + +/* Media tab background override — the emoji tab uses + `--background-tertiary` (inherited from `.picker`), but the + Media tab reads as a lighter surface more like the rest of + the app. Applied to the main row wrapper only when the user + is on the Media tab. */ +.mainMedia { + background-color: var(--background-primary); +} + +.mainMedia .grid { + background-color: var(--background-primary); +} + +.mediaGrid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); + gap: 8px; + padding: 0 4px 4px; +} + +.mediaCard { + position: relative; + aspect-ratio: 1; + border-radius: 8px; + overflow: hidden; + background-color: var(--background-secondary); + border: 1px solid var(--background-modifier-accent); + cursor: pointer; + transition: border-color 0.12s, transform 0.12s; +} + +.mediaCard:hover { + border-color: var(--brand-primary, #4641d9); +} + +.mediaCardImage { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +/* Small play badge overlay for video tiles in the Media tab. + Sits in the bottom-right corner of the tile so the thumbnail + reads as a video at a glance without mistaking it for a + still image. Only rendered when the card's kind is 'video'. */ +.mediaCardPlayBadge { + position: absolute; + bottom: 6px; + right: 6px; + display: inline-flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + border-radius: 50%; + background-color: rgba(0, 0, 0, 0.7); + color: #fff; + pointer-events: none; + /* Nudge the play triangle 1px right — same geometric- + centroid fix the AttachmentVideo play badge uses. */ + padding-left: 2px; + box-sizing: border-box; +} + +.mediaCardPlaceholder { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-primary-muted, #a0a3a8); + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.mediaCardRemove { + position: absolute; + top: 6px; + right: 6px; + display: inline-flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + padding: 0; + background-color: rgba(0, 0, 0, 0.7); + border: none; + border-radius: 50%; + color: #fff; + cursor: pointer; + opacity: 0; + visibility: hidden; + transition: opacity 0.12s ease, visibility 0.12s ease, background-color 0.12s; +} + +.mediaCard:hover .mediaCardRemove, +.mediaCard:focus-within .mediaCardRemove { + opacity: 1; + visibility: visible; +} + +.mediaCardRemove:hover { + background-color: var(--status-danger, #ed4245); +} + +/* ── Inspector bar ────────────────────────────────────────────── */ +.inspector { + display: flex; + align-items: center; + gap: 10px; + height: 44px; + padding: 0 14px; + border-top: 1px solid var(--background-modifier-accent); + background-color: var(--background-primary); + flex-shrink: 0; +} + +.inspectorEmoji { + display: inline-flex; + align-items: center; + font-size: 1.5rem; +} + +.inspectorName { + font-size: 0.875rem; + font-weight: 600; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.inspectorHint { + font-size: 0.8125rem; + color: var(--text-tertiary); +} + +/* ── Light theme overrides ──────────────────────────────────── + Promote the picker chrome rows (tab bar, search row, media-tab + filter chips) from the inherited --background-tertiary up to + --background-primary in light theme so the picker reads as a + brighter surface than the chat body instead of a dimmer gray + one. Dark theme keeps the original inherited look. */ +[data-theme='light'] .tabBar, +[data-theme='light'] .searchRow, +[data-theme='light'] .filterChips { + background-color: var(--background-primary); +} + +/* The inner rounded search input itself sits on top of the + now-white search row, so the dark-theme color-mix surface + reads as a muddy tan against it. Switch it to the hover + modifier shade so it stays distinct from the row without + looking off-palette. */ +[data-theme='light'] .searchBar { + background-color: var(--background-modifier-hover); +} diff --git a/packages/shared/src/components/channel/EmojiPicker.tsx b/packages/shared/src/components/channel/EmojiPicker.tsx new file mode 100644 index 0000000..60658ef --- /dev/null +++ b/packages/shared/src/components/channel/EmojiPicker.tsx @@ -0,0 +1,736 @@ +import { + Bicycle, + BowlFood, + CaretDown, + Clock, + Flag, + GameController, + Gif, + Heart, + ImageSquare, + Leaf, + Magnet, + MagnifyingGlass, + Smiley, + SmileyWink, + Sticker, + X, +} from '@phosphor-icons/react'; +import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'; +import { useQuery } from 'convex/react'; +import emojiData from '@app/data/emojis.json'; +import { api } from '../../../../../convex/_generated/api'; +import { GifPicker } from './GifPicker'; +import { TwemojiImg } from './TwemojiImg'; +import styles from './EmojiPicker.module.css'; + +export type ExpressionTab = 'gifs' | 'media' | 'stickers' | 'emojis'; + +export type EmojiPickerValue = + | { + kind: 'unicode'; + surrogates: string; + name: string; + } + | { + kind: 'custom'; + shortcode: string; + url: string; + } + | { + // Picked from the GIFs tab — composer should send this as + // a plain text URL so the existing link-embed renderer + // shows it inline. + kind: 'gif'; + url: string; + }; + +interface CustomEmojiListEntry { + _id: string; + name: string; + src: string; +} + +interface EmojiPickerProps { + onSelect: (value: EmojiPickerValue) => void; + onClose?: () => void; + mobile?: boolean; + initialTab?: ExpressionTab; + onTabChange?: (tab: ExpressionTab) => void; +} + +interface EmojiEntry { + names: string[]; + surrogates: string; +} + +type EmojiData = Record<string, EmojiEntry[]>; + +const EXPRESSION_TABS: Array<{ key: ExpressionTab; label: string }> = [ + { key: 'gifs', label: 'GIFs' }, + { key: 'media', label: 'Media' }, + { key: 'stickers', label: 'Stickers' }, + { key: 'emojis', label: 'Emojis' }, +]; + +interface CategoryDef { + key: string; + label: string; + icon: ReactNode; +} + +const CATEGORIES: CategoryDef[] = [ + { key: 'people', label: 'Smileys & People', icon: <Smiley size={20} /> }, + { key: 'nature', label: 'Animals & Nature', icon: <Leaf size={20} /> }, + { key: 'food', label: 'Food & Drink', icon: <BowlFood size={20} /> }, + { key: 'activity', label: 'Activities', icon: <GameController size={20} /> }, + { key: 'travel', label: 'Travel & Places', icon: <Bicycle size={20} /> }, + { key: 'objects', label: 'Objects', icon: <Magnet size={20} /> }, + { key: 'symbols', label: 'Symbols', icon: <Heart size={20} /> }, + { key: 'flags', label: 'Flags', icon: <Flag size={20} /> }, +]; + +const RECENTS_STORAGE_KEY = 'discord_clone_recent_emojis_v1'; +const MAX_RECENTS = 24; + +function loadRecents(): EmojiPickerValue[] { + if (typeof localStorage === 'undefined') return []; + try { + const raw = localStorage.getItem(RECENTS_STORAGE_KEY); + return raw ? (JSON.parse(raw) as EmojiPickerValue[]) : []; + } catch { + return []; + } +} + +function saveRecents(list: EmojiPickerValue[]) { + if (typeof localStorage === 'undefined') return; + try { + localStorage.setItem(RECENTS_STORAGE_KEY, JSON.stringify(list.slice(0, MAX_RECENTS))); + } catch {} +} + +export function EmojiPicker({ + onSelect, + onClose, + mobile = false, + initialTab = 'emojis', + onTabChange, +}: EmojiPickerProps) { + const [activeTab, setActiveTabState] = useState<ExpressionTab>(initialTab); + const setActiveTab = (tab: ExpressionTab) => { + setActiveTabState(tab); + onTabChange?.(tab); + }; + // Allow the parent to imperatively switch tabs by changing + // `initialTab` while the picker is mounted — used by the composer + // when the user clicks a different expression-button without + // closing the picker first. + useEffect(() => { + setActiveTabState(initialTab); + }, [initialTab]); + const [search, setSearch] = useState(''); + const [activeCategory, setActiveCategory] = useState<string>('people'); + const [collapsed, setCollapsed] = useState<Set<string>>(new Set()); + const [hovered, setHovered] = useState<EmojiPickerValue | null>(null); + const [recents, setRecents] = useState<EmojiPickerValue[]>(() => loadRecents()); + + // Saved-media library — only fetched when the Media tab is open + // to keep the picker cheap during normal emoji use. + const myUserId = + typeof localStorage !== 'undefined' + ? localStorage.getItem('userId') + : null; + const savedMedia = + useQuery( + api.savedMedia.list, + myUserId && activeTab === 'media' + ? { userId: myUserId as any } + : 'skip', + ) ?? []; + + const handleRepostSaved = (item: any) => { + // ChannelTextarea listens for this event and re-uses the + // stored attachment metadata so the same file can be + // re-posted into the active channel without re-uploading. + window.dispatchEvent( + new CustomEvent('brycord:repost-saved-media', { + detail: { + url: item.url, + filename: item.filename, + mimeType: item.mimeType, + width: item.width, + height: item.height, + size: item.size, + encryptionKey: item.encryptionKey, + encryptionIv: item.encryptionIv, + }, + }), + ); + onClose?.(); + }; + + const gridRef = useRef<HTMLDivElement>(null); + const searchRef = useRef<HTMLInputElement>(null); + const categoryRefs = useRef<Record<string, HTMLDivElement | null>>({}); + + const data = emojiData as unknown as EmojiData; + + useEffect(() => { + if (!mobile && activeTab === 'emojis') { + searchRef.current?.focus(); + } + }, [activeTab, mobile]); + + const filtered = useMemo(() => { + const q = search.trim().toLowerCase(); + if (!q) return null; + const results: EmojiEntry[] = []; + for (const cat of CATEGORIES) { + for (const entry of data[cat.key] ?? []) { + if (entry.names.some((n) => n.toLowerCase().includes(q))) { + results.push(entry); + } + } + } + return results; + }, [search, data]); + + const customEmojis = (useQuery(api.customEmojis.list, {}) ?? []) as CustomEmojiListEntry[]; + + const recentKey = (v: EmojiPickerValue): string => + v.kind === 'unicode' ? `u:${v.surrogates}` : `c:${v.shortcode}`; + + const addRecent = (value: EmojiPickerValue) => { + setRecents((prev) => { + const key = recentKey(value); + const next = [value, ...prev.filter((r) => recentKey(r) !== key)].slice(0, MAX_RECENTS); + saveRecents(next); + return next; + }); + }; + + const handleSelect = (entry: EmojiEntry) => { + const value: EmojiPickerValue = { + kind: 'unicode', + surrogates: entry.surrogates, + name: entry.names[0] ?? '', + }; + addRecent(value); + onSelect(value); + }; + + const handleSelectRecent = (value: EmojiPickerValue) => { + addRecent(value); + onSelect(value); + }; + + const handleCategoryJump = (key: string) => { + setActiveCategory(key); + const el = categoryRefs.current[key]; + if (el && gridRef.current) { + gridRef.current.scrollTo({ top: el.offsetTop - 8, behavior: 'smooth' }); + } + }; + + const handleScroll = () => { + const grid = gridRef.current; + if (!grid) return; + const scrollTop = grid.scrollTop; + let best: string = activeCategory; + let bestDelta = Number.POSITIVE_INFINITY; + for (const key of Object.keys(categoryRefs.current)) { + const el = categoryRefs.current[key]; + if (!el) continue; + const delta = Math.abs(el.offsetTop - scrollTop - 8); + if (delta < bestDelta) { + bestDelta = delta; + best = key; + } + } + if (best !== activeCategory) setActiveCategory(best); + }; + + const toggleCollapsed = (key: string) => { + setCollapsed((prev) => { + const next = new Set(prev); + if (next.has(key)) next.delete(key); + else next.add(key); + return next; + }); + }; + + const renderSectionHeader = (key: string, label: string) => { + const isCollapsed = collapsed.has(key); + return ( + <button + type="button" + className={styles.sectionHeader} + onClick={() => toggleCollapsed(key)} + > + <span className={styles.sectionHeaderLabel}>{label}</span> + <CaretDown + size={14} + weight="bold" + className={`${styles.sectionHeaderCaret} ${ + isCollapsed ? styles.sectionHeaderCaretCollapsed : '' + }`} + /> + </button> + ); + }; + + // The index is included in the key because `emojis.json` contains + // the same surrogate in multiple categories (e.g. 🌆 lives under + // both "travel" and "nature" in some builds of the source data), + // which makes React complain about duplicate keys. Pair the + // surrogate with its map index so keys stay unique even when the + // underlying data isn't. + const renderUnicodeButton = (entry: EmojiEntry, idx: number) => ( + <button + key={`${entry.surrogates}-${idx}`} + type="button" + className={styles.emojiButton} + onClick={() => handleSelect(entry)} + onMouseEnter={() => + setHovered({ + kind: 'unicode', + surrogates: entry.surrogates, + name: entry.names[0] ?? '', + }) + } + onMouseLeave={() => setHovered(null)} + title={`:${entry.names[0] ?? ''}:`} + > + <TwemojiImg emoji={entry.surrogates} size={28} /> + </button> + ); + + const handleSelectCustom = (entry: CustomEmojiListEntry) => { + const value: EmojiPickerValue = { + kind: 'custom', + shortcode: entry.name, + url: entry.src, + }; + addRecent(value); + onSelect(value); + }; + + const renderCustomButton = (entry: CustomEmojiListEntry) => ( + <button + key={entry._id} + type="button" + className={styles.emojiButton} + onClick={() => handleSelectCustom(entry)} + onMouseEnter={() => + setHovered({ kind: 'custom', shortcode: entry.name, url: entry.src }) + } + onMouseLeave={() => setHovered(null)} + title={`:${entry.name}:`} + > + <img + src={entry.src} + alt={entry.name} + draggable={false} + style={{ width: 32, height: 32, objectFit: 'contain' }} + /> + </button> + ); + + const renderRecentButton = (r: EmojiPickerValue, i: number) => { + if (r.kind === 'custom') { + return ( + <button + key={`r-${i}-c-${r.shortcode}`} + type="button" + className={styles.emojiButton} + onClick={() => handleSelectRecent(r)} + onMouseEnter={() => setHovered(r)} + onMouseLeave={() => setHovered(null)} + title={`:${r.shortcode}:`} + > + <img + src={r.url} + alt={r.shortcode} + draggable={false} + style={{ width: 32, height: 32, objectFit: 'contain' }} + /> + </button> + ); + } + return ( + <button + key={`r-${i}-u-${r.surrogates}`} + type="button" + className={styles.emojiButton} + onClick={() => handleSelectRecent(r)} + onMouseEnter={() => setHovered(r)} + onMouseLeave={() => setHovered(null)} + > + <TwemojiImg emoji={r.surrogates} size={28} /> + </button> + ); + }; + + const renderSidebarTab = (key: string, label: string, icon: ReactNode) => { + const isActive = activeCategory === key; + return ( + <button + key={key} + type="button" + className={`${styles.sideTab} ${isActive ? styles.sideTabActive : ''}`} + onClick={() => handleCategoryJump(key)} + title={label} + aria-label={label} + > + {icon} + </button> + ); + }; + + // Mobile layout - flat sticky headers + bottom category bar + if (mobile) { + return ( + <div className={`${styles.picker} ${styles.pickerMobile}`}> + <div className={styles.searchBar}> + <MagnifyingGlass size={16} className={styles.searchIcon} /> + <input + ref={searchRef} + className={styles.searchInput} + placeholder="Search emoji" + value={search} + onChange={(e) => setSearch(e.target.value)} + /> + {search && ( + <button type="button" className={styles.searchClear} onClick={() => setSearch('')}> + <X size={14} /> + </button> + )} + </div> + <div className={styles.grid} ref={gridRef} onScroll={handleScroll}> + {filtered ? ( + filtered.length === 0 ? ( + <div className={styles.noResults}>No emoji found</div> + ) : ( + <> + <div className={styles.categoryHeader}>Search Results</div> + <div className={styles.emojiGrid}> + {filtered.map(renderUnicodeButton)} + </div> + </> + ) + ) : ( + <> + {customEmojis.length > 0 && ( + <div + ref={(el) => { + categoryRefs.current['custom'] = el; + }} + > + <div className={styles.categoryHeader}>Server Emojis</div> + <div className={styles.emojiGrid}> + {customEmojis.map(renderCustomButton)} + </div> + </div> + )} + {recents.length > 0 && ( + <div + ref={(el) => { + categoryRefs.current['recent'] = el; + }} + > + <div className={styles.categoryHeader}>Recently Used</div> + <div className={styles.emojiGrid}> + {recents.map((r, i) => renderRecentButton(r, i))} + </div> + </div> + )} + {CATEGORIES.map((cat) => ( + <div + key={cat.key} + ref={(el) => { + categoryRefs.current[cat.key] = el; + }} + > + <div className={styles.categoryHeader}>{cat.label}</div> + <div className={styles.emojiGrid}> + {(data[cat.key] ?? []).map(renderUnicodeButton)} + </div> + </div> + ))} + </> + )} + </div> + <div className={styles.categoryBar}> + {customEmojis.length > 0 && ( + <button + type="button" + className={`${styles.categoryTab} ${activeCategory === 'custom' ? styles.categoryTabActive : ''}`} + onClick={() => handleCategoryJump('custom')} + title="Server Emojis" + > + <SmileyWink size={20} /> + </button> + )} + {recents.length > 0 && ( + <button + type="button" + className={`${styles.categoryTab} ${activeCategory === 'recent' ? styles.categoryTabActive : ''}`} + onClick={() => handleCategoryJump('recent')} + title="Recently Used" + > + <Clock size={20} /> + </button> + )} + {CATEGORIES.map((cat) => ( + <button + key={cat.key} + type="button" + className={`${styles.categoryTab} ${activeCategory === cat.key ? styles.categoryTabActive : ''}`} + onClick={() => handleCategoryJump(cat.key)} + title={cat.label} + > + {cat.icon} + </button> + ))} + </div> + {onClose && ( + <button type="button" className={styles.searchClear} onClick={onClose}> + <X size={18} /> + </button> + )} + </div> + ); + } + + // Desktop layout - top tab bar + sidebar + grid + inspector + return ( + <div className={styles.picker}> + <div className={styles.tabBar}> + {EXPRESSION_TABS.map((tab) => { + const isActive = activeTab === tab.key; + return ( + <button + key={tab.key} + type="button" + className={`${styles.tabButton} ${isActive ? styles.tabButtonActive : ''}`} + onClick={() => setActiveTab(tab.key)} + > + {tab.label} + </button> + ); + })} + </div> + + {activeTab !== 'gifs' && ( + <div className={styles.searchRow}> + <div className={styles.searchBar}> + <MagnifyingGlass size={16} className={styles.searchIcon} /> + <input + ref={searchRef} + className={styles.searchInput} + placeholder={activeTab === 'emojis' ? 'Search emoji' : 'Coming soon'} + value={search} + onChange={(e) => setSearch(e.target.value)} + disabled={activeTab !== 'emojis'} + /> + {search && ( + <button type="button" className={styles.searchClear} onClick={() => setSearch('')}> + <X size={14} /> + </button> + )} + </div> + </div> + )} + + <div className={styles.main}> + {activeTab === 'emojis' && ( + <div className={styles.sideBar}> + {customEmojis.length > 0 && + renderSidebarTab('custom', 'Server Emojis', <SmileyWink size={20} />)} + {recents.length > 0 && + renderSidebarTab('recent', 'Recently Used', <Clock size={20} />)} + {CATEGORIES.map((cat) => renderSidebarTab(cat.key, cat.label, cat.icon))} + </div> + )} + + <div className={styles.grid} ref={gridRef} onScroll={handleScroll}> + {activeTab === 'gifs' ? ( + <GifPicker + onSelectGif={(url) => { + onSelect({ kind: 'gif', url }); + onClose?.(); + }} + /> + ) : activeTab === 'media' ? ( + savedMedia.length === 0 ? ( + <div className={styles.comingSoon}> + Nothing saved yet. Star an attachment to bookmark it + here for quick re-sharing. + </div> + ) : ( + <div + style={{ + display: 'grid', + gridTemplateColumns: 'repeat(auto-fill, minmax(96px, 1fr))', + gap: 8, + padding: 8, + }} + > + {savedMedia.map((item: any) => { + const isImage = item.kind === 'image'; + const isVideo = item.kind === 'video'; + return ( + <button + key={item._id} + type="button" + title={item.filename} + onClick={() => handleRepostSaved(item)} + style={{ + aspectRatio: '1 / 1', + borderRadius: 6, + overflow: 'hidden', + border: '1px solid var(--background-modifier-accent)', + background: 'var(--background-tertiary)', + cursor: 'pointer', + padding: 0, + position: 'relative', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + color: 'var(--text-tertiary)', + }} + > + {isImage ? ( + // Server-stored URL is publicly fetchable but + // the bytes are encrypted — show a generic + // thumbnail label since we'd need the per-file + // key to decrypt for a real preview. + <ImageSquare size={32} weight="fill" /> + ) : isVideo ? ( + <Gif size={32} weight="fill" /> + ) : ( + <Sticker size={32} weight="fill" /> + )} + <span + style={{ + position: 'absolute', + bottom: 4, + left: 4, + right: 4, + fontSize: 9, + color: 'var(--text-secondary)', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + textAlign: 'center', + }} + > + {item.filename} + </span> + </button> + ); + })} + </div> + ) + ) : activeTab !== 'emojis' ? ( + <div className={styles.comingSoon}> + {EXPRESSION_TABS.find((t) => t.key === activeTab)?.label} are coming soon. + </div> + ) : filtered ? ( + filtered.length === 0 ? ( + <div className={styles.noResults}>No emoji found</div> + ) : ( + <div className={styles.emojiGrid}>{filtered.map(renderUnicodeButton)}</div> + ) + ) : ( + <> + {customEmojis.length > 0 && ( + <div + className={styles.section} + ref={(el) => { + categoryRefs.current['custom'] = el; + }} + > + {renderSectionHeader('custom', 'Server Emojis')} + {!collapsed.has('custom') && ( + <div className={styles.emojiGrid}> + {customEmojis.map(renderCustomButton)} + </div> + )} + </div> + )} + {recents.length > 0 && ( + <div + className={styles.section} + ref={(el) => { + categoryRefs.current['recent'] = el; + }} + > + {renderSectionHeader('recent', 'Recently Used')} + {!collapsed.has('recent') && ( + <div className={styles.emojiGrid}> + {recents.map((r, i) => renderRecentButton(r, i))} + </div> + )} + </div> + )} + {CATEGORIES.map((cat) => ( + <div + key={cat.key} + className={styles.section} + ref={(el) => { + categoryRefs.current[cat.key] = el; + }} + > + {renderSectionHeader(cat.key, cat.label)} + {!collapsed.has(cat.key) && ( + <div className={styles.emojiGrid}> + {(data[cat.key] ?? []).map(renderUnicodeButton)} + </div> + )} + </div> + ))} + </> + )} + </div> + </div> + + {activeTab === 'emojis' && ( + <div className={styles.inspector}> + {hovered ? ( + hovered.kind === 'custom' ? ( + <> + <span className={styles.inspectorEmoji}> + <img + src={hovered.url} + alt={hovered.shortcode} + style={{ width: 32, height: 32, objectFit: 'contain' }} + /> + </span> + <span className={styles.inspectorName}>:{hovered.shortcode}:</span> + </> + ) : ( + <> + <span className={styles.inspectorEmoji}> + <TwemojiImg emoji={hovered.surrogates} size={32} /> + </span> + <span className={styles.inspectorName}>:{hovered.name}:</span> + </> + ) + ) : ( + <span className={styles.inspectorName}>Pick your emoji</span> + )} + </div> + )} + </div> + ); +} + +// Suppress unused icon warnings — they're kept imported for parity with +// the original's GIFs/Media/Stickers tabs. +void Gif; +void ImageSquare; +void Sticker; + +export default EmojiPicker; diff --git a/packages/shared/src/components/channel/EncryptedAttachment.tsx b/packages/shared/src/components/channel/EncryptedAttachment.tsx new file mode 100644 index 0000000..0b1c8a7 --- /dev/null +++ b/packages/shared/src/components/channel/EncryptedAttachment.tsx @@ -0,0 +1,249 @@ +import { useEffect, useState } from 'react'; +import { usePlatform } from '../../platform'; +import { AttachmentAudio } from './AttachmentAudio'; +import { AttachmentVideo } from './AttachmentVideo'; + +export interface AttachmentMetadata { + type: 'attachment'; + url: string; + filename: string; + mimeType: string; + size: number; + /** Hex-encoded AES-256 key. The file bytes are encrypted with this + * key before upload, so we need it to decrypt back to a blob URL. */ + key: string; + iv: string; + width?: number; + height?: number; +} + +const TAG_HEX_LEN = 32; + +// Shared blob-URL cache so re-mounts don't refetch + redecrypt the +// same file. Keyed by the remote storage URL. +const attachmentCache = new Map<string, string>(); + +function fromHexString(hex: string): Uint8Array { + const matches = hex.match(/.{1,2}/g) ?? []; + return new Uint8Array(matches.map((b) => parseInt(b, 16))); +} + +function toHexString(bytes: Uint8Array): string { + let s = ''; + for (const b of bytes) s += b.toString(16).padStart(2, '0'); + return s; +} + +/** + * Fetch an encrypted attachment from storage, decrypt it with the + * per-file AES key from the message metadata, and return a blob: URL + * that can be fed to <img>, <video>, <audio>, or <a download>. + */ +export function useDecryptedAttachmentUrl(metadata: AttachmentMetadata): { + url: string | null; + loading: boolean; + error: string | null; +} { + const { crypto } = usePlatform(); + const [state, setState] = useState<{ + url: string | null; + loading: boolean; + error: string | null; + }>(() => { + const cached = attachmentCache.get(metadata.url); + return { url: cached ?? null, loading: !cached, error: null }; + }); + + useEffect(() => { + const cached = attachmentCache.get(metadata.url); + if (cached) { + setState({ url: cached, loading: false, error: null }); + return; + } + + let cancelled = false; + setState({ url: null, loading: true, error: null }); + + (async () => { + try { + const res = await fetch(metadata.url); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + const buf = new Uint8Array(await res.arrayBuffer()); + const hex = toHexString(buf); + if (hex.length < TAG_HEX_LEN) throw new Error('Invalid file data'); + const contentHex = hex.slice(0, -TAG_HEX_LEN); + const tagHex = hex.slice(-TAG_HEX_LEN); + const plaintextBuf = await crypto.decryptData( + contentHex, + metadata.key, + metadata.iv, + tagHex, + { encoding: 'buffer' }, + ); + const bytes = + plaintextBuf instanceof Uint8Array + ? plaintextBuf + : new Uint8Array(plaintextBuf as ArrayBuffer); + const blob = new Blob([bytes], { type: metadata.mimeType }); + const objectUrl = URL.createObjectURL(blob); + attachmentCache.set(metadata.url, objectUrl); + if (!cancelled) setState({ url: objectUrl, loading: false, error: null }); + } catch (err: any) { + console.error('Attachment decrypt error:', err); + if (!cancelled) + setState({ url: null, loading: false, error: err?.message ?? 'decrypt failed' }); + } + })(); + + return () => { + cancelled = true; + }; + }, [metadata.url, metadata.key, metadata.iv, metadata.mimeType]); + + return state; +} + +interface AttachmentProps { + metadata: AttachmentMetadata; + onImageClick?: (url: string, metadata: AttachmentMetadata) => void; + className?: string; +} + +export function EncryptedAttachment({ metadata, onImageClick, className }: AttachmentProps) { + const { url, loading, error } = useDecryptedAttachmentUrl(metadata); + const kind = metadata.mimeType.split('/')[0]; + + if (error) { + return <div style={{ color: 'var(--status-danger)', fontSize: 13 }}>{error}</div>; + } + + if (kind === 'image') { + // Reserve the exact final layout box up-front so the loaded + // image lands in the same slot the placeholder occupied — no + // post-load height shift, no scroll jump. When the metadata + // carries both width + height we compute the box from them; + // otherwise we fall back to a ratio-aware aspect-ratio so the + // browser still reserves a sensible chunk of space. + const hasDims = !!metadata.width && !!metadata.height; + const maxW = metadata.width ? Math.min(metadata.width, 400) : 300; + const renderedH = + hasDims + ? Math.round(maxW * (metadata.height! / metadata.width!)) + : undefined; + const sharedBoxStyle: React.CSSProperties = { + width: maxW, + ...(renderedH !== undefined ? { height: renderedH } : {}), + ...(hasDims + ? { aspectRatio: `${metadata.width} / ${metadata.height}` } + : { aspectRatio: '4 / 3' }), + maxHeight: '50vh', + borderRadius: 'var(--radius-lg)', + }; + if (loading || !url) { + return ( + <div + style={{ + ...sharedBoxStyle, + backgroundColor: 'var(--background-tertiary)', + }} + /> + ); + } + return ( + <img + src={url} + alt={metadata.filename} + className={className} + // `loading="eager"` so the image decodes synchronously + // with the placeholder it's replacing — keeps the + // "scroll to bottom on initial load" anchor accurate + // instead of jumping after a deferred lazy decode. + loading="eager" + decoding="async" + style={{ + ...sharedBoxStyle, + objectFit: 'cover', + cursor: 'pointer', + }} + onLoad={() => { + // Tell the Messages scroller that an attachment + // finished decoding so it can re-pin to bottom if + // the user is still anchored there. Belt-and- + // suspenders — the parent ResizeObserver should + // also catch this, but a same-size box with + // `objectFit: cover` may not trigger one. + window.dispatchEvent( + new CustomEvent('brycord:attachment-loaded'), + ); + }} + onClick={() => onImageClick?.(url, metadata)} + /> + ); + } + + if (kind === 'video') { + if (loading || !url) { + return ( + <div + style={{ + width: 400, + height: 225, + backgroundColor: 'var(--background-tertiary)', + borderRadius: 'var(--radius-lg)', + }} + /> + ); + } + return ( + <AttachmentVideo + src={url} + filename={metadata.filename} + width={metadata.width} + height={metadata.height} + attachment={metadata} + /> + ); + } + + if (kind === 'audio') { + if (loading || !url) { + return ( + <div + style={{ + width: 360, + height: 96, + backgroundColor: 'var(--background-tertiary)', + borderRadius: 'var(--radius-lg)', + }} + /> + ); + } + return ( + <AttachmentAudio + src={url} + filename={metadata.filename} + attachment={metadata} + /> + ); + } + + // Generic file + if (loading || !url) { + return ( + <div style={{ padding: 10, color: 'var(--text-tertiary)' }}> + {metadata.filename} (decrypting…) + </div> + ); + } + return ( + <a + href={url} + download={metadata.filename} + className={className} + target="_blank" + rel="noopener noreferrer" + > + {metadata.filename} + </a> + ); +} diff --git a/packages/shared/src/components/channel/FileSizeLimitModal.module.css b/packages/shared/src/components/channel/FileSizeLimitModal.module.css new file mode 100644 index 0000000..ce0b666 --- /dev/null +++ b/packages/shared/src/components/channel/FileSizeLimitModal.module.css @@ -0,0 +1,45 @@ +.content { + display: flex; + flex-direction: column; + align-items: center; + gap: 14px; + padding: 20px 24px 8px; + text-align: center; +} + +.iconRow { + display: flex; + justify-content: center; +} + +.iconBadge { + display: flex; + align-items: center; + justify-content: center; + width: 56px; + height: 56px; + border-radius: 50%; + background-color: rgba(234, 80, 80, 0.15); + color: var(--status-danger, #da373c); +} + +.body { + margin: 0; + font-size: 14px; + line-height: 1.5; + color: var(--text-secondary, #a0a3a8); + max-width: 360px; +} + +.body strong { + color: var(--text-primary, #fff); + font-weight: 600; +} + +.hint { + margin: 0; + font-size: 12px; + line-height: 1.45; + color: var(--text-primary-muted, #a0a3a8); + max-width: 360px; +} diff --git a/packages/shared/src/components/channel/FileSizeLimitModal.tsx b/packages/shared/src/components/channel/FileSizeLimitModal.tsx new file mode 100644 index 0000000..ff09b09 --- /dev/null +++ b/packages/shared/src/components/channel/FileSizeLimitModal.tsx @@ -0,0 +1,71 @@ +/** + * FileSizeLimitModal — shown when a user drops (or picks) a file that + * exceeds the homeserver's advertised maximum upload size. Clicking + * Cancel closes the modal; there's no "upload anyway" path because + * the homeserver will just reject the POST with a 413 regardless. + * + * The numbers we show come from `MediaManager.getMaxUploadSize` + * (which calls `GET /_matrix/media/v3/config` on the user's own + * homeserver). This is a per-homeserver-federation policy — different + * homeservers have different limits — so the copy reflects that. + */ +import { Modal, Button } from '@brycord/ui'; +import { WarningCircle } from '@phosphor-icons/react'; +import styles from './FileSizeLimitModal.module.css'; + +interface FileSizeLimitModalProps { + isOpen: boolean; + onClose: () => void; + /** The file(s) that tripped the limit — for display only. */ + fileName: string; + /** Actual file size in bytes. */ + actualBytes: number; + /** Homeserver-advertised maximum in bytes. */ + limitBytes: number; +} + +function formatBytes(bytes: number): string { + if (!Number.isFinite(bytes)) return 'unlimited'; + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; + return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`; +} + +export function FileSizeLimitModal({ + isOpen, + onClose, + fileName, + actualBytes, + limitBytes, +}: FileSizeLimitModalProps) { + if (!isOpen) return null; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="small"> + <Modal.Header title="File size limit exceeded" onClose={onClose} /> + <Modal.Content className={styles.content}> + <div className={styles.iconRow}> + <div className={styles.iconBadge}> + <WarningCircle size={28} weight="fill" /> + </div> + </div> + <p className={styles.body}> + The file <strong>{fileName}</strong> is{' '} + <strong>{formatBytes(actualBytes)}</strong>, which exceeds the + maximum upload size allowed by this homeserver of{' '} + <strong>{formatBytes(limitBytes)}</strong>. + </p> + <p className={styles.hint}> + Try a smaller file, compress the file, or upload it to another + service and share the link instead. + </p> + </Modal.Content> + <Modal.Footer> + <Button variant="primary" onClick={onClose}> + Cancel + </Button> + </Modal.Footer> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/channel/FileUploadDropZone.module.css b/packages/shared/src/components/channel/FileUploadDropZone.module.css new file mode 100644 index 0000000..7ef69b2 --- /dev/null +++ b/packages/shared/src/components/channel/FileUploadDropZone.module.css @@ -0,0 +1,105 @@ +/* Fills its parent completely — used as a full-window wrapper in + GuildsLayout so a drop anywhere in the Brycord viewport triggers + the overlay. The `display: contents`-style layout contract is + preserved by `height: 100%` + `width: 100%` so the inner tree + (which expects a block filling its parent) still lays out the + same as before. */ +.root { + position: relative; + display: flex; + flex-direction: column; + height: 100%; + width: 100%; + min-height: 0; +} + +.overlay { + position: absolute; + inset: 0; + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(3px); + pointer-events: none; /* let the drop event fall through to .root */ + animation: fadeIn 120ms ease-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.card { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + padding: 32px 40px; + background-color: var(--background-floating, #1a1b1e); + border: 2px dashed var(--brand-primary, #5865f2); + border-radius: 12px; + max-width: 420px; + text-align: center; + box-shadow: 0 20px 48px rgba(0, 0, 0, 0.6); + /* cap re-enable so the card itself doesn't block the drop event + (though the overlay is pointer-events:none anyway). */ + pointer-events: none; +} + +.iconBadge { + display: flex; + align-items: center; + justify-content: center; + width: 72px; + height: 72px; + border-radius: 50%; + background-color: rgba(88, 101, 242, 0.15); + color: var(--brand-primary, #5865f2); + margin-bottom: 4px; +} + +.title { + font-size: 18px; + font-weight: 700; + color: var(--text-primary, #fff); +} + +.subtitle { + font-size: 13px; + line-height: 1.45; + color: var(--text-secondary, #a0a3a8); + max-width: 320px; +} + +.hint { + display: inline-flex; + align-items: center; + gap: 8px; + margin-top: 4px; + padding: 6px 12px; + background-color: rgba(0, 0, 0, 0.4); + border-radius: 6px; + font-size: 12px; + font-weight: 500; + color: var(--text-primary, #fff); +} + +.hintKey { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 18px; + height: 18px; + padding: 0 5px; + border-radius: 4px; + background-color: rgba(255, 255, 255, 0.15); + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 11px; + font-weight: 600; +} diff --git a/packages/shared/src/components/channel/FileUploadDropZone.tsx b/packages/shared/src/components/channel/FileUploadDropZone.tsx new file mode 100644 index 0000000..03b3a15 --- /dev/null +++ b/packages/shared/src/components/channel/FileUploadDropZone.tsx @@ -0,0 +1,128 @@ +import { File as FileIcon } from '@phosphor-icons/react'; +import { useEffect, useRef, useState, type ReactNode } from 'react'; +import styles from './FileUploadDropZone.module.css'; + +export type DropMode = 'compose' | 'instant'; + +interface FileUploadDropZoneProps { + channelName: string; + onDropFiles: (files: File[], mode: DropMode) => void; + disabled?: boolean; + children: ReactNode; +} + +/** + * Full-area file drop target. Wrap the whole chat region with this so + * a drop anywhere in the viewport — sidebar, chat body, members — is + * captured. The overlay covers the full root and shows a centered + * "Upload to #channel" card. + * + * Drag counter guards nested dragenter/dragleave events; the overlay + * uses `pointer-events: none` so the drop itself falls through to + * the root handler. + */ +export function FileUploadDropZone({ + channelName, + onDropFiles, + disabled = false, + children, +}: FileUploadDropZoneProps) { + const [isDragging, setIsDragging] = useState(false); + const dragCounter = useRef(0); + + const hasFiles = (e: React.DragEvent): boolean => { + const items = e.dataTransfer?.items; + if (items) { + for (let i = 0; i < items.length; i++) { + if (items[i].kind === 'file') return true; + } + } + const types = e.dataTransfer?.types; + return !!types && Array.from(types).includes('Files'); + }; + + const handleDragEnter = (e: React.DragEvent) => { + if (disabled || !hasFiles(e)) return; + e.preventDefault(); + e.stopPropagation(); + dragCounter.current += 1; + if (dragCounter.current === 1) setIsDragging(true); + }; + + const handleDragOver = (e: React.DragEvent) => { + if (disabled || !hasFiles(e)) return; + e.preventDefault(); + e.stopPropagation(); + if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy'; + }; + + const handleDragLeave = (e: React.DragEvent) => { + if (disabled || !hasFiles(e)) return; + e.preventDefault(); + e.stopPropagation(); + dragCounter.current -= 1; + if (dragCounter.current <= 0) { + dragCounter.current = 0; + setIsDragging(false); + } + }; + + const handleDrop = (e: React.DragEvent) => { + if (disabled) return; + if (!hasFiles(e)) return; + e.preventDefault(); + e.stopPropagation(); + dragCounter.current = 0; + setIsDragging(false); + + const files: File[] = []; + if (e.dataTransfer?.files) { + for (let i = 0; i < e.dataTransfer.files.length; i++) { + files.push(e.dataTransfer.files[i]); + } + } + if (files.length === 0) return; + const mode: DropMode = e.shiftKey ? 'instant' : 'compose'; + onDropFiles(files, mode); + }; + + // Window blur can strand the overlay if the user drags out of the + // viewport without a matching dragleave. Reset defensively. + useEffect(() => { + const handleWindowBlur = () => { + dragCounter.current = 0; + setIsDragging(false); + }; + window.addEventListener('blur', handleWindowBlur); + return () => window.removeEventListener('blur', handleWindowBlur); + }, []); + + return ( + <div + className={styles.root} + onDragEnter={handleDragEnter} + onDragOver={handleDragOver} + onDragLeave={handleDragLeave} + onDrop={handleDrop} + > + {children} + {isDragging && ( + <div className={styles.overlay} aria-hidden="true"> + <div className={styles.card}> + <div className={styles.iconBadge}> + <FileIcon size={32} weight="duotone" /> + </div> + <div className={styles.title}>Upload to #{channelName}</div> + <div className={styles.subtitle}> + You can add comments before uploading. Hold shift to upload directly. + </div> + <div className={styles.hint}> + <span className={styles.hintKey}>⇧</span> + <span>Hold for instant upload</span> + </div> + </div> + </div> + )} + </div> + ); +} diff --git a/packages/shared/src/components/channel/FileUploadModal.module.css b/packages/shared/src/components/channel/FileUploadModal.module.css new file mode 100644 index 0000000..dd7de0b --- /dev/null +++ b/packages/shared/src/components/channel/FileUploadModal.module.css @@ -0,0 +1,142 @@ +.content { + display: flex; + flex-direction: column; + gap: 16px; + padding: 16px 20px 4px; +} + +.destination { + font-size: 13px; + color: var(--text-secondary, #a0a3a8); +} + +.destination strong { + color: var(--text-primary, #fff); + font-weight: 600; +} + +.previewGrid { + display: flex; + flex-wrap: wrap; + gap: 10px; + max-height: 320px; + overflow-y: auto; +} + +.preview { + position: relative; + display: flex; + flex-direction: column; + gap: 6px; + padding: 10px; + width: 140px; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.03)); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); + border-radius: 8px; +} + +.removeButton { + position: absolute; + top: 4px; + right: 4px; + display: flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + padding: 0; + background-color: rgba(0, 0, 0, 0.7); + border: none; + border-radius: 4px; + color: #fff; + cursor: pointer; + z-index: 1; + transition: background-color 0.1s; +} + +.removeButton:hover { + background-color: var(--status-danger, #da373c); +} + +.previewImage { + width: 100%; + height: 100px; + object-fit: cover; + border-radius: 6px; + background-color: rgba(0, 0, 0, 0.3); +} + +.previewIcon { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100px; + background-color: rgba(88, 101, 242, 0.12); + color: var(--brand-primary, #5865f2); + border-radius: 6px; +} + +.previewMeta { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} + +.previewName { + font-size: 12px; + font-weight: 500; + color: var(--text-primary, #fff); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.previewSize { + font-size: 11px; + color: var(--text-primary-muted, #a0a3a8); +} + +.captionLabel { + display: flex; + flex-direction: column; + gap: 6px; +} + +.captionLabelText { + font-size: 11px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--text-primary-muted, #a0a3a8); +} + +.captionInput { + width: 100%; + padding: 10px 12px; + background-color: var(--background-tertiary, rgba(0, 0, 0, 0.3)); + border: 1px solid transparent; + border-radius: 6px; + color: var(--text-primary, #fff); + font-size: 14px; + font-family: inherit; + outline: none; + box-sizing: border-box; + transition: border-color 0.1s; +} + +.captionInput:focus { + border-color: var(--brand-primary, #5865f2); +} + +.errorBanner { + padding: 10px 12px; + background-color: rgba(234, 80, 80, 0.15); + border: 1px solid rgba(234, 80, 80, 0.45); + border-radius: 6px; + color: var(--text-primary, #fff); + font-size: 13px; + line-height: 1.45; + word-break: break-word; +} diff --git a/packages/shared/src/components/channel/FileUploadModal.tsx b/packages/shared/src/components/channel/FileUploadModal.tsx new file mode 100644 index 0000000..0366a9b --- /dev/null +++ b/packages/shared/src/components/channel/FileUploadModal.tsx @@ -0,0 +1,194 @@ +/** + * FileUploadModal — compose UI that opens after dropping file(s) + * onto the chat area (unless Shift was held, which triggers an + * instant upload instead). Shows a preview of each file, a single + * shared caption input, and a Send button. + * + * Behaviour: + * - Image files (mime starts with `image/`): show as thumbnails. + * - Other files: show filename + extension badge + size. + * - Caption is sent attached to the FIRST file only (matches + * Discord where a single comment lives above a group of files). + * - Enter in the caption input sends; Shift+Enter inserts a newline. + * - Cancel discards everything and closes. + */ +import { useEffect, useMemo, useState } from 'react'; +import { File as FileIcon, X, PaperPlaneTilt } from '@phosphor-icons/react'; +import { Modal, Button } from '@brycord/ui'; +import styles from './FileUploadModal.module.css'; + +interface FileUploadModalProps { + isOpen: boolean; + onClose: () => void; + channelName: string; + files: File[]; + onSend: (caption: string) => Promise<void> | void; +} + +function formatSize(bytes: number): string { + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; +} + +function FilePreview({ file, onRemove }: { file: File; onRemove?: () => void }) { + // Create a local object URL for image previews. Revoke on unmount + // so we don't leak. For non-image files we skip the URL entirely. + const objectUrl = useMemo(() => { + if (file.type.startsWith('image/')) return URL.createObjectURL(file); + return null; + }, [file]); + + useEffect(() => { + return () => { + if (objectUrl) URL.revokeObjectURL(objectUrl); + }; + }, [objectUrl]); + + return ( + <div className={styles.preview}> + {onRemove && ( + <button + type="button" + className={styles.removeButton} + onClick={onRemove} + aria-label={`Remove ${file.name}`} + > + <X size={14} weight="bold" /> + </button> + )} + {objectUrl ? ( + <img src={objectUrl} alt={file.name} className={styles.previewImage} /> + ) : ( + <div className={styles.previewIcon}> + <FileIcon size={40} weight="duotone" /> + </div> + )} + <div className={styles.previewMeta}> + <div className={styles.previewName} title={file.name}> + {file.name} + </div> + <div className={styles.previewSize}>{formatSize(file.size)}</div> + </div> + </div> + ); +} + +export function FileUploadModal({ + isOpen, + onClose, + channelName, + files, + onSend, +}: FileUploadModalProps) { + const [caption, setCaption] = useState(''); + const [sending, setSending] = useState(false); + const [workingFiles, setWorkingFiles] = useState<File[]>(files); + const [error, setError] = useState<string | null>(null); + + // Reset local state whenever the modal opens with a fresh file list. + useEffect(() => { + if (isOpen) { + setWorkingFiles(files); + setCaption(''); + setSending(false); + setError(null); + } + }, [isOpen, files]); + + const handleRemove = (index: number) => { + setWorkingFiles((prev) => { + const next = prev.filter((_, i) => i !== index); + // If the user removed the last file, close the modal. + if (next.length === 0) onClose(); + return next; + }); + }; + + const handleSend = async () => { + if (workingFiles.length === 0 || sending) return; + setSending(true); + setError(null); + try { + await onSend(caption); + onClose(); + } catch (err: any) { + console.error('Failed to upload file(s):', err); + // Surface the error inline so the user knows why their + // upload failed. Common causes: 413 (too large — shouldn't + // happen anymore since the drop-zone pre-checks), 520/502 + // (homeserver's media CDN is misbehaving), auth token + // expired, network drop. The modal stays open so they can + // hit Upload again to retry, or Cancel to bail out. + const code = err?.httpStatus || err?.status; + const matrixCode = err?.errcode || err?.data?.errcode; + const detail = err?.data?.error || err?.message; + let message = 'Upload failed'; + if (code) message += ` (HTTP ${code})`; + if (matrixCode) message += ` — ${matrixCode}`; + if (detail) message += `: ${detail}`; + setError(message); + setSending(false); + } + }; + + const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + void handleSend(); + } + }; + + if (!isOpen) return null; + + const title = + workingFiles.length === 1 + ? `Upload ${workingFiles[0].name}` + : `Upload ${workingFiles.length} files`; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="medium"> + <Modal.Header title={title} onClose={onClose} /> + <Modal.Content className={styles.content}> + <div className={styles.destination}> + Sending to <strong>#{channelName}</strong> + </div> + <div className={styles.previewGrid}> + {workingFiles.map((file, i) => ( + <FilePreview + key={`${file.name}-${i}`} + file={file} + onRemove={workingFiles.length > 1 ? () => handleRemove(i) : undefined} + /> + ))} + </div> + <label className={styles.captionLabel}> + <span className={styles.captionLabelText}>Add a comment</span> + <input + type="text" + className={styles.captionInput} + placeholder="Optional comment…" + value={caption} + onChange={(e) => setCaption(e.target.value)} + onKeyDown={handleKeyDown} + autoFocus + /> + </label> + {error && <div className={styles.errorBanner}>{error}</div>} + </Modal.Content> + <Modal.Footer> + <Button variant="secondary" onClick={onClose} disabled={sending}> + Cancel + </Button> + <Button + variant="primary" + onClick={handleSend} + disabled={workingFiles.length === 0 || sending} + icon={<PaperPlaneTilt size={16} weight="fill" />} + > + {sending ? 'Uploading…' : 'Upload'} + </Button> + </Modal.Footer> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/channel/GifPicker.module.css b/packages/shared/src/components/channel/GifPicker.module.css new file mode 100644 index 0000000..ec8599f --- /dev/null +++ b/packages/shared/src/components/channel/GifPicker.module.css @@ -0,0 +1,202 @@ +/* ── GifPicker ───────────────────────────────────────────────── + Klipy-backed GIF browser embedded in the EmojiPicker's GIFs tab. + Search bar at the top, optional featured tile row + grid below. */ + +.root { + display: flex; + flex-direction: column; + height: 100%; + min-height: 0; + gap: 10px; + padding: 10px 12px; + box-sizing: border-box; +} + +.searchBar { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + background: var(--background-tertiary); + border: 1px solid var(--background-modifier-accent); + border-radius: 8px; + flex-shrink: 0; +} + +.searchIcon { + color: var(--text-tertiary); + flex-shrink: 0; +} + +.searchInput { + flex: 1; + background: transparent; + border: none; + outline: none; + color: var(--text-primary); + font: inherit; + font-size: 14px; + min-width: 0; +} + +.featuredRow { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; + flex-shrink: 0; +} + +.featuredTile { + position: relative; + display: flex; + flex-direction: column; + justify-content: flex-end; + align-items: flex-start; + padding: 14px; + height: 96px; + border-radius: 8px; + border: none; + cursor: pointer; + color: #ffffff; + overflow: hidden; + transition: transform 0.12s ease; +} + +.featuredTile:hover { + transform: translateY(-1px); +} + +.featuredFavorites { + background: linear-gradient(135deg, #4641d9 0%, #7b5ce7 100%); +} + +.featuredTrending { + background: linear-gradient(135deg, #f04f88 0%, #ff7c5c 100%); +} + +.featuredIcon { + position: absolute; + top: 12px; + right: 12px; + opacity: 0.85; +} + +.featuredLabel { + font-size: 14px; + font-weight: 700; + letter-spacing: 0.01em; +} + +.subHeaderRow { + display: flex; + align-items: center; + gap: 12px; + padding: 0 4px; + flex-shrink: 0; +} + +.backLink { + background: none; + border: none; + color: var(--brand-primary, #5865f2); + font: inherit; + font-size: 12px; + font-weight: 600; + cursor: pointer; + padding: 0; +} + +.subHeaderTitle { + font-size: 13px; + font-weight: 700; + color: var(--text-primary); + letter-spacing: 0.02em; + text-transform: uppercase; +} + +.grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-auto-rows: max-content; + gap: 8px; + overflow-y: auto; + min-height: 0; + flex: 1; + padding-bottom: 4px; + scrollbar-width: none; + -ms-overflow-style: none; +} + +.grid::-webkit-scrollbar { + display: none; +} + +.tile { + position: relative; + border: none; + padding: 0; + margin: 0; + cursor: pointer; + border-radius: 8px; + overflow: hidden; + background: var(--background-tertiary); + aspect-ratio: 16 / 9; + width: 100%; + min-width: 0; + min-height: 0; + transition: transform 0.12s ease; +} + +.tile:hover { + transform: scale(1.02); +} + +.tileImage { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +.favoriteButton { + position: absolute; + top: 6px; + right: 6px; + width: 26px; + height: 26px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + border: none; + background: rgba(0, 0, 0, 0.55); + color: #ffffff; + cursor: pointer; + opacity: 0; + transition: opacity 0.12s ease, background-color 0.12s ease; +} + +.tile:hover .favoriteButton { + opacity: 1; +} + +.favoriteButton:hover { + background: rgba(0, 0, 0, 0.75); +} + +.favoriteButtonActive { + opacity: 1; + color: var(--brand-primary, #5865f2); +} + +.statusMessage, +.statusMessageError { + padding: 24px 12px; + text-align: center; + color: var(--text-tertiary); + font-size: 13px; +} + +.statusMessageError { + color: var(--status-danger, #ed4245); +} diff --git a/packages/shared/src/components/channel/GifPicker.tsx b/packages/shared/src/components/channel/GifPicker.tsx new file mode 100644 index 0000000..e607062 --- /dev/null +++ b/packages/shared/src/components/channel/GifPicker.tsx @@ -0,0 +1,260 @@ +/** + * GifPicker — Klipy-backed GIF browser embedded in the EmojiPicker's + * "GIFs" tab. Layout matches the new UI screenshot: + * + * ┌────────────────────────────────────┐ + * │ [ 🔍 Search Tenor / Klipy ] │ + * │ ┌──────────┐ ┌──────────────────┐ │ + * │ │ ★ │ │ Trending GIFs │ │ + * │ │ Favorites│ │ │ │ + * │ └──────────┘ └──────────────────┘ │ + * │ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │ + * │ │ G │ │ G │ │ G │ │ G │ │ + * │ └────┘ └────┘ └────┘ └────┘ │ + * └────────────────────────────────────┘ + * + * Click on any tile fires `onSelectGif(url)` — the parent picker + * forwards that to the composer, which sends the GIF URL as a + * plain text message. The auto-link parser in MessageContent + + * the LinkEmbed/DirectMediaEmbed renderer take it from there. + */ +import { useEffect, useMemo, useState } from 'react'; +import { useAction } from 'convex/react'; +import { MagnifyingGlass, Star, TrendUp } from '@phosphor-icons/react'; +import { api } from '../../../../../convex/_generated/api'; +import styles from './GifPicker.module.css'; + +interface GifResult { + id: string; + title: string; + url: string; + previewUrl: string; + width?: number; + height?: number; +} + +interface GifPickerProps { + onSelectGif: (url: string) => void; +} + +type Tab = 'home' | 'favorites' | 'trending'; + +const FAVORITES_KEY = 'gifPicker.favorites'; + +function loadFavorites(): GifResult[] { + try { + const raw = localStorage.getItem(FAVORITES_KEY); + if (!raw) return []; + const parsed = JSON.parse(raw); + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } +} + +function saveFavorites(items: GifResult[]) { + try { + localStorage.setItem(FAVORITES_KEY, JSON.stringify(items.slice(0, 100))); + } catch { + /* ignore quota */ + } +} + +export function GifPicker({ onSelectGif }: GifPickerProps) { + const [search, setSearch] = useState(''); + const [tab, setTab] = useState<Tab>('home'); + const [trending, setTrending] = useState<GifResult[]>([]); + const [searchResults, setSearchResults] = useState<GifResult[]>([]); + const [favorites, setFavorites] = useState<GifResult[]>(() => + loadFavorites(), + ); + const [loading, setLoading] = useState(false); + const [error, setError] = useState<string | null>(null); + + const searchAction = useAction(api.gifs.search); + const trendingAction = useAction(api.gifs.trending); + + // Load trending feed once when the picker mounts. The result is + // cached for the rest of the session — no need to refetch every + // time the user toggles back to the home tab. + useEffect(() => { + let cancelled = false; + (async () => { + setLoading(true); + setError(null); + try { + const res: any = await trendingAction({ limit: 24 }); + if (cancelled) return; + setTrending(res?.results ?? []); + } catch (err: any) { + if (cancelled) return; + setError(err?.message ?? 'Failed to load GIFs.'); + } finally { + if (!cancelled) setLoading(false); + } + })(); + return () => { + cancelled = true; + }; + }, [trendingAction]); + + // Debounced search — fires 350ms after the last keystroke so we + // don't hammer the upstream API on every character. + useEffect(() => { + const q = search.trim(); + if (!q) { + setSearchResults([]); + return; + } + const t = window.setTimeout(async () => { + setLoading(true); + setError(null); + try { + const res: any = await searchAction({ q, limit: 24 }); + setSearchResults(res?.results ?? []); + } catch (err: any) { + setError(err?.message ?? 'Search failed.'); + } finally { + setLoading(false); + } + }, 350); + return () => window.clearTimeout(t); + }, [search, searchAction]); + + const handlePick = (gif: GifResult) => { + onSelectGif(gif.url); + }; + + const toggleFavorite = (gif: GifResult, e: React.MouseEvent) => { + e.stopPropagation(); + setFavorites((prev) => { + const exists = prev.some((g) => g.id === gif.id); + const next = exists + ? prev.filter((g) => g.id !== gif.id) + : [gif, ...prev]; + saveFavorites(next); + return next; + }); + }; + + const isFavorited = (gif: GifResult) => + favorites.some((g) => g.id === gif.id); + + // Decide which list to render. Searching always wins — once the + // user types anything, we show the search results regardless of + // which featured tab was active. + const isSearching = search.trim().length > 0; + const displayList: GifResult[] = useMemo(() => { + if (isSearching) return searchResults; + if (tab === 'favorites') return favorites; + if (tab === 'trending') return trending; + // Home → trending + return trending; + }, [isSearching, searchResults, tab, favorites, trending]); + + const showFeaturedRow = !isSearching && tab === 'home'; + + return ( + <div className={styles.root}> + <div className={styles.searchBar}> + <MagnifyingGlass + size={16} + weight="regular" + className={styles.searchIcon} + /> + <input + type="text" + className={styles.searchInput} + placeholder="Search Tenor" + value={search} + onChange={(e) => setSearch(e.target.value)} + autoFocus + /> + </div> + + {showFeaturedRow && ( + <div className={styles.featuredRow}> + <button + type="button" + className={`${styles.featuredTile} ${styles.featuredFavorites}`} + onClick={() => setTab('favorites')} + > + <Star size={28} weight="fill" className={styles.featuredIcon} /> + <span className={styles.featuredLabel}>Favorites</span> + </button> + <button + type="button" + className={`${styles.featuredTile} ${styles.featuredTrending}`} + onClick={() => setTab('trending')} + > + <TrendUp size={28} weight="fill" className={styles.featuredIcon} /> + <span className={styles.featuredLabel}>Trending GIFs</span> + </button> + </div> + )} + + {tab !== 'home' && !isSearching && ( + <div className={styles.subHeaderRow}> + <button + type="button" + className={styles.backLink} + onClick={() => setTab('home')} + > + ← Back + </button> + <span className={styles.subHeaderTitle}> + {tab === 'favorites' ? 'Favorites' : 'Trending GIFs'} + </span> + </div> + )} + + {loading && displayList.length === 0 ? ( + <div className={styles.statusMessage}>Loading GIFs…</div> + ) : error ? ( + <div className={styles.statusMessageError}>{error}</div> + ) : displayList.length === 0 ? ( + <div className={styles.statusMessage}> + {isSearching + ? 'No GIFs match your search.' + : tab === 'favorites' + ? 'No favorites yet. Click the star on a GIF to save it here.' + : 'No GIFs to show.'} + </div> + ) : ( + <div className={styles.grid}> + {displayList.map((gif) => { + const fav = isFavorited(gif); + return ( + <button + key={gif.id} + type="button" + className={styles.tile} + onClick={() => handlePick(gif)} + title={gif.title || 'GIF'} + > + <img + src={gif.previewUrl || gif.url} + alt={gif.title || 'GIF'} + className={styles.tileImage} + loading="lazy" + draggable={false} + /> + <button + type="button" + className={`${styles.favoriteButton} ${ + fav ? styles.favoriteButtonActive : '' + }`} + onClick={(e) => toggleFavorite(gif, e)} + aria-label={fav ? 'Unfavorite GIF' : 'Favorite GIF'} + title={fav ? 'Unfavorite' : 'Favorite'} + > + <Star size={14} weight={fav ? 'fill' : 'regular'} /> + </button> + </button> + ); + })} + </div> + )} + </div> + ); +} diff --git a/packages/shared/src/components/channel/ImageLightbox.module.css b/packages/shared/src/components/channel/ImageLightbox.module.css new file mode 100644 index 0000000..e360600 --- /dev/null +++ b/packages/shared/src/components/channel/ImageLightbox.module.css @@ -0,0 +1,238 @@ +/* ── Backdrop ───────────────────────────────────────────────── + Full-viewport dimmer behind the image. When the image is + zoomed the backdrop allows scroll so the user can pan through + overflow content. */ +.backdrop { + position: fixed; + inset: 0; + z-index: 16000; + display: flex; + /* `safe center` keeps the image centred when it fits the + viewport but stops centering once the content overflows — + without `safe`, an oversized child gets clipped at the top / + left and the user can't scroll back into the hidden region. */ + align-items: safe center; + justify-content: safe center; + background-color: rgba(0, 0, 0, 0.85); + padding: 80px 48px 48px; + box-sizing: border-box; + overflow: auto; + /* Desktop click-outside-to-close affordance — mobile drops + this via the media query below since tapping empty space + there is more likely to be accidental. */ + cursor: zoom-out; +} + +/* Mobile: drop the backdrop padding so the image can use the full + width of the screen. The mobile header buttons are position: fixed + so they still float over the image instead of occupying layout. */ +@media (max-width: 768px) { + .backdrop { + padding: 0; + cursor: default; + } +} + +/* Image — fits the viewport by default. Toggling `.imageZoomed` + drops the max constraints so the natural size kicks in; the + backdrop becomes scrollable to pan through the overflow. */ +.image { + max-width: 100%; + max-height: 100%; + width: auto; + height: auto; + object-fit: contain; + border-radius: 4px; + box-shadow: 0 16px 48px rgba(0, 0, 0, 0.6); + cursor: zoom-in; + user-select: none; + -webkit-user-drag: none; + transition: transform 0.2s ease; +} + +.imageZoomed { + max-width: none; + max-height: none; + /* Force the image to its natural pixel dimensions so the + backdrop's overflow:auto actually has something to scroll. */ + width: auto; + height: auto; + min-width: max-content; + min-height: max-content; + cursor: zoom-out; +} + +/* ── Mobile header row ─────────────────────────────────────── + Bare minimum on mobile: an X button pinned top-left and a + three-dot menu button pinned top-right. Everything else lives + in the MobileImageActionsSheet the menu button opens. + Rendered instead of the desktop `.header` on touch viewports. */ +.mobileHeader { + position: fixed; + top: 14px; + left: 14px; + right: 14px; + display: flex; + align-items: center; + justify-content: space-between; + pointer-events: none; + z-index: 1; +} + +.mobileHeaderButton { + display: inline-flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + padding: 0; + background: transparent; + border: none; + border-radius: 50%; + color: var(--text-primary, #fff); + cursor: pointer; + pointer-events: auto; + -webkit-tap-highlight-color: transparent; + transition: background-color 0.12s; +} + +.mobileHeaderButton:active { + background-color: rgba(255, 255, 255, 0.12); +} + +/* ── Desktop header row ────────────────────────────────────── + Sits above the image, pinned to the top of the viewport. Holds + three independent cards: file info (left), action controls + (right), close button (far right). */ +.header { + position: fixed; + top: 16px; + left: 24px; + right: 24px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + height: 40px; + pointer-events: none; + z-index: 1; +} + +/* File info card — filename + dimensions. Spec from Fluxer: + `--background-textarea` surface with a 1px accent border and + a soft drop shadow. Constrained max-width so it never shoves + the action cards off-screen on narrow viewports. */ +.fileInfoCard { + display: flex; + flex-direction: column; + justify-content: center; + gap: 0.125rem; + padding: 0.25rem 0.75rem; + border-radius: var(--radius-lg, 10px); + background-color: var(--background-textarea, rgba(24, 25, 28, 0.85)); + border: 1px solid var(--background-modifier-accent); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + pointer-events: auto; + min-width: 0; + max-width: calc(100% - 260px); + height: 100%; + box-sizing: border-box; +} + +.filename { + font-size: 13px; + font-weight: 700; + color: var(--text-primary, #fff); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + line-height: 1.2; +} + +.dimensions { + font-size: 11px; + color: var(--text-primary-muted, rgba(255, 255, 255, 0.6)); + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + line-height: 1.2; +} + +/* Right-side cluster: action card + close card, separated so the + close button can sit visually apart like the Fluxer reference. */ +.headerRight { + display: flex; + align-items: stretch; + gap: 8px; + height: 100%; + pointer-events: none; +} + +/* Action card — zoom / favorite / save / open. Same surface + treatment as the file info card, tighter padding and gap. */ +.actionCard { + display: flex; + align-items: center; + gap: 0.125rem; + padding: 0.25rem 0.375rem; + border-radius: var(--radius-lg, 10px); + background-color: var(--background-textarea, rgba(24, 25, 28, 0.85)); + border: 1px solid var(--background-modifier-accent); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + pointer-events: auto; + height: 100%; + box-sizing: border-box; +} + +/* Close card — its own standalone pill. Square-ish so the X + button reads as a self-contained control. */ +.closeCard { + display: flex; + align-items: center; + justify-content: center; + padding: 0.25rem; + border-radius: var(--radius-lg, 10px); + background-color: var(--background-textarea, rgba(24, 25, 28, 0.85)); + border: 1px solid var(--background-modifier-accent); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + pointer-events: auto; + height: 100%; + box-sizing: border-box; +} + +.actionButton { + display: inline-flex; + align-items: center; + justify-content: center; + width: 30px; + height: 30px; + padding: 0; + background: transparent; + border: none; + border-radius: 6px; + color: var(--text-primary, #fff); + cursor: pointer; + transition: background-color 0.12s, color 0.12s; +} + +.actionButton:hover:not(:disabled) { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.1)); +} + +.actionButton:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +/* Favorited state — brand-primary fill + white icon so the + star reads as "on" even when the button isn't hovered. + Matches the spec'd `--brand-primary` / `--text-on-brand-primary` + tokens. */ +.actionButtonActive { + background-color: var(--brand-primary); + color: var(--text-on-brand-primary); +} + +.actionButtonActive:hover:not(:disabled) { + background-color: var(--brand-primary); + color: var(--text-on-brand-primary); + filter: brightness(1.08); +} diff --git a/packages/shared/src/components/channel/ImageLightbox.tsx b/packages/shared/src/components/channel/ImageLightbox.tsx new file mode 100644 index 0000000..35f15f5 --- /dev/null +++ b/packages/shared/src/components/channel/ImageLightbox.tsx @@ -0,0 +1,298 @@ +/** + * ImageLightbox — full-screen image viewer opened by clicking an + * image attachment in the chat. Shows a file info card (filename + + * dimensions) on the left, an action card (zoom toggle, download, + * open-external) on the right, and a standalone close card. Clicking + * the image toggles between fit-to-viewport and native-size modes — + * when zoomed, the backdrop scrolls so the user can pan through the + * overflow. + * + * Layout mirrors the Brycord/Fluxer reference 1:1, with Matrix- + * specific bits (useMxcUrl, SavedMediaStore, MobileImageActionsSheet) + * stripped out. The `src` prop is a blob URL already decrypted by + * EncryptedAttachment — no async resolution happens here. + * + * Keyboard: Escape closes, `+` / `-` toggle zoom, `0` resets. + */ +import { useEffect, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { AnimatePresence, motion } from 'framer-motion'; +import { useMutation, useQuery } from 'convex/react'; +import { + Download, + ArrowSquareOut, + Star, + X, + MagnifyingGlassPlus, + MagnifyingGlassMinus, +} from '@phosphor-icons/react'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import type { AttachmentMetadata } from './EncryptedAttachment'; +import styles from './ImageLightbox.module.css'; + +interface ImageLightboxProps { + isOpen: boolean; + onClose: () => void; + /** Blob URL of the decrypted image bytes. */ + src: string; + /** Filename to show in the info card and to use for the download + * attribute. Falls back to `alt` then to a generic "image". */ + filename?: string; + /** Legacy alias for `filename` kept so existing MessageGroup callers + * that only pass `alt` still render a label. */ + alt?: string; + /** Pixel dimensions used by the info card's `WxH` readout. */ + width?: number; + height?: number; + /** Byte size of the attachment — rendered alongside the dimensions + * when present. */ + size?: number; + mimeType?: string; + /** + * Original encrypted attachment metadata. When present, the + * lightbox shows a star button so the user can save the image to + * their personal media library — the saved entry stores the same + * url + per-file key + iv so it can be re-posted later without + * re-uploading the bytes. + */ + attachment?: AttachmentMetadata; +} + +function formatBytes(bytes: number): string { + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; + return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`; +} + +export function ImageLightbox({ + isOpen, + onClose, + src, + filename, + alt, + width, + height, + size, + attachment, +}: ImageLightboxProps) { + const [zoomed, setZoomed] = useState(false); + + const label = filename ?? alt ?? 'image'; + + // Saved-media wiring — fetches the user's library once so we can + // flip the star button between "save" and "unsave" states. The + // list query is cheap (per-user, indexed) and stays cached. + const myUserId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const savedList = useQuery( + api.savedMedia.list, + myUserId && isOpen ? { userId: myUserId as Id<'userProfiles'> } : 'skip', + ); + const isSaved = !!( + attachment && + savedList?.some((m) => m.url === attachment.url) + ); + const saveMutation = useMutation(api.savedMedia.save); + const removeMutation = useMutation(api.savedMedia.remove); + + const handleToggleSaved = async () => { + if (!attachment || !myUserId) return; + try { + if (isSaved) { + await removeMutation({ + userId: myUserId as Id<'userProfiles'>, + url: attachment.url, + }); + } else { + const kind = attachment.mimeType.split('/')[0]; // image | video | audio + await saveMutation({ + userId: myUserId as Id<'userProfiles'>, + url: attachment.url, + kind, + filename: attachment.filename, + mimeType: attachment.mimeType, + width: attachment.width, + height: attachment.height, + size: attachment.size, + encryptionKey: attachment.key, + encryptionIv: attachment.iv, + }); + } + } catch (err) { + console.warn('Failed to toggle saved media:', err); + } + }; + + // Close on Escape, toggle zoom via keyboard shortcuts. Also lock + // body scroll while the lightbox is open so the background chat + // doesn't jitter when the backdrop consumes the viewport. + useEffect(() => { + if (!isOpen) return; + const handleKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + else if (e.key === '+' || e.key === '=') setZoomed(true); + else if (e.key === '-' || e.key === '_') setZoomed(false); + else if (e.key === '0') setZoomed(false); + }; + document.addEventListener('keydown', handleKey); + const prevOverflow = document.body.style.overflow; + document.body.style.overflow = 'hidden'; + return () => { + document.removeEventListener('keydown', handleKey); + document.body.style.overflow = prevOverflow; + }; + }, [isOpen, onClose]); + + // Reset transient state whenever the modal closes so a previously + // zoomed session doesn't bleed into the next attachment clicked. + useEffect(() => { + if (!isOpen) setZoomed(false); + }, [isOpen]); + + const dimensionsLabel = width && height ? `${width}×${height}` : ''; + const sizeLabel = size ? formatBytes(size) : ''; + const metaLabel = [dimensionsLabel, sizeLabel].filter(Boolean).join(' · '); + + const handleDownload = () => { + if (!src) return; + // Trigger a download via a temporary <a download>. Blob URLs + // honour the `download` attribute in all modern browsers. + const a = document.createElement('a'); + a.href = src; + a.download = label; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + }; + + const handleOpenExternal = () => { + if (!src) return; + window.open(src, '_blank', 'noopener,noreferrer'); + }; + + const handleToggleZoom = () => setZoomed((z) => !z); + + // Wrap every action button click so it doesn't bubble up to the + // backdrop (which would close the lightbox). + const stop = + (fn: () => void) => + (e: React.MouseEvent) => { + e.stopPropagation(); + fn(); + }; + + return createPortal( + <AnimatePresence> + {isOpen && ( + <motion.div + className={styles.backdrop} + initial={{ opacity: 0 }} + animate={{ opacity: 1 }} + exit={{ opacity: 0 }} + transition={{ duration: 0.15 }} + onClick={onClose} + > + {/* Desktop header — file info card on the left, action + card + close card on the right. Each sits in its + own `--background-textarea` pill. */} + <div className={styles.header} onClick={(e) => e.stopPropagation()}> + <div className={styles.fileInfoCard}> + <div className={styles.filename} title={label}> + {label} + </div> + {metaLabel && <div className={styles.dimensions}>{metaLabel}</div>} + </div> + + <div className={styles.headerRight}> + <div className={styles.actionCard}> + <button + type="button" + className={styles.actionButton} + onClick={stop(handleToggleZoom)} + aria-label={zoomed ? 'Zoom out' : 'Zoom in'} + title={zoomed ? 'Zoom out' : 'Zoom in'} + > + {zoomed ? ( + <MagnifyingGlassMinus size={18} weight="regular" /> + ) : ( + <MagnifyingGlassPlus size={18} weight="regular" /> + )} + </button> + {attachment && ( + <button + type="button" + className={styles.actionButton} + onClick={stop(() => void handleToggleSaved())} + aria-label={isSaved ? 'Unfavorite' : 'Favorite'} + title={isSaved ? 'Unfavorite' : 'Favorite'} + style={ + isSaved + ? { color: 'var(--brand-primary, #5865f2)' } + : undefined + } + > + <Star + size={18} + weight={isSaved ? 'fill' : 'regular'} + /> + </button> + )} + <button + type="button" + className={styles.actionButton} + onClick={stop(handleDownload)} + aria-label="Download" + title="Download" + disabled={!src} + > + <Download size={18} weight="regular" /> + </button> + <button + type="button" + className={styles.actionButton} + onClick={stop(handleOpenExternal)} + aria-label="Open in new tab" + title="Open in new tab" + disabled={!src} + > + <ArrowSquareOut size={18} weight="regular" /> + </button> + </div> + + <div className={styles.closeCard}> + <button + type="button" + className={styles.actionButton} + onClick={stop(onClose)} + aria-label="Close" + title="Close (Esc)" + > + <X size={20} weight="bold" /> + </button> + </div> + </div> + </div> + + {/* Centered image. Clicking it toggles zoom — event is + stopped so the click doesn't propagate to the + backdrop's close handler. */} + <motion.img + key={src || 'loading'} + src={src || undefined} + alt={label} + className={`${styles.image} ${zoomed ? styles.imageZoomed : ''}`} + initial={{ opacity: 0, scale: 0.96 }} + animate={{ opacity: 1, scale: 1 }} + exit={{ opacity: 0, scale: 0.96 }} + transition={{ duration: 0.18, ease: 'easeOut' }} + onClick={stop(handleToggleZoom)} + draggable={false} + /> + </motion.div> + )} + </AnimatePresence>, + document.body, + ); +} diff --git a/packages/shared/src/components/channel/InlineMessageEditor.module.css b/packages/shared/src/components/channel/InlineMessageEditor.module.css new file mode 100644 index 0000000..be02009 --- /dev/null +++ b/packages/shared/src/components/channel/InlineMessageEditor.module.css @@ -0,0 +1,98 @@ +/* ── Fluxer-style inline editor ──────────────────────────────── + Lives in-place of the normal message body when the user edits. + Rounded contenteditable box with the current text, an emoji + button pinned top-right, and a keyboard-hint footer underneath + telling the user how to save or cancel. */ + +.wrapper { + display: flex; + flex-direction: column; + gap: 0.3125rem; + margin-top: 0.125rem; + max-width: 100%; +} + +.box { + position: relative; + display: flex; + align-items: flex-start; + gap: 0.5rem; + padding: 0.625rem 0.75rem; + border-radius: 0.5rem; + background-color: var(--background-secondary); + border: 1px solid var(--background-modifier-accent); +} + +.editor { + flex: 1; + min-width: 0; + min-height: 1.25rem; + max-height: 320px; + overflow-y: auto; + font-size: 0.9375rem; + line-height: 1.375rem; + color: var(--text-primary); + outline: none; + word-wrap: break-word; + white-space: pre-wrap; +} + +.editor:empty::before { + content: attr(data-placeholder); + color: var(--text-tertiary); + pointer-events: none; +} + +.emojiButton { + flex-shrink: 0; + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.75rem; + height: 1.75rem; + border: none; + border-radius: 50%; + background-color: transparent; + color: var(--text-tertiary); + cursor: pointer; + transition: background-color 0.12s, color 0.12s; +} + +.emojiButton:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +.hint { + display: flex; + align-items: center; + gap: 0.25rem; + padding-left: 0.25rem; + font-size: 0.75rem; + color: var(--text-tertiary); +} + +.hint strong { + color: var(--brand-primary, #4641d9); + font-weight: 600; + cursor: pointer; +} + +.hint strong:hover { + text-decoration: underline; +} + +.hintSeparator { + display: inline-block; + width: 3px; + height: 3px; + border-radius: 50%; + background-color: var(--text-tertiary); + margin: 0 0.125rem; +} + +.error { + font-size: 0.75rem; + color: var(--status-danger); + padding-left: 0.25rem; +} diff --git a/packages/shared/src/components/channel/InlineMessageEditor.tsx b/packages/shared/src/components/channel/InlineMessageEditor.tsx new file mode 100644 index 0000000..7ca55b8 --- /dev/null +++ b/packages/shared/src/components/channel/InlineMessageEditor.tsx @@ -0,0 +1,147 @@ +/** + * InlineMessageEditor — fluxer-style inline edit UI for a message. + * + * Renders a rounded contenteditable box in place of the normal + * message body. Keyboard model matches fluxer: Enter saves, Escape + * cancels, Shift+Enter inserts a newline. A secondary "escape to + * cancel • enter to save" hint sits under the box and doubles as + * clickable shortcuts (the words `cancel` and `save` are buttons). + * + * The editor is a contenteditable `div` rather than a textarea so + * future work can drop mention pills / custom emoji into the same + * edit path (matching the composer's behaviour). For v1 we just + * serialise `innerText` back to plain text on save. + */ +import { useCallback, useEffect, useRef, useState } from 'react'; +import { Smiley } from '@phosphor-icons/react'; +import styles from './InlineMessageEditor.module.css'; + +export interface InlineMessageEditorProps { + /** Current body we're editing — pre-filled into the box. For + * attachments this is the existing caption (MSC2530 `body` is + * the caption when `filename` is set). */ + initialContent: string; + /** Called with the new text when the user commits. Empty + * strings are allowed (clearing a caption, or blanking a text + * message to effectively delete it — the caller decides). */ + onSave: (newContent: string) => Promise<void> | void; + /** Called when the user escapes or clicks "cancel". Parent + * should clear its `editingEventId` state. */ + onCancel: () => void; + /** Placeholder shown when the box is empty. Callers typically + * pass a type-specific hint ("Add a caption…" for attachments, + * "Message" for text). */ + placeholder?: string; +} + +export function InlineMessageEditor({ + initialContent, + onSave, + onCancel, + placeholder = 'Message', +}: InlineMessageEditorProps) { + const editorRef = useRef<HTMLDivElement>(null); + const [saving, setSaving] = useState(false); + const [error, setError] = useState<string | null>(null); + + // Seed the editor once on mount with the existing content, then + // drop the caret at the end so the user can keep typing. A ref + // rather than React-controlled content so React doesn't fight + // the contenteditable on every keystroke. + useEffect(() => { + const el = editorRef.current; + if (!el) return; + el.textContent = initialContent; + el.focus(); + // Move caret to end of content. + const range = document.createRange(); + range.selectNodeContents(el); + range.collapse(false); + const sel = window.getSelection(); + if (sel) { + sel.removeAllRanges(); + sel.addRange(range); + } + // Intentionally depend on nothing so this only runs once — + // we don't want to clobber the user's typing if `initialContent` + // ever changes identity during an edit session. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const handleSave = useCallback(async () => { + if (saving) return; + const next = editorRef.current?.innerText ?? ''; + // Trim trailing whitespace but preserve interior newlines so + // multi-line edits survive round-tripping. + const trimmed = next.replace(/\s+$/g, ''); + setError(null); + setSaving(true); + try { + await onSave(trimmed); + } catch (err: any) { + setError(err?.message || 'Failed to save edit.'); + setSaving(false); + return; + } + setSaving(false); + }, [onSave, saving]); + + const handleKeyDown = useCallback( + (e: React.KeyboardEvent<HTMLDivElement>) => { + if (e.key === 'Escape') { + e.preventDefault(); + onCancel(); + return; + } + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + void handleSave(); + return; + } + // Shift+Enter falls through so the contenteditable inserts a + // line break normally. + }, + [handleSave, onCancel], + ); + + return ( + <div className={styles.wrapper}> + <div className={styles.box}> + <div + ref={editorRef} + className={styles.editor} + contentEditable + suppressContentEditableWarning + data-placeholder={placeholder} + role="textbox" + aria-multiline="true" + onKeyDown={handleKeyDown} + /> + <button + type="button" + className={styles.emojiButton} + aria-label="Insert emoji" + // Emoji picker integration for the inline editor + // is a follow-up — for v1 the button is decorative + // so the surface visually matches fluxer. Wiring it + // would reuse the EmojiPicker popover pattern from + // ChannelTextarea. + onClick={(e) => e.preventDefault()} + > + <Smiley size={18} weight="fill" /> + </button> + </div> + {error ? ( + <div className={styles.error}>{error}</div> + ) : ( + <div className={styles.hint}> + <span>escape to</span> + <strong onClick={onCancel}>cancel</strong> + <span className={styles.hintSeparator} /> + <span>enter to</span> + <strong onClick={() => void handleSave()}>save</strong> + </div> + )} + </div> + ); +} diff --git a/packages/shared/src/components/channel/LinkEmbed.module.css b/packages/shared/src/components/channel/LinkEmbed.module.css new file mode 100644 index 0000000..3be6218 --- /dev/null +++ b/packages/shared/src/components/channel/LinkEmbed.module.css @@ -0,0 +1,203 @@ +/* ── Link Embed — matches Fluxer's embed card ────────────────────────── */ + +.embed { + position: relative; + display: inline-grid; + inline-size: fit-content; + max-inline-size: 100%; + max-width: 432px; + box-sizing: border-box; + border-radius: 8px; + background: var(--background-primary); + border: 1px solid var(--background-modifier-accent); + border-left: 4px solid var(--brand-primary); + margin-top: 4px; +} + +.grid { + overflow: hidden; + padding: 12px 12px 14px 12px; + display: grid; + grid-template-columns: auto; + grid-template-rows: auto; +} + +.embedContent { + min-width: 0; + display: flex; + flex-direction: column; + gap: 8px; +} + +.embedContent > *:first-child { + margin-top: 4px; +} + +/* Provider (e.g. "YouTube", "GitHub") */ +.provider { + font-size: 0.75rem; + line-height: 1rem; + font-weight: 500; + color: var(--text-tertiary); +} + +/* Author row */ +.author { + display: flex; + align-items: center; + min-width: 0; +} + +.authorIcon { + flex-shrink: 0; + margin-right: 8px; + width: 24px; + height: 24px; + object-fit: cover; + border-radius: 50%; +} + +.authorName { + font-size: 0.875rem; + font-weight: 600; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* Title */ +.title { + font-size: 1rem; + font-weight: 600; + display: inline-block; + color: var(--text-link); + text-decoration: none; + cursor: pointer; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.title:hover { + text-decoration: underline; +} + +/* Description */ +.description { + font-size: 0.875rem; + line-height: 1.125rem; + white-space: pre-line; + color: var(--text-primary); + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + overflow: hidden; +} + +/* ── Media (image / video thumbnail) ─────────────────────────────────── */ + +.mediaContainer { + position: relative; + border-radius: 4px; + overflow: hidden; + margin-top: 4px; + contain: paint; + cursor: pointer; +} + +.mediaImage { + display: block; + max-width: 100%; + max-height: 300px; + width: 100%; + border-radius: 4px; + object-fit: cover; +} + +/* Dark overlay for video thumbnails */ +.mediaOverlay { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background: rgba(0, 0, 0, 0.4); + opacity: 0; + transition: opacity 0.2s ease; + text-decoration: none; +} + +.mediaContainer:hover .mediaOverlay { + opacity: 1; +} + +.mediaControls { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.playButton, +.openButton { + display: flex; + height: 3.5rem; + width: 3.5rem; + align-items: center; + justify-content: center; + border-radius: 50%; + background: rgba(0, 0, 0, 0.75); + cursor: pointer; + transition: background 200ms ease; + color: #fff; + border: none; + padding: 0; +} + +.playButton:hover, +.openButton:hover { + background: rgba(0, 0, 0, 0.85); +} + +.openButton { + height: 2.75rem; + width: 2.75rem; +} + +.directVideoWrapper { + position: relative; + display: inline-block; +} + +.directVideo { + max-width: 400px; + max-height: 300px; + border-radius: 8px; + display: block; +} + +.directPlayOverlay { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background: rgba(0, 0, 0, 0.35); + border: none; + border-radius: 8px; + cursor: pointer; + color: #fff; + transition: background 0.15s; +} + +.directPlayOverlay:hover { + background: rgba(0, 0, 0, 0.5); +} + +.directImage { + max-width: 400px; + max-height: 300px; + border-radius: 8px; + display: block; + object-fit: contain; +} diff --git a/packages/shared/src/components/channel/LinkEmbed.tsx b/packages/shared/src/components/channel/LinkEmbed.tsx new file mode 100644 index 0000000..007c2cb --- /dev/null +++ b/packages/shared/src/components/channel/LinkEmbed.tsx @@ -0,0 +1,265 @@ +import { useEffect, useRef, useState } from 'react'; +import { ArrowSquareOut, Play } from '@phosphor-icons/react'; +import { useAction } from 'convex/react'; +import { api } from '../../../../../convex/_generated/api'; +import { usePlatform } from '../../platform'; +import styles from './LinkEmbed.module.css'; + +interface UrlPreview { + title?: string; + description?: string; + imageUrl?: string; + siteName?: string; +} + +const VIDEO_HOSTS = [ + 'youtube.com', + 'youtu.be', + 'www.youtube.com', + 'vimeo.com', + 'www.vimeo.com', + 'twitch.tv', + 'www.twitch.tv', + 'dailymotion.com', + 'www.dailymotion.com', +]; + +const DIRECT_VIDEO_EXTS = ['.mp4', '.webm', '.ogg', '.mov']; +const DIRECT_IMAGE_EXTS = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg']; + +function isDirectMedia(url: string): 'video' | 'image' | null { + try { + const pathname = new URL(url).pathname.toLowerCase(); + if (DIRECT_VIDEO_EXTS.some((ext) => pathname.endsWith(ext))) return 'video'; + if (DIRECT_IMAGE_EXTS.some((ext) => pathname.endsWith(ext))) return 'image'; + } catch {} + return null; +} + +function isVideoUrl(url: string): boolean { + try { + const hostname = new URL(url).hostname; + return VIDEO_HOSTS.some((h) => hostname === h || hostname.endsWith('.' + h)); + } catch { + return false; + } +} + +// Module-scope cache so we don't refetch the same URL every re-render. +// `null` means "we tried and there's no preview" — we cache that too to +// avoid hammering the fetcher for URLs that will never resolve. +const previewCache = new Map<string, UrlPreview | null>(); + +function normaliseMetadata(raw: any): UrlPreview | null { + if (!raw || typeof raw !== 'object') return null; + + // Accept a few possible shapes: the Electron preload IPC returns + // `{ title, description, image, siteName, url }`; matrix-js-sdk style + // returns `{ 'og:title', 'og:description', 'og:image', 'og:site_name' }`. + const title = + raw.title ?? raw['og:title'] ?? raw.ogTitle ?? undefined; + const description = + raw.description ?? raw['og:description'] ?? raw.ogDescription ?? undefined; + const imageUrl = + raw.image ?? raw.imageUrl ?? raw['og:image'] ?? raw.ogImage ?? undefined; + const siteName = + raw.siteName ?? raw['og:site_name'] ?? raw.ogSiteName ?? undefined; + + if (!title && !description && !imageUrl) return null; + return { title, description, imageUrl, siteName }; +} + +function useUrlPreview(url: string): UrlPreview | null { + const platform = usePlatform(); + const fetchPreviewAction = useAction(api.links.fetchPreview); + const [preview, setPreview] = useState<UrlPreview | null>( + previewCache.get(url) ?? null, + ); + + useEffect(() => { + if (previewCache.has(url)) { + setPreview(previewCache.get(url) ?? null); + return; + } + + let cancelled = false; + + (async () => { + try { + // Prefer the platform-native fetcher when available (Electron + // ships one via IPC — no CORS, no round-trip through the + // backend). On web, `fetchMetadata` returns null due to CORS, + // so we fall through to the Convex Node action which performs + // the fetch server-side. + let result: UrlPreview | null = null; + const fetcher = platform?.links?.fetchMetadata; + if (typeof fetcher === 'function') { + try { + const raw = await fetcher(url); + result = normaliseMetadata(raw); + } catch { + result = null; + } + } + if (!result) { + try { + const raw = await fetchPreviewAction({ url }); + if (!cancelled) result = normaliseMetadata(raw); + } catch { + result = null; + } + } + if (cancelled) return; + previewCache.set(url, result); + setPreview(result); + } catch { + if (!cancelled) previewCache.set(url, null); + } + })(); + + return () => { + cancelled = true; + }; + }, [url, platform, fetchPreviewAction]); + + return preview; +} + +function DirectMediaEmbed({ url, type }: { url: string; type: 'video' | 'image' }) { + const videoRef = useRef<HTMLVideoElement>(null); + const [playing, setPlaying] = useState(false); + + if (type === 'video') { + const handlePlay = () => { + if (!videoRef.current) return; + videoRef.current.controls = true; + void videoRef.current.play(); + setPlaying(true); + }; + + return ( + <div className={styles.embed}> + <div className={styles.directVideoWrapper}> + <video + ref={videoRef} + className={styles.directVideo} + src={url} + preload="metadata" + onPause={() => { + if (videoRef.current && videoRef.current.ended) { + videoRef.current.controls = false; + setPlaying(false); + } + }} + onEnded={() => { + if (videoRef.current) { + videoRef.current.controls = false; + setPlaying(false); + } + }} + /> + {!playing && ( + <button + type="button" + className={styles.directPlayOverlay} + onClick={handlePlay} + > + <Play size={36} weight="fill" /> + </button> + )} + </div> + </div> + ); + } + + return ( + <div className={styles.embed}> + <a href={url} target="_blank" rel="noopener noreferrer"> + <img + className={styles.directImage} + src={url} + alt="" + loading="lazy" + /> + </a> + </div> + ); +} + +interface LinkEmbedProps { + url: string; +} + +export function LinkEmbed({ url }: LinkEmbedProps) { + const directType = isDirectMedia(url); + if (directType) { + return <DirectMediaEmbed url={url} type={directType} />; + } + + const preview = useUrlPreview(url); + + // If the platform has no metadata fetcher (or it returned null / threw), + // degrade gracefully to nothing — the raw link is already rendered in + // the message body by MessageContent. + if (!preview) return null; + + const isVideo = isVideoUrl(url); + const hasImage = !!preview.imageUrl; + + return ( + <div className={styles.embed}> + <div className={styles.grid}> + <div className={styles.embedContent}> + {preview.siteName && ( + <div className={styles.provider}>{preview.siteName}</div> + )} + + {preview.title && ( + <a + className={styles.title} + href={url} + target="_blank" + rel="noopener noreferrer" + > + {preview.title} + </a> + )} + + {preview.description && ( + <div className={styles.description}>{preview.description}</div> + )} + + {hasImage && ( + <div className={styles.mediaContainer}> + <img + className={styles.mediaImage} + src={preview.imageUrl} + alt={preview.title || ''} + loading="lazy" + /> + {isVideo && ( + <a + className={styles.mediaOverlay} + href={url} + target="_blank" + rel="noopener noreferrer" + > + <div className={styles.mediaControls}> + <button type="button" className={styles.playButton}> + <Play size={28} weight="fill" /> + </button> + <button type="button" className={styles.openButton}> + <ArrowSquareOut size={22} /> + </button> + </div> + </a> + )} + </div> + )} + </div> + </div> + </div> + ); +} + +export default LinkEmbed; diff --git a/packages/shared/src/components/channel/MentionAutocomplete.module.css b/packages/shared/src/components/channel/MentionAutocomplete.module.css new file mode 100644 index 0000000..166f840 --- /dev/null +++ b/packages/shared/src/components/channel/MentionAutocomplete.module.css @@ -0,0 +1,104 @@ +/* Mention autocomplete popup — matches Fluxer's Autocomplete look. The + popup floats above the chat input, slightly rounded, with a compact + section header and individual rows for each mentionable user. */ + +.container { + overflow: hidden; + border-radius: 8px; + background-color: var(--background-primary, #111214); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.24), 0 0 0 1px rgba(255, 255, 255, 0.04); + display: flex; + flex-direction: column; + max-height: 420px; +} + +.header { + flex: 0 0 auto; + padding: 8px 12px 4px; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.02em; + text-transform: uppercase; + color: var(--text-primary-muted, #a0a3a8); + user-select: none; +} + +.scroller { + display: flex; + flex-direction: column; + gap: 2px; + padding: 4px 8px 8px; + overflow-y: auto; + min-height: 0; +} + +.row { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + padding: 6px 8px; + border: none; + background-color: transparent; + border-radius: 6px; + text-align: left; + cursor: pointer; + color: var(--text-primary, #fff); + font: inherit; + min-height: 32px; +} + +.rowActive, +.row:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.06)); +} + +.icon { + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; +} + +.everyoneIcon { + width: 24px; + height: 24px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--brand-primary, #5865f2); + color: #fff; +} + +.nameWrapper { + min-width: 0; + flex: 1 1 auto; + overflow: hidden; +} + +.name { + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-weight: 500; + font-size: 14px; + color: var(--text-primary, #fff); + line-height: 1.25; +} + +.description { + flex: 0 1 auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: right; + font-weight: 400; + color: var(--text-primary-muted, #a0a3a8); + font-size: 12px; + line-height: 1.33; + max-width: 50%; +} diff --git a/packages/shared/src/components/channel/MentionAutocomplete.tsx b/packages/shared/src/components/channel/MentionAutocomplete.tsx new file mode 100644 index 0000000..854d48e --- /dev/null +++ b/packages/shared/src/components/channel/MentionAutocomplete.tsx @@ -0,0 +1,257 @@ +/** + * Mention autocomplete popup — opens above the chat input when the user + * types `@`. Renders the server members for the current channel, with + * arrow-key navigation and Enter/click to insert. + */ +import { + forwardRef, + useEffect, + useImperativeHandle, + useLayoutEffect, + useMemo, + useRef, + useState, +} from 'react'; +import { createPortal } from 'react-dom'; +import { useQuery } from 'convex/react'; +import { Avatar } from '@discord-clone/ui'; +import { Megaphone } from '@phosphor-icons/react'; +import { api } from '../../../../../convex/_generated/api'; +import styles from './MentionAutocomplete.module.css'; + +export type MentionItem = + | { + kind: 'user'; + userId: string; + displayName: string; + username: string; + avatar?: string; + } + | { + kind: 'everyone'; + }; + +export interface MentionAutocompleteHandle { + /** Move the keyboard selection. Returns true if the popup consumed the key. */ + moveSelection: (delta: number) => boolean; + /** Commit the currently highlighted item. Returns true if something was selected. */ + commit: () => boolean; +} + +interface Props { + channelId: string; + query: string; + anchorEl: HTMLElement | null; + onSelect: (item: MentionItem) => void; + onClose: () => void; +} + +const MAX_RESULTS = 10; + +/** + * Rank a candidate against the query. Lower score = better. + * -2: startsWith match (best) + * -1: word-boundary match + * 0: substring match anywhere + * null: no match + */ +function scoreMatch(candidate: string, query: string): number | null { + if (!query) return 0; + const c = candidate.toLowerCase(); + const q = query.toLowerCase(); + if (c.startsWith(q)) return -2; + const words = c.split(/[\s_\-.:/]+/); + for (const w of words) { + if (w !== c && w.startsWith(q)) return -1; + } + return c.includes(q) ? 0 : null; +} + +interface ConvexMember { + id: string; + username: string; + displayName: string | null; + avatarUrl: string | null; +} + +export const MentionAutocomplete = forwardRef<MentionAutocompleteHandle, Props>( + function MentionAutocomplete({ channelId, query, anchorEl, onSelect, onClose }, ref) { + const [selected, setSelected] = useState(0); + const [pos, setPos] = useState<{ left: number; bottom: number; width: number } | null>( + null, + ); + const containerRef = useRef<HTMLDivElement>(null); + + const membersRaw = useQuery( + api.members.getChannelMembers, + channelId ? { channelId: channelId as any } : 'skip', + ) as ConvexMember[] | undefined; + + const items = useMemo<MentionItem[]>(() => { + const result: MentionItem[] = []; + if (scoreMatch('everyone', query) !== null) { + result.push({ kind: 'everyone' }); + } + if (membersRaw) { + type Scored = { item: MentionItem; score: number; name: string }; + const scored: Scored[] = []; + for (const m of membersRaw) { + const name = m.displayName || m.username; + const s1 = scoreMatch(name, query); + const s2 = scoreMatch(m.username, query); + const best = + s1 === null && s2 === null + ? null + : Math.min(s1 ?? Infinity, s2 ?? Infinity); + if (best === null) continue; + scored.push({ + item: { + kind: 'user', + userId: m.id, + displayName: name, + username: m.username, + avatar: m.avatarUrl ?? undefined, + }, + score: best, + name, + }); + } + scored.sort((a, b) => { + if (a.score !== b.score) return a.score - b.score; + return a.name.localeCompare(b.name); + }); + result.push(...scored.slice(0, MAX_RESULTS).map((s) => s.item)); + } + return result; + }, [membersRaw, query]); + + // Clamp selection when the list changes. + useEffect(() => { + setSelected((prev) => { + if (items.length === 0) return 0; + if (prev >= items.length) return items.length - 1; + return prev; + }); + }, [items]); + + // Close if the filter yielded nothing — but only after the query + // has had a chance to resolve. While the query is still loading + // (membersRaw === undefined) we keep the popup open so the user + // doesn't see it flicker. + useEffect(() => { + if (membersRaw !== undefined && items.length === 0) { + onClose(); + } + }, [membersRaw, items.length, onClose]); + + // Position above the anchor (the textarea). + useLayoutEffect(() => { + if (!anchorEl) return; + const update = () => { + const rect = anchorEl.getBoundingClientRect(); + setPos({ + left: rect.left, + bottom: window.innerHeight - rect.top + 8, + width: Math.max(280, Math.min(rect.width, 520)), + }); + }; + update(); + window.addEventListener('resize', update); + window.addEventListener('scroll', update, true); + return () => { + window.removeEventListener('resize', update); + window.removeEventListener('scroll', update, true); + }; + }, [anchorEl]); + + // Keep the highlighted row in view. + useEffect(() => { + const container = containerRef.current; + if (!container) return; + const el = container.querySelector<HTMLElement>(`[data-index="${selected}"]`); + el?.scrollIntoView({ block: 'nearest' }); + }, [selected]); + + useImperativeHandle( + ref, + () => ({ + moveSelection: (delta) => { + if (items.length === 0) return false; + setSelected((prev) => { + const next = (prev + delta + items.length) % items.length; + return next; + }); + return true; + }, + commit: () => { + const item = items[selected]; + if (!item) return false; + onSelect(item); + return true; + }, + }), + [items, selected, onSelect], + ); + + if (!pos || items.length === 0) return null; + + return createPortal( + <div + ref={containerRef} + className={styles.container} + style={{ + position: 'fixed', + left: pos.left, + bottom: pos.bottom, + width: pos.width, + zIndex: 15000, + }} + // Prevent the contenteditable from losing focus when users + // click into the popup. + onMouseDown={(e) => e.preventDefault()} + > + <div className={styles.header}>MEMBERS</div> + <div className={styles.scroller}> + {items.map((item, index) => { + const isActive = index === selected; + return ( + <button + key={item.kind === 'everyone' ? 'everyone' : item.userId} + type="button" + data-index={index} + className={`${styles.row} ${isActive ? styles.rowActive : ''}`} + onMouseEnter={() => setSelected(index)} + onClick={() => onSelect(item)} + > + <div className={styles.icon}> + {item.kind === 'everyone' ? ( + <div className={styles.everyoneIcon}> + <Megaphone size={16} weight="fill" /> + </div> + ) : ( + <Avatar + src={item.avatar} + fallback={item.displayName} + size={24} + /> + )} + </div> + <div className={styles.nameWrapper}> + <div className={styles.name}> + {item.kind === 'everyone' ? '@everyone' : item.displayName} + </div> + </div> + <div className={styles.description}> + {item.kind === 'everyone' + ? 'Notify everyone in this channel' + : item.username} + </div> + </button> + ); + })} + </div> + </div>, + document.body, + ); + }, +); diff --git a/packages/shared/src/components/channel/MessageActionBar.module.css b/packages/shared/src/components/channel/MessageActionBar.module.css new file mode 100644 index 0000000..9301ae4 --- /dev/null +++ b/packages/shared/src/components/channel/MessageActionBar.module.css @@ -0,0 +1,112 @@ +/* Message action bar - appears on hover */ + +.container { + position: absolute; + top: -16px; + right: 16px; + display: none; + align-items: center; + padding: 2px; + background-color: var(--background-primary); + border: 1px solid var(--background-header-secondary); + border-radius: 8px; + z-index: 1; +} + +/* Show on hover from parent message */ +:global(.messageHoverable):hover > .container, +:global(.actionBarForceVisible) > .container { + display: flex; +} + +/* Mobile: never show the desktop hover action bar. Mobile users get + the long-press bottom sheet (MobileMessageActionsSheet) instead — + the hover bar would be both useless (no hover) and visually noisy + when a tap accidentally triggers the :hover state on iOS. */ +@media (max-width: 768px) { + :global(.messageHoverable):hover > .container, + :global(.actionBarForceVisible) > .container { + display: none; + } +} + +.button { + display: flex; + align-items: center; + justify-content: center; + min-width: 30px; + height: 30px; + padding: 4px; + border-radius: 6px; + background: none; + border: none; + cursor: pointer; + color: var(--text-tertiary); + transition: background-color 0.1s, color 0.1s; +} + +.button:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +.quickEmoji { + width: 20px; + height: 20px; + display: block; +} + +.divider { + width: 1px; + height: 20px; + margin: 0 2px; + background-color: var(--background-modifier-accent); +} + +/* ── More dropdown menu ───────────────────────────────────────── */ + +.moreMenu { + min-width: 188px; + padding: 6px 8px; + background-color: var(--background-floating, var(--background-primary)); + border-radius: 8px; + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.24); +} + +.menuItem { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + padding: 6px 8px; + border-radius: 4px; + background: none; + border: none; + cursor: pointer; + color: var(--text-secondary); + font-size: 0.875rem; + font-weight: 500; + font-family: inherit; + text-align: left; + transition: background-color 0.1s, color 0.1s; +} + +.menuItem:hover { + background-color: var(--brand-primary); + color: #fff; +} + +.menuItemDanger { + color: var(--status-danger); +} + +.menuItemDanger:hover { + background-color: var(--status-danger); + color: #fff; +} + +.menuDivider { + height: 1px; + margin: 4px 8px; + background-color: var(--background-modifier-accent); +} diff --git a/packages/shared/src/components/channel/MessageActionBar.tsx b/packages/shared/src/components/channel/MessageActionBar.tsx new file mode 100644 index 0000000..ceeea90 --- /dev/null +++ b/packages/shared/src/components/channel/MessageActionBar.tsx @@ -0,0 +1,326 @@ +import { + ArrowBendUpLeft, + ArrowBendUpRight, + Copy, + DotsThree, + Link, + PencilSimple, + PushPin, + Smiley, + Trash, +} from '@phosphor-icons/react'; +import { useEffect, useLayoutEffect, useRef, useState, type MouseEvent } from 'react'; +import { createPortal } from 'react-dom'; +import { getTwemojiUrl } from '../../utils/twemoji'; +import styles from './MessageActionBar.module.css'; + +// Default quick-reaction emojis shown as the first three icons on the +// hover bar. Matches the new UI's set. +const QUICK_EMOJIS: Array<{ emoji: string; name: string }> = [ + { emoji: '😄', name: 'smile' }, + { emoji: '👍', name: 'thumbsup' }, + { emoji: '👌', name: 'ok_hand' }, +]; + +interface MessageActionBarProps { + isOwnMessage: boolean; + onReply?: () => void; + onEdit?: () => void; + onDelete?: () => void; + onReact?: (e?: MouseEvent<HTMLButtonElement>) => void; + onQuickReact?: (emoji: string) => void; + onPin?: () => void; + onCopyText?: () => void; + onCopyLink?: () => void; + onForward?: () => void; + /** + * External trigger for opening the More dropdown (e.g. right-click + * on a message). When set, the dropdown opens at `{ x, y }` and the + * hover action bar stays visible via the `.actionBarForceVisible` + * class on the parent. Pass null to close. + */ + externalMenuAt?: { x: number; y: number } | null; + onExternalMenuClose?: () => void; + /** + * Fires when the local More menu opens / closes so the parent + * (MessageGroup) can keep the hover bar pinned to visible while + * the dropdown is on screen. Without this the bar disappears as + * soon as the pointer leaves the message row. + */ + onMenuOpenChange?: (isOpen: boolean) => void; +} + +/** + * Hover quick-action bar + right-click context menu. + * + * Appears via `:global(.messageHoverable):hover > .container` — must be + * rendered as a direct child of an element with the `messageHoverable` + * class. + * + * The "More" button opens a portal-rendered dropdown with additional + * actions (edit/delete/pin/copy/copy link). + */ +export function MessageActionBar({ + isOwnMessage, + onReply, + onEdit, + onDelete, + onReact, + onQuickReact, + onPin, + onCopyText, + onCopyLink, + onForward, + externalMenuAt, + onExternalMenuClose, + onMenuOpenChange, +}: MessageActionBarProps) { + const moreButtonRef = useRef<HTMLButtonElement>(null); + const menuRef = useRef<HTMLDivElement>(null); + const [menuAt, setMenuAt] = useState<{ x: number; y: number } | null>(null); + // Adjusted position after we measure the menu's actual size and + // clamp it to the viewport. Falls back to the requested coords + // while the measurement is in flight (one frame at most). + const [adjustedPos, setAdjustedPos] = useState< + { top: number; left: number } | null + >(null); + + // Merge external right-click menu state with local more-button state. + const effectiveMenuAt = externalMenuAt ?? menuAt; + + // Notify the parent whenever the menu open state flips, so the + // message row can pin the hover bar visible while the dropdown is + // up. Triggered for both local and external menus. + useEffect(() => { + onMenuOpenChange?.(!!effectiveMenuAt); + }, [effectiveMenuAt, onMenuOpenChange]); + + useEffect(() => { + if (!effectiveMenuAt) return; + const onKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') closeMenu(); + }; + document.addEventListener('keydown', onKey); + return () => document.removeEventListener('keydown', onKey); + }, [effectiveMenuAt]); + + // Clamp the menu inside the viewport. When the requested top/left + // would push the menu off-screen, flip vertically (open upwards) + // or shift horizontally so it stays fully visible. + useLayoutEffect(() => { + if (!effectiveMenuAt) { + setAdjustedPos(null); + return; + } + const measure = () => { + const el = menuRef.current; + if (!el) return; + const rect = el.getBoundingClientRect(); + const margin = 8; + const vw = window.innerWidth; + const vh = window.innerHeight; + let top = effectiveMenuAt.y; + let left = effectiveMenuAt.x - 180; + // Vertical: flip upwards if the menu would overflow the + // bottom of the viewport. Use the menu's measured height + // so the flip lines its bottom up with the requested y. + if (top + rect.height + margin > vh) { + top = Math.max(margin, effectiveMenuAt.y - rect.height); + } + top = Math.max(margin, Math.min(top, vh - rect.height - margin)); + // Horizontal: shift left if it would overflow the right + // edge, then clamp at the left margin. + if (left + rect.width + margin > vw) { + left = vw - rect.width - margin; + } + left = Math.max(margin, left); + setAdjustedPos({ top, left }); + }; + // One frame to let the menu mount, one fallback in case + // requestAnimationFrame fires before layout settles. + const raf = requestAnimationFrame(measure); + return () => cancelAnimationFrame(raf); + }, [effectiveMenuAt]); + + const closeMenu = () => { + setMenuAt(null); + onExternalMenuClose?.(); + }; + + const openMoreMenu = () => { + const rect = moreButtonRef.current?.getBoundingClientRect(); + if (!rect) return; + setMenuAt({ x: rect.right, y: rect.bottom + 4 }); + }; + + const runAndClose = (handler?: () => void) => () => { + handler?.(); + closeMenu(); + }; + + return ( + <div className={styles.container}> + {onQuickReact && + QUICK_EMOJIS.map((qe) => ( + <button + key={qe.name} + type="button" + className={styles.button} + onClick={() => onQuickReact(qe.emoji)} + aria-label={`React with :${qe.name}:`} + title={`:${qe.name}:`} + > + <img + src={getTwemojiUrl(qe.emoji)} + alt={qe.emoji} + className={styles.quickEmoji} + draggable={false} + /> + </button> + ))} + <button + type="button" + className={styles.button} + onClick={(e) => onReact?.(e)} + aria-label="Add reaction" + title="Add Reaction" + > + <Smiley size={20} /> + </button> + <button + type="button" + className={styles.button} + onClick={onReply} + aria-label="Reply" + title="Reply" + > + <ArrowBendUpLeft size={20} /> + </button> + {isOwnMessage && onEdit && ( + <button + type="button" + className={styles.button} + onClick={onEdit} + aria-label="Edit" + title="Edit" + > + <PencilSimple size={20} /> + </button> + )} + <button + type="button" + ref={moreButtonRef} + className={styles.button} + onClick={openMoreMenu} + aria-label="More" + title="More" + > + <DotsThree size={20} weight="bold" /> + </button> + + {effectiveMenuAt && + createPortal( + <> + <div + style={{ position: 'fixed', inset: 0, zIndex: 14999 }} + onClick={closeMenu} + onContextMenu={(e) => { + e.preventDefault(); + closeMenu(); + }} + /> + <div + ref={menuRef} + className={styles.moreMenu} + style={{ + position: 'fixed', + top: adjustedPos?.top ?? effectiveMenuAt.y, + left: adjustedPos?.left ?? effectiveMenuAt.x - 180, + // Hide the menu for the first frame while we + // measure it; revealing it after the clamp + // avoids a flash at the wrong position. + visibility: adjustedPos ? 'visible' : 'hidden', + zIndex: 15000, + }} + onClick={(e) => e.stopPropagation()} + > + {onReply && ( + <button + type="button" + className={styles.menuItem} + onClick={runAndClose(onReply)} + > + <ArrowBendUpLeft size={16} /> + <span>Reply</span> + </button> + )} + {onForward && ( + <button + type="button" + className={styles.menuItem} + onClick={runAndClose(onForward)} + > + <ArrowBendUpRight size={16} /> + <span>Forward</span> + </button> + )} + {onPin && ( + <button + type="button" + className={styles.menuItem} + onClick={runAndClose(onPin)} + > + <PushPin size={16} /> + <span>Pin Message</span> + </button> + )} + {onCopyText && ( + <button + type="button" + className={styles.menuItem} + onClick={runAndClose(onCopyText)} + > + <Copy size={16} /> + <span>Copy Text</span> + </button> + )} + {onCopyLink && ( + <button + type="button" + className={styles.menuItem} + onClick={runAndClose(onCopyLink)} + > + <Link size={16} /> + <span>Copy Link</span> + </button> + )} + {isOwnMessage && onEdit && ( + <button + type="button" + className={styles.menuItem} + onClick={runAndClose(onEdit)} + > + <PencilSimple size={16} /> + <span>Edit Message</span> + </button> + )} + {isOwnMessage && onDelete && ( + <> + <div className={styles.menuDivider} /> + <button + type="button" + className={`${styles.menuItem} ${styles.menuItemDanger}`} + onClick={runAndClose(onDelete)} + > + <Trash size={16} /> + <span>Delete Message</span> + </button> + </> + )} + </div> + </>, + document.body, + )} + </div> + ); +} diff --git a/packages/shared/src/components/channel/MessageContent.module.css b/packages/shared/src/components/channel/MessageContent.module.css new file mode 100644 index 0000000..4c625c0 --- /dev/null +++ b/packages/shared/src/components/channel/MessageContent.module.css @@ -0,0 +1,163 @@ +.content { + word-wrap: break-word; + white-space: pre-wrap; +} + +.emoji { + width: 1.375em; + height: 1.375em; + vertical-align: -0.3em; + display: inline; + object-fit: contain; +} + +.emojiOnly .emoji { + width: 3rem; + height: 3rem; + vertical-align: -0.5em; +} + +/* MSC2545 custom emoji — inline image from a server's image pack. + Sized to the line-height so it sits cleanly next to text. Animated + GIF / WebP play automatically because they're just <img> elements. */ +.customEmoji { + display: inline-block; + height: 1.4em; + width: auto; + max-width: 3em; + vertical-align: -0.35em; + object-fit: contain; +} + +.emojiOnly .customEmoji { + height: 3rem; + max-width: 4rem; + vertical-align: -0.5em; +} + +/* Inline formatting */ +.bold { + font-weight: 700; +} + +.italic { + font-style: italic; +} + +.strikethrough { + text-decoration: line-through; +} + +.inlineCode { + padding: 0.1em 0.3em; + margin: 0 0.1em; + border-radius: var(--radius-sm); + background-color: var(--background-secondary); + font-family: var(--font-mono); + font-size: 0.85em; + line-height: 1.125rem; + color: var(--text-primary); + white-space: pre-wrap; +} + +/* Code blocks */ +.codeBlock { + margin: 4px 0; + border-radius: var(--radius-lg); + background-color: var(--background-secondary); + border: 1px solid var(--background-tertiary); + overflow: hidden; + white-space: pre; +} + +.codeBlockHeader { + display: flex; + align-items: center; + justify-content: space-between; + padding: 6px 12px; + background-color: var(--background-tertiary); + font-size: var(--font-size-xs); + color: var(--text-secondary); + font-family: var(--font-sans); + font-weight: 600; + text-transform: lowercase; +} + +.codeBlockBody { + padding: 8px 12px; + font-family: var(--font-mono); + font-size: 0.875rem; + line-height: 1.125rem; + color: var(--text-primary); + overflow-x: auto; + white-space: pre; +} + +/* Block quotes */ +.blockquote { + display: flex; + margin: 2px 0; +} + +.blockquoteBorder { + flex-shrink: 0; + width: 4px; + border-radius: var(--radius-full); + background-color: var(--interactive-muted); + margin-right: 12px; +} + +.blockquoteContent { + color: var(--text-primary); + white-space: pre-wrap; +} + +/* Links */ +.link { + color: var(--text-link); + text-decoration: none; + cursor: pointer; +} + +.link:hover { + text-decoration: underline; +} + +/* Spoilers */ +.spoiler { + background-color: var(--background-accent); + border-radius: var(--radius-sm); + padding: 0 2px; + cursor: pointer; + transition: background-color 0.1s ease; +} + +.spoilerHidden { + background-color: var(--background-accent); + color: transparent; +} + +.spoilerHidden:hover { + background-color: var(--background-modifier-accent); +} + +.spoilerRevealed { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +/* Mentions */ +.mention { + padding: 0 2px; + border-radius: var(--radius-sm); + background-color: rgba(88, 101, 242, 0.3); + color: var(--brand-primary); + font-weight: 500; + cursor: pointer; + transition: background-color 0.1s, color 0.1s; +} + +.mention:hover { + background-color: var(--brand-primary); + color: #fff; +} diff --git a/packages/shared/src/components/channel/MessageContent.tsx b/packages/shared/src/components/channel/MessageContent.tsx new file mode 100644 index 0000000..d5e9181 --- /dev/null +++ b/packages/shared/src/components/channel/MessageContent.tsx @@ -0,0 +1,213 @@ +import type { ReactNode } from 'react'; +import { getTwemojiUrl } from '../../utils/twemoji'; +import styles from './MessageContent.module.css'; + +interface MentionMember { + displayName: string; + username: string; + userId: string; +} + +export interface CustomEmojiEntry { + name: string; + url: string; +} + +interface MessageContentProps { + content: string; + members?: MentionMember[]; + customEmojis?: CustomEmojiEntry[]; +} + +const EMOJI_REGEX = + /(?:\p{Emoji_Presentation}|\p{Emoji}\uFE0F)(?:\u200D(?:\p{Emoji_Presentation}|\p{Emoji}\uFE0F))*|\p{Regional_Indicator}{2}/gu; + +const CUSTOM_EMOJI_REGEX = /:([a-z0-9_]+):/gi; + +const URL_REGEX = /https?:\/\/[^\s<>"']+/gi; + +// Escape user-supplied strings for safe inclusion in a regex. +function escapeRegex(s: string): string { + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +interface Token { + type: 'emoji' | 'mention' | 'customEmoji' | 'url'; + index: number; + length: number; + text: string; + // For customEmoji / url: the resolved URL. + url?: string; +} + +// Find the earliest token (emoji or mention) in `text` starting at `from`. +function findNextToken( + text: string, + from: number, + members: MentionMember[], + customEmojiMap: Map<string, string>, +): Token | null { + let best: Token | null = null; + + // URL — earliest match at or after `from`. Trailing punctuation + // is commonly prose, not part of the URL. + URL_REGEX.lastIndex = from; + const urlMatch = URL_REGEX.exec(text); + if (urlMatch) { + let matchText = urlMatch[0]; + const trimmed = matchText.replace(/[),.;!?]+$/, ''); + matchText = trimmed; + best = { + type: 'url', + index: urlMatch.index, + length: matchText.length, + text: matchText, + url: matchText, + }; + } + + // Emoji — next match at or after `from`. + EMOJI_REGEX.lastIndex = from; + const emojiMatch = EMOJI_REGEX.exec(text); + if (emojiMatch && (!best || emojiMatch.index < best.index)) { + best = { + type: 'emoji', + index: emojiMatch.index, + length: emojiMatch[0].length, + text: emojiMatch[0], + }; + } + + // Custom emoji `:shortcode:` — match only when we have a registered + // emoji with that name. Unknown shortcodes fall through as plain text. + if (customEmojiMap.size > 0) { + CUSTOM_EMOJI_REGEX.lastIndex = from; + let m: RegExpExecArray | null; + while ((m = CUSTOM_EMOJI_REGEX.exec(text)) !== null) { + const name = m[1].toLowerCase(); + const url = customEmojiMap.get(name); + if (url) { + if (!best || m.index < best.index) { + best = { + type: 'customEmoji', + index: m.index, + length: m[0].length, + text: name, + url, + }; + } + break; + } + // Unknown shortcode — keep searching past this match. + } + } + + // @everyone + const everyoneIdx = text.indexOf('@everyone', from); + if (everyoneIdx !== -1 && (!best || everyoneIdx < best.index)) { + best = { type: 'mention', index: everyoneIdx, length: '@everyone'.length, text: '@everyone' }; + } + + // @{DisplayName} — prefer longest display name match so "@Alice Smith" + // beats "@Alice". Sort members by descending display-name length. + const sorted = [...members].sort( + (a, b) => (b.displayName?.length ?? 0) - (a.displayName?.length ?? 0), + ); + for (const m of sorted) { + const name = m.displayName || m.username; + if (!name) continue; + const needle = `@${name}`; + const idx = text.indexOf(needle, from); + if (idx !== -1 && (!best || idx < best.index)) { + best = { type: 'mention', index: idx, length: needle.length, text: needle }; + } + } + + // Generic @word fallback — single run of word chars after an @. + const genericRe = /@[\w]+/g; + genericRe.lastIndex = from; + const gm = genericRe.exec(text); + if (gm && (!best || gm.index < best.index)) { + best = { type: 'mention', index: gm.index, length: gm[0].length, text: gm[0] }; + } + + return best; +} + +function renderContent( + text: string, + members: MentionMember[], + customEmojiMap: Map<string, string>, + keyPrefix: string, +): ReactNode[] { + const parts: ReactNode[] = []; + let cursor = 0; + let safety = 0; + while (cursor < text.length && safety++ < 10000) { + const tok = findNextToken(text, cursor, members, customEmojiMap); + if (!tok) { + parts.push(<span key={`${keyPrefix}t${cursor}`}>{text.slice(cursor)}</span>); + break; + } + if (tok.index > cursor) { + parts.push(<span key={`${keyPrefix}t${cursor}`}>{text.slice(cursor, tok.index)}</span>); + } + if (tok.type === 'emoji') { + parts.push( + <img + key={`${keyPrefix}e${tok.index}`} + src={getTwemojiUrl(tok.text)} + alt={tok.text} + className={styles.emoji} + draggable={false} + />, + ); + } else if (tok.type === 'customEmoji') { + parts.push( + <img + key={`${keyPrefix}c${tok.index}`} + src={tok.url} + alt={`:${tok.text}:`} + title={`:${tok.text}:`} + className={`${styles.customEmoji} ${styles.emoji}`} + draggable={false} + />, + ); + } else if (tok.type === 'url') { + parts.push( + <a + key={`${keyPrefix}u${tok.index}`} + className={styles.link} + href={tok.url} + target="_blank" + rel="noopener noreferrer" + > + {tok.text} + </a>, + ); + } else { + parts.push( + <span key={`${keyPrefix}m${tok.index}`} className={styles.mention}> + {tok.text} + </span>, + ); + } + cursor = tok.index + tok.length; + } + if (parts.length === 0) { + parts.push(<span key={`${keyPrefix}t0`}>{text}</span>); + } + // Silence unused escapeRegex in case linter complains. + void escapeRegex; + return parts; +} + +export function MessageContent({ + content, + members = [], + customEmojis = [], +}: MessageContentProps) { + const map = new Map<string, string>(); + for (const e of customEmojis) map.set(e.name.toLowerCase(), e.url); + return <>{renderContent(content, members, map, 'mc')}</>; +} diff --git a/packages/shared/src/components/channel/MessageGroup.module.css b/packages/shared/src/components/channel/MessageGroup.module.css new file mode 100644 index 0000000..6740c76 --- /dev/null +++ b/packages/shared/src/components/channel/MessageGroup.module.css @@ -0,0 +1,374 @@ +/* Message group layout */ + +.group { + padding: 0; + margin-top: 1.0625rem; + position: relative; + user-select: text; + -webkit-user-select: text; + word-break: break-word; +} + +.contentColumn { + min-width: 0; + display: flex; + flex-direction: column; +} + +/* Avatar positioned in the gutter of the first message */ +.avatarSlot { + position: absolute; + left: 16px; + cursor: pointer; +} + +/* --- Reply context (above message) --- */ + +.replyContext { + display: flex; + align-items: center; + gap: 4px; + position: relative; + margin-bottom: 4px; + margin-top: 2px; + font-size: 0.875rem; + line-height: 1.125rem; + cursor: pointer; + min-width: 0; + overflow: visible; +} + +.replyContext:hover .replyText { + color: var(--text-primary); +} + +/* L-shaped spine: vertical arm goes down to above the avatar, horizontal arm goes right to reply content */ +.replySpine { + position: absolute; + left: -36px; + top: 50%; + bottom: 0; + width: 33px; + border-left: 2px solid var(--text-muted); + border-top: 2px solid var(--text-muted); + border-top-left-radius: 6px; + box-sizing: border-box; + pointer-events: none; +} + +.replyAuthor { + font-weight: 600; + font-size: 0.8125rem; + color: var(--text-primary); + white-space: nowrap; + flex-shrink: 0; +} + +.replyText { + color: var(--text-muted); + font-size: 0.8125rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; +} + +.replyContentMissing { + color: var(--text-muted); + font-size: 0.8125rem; + font-style: italic; + padding-left: 4px; +} + +/* --- Header --- */ + +.header { + display: flex; + align-items: baseline; + gap: 8px; + min-height: 1.375rem; + line-height: 1.375rem; +} + +.username { + font-weight: 600; + font-size: 1rem; + color: var(--text-primary); + cursor: pointer; +} + +.username:hover { + text-decoration: underline; +} + +.timestamp { + font-size: 0.75rem; + color: var(--text-muted); + font-weight: 400; +} + +/* --- Individual message content --- */ + +.messageContent { + position: relative; + padding-left: 72px; + padding-right: 48px; +} + +.messageContent:hover { + background-color: var(--background-modifier-hover); +} + +/* Mentioned-row highlight — copies Fluxer's exact golden tint. A 2px + ::before bar sits on the inline-start edge; the row gets a low-alpha + gold background, nudged slightly on hover. Fires when the current + user is in `m.mentions.user_ids` or when `m.mentions.room` is set. */ +.messageMentioned::before { + content: ''; + position: absolute; + inset-block: 0; + inset-inline-start: 0; + width: 2px; + background-color: rgb(234 197 50); + pointer-events: none; +} + +.messageMentioned { + background-color: rgb(234 197 50 / 0.1); +} + +.messageMentioned:hover { + background-color: rgb(234 197 50 / 0.14); +} + +/* First message includes the avatar + header row */ +.firstMessage { + padding-top: 2px; +} + +/* Compact timestamp: sits in the avatar gutter, visible on hover */ +.compactTimestamp { + position: absolute; + left: 0; + width: 72px; + text-align: center; + top: 0; + font-size: 0.6875rem; + line-height: 1.375rem; + color: var(--text-muted); + opacity: 0; + pointer-events: none; +} + +.messageContent:hover .compactTimestamp { + opacity: 1; +} + +/* --- Message text --- */ + +.text { + font-size: 1rem; + line-height: var(--message-line-height, 1.375rem); + color: var(--text-primary); + word-wrap: break-word; + white-space: pre-wrap; +} + +/* "(edited)" tag shown inline next to edited messages. Matches + the fluxer treatment — small, muted, one-space offset. */ +.editedTag { + margin-left: 0.25rem; + font-size: 0.625rem; + color: var(--text-tertiary); + user-select: none; + vertical-align: baseline; +} + +/* --- Encrypted placeholder --- */ + +.encryptedPlaceholder { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 0.875rem; + line-height: var(--message-line-height, 1.375rem); + color: var(--text-muted); + font-style: italic; +} + +/* Tombstone for a redacted message — rendered by MessageGroup + when `message.isDeleted` is true. Mirrors the encrypted + placeholder's visual language (muted italic + leading icon) + so the two states feel like siblings. */ +.deletedPlaceholder { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 0.875rem; + line-height: var(--message-line-height, 1.375rem); + color: var(--text-muted); + font-style: italic; +} + +/* --- Attachments --- */ + +.attachments { + margin-top: 4px; +} + +.imageAttachment { + border-radius: var(--radius-lg); + cursor: pointer; + display: block; + /* Belt-and-braces: the inline `maxWidth` style caps the image + dimensionally (so we don't upscale tiny images), but on narrow + viewports a 400px image would still overflow the parent's + horizontal padding and bleed into the screen edge. `max-width: 100%` + keeps it inside the message's content box regardless of the + absolute pixel cap. `height: auto` preserves aspect ratio when + width is the constraint. */ + max-width: 100%; + height: auto; +} + +/* ── Spoiler attachments ───────────────────────────────────── + Wrapper lets the "SPOILER" cover button overlay the image + via absolute positioning without reflowing the message. + The image itself gets a blur filter while covered so even + partial glimpses don't give away the content. */ + +.spoilerableWrap { + position: relative; + display: inline-block; + max-width: 100%; +} + +.imageAttachmentBlurred { + filter: blur(44px); + transform: scale(1); + /* Prevent blurred edges from bleeding outside the rounded + corners of the wrapper. */ + clip-path: inset(0 round var(--radius-lg)); +} + +.spoilerCover { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba(0, 0, 0, 0.45); + border: none; + border-radius: var(--radius-lg); + cursor: pointer; + transition: background-color 0.12s; + padding: 0; +} + +.spoilerCover:hover { + background-color: rgba(0, 0, 0, 0.6); +} + +.spoilerCoverLabel { + padding: 8px 16px; + background-color: rgba(0, 0, 0, 0.75); + border-radius: 999px; + color: #fff; + font-size: 0.8125rem; + font-weight: 800; + letter-spacing: 0.08em; + pointer-events: none; +} + +.fileAttachment { + display: inline-flex; + align-items: center; + padding: 8px 12px; + background-color: var(--background-secondary); + border-radius: var(--radius-lg); + color: var(--text-link); + font-size: 0.875rem; + text-decoration: none; + margin-top: 4px; +} + +.fileAttachment:hover { + text-decoration: underline; +} + +/* --- Reactions --- */ + +.reactions { + display: flex; + flex-wrap: wrap; + gap: 4px; + margin-top: 4px; +} + +.reactionChip { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 2px 8px; + border-radius: var(--radius-lg); + background-color: var(--background-secondary); + border: 1px solid transparent; + cursor: pointer; + font-size: 0.875rem; + color: var(--text-primary); + transition: background-color 0.15s; +} + +.reactionChip:hover { + background-color: var(--background-modifier-hover); +} + +.reactionMe { + border-color: var(--brand-primary); + background-color: rgba(88, 101, 242, 0.15); +} + +.reactionCount { + font-size: 0.75rem; + color: var(--text-secondary); + font-weight: 500; +} + +/* Custom MSC2545 emoji reaction — rendered as an inline image in the + chip. Matched to the Twemoji size used for unicode reactions so + both styles of chip look the same height. Animated GIF / WebP + emojis play automatically because it's a normal <img>. */ +.reactionCustomEmoji { + width: 16px; + height: 16px; + object-fit: contain; + display: inline-block; + vertical-align: middle; +} + +/* ── Hover gutter timestamp ────────────────────────────────────── + Follow-up messages in a sender group don't have an avatar / name + header — the timestamp lives in the left gutter (where the avatar + would be) and only fades in on hover, matching the new UI. */ +.gutterTimestamp { + position: absolute; + top: 4px; + left: 16px; + width: 56px; + font-size: 11px; + color: var(--text-tertiary); + text-align: right; + padding-right: 8px; + box-sizing: border-box; + opacity: 0; + transition: opacity 0.1s ease; + pointer-events: none; + user-select: none; + font-variant-numeric: tabular-nums; +} + +.messageContent:hover .gutterTimestamp, +.messageContent.actionBarForceVisible .gutterTimestamp, +:global(.messageHoverable):hover .gutterTimestamp { + opacity: 1; +} diff --git a/packages/shared/src/components/channel/MessageGroup.tsx b/packages/shared/src/components/channel/MessageGroup.tsx new file mode 100644 index 0000000..17e8214 --- /dev/null +++ b/packages/shared/src/components/channel/MessageGroup.tsx @@ -0,0 +1,735 @@ +import { useMutation, useQuery } from 'convex/react'; +import { useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { Avatar } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import { useIsMobile } from '../../hooks/useIsMobile'; +import { EmojiPicker, type EmojiPickerValue } from './EmojiPicker'; +import { EncryptedAttachment, type AttachmentMetadata } from './EncryptedAttachment'; +import { ImageLightbox } from './ImageLightbox'; +import { LinkEmbed } from './LinkEmbed'; +import type { DecryptedMessage } from './Messages'; +import { MessageActionBar } from './MessageActionBar'; +import { MessageContent } from './MessageContent'; +import { MobileMessageActionsSheet } from './MobileMessageActionsSheet'; +import { + MemberProfilePopout, + type MemberProfilePopoutMember, +} from '../member/MemberProfilePopout'; +import { PinConfirmationModal } from './PinConfirmationModal'; +import { ReactionsModal } from './ReactionsModal'; +import { Tooltip } from '@discord-clone/ui'; +import { reactionKeyToName } from '../../utils/emojiLookup'; +import type { PinnedMessage } from './PinnedMessageRow'; +import { TwemojiImg } from './TwemojiImg'; +import { resolveReactionKeyToUnicode } from '../../utils/emojiLookup'; +import styles from './MessageGroup.module.css'; + +interface MessageGroupProps { + messages: DecryptedMessage[]; + channelId: string; + onReply?: (eventId: string, username: string) => void; +} + +const URL_REGEX = /https?:\/\/[^\s<>"']+/gi; + +function extractUrls(text: string): string[] { + const matches = text.match(URL_REGEX) ?? []; + // Strip trailing punctuation that's almost never part of the URL but + // commonly butts up against one in prose ("see https://foo.com."). + const cleaned = matches.map((m) => m.replace(/[),.;!?]+$/, '')); + return Array.from(new Set(cleaned)); +} + +function formatTime(ts: number): string { + const date = new Date(ts); + return date.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' }); +} + +function formatFullTime(ts: number): string { + const date = new Date(ts); + return date.toLocaleString([], { + month: 'short', + day: 'numeric', + year: 'numeric', + hour: 'numeric', + minute: '2-digit', + }); +} + +export function MessageGroup({ messages, channelId, onReply }: MessageGroupProps) { + const first = messages[0]; + const myUserId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const channelMembers = useQuery(api.members.getChannelMembers, { + channelId: channelId as any, + }); + const mentionMembers = Array.isArray(channelMembers) + ? channelMembers.map((m: any) => ({ + displayName: m.displayName || m.username || '', + username: m.username || '', + userId: m.id, + })) + : []; + const removeMessage = useMutation(api.messages.remove); + const addReaction = useMutation(api.reactions.add); + const removeReaction = useMutation(api.reactions.remove); + + // Custom emoji catalog — powers `:shortcode:` rendering inside + // message bodies and custom-emoji reaction chips. One query per + // group keeps the subscription count reasonable. + const customEmojiDocs = (useQuery(api.customEmojis.list, {}) ?? []) as Array<{ + _id: string; + name: string; + src: string; + }>; + const customEmojiList = customEmojiDocs.map((e) => ({ name: e.name, url: e.src })); + const customEmojiByName = new Map<string, string>(); + for (const e of customEmojiDocs) customEmojiByName.set(e.name.toLowerCase(), e.src); + + // Reaction picker state — a single picker shared across the group. + // Records which message was clicked so the chosen emoji is bound + // to the right target. + const [reactPicker, setReactPicker] = useState<{ + messageId: string; + pos: { top: number; left: number }; + } | null>(null); + + // Image lightbox state — tracks the decrypted blob URL + the + // full attachment metadata of the image that was clicked so the + // lightbox info card can render filename / size / dimensions. + // Null means the lightbox is closed. + const [lightboxItem, setLightboxItem] = useState<{ + src: string; + attachment: AttachmentMetadata; + } | null>(null); + + // Right-click context menu state. When set, the MessageActionBar for + // the target message opens its More dropdown at the click coordinates + // and the parent row gets `actionBarForceVisible` so the hover bar + // stays pinned regardless of cursor position. + const [contextMenu, setContextMenu] = useState<{ + messageId: string; + x: number; + y: number; + } | null>(null); + // Tracks which message currently has its local More dropdown open + // so we can pin that row's hover bar visible even when the pointer + // leaves the row. Flips back to null when the dropdown closes. + const [localMenuOpenFor, setLocalMenuOpenFor] = useState<string | null>( + null, + ); + const [mobileSheetForMsg, setMobileSheetForMsg] = + useState<DecryptedMessage | null>(null); + + // Author profile popout — opens when the user clicks the avatar + // or username in the message header, same component MemberListContainer + // uses so profile cards look identical regardless of entry point. + const [authorPopout, setAuthorPopout] = useState<{ + anchorRect: DOMRect; + member: MemberProfilePopoutMember; + } | null>(null); + + // Full reactions breakdown modal — opens when the user clicks a + // reaction chip's tooltip or the chip itself. Tracks the target + // message id so the modal can read its latest reaction rows. + const [reactionsModalMsgId, setReactionsModalMsgId] = useState< + string | null + >(null); + + const longPressTimerRef = useRef<number | null>(null); + const isMobile = useIsMobile(); + + const openAuthorPopout = (msg: DecryptedMessage, rect: DOMRect) => { + setAuthorPopout({ + anchorRect: rect, + member: { + userId: msg.senderId, + displayName: msg.authorName, + avatarUrl: msg.authorAvatarUrl, + }, + }); + }; + + const startLongPress = (msg: DecryptedMessage) => { + if (!isMobile) return; + if (longPressTimerRef.current !== null) { + window.clearTimeout(longPressTimerRef.current); + } + longPressTimerRef.current = window.setTimeout(() => { + setMobileSheetForMsg(msg); + }, 450); + }; + const cancelLongPress = () => { + if (longPressTimerRef.current !== null) { + window.clearTimeout(longPressTimerRef.current); + longPressTimerRef.current = null; + } + }; + + const handleCopyText = (content: string) => { + if (typeof navigator !== 'undefined' && navigator.clipboard) { + void navigator.clipboard.writeText(content); + } + }; + + const handleCopyLink = (messageId: string) => { + if (typeof navigator !== 'undefined' && navigator.clipboard) { + const url = `${window.location.origin}${window.location.pathname}#msg-${messageId}`; + void navigator.clipboard.writeText(url); + } + }; + + // Pin / unpin both flow through the PinConfirmationModal so the + // user gets the same confirmation step the new UI uses. The + // variant flips based on whether the target message is already + // pinned. + const [pinTarget, setPinTarget] = useState<{ + message: PinnedMessage; + variant: 'pin' | 'unpin'; + } | null>(null); + const handlePin = (messageId: string) => { + const msg = messages.find((m) => m.id === messageId); + if (!msg) return; + setPinTarget({ + message: { + id: msg.id, + authorName: msg.authorName, + authorAvatarUrl: msg.authorAvatarUrl, + content: msg.content, + timestamp: msg.timestamp, + attachments: msg.attachments, + }, + variant: msg.pinned ? 'unpin' : 'pin', + }); + }; + + const openReactPicker = (messageId: string, anchorEl: Element | null) => { + const rect = anchorEl?.getBoundingClientRect(); + if (!rect) return; + setReactPicker({ + messageId, + pos: { + top: rect.bottom + 8, + left: Math.max(8, rect.right - 480), + }, + }); + }; + + const handlePickReaction = async (value: EmojiPickerValue) => { + if (!reactPicker || !myUserId) return; + try { + // For unicode we store the raw surrogates as the reaction key. + // For custom server emojis we store the shortcode — the render + // path below rehydrates it via `customEmojiByName` and falls + // back through `resolveReactionKeyToUnicode` for legacy / + // unknown keys. + const emojiKey = + value.kind === 'custom' ? value.shortcode : value.surrogates; + await addReaction({ + messageId: reactPicker.messageId as any, + userId: myUserId as any, + emoji: emojiKey, + }); + } catch (err) { + console.error('Failed to add reaction:', err); + } + setReactPicker(null); + }; + + const handleDelete = async (messageId: string) => { + if (!myUserId) return; + try { + await removeMessage({ id: messageId as any, userId: myUserId as any }); + } catch (err) { + console.error('Failed to delete message:', err); + } + }; + + const handleToggleReaction = async (messageId: string, emoji: string, me: boolean) => { + if (!myUserId) return; + try { + if (me) { + await removeReaction({ + messageId: messageId as any, + userId: myUserId as any, + emoji, + }); + } else { + await addReaction({ + messageId: messageId as any, + userId: myUserId as any, + emoji, + }); + } + } catch (err) { + console.error('Failed to toggle reaction:', err); + } + }; + + return ( + <div className={styles.group}> + <div className={styles.contentColumn}> + {messages.map((msg, i) => { + const isFirst = i === 0; + const isOwn = msg.senderId === myUserId; + const menuOpenForThis = contextMenu?.messageId === msg.id; + return ( + <div + key={msg.id} + data-message-id={msg.id} + className={`${styles.messageContent} ${isFirst ? styles.firstMessage : ''} messageHoverable ${menuOpenForThis || localMenuOpenFor === msg.id ? 'actionBarForceVisible' : ''}`} + onContextMenu={(e) => { + if (isMobile) return; + e.preventDefault(); + setContextMenu({ messageId: msg.id, x: e.clientX, y: e.clientY }); + }} + onTouchStart={() => startLongPress(msg)} + onTouchEnd={cancelLongPress} + onTouchMove={cancelLongPress} + onTouchCancel={cancelLongPress} + > + <MessageActionBar + isOwnMessage={isOwn} + onMenuOpenChange={(open) => + setLocalMenuOpenFor(open ? msg.id : null) + } + onReply={() => onReply?.(msg.id, first.authorName)} + onDelete={() => handleDelete(msg.id)} + onReact={(e) => openReactPicker(msg.id, e?.currentTarget ?? null)} + onQuickReact={async (emoji) => { + if (!myUserId) return; + try { + await addReaction({ + messageId: msg.id as any, + userId: myUserId as any, + emoji, + }); + } catch (err) { + console.error('Failed to add quick reaction:', err); + } + }} + onPin={() => handlePin(msg.id)} + onCopyText={() => handleCopyText(msg.content)} + onCopyLink={() => handleCopyLink(msg.id)} + externalMenuAt={ + menuOpenForThis ? { x: contextMenu.x, y: contextMenu.y } : null + } + onExternalMenuClose={() => setContextMenu(null)} + /> + {isFirst && msg.replyToId && ( + <div + className={styles.replyContext} + role="button" + tabIndex={0} + onClick={() => { + if (!msg.replyToId) return; + window.dispatchEvent( + new CustomEvent('brycord:scroll-to-message', { + detail: { + channelId, + messageId: msg.replyToId, + }, + }), + ); + }} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + if (!msg.replyToId) return; + window.dispatchEvent( + new CustomEvent('brycord:scroll-to-message', { + detail: { + channelId, + messageId: msg.replyToId, + }, + }), + ); + } + }} + style={{ cursor: 'pointer' }} + > + <div className={styles.replySpine} /> + <span className={styles.replyAuthor}> + {msg.replyToAuthorName ?? 'Unknown'} + </span> + <span className={styles.replyText}> + {msg.replyToContent ?? <em>(missing context)</em>} + </span> + </div> + )} + {isFirst && ( + <> + <div + className={styles.avatarSlot} + role="button" + tabIndex={0} + onClick={(e) => { + e.stopPropagation(); + openAuthorPopout( + first, + (e.currentTarget as HTMLElement).getBoundingClientRect(), + ); + }} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + openAuthorPopout( + first, + (e.currentTarget as HTMLElement).getBoundingClientRect(), + ); + } + }} + style={{ cursor: 'pointer' }} + > + <Avatar + src={first.authorAvatarUrl} + size={40} + fallback={first.authorName} + /> + </div> + <div className={styles.header}> + <span + className={styles.username} + role="button" + tabIndex={0} + onClick={(e) => { + e.stopPropagation(); + openAuthorPopout( + first, + (e.currentTarget as HTMLElement).getBoundingClientRect(), + ); + }} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + openAuthorPopout( + first, + (e.currentTarget as HTMLElement).getBoundingClientRect(), + ); + } + }} + style={{ + cursor: 'pointer', + color: first.authorRoleColor ?? undefined, + }} + > + {first.authorName} + </span> + <span + className={styles.timestamp} + title={formatFullTime(first.timestamp)} + > + {formatTime(first.timestamp)} + </span> + </div> + </> + )} + {!isFirst && ( + <span + className={styles.gutterTimestamp} + title={formatFullTime(msg.timestamp)} + > + {new Date(msg.timestamp).toLocaleTimeString([], { + hour: 'numeric', + minute: '2-digit', + hour12: true, + })} + </span> + )} + {msg.content && ( + <div className={styles.text}> + <MessageContent + content={msg.content} + members={mentionMembers} + customEmojis={customEmojiList} + /> + {msg.editedTimestamp && ( + <span className={styles.editedTag}> (edited)</span> + )} + </div> + )} + {msg.content && + extractUrls(msg.content) + .slice(0, 3) + .map((url, idx) => ( + <LinkEmbed key={`embed-${idx}-${url}`} url={url} /> + ))} + {msg.attachments.length > 0 && ( + <div className={styles.attachments}> + {msg.attachments.map((att, j) => { + const kind = att.mimeType.split('/')[0]; + const cls = + kind === 'image' || kind === 'video' + ? styles.imageAttachment + : styles.fileAttachment; + return ( + <EncryptedAttachment + key={`${msg.id}-${j}`} + metadata={att} + className={cls} + onImageClick={(url, attachment) => { + setLightboxItem({ src: url, attachment }); + }} + /> + ); + })} + </div> + )} + {msg.reactions.length > 0 && ( + <div className={styles.reactions}> + {msg.reactions.map((r) => { + // Custom emoji wins over unicode lookup. The reaction + // key is a plain shortcode, so check the custom map + // first; if no match, fall through to the existing + // shortcode→unicode path. + const customUrl = /^[a-zA-Z0-9_]+$/.test(r.emoji) + ? customEmojiByName.get(r.emoji.toLowerCase()) + : undefined; + // Tooltip body: emoji glyph + ":name: reacted by + // A, B, C, and N others" + hint line. Names come + // straight from the enriched payload so we don't + // refetch on hover. Marked interactive so the user + // can move onto the panel and click it to open the + // full reactions modal. + const emojiLabel = `:${reactionKeyToName(r.emoji)}:`; + const displayNames = r.users + .slice(0, 3) + .map((u) => u.displayName || u.username); + const extras = Math.max(0, r.count - displayNames.length); + const namesText = + displayNames.length === 0 + ? '' + : extras === 0 + ? displayNames.length === 1 + ? displayNames[0] + : displayNames.length === 2 + ? `${displayNames[0]} and ${displayNames[1]}` + : `${displayNames.slice(0, -1).join(', ')}, and ${displayNames[displayNames.length - 1]}` + : `${displayNames.join(', ')}, and ${extras} ${extras === 1 ? 'other' : 'others'}`; + const tooltipContent = ( + <div + style={{ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + gap: 6, + textAlign: 'center', + maxWidth: 240, + }} + > + <div + style={{ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }} + > + {customUrl ? ( + <img + src={customUrl} + alt={emojiLabel} + draggable={false} + style={{ + width: 32, + height: 32, + objectFit: 'contain', + }} + /> + ) : ( + <TwemojiImg + emoji={resolveReactionKeyToUnicode(r.emoji)} + size={32} + /> + )} + </div> + <span style={{ fontWeight: 600 }}> + {emojiLabel} reacted by + </span> + <span style={{ fontWeight: 500 }}>{namesText}</span> + <span + style={{ + fontSize: '0.6875rem', + color: 'var(--text-tertiary)', + marginTop: 2, + }} + > + Click to view all reactions + </span> + </div> + ); + return ( + <Tooltip + key={r.emoji} + content={tooltipContent} + placement="top" + delay={200} + interactive + onContentClick={() => setReactionsModalMsgId(msg.id)} + > + <button + type="button" + className={`${styles.reactionChip} ${r.me ? styles.reactionMe : ''}`} + onClick={() => setReactionsModalMsgId(msg.id)} + > + {customUrl ? ( + <img + src={customUrl} + alt={`:${r.emoji}:`} + title={`:${r.emoji}:`} + draggable={false} + style={{ + width: 16, + height: 16, + objectFit: 'contain', + verticalAlign: 'middle', + }} + /> + ) : ( + <TwemojiImg + emoji={resolveReactionKeyToUnicode(r.emoji)} + size={16} + /> + )} + <span className={styles.reactionCount}>{r.count}</span> + </button> + </Tooltip> + ); + })} + </div> + )} + </div> + ); + })} + </div> + {reactPicker && + createPortal( + <div + style={{ + position: 'fixed', + top: reactPicker.pos.top, + left: reactPicker.pos.left, + zIndex: 15000, + }} + onClick={(e) => e.stopPropagation()} + > + <EmojiPicker + onSelect={handlePickReaction} + onClose={() => setReactPicker(null)} + /> + </div>, + document.body, + )} + <ImageLightbox + isOpen={!!lightboxItem} + src={lightboxItem?.src ?? ''} + filename={lightboxItem?.attachment.filename} + mimeType={lightboxItem?.attachment.mimeType} + size={lightboxItem?.attachment.size} + width={lightboxItem?.attachment.width} + height={lightboxItem?.attachment.height} + attachment={lightboxItem?.attachment} + onClose={() => setLightboxItem(null)} + /> + + <PinConfirmationModal + isOpen={!!pinTarget} + onClose={() => setPinTarget(null)} + channelId={channelId} + messageId={pinTarget?.message.id ?? null} + message={pinTarget?.message ?? null} + variant={pinTarget?.variant ?? 'pin'} + /> + + {authorPopout && ( + <MemberProfilePopout + anchorRect={authorPopout.anchorRect} + member={authorPopout.member} + onClose={() => setAuthorPopout(null)} + /> + )} + + {(() => { + // Look up the target message by id from the current group + // so the modal always renders against the latest reaction + // rows (the group array is re-created on every parent + // re-render by Messages.tsx's memoized decrypt step). + const targetMsg = reactionsModalMsgId + ? messages.find((m) => m.id === reactionsModalMsgId) ?? null + : null; + return ( + <ReactionsModal + isOpen={!!targetMsg} + onClose={() => setReactionsModalMsgId(null)} + reactions={targetMsg?.reactions ?? []} + myUserId={myUserId} + customEmojiByName={customEmojiByName} + onRemoveOwnReaction={async (emoji) => { + if (!targetMsg || !myUserId) return; + try { + await removeReaction({ + messageId: targetMsg.id as any, + userId: myUserId as any, + emoji, + }); + } catch (err) { + console.error('Failed to remove reaction:', err); + } + }} + /> + ); + })()} + + {mobileSheetForMsg && ( + <MobileMessageActionsSheet + isOpen={!!mobileSheetForMsg} + onClose={() => setMobileSheetForMsg(null)} + isOwnMessage={mobileSheetForMsg.senderId === myUserId} + canDelete={mobileSheetForMsg.senderId === myUserId} + hasContent={!!mobileSheetForMsg.content} + onQuickReact={async (emoji) => { + if (!myUserId) return; + try { + await addReaction({ + messageId: mobileSheetForMsg.id as any, + userId: myUserId as any, + emoji, + }); + } catch (err) { + console.error('Failed to add quick reaction:', err); + } + }} + onAddReaction={() => { + // No anchor on mobile — just open a centered picker + // by passing `null` as the anchor. + setReactPicker({ + messageId: mobileSheetForMsg.id, + anchor: null as any, + }); + }} + onReply={() => { + onReply?.(mobileSheetForMsg.id, first.authorName); + }} + onForward={() => { + /* forward not implemented yet */ + }} + onEdit={ + mobileSheetForMsg.senderId === myUserId + ? () => { + /* edit not implemented via sheet yet */ + } + : undefined + } + onPin={() => handlePin(mobileSheetForMsg.id)} + isPinned={mobileSheetForMsg.pinned} + onCopyText={() => handleCopyText(mobileSheetForMsg.content)} + onDelete={ + mobileSheetForMsg.senderId === myUserId + ? () => handleDelete(mobileSheetForMsg.id) + : undefined + } + /> + )} + </div> + ); +} diff --git a/packages/shared/src/components/channel/MessageInspectModal.module.css b/packages/shared/src/components/channel/MessageInspectModal.module.css new file mode 100644 index 0000000..9f23a6d --- /dev/null +++ b/packages/shared/src/components/channel/MessageInspectModal.module.css @@ -0,0 +1,101 @@ +.content { + display: flex; + flex-direction: column; + padding: 0; + max-height: 70vh; + overflow-y: auto; +} + +.sections { + display: flex; + flex-direction: column; + gap: 12px; + padding: 16px 20px 20px; +} + +.section { + background-color: var(--background-secondary, rgba(255, 255, 255, 0.03)); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); + border-radius: 6px; + overflow: hidden; +} + +.sectionAccent { + border-color: rgba(234, 197, 50, 0.5); + background-color: rgba(234, 197, 50, 0.06); +} + +.sectionHeader { + display: flex; + align-items: center; + justify-content: space-between; + padding: 8px 12px; + background-color: rgba(0, 0, 0, 0.15); + border-bottom: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); +} + +.sectionLabel { + font-size: 11px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--text-primary-muted, #a0a3a8); +} + +.copyButton { + display: inline-flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + padding: 0; + background-color: transparent; + border: none; + border-radius: 4px; + color: var(--text-primary-muted, #a0a3a8); + cursor: pointer; + transition: background-color 0.1s, color 0.1s; +} + +.copyButton:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.06)); + color: var(--text-primary, #fff); +} + +.sectionValue { + padding: 10px 12px; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 12px; + line-height: 1.5; + color: var(--text-primary, #fff); + word-break: break-all; +} + +.jsonBlock { + margin: 0; + padding: 10px 12px; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 11.5px; + line-height: 1.5; + color: var(--text-primary, #fff); + white-space: pre-wrap; + word-break: break-word; + max-height: 280px; + overflow-y: auto; +} + +.emptyState { + padding: 32px 24px; + text-align: center; + color: var(--text-primary-muted, #a0a3a8); + font-size: 14px; +} + +.warning { + padding: 8px 12px; + background-color: rgba(234, 80, 80, 0.15); + border: 1px solid rgba(234, 80, 80, 0.4); + border-radius: 6px; + color: var(--text-primary, #fff); + font-size: 13px; +} diff --git a/packages/shared/src/components/channel/MessageInspectModal.tsx b/packages/shared/src/components/channel/MessageInspectModal.tsx new file mode 100644 index 0000000..16c52b6 --- /dev/null +++ b/packages/shared/src/components/channel/MessageInspectModal.tsx @@ -0,0 +1,185 @@ +/** + * MessageInspectModal — debug/dev view that dumps the raw Convex message + * document behind a rendered message. Callers pass the already-decrypted + * message object (the same shape that flows through Messages / + * MessageGroup props), and this modal pretty-prints the document plus + * a handful of broken-out fields that are commonly useful when + * debugging rendering issues. + */ +import { useMemo, useState } from 'react'; +import { Copy, CheckCircle } from '@phosphor-icons/react'; +import { Modal, Button } from '@discord-clone/ui'; +import styles from './MessageInspectModal.module.css'; + +interface MessageInspectModalProps { + isOpen: boolean; + onClose: () => void; + message: Record<string, unknown> | null; +} + +function safeStringify(value: unknown): string { + try { + return JSON.stringify(value, null, 2); + } catch { + return String(value); + } +} + +export function MessageInspectModal({ isOpen, onClose, message }: MessageInspectModalProps) { + const [copied, setCopied] = useState<string | null>(null); + + const summary = useMemo(() => { + if (!message) return null; + const m = message as any; + return { + id: String(m._id ?? ''), + channelId: String(m.channelId ?? ''), + senderId: String(m.senderId ?? ''), + timestamp: typeof m._creationTime === 'number' ? m._creationTime : Number(m.createdAt ?? 0), + content: typeof m.decryptedContent === 'string' ? m.decryptedContent : null, + attachments: m.attachments ?? null, + raw: m, + }; + }, [message]); + + const handleCopy = (label: string, text: string) => { + navigator.clipboard.writeText(text).then( + () => { + setCopied(label); + setTimeout(() => setCopied((c) => (c === label ? null : c)), 1500); + }, + () => {}, + ); + }; + + if (!isOpen) return null; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="large"> + <Modal.Header title="Inspect Message" onClose={onClose} /> + <Modal.Content className={styles.content}> + {!summary ? ( + <div className={styles.emptyState}>No message selected.</div> + ) : ( + <div className={styles.sections}> + <Section + label="Message ID" + value={summary.id} + onCopy={handleCopy} + copied={copied} + /> + <Section + label="Channel ID" + value={summary.channelId} + onCopy={handleCopy} + copied={copied} + /> + <Section + label="Sender ID" + value={summary.senderId} + onCopy={handleCopy} + copied={copied} + /> + <Section + label="Timestamp" + value={`${new Date(summary.timestamp).toISOString()} (${summary.timestamp})`} + onCopy={handleCopy} + copied={copied} + /> + + {summary.content !== null && ( + <JsonBlock + label="Decrypted Content" + value={summary.content} + onCopy={handleCopy} + copied={copied} + accent + /> + )} + + {summary.attachments && ( + <JsonBlock + label="Attachments" + value={safeStringify(summary.attachments)} + onCopy={handleCopy} + copied={copied} + /> + )} + + <JsonBlock + label="Raw Document" + value={safeStringify(summary.raw)} + onCopy={handleCopy} + copied={copied} + /> + </div> + )} + </Modal.Content> + <Modal.Footer> + <Button variant="secondary" onClick={onClose}> + Close + </Button> + </Modal.Footer> + </Modal.Root> + ); +} + +function Section({ + label, + value, + onCopy, + copied, +}: { + label: string; + value: string; + onCopy: (label: string, text: string) => void; + copied: string | null; +}) { + return ( + <div className={styles.section}> + <div className={styles.sectionHeader}> + <span className={styles.sectionLabel}>{label}</span> + <button + type="button" + className={styles.copyButton} + onClick={() => onCopy(label, value)} + aria-label={`Copy ${label}`} + > + {copied === label ? <CheckCircle size={14} weight="fill" /> : <Copy size={14} weight="fill" />} + </button> + </div> + <div className={styles.sectionValue}>{value}</div> + </div> + ); +} + +function JsonBlock({ + label, + value, + onCopy, + copied, + accent, +}: { + label: string; + value: string; + onCopy: (label: string, text: string) => void; + copied: string | null; + accent?: boolean; +}) { + return ( + <div className={`${styles.section} ${accent ? styles.sectionAccent : ''}`}> + <div className={styles.sectionHeader}> + <span className={styles.sectionLabel}>{label}</span> + <button + type="button" + className={styles.copyButton} + onClick={() => onCopy(label, value)} + aria-label={`Copy ${label}`} + > + {copied === label ? <CheckCircle size={14} weight="fill" /> : <Copy size={14} weight="fill" />} + </button> + </div> + <pre className={styles.jsonBlock}>{value}</pre> + </div> + ); +} diff --git a/packages/shared/src/components/channel/Messages.module.css b/packages/shared/src/components/channel/Messages.module.css new file mode 100644 index 0000000..61917e6 --- /dev/null +++ b/packages/shared/src/components/channel/Messages.module.css @@ -0,0 +1,120 @@ +.container { + height: 100%; + overflow-y: auto; + overflow-x: hidden; + overflow-anchor: none; + + /* ── Fluxer chat scrollbar ──────────────────────────────────── + Exact copy of Fluxer's `Scroller.module.css` rules. The track + gets pinned to `--background-secondary-lighter` (same override + Fluxer's Messages.module.css uses) so the thumb has a matching + surface to ride on. Firefox fallback uses `scrollbar-color`. + + The 4px transparent border + `background-clip: padding-box` + trick is what gives the thumb its visible inset — the element + is 16px wide total but only 8px of thumb renders inside the + border, floating in the middle of the gutter. */ + --scrollbar-track-bg: var(--background-secondary-lighter); + scrollbar-color: var(--scrollbar-thumb-bg) var(--scrollbar-track-bg); +} + +.container::-webkit-scrollbar { + width: 16px; + height: 16px; +} + +.container::-webkit-scrollbar-corner { + background-color: transparent; +} + +.container::-webkit-scrollbar-thumb { + background-color: var(--scrollbar-thumb-bg); + min-height: 40px; +} + +.container::-webkit-scrollbar-thumb:hover { + background-color: var(--scrollbar-thumb-bg-hover); +} + +.container::-webkit-scrollbar-thumb, +.container::-webkit-scrollbar-track { + border: 4px solid transparent; + background-clip: padding-box; + border-radius: 8px; +} + +.container::-webkit-scrollbar-track { + background-color: var(--scrollbar-track-bg); +} + +.scroller { + display: flex; + flex-direction: column; + justify-content: flex-end; + min-height: 100%; + padding-bottom: 8px; + overflow-anchor: none; +} + +/* ── Day divider ───────────────────────────────────────────────── + Horizontal rule with the date centered on it, mirroring fluxer's + per-day separator between message groups. The rule uses flex + pseudo-elements so the line fills every remaining pixel on both + sides of the label regardless of how wide the date string is. */ +.dayDivider { + display: flex; + align-items: center; + gap: 0.75rem; + margin: 0.75rem 1rem 0.5rem; + color: var(--text-tertiary); + font-size: 0.75rem; + font-weight: 600; + user-select: none; +} + +.dayDivider::before, +.dayDivider::after { + content: ''; + flex: 1; + height: 1px; + background-color: var(--background-modifier-accent); +} + +.pollWrapper { + padding: 0.25rem 1rem 0.25rem 4.5rem; +} + +/* ── Day divider ────────────────────────────────────────────────── + Renders between message groups whenever the calendar day changes. + Centred date label with thin horizontal lines on both sides — the + `flex: 1` pseudo-elements grow to fill whatever the label doesn't. */ +.dayDivider { + display: flex; + align-items: center; + gap: 0.75rem; + margin: 0.75rem 1rem 0.5rem; + color: var(--text-tertiary); + font-size: 0.75rem; + font-weight: 600; + user-select: none; +} + +.dayDivider::before, +.dayDivider::after { + content: ''; + flex: 1; + height: 1px; + background-color: var(--background-modifier-accent); +} + +/* ── Jump highlight ─────────────────────────────────────────────── + Pulsed background applied to a message row by the + `brycord:scroll-to-message` listener. Class is added globally + (via `.messageJumpHighlight`) so it can target the + `[data-message-id]` node from MessageGroup, which sits inside + `.scroller` but uses its own module class names. */ +:global(.messageJumpHighlight) { + background-color: rgba(88, 101, 242, 0.16); + box-shadow: inset 2px 0 0 var(--brand-primary, #5865f2); + transition: background-color 0.25s ease, box-shadow 0.25s ease; +} diff --git a/packages/shared/src/components/channel/Messages.tsx b/packages/shared/src/components/channel/Messages.tsx new file mode 100644 index 0000000..e1fe791 --- /dev/null +++ b/packages/shared/src/components/channel/Messages.tsx @@ -0,0 +1,748 @@ +import { usePaginatedQuery, useQuery } from 'convex/react'; +import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; +import { api } from '../../../../../convex/_generated/api'; +import { usePlatform } from '../../platform'; +import { MessageGroup } from './MessageGroup'; +import { PollCard } from './PollCard'; +import { ChannelWelcomeSection } from './ChannelWelcomeSection'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import styles from './Messages.module.css'; + +interface MessagesProps { + channelId: string; + onReply?: (eventId: string, username: string) => void; +} + +import type { AttachmentMetadata } from './EncryptedAttachment'; + +export interface ReactionUser { + userId: string; + username: string; + displayName: string | null; +} + +export interface DecryptedMessage { + id: string; + channelId: string; + senderId: string; + authorName: string; + authorAvatarUrl: string | null; + authorRoleColor: string | null; + content: string; + timestamp: number; + editedTimestamp: number | null; + replyToId: string | null; + replyToAuthorName: string | null; + replyToContent: string | null; + attachments: AttachmentMetadata[]; + reactions: Array<{ + emoji: string; + count: number; + me: boolean; + users: ReactionUser[]; + }>; + pinned: boolean; +} + +// Ciphertext format on disk is `content + tag` as hex, where the tag is +// the last 32 hex chars (16 bytes of GCM auth tag). Must be split before +// calling crypto.decryptData, which expects them as separate args. +const TAG_LENGTH = 32; + +// Small LRU-ish cache for decrypted messages so re-renders don't redecrypt. +const decryptionCache = new Map<string, string>(); +const MAX_CACHE = 2000; + +function cacheSet(id: string, content: string) { + if (decryptionCache.size >= MAX_CACHE) { + const firstKey = decryptionCache.keys().next().value; + if (firstKey !== undefined) decryptionCache.delete(firstKey); + } + decryptionCache.set(id, content); +} + +// ── Day divider helpers ───────────────────────────────────────────── +// +// Inserts a "Tuesday, April 7, 2026"-style separator between message +// groups whenever the calendar day changes. Compares year/month/day +// rather than the timestamp delta — two messages 23 hours apart can +// still cross midnight. + +function isSameDay(a: number, b: number): boolean { + const da = new Date(a); + const db = new Date(b); + return ( + da.getFullYear() === db.getFullYear() && + da.getMonth() === db.getMonth() && + da.getDate() === db.getDate() + ); +} + +const DAY_DIVIDER_FORMATTER = new Intl.DateTimeFormat(undefined, { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', +}); + +function formatDayDivider(ts: number): string { + return DAY_DIVIDER_FORMATTER.format(new Date(ts)); +} + +function DayDivider({ timestamp }: { timestamp: number }) { + return ( + <div className={styles.dayDivider} role="separator"> + {formatDayDivider(timestamp)} + </div> + ); +} + +export function Messages({ channelId, onReply }: MessagesProps) { + const { crypto } = usePlatform(); + const scrollerRef = useRef<HTMLDivElement>(null); + + // Pending jump target — set by the `brycord:scroll-to-message` + // listener when the message isn't rendered yet. A second effect + // further down reacts to it: if the target is now in the DOM, + // scroll to it; otherwise call `loadMore` to fetch another page + // of history and try again on the next render. + const pendingJumpRef = useRef<string | null>(null); + + // channelKeysByVersion holds every key we have for this channel, + // keyed by the server-stored keyVersion. A DM that's been rotated + // will have multiple entries — old messages decrypt with the + // version they were written under, new messages use the latest. + const [channelKeysByVersion, setChannelKeysByVersion] = useState< + Map<number, string> + >(new Map()); + + const userId = typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const privateKeyPem = typeof sessionStorage !== 'undefined' ? sessionStorage.getItem('privateKey') : null; + + const allKeys = useQuery( + api.channelKeys.getKeysForUser, + userId ? { userId: userId as any } : 'skip', + ); + + // Walk every bundle we have, decrypt the ones tagged for this + // channel, and build a {version → keyHex} map. A single bundle's + // plaintext is a JSON object mapping channelId → keyHex (legacy + // bundles can carry multiple channel ids), so we still merge by + // channelId, then bucket by the row's `key_version`. + useEffect(() => { + let cancelled = false; + if (!allKeys || !privateKeyPem) { + setChannelKeysByVersion(new Map()); + return; + } + (async () => { + const next = new Map<number, string>(); + for (const item of allKeys) { + try { + const bundleJson = await crypto.privateDecrypt( + privateKeyPem, + item.encrypted_key_bundle, + ); + const parsed = JSON.parse(bundleJson) as Record<string, string>; + const keyForThisChannel = parsed[channelId]; + if (keyForThisChannel) { + next.set(item.key_version ?? 1, keyForThisChannel); + } + } catch (err) { + console.error( + `Failed to decrypt key bundle for ${item.channel_id}`, + err, + ); + } + } + if (cancelled) return; + setChannelKeysByVersion(next); + })(); + return () => { + cancelled = true; + }; + }, [allKeys, privateKeyPem, channelId]); + + // Fallback "default" key — use the highest version we have. Needed + // for legacy messages that were written before `keyVersion` was + // tracked on messages (they come back without the field). + const channelKey = useMemo(() => { + if (channelKeysByVersion.size === 0) return null; + let maxVersion = -Infinity; + let latest: string | null = null; + for (const [ver, key] of channelKeysByVersion) { + if (ver > maxVersion) { + maxVersion = ver; + latest = key; + } + } + return latest; + }, [channelKeysByVersion]); + + const keyBundle = allKeys?.find((k) => k.channel_id === channelId) ?? null; + + const { + results: pagedMessages, + status, + loadMore, + } = usePaginatedQuery( + api.messages.list, + channelId ? { channelId: channelId as any, userId: (userId as any) ?? undefined } : 'skip', + { initialNumItems: 50 }, + ); + + const [decryptedMap, setDecryptedMap] = useState<Map<string, string>>(new Map()); + + // Reply previews — separate map keyed by the *child* message id so + // the per-row render can grab the parent's plaintext without + // re-decrypting on every paint. Filled by the effect below. + const [replyPreviewMap, setReplyPreviewMap] = useState<Map<string, string>>( + new Map(), + ); + + // Helper: try to scroll to whatever's in `pendingJumpRef`. If the + // target is in the DOM we scroll + flash, clear the pending ref, + // and we're done. Otherwise we kick off another paginate-up so + // older history loads, then re-poll on the next render via the + // effect below. + const tryFulfillJump = useCallback(() => { + const target = pendingJumpRef.current; + if (!target) return; + const scroller = scrollerRef.current; + if (!scroller) return; + const el = scroller.querySelector<HTMLElement>( + `[data-message-id="${CSS.escape(target)}"]`, + ); + if (el) { + pendingJumpRef.current = null; + el.scrollIntoView({ behavior: 'smooth', block: 'center' }); + el.classList.add('messageJumpHighlight'); + window.setTimeout(() => { + el.classList.remove('messageJumpHighlight'); + }, 1600); + return; + } + // Not in the DOM — load more older pages until either the + // message appears or the listing is exhausted. + if (status === 'CanLoadMore') { + loadMore(50); + } else if (status === 'Exhausted') { + pendingJumpRef.current = null; + } + }, [status, loadMore]); + + // Listen for `brycord:scroll-to-message` window events fired by + // reply previews, pin entries, and search results. + useEffect(() => { + const onScrollTo = (e: Event) => { + const detail = (e as CustomEvent<{ + channelId?: string; + messageId?: string; + }>).detail; + if (!detail?.messageId) return; + if (detail.channelId && detail.channelId !== channelId) return; + pendingJumpRef.current = detail.messageId; + tryFulfillJump(); + }; + window.addEventListener('brycord:scroll-to-message', onScrollTo); + return () => + window.removeEventListener('brycord:scroll-to-message', onScrollTo); + }, [channelId, tryFulfillJump]); + + // Re-attempt the jump whenever a new page of messages lands. The + // loader call above triggers a re-render with more rows, this + // effect runs, and we either find the target or queue the next + // paginate-up. + useEffect(() => { + if (pendingJumpRef.current) tryFulfillJump(); + }, [pagedMessages, tryFulfillJump]); + + useEffect(() => { + if (channelKeysByVersion.size === 0 || !pagedMessages) return; + let cancelled = false; + (async () => { + const next = new Map(decryptedMap); + let changed = false; + for (const msg of pagedMessages as any[]) { + const id = msg.id as string; + if (next.has(id)) continue; + const cached = decryptionCache.get(id); + if (cached) { + next.set(id, cached); + changed = true; + continue; + } + if (!msg.ciphertext || msg.ciphertext.length < TAG_LENGTH) { + next.set(id, '[Invalid Encrypted Message]'); + changed = true; + continue; + } + // Pick the key matching this message's version. Messages + // that predate key rotation default to version 1. If the + // exact version is missing (shouldn't happen normally), + // fall back to the latest key and try that so we never + // lock the user out of their own history. + const msgVersion: number = msg.keyVersion ?? 1; + const keyForVersion = + channelKeysByVersion.get(msgVersion) ?? channelKey; + if (!keyForVersion) { + next.set(id, '[Unable to decrypt]'); + changed = true; + continue; + } + const tag = msg.ciphertext.slice(-TAG_LENGTH); + const contentHex = msg.ciphertext.slice(0, -TAG_LENGTH); + try { + const plaintext = await crypto.decryptData( + contentHex, + keyForVersion, + msg.nonce, + tag, + ); + if (cancelled) return; + cacheSet(id, plaintext); + next.set(id, plaintext); + changed = true; + } catch { + next.set(id, '[Unable to decrypt]'); + changed = true; + } + } + if (changed && !cancelled) setDecryptedMap(next); + })(); + return () => { + cancelled = true; + }; + }, [pagedMessages, channelKeysByVersion, channelKey]); + + // Reply preview decryption — same pattern as the main loop but + // keyed by child message id and using the parent's `replyToContent` + // + `replyToNonce` + `replyToKeyVersion` fields. Cached so a + // re-render of pagedMessages doesn't redecrypt every preview. + useEffect(() => { + if (channelKeysByVersion.size === 0 || !pagedMessages) return; + let cancelled = false; + (async () => { + const next = new Map(replyPreviewMap); + let changed = false; + for (const msg of pagedMessages as any[]) { + const id = msg.id as string; + if (next.has(id)) continue; + if ( + !msg.replyToContent || + !msg.replyToNonce || + msg.replyToContent.length < TAG_LENGTH + ) { + continue; + } + const cacheKey = `reply:${id}`; + const cached = decryptionCache.get(cacheKey); + if (cached) { + next.set(id, cached); + changed = true; + continue; + } + const replyVersion: number = msg.replyToKeyVersion ?? 1; + const keyForVersion = + channelKeysByVersion.get(replyVersion) ?? channelKey; + if (!keyForVersion) continue; + const tag = msg.replyToContent.slice(-TAG_LENGTH); + const contentHex = msg.replyToContent.slice(0, -TAG_LENGTH); + try { + const plaintext = await crypto.decryptData( + contentHex, + keyForVersion, + msg.replyToNonce, + tag, + ); + if (cancelled) return; + // Strip JSON wrappers so the preview shows the + // human-readable text, not raw {"text":"…"} dumps. + let preview = plaintext; + try { + const parsed = JSON.parse(plaintext); + if (parsed && typeof parsed === 'object') { + if (Array.isArray(parsed)) { + preview = parsed + .filter((p: any) => p?.type === 'attachment') + .map((p: any) => p?.filename || 'attachment') + .join(', '); + } else if (parsed.type === 'attachment') { + preview = parsed.filename || parsed.mimeType || 'Attachment'; + } else if (parsed.text !== undefined) { + preview = String(parsed.text); + } + } + } catch { + /* plain text */ + } + cacheSet(cacheKey, preview); + next.set(id, preview); + changed = true; + } catch { + /* leave unset — UI shows the missing-context fallback */ + } + } + if (changed && !cancelled) setReplyPreviewMap(next); + })(); + return () => { + cancelled = true; + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [pagedMessages, channelKeysByVersion, channelKey]); + + const decrypted: DecryptedMessage[] = useMemo(() => { + if (!pagedMessages) return []; + return (pagedMessages as any[]) + .slice() + .reverse() + .map((msg) => { + const id = msg.id as string; + const content = decryptedMap.get(id) ?? ''; + const raw = (() => { + try { + return JSON.parse(content); + } catch { + return null; + } + })(); + + let text = content; + const attachments: AttachmentMetadata[] = []; + // Two legacy formats: + // 1. Single object: { type: 'attachment', url, key, iv, ... } + // 2. Array of attachment objects + // 3. { text: '...' } wrapper from newer sends + if (raw) { + if (Array.isArray(raw)) { + for (const item of raw) { + if (item?.type === 'attachment' && item.url && item.key && item.iv) { + attachments.push(item as AttachmentMetadata); + } + } + text = ''; + } else if (raw.type === 'attachment' && raw.url && raw.key && raw.iv) { + attachments.push(raw as AttachmentMetadata); + text = ''; + } else if (raw.text !== undefined) { + text = String(raw.text); + } + } + + // Reply preview comes from `replyPreviewMap`, populated + // asynchronously by the dedicated decryption effect above. + const replyPreview = replyPreviewMap.get(id) ?? null; + + // `msg.reactions` is either an array of {emoji,count,me,users} + // (new server shape) or null. Older cached rows can still + // surface a Record without `users` — fall back to an + // empty users list in that case so the client never + // crashes on forward-compat. + const reactionsArray: DecryptedMessage['reactions'] = Array.isArray( + msg.reactions, + ) + ? msg.reactions.map((r: any) => ({ + emoji: String(r.emoji ?? ''), + count: Number(r.count ?? 0), + me: Boolean(r.me ?? false), + users: Array.isArray(r.users) + ? r.users.map((u: any) => ({ + userId: String(u.userId ?? ''), + username: String(u.username ?? 'Unknown'), + displayName: u.displayName ?? null, + })) + : [], + })) + : msg.reactions && typeof msg.reactions === 'object' + ? Object.entries(msg.reactions).map(([emoji, info]) => ({ + emoji, + count: (info as any).count ?? 0, + me: (info as any).me ?? false, + users: [], + })) + : []; + + return { + id, + channelId, + senderId: msg.sender_id as string, + authorName: msg.displayName || msg.username || 'User', + authorAvatarUrl: msg.avatarUrl ?? null, + authorRoleColor: msg.senderRoleColor ?? null, + content: text, + timestamp: msg.created_at ? new Date(msg.created_at).getTime() : Date.now(), + editedTimestamp: msg.editedAt ?? null, + replyToId: msg.replyToId ?? null, + replyToAuthorName: msg.replyToDisplayName || msg.replyToUsername || null, + replyToContent: replyPreview, + attachments, + reactions: reactionsArray, + pinned: msg.pinned ?? false, + } as DecryptedMessage; + }); + }, [pagedMessages, decryptedMap, replyPreviewMap, channelId]); + + // Pinned-to-bottom tracking. Matches the new UI's approach: a single + // boolean in a ref, updated on every onScroll event via isNearBottom. + // MutationObserver + ResizeObserver below drive all auto-scroll + // behaviour based on this one bit. + const pinnedRef = useRef(true); + + // Anchor for scroll-preservation on pagination. Instead of tracking + // an absolute scrollTop we remember the distance between the top of + // the oldest visible message and the bottom of the scroller (i.e. + // `scrollHeight - scrollTop`). As long as this anchor is set, every + // observed mutation re-pins scrollTop so that the same "bottom + // offset" is preserved — the reader stays glued to whichever + // message they were looking at when they scrolled into the loader + // region, regardless of how many growth events the decryption + // pipeline produces. + // + // The anchor is cleared when: + // - The query reports no more pages (`status !== 'CanLoadMore'`) + // - OR the next onScroll fires AFTER the user actually moves + // the scroller (tracked via a "recently restored" flag) + const scrollAnchorRef = useRef<{ bottomOffset: number } | null>(null); + const restoreActiveRef = useRef(false); + + // On channel switch, re-pin and scroll to bottom. + useLayoutEffect(() => { + pinnedRef.current = true; + scrollAnchorRef.current = null; + restoreActiveRef.current = false; + const el = scrollerRef.current; + if (el) el.scrollTop = el.scrollHeight; + }, [channelId]); + + // Observe DOM mutations + resizes inside the scroller. If the user + // is pinned to bottom, snap to bottom on every content change. If + // the user triggered a paginate-up, preserve their position by + // adjusting scrollTop against the height delta. + useEffect(() => { + const el = scrollerRef.current; + if (!el) return; + + const onContentChange = () => { + // Pagination anchor wins over pinned — the user is clearly + // scrolled up and reading older context, so we must NOT + // slam them to the bottom. + const anchor = scrollAnchorRef.current; + if (anchor) { + // Restore the same distance-from-bottom on every + // mutation until the anchor is cleared. This handles + // the case where decryption triggers multiple rounds + // of height growth after a single loadMore call. + restoreActiveRef.current = true; + el.scrollTop = Math.max(0, el.scrollHeight - anchor.bottomOffset); + // Flip the flag off on the next tick so a real user + // scroll after this restore can still update pinned + // tracking without being mistaken for our own restore. + requestAnimationFrame(() => { + restoreActiveRef.current = false; + }); + return; + } + if (pinnedRef.current) { + el.scrollTop = el.scrollHeight; + } + }; + + const mutationObs = new MutationObserver(onContentChange); + mutationObs.observe(el, { childList: true, subtree: true }); + + // ResizeObserver catches async content like attachments loading + // after the DOM is already in place (images decoding, blob URLs + // resolving, emoji images flushing). Also observes the scroller + // itself so window resizes (e.g. dragging the bottom edge up) + // keep the bottom-pinned position locked instead of clipping + // the latest messages. + const content = el.firstElementChild; + let resizeObs: ResizeObserver | undefined; + if (content) { + resizeObs = new ResizeObserver(onContentChange); + resizeObs.observe(content); + resizeObs.observe(el); + } + + // Final safety net for non-ResizeObserver-friendly resizes + // (split-pane drags that don't trigger an element resize) — + // listen for window resize and re-run the pin check too. + const onWindowResize = () => onContentChange(); + window.addEventListener('resize', onWindowResize); + + // Image / video / audio attachments fire this once their + // decoded bytes are painted. Re-runs the same pin-to-bottom + // path so the scroll anchor catches the late layout shift + // even when the placeholder + final image have identical + // box dimensions (the ResizeObserver wouldn't fire then). + const onAttachmentLoaded = () => onContentChange(); + window.addEventListener( + 'brycord:attachment-loaded', + onAttachmentLoaded, + ); + + return () => { + mutationObs.disconnect(); + resizeObs?.disconnect(); + window.removeEventListener('resize', onWindowResize); + window.removeEventListener( + 'brycord:attachment-loaded', + onAttachmentLoaded, + ); + }; + }, [channelId]); + + const handleScroll = useCallback(() => { + const el = scrollerRef.current; + if (!el) return; + + // Ignore scroll events triggered by our own anchor-restore in + // the observer. Without this guard, the restore would look + // like a user scroll and nuke the anchor before the next + // mutation arrives. + if (restoreActiveRef.current) return; + + const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight; + pinnedRef.current = distanceFromBottom < 150; + + // If the user has visibly moved away from the loader region + // (far enough down that another paginate-up can't trigger), + // clear the anchor. This lets the next near-top scroll start + // a fresh pagination cycle instead of chaining onto stale + // coords from a previous one. + if (scrollAnchorRef.current && el.scrollTop > 200) { + scrollAnchorRef.current = null; + } + + // Load older messages when the user nears the top. Capture + // the current distance-from-bottom into the anchor ref BEFORE + // firing loadMore so the content-change observer can pin the + // viewport to the same message the reader was looking at. + if (el.scrollTop < 80 && status === 'CanLoadMore' && !scrollAnchorRef.current) { + scrollAnchorRef.current = { + bottomOffset: el.scrollHeight - el.scrollTop, + }; + loadMore(50); + } + }, [status, loadMore]); + + const groups = useMemo(() => { + const result: DecryptedMessage[][] = []; + for (const msg of decrypted) { + const last = result[result.length - 1]; + if (last && last[last.length - 1].senderId === msg.senderId) { + const gap = msg.timestamp - last[last.length - 1].timestamp; + if (gap < 7 * 60 * 1000 && !msg.replyToId) { + last.push(msg); + continue; + } + } + result.push([msg]); + } + return result; + }, [decrypted]); + + // Channel info — used by the welcome header at the top of the + // scroller so we can render either a DM intro or a "Welcome to + // #name" block. Cheap reactive query, same as ChannelView. + const channelDoc = useQuery( + api.channels.get, + channelId ? { id: channelId as Id<'channels'> } : 'skip', + ); + + // Polls are independent Convex documents — fetch every poll in + // this channel and interleave them with the message groups by + // creation timestamp. Poll counts are small per channel so + // collecting them in one query is fine. + const pollsInChannel = + useQuery( + api.polls.listByChannel, + channelId ? { channelId: channelId as Id<'channels'> } : 'skip', + ) ?? []; + + type TimelineItem = + | { kind: 'group'; key: string; ts: number; group: DecryptedMessage[] } + | { kind: 'poll'; key: string; ts: number; pollId: Id<'polls'> }; + + const items = useMemo<TimelineItem[]>(() => { + const list: TimelineItem[] = groups.map((group) => ({ + kind: 'group', + key: `g-${group[0].id}`, + ts: group[0].timestamp, + group, + })); + for (const poll of pollsInChannel) { + list.push({ + kind: 'poll', + key: `p-${poll._id}`, + ts: poll.createdAt, + pollId: poll._id, + }); + } + list.sort((a, b) => a.ts - b.ts); + return list; + }, [groups, pollsInChannel]); + + return ( + <div className={styles.container} ref={scrollerRef} onScroll={handleScroll}> + <div className={styles.scroller}> + {status === 'LoadingMore' && ( + <div + style={{ + padding: '8px 16px', + color: 'var(--text-tertiary)', + fontSize: 13, + textAlign: 'center', + }} + > + Loading older messages… + </div> + )} + {/* Welcome header — only shown once we've loaded every + page so the user actually sees the start of the + history. While there are still older messages to + fetch, the loading row above takes its place. */} + {status === 'Exhausted' && ( + <ChannelWelcomeSection + channelId={channelId} + channelName={channelDoc?.name} + channelType={channelDoc?.type} + /> + )} + {(() => { + // Walk the timeline once, inserting a `DayDivider` + // whenever the calendar day changes. `lastTs` tracks + // the most-recent rendered item so two consecutive + // items on the same day skip the divider. + let lastTs: number | null = null; + const out: React.ReactNode[] = []; + for (const item of items) { + if (lastTs === null || !isSameDay(lastTs, item.ts)) { + out.push( + <DayDivider key={`day-${item.ts}`} timestamp={item.ts} />, + ); + } + lastTs = item.ts; + if (item.kind === 'group') { + out.push( + <MessageGroup + key={item.key} + messages={item.group as any} + channelId={channelId} + onReply={onReply} + />, + ); + } else { + out.push( + <div key={item.key} className={styles.pollWrapper}> + <PollCard pollId={item.pollId} /> + </div>, + ); + } + } + return out; + })()} + </div> + </div> + ); +} diff --git a/packages/shared/src/components/channel/MobileExpressionPickerSheet.module.css b/packages/shared/src/components/channel/MobileExpressionPickerSheet.module.css new file mode 100644 index 0000000..56a2286 --- /dev/null +++ b/packages/shared/src/components/channel/MobileExpressionPickerSheet.module.css @@ -0,0 +1,296 @@ +.root { + display: flex; + flex-direction: column; + height: 100%; + padding: 4px 12px 8px; + gap: 12px; +} + +/* ── Segmented tab selector ─────────────────────────────────────────── + Matches the Fluxer ExpressionPicker header tab list — rounded pill + container, inset padding, animated active tab background. Uses a + CSS-transform transition on the .tabBackground element instead of + framer-motion's layoutId so it stays lightweight. */ + +.tabBar { + position: relative; + display: flex; + border-radius: 10px; + background: var(--background-tertiary); + padding: 3px; + flex-shrink: 0; +} + +.tabBackground { + position: absolute; + top: 3px; + bottom: 3px; + height: calc(100% - 6px); + border-radius: 8px; + background: var(--background-secondary); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08); + transition: left 0.22s cubic-bezier(0.22, 1, 0.36, 1); + pointer-events: none; +} + +.tab { + position: relative; + z-index: 10; + flex: 1; + border: none; + border-radius: 8px; + padding: 8px 12px; + font: inherit; + font-size: 14px; + font-weight: 600; + line-height: 18px; + text-align: center; + background: transparent; + color: var(--text-secondary); + transition: color 0.15s ease; + cursor: pointer; + -webkit-tap-highlight-color: transparent; +} + +.tabActive { + color: var(--text-primary); +} + +.tabDisabled { + opacity: 0.55; + cursor: default; +} + +/* ── Search bar ────────────────────────────────────────────────────── + Mirrors Fluxer's PickerSearchInput — 36px min height, rounded, + magnifying-glass leading icon, clear-X trailing button. */ + +.searchBar { + position: relative; + display: flex; + align-items: center; + min-height: 44px; + padding: 0 2.25rem 0 2.25rem; + border-radius: 8px; + border: 1px solid var(--background-modifier-accent); + background-color: var(--background-tertiary); + flex-shrink: 0; +} + +.searchIcon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: var(--text-tertiary); + pointer-events: none; +} + +.searchInput { + flex: 1; + background: transparent; + border: none; + outline: none; + box-shadow: none; + -webkit-appearance: none; + appearance: none; + -webkit-tap-highlight-color: transparent; + padding: 8px 0; + font: inherit; + font-size: 15px; + color: var(--text-primary); +} + +/* Kill every browser focus/active/autofill ring so the only visual + is the rounded `.searchBar` surface. Firefox and Chromium ship + different default blue rings on text inputs. */ +.searchInput:focus, +.searchInput:focus-visible, +.searchInput:active { + outline: none; + box-shadow: none; + border: none; +} + +.searchInput::placeholder { + color: var(--text-tertiary-muted, var(--text-tertiary)); +} + +.searchClear { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + display: flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + border: none; + border-radius: 50%; + background: var(--background-modifier-hover, rgba(255, 255, 255, 0.1)); + color: var(--text-primary); + cursor: pointer; + -webkit-tap-highlight-color: transparent; +} + +/* ── Body / scrolling area ───────────────────────────────────────────*/ + +.body { + flex: 1; + min-height: 0; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + overscroll-behavior: contain; +} + +.comingSoon { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + color: var(--text-tertiary); + font-size: 14px; + font-weight: 500; +} + +/* ── Collapsible category sections ─────────────────────────────────── + The header is a clickable row with a caret that rotates; the emoji + grid slides in/out below when toggled. Headers are intentionally + NOT position: sticky — the user wants them to scroll naturally with + the content, matching the Fluxer drawer layout in the reference. */ + +.categorySection { + display: flex; + flex-direction: column; + margin-bottom: 4px; +} + +.categoryHeader { + display: flex; + align-items: center; + gap: 6px; + padding: 10px 4px; + border: none; + background: none; + color: var(--text-primary); + font: inherit; + font-size: 15px; + font-weight: 700; + text-align: left; + cursor: pointer; + -webkit-tap-highlight-color: transparent; +} + +.categoryCaret { + flex-shrink: 0; + color: var(--text-primary-muted, var(--text-tertiary)); + transition: transform 0.15s ease; +} + +.categoryCaretOpen { + transform: rotate(90deg); +} + +.categoryGrid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(40px, 1fr)); + gap: 2px; + padding: 4px 0 12px; +} + +.emojiButton { + display: flex; + align-items: center; + justify-content: center; + aspect-ratio: 1; + border: none; + border-radius: 6px; + background: none; + padding: 4px; + cursor: pointer; + transition: background-color 0.1s; + -webkit-tap-highlight-color: transparent; +} + +.emojiButton:active { + background-color: var(--background-modifier-hover); +} + +.emojiImg { + width: 28px; + height: 28px; + display: block; + pointer-events: none; +} + +/* ── Search results layout ──────────────────────────────────────────*/ + +.searchResults { + display: flex; + flex-direction: column; + padding-top: 4px; +} + +.searchResultsLabel { + font-size: 12px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--text-tertiary); + padding: 4px 4px 6px; +} + +.noResults { + text-align: center; + color: var(--text-tertiary); + font-size: 14px; + padding: 32px 0; +} + +/* ── Bottom category nav bar ──────────────────────────────────────── + Horizontal row of icon buttons below the body, for jumping directly + to a category. Mirrors Fluxer's `categoryListBottom` — pinned to + the sheet's bottom, subtle top divider, safe-area padding so it + clears the home indicator on notched phones. */ + +.bottomCategoryBar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 4px; + padding: 8px 4px calc(8px + env(safe-area-inset-bottom, 0px)); + border-top: 1px solid var(--background-header-secondary); + flex-shrink: 0; + overflow-x: auto; + scrollbar-width: none; +} + +.bottomCategoryBar::-webkit-scrollbar { + display: none; +} + +.bottomCategoryButton { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + flex-shrink: 0; + border: none; + border-radius: 8px; + background: transparent; + color: var(--text-primary-muted, var(--text-tertiary)); + cursor: pointer; + transition: background-color 0.15s, color 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.bottomCategoryButton:active { + background-color: var(--background-modifier-hover); +} + +.bottomCategoryButtonActive { + color: var(--text-primary); + background-color: var(--background-modifier-hover); +} diff --git a/packages/shared/src/components/channel/MobileExpressionPickerSheet.tsx b/packages/shared/src/components/channel/MobileExpressionPickerSheet.tsx new file mode 100644 index 0000000..851aa0d --- /dev/null +++ b/packages/shared/src/components/channel/MobileExpressionPickerSheet.tsx @@ -0,0 +1,369 @@ +/** + * MobileExpressionPickerSheet — the mobile emoji drawer. Ported from + * the new UI so the mobile layout matches: segmented tab selector at + * the top (GIFs / Media / Stickers / Emojis — only Emojis wired), + * search bar, collapsible category sections, and a bottom icon bar + * for jump-to-category navigation. Opens from the composer's emoji + * button when `useIsMobile()` is true, replacing the desktop + * portal-based picker. + */ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { + Bicycle, + BowlFood, + CaretRight, + Flag, + GameController, + Heart, + Leaf, + Magnet, + MagnifyingGlass, + Smiley, + X, +} from '@phosphor-icons/react'; +import { BottomSheet } from '@discord-clone/ui'; +import { emojiToCodepoint, getTwemojiUrl } from '../../utils/twemoji'; +import emojiData from '@app/data/emojis.json'; +import type { EmojiPickerValue } from './EmojiPicker'; +import styles from './MobileExpressionPickerSheet.module.css'; + +interface EmojiEntry { + names: string[]; + surrogates: string; +} + +type EmojiCategory = keyof typeof emojiData; + +const CATEGORIES: Array<{ + key: EmojiCategory; + label: string; + icon: React.ComponentType<{ size?: number; weight?: 'regular' | 'fill' | 'bold' }>; +}> = [ + { key: 'people' as EmojiCategory, label: 'People', icon: Smiley }, + { key: 'nature' as EmojiCategory, label: 'Nature', icon: Leaf }, + { key: 'food' as EmojiCategory, label: 'Food & Drink', icon: BowlFood }, + { key: 'activity' as EmojiCategory, label: 'Activities', icon: GameController }, + { key: 'travel' as EmojiCategory, label: 'Travel & Places', icon: Bicycle }, + { key: 'objects' as EmojiCategory, label: 'Objects', icon: Magnet }, + { key: 'symbols' as EmojiCategory, label: 'Symbols', icon: Heart }, + { key: 'flags' as EmojiCategory, label: 'Flags', icon: Flag }, +]; + +type Tab = 'gifs' | 'media' | 'stickers' | 'emojis'; + +const TABS: Array<{ id: Tab; label: string; enabled: boolean }> = [ + { id: 'gifs', label: 'GIFs', enabled: false }, + { id: 'media', label: 'Media', enabled: false }, + { id: 'stickers', label: 'Stickers', enabled: false }, + { id: 'emojis', label: 'Emojis', enabled: true }, +]; + +interface MobileExpressionPickerSheetProps { + isOpen: boolean; + onClose: () => void; + onSelect: (value: EmojiPickerValue) => void; + initialTab?: Tab; +} + +function TwemojiTile({ surrogates }: { surrogates: string }) { + return ( + <img + src={getTwemojiUrl(surrogates)} + alt={surrogates} + className={styles.emojiImg} + loading="lazy" + draggable={false} + /> + ); +} + +export function MobileExpressionPickerSheet({ + isOpen, + onClose, + onSelect, + initialTab = 'emojis', +}: MobileExpressionPickerSheetProps) { + const [activeTab, setActiveTab] = useState<Tab>(initialTab); + const [search, setSearch] = useState(''); + const [collapsed, setCollapsed] = useState<Set<string>>(() => new Set()); + const [activeCategory, setActiveCategory] = useState<string>('people'); + + const searchRef = useRef<HTMLInputElement>(null); + const tabBarRef = useRef<HTMLDivElement>(null); + const bodyRef = useRef<HTMLDivElement>(null); + const categoryRefs = useRef<Record<string, HTMLDivElement | null>>({}); + + // Snap to the caller's preferred tab every time the sheet opens fresh. + useEffect(() => { + if (isOpen) { + setActiveTab(initialTab); + setSearch(''); + } + }, [isOpen, initialTab]); + + // Focus the search input once the slide-in animation settles. + useEffect(() => { + if (!isOpen) return; + const t = setTimeout(() => searchRef.current?.focus(), 260); + return () => clearTimeout(t); + }, [isOpen]); + + const allEmojis = useMemo(() => { + const result: Record<string, EmojiEntry[]> = {}; + for (const cat of CATEGORIES) { + const entries: EmojiEntry[] = (emojiData as any)[cat.key] || []; + result[cat.key] = entries; + } + return result; + }, []); + + const filtered = useMemo(() => { + if (!search.trim()) return null; + const q = search.toLowerCase(); + const unicodeResults: EmojiEntry[] = []; + for (const cat of Object.values(allEmojis)) { + for (const emoji of cat) { + if (emoji.names.some((n) => n.includes(q))) { + unicodeResults.push(emoji); + } + } + } + return { unicodeResults }; + }, [search, allEmojis]); + + const handleSelectUnicode = useCallback( + (entry: EmojiEntry) => { + onSelect({ + kind: 'unicode', + surrogates: entry.surrogates, + name: entry.names[0] ?? entry.surrogates, + }); + onClose(); + }, + [onSelect, onClose], + ); + + const toggleCategory = (key: string) => { + setCollapsed((prev) => { + const next = new Set(prev); + if (next.has(key)) next.delete(key); + else next.add(key); + return next; + }); + }; + + const scrollToCategory = (key: string) => { + setCollapsed((prev) => { + if (!prev.has(key)) return prev; + const next = new Set(prev); + next.delete(key); + return next; + }); + setActiveCategory(key); + requestAnimationFrame(() => { + const el = categoryRefs.current[key]; + const body = bodyRef.current; + if (!el || !body) return; + const bodyRect = body.getBoundingClientRect(); + const elRect = el.getBoundingClientRect(); + const target = body.scrollTop + (elRect.top - bodyRect.top); + body.scrollTo({ top: target, behavior: 'smooth' }); + }); + }; + + const handleBodyScroll = () => { + if (search) return; + const body = bodyRef.current; + if (!body) return; + const bodyTop = body.getBoundingClientRect().top; + let current = activeCategory; + for (const cat of CATEGORIES) { + const el = categoryRefs.current[cat.key]; + if (!el) continue; + const elTop = el.getBoundingClientRect().top - bodyTop; + if (elTop <= 24) current = cat.key; + } + if (current !== activeCategory) setActiveCategory(current); + }; + + const activeIndex = TABS.findIndex((t) => t.id === activeTab); + const tabBackgroundStyle: React.CSSProperties = { + width: `calc((100% - 6px) / ${TABS.length})`, + left: `calc(3px + (100% - 6px) * ${activeIndex} / ${TABS.length})`, + }; + + return ( + <BottomSheet isOpen={isOpen} onClose={onClose} disableDefaultHeader showHandle> + <div className={styles.root}> + {/* Segmented tab selector */} + <div className={styles.tabBar} ref={tabBarRef} role="tablist"> + <div className={styles.tabBackground} style={tabBackgroundStyle} /> + {TABS.map((tab) => { + const isActive = tab.id === activeTab; + return ( + <button + key={tab.id} + type="button" + role="tab" + aria-selected={isActive} + className={`${styles.tab} ${isActive ? styles.tabActive : ''} ${ + !tab.enabled ? styles.tabDisabled : '' + }`} + onClick={() => { + if (tab.enabled) setActiveTab(tab.id); + }} + > + {tab.label} + </button> + ); + })} + </div> + + <div className={styles.searchBar}> + <MagnifyingGlass + size={18} + weight="regular" + className={styles.searchIcon} + /> + <input + ref={searchRef} + className={styles.searchInput} + placeholder="Find the emoji of your dreams" + value={search} + onChange={(e) => setSearch(e.target.value)} + /> + {search && ( + <button + type="button" + className={styles.searchClear} + onClick={() => setSearch('')} + aria-label="Clear search" + > + <X size={12} weight="bold" /> + </button> + )} + </div> + + <div + className={styles.body} + ref={bodyRef} + onScroll={handleBodyScroll} + > + {activeTab !== 'emojis' ? ( + <div className={styles.comingSoon}>Coming Soon</div> + ) : filtered ? ( + <div className={styles.searchResults}> + <div className={styles.searchResultsLabel}>Results</div> + {filtered.unicodeResults.length === 0 ? ( + <div className={styles.noResults}>No emoji found</div> + ) : ( + <div className={styles.categoryGrid}> + {filtered.unicodeResults.map((emoji, idx) => ( + <button + key={`${emoji.surrogates}-${idx}`} + type="button" + className={styles.emojiButton} + onClick={() => handleSelectUnicode(emoji)} + > + <TwemojiTile surrogates={emoji.surrogates} /> + </button> + ))} + </div> + )} + </div> + ) : ( + <> + {CATEGORIES.map((cat) => ( + <CategorySection + key={cat.key} + title={cat.label} + isCollapsed={collapsed.has(cat.key)} + onToggle={() => toggleCategory(cat.key)} + sectionRef={(el) => { + categoryRefs.current[cat.key] = el; + }} + > + {allEmojis[cat.key].map((emoji, idx) => ( + <button + key={`${cat.key}-${emoji.surrogates}-${idx}`} + type="button" + className={styles.emojiButton} + onClick={() => handleSelectUnicode(emoji)} + > + <TwemojiTile surrogates={emoji.surrogates} /> + </button> + ))} + </CategorySection> + ))} + </> + )} + </div> + + {activeTab === 'emojis' && !search && ( + <div className={styles.bottomCategoryBar}> + {CATEGORIES.map((cat) => { + const Icon = cat.icon; + return ( + <button + key={cat.key} + type="button" + className={`${styles.bottomCategoryButton} ${ + activeCategory === cat.key + ? styles.bottomCategoryButtonActive + : '' + }`} + onClick={() => scrollToCategory(cat.key)} + aria-label={cat.label} + > + <Icon size={20} weight="fill" /> + </button> + ); + })} + </div> + )} + </div> + + {/* Silence the unused import warning — emojiToCodepoint is + referenced elsewhere in TwemojiTile variants and kept for + parity with the desktop picker's helpers. */} + <span style={{ display: 'none' }}>{emojiToCodepoint('')}</span> + </BottomSheet> + ); +} + +interface CategorySectionProps { + title: string; + isCollapsed: boolean; + onToggle: () => void; + children: React.ReactNode; + sectionRef?: (el: HTMLDivElement | null) => void; +} + +function CategorySection({ + title, + isCollapsed, + onToggle, + children, + sectionRef, +}: CategorySectionProps) { + return ( + <div className={styles.categorySection} ref={sectionRef}> + <button + type="button" + className={styles.categoryHeader} + onClick={onToggle} + aria-expanded={!isCollapsed} + > + <CaretRight + size={14} + weight="bold" + className={`${styles.categoryCaret} ${ + !isCollapsed ? styles.categoryCaretOpen : '' + }`} + /> + <span>{title}</span> + </button> + {!isCollapsed && <div className={styles.categoryGrid}>{children}</div>} + </div> + ); +} diff --git a/packages/shared/src/components/channel/MobileImageActionsSheet.module.css b/packages/shared/src/components/channel/MobileImageActionsSheet.module.css new file mode 100644 index 0000000..b798575 --- /dev/null +++ b/packages/shared/src/components/channel/MobileImageActionsSheet.module.css @@ -0,0 +1,83 @@ +/* Mirrors MobileMessageActionsSheet.module.css so the two mobile + action sheets share a visual language. Same body padding, same + rounded-card action list, same inset divider trick between rows. */ + +.body { + display: flex; + flex-direction: column; + gap: 16px; + padding: 8px 16px 32px; +} + +.title { + text-align: center; + font-size: 14px; + font-weight: 700; + color: var(--text-primary-muted, #a0a3a8); + padding-bottom: 4px; + border-bottom: 1px solid var(--background-header-secondary); + margin: 0 -16px 4px; + padding: 0 16px 12px; +} + +.actionList { + display: flex; + flex-direction: column; + gap: 0; + border-radius: 12px; + overflow: hidden; +} + +.actionItem { + position: relative; + display: flex; + align-items: center; + gap: 14px; + width: 100%; + padding: 16px; + background-color: var(--background-secondary-alt); + border: none; + border-radius: 0; + color: var(--text-primary, #fff); + font: inherit; + font-size: 15px; + font-weight: 600; + text-align: left; + cursor: pointer; + transition: background-color 0.1s; + -webkit-tap-highlight-color: transparent; +} + +.actionItem:active { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.08)); +} + +.actionItem svg { + flex-shrink: 0; + color: var(--text-primary-muted, #a0a3a8); +} + +/* Inset 1px divider between adjacent rows in the same card — same + trick MobileMessageActionsSheet uses so the rows read as a + single grouped card instead of floating buttons. */ +.actionItem:not(:last-child)::after { + content: ''; + position: absolute; + left: 1rem; + right: 1rem; + bottom: 0; + height: 1px; + background-color: var(--background-header-secondary); + pointer-events: none; +} + +/* Destructive variant — Fluxer's exact delete red, tied to the + saturation-factor token so it tracks theme changes. Overrides + both the label and the icon color. */ +.actionItemDanger { + color: hsl(350, calc(90% * var(--saturation-factor)), 65%); +} + +.actionItemDanger svg { + color: hsl(350, calc(90% * var(--saturation-factor)), 65%); +} diff --git a/packages/shared/src/components/channel/MobileImageActionsSheet.tsx b/packages/shared/src/components/channel/MobileImageActionsSheet.tsx new file mode 100644 index 0000000..ba94649 --- /dev/null +++ b/packages/shared/src/components/channel/MobileImageActionsSheet.tsx @@ -0,0 +1,128 @@ +/** + * MobileImageActionsSheet — three-dots menu opened from the mobile + * ImageLightbox header. Renders a Fluxer-style grouped action list + * inside a `BottomSheet`, same visual idiom as the existing + * MobileMessageActionsSheet so the two sheets feel like siblings. + * + * Group layout (matches the user's spec, not the richer reference): + * 1. Favorite / Remove from Favorites (single-row group) + * 2. Save Image • Open Link (two-row group) + * 3. Delete Message (danger, single-row group) + * + * Each row only renders when its callback is actually provided so + * callers with limited capability (e.g. the pinned-message popover, + * which can't delete the source message) can drop rows without + * leaving empty groups behind. + */ +import { BottomSheet } from '@brycord/ui'; +import { + Star, + Download, + ArrowSquareOut, + Trash, +} from '@phosphor-icons/react'; +import styles from './MobileImageActionsSheet.module.css'; + +interface MobileImageActionsSheetProps { + isOpen: boolean; + onClose: () => void; + isFavorited: boolean; + onToggleFavorite?: () => void; + onSaveImage?: () => void; + onOpenLink?: () => void; + /** When provided, a red "Delete Message" row is appended at the + * bottom. Lets callers hide the row entirely when the current + * user doesn't have permission or when the lightbox is rendered + * from a context with no parent message (pinned popover). */ + onDeleteMessage?: () => void; +} + +export function MobileImageActionsSheet({ + isOpen, + onClose, + isFavorited, + onToggleFavorite, + onSaveImage, + onOpenLink, + onDeleteMessage, +}: MobileImageActionsSheetProps) { + // Wrap each action so tapping always closes the sheet after the + // handler fires. Mirrors the pattern in MobileMessageActionsSheet. + const wrap = (fn?: () => void) => () => { + if (!fn) return; + fn(); + onClose(); + }; + + return ( + <BottomSheet + isOpen={isOpen} + onClose={onClose} + disableDefaultHeader + showHandle + initialHeightSvh={40} + expandable + zIndex={17000} + > + <div className={styles.body}> + <div className={styles.title}>Media Options</div> + + {onToggleFavorite && ( + <div className={styles.actionList}> + <button + type="button" + className={styles.actionItem} + onClick={wrap(onToggleFavorite)} + > + <Star + size={20} + weight={isFavorited ? 'fill' : 'regular'} + /> + <span> + {isFavorited ? 'Remove from Favorites' : 'Add to Favorites'} + </span> + </button> + </div> + )} + + {(onSaveImage || onOpenLink) && ( + <div className={styles.actionList}> + {onSaveImage && ( + <button + type="button" + className={styles.actionItem} + onClick={wrap(onSaveImage)} + > + <Download size={20} weight="regular" /> + <span>Save Image</span> + </button> + )} + {onOpenLink && ( + <button + type="button" + className={styles.actionItem} + onClick={wrap(onOpenLink)} + > + <ArrowSquareOut size={20} weight="regular" /> + <span>Open Link</span> + </button> + )} + </div> + )} + + {onDeleteMessage && ( + <div className={styles.actionList}> + <button + type="button" + className={`${styles.actionItem} ${styles.actionItemDanger}`} + onClick={wrap(onDeleteMessage)} + > + <Trash size={20} weight="fill" /> + <span>Delete Message</span> + </button> + </div> + )} + </div> + </BottomSheet> + ); +} diff --git a/packages/shared/src/components/channel/MobileMessageActionsSheet.module.css b/packages/shared/src/components/channel/MobileMessageActionsSheet.module.css new file mode 100644 index 0000000..205e9d8 --- /dev/null +++ b/packages/shared/src/components/channel/MobileMessageActionsSheet.module.css @@ -0,0 +1,108 @@ +.body { + display: flex; + flex-direction: column; + gap: 16px; + padding: 16px 16px 32px; +} + +/* Quick reactions row — the row itself is transparent; each button + is its own rounded tile on top of the drawer background, matching + the Fluxer mobile layout. */ + +.quickRow { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 8px; +} + +.quickButton { + display: flex; + align-items: center; + justify-content: center; + min-height: 56px; + padding: 0; + background-color: var(--background-modifier-hover); + border: none; + border-radius: 10px; + color: var(--text-primary, #fff); + cursor: pointer; + transition: background-color 0.1s; + -webkit-tap-highlight-color: transparent; +} + +.quickButton:active { + background-color: var(--background-modifier-active, rgba(255, 255, 255, 0.08)); +} + +.quickEmoji { + width: 28px; + height: 28px; + display: block; + pointer-events: none; +} + +/* Action list — items sit flush with no outer gap. The whole list + is a single rounded card; a 1px divider (inset by 1rem on each + side) separates adjacent items. First / last items get their + rounded corners via the parent's overflow: hidden. */ + +.actionList { + display: flex; + flex-direction: column; + gap: 0; + border-radius: 12px; + overflow: hidden; +} + +.actionItem { + position: relative; + display: flex; + align-items: center; + gap: 14px; + width: 100%; + padding: 16px 16px; + background-color: var(--background-secondary-alt); + border: none; + border-radius: 0; + color: var(--text-primary, #fff); + font: inherit; + font-size: 15px; + font-weight: 600; + text-align: left; + cursor: pointer; + transition: background-color 0.1s; + -webkit-tap-highlight-color: transparent; +} + +.actionItem:active { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.08)); +} + +.actionItem svg { + flex-shrink: 0; + color: var(--text-primary-muted, #a0a3a8); +} + +/* Thin inset divider between adjacent items — drawn as an absolutely + positioned element at the bottom of every item except the last. */ +.actionItem:not(:last-child)::after { + content: ''; + position: absolute; + left: 1rem; + right: 1rem; + bottom: 0; + height: 1px; + background-color: var(--background-header-secondary); + pointer-events: none; +} + +.actionItemDanger { + /* Fluxer's exact delete red — HSL tied to saturation-factor so + it follows the theme's saturation setting in both dark and + light modes. Overrides both the text and icon color. */ + color: hsl(350, calc(90% * var(--saturation-factor)), 65%); +} + +.actionItemDanger svg { + color: hsl(350, calc(90% * var(--saturation-factor)), 65%); +} diff --git a/packages/shared/src/components/channel/MobileMessageActionsSheet.tsx b/packages/shared/src/components/channel/MobileMessageActionsSheet.tsx new file mode 100644 index 0000000..5ec242f --- /dev/null +++ b/packages/shared/src/components/channel/MobileMessageActionsSheet.tsx @@ -0,0 +1,232 @@ +/** + * MobileMessageActionsSheet — long-press menu for messages on mobile. + * Mirrors the action set in `MessageActionBar` (the desktop hover bar + * + its More menu) but laid out as a bottom sheet drawer instead of a + * floating popover, since hover-based UI doesn't work on touch. + * + * Layout matches Discord/Fluxer mobile: + * ┌────── drag handle ──────┐ + * │ [👍] [👌] [🎉] [❤] [+] │ ← quick reactions row + * ├──────────────────────────┤ + * │ ☺ Add Reaction │ + * │ ↩ Reply │ + * │ → Forward │ + * │ ✎ Edit Message │ (own messages only) + * │ 📌 Pin Message │ + * │ ⧉ Copy Text │ + * │ 🔍 Inspect Message │ + * │ 🗑 Delete Message │ (own messages or admins) + * └──────────────────────────┘ + * + * Reuses `BottomSheet` from `@brycord/ui` (same component the emoji + * picker and channel-details drawer use). Each action call closes + * the sheet immediately so the user sees the result of their tap + * without an extra dismiss step. + */ +import { + Smiley, + ArrowBendUpLeft, + ArrowBendUpRight, + PencilSimple, + Trash, + PushPin, + Copy, + MagnifyingGlass, + Plus, +} from '@phosphor-icons/react'; +import { BottomSheet } from '@discord-clone/ui'; +import styles from './MobileMessageActionsSheet.module.css'; + +// Mirrors the QUICK_EMOJIS in MessageActionBar so users get the same +// shortcuts on mobile as on desktop. Adding a 4th gives the row +// better visual rhythm with the trailing "+" tile (5 cells total). +const QUICK_EMOJIS = [ + { emoji: '👍', name: 'thumbsup' }, + { emoji: '👌', name: 'ok_hand' }, + { emoji: '🎉', name: 'tada' }, + { emoji: '❤️', name: 'heart' }, +]; + +function emojiToCodepoint(emoji: string): string { + const codepoints: string[] = []; + for (let i = 0; i < emoji.length; i++) { + const code = emoji.codePointAt(i)!; + if (code === 0xfe0f) continue; + codepoints.push(code.toString(16)); + if (code > 0xffff) i++; + } + return codepoints.join('-'); +} + +interface MobileMessageActionsSheetProps { + isOpen: boolean; + onClose: () => void; + isOwnMessage: boolean; + canDelete: boolean; + hasContent: boolean; + /** Quick reaction handler — receives the unicode emoji string. */ + onQuickReact: (emoji: string) => void; + /** Opens the full reaction picker. */ + onAddReaction: () => void; + onReply: () => void; + onForward: () => void; + onEdit?: () => void; + onPin?: () => void; + /** + * True when the target message is currently pinned. Flips the + * "Pin Message" label to "Unpin Message" so the action mirrors + * its resulting state. + */ + isPinned?: boolean; + onCopyText?: () => void; + onInspect?: () => void; + onDelete?: () => void; +} + +export function MobileMessageActionsSheet({ + isOpen, + onClose, + isOwnMessage, + canDelete, + hasContent, + onQuickReact, + onAddReaction, + onReply, + onForward, + onEdit, + onPin, + isPinned, + onCopyText, + onInspect, + onDelete, +}: MobileMessageActionsSheetProps) { + // Wrap each action so the sheet always closes after a selection. + // Returning a no-arg handler keeps the JSX clean below. + const wrap = (fn?: () => void) => () => { + if (!fn) return; + fn(); + onClose(); + }; + + return ( + <BottomSheet + isOpen={isOpen} + onClose={onClose} + disableDefaultHeader + showHandle + // Open at half height by default. The user can drag the + // handle UP to expand toward the full sheet height if + // they need to see all the actions, or down past the + // threshold to dismiss. + initialHeightSvh={50} + expandable + > + <div className={styles.body}> + {/* Quick emoji row */} + <div className={styles.quickRow}> + {QUICK_EMOJIS.map((qe) => ( + <button + key={qe.name} + type="button" + className={styles.quickButton} + onClick={() => { + onQuickReact(qe.emoji); + onClose(); + }} + aria-label={`React with :${qe.name}:`} + > + <img + src={`https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/svg/${emojiToCodepoint(qe.emoji)}.svg`} + alt={qe.emoji} + className={styles.quickEmoji} + draggable={false} + /> + </button> + ))} + <button + type="button" + className={styles.quickButton} + onClick={wrap(onAddReaction)} + aria-label="Add reaction" + > + <Plus size={22} weight="bold" /> + </button> + </div> + + {/* Group 1 — Add Reaction lives alone so it visually + echoes the quick reactions row above it. */} + <div className={styles.actionList}> + <button type="button" className={styles.actionItem} onClick={wrap(onAddReaction)}> + <Smiley size={20} weight="fill" /> + <span>Add Reaction</span> + </button> + </div> + + {/* Group 2 — message interaction actions (Reply / + Forward / Edit). Edit only shows on own messages. */} + <div className={styles.actionList}> + <button type="button" className={styles.actionItem} onClick={wrap(onReply)}> + <ArrowBendUpLeft size={20} weight="fill" /> + <span>Reply</span> + </button> + <button type="button" className={styles.actionItem} onClick={wrap(onForward)}> + <ArrowBendUpRight size={20} weight="fill" /> + <span>Forward</span> + </button> + {isOwnMessage && onEdit && ( + <button type="button" className={styles.actionItem} onClick={wrap(onEdit)}> + <PencilSimple size={20} weight="fill" /> + <span>Edit Message</span> + </button> + )} + </div> + + {/* Group 3 — mod / own-message actions (Pin / Delete). + Only renders when at least one of the two is + available, so viewers with no power on someone + else's message don't see an empty card. */} + {(onPin || (canDelete && onDelete)) && ( + <div className={styles.actionList}> + {onPin && ( + <button type="button" className={styles.actionItem} onClick={wrap(onPin)}> + <PushPin size={20} weight="fill" /> + <span>{isPinned ? 'Unpin Message' : 'Pin Message'}</span> + </button> + )} + {canDelete && onDelete && ( + <button + type="button" + className={`${styles.actionItem} ${styles.actionItemDanger}`} + onClick={wrap(onDelete)} + > + <Trash size={20} weight="fill" /> + <span>Delete Message</span> + </button> + )} + </div> + )} + + {/* Group 4 — text / debug utilities (Copy Text + + Inspect Message). Copy Text is only shown when + there's actual text to copy — not for pure + attachment messages. */} + {((hasContent && onCopyText) || onInspect) && ( + <div className={styles.actionList}> + {hasContent && onCopyText && ( + <button type="button" className={styles.actionItem} onClick={wrap(onCopyText)}> + <Copy size={20} weight="fill" /> + <span>Copy Text</span> + </button> + )} + {onInspect && ( + <button type="button" className={styles.actionItem} onClick={wrap(onInspect)}> + <MagnifyingGlass size={20} weight="fill" /> + <span>Inspect Message</span> + </button> + )} + </div> + )} + </div> + </BottomSheet> + ); +} diff --git a/packages/shared/src/components/channel/MobilePinActionsSheet.module.css b/packages/shared/src/components/channel/MobilePinActionsSheet.module.css new file mode 100644 index 0000000..4b394c4 --- /dev/null +++ b/packages/shared/src/components/channel/MobilePinActionsSheet.module.css @@ -0,0 +1,68 @@ +/* ── Mobile pin actions sheet ──────────────────────────────────────── + Bottom sheet opened by long-pressing a pinned message in the + ChannelDetailsDrawer Pins tab. Currently only has one action + ("Unpin Message"), but structured as a card stack so we can + drop in "Jump to Message" / "Copy Link" etc. later without + reshuffling the layout. */ + +.body { + display: flex; + flex-direction: column; + gap: 12px; + padding: 8px 16px 32px; +} + +.actionList { + background-color: var(--background-secondary-alt); + border-radius: 0.75rem; + overflow: hidden; +} + +.actionItem { + position: relative; + display: flex; + align-items: center; + gap: 14px; + width: 100%; + padding: 16px; + background: transparent; + border: none; + color: var(--text-primary); + font: inherit; + font-size: 15px; + font-weight: 600; + text-align: left; + cursor: pointer; + transition: background-color 0.1s; + -webkit-tap-highlight-color: transparent; +} + +.actionItem:active { + background-color: var(--background-modifier-hover); +} + +.actionItem svg { + flex-shrink: 0; + color: var(--text-primary-muted); +} + +.actionItem:not(:last-child)::after { + content: ''; + position: absolute; + left: calc(16px + 20px + 14px); + right: 16px; + bottom: 0; + height: 1px; + background-color: var(--background-header-secondary); + pointer-events: none; +} + +/* Danger variant — Unpin Message. Fluxer reference uses the same + red HSL as Delete Message in the message actions sheet. */ +.actionItemDanger { + color: hsl(350, calc(90% * var(--saturation-factor)), 65%); +} + +.actionItemDanger svg { + color: hsl(350, calc(90% * var(--saturation-factor)), 65%); +} diff --git a/packages/shared/src/components/channel/MobilePinActionsSheet.tsx b/packages/shared/src/components/channel/MobilePinActionsSheet.tsx new file mode 100644 index 0000000..ded3d16 --- /dev/null +++ b/packages/shared/src/components/channel/MobilePinActionsSheet.tsx @@ -0,0 +1,65 @@ +/** + * MobilePinActionsSheet — bottom sheet opened by long-pressing a + * pinned message in the mobile Pins tab. Mirrors the pattern used + * by MobileMessageActionsSheet: a rounded card of danger-styled + * action rows, with inset dividers between them. + * + * Currently only has one action ("Unpin Message") since that's the + * only contextual action the pinned list needs — jumping to the + * message is already the tap behaviour on the card itself, and + * there's no "reply to pin" or "react to pin" concept. Structured + * as a card stack so future actions can drop in without reshuffling. + */ +import { PushPinSlash } from '@phosphor-icons/react'; +import { BottomSheet } from '@brycord/ui'; +import styles from './MobilePinActionsSheet.module.css'; + +interface MobilePinActionsSheetProps { + isOpen: boolean; + onClose: () => void; + /** True when the local user has permission to unpin — the sheet + * simply doesn't render the Unpin action otherwise. */ + canUnpin: boolean; + onUnpin: () => void; +} + +export function MobilePinActionsSheet({ + isOpen, + onClose, + canUnpin, + onUnpin, +}: MobilePinActionsSheetProps) { + // Wrap each action so tapping it always closes the sheet. Mirrors + // the pattern in MobileMessageActionsSheet so behaviour is + // consistent across both bottom sheets. + const wrap = (fn: () => void) => () => { + fn(); + onClose(); + }; + + return ( + <BottomSheet + isOpen={isOpen} + onClose={onClose} + disableDefaultHeader + showHandle + initialHeightSvh={25} + expandable + > + <div className={styles.body}> + {canUnpin && ( + <div className={styles.actionList}> + <button + type="button" + className={`${styles.actionItem} ${styles.actionItemDanger}`} + onClick={wrap(onUnpin)} + > + <PushPinSlash size={20} weight="fill" /> + <span>Unpin Message</span> + </button> + </div> + )} + </div> + </BottomSheet> + ); +} diff --git a/packages/shared/src/components/channel/PendingAttachmentRow.module.css b/packages/shared/src/components/channel/PendingAttachmentRow.module.css new file mode 100644 index 0000000..3385025 --- /dev/null +++ b/packages/shared/src/components/channel/PendingAttachmentRow.module.css @@ -0,0 +1,278 @@ +/* ── Pending attachment row ────────────────────────────────── + Horizontal strip of cards that sits directly above the chat + input. Horizontally scrollable when the user has too many + files for the visible width, with a subtle bottom border so + the row visually detaches from the textarea pill below it. */ + +.row { + display: flex; + gap: 10px; + /* Top padding is bumped so the floating action bar (which + hangs above each card by 14px) has room to live inside + the row's own clip box — `overflow-x: auto` forces + overflow-y to also clip, so we can't just let it leak. */ + padding: 24px 16px 12px; + overflow-x: auto; + overflow-y: hidden; + scrollbar-width: thin; + scrollbar-color: var(--background-modifier-accent) transparent; +} + +.row::-webkit-scrollbar { + height: 6px; +} + +.row::-webkit-scrollbar-track { + background: transparent; +} + +.row::-webkit-scrollbar-thumb { + background-color: var(--background-modifier-accent); + border-radius: 999px; +} + +/* ── Individual card ────────────────────────────────────────── */ + +.card { + position: relative; + display: flex; + flex-direction: column; + flex-shrink: 0; + min-width: 200px; + max-width: 200px; + min-height: 200px; + max-height: 200px; + padding: 8px; + background-color: var(--background-primary); + border: 1px solid var(--background-modifier-accent); + border-radius: 0.5rem; + transition: border-color 0.12s; + box-sizing: border-box; +} + +.card:hover { + border-color: var(--background-modifier-hover); +} + +.cardSpoiler { + border-color: var(--brand-primary); +} + +.cardError { + border-color: hsl(0, calc(70% * var(--saturation-factor, 1)), 55%); +} + +/* ── Preview area ───────────────────────────────────────────── */ + +.preview { + position: relative; + flex: 1; + width: 100%; + min-height: 0; + background-color: var(--background-secondary-alt, var(--background-secondary)); + border-radius: 0.375rem; + overflow: hidden; +} + +.previewImage, +.previewVideo { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +.iconPreview { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-primary-muted, #a0a3a8); +} + +/* Small play badge over video preview thumbnails so it reads + as playable (Fluxer pattern). */ +.videoPlayBadge { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 44px; + height: 44px; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba(0, 0, 0, 0.55); + border-radius: 50%; + color: #fff; + pointer-events: none; +} + +/* Spoiler overlay — dark film + eye-slash icon + "SPOILER" + label so the card reads as hidden even though it's staged. */ +.spoilerOverlay { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 6px; + background-color: rgba(0, 0, 0, 0.75); + color: #fff; + font-size: 0.6875rem; + font-weight: 800; + letter-spacing: 0.1em; + pointer-events: none; +} + +.altBadge { + position: absolute; + top: 8px; + left: 8px; + padding: 2px 6px; + background-color: rgba(0, 0, 0, 0.65); + border-radius: 4px; + color: #fff; + font-size: 0.625rem; + font-weight: 800; + letter-spacing: 0.06em; +} + +/* ── Always-visible action bar (Spoiler / Edit / Delete) ──── + Floating pill that hangs above the card's top-right edge, + matching MessageActionBar's visual language: + `--background-primary` surface with a 1px + `--background-header-secondary` border and 8px radius. + Lives as a direct child of the card (not .preview) so it + can escape the preview's overflow clip. The row reserves + enough top padding for the overhang to avoid being clipped + by its own `overflow-x: auto` clip box. */ + +.actions { + position: absolute; + top: -14px; + right: 8px; + display: flex; + align-items: center; + padding: 2px; + background-color: var(--background-primary); + border: 1px solid var(--background-header-secondary); + border-radius: 8px; + z-index: 3; +} + +.actionButton { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 30px; + height: 30px; + padding: 4px; + background: none; + border: none; + border-radius: 6px; + color: var(--text-tertiary); + cursor: pointer; + transition: background-color 0.1s, color 0.1s; +} + +.actionButton:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +.actionButtonActive { + color: var(--brand-primary); +} + +.actionButtonActive:hover { + background-color: var(--background-modifier-hover); + color: var(--brand-primary); + filter: brightness(1.1); +} + +.actionButtonDanger { + color: var(--status-danger); +} + +.actionButtonDanger:hover { + background-color: var(--background-modifier-hover); + color: var(--status-danger); +} + +/* ── Upload progress bar ───────────────────────────────────── + Fixed to the bottom of the preview area. Track is the same + --background-modifier-accent as the card border; fill is + brand-primary and scales via a --progress CSS variable so + we never reach into the DOM to mutate widths. */ + +.progress { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 3px; + background-color: rgba(0, 0, 0, 0.55); + overflow: hidden; + z-index: 1; +} + +.progressFill { + height: 100%; + width: var(--progress, 0%); + background-color: var(--brand-primary); + transition: width 0.12s linear; +} + +/* ── Meta row below the preview ─────────────────────────── */ + +.meta { + display: flex; + flex-direction: column; + gap: 2px; + padding: 8px 4px 0; + background-color: var(--background-primary); +} + +.filename { + font-size: 0.8125rem; + font-weight: 600; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* Bottom row of the meta — size on the left, uppercase file-type + badge flush to the right. Matches the Fluxer pattern of + surfacing the extension as a brand-primary label. */ +.metaBottomRow { + display: flex; + align-items: center; + justify-content: space-between; + gap: 6px; +} + +.size { + font-size: 0.6875rem; + font-weight: 500; + color: var(--text-primary-muted, #a0a3a8); + font-variant-numeric: tabular-nums; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.ext { + font-size: 0.6875rem; + font-weight: 800; + letter-spacing: 0.04em; + color: var(--brand-primary-light); + flex-shrink: 0; +} + +.cardError .size { + color: hsl(0, calc(70% * var(--saturation-factor, 1)), 60%); +} diff --git a/packages/shared/src/components/channel/PendingAttachmentRow.tsx b/packages/shared/src/components/channel/PendingAttachmentRow.tsx new file mode 100644 index 0000000..17e1a47 --- /dev/null +++ b/packages/shared/src/components/channel/PendingAttachmentRow.tsx @@ -0,0 +1,105 @@ +import { FileAudio, FileText, FileVideo, X } from '@phosphor-icons/react'; +import styles from './PendingAttachmentRow.module.css'; + +export interface PendingAttachment { + id: string; + file: File; + previewUrl?: string | null; + progress?: number | null; + error?: string | null; +} + +interface PendingAttachmentRowProps { + attachments: PendingAttachment[]; + onRemove: (id: string) => void; +} + +function formatBytes(n: number): string { + if (n < 1024) return `${n} B`; + if (n < 1024 * 1024) return `${Math.round(n / 1024)} KB`; + return `${(n / (1024 * 1024)).toFixed(1)} MB`; +} + +function fileExt(name: string): string { + const idx = name.lastIndexOf('.'); + return idx >= 0 ? name.slice(idx + 1).toUpperCase() : 'FILE'; +} + +/** + * Horizontal strip of staged attachments shown above the composer. + * Each card has a preview (image / video / icon), filename + size, + * a remove button, and an optional progress bar during upload. + */ +export function PendingAttachmentRow({ attachments, onRemove }: PendingAttachmentRowProps) { + if (attachments.length === 0) return null; + + return ( + <div className={styles.row}> + {attachments.map((att) => { + const kind = att.file.type.split('/')[0]; + const hasProgress = att.progress !== null && att.progress !== undefined; + return ( + <div + key={att.id} + className={`${styles.card} ${att.error ? styles.cardError : ''}`} + > + <div className={styles.preview}> + {kind === 'image' && att.previewUrl ? ( + <img + src={att.previewUrl} + alt={att.file.name} + className={styles.previewImage} + /> + ) : kind === 'video' && att.previewUrl ? ( + <video src={att.previewUrl} className={styles.previewVideo} muted /> + ) : kind === 'audio' ? ( + <div className={styles.iconPreview}> + <FileAudio size={32} weight="regular" /> + </div> + ) : kind === 'video' ? ( + <div className={styles.iconPreview}> + <FileVideo size={32} weight="regular" /> + </div> + ) : ( + <div className={styles.iconPreview}> + <FileText size={32} weight="regular" /> + </div> + )} + </div> + + <div className={styles.actions}> + <button + type="button" + className={`${styles.actionButton} ${styles.actionButtonDanger}`} + onClick={() => onRemove(att.id)} + aria-label="Remove attachment" + title="Remove" + > + <X size={16} weight="bold" /> + </button> + </div> + + {hasProgress && ( + <div className={styles.progress}> + <div + className={styles.progressFill} + style={{ width: `${Math.round((att.progress ?? 0) * 100)}%` }} + /> + </div> + )} + + <div className={styles.meta}> + <div className={styles.filename} title={att.file.name}> + {att.file.name} + </div> + <div className={styles.metaBottomRow}> + <span className={styles.size}>{formatBytes(att.file.size)}</span> + <span className={styles.ext}>{fileExt(att.file.name)}</span> + </div> + </div> + </div> + ); + })} + </div> + ); +} diff --git a/packages/shared/src/components/channel/PinConfirmationModal.module.css b/packages/shared/src/components/channel/PinConfirmationModal.module.css new file mode 100644 index 0000000..e1dca25 --- /dev/null +++ b/packages/shared/src/components/channel/PinConfirmationModal.module.css @@ -0,0 +1,119 @@ +/* ── Pin confirmation modal ────────────────────────────────────────── + Fluxer-style "Pin it. Pin it good." dialog shown when the user + taps Pin Message from either the desktop action bar or the + mobile long-press sheet. Uses the shared Modal primitive for + the chrome (backdrop, centring, size), so the styling here is + just the content layout: description + static preview card + + two stacked action buttons. */ + +.body { + display: flex; + flex-direction: column; + gap: 16px; + padding: 0; +} + +/* Override the shared Modal.Header — strip its default 1px + bottom divider so the title sits flush against the body. The + title + description + preview form one cohesive block, and + the divider added visual noise between the two. */ +.headerFlush { + border-bottom: none; +} + +.description { + font-size: 0.9375rem; + line-height: 1.4; + color: var(--text-secondary); + margin: 0; +} + +/* The PinnedMessageRow card already has its own margin/padding; + wrap it in a container that resets the horizontal margin so the + preview spans the modal body cleanly. */ +.previewWrap { + /* Zero out the 12px horizontal margin PinnedMessageRow adds so + the preview card aligns with the modal body padding. */ + margin: 0 -12px; +} + +.actions { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 4px; +} + +.primaryButton { + display: flex; + align-items: center; + justify-content: center; + padding: 12px 16px; + background-color: var(--brand-primary); + border: none; + border-radius: 0.75rem; + color: #fff; + font: inherit; + font-size: 0.9375rem; + font-weight: 700; + cursor: pointer; + transition: filter 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.primaryButton:active:not(:disabled) { + filter: brightness(0.92); +} + +.primaryButton:disabled { + opacity: 0.55; + cursor: default; +} + +/* Danger variant — used by the unpin confirmation. Uses the + global `--button-danger-fill` token so the red matches every + other destructive button in the app (and picks up theme + changes for free). Active state darkens via + `--button-danger-active-fill`. */ +.primaryButtonDanger { + background-color: var(--button-danger-fill); +} + +.primaryButtonDanger:hover:not(:disabled) { + filter: brightness(1.05); +} + +.primaryButtonDanger:active:not(:disabled) { + background-color: var(--button-danger-active-fill); + filter: none; +} + +.secondaryButton { + display: flex; + align-items: center; + justify-content: center; + padding: 12px 16px; + background-color: var(--background-secondary-alt); + border: none; + border-radius: 0.75rem; + color: var(--text-primary); + font: inherit; + font-size: 0.9375rem; + font-weight: 700; + cursor: pointer; + transition: background-color 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.secondaryButton:hover, +.secondaryButton:active { + background-color: var(--background-modifier-hover); +} + +.error { + padding: 10px 14px; + background-color: hsl(0, calc(60% * var(--saturation-factor)), 22%); + color: hsl(0, calc(80% * var(--saturation-factor)), 85%); + border-radius: 0.5rem; + font-size: 0.8125rem; +} diff --git a/packages/shared/src/components/channel/PinConfirmationModal.tsx b/packages/shared/src/components/channel/PinConfirmationModal.tsx new file mode 100644 index 0000000..6178550 --- /dev/null +++ b/packages/shared/src/components/channel/PinConfirmationModal.tsx @@ -0,0 +1,136 @@ +/** + * PinConfirmationModal — confirmation dialog for both the pin and + * unpin flows. Renders a read-only `PinnedMessageRow` preview of the + * target message, a variant-specific headline + description, and a + * primary confirm button (danger-styled for unpin). + * + * - `'pin'` → "Pin it. Pin it good." + blue primary button + * - `'unpin'` → "Unpin Message" + red primary button + * + * Calls `api.messages.setPinned` on confirm. + */ +import { useState } from 'react'; +import { useMutation } from 'convex/react'; +import { Modal } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import { PinnedMessageRow, type PinnedMessage } from './PinnedMessageRow'; +import styles from './PinConfirmationModal.module.css'; + +export type PinConfirmationVariant = 'pin' | 'unpin'; + +interface PinConfirmationModalProps { + isOpen: boolean; + onClose: () => void; + channelId: string; + messageId: string | null; + message: PinnedMessage | null; + variant?: PinConfirmationVariant; +} + +const VARIANT_COPY: Record< + PinConfirmationVariant, + { + title: string; + description: string; + primaryLabel: string; + busyLabel: string; + failedLabel: string; + } +> = { + pin: { + title: 'Pin it. Pin it good.', + description: + "Pin this message to the channel for all to see. Unless you're chicken.", + primaryLabel: 'Pin it real good', + busyLabel: 'Pinning…', + failedLabel: 'Failed to pin message.', + }, + unpin: { + title: 'Unpin Message', + description: 'Do you want to send this pin back in time?', + primaryLabel: 'Unpin it', + busyLabel: 'Unpinning…', + failedLabel: 'Failed to unpin message.', + }, +}; + +export function PinConfirmationModal({ + isOpen, + onClose, + channelId, + messageId, + message, + variant = 'pin', +}: PinConfirmationModalProps) { + const setPinned = useMutation(api.messages.pin); + const [busy, setBusy] = useState(false); + const [error, setError] = useState<string | null>(null); + + const copy = VARIANT_COPY[variant]; + + const handleConfirm = async () => { + if (!messageId || busy) return; + setBusy(true); + setError(null); + try { + await setPinned({ + id: messageId as Id<'messages'>, + pinned: variant === 'pin', + }); + onClose(); + } catch (err: any) { + setError(err?.message || copy.failedLabel); + } finally { + setBusy(false); + } + }; + + // Silence unused import lint — channelId is kept on the props + // surface for future bulk-unpin flows that need scoping. + void channelId; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="small"> + <Modal.Header + title={copy.title} + onClose={onClose} + className={styles.headerFlush} + /> + <Modal.Content> + <div className={styles.body}> + <p className={styles.description}>{copy.description}</p> + + {message && ( + <div className={styles.previewWrap}> + <PinnedMessageRow message={message} /> + </div> + )} + + {error && <div className={styles.error}>{error}</div>} + + <div className={styles.actions}> + <button + type="button" + className={`${styles.primaryButton} ${ + variant === 'unpin' ? styles.primaryButtonDanger : '' + }`} + onClick={handleConfirm} + disabled={busy || !messageId} + > + {busy ? copy.busyLabel : copy.primaryLabel} + </button> + <button + type="button" + className={styles.secondaryButton} + onClick={onClose} + disabled={busy} + > + Cancel + </button> + </div> + </div> + </Modal.Content> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/channel/PinnedMessageRow.module.css b/packages/shared/src/components/channel/PinnedMessageRow.module.css new file mode 100644 index 0000000..c2a9fac --- /dev/null +++ b/packages/shared/src/components/channel/PinnedMessageRow.module.css @@ -0,0 +1,250 @@ +/* Shared card used by the mobile Pins tab, the desktop pins + popover, and the Pin-it-good confirmation modal. Renders a + pinned message with its author, timestamp, text content, and + any image / file attachments inline. Each row is its own + rounded `--background-secondary` surface so the list reads + as a stack of cards (matches the Fluxer reference). */ + +.card { + position: relative; + display: flex; + align-items: flex-start; + gap: 12px; + padding: 12px 14px; + margin: 6px 12px; + background-color: var(--background-secondary); + border: 1px solid var(--background-header-secondary); + border-radius: 0.625rem; + color: var(--text-primary); + font: inherit; + text-align: left; + cursor: pointer; + transition: background-color 0.1s, border-color 0.1s; + -webkit-tap-highlight-color: transparent; + width: auto; +} + +.card:hover { + background-color: var(--background-modifier-hover); + border-color: var(--background-modifier-accent); +} + +.cardStatic { + cursor: default; +} + +.cardStatic:hover { + background-color: var(--background-secondary); + border-color: var(--background-header-secondary); +} + +/* Mobile (≤768px) overrides — the Pins tab lives inside the + ChannelDetailsDrawer, whose own sheet surface already uses + `--background-secondary`, so a card on that same color would + disappear into the background. Flip to + `--background-modifier-hover` (hsla(220, 13%, 100%, 0.05) — a + translucent white wash) so each card visually lifts off the + drawer surface. Desktop keeps the default `--background-secondary` + because the popover uses `--background-primary` as its own + surface and the card stands out against it naturally. */ +@media (max-width: 768px) { + .card { + background-color: var(--background-modifier-hover); + } + + .cardStatic:hover { + background-color: var(--background-modifier-hover); + } + +} + +.avatar { + flex-shrink: 0; + padding-top: 2px; +} + +.body { + min-width: 0; + flex: 1; + display: flex; + flex-direction: column; + gap: 6px; +} + +.meta { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; + flex-wrap: nowrap; +} + +.author { + font-size: 0.9375rem; + font-weight: 700; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.timestamp { + font-size: 0.75rem; + color: var(--text-primary-muted); + font-weight: 500; + flex-shrink: 0; +} + +/* Desktop hover actions — sit at the far right of the meta row + (pushed there via `margin-left: auto`) and fade in on card + hover. Hidden by default so the author + timestamp read + cleanly when the card is idle. */ +.hoverActions { + display: flex; + align-items: center; + gap: 6px; + margin-left: auto; + opacity: 0; + visibility: hidden; + transition: opacity 0.12s ease, visibility 0.12s ease; + flex-shrink: 0; +} + +.card:hover .hoverActions, +.card:focus-within .hoverActions { + opacity: 1; + visibility: visible; +} + +.jumpButton { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 4px 10px; + background-color: var(--background-primary); + border: 1px solid var(--background-header-secondary); + border-radius: 0.375rem; + color: var(--text-primary-muted); + font: inherit; + font-size: 0.75rem; + font-weight: 700; + cursor: pointer; + transition: background-color 0.1s, border-color 0.1s, color 0.1s; + -webkit-tap-highlight-color: transparent; +} + +.jumpButton:hover { + background-color: var(--background-modifier-hover); + border-color: var(--background-modifier-accent); + color: var(--text-primary); +} + +.closeButton { + display: inline-flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + padding: 0; + background-color: var(--background-primary); + border: 1px solid var(--background-header-secondary); + border-radius: 0; + color: var(--text-primary-muted); + cursor: pointer; + transition: background-color 0.1s, color 0.1s, border-color 0.1s; + -webkit-tap-highlight-color: transparent; +} + +.closeButton:hover { + background-color: var(--background-modifier-hover); + color: hsl(350, calc(90% * var(--saturation-factor)), 65%); + border-color: var(--background-modifier-accent); +} + +.content { + font-size: 0.9375rem; + line-height: 1.4; + color: var(--text-primary); + word-break: break-word; +} + +.content :global(p) { + margin: 0; +} + +.undecryptable { + font-style: italic; + color: var(--text-primary-muted); +} + +/* Attachments — mirror the same layout MessageGroup uses, sized + smaller so previews fit comfortably inside the card. */ + +.attachments { + display: flex; + flex-direction: column; + gap: 6px; + margin-top: 2px; +} + +.imageAttachment { + display: block; + max-width: 100%; + max-height: 260px; + border-radius: 0.5rem; + object-fit: contain; + cursor: zoom-in; + background-color: var(--background-secondary); +} + +.fileAttachment { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 12px; + background-color: var(--background-secondary); + border: 1px solid var(--background-header-secondary); + border-radius: 0.5rem; + color: var(--text-link, var(--brand-primary-light)); + font-size: 0.8125rem; + font-weight: 600; + text-decoration: none; + max-width: 100%; + word-break: break-all; +} + +.fileAttachment:hover { + text-decoration: underline; +} + +/* Terminator block shown at the bottom of the pinned list — flag + icon + "You've reached the end" + explanatory body. Also used + as the empty state when there are zero pins. */ + +.reachedEnd { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + padding: 32px 24px 20px; + text-align: center; +} + +.reachedEndIcon { + color: var(--text-primary-muted); + margin-bottom: 4px; +} + +.reachedEndTitle { + font-size: 1.0625rem; + font-weight: 800; + color: var(--text-primary); +} + +.reachedEndBody { + font-size: 0.8125rem; + color: var(--text-primary-muted); + line-height: 1.4; + max-width: 280px; +} diff --git a/packages/shared/src/components/channel/PinnedMessageRow.tsx b/packages/shared/src/components/channel/PinnedMessageRow.tsx new file mode 100644 index 0000000..b1b4568 --- /dev/null +++ b/packages/shared/src/components/channel/PinnedMessageRow.tsx @@ -0,0 +1,178 @@ +/** + * PinnedMessageRow — shared card used by the desktop pins popover, + * the mobile Pins drawer, and the pin confirmation modal. Matches + * the new UI's behaviour: + * + * - `showHoverActions` (desktop) → card is non-clickable; a "Jump" + * button + close X appear on the right side of the meta row and + * fire `onJumpTo` / `onUnpin` respectively. + * - `showHoverActions === false` (mobile / preview) → whole card + * is tappable to `onJumpTo`; no hover buttons. The confirmation + * modal renders with `onJumpTo={undefined}` so the card is a + * read-only preview. + */ +import { Flag, X } from '@phosphor-icons/react'; +import { Avatar } from '@discord-clone/ui'; +import { EncryptedAttachment, type AttachmentMetadata } from './EncryptedAttachment'; +import styles from './PinnedMessageRow.module.css'; + +export interface PinnedMessage { + id: string; + authorName: string; + authorAvatarUrl: string | null; + content: string; + timestamp: number; + attachments?: AttachmentMetadata[]; +} + +interface PinnedMessageRowProps { + message: PinnedMessage; + /** Handler for navigating to the pinned message. */ + onJumpTo?: () => void; + /** Handler for unpinning the message. Only rendered when the + * caller also enables `showHoverActions`. */ + onUnpin?: () => void; + /** + * Desktop layout flag. When true, the card is not clickable and + * the meta row shows a Jump button + close X. When false (mobile + * / static preview), the whole card is the button. + */ + showHoverActions?: boolean; + /** Whether the viewer has permission to unpin. Hides the X when + * they don't, even if `showHoverActions` is on. */ + canUnpin?: boolean; +} + +function formatTimestamp(ts: number): string { + const date = new Date(ts); + const now = new Date(); + const isToday = date.toDateString() === now.toDateString(); + const yesterday = new Date(now); + yesterday.setDate(yesterday.getDate() - 1); + const isYesterday = date.toDateString() === yesterday.toDateString(); + const time = date.toLocaleTimeString([], { + hour: 'numeric', + minute: '2-digit', + }); + if (isToday) return `Today at ${time}`; + if (isYesterday) return `Yesterday at ${time}`; + return `${date.toLocaleDateString()}, ${time}`; +} + +export function PinnedMessageRow({ + message, + onJumpTo, + onUnpin, + showHoverActions = false, + canUnpin = true, +}: PinnedMessageRowProps) { + const isClickable = !showHoverActions && !!onJumpTo; + + const handleCardClick = () => { + if (isClickable && onJumpTo) onJumpTo(); + }; + + const handleJumpClick = (e: React.MouseEvent) => { + e.stopPropagation(); + onJumpTo?.(); + }; + + const handleUnpinClick = (e: React.MouseEvent) => { + e.stopPropagation(); + onUnpin?.(); + }; + + return ( + <div + className={`${styles.card} ${isClickable ? '' : styles.cardStatic}`} + role={isClickable ? 'button' : undefined} + tabIndex={isClickable ? 0 : undefined} + onClick={isClickable ? handleCardClick : undefined} + onKeyDown={ + isClickable + ? (e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + handleCardClick(); + } + } + : undefined + } + > + <div className={styles.avatar}> + <Avatar + src={message.authorAvatarUrl || undefined} + fallback={message.authorName} + size={36} + /> + </div> + <div className={styles.body}> + <div className={styles.meta}> + <span className={styles.author}>{message.authorName}</span> + <span className={styles.timestamp}> + {formatTimestamp(message.timestamp)} + </span> + {showHoverActions && ( + <div className={styles.hoverActions}> + {onJumpTo && ( + <button + type="button" + className={styles.jumpButton} + onClick={handleJumpClick} + > + Jump + </button> + )} + {canUnpin && onUnpin && ( + <button + type="button" + className={styles.closeButton} + onClick={handleUnpinClick} + aria-label="Unpin message" + title="Unpin message" + > + <X size={14} weight="bold" /> + </button> + )} + </div> + )} + </div> + {message.content ? ( + <div className={styles.content}>{message.content}</div> + ) : !message.attachments || message.attachments.length === 0 ? ( + <div className={`${styles.content} ${styles.undecryptable}`}> + (no preview) + </div> + ) : null} + {message.attachments && message.attachments.length > 0 && ( + <div + className={styles.attachments} + onClick={(e) => e.stopPropagation()} + > + {message.attachments.map((att, i) => ( + <EncryptedAttachment + key={`${message.id}-att-${i}`} + metadata={att} + /> + ))} + </div> + )} + </div> + </div> + ); +} + +/** "You've reached the end" terminator used below the list and as + * the empty state. */ +export function ReachedEndNotice() { + return ( + <div className={styles.reachedEnd}> + <Flag size={32} weight="regular" className={styles.reachedEndIcon} /> + <div className={styles.reachedEndTitle}>You've reached the end</div> + <div className={styles.reachedEndBody}> + Members with the "Pin Messages" permission can pin messages for + everyone to see. + </div> + </div> + ); +} diff --git a/packages/shared/src/components/channel/PollCard.module.css b/packages/shared/src/components/channel/PollCard.module.css new file mode 100644 index 0000000..1f8769d --- /dev/null +++ b/packages/shared/src/components/channel/PollCard.module.css @@ -0,0 +1,251 @@ +.card { + display: flex; + flex-direction: column; + gap: 0.625rem; + padding: 0.875rem 1rem; + margin-top: 0.25rem; + border: 1px solid var(--background-modifier-accent); + border-radius: 0.5rem; + background-color: var(--background-secondary); + max-width: 520px; +} + +.question { + font-size: 1rem; + font-weight: 700; + color: var(--text-primary); + word-wrap: break-word; +} + +.meta { + font-size: 0.75rem; + font-weight: 600; + color: var(--text-tertiary); + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.metaEnded { + color: var(--status-warning, #f0b232); +} + +.answers { + display: flex; + flex-direction: column; + gap: 0.375rem; +} + +/* Each answer row is a button so the whole row is tappable. + The fill bar is a pseudo-element that sits behind the label + and grows from 0% to `var(--pct)` based on the tally. */ +.answer { + position: relative; + display: flex; + align-items: center; + gap: 0.625rem; + width: 100%; + padding: 0.625rem 0.75rem; + border: 1px solid var(--background-modifier-accent); + border-radius: 0.375rem; + background-color: transparent; + color: var(--text-primary); + font: inherit; + font-size: 0.875rem; + font-weight: 500; + text-align: left; + cursor: pointer; + overflow: hidden; + transition: background-color 0.12s, border-color 0.12s; +} + +.answer::before { + content: ''; + position: absolute; + inset: 0; + width: var(--pct, 0%); + background-color: var(--brand-primary-muted, rgba(70, 65, 217, 0.15)); + transition: width 0.25s ease; + pointer-events: none; +} + +.answer:hover:not(.answerDisabled) { + border-color: var(--brand-primary, #4641d9); + background-color: var(--background-modifier-hover); +} + +.answer > * { + position: relative; + z-index: 1; +} + +.answerSelected { + border-color: var(--brand-primary, #4641d9); +} + +.answerSelected::before { + background-color: var(--brand-primary-faded, rgba(70, 65, 217, 0.22)); +} + +.answerDisabled { + cursor: default; +} + +.radio { + flex-shrink: 0; + width: 1.125rem; + height: 1.125rem; + border-radius: 50%; + border: 2px solid var(--background-modifier-accent); + background-color: var(--background-primary); +} + +.radioCheckbox { + border-radius: 0.25rem; +} + +.radioChecked { + border-color: var(--brand-primary, #4641d9); + background-color: var(--brand-primary, #4641d9); + box-shadow: inset 0 0 0 3px var(--background-primary); +} + +.label { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.count { + flex-shrink: 0; + font-size: 0.75rem; + font-weight: 600; + color: var(--text-tertiary); +} + +.totalRow { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + margin-top: 0.125rem; + font-size: 0.75rem; + color: var(--text-tertiary); +} + +.endButton { + padding: 0.25rem 0.625rem; + border: 1px solid var(--background-modifier-accent); + border-radius: 0.25rem; + background-color: transparent; + color: var(--text-tertiary); + font: inherit; + font-size: 0.75rem; + font-weight: 600; + cursor: pointer; + transition: border-color 0.12s, color 0.12s, background-color 0.12s; +} + +.endButton:hover { + color: var(--text-primary); + border-color: var(--brand-primary, #4641d9); + background-color: var(--background-modifier-hover); +} + +.endModalBody { + display: flex; + flex-direction: column; + gap: 0.875rem; + padding: 0.25rem 0.25rem 0.5rem; +} + +.endModalDescription { + margin: 0; + font-size: 0.875rem; + line-height: 1.45; + color: var(--text-secondary, var(--text-primary)); +} + +.endModalQuestion { + padding: 0.625rem 0.75rem; + border: 1px solid var(--background-modifier-accent); + border-radius: 0.375rem; + background-color: var(--background-secondary); + font-size: 0.875rem; + font-weight: 600; + color: var(--text-primary); + word-wrap: break-word; +} + +.endModalError { + color: var(--status-danger); + font-size: 0.8125rem; +} + +.endModalActions { + display: flex; + justify-content: flex-end; + gap: 0.5rem; +} + +/* ── Reaction row ────────────────────────────────────────────── + Matches MessageGroup's reaction chips so polls and messages + feel visually unified. */ +.reactions { + display: flex; + flex-wrap: wrap; + gap: 0.25rem; + margin-top: 0.25rem; +} + +.reactionChip { + display: inline-flex; + align-items: center; + gap: 0.25rem; + height: 1.5rem; + padding: 0 0.5rem; + border: 1px solid var(--background-modifier-accent); + border-radius: 0.5rem; + background-color: var(--background-tertiary); + color: var(--text-primary); + font: inherit; + font-size: 0.75rem; + font-weight: 600; + cursor: pointer; + transition: background-color 0.12s, border-color 0.12s; +} + +.reactionChip:hover { + background-color: var(--background-modifier-hover); +} + +.reactionMe { + border-color: var(--brand-primary, #4641d9); + background-color: var(--brand-primary-faded, rgba(70, 65, 217, 0.22)); +} + +.reactionCount { + color: var(--text-secondary); +} + +.addReactionChip { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.75rem; + height: 1.5rem; + padding: 0; + border: 1px dashed var(--background-modifier-accent); + border-radius: 0.5rem; + background-color: transparent; + color: var(--text-tertiary); + cursor: pointer; + transition: color 0.12s, border-color 0.12s, background-color 0.12s; +} + +.addReactionChip:hover { + color: var(--text-primary); + border-color: var(--brand-primary, #4641d9); + background-color: var(--background-modifier-hover); +} diff --git a/packages/shared/src/components/channel/PollCard.tsx b/packages/shared/src/components/channel/PollCard.tsx new file mode 100644 index 0000000..d69fef2 --- /dev/null +++ b/packages/shared/src/components/channel/PollCard.tsx @@ -0,0 +1,349 @@ +/** + * PollCard — renders a Convex-backed poll inline in the message + * timeline. Single- and multi-selection polls are distinguished by + * `poll.allowMultiple` (radio vs checkbox). Disclosed polls show the + * live tally; undisclosed polls hide counts until the poll is closed. + * + * Clicks send (or clear) the viewer's vote via `api.polls.vote` / + * `api.polls.clearVote`. Because the parent `useQuery(api.polls.get)` + * is reactive, the tally updates live for every viewer whenever + * anyone votes. + */ +import { useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { useMutation, useQuery } from 'convex/react'; +import { Smiley } from '@phosphor-icons/react'; +import { Modal, Button } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import { EmojiPicker, type EmojiPickerValue } from './EmojiPicker'; +import { TwemojiImg } from './TwemojiImg'; +import { resolveReactionKeyToUnicode } from '../../utils/emojiLookup'; +import styles from './PollCard.module.css'; + +interface PollCardProps { + pollId: Id<'polls'>; +} + +export function PollCard({ pollId }: PollCardProps) { + const myUserId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + + const result = useQuery(api.polls.get, { + pollId, + userId: (myUserId ?? undefined) as Id<'userProfiles'> | undefined, + }); + + const voteMutation = useMutation(api.polls.vote); + const clearVoteMutation = useMutation(api.polls.clearVote); + const closeMutation = useMutation(api.polls.close); + const addReactionMutation = useMutation(api.polls.addReaction); + const removeReactionMutation = useMutation(api.polls.removeReaction); + + // Reaction picker — anchored off the Add Reaction button. `null` + // means the picker is closed. + const addReactionButtonRef = useRef<HTMLButtonElement | null>(null); + const [reactPickerPos, setReactPickerPos] = useState< + { top: number; left: number } | null + >(null); + + const [endConfirmOpen, setEndConfirmOpen] = useState(false); + const [ending, setEnding] = useState(false); + const [endError, setEndError] = useState<string | null>(null); + + if (!result) { + return <div className={styles.card}>Loading poll…</div>; + } + + const { poll, totals, totalVotes, myVote, reactions } = result; + const isEnded = poll.closed || (poll.closesAt != null && poll.closesAt < Date.now()); + const isMultiple = poll.allowMultiple; + const countsVisible = poll.disclosed || isEnded; + + const mySelectionSet = new Set<string>(myVote ?? []); + + const handleToggleReaction = (emoji: string, me: boolean) => { + if (!myUserId) return; + if (me) { + void removeReactionMutation({ + pollId: poll._id, + userId: myUserId as Id<'userProfiles'>, + emoji, + }); + } else { + void addReactionMutation({ + pollId: poll._id, + userId: myUserId as Id<'userProfiles'>, + emoji, + }); + } + }; + + const openReactPicker = () => { + const btn = addReactionButtonRef.current; + if (!btn) return; + const rect = btn.getBoundingClientRect(); + setReactPickerPos({ + // Anchor the picker so its bottom edge sits just above the + // button — matches the message reaction picker placement. + top: Math.max(8, rect.top - 440), + left: Math.max(8, rect.left - 240), + }); + }; + + const handlePickReaction = (value: EmojiPickerValue) => { + if (!myUserId) return; + // GIF picks are meaningless as reactions — silently drop them. + if (value.kind === 'gif') { + setReactPickerPos(null); + return; + } + const emojiKey = + value.kind === 'custom' ? value.shortcode : value.surrogates; + // If the viewer already reacted with this emoji, toggle it off + // instead of inserting a duplicate row (mutation is idempotent + // either way, but this matches chat behaviour). + const already = reactions?.some((r) => r.emoji === emojiKey && r.me); + if (already) { + void removeReactionMutation({ + pollId: poll._id, + userId: myUserId as Id<'userProfiles'>, + emoji: emojiKey, + }); + } else { + void addReactionMutation({ + pollId: poll._id, + userId: myUserId as Id<'userProfiles'>, + emoji: emojiKey, + }); + } + setReactPickerPos(null); + }; + + const handleClick = (optionId: string) => { + if (isEnded) return; + if (!myUserId) return; + + let next: string[]; + if (isMultiple) { + const current = new Set(mySelectionSet); + if (current.has(optionId)) current.delete(optionId); + else current.add(optionId); + next = Array.from(current); + } else { + next = mySelectionSet.has(optionId) ? [] : [optionId]; + } + + if (next.length === 0) { + void clearVoteMutation({ + pollId: poll._id, + userId: myUserId as Id<'userProfiles'>, + }); + } else { + void voteMutation({ + pollId: poll._id, + userId: myUserId as Id<'userProfiles'>, + optionIds: next, + }); + } + }; + + // Bars scale to the leading answer so the winner reaches 100% and + // the rest scale proportionally — matches the new UI's visuals. + let maxCount = 0; + for (const opt of poll.options) { + const c = totals[opt.id] ?? 0; + if (c > maxCount) maxCount = c; + } + + return ( + <div className={styles.card}> + <div className={styles.question}>{poll.question || 'Poll'}</div> + + {!countsVisible && !isEnded && ( + <div className={styles.meta}>Results hidden until the poll ends</div> + )} + {isEnded && ( + <div className={`${styles.meta} ${styles.metaEnded}`}>Poll ended</div> + )} + + <div className={styles.answers}> + {poll.options.map((option) => { + const count = totals[option.id] ?? 0; + const selected = mySelectionSet.has(option.id); + const pct = !countsVisible + ? 0 + : maxCount === 0 + ? 0 + : (count / maxCount) * 100; + return ( + <button + type="button" + key={option.id} + className={[ + styles.answer, + selected ? styles.answerSelected : '', + isEnded ? styles.answerDisabled : '', + ] + .filter(Boolean) + .join(' ')} + style={{ ['--pct' as any]: `${pct}%` }} + onClick={() => handleClick(option.id)} + disabled={isEnded} + aria-pressed={selected} + > + <span + className={[ + styles.radio, + isMultiple ? styles.radioCheckbox : '', + selected ? styles.radioChecked : '', + ] + .filter(Boolean) + .join(' ')} + /> + <span className={styles.label}>{option.text}</span> + {countsVisible && ( + <span className={styles.count}> + {count} {count === 1 ? 'vote' : 'votes'} + </span> + )} + </button> + ); + })} + </div> + + <div className={styles.totalRow}> + <span> + {totalVotes} {totalVotes === 1 ? 'voter' : 'voters'} + {isMultiple ? ' · multi-select' : ''} + </span> + {myUserId === poll.createdBy && !isEnded && ( + <button + type="button" + className={styles.endButton} + onClick={() => { + setEndError(null); + setEndConfirmOpen(true); + }} + > + End Poll + </button> + )} + </div> + + {(reactions.length > 0 || myUserId) && ( + <div className={styles.reactions}> + {reactions.map((r) => ( + <button + key={r.emoji} + type="button" + className={`${styles.reactionChip} ${r.me ? styles.reactionMe : ''}`} + onClick={() => handleToggleReaction(r.emoji, r.me)} + > + <TwemojiImg + emoji={resolveReactionKeyToUnicode(r.emoji)} + size={16} + /> + <span className={styles.reactionCount}>{r.count}</span> + </button> + ))} + {myUserId && ( + <button + ref={addReactionButtonRef} + type="button" + className={styles.addReactionChip} + onClick={openReactPicker} + aria-label="Add reaction" + title="Add reaction" + > + <Smiley size={14} weight="regular" /> + </button> + )} + </div> + )} + + {reactPickerPos && + createPortal( + <div + style={{ + position: 'fixed', + top: reactPickerPos.top, + left: reactPickerPos.left, + zIndex: 15000, + }} + onClick={(e) => e.stopPropagation()} + > + <EmojiPicker + onSelect={handlePickReaction} + onClose={() => setReactPickerPos(null)} + /> + </div>, + document.body, + )} + + <Modal.Root + isOpen={endConfirmOpen} + onClose={() => { + if (ending) return; + setEndConfirmOpen(false); + }} + size="small" + > + <Modal.Header + title="End poll?" + onClose={() => { + if (ending) return; + setEndConfirmOpen(false); + }} + /> + <Modal.Content> + <div className={styles.endModalBody}> + <p className={styles.endModalDescription}> + Voting will be locked and the results will be finalised. + {!poll.disclosed && ( + <> Hidden vote counts will become visible to everyone.</> + )} + </p> + <div className={styles.endModalQuestion}> + {poll.question || 'Poll'} + </div> + {endError && <div className={styles.endModalError}>{endError}</div>} + <div className={styles.endModalActions}> + <Button + variant="secondary" + size="sm" + onClick={() => setEndConfirmOpen(false)} + disabled={ending} + > + Cancel + </Button> + <Button + variant="danger" + size="sm" + loading={ending} + onClick={async () => { + if (!myUserId) return; + setEnding(true); + setEndError(null); + try { + await closeMutation({ + pollId: poll._id, + userId: myUserId as Id<'userProfiles'>, + }); + setEndConfirmOpen(false); + } catch (err: any) { + setEndError(err?.message || 'Failed to end the poll.'); + } finally { + setEnding(false); + } + }} + > + End Poll + </Button> + </div> + </div> + </Modal.Content> + </Modal.Root> + </div> + ); +} diff --git a/packages/shared/src/components/channel/ReactionsModal.module.css b/packages/shared/src/components/channel/ReactionsModal.module.css new file mode 100644 index 0000000..781e4d2 --- /dev/null +++ b/packages/shared/src/components/channel/ReactionsModal.module.css @@ -0,0 +1,128 @@ +.body { + display: flex; + gap: 12px; + min-height: 320px; + max-height: 480px; + padding: 4px 4px 16px; +} + +/* ── Emoji column ───────────────────────────────────────────── */ + +.emojiColumn { + display: flex; + flex-direction: column; + gap: 4px; + min-width: 96px; + flex-shrink: 0; + overflow-y: auto; + padding-right: 4px; + scrollbar-width: thin; +} + +.emojiChip { + display: flex; + align-items: center; + gap: 6px; + padding: 6px 10px; + border: 1px solid transparent; + border-radius: 8px; + background-color: var(--background-tertiary); + color: var(--text-primary); + font: inherit; + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + transition: background-color 0.1s, border-color 0.1s; +} + +.emojiChip:hover { + background-color: var(--background-modifier-hover); +} + +.emojiChipActive { + background-color: var(--background-modifier-selected, var(--background-modifier-hover)); + border-color: var(--brand-primary, #5865f2); +} + +.emojiChipImage { + width: 20px; + height: 20px; + object-fit: contain; +} + +.emojiCount { + color: var(--text-secondary); +} + +/* ── Users column ───────────────────────────────────────────── */ + +.usersColumn { + flex: 1; + min-width: 0; + padding: 8px; + border-radius: 8px; + background-color: var(--background-tertiary); + overflow-y: auto; + scrollbar-width: thin; +} + +.userRow { + display: flex; + align-items: center; + gap: 10px; + padding: 6px 8px; + border-radius: 6px; + transition: background-color 0.1s; +} + +.userRow:hover { + background-color: var(--background-modifier-hover); +} + +.userAvatar { + flex-shrink: 0; +} + +.userMeta { + display: flex; + align-items: baseline; + gap: 6px; + min-width: 0; + flex: 1; +} + +.userDisplayName { + font-size: 0.9375rem; + font-weight: 600; + color: var(--text-primary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.userTag { + font-size: 0.75rem; + color: var(--text-tertiary); + white-space: nowrap; +} + +.removeButton { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + padding: 0; + border: none; + border-radius: 4px; + background: transparent; + color: var(--text-tertiary); + cursor: pointer; + transition: background-color 0.1s, color 0.1s; + flex-shrink: 0; +} + +.removeButton:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} diff --git a/packages/shared/src/components/channel/ReactionsModal.tsx b/packages/shared/src/components/channel/ReactionsModal.tsx new file mode 100644 index 0000000..3a7086f --- /dev/null +++ b/packages/shared/src/components/channel/ReactionsModal.tsx @@ -0,0 +1,139 @@ +/** + * ReactionsModal — full breakdown of every reaction on a single + * message. Matches the Discord "Reactions" popup: an emoji chip + * column on the left, the user list for the selected emoji on the + * right. Clicking the X next to your own row removes that reaction; + * other users' rows render the close affordance as `null` so the + * column only lets you manage your own state. + */ +import { useState } from 'react'; +import { X } from '@phosphor-icons/react'; +import { Avatar, Modal } from '@discord-clone/ui'; +import type { ReactionUser } from './Messages'; +import { TwemojiImg } from './TwemojiImg'; +import { resolveReactionKeyToUnicode } from '../../utils/emojiLookup'; +import styles from './ReactionsModal.module.css'; + +export interface ReactionsModalReaction { + emoji: string; + count: number; + me: boolean; + users: ReactionUser[]; +} + +interface ReactionsModalProps { + isOpen: boolean; + onClose: () => void; + reactions: ReactionsModalReaction[]; + myUserId: string | null; + customEmojiByName: Map<string, string>; + /** + * Called when the viewer clicks the X next to their own row — the + * parent is responsible for wiring `api.reactions.remove`. The + * modal trusts the parent and optimistically just closes if this + * was the viewer's last remaining reaction. + */ + onRemoveOwnReaction: (emoji: string) => void; +} + +export function ReactionsModal({ + isOpen, + onClose, + reactions, + myUserId, + customEmojiByName, + onRemoveOwnReaction, +}: ReactionsModalProps) { + // Which emoji tab is active — defaults to the first reaction. + // Resets whenever the set of reactions changes (e.g. after removal). + const [activeEmoji, setActiveEmoji] = useState<string | null>(null); + const effectiveEmoji = + activeEmoji && reactions.some((r) => r.emoji === activeEmoji) + ? activeEmoji + : reactions[0]?.emoji ?? null; + + const activeReaction = reactions.find((r) => r.emoji === effectiveEmoji); + + const renderEmojiGlyph = (emojiKey: string) => { + const customUrl = /^[a-zA-Z0-9_]+$/.test(emojiKey) + ? customEmojiByName.get(emojiKey.toLowerCase()) + : undefined; + if (customUrl) { + return ( + <img + src={customUrl} + alt={`:${emojiKey}:`} + className={styles.emojiChipImage} + draggable={false} + /> + ); + } + return ( + <TwemojiImg + emoji={resolveReactionKeyToUnicode(emojiKey)} + size={20} + /> + ); + }; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="medium"> + <Modal.Header title="Reactions" onClose={onClose} /> + <Modal.Content> + <div className={styles.body}> + <div className={styles.emojiColumn}> + {reactions.map((r) => { + const isActive = effectiveEmoji === r.emoji; + return ( + <button + key={r.emoji} + type="button" + className={`${styles.emojiChip} ${isActive ? styles.emojiChipActive : ''}`} + onClick={() => setActiveEmoji(r.emoji)} + > + {renderEmojiGlyph(r.emoji)} + <span className={styles.emojiCount}>{r.count}</span> + </button> + ); + })} + </div> + <div className={styles.usersColumn}> + {activeReaction?.users.map((u) => { + const isMe = myUserId !== null && u.userId === myUserId; + return ( + <div key={u.userId} className={styles.userRow}> + <div className={styles.userAvatar}> + <Avatar + src={undefined} + fallback={u.displayName || u.username} + size={32} + /> + </div> + <div className={styles.userMeta}> + <span className={styles.userDisplayName}> + {u.displayName || u.username} + </span> + <span className={styles.userTag}>{u.username}</span> + </div> + {isMe && ( + <button + type="button" + className={styles.removeButton} + onClick={() => + onRemoveOwnReaction(activeReaction.emoji) + } + aria-label="Remove your reaction" + title="Remove your reaction" + > + <X size={14} weight="bold" /> + </button> + )} + </div> + ); + })} + </div> + </div> + </Modal.Content> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/channel/ReplyBar.module.css b/packages/shared/src/components/channel/ReplyBar.module.css new file mode 100644 index 0000000..a7b0360 --- /dev/null +++ b/packages/shared/src/components/channel/ReplyBar.module.css @@ -0,0 +1,35 @@ +.container { + display: flex; + align-items: center; + justify-content: space-between; + padding: 8px 12px; + background-color: var(--background-secondary); + border-radius: var(--radius-lg) var(--radius-lg) 0 0; + border-bottom: 2px solid var(--brand-primary); +} + +.text { + font-size: 0.875rem; + color: var(--text-secondary); +} + +.text strong { + color: var(--text-primary); +} + +.cancel { + display: flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + background: none; + border: none; + cursor: pointer; + color: var(--interactive-normal); + border-radius: var(--radius-sm); +} + +.cancel:hover { + color: var(--interactive-hover); +} diff --git a/packages/shared/src/components/channel/ReplyBar.tsx b/packages/shared/src/components/channel/ReplyBar.tsx new file mode 100644 index 0000000..b47a66b --- /dev/null +++ b/packages/shared/src/components/channel/ReplyBar.tsx @@ -0,0 +1,20 @@ +import { X } from '@phosphor-icons/react'; +import styles from './ReplyBar.module.css'; + +interface ReplyBarProps { + replyingTo: string; // username + onCancel: () => void; +} + +export function ReplyBar({ replyingTo, onCancel }: ReplyBarProps) { + return ( + <div className={styles.container}> + <span className={styles.text}> + Replying to <strong>{replyingTo}</strong> + </span> + <button className={styles.cancel} onClick={onCancel}> + <X size={16} weight="bold" /> + </button> + </div> + ); +} diff --git a/packages/shared/src/components/channel/SaveMediaModal.module.css b/packages/shared/src/components/channel/SaveMediaModal.module.css new file mode 100644 index 0000000..1e33079 --- /dev/null +++ b/packages/shared/src/components/channel/SaveMediaModal.module.css @@ -0,0 +1,232 @@ +/* ── Save Media modal body ──────────────────────────────────────── + Shell chrome (backdrop, centring, header bar) comes from the + shared `Modal` primitive; this module only styles the form + controls inside `Modal.Content`. */ + +.body { + display: flex; + flex-direction: column; + gap: 16px; + padding: 0; +} + +.field { + display: flex; + flex-direction: column; + gap: 6px; +} + +.label { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--text-primary-muted, #a0a3a8); +} + +.input { + height: 40px; + padding: 0 12px; + background-color: var(--form-surface-background, #1e2024); + border: 1px solid var(--background-modifier-accent); + border-radius: 6px; + color: var(--text-primary); + font: inherit; + font-size: 0.9375rem; + outline: none; + transition: border-color 0.12s; +} + +.input:focus, +.input:focus-visible { + outline: none; + border-color: var(--brand-primary, #4641d9); +} + +.input::placeholder { + color: var(--text-tertiary); +} + +.input:disabled { + opacity: 0.55; + cursor: not-allowed; +} + +.textarea { + min-height: 80px; + padding: 10px 12px; + background-color: var(--form-surface-background, #1e2024); + border: 1px solid var(--background-modifier-accent); + border-radius: 6px; + color: var(--text-primary); + font: inherit; + font-size: 0.9375rem; + resize: vertical; + outline: none; + transition: border-color 0.12s; +} + +.textarea:focus, +.textarea:focus-visible { + outline: none; + border-color: var(--brand-primary, #4641d9); +} + +.textarea::placeholder { + color: var(--text-tertiary); +} + +/* ── Tags ────────────────────────────────────────────────────── */ + +.tagChips { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.tagChip { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 8px; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.04)); + border: 1px solid var(--background-modifier-accent); + border-radius: 999px; + font-size: 0.8125rem; + color: var(--text-primary); +} + +.tagChipText { + max-width: 140px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.tagChipRemove { + display: inline-flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + padding: 0; + background: transparent; + border: none; + border-radius: 50%; + color: var(--text-primary-muted, #a0a3a8); + cursor: pointer; + transition: background-color 0.1s, color 0.1s; +} + +.tagChipRemove:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +/* Input + Add button row */ +.tagRow { + display: flex; + align-items: center; + gap: 8px; +} + +.tagRow .input { + flex: 1; +} + +.addButton { + height: 40px; + padding: 0 16px; + background-color: var(--brand-primary, #4641d9); + border: none; + border-radius: 6px; + color: #fff; + font: inherit; + font-size: 0.875rem; + font-weight: 700; + cursor: pointer; + transition: filter 0.12s; + flex-shrink: 0; +} + +.addButton:hover:not(:disabled) { + filter: brightness(1.08); +} + +.addButton:active:not(:disabled) { + filter: brightness(0.92); +} + +.addButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* ── Footer actions ──────────────────────────────────────────── */ + +.actions { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 8px; + margin-top: 4px; +} + +.secondaryButton { + height: 40px; + padding: 0 18px; + background: transparent; + border: none; + border-radius: 6px; + color: var(--text-primary); + font: inherit; + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + transition: background-color 0.12s; +} + +.secondaryButton:hover:not(:disabled) { + background-color: var(--background-modifier-hover); +} + +.secondaryButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.primaryButton { + height: 40px; + padding: 0 20px; + background-color: var(--brand-primary, #4641d9); + border: none; + border-radius: 6px; + color: #fff; + font: inherit; + font-size: 0.875rem; + font-weight: 700; + cursor: pointer; + transition: filter 0.12s; +} + +.primaryButton:hover:not(:disabled) { + filter: brightness(1.08); +} + +.primaryButton:active:not(:disabled) { + filter: brightness(0.92); +} + +.primaryButton:disabled { + opacity: 0.55; + cursor: not-allowed; +} + +/* Inline save error row */ +.error { + padding: 10px 14px; + background-color: hsl(0, calc(60% * var(--saturation-factor, 1)), 22%); + color: hsl(0, calc(80% * var(--saturation-factor, 1)), 85%); + border-radius: 6px; + font-size: 0.8125rem; +} diff --git a/packages/shared/src/components/channel/SaveMediaModal.tsx b/packages/shared/src/components/channel/SaveMediaModal.tsx new file mode 100644 index 0000000..da9875a --- /dev/null +++ b/packages/shared/src/components/channel/SaveMediaModal.tsx @@ -0,0 +1,245 @@ +/** + * SaveMediaModal — "Add to Saved Media" dialog opened from the star + * button in ImageLightbox. Collects a user-editable name, alt text, + * and up to 10 tags, then writes the item through SavedMediaManager + * into `io.brycord.saved_media` account data. + * + * Name / alt text / tag limits match Fluxer's equivalent modal. On + * successful save we also push the fresh snapshot into SavedMediaStore + * optimistically so the EmojiPicker Media tab and the ImageLightbox + * star both reflect the new state instantly — the accountData + * listener in MatrixActions will fire on the next sync tick as an + * idempotent no-op. + * + * Save failures (homeserver rejection, network blip, account-data + * size limit) surface inline under the body via the `.error` row + * and keep the modal open so the user can retry. + */ +import { useEffect, useState } from 'react'; +import { Modal } from '@brycord/ui'; +import { X } from '@phosphor-icons/react'; +import { + SavedMediaManager, + classifyMediaKind, +} from '@brycord/matrix-client'; +import type { SavedMediaItem } from '@brycord/matrix-client'; +import SavedMediaStore from '@app/stores/SavedMediaStore'; +import styles from './SaveMediaModal.module.css'; + +interface SaveMediaModalProps { + isOpen: boolean; + onClose: () => void; + /** Raw mxc:// URL of the asset being saved. Dedupe key. */ + mxcUrl: string; + /** Filename the media was originally uploaded with. Used as the + * default value of the Name field and preserved on the item. */ + originalFilename: string; + /** MIME type captured from the Attachment — drives `kind`. */ + contentType?: string; + width?: number; + height?: number; + /** Fired after a successful save so callers can flip their + * local "is saved" state (e.g. the ImageLightbox star). */ + onSaved?: (item: SavedMediaItem) => void; +} + +const NAME_MAX = 120; +const ALT_MAX = 320; +const TAGS_MAX = 10; +const TAG_MAX_LENGTH = 32; + +export function SaveMediaModal({ + isOpen, + onClose, + mxcUrl, + originalFilename, + contentType, + width, + height, + onSaved, +}: SaveMediaModalProps) { + const [name, setName] = useState(originalFilename); + const [altText, setAltText] = useState(''); + const [tags, setTags] = useState<string[]>([]); + const [tagDraft, setTagDraft] = useState(''); + const [error, setError] = useState<string | null>(null); + const [saving, setSaving] = useState(false); + + // Reset every time the modal opens on a new asset. Without this + // a stale tag draft from a previous save would bleed into the + // next favorite action. + useEffect(() => { + if (!isOpen) return; + const existing = SavedMediaStore.getByMxc(mxcUrl); + setName(existing?.name ?? originalFilename); + setAltText(existing?.altText ?? ''); + setTags(existing?.tags ?? []); + setTagDraft(''); + setError(null); + setSaving(false); + }, [isOpen, mxcUrl, originalFilename]); + + const canSave = name.trim().length > 0 && !saving; + + const addTag = () => { + const raw = tagDraft.trim(); + if (!raw) return; + if (tags.length >= TAGS_MAX) return; + const tag = raw.slice(0, TAG_MAX_LENGTH); + // Drop duplicates (case-insensitive) so re-entering the + // same tag doesn't create visual dupes. + if (tags.some((t) => t.toLowerCase() === tag.toLowerCase())) { + setTagDraft(''); + return; + } + setTags([...tags, tag]); + setTagDraft(''); + }; + + const removeTag = (tag: string) => { + setTags(tags.filter((t) => t !== tag)); + }; + + const handleTagKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { + if (e.key === 'Enter') { + e.preventDefault(); + addTag(); + } + }; + + const handleSave = async () => { + if (!canSave) return; + setSaving(true); + setError(null); + try { + const item = await SavedMediaManager.getInstance().saveItem({ + mxcUrl, + kind: classifyMediaKind(contentType), + name: name.trim().slice(0, NAME_MAX), + altText: altText.trim() ? altText.trim().slice(0, ALT_MAX) : undefined, + tags, + originalFilename, + contentType, + width, + height, + }); + // Optimistic store push — the accountData listener will + // also fire on the next sync tick with the same content, + // which is an idempotent no-op. + SavedMediaStore.handleDataLoaded( + SavedMediaManager.getInstance().getData(), + ); + onSaved?.(item); + onClose(); + } catch (err: any) { + setError(err?.message || 'Failed to save media.'); + setSaving(false); + } + }; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="small" zIndex={17000}> + <Modal.Header title="Add to Saved Media" onClose={onClose} /> + <Modal.Content> + <div className={styles.body}> + <div className={styles.field}> + <label className={styles.label} htmlFor="save-media-name"> + Name + </label> + <input + id="save-media-name" + type="text" + className={styles.input} + value={name} + onChange={(e) => setName(e.target.value.slice(0, NAME_MAX))} + maxLength={NAME_MAX} + autoFocus + /> + </div> + + <div className={styles.field}> + <label className={styles.label} htmlFor="save-media-alt"> + Alt Text + </label> + <textarea + id="save-media-alt" + className={styles.textarea} + placeholder="Describe the media" + value={altText} + onChange={(e) => setAltText(e.target.value.slice(0, ALT_MAX))} + maxLength={ALT_MAX} + rows={3} + /> + </div> + + <div className={styles.field}> + <label className={styles.label} htmlFor="save-media-tag"> + Tags ({tags.length}/{TAGS_MAX}) + </label> + {tags.length > 0 && ( + <div className={styles.tagChips}> + {tags.map((tag) => ( + <span key={tag} className={styles.tagChip}> + <span className={styles.tagChipText}>{tag}</span> + <button + type="button" + className={styles.tagChipRemove} + onClick={() => removeTag(tag)} + aria-label={`Remove tag ${tag}`} + > + <X size={12} weight="bold" /> + </button> + </span> + ))} + </div> + )} + <div className={styles.tagRow}> + <input + id="save-media-tag" + type="text" + className={styles.input} + placeholder="Add a tag" + value={tagDraft} + onChange={(e) => setTagDraft(e.target.value.slice(0, TAG_MAX_LENGTH))} + onKeyDown={handleTagKeyDown} + maxLength={TAG_MAX_LENGTH} + disabled={tags.length >= TAGS_MAX} + /> + <button + type="button" + className={styles.addButton} + onClick={addTag} + disabled={ + tagDraft.trim().length === 0 || tags.length >= TAGS_MAX + } + > + Add + </button> + </div> + </div> + + {error && <div className={styles.error}>{error}</div>} + + <div className={styles.actions}> + <button + type="button" + className={styles.secondaryButton} + onClick={onClose} + disabled={saving} + > + Cancel + </button> + <button + type="button" + className={styles.primaryButton} + onClick={handleSave} + disabled={!canSave} + > + {saving ? 'Saving…' : 'Save'} + </button> + </div> + </div> + </Modal.Content> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/channel/SearchDrawer.module.css b/packages/shared/src/components/channel/SearchDrawer.module.css new file mode 100644 index 0000000..dc0b591 --- /dev/null +++ b/packages/shared/src/components/channel/SearchDrawer.module.css @@ -0,0 +1,289 @@ +.body { + display: flex; + flex-direction: column; + gap: 12px; + padding: 16px 20px 20px; + min-height: 360px; +} + +.searchInputWrapper { + display: flex; + align-items: center; + gap: 8px; + padding: 0 12px; + height: 40px; + border-radius: 12px; + background-color: var(--background-tertiary, rgba(255, 255, 255, 0.04)); + border: 1px solid var(--user-area-divider-color, rgba(255, 255, 255, 0.08)); +} + +.searchInputIcon { + color: var(--text-tertiary); + flex-shrink: 0; +} + +.searchInput { + flex: 1; + min-width: 0; + background: transparent; + border: none; + outline: none; + color: var(--text-primary); + font: inherit; + font-size: 0.9375rem; +} + +.searchInput::placeholder { + color: var(--text-tertiary); +} + +.searchClearButton { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + background: transparent; + border: none; + border-radius: 50%; + color: var(--text-secondary); + cursor: pointer; +} + +.searchClearButton:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.08)); + color: var(--text-primary); +} + +.chipRow { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.chip { + padding: 6px 12px; + border-radius: 9999px; + background-color: var(--background-tertiary, rgba(255, 255, 255, 0.04)); + border: 1px solid var(--user-area-divider-color, rgba(255, 255, 255, 0.08)); + color: var(--text-primary); + font: inherit; + font-size: 0.8125rem; + font-weight: 500; + cursor: pointer; + transition: background-color 0.15s; +} + +.chip:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.08)); +} + +.searchButton { + display: flex; + align-items: center; + justify-content: center; + height: 44px; + margin-top: 4px; + padding: 0 16px; + background-color: var(--brand-primary, #5865f2); + color: #ffffff; + border: none; + border-radius: 12px; + font: inherit; + font-size: 0.9375rem; + font-weight: 600; + cursor: pointer; + transition: filter 0.15s; +} + +.searchButton:hover:not(:disabled) { + filter: brightness(1.1); +} + +.searchButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.results { + display: flex; + flex-direction: column; + min-height: 200px; +} + +.placeholder { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + padding: 40px 16px; + color: var(--text-tertiary); + text-align: center; +} + +.placeholderTitle { + font-size: 1rem; + font-weight: 600; + color: var(--text-primary); + margin-top: 4px; +} + +.placeholderSubtitle { + font-size: 0.8125rem; + color: var(--text-tertiary); +} + +/* ── Mobile search result cards ──────────────────────────────────────── */ + +.resultsList { + display: flex; + flex-direction: column; + gap: 16px; + padding-top: 4px; +} + +.resultsCount { + text-align: center; + font-size: 0.875rem; + color: var(--text-primary); + font-weight: 500; + padding: 4px 0 8px; +} + +.resultsGroup { + display: flex; + flex-direction: column; + gap: 8px; +} + +.resultsGroupHeader { + display: flex; + align-items: center; + gap: 6px; + color: var(--text-primary); + font-size: 0.9375rem; + font-weight: 600; +} + +.resultsGroupHeader svg { + color: var(--text-secondary); + flex-shrink: 0; +} + +.resultsGroupBody { + display: flex; + flex-direction: column; + gap: 8px; +} + +.resultCard { + display: flex; + gap: 12px; + padding: 12px; + background-color: var(--background-tertiary, rgba(255, 255, 255, 0.04)); + border: none; + border-radius: 12px; + color: var(--text-primary); + text-align: left; + cursor: pointer; + font: inherit; + transition: background-color 0.15s ease; +} + +.resultCard:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.08)); +} + +.resultCardAvatar { + flex-shrink: 0; + line-height: 0; +} + +.resultCardBody { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 2px; +} + +.resultCardHeader { + display: flex; + align-items: baseline; + gap: 8px; + min-width: 0; +} + +.resultCardAuthor { + font-size: 0.9375rem; + font-weight: 600; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.resultCardTimestamp { + font-size: 0.75rem; + color: var(--text-tertiary); + font-weight: 400; + flex-shrink: 0; + white-space: nowrap; +} + +.resultCardContent { + font-size: 0.875rem; + line-height: 1.25rem; + color: var(--text-secondary); + word-wrap: break-word; + overflow-wrap: break-word; + max-height: 5rem; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; +} + +/* Embed + attachment wrappers — render below the text preview in each + result card. The embed block hides itself when LinkEmbeds renders + nothing (no URLs in the message) so there's no phantom gap. */ +.resultCardEmbeds { + margin-top: 6px; +} + +.resultCardEmbeds:empty { + display: none; +} + +.resultCardAttachments { + display: flex; + flex-direction: column; + gap: 6px; + margin-top: 6px; +} + +.resultCardImage { + max-width: 100%; + max-height: 220px; + border-radius: 8px; + object-fit: cover; + display: block; +} + +.resultCardFile { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 12px; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.05)); + border-radius: 8px; + color: var(--text-link, var(--brand-primary)); + font-size: 0.8125rem; + text-decoration: none; + word-break: break-all; +} + +.resultCardFile:hover { + text-decoration: underline; +} diff --git a/packages/shared/src/components/channel/SearchDrawer.tsx b/packages/shared/src/components/channel/SearchDrawer.tsx new file mode 100644 index 0000000..242191f --- /dev/null +++ b/packages/shared/src/components/channel/SearchDrawer.tsx @@ -0,0 +1,326 @@ +/** + * SearchDrawer — mobile bottom sheet opened by tapping the search icon + * in ChannelHeader on mobile. Wraps SearchStore in a BottomSheet with a + * search input, filter chips, and a compact mobile-styled result list. + */ +import { observer } from 'mobx-react-lite'; +import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { MagnifyingGlass, X, Hash } from '@phosphor-icons/react'; +import { Avatar, BottomSheet } from '@brycord/ui'; +import SearchStore, { type SearchResult } from '@app/stores/SearchStore'; +import ChannelStore from '@app/stores/ChannelStore'; +import { MessageContent } from './MessageContent'; +import { LinkEmbeds } from './LinkEmbed'; +import styles from './SearchDrawer.module.css'; + +interface SearchDrawerProps { + isOpen: boolean; + onClose: () => void; + serverId?: string; + onScrollToMessage?: (messageId: string) => void; +} + +const FILTER_CHIPS: Array<{ key: string; label: string }> = [ + { key: 'from:', label: 'From' }, + { key: 'has:', label: 'Has' }, + { key: 'mentions:', label: 'Mentions' }, + { key: 'before:', label: 'Before' }, + { key: 'after:', label: 'After' }, + { key: 'pinned:true', label: 'Pinned' }, +]; + +function formatTimestamp(ts: number): string { + const date = new Date(ts); + const now = new Date(); + const isToday = date.toDateString() === now.toDateString(); + const yesterday = new Date(now); + yesterday.setDate(yesterday.getDate() - 1); + const isYesterday = date.toDateString() === yesterday.toDateString(); + const time = date.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' }); + if (isToday) return `Today at ${time}`; + if (isYesterday) return `Yesterday at ${time}`; + return `${date.toLocaleDateString()} ${time}`; +} + +function groupByChannel( + results: SearchResult[], +): Array<{ channelId: string; results: SearchResult[] }> { + const groups = new Map<string, SearchResult[]>(); + for (const result of results) { + const cId = result.message.channelId; + if (!groups.has(cId)) groups.set(cId, []); + groups.get(cId)!.push(result); + } + return Array.from(groups.entries()).map(([channelId, results]) => ({ channelId, results })); +} + +/** + * Compact mobile search result list — renders a "N Results" counter, then + * channel-grouped cards. Each card shows the avatar, author, timestamp, + * and a preview of the message content. Tapping a card jumps to the + * message in the underlying chat view. + */ +const MobileSearchResults = observer(function MobileSearchResults({ + onJump, +}: { + onJump: (messageId: string) => void; +}) { + const { query, results, isSearching } = SearchStore; + const hasQuery = query.trim().length > 0; + const grouped = useMemo(() => groupByChannel(results), [results]); + + if (!hasQuery) { + return ( + <div className={styles.placeholder}> + <MagnifyingGlass size={48} weight="regular" /> + <div className={styles.placeholderTitle}>Search Messages</div> + <div className={styles.placeholderSubtitle}> + Use filters or enter keywords to find messages + </div> + </div> + ); + } + + if (isSearching) { + return ( + <div className={styles.placeholder}> + <div className={styles.placeholderSubtitle}>Searching…</div> + </div> + ); + } + + if (results.length === 0) { + return ( + <div className={styles.placeholder}> + <div className={styles.placeholderTitle}>No results</div> + <div className={styles.placeholderSubtitle}>Try a different search term.</div> + </div> + ); + } + + return ( + <div className={styles.resultsList}> + <div className={styles.resultsCount}> + {results.length} {results.length === 1 ? 'Result' : 'Results'} + </div> + {grouped.map((group) => { + const channel = ChannelStore.getChannel(group.channelId); + return ( + <div key={group.channelId} className={styles.resultsGroup}> + <div className={styles.resultsGroupHeader}> + <Hash size={14} weight="bold" /> + <span>{channel?.name || 'Unknown'}</span> + </div> + <div className={styles.resultsGroupBody}> + {group.results.map((result) => { + const { message } = result; + const author = message.author; + return ( + <div + key={message.id} + role="button" + tabIndex={0} + className={styles.resultCard} + onClick={() => onJump(message.id)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + onJump(message.id); + } + }} + > + <div className={styles.resultCardAvatar}> + <Avatar + src={author.avatar} + fallback={author.displayName || author.username} + size={32} + /> + </div> + <div className={styles.resultCardBody}> + <div className={styles.resultCardHeader}> + <span className={styles.resultCardAuthor}> + {author.displayName || author.username} + </span> + <span className={styles.resultCardTimestamp}> + {formatTimestamp(message.timestamp)} + </span> + </div> + {message.content && ( + <div className={styles.resultCardContent}> + <MessageContent content={message.content} /> + </div> + )} + {!message.decryptionFailed && ( + <div + className={styles.resultCardEmbeds} + onClick={(e) => e.stopPropagation()} + > + <LinkEmbeds content={message.content} /> + </div> + )} + {message.attachments.length > 0 && ( + <div className={styles.resultCardAttachments}> + {message.attachments.map((att) => + att.contentType.startsWith('image/') ? ( + <img + key={att.id} + src={att.url} + alt={att.filename} + className={styles.resultCardImage} + loading="lazy" + /> + ) : ( + <a + key={att.id} + href={att.url} + className={styles.resultCardFile} + target="_blank" + rel="noopener noreferrer" + onClick={(e) => e.stopPropagation()} + > + {att.filename} + </a> + ), + )} + </div> + )} + </div> + </div> + ); + })} + </div> + </div> + ); + })} + </div> + ); +}); + +export const SearchDrawer = observer(function SearchDrawer({ + isOpen, + onClose, + serverId, + onScrollToMessage, +}: SearchDrawerProps) { + const inputRef = useRef<HTMLInputElement>(null); + const debounceRef = useRef<ReturnType<typeof setTimeout>>(undefined); + + // Focus the input whenever the drawer opens — matches Fluxer's UX + // where the keyboard appears immediately on open. + useEffect(() => { + if (!isOpen) return; + const t = setTimeout(() => inputRef.current?.focus(), 120); + return () => clearTimeout(t); + }, [isOpen]); + + useEffect(() => { + return () => { + if (debounceRef.current) clearTimeout(debounceRef.current); + }; + }, []); + + const handleChange = useCallback( + (e: React.ChangeEvent<HTMLInputElement>) => { + const value = e.target.value; + SearchStore.setQuery(value); + if (debounceRef.current) clearTimeout(debounceRef.current); + if (value.trim() && serverId) { + if (!SearchStore.isOpen) SearchStore.openSearch(); + debounceRef.current = setTimeout(() => { + SearchStore.searchMessages(serverId, value); + }, 300); + } else if (SearchStore.isOpen) { + SearchStore.closeSearch(); + } + }, + [serverId], + ); + + const handleKeyDown = useCallback( + (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && SearchStore.query.trim() && serverId) { + if (!SearchStore.isOpen) SearchStore.openSearch(); + SearchStore.searchMessages(serverId, SearchStore.query); + } + }, + [serverId], + ); + + const handleChipClick = useCallback((key: string) => { + const current = SearchStore.query; + const separator = current.length > 0 && !current.endsWith(' ') ? ' ' : ''; + SearchStore.setQuery(current + separator + key + ' '); + inputRef.current?.focus(); + }, []); + + const handleClear = useCallback(() => { + SearchStore.setQuery(''); + SearchStore.closeSearch(); + inputRef.current?.focus(); + }, []); + + const handleSearch = useCallback(() => { + if (!serverId || !SearchStore.query.trim()) return; + if (!SearchStore.isOpen) SearchStore.openSearch(); + SearchStore.searchMessages(serverId, SearchStore.query); + }, [serverId]); + + return ( + <BottomSheet isOpen={isOpen} onClose={onClose} title="Search"> + <div className={styles.body}> + <div className={styles.searchInputWrapper}> + <MagnifyingGlass size={16} className={styles.searchInputIcon} /> + <input + ref={inputRef} + className={styles.searchInput} + placeholder="Search messages" + value={SearchStore.query} + onChange={handleChange} + onKeyDown={handleKeyDown} + /> + {SearchStore.query && ( + <button + type="button" + className={styles.searchClearButton} + onClick={handleClear} + aria-label="Clear search" + > + <X size={14} weight="bold" /> + </button> + )} + </div> + + <div className={styles.chipRow}> + {FILTER_CHIPS.map((chip) => ( + <button + key={chip.key} + type="button" + className={styles.chip} + onClick={() => handleChipClick(chip.key)} + > + {chip.label} + </button> + ))} + </div> + + <button + type="button" + className={styles.searchButton} + onClick={handleSearch} + disabled={!SearchStore.query.trim() || !serverId} + > + Search + </button> + + <div className={styles.results}> + <MobileSearchResults + onJump={(id) => { + onScrollToMessage?.(id); + onClose(); + }} + /> + </div> + </div> + </BottomSheet> + ); +}); diff --git a/packages/shared/src/components/channel/SearchPanel.module.css b/packages/shared/src/components/channel/SearchPanel.module.css new file mode 100644 index 0000000..2e8c9e2 --- /dev/null +++ b/packages/shared/src/components/channel/SearchPanel.module.css @@ -0,0 +1,264 @@ +.panel { + display: flex; + flex-direction: column; + width: 420px; + flex-shrink: 0; + background-color: var(--background-secondary); + border-left: 1px solid var(--background-modifier-accent); + height: 100%; + overflow: hidden; +} + +.header { + display: flex; + align-items: center; + justify-content: space-between; + height: 3.5rem; + padding: 0 1rem; + border-bottom: 1px solid var(--background-modifier-accent); + flex-shrink: 0; +} + +.headerTitle { + font-size: 0.875rem; + font-weight: 600; + color: var(--text-primary); + margin: 0; +} + +.closeButton { + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + padding: 0; + border: none; + border-radius: 4px; + background: transparent; + color: var(--text-tertiary); + cursor: pointer; + transition: background-color 0.12s, color 0.12s; +} + +.closeButton:hover { + background: var(--background-modifier-hover); + color: var(--text-primary); +} + +.resultsList { + flex: 1; + overflow-y: auto; + padding: 0.5rem; +} + +/* ── Channel group headers ──────────────────────────────────── */ + +.channelGroup { + margin-bottom: 4px; +} + +.channelGroupHeader { + display: flex; + align-items: center; + gap: 6px; + padding: 12px 8px 4px; + font-size: 0.8125rem; + font-weight: 700; + color: var(--text-primary); +} + +.channelGroupIcon { + color: var(--channel-icon, var(--text-tertiary)); + flex-shrink: 0; +} + +.channelGroupName { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.channelGroupCount { + margin-left: auto; + font-size: 0.6875rem; + font-weight: 600; + color: var(--text-tertiary); +} + +/* ── Result row ────────────────────────────────────────────── */ + +.resultItem { + display: flex; + align-items: flex-start; + gap: 0.75rem; + padding: 0.5rem; + margin-bottom: 0.5rem; + width: 100%; + text-align: left; + font: inherit; + border-radius: 0.375rem; + border: 1px solid var(--background-header-secondary, var(--background-modifier-accent)); + background-color: var(--background-secondary-lighter, var(--background-primary)); + position: relative; + cursor: pointer; + transition: background-color 0.15s; +} + +.resultItem:hover { + background-color: var(--background-modifier-hover); +} + +.resultItem:hover .jumpButton { + opacity: 1; +} + +.resultAvatar { + flex-shrink: 0; + padding-top: 2px; +} + +.resultBody { + flex: 1; + min-width: 0; +} + +.resultHeader { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; + flex-wrap: nowrap; + margin-bottom: 2px; +} + +.resultAuthor { + font-size: 0.9375rem; + font-weight: 700; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.resultTimestamp { + font-size: 0.75rem; + color: var(--text-primary-muted); + font-weight: 500; + flex-shrink: 0; +} + +.resultContent { + font-size: 0.875rem; + color: var(--text-primary); + line-height: 1.375rem; + word-wrap: break-word; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; +} + +.resultAttachments { + display: flex; + flex-direction: column; + gap: 6px; + margin-top: 6px; + min-width: 0; +} + +/* Constrain image / video / link-embed inside the 645px column so + they don't stretch out the result card. */ +.resultAttachments > * { + max-width: 100%; +} + +.resultAttachments img, +.resultAttachments video { + max-width: 100%; + max-height: 260px; + border-radius: 6px; +} + +.resultEmbeds { + margin-top: 6px; +} + +.highlight { + background-color: rgba(250, 168, 26, 0.3); + color: var(--text-primary); + border-radius: 2px; + padding: 0 1px; +} + +/* Jump button — lives inside the header row right after the + timestamp (pushed flush-right via `margin-left: auto`) so the + message content below gets the full width of the result card. + Matches PinnedMessageRow's muted tile-button look and only + fades in on card hover to keep the idle state clean. */ +.jumpButton { + display: inline-flex; + align-items: center; + justify-content: center; + margin-left: auto; + padding: 4px 10px; + background-color: var(--background-primary); + border: 1px solid var(--background-header-secondary); + border-radius: 0.375rem; + color: var(--text-primary-muted); + font: inherit; + font-size: 0.75rem; + font-weight: 700; + cursor: pointer; + opacity: 0; + visibility: hidden; + transition: + opacity 0.12s ease, + visibility 0.12s ease, + background-color 0.1s, + border-color 0.1s, + color 0.1s; + flex-shrink: 0; + -webkit-tap-highlight-color: transparent; +} + +.resultItem:hover .jumpButton, +.resultItem:focus-within .jumpButton { + opacity: 1; + visibility: visible; +} + +.jumpButton:hover { + background-color: var(--background-modifier-hover); + border-color: var(--background-modifier-accent); + color: var(--text-primary); +} + +/* ── Empty / loading states ─────────────────────────────────── */ + +.emptyState { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 3rem 1.5rem; + text-align: center; +} + +.emptyIcon { + color: var(--text-tertiary); + margin-bottom: 12px; +} + +.emptyTitle { + font-size: 1rem; + font-weight: 600; + color: var(--text-primary); + margin: 0 0 4px; +} + +.emptyDescription { + font-size: 0.8125rem; + color: var(--text-tertiary); + margin: 0; +} diff --git a/packages/shared/src/components/channel/SearchPanel.tsx b/packages/shared/src/components/channel/SearchPanel.tsx new file mode 100644 index 0000000..830dde9 --- /dev/null +++ b/packages/shared/src/components/channel/SearchPanel.tsx @@ -0,0 +1,490 @@ +/** + * SearchPanel — cross-channel message search rendered to the right of + * the chat column (replacing the members list) whenever the channel + * header's search input has a non-empty query. Pulls the latest N + * messages from every non-DM channel via `api.messages.searchScan` + * in one round-trip, decrypts them client-side using the per-channel + * key bundles we already hold, and does a case-insensitive substring + * match against the decrypted plaintext. + * + * Results are grouped by channel (with a `# channel-name` header), + * and each result row renders the author / timestamp / content plus + * any attachments (via `EncryptedAttachment`) and link embeds (via + * `LinkEmbed`) — matching the Brycord reference layout so messages + * read the same in search as they do in the timeline. + * + * Clicking a result dispatches `brycord:scroll-to-message` so the + * Messages component can jump to the corresponding message in its + * channel. Navigation to a different channel is the caller's + * responsibility (the same event already pops open the right view). + */ +import { useQuery } from 'convex/react'; +import { Hash, MagnifyingGlass, X } from '@phosphor-icons/react'; +import { useEffect, useMemo, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Avatar } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import { usePlatform } from '../../platform'; +import { + EncryptedAttachment, + type AttachmentMetadata, +} from './EncryptedAttachment'; +import { LinkEmbed } from './LinkEmbed'; +import styles from './SearchPanel.module.css'; + +interface SearchPanelProps { + channelId: string; + query: string; + onClose: () => void; +} + +const TAG_LENGTH = 32; +const PER_CHANNEL_LIMIT = 100; +const URL_REGEX = /https?:\/\/[^\s<>"']+/gi; + +interface SearchResult { + id: string; + channelId: string; + authorName: string; + authorAvatarUrl: string | null; + content: string; + attachments: AttachmentMetadata[]; + timestamp: number; +} + +interface ChannelGroup { + channelId: string; + channelName: string; + results: SearchResult[]; +} + +function stripFilters(raw: string): string { + return raw + .split(/\s+/) + .filter( + (tok) => + !/^(from:|mentions:|has:|before:|during:|after:|pinned:)/i.test(tok), + ) + .join(' ') + .trim(); +} + +function extractUrls(text: string): string[] { + const matches = text.match(URL_REGEX) ?? []; + const cleaned = matches.map((m) => m.replace(/[),.;!?]+$/, '')); + return Array.from(new Set(cleaned)); +} + +function highlight(text: string, needle: string): React.ReactNode { + if (!needle) return text; + const lower = text.toLowerCase(); + const target = needle.toLowerCase(); + const out: React.ReactNode[] = []; + let cursor = 0; + let idx = lower.indexOf(target, cursor); + let key = 0; + while (idx !== -1) { + if (idx > cursor) out.push(text.slice(cursor, idx)); + out.push( + <mark key={`h-${key++}`} className={styles.highlight}> + {text.slice(idx, idx + needle.length)} + </mark>, + ); + cursor = idx + needle.length; + idx = lower.indexOf(target, cursor); + } + if (cursor < text.length) out.push(text.slice(cursor)); + return out; +} + +function formatTimestamp(ms: number): string { + const date = new Date(ms); + const now = new Date(); + const isToday = date.toDateString() === now.toDateString(); + const yesterday = new Date(now); + yesterday.setDate(yesterday.getDate() - 1); + const isYesterday = date.toDateString() === yesterday.toDateString(); + const time = date.toLocaleTimeString([], { + hour: 'numeric', + minute: '2-digit', + }); + if (isToday) return `Today at ${time}`; + if (isYesterday) return `Yesterday at ${time}`; + return `${date.toLocaleDateString()} ${time}`; +} + +/** + * Parse a decrypted message blob and split it into plaintext + a + * (possibly empty) list of attachments. Mirrors the exact shape + * handling used by `Messages.tsx`, so attachments in search results + * render the same way they do in the live timeline. + */ +function parseDecrypted(raw: string): { + text: string; + attachments: AttachmentMetadata[]; +} { + if (!raw) return { text: '', attachments: [] }; + let text = raw; + const attachments: AttachmentMetadata[] = []; + try { + const parsed = JSON.parse(raw); + if (parsed && typeof parsed === 'object') { + if (Array.isArray(parsed)) { + for (const item of parsed) { + if ( + item?.type === 'attachment' && + item.url && + item.key && + item.iv + ) { + attachments.push(item as AttachmentMetadata); + } + } + text = ''; + } else if ( + parsed.type === 'attachment' && + parsed.url && + parsed.key && + parsed.iv + ) { + attachments.push(parsed as AttachmentMetadata); + text = ''; + } else if (parsed.text !== undefined) { + text = String(parsed.text); + } + } + } catch { + /* plain string */ + } + return { text, attachments }; +} + +export function SearchPanel({ channelId, query, onClose }: SearchPanelProps) { + const { crypto } = usePlatform(); + const navigate = useNavigate(); + const needle = useMemo(() => stripFilters(query).toLowerCase(), [query]); + + const userId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const privateKeyPem = + typeof sessionStorage !== 'undefined' + ? sessionStorage.getItem('privateKey') + : null; + + // --- Channel metadata ----------------------------------------- + const allChannels = useQuery(api.channels.list, {}) ?? []; + const textChannels = useMemo( + () => allChannels.filter((c) => c.type === 'text'), + [allChannels], + ); + const channelNameById = useMemo(() => { + const map = new Map<string, string>(); + for (const c of textChannels) map.set(c._id, c.name); + return map; + }, [textChannels]); + + // --- Channel key decryption ----------------------------------- + const allKeys = useQuery( + api.channelKeys.getKeysForUser, + userId ? { userId: userId as any } : 'skip', + ); + const [keyByChannel, setKeyByChannel] = useState<Map<string, string>>( + new Map(), + ); + + useEffect(() => { + let cancelled = false; + if (!allKeys || !privateKeyPem) { + setKeyByChannel(new Map()); + return; + } + (async () => { + const merged: Record<string, string> = {}; + for (const item of allKeys) { + try { + const bundleJson = await crypto.privateDecrypt( + privateKeyPem, + item.encrypted_key_bundle, + ); + Object.assign(merged, JSON.parse(bundleJson)); + } catch (err) { + console.error( + `Failed to decrypt key bundle for ${item.channel_id}`, + err, + ); + } + } + if (cancelled) return; + setKeyByChannel(new Map(Object.entries(merged))); + })(); + return () => { + cancelled = true; + }; + }, [allKeys, privateKeyPem, crypto]); + + // --- Multi-channel message fetch ------------------------------ + const channelIdList = useMemo( + () => textChannels.map((c) => c._id), + [textChannels], + ); + const scan = useQuery( + api.messages.searchScan, + channelIdList.length > 0 + ? { + channelIds: channelIdList as any, + perChannelLimit: PER_CHANNEL_LIMIT, + userId: (userId as any) ?? undefined, + } + : 'skip', + ) as Array<{ channelId: string; messages: any[] }> | undefined; + + // --- Decrypt --------------------------------------------------- + // Keyed by message id so decryption is incremental — new scan + // results only redo the rows we haven't seen yet. + const [decryptedMap, setDecryptedMap] = useState<Map<string, string>>( + new Map(), + ); + + useEffect(() => { + if (!scan || keyByChannel.size === 0) return; + let cancelled = false; + (async () => { + const next = new Map(decryptedMap); + let changed = false; + for (const group of scan) { + const key = keyByChannel.get(group.channelId); + if (!key) continue; + for (const msg of group.messages) { + const id = msg.id as string; + if (next.has(id)) continue; + if (!msg.ciphertext || msg.ciphertext.length < TAG_LENGTH) { + next.set(id, ''); + changed = true; + continue; + } + const tag = msg.ciphertext.slice(-TAG_LENGTH); + const contentHex = msg.ciphertext.slice(0, -TAG_LENGTH); + try { + const plaintext = await crypto.decryptData( + contentHex, + key, + msg.nonce, + tag, + ); + if (cancelled) return; + next.set(id, plaintext); + changed = true; + } catch { + next.set(id, ''); + changed = true; + } + } + } + if (changed && !cancelled) setDecryptedMap(next); + })(); + return () => { + cancelled = true; + }; + // decryptedMap intentionally excluded — we mutate a copy + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [scan, keyByChannel, crypto]); + + // --- Filter + shape results ----------------------------------- + const grouped: ChannelGroup[] = useMemo(() => { + if (!scan || !needle) return []; + const groups: ChannelGroup[] = []; + for (const group of scan) { + const channelName = channelNameById.get(group.channelId) ?? 'channel'; + const hits: SearchResult[] = []; + for (const msg of group.messages) { + const id = msg.id as string; + const raw = decryptedMap.get(id); + if (!raw) continue; + const { text, attachments } = parseDecrypted(raw); + // Match against the visible text OR an attachment filename so + // queries like "screenshot.png" still surface the message. + const filenameHit = attachments.some((a) => + a.filename?.toLowerCase().includes(needle), + ); + if (!text.toLowerCase().includes(needle) && !filenameHit) continue; + hits.push({ + id, + channelId: group.channelId, + authorName: msg.displayName || msg.username || 'User', + authorAvatarUrl: msg.avatarUrl ?? null, + content: text, + attachments, + timestamp: msg.created_at + ? new Date(msg.created_at).getTime() + : Date.now(), + }); + } + if (hits.length > 0) { + hits.sort((a, b) => b.timestamp - a.timestamp); + groups.push({ channelId: group.channelId, channelName, results: hits }); + } + } + // Channel containing the most matches first; within a channel, + // most-recent first (already sorted above). + groups.sort((a, b) => b.results.length - a.results.length); + return groups; + }, [scan, decryptedMap, needle, channelNameById]); + + const totalResults = useMemo( + () => grouped.reduce((acc, g) => acc + g.results.length, 0), + [grouped], + ); + + const handleJump = (targetChannelId: string, messageId: string) => { + // Server channels all live under the single `home` route in + // this app (convex is single-server). If the hit is in a + // different channel, navigate there first, then dispatch the + // scroll-to event so Messages.tsx can find and scroll into view. + if (targetChannelId !== channelId) { + navigate(`/channels/home/${targetChannelId}`); + } + window.dispatchEvent( + new CustomEvent('brycord:scroll-to-message', { + detail: { messageId, channelId: targetChannelId }, + }), + ); + }; + + const isLoading = + !scan || (keyByChannel.size === 0 && (allKeys?.length ?? 0) > 0); + + return ( + <div className={styles.panel}> + <div className={styles.header}> + <h2 className={styles.headerTitle}> + {needle && !isLoading + ? `${totalResults} ${totalResults === 1 ? 'Result' : 'Results'}` + : 'Search'} + </h2> + <button + type="button" + onClick={onClose} + aria-label="Close search" + className={styles.closeButton} + > + <X size={18} weight="bold" /> + </button> + </div> + + <div className={styles.resultsList}> + {!needle ? ( + <div className={styles.emptyState}> + <MagnifyingGlass size={40} className={styles.emptyIcon} /> + <p className={styles.emptyTitle}>Search Messages</p> + <p className={styles.emptyDescription}> + Type in the search bar above to find messages. + </p> + </div> + ) : isLoading ? ( + <div className={styles.emptyState}> + <MagnifyingGlass size={40} className={styles.emptyIcon} /> + <p className={styles.emptyTitle}>Searching…</p> + <p className={styles.emptyDescription}> + Scanning your channels. + </p> + </div> + ) : grouped.length === 0 ? ( + <div className={styles.emptyState}> + <MagnifyingGlass size={40} className={styles.emptyIcon} /> + <p className={styles.emptyTitle}>No Results</p> + <p className={styles.emptyDescription}> + Try a different search term. + </p> + </div> + ) : ( + grouped.map((group) => ( + <div key={group.channelId} className={styles.channelGroup}> + <div className={styles.channelGroupHeader}> + <Hash + size={16} + weight="bold" + className={styles.channelGroupIcon} + /> + <span className={styles.channelGroupName}> + {group.channelName} + </span> + <span className={styles.channelGroupCount}> + {group.results.length}{' '} + {group.results.length === 1 ? 'result' : 'results'} + </span> + </div> + {group.results.map((r) => { + const urls = r.content ? extractUrls(r.content).slice(0, 2) : []; + return ( + <div + key={r.id} + role="button" + tabIndex={0} + className={styles.resultItem} + onClick={() => handleJump(r.channelId, r.id)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + handleJump(r.channelId, r.id); + } + }} + > + <div className={styles.resultAvatar}> + <Avatar + src={r.authorAvatarUrl ?? undefined} + size={40} + fallback={r.authorName} + /> + </div> + <div className={styles.resultBody}> + <div className={styles.resultHeader}> + <span className={styles.resultAuthor}> + {r.authorName} + </span> + <span className={styles.resultTimestamp}> + {formatTimestamp(r.timestamp)} + </span> + <button + type="button" + className={styles.jumpButton} + onClick={(e) => { + e.stopPropagation(); + handleJump(r.channelId, r.id); + }} + > + Jump + </button> + </div> + {r.content && ( + <div className={styles.resultContent}> + {highlight(r.content, needle)} + </div> + )} + {urls.length > 0 && ( + <div className={styles.resultEmbeds}> + {urls.map((url) => ( + <LinkEmbed key={url} url={url} /> + ))} + </div> + )} + {r.attachments.length > 0 && ( + <div className={styles.resultAttachments}> + {r.attachments.map((att, j) => ( + <EncryptedAttachment + key={`${r.id}-${j}`} + metadata={att} + /> + ))} + </div> + )} + </div> + </div> + ); + })} + </div> + )) + )} + </div> + </div> + ); +} diff --git a/packages/shared/src/components/channel/TwemojiImg.tsx b/packages/shared/src/components/channel/TwemojiImg.tsx new file mode 100644 index 0000000..29eb5d6 --- /dev/null +++ b/packages/shared/src/components/channel/TwemojiImg.tsx @@ -0,0 +1,41 @@ +import { useState } from 'react'; +import { emojiToCodepoint, getTwemojiUrl } from '../../utils/twemoji'; + +interface TwemojiImgProps { + emoji: string; + size?: number; + className?: string; +} + +/** + * Render a unicode emoji via the Twemoji 14 CDN. Falls back to the + * system emoji if the CDN image 404s (Unicode 15+ characters that + * Twemoji 14 never shipped). + */ +export function TwemojiImg({ emoji, size = 22, className }: TwemojiImgProps) { + const [broken, setBroken] = useState(false); + if (broken) { + return ( + <span + className={className} + style={{ fontSize: size, lineHeight: 1, display: 'inline-block' }} + > + {emoji} + </span> + ); + } + return ( + <img + src={getTwemojiUrl(emoji)} + alt={emoji} + width={size} + height={size} + loading="lazy" + draggable={false} + className={className} + style={{ display: 'inline-block', verticalAlign: 'middle' }} + onError={() => setBroken(true)} + data-emoji-codepoint={emojiToCodepoint(emoji)} + /> + ); +} diff --git a/packages/shared/src/components/channel/TypingUsers.module.css b/packages/shared/src/components/channel/TypingUsers.module.css new file mode 100644 index 0000000..1fb1a07 --- /dev/null +++ b/packages/shared/src/components/channel/TypingUsers.module.css @@ -0,0 +1,36 @@ +.container { + display: flex; + align-items: center; + gap: 4px; + height: 24px; + padding: 0 12px; + font-size: 0.75rem; +} + +.dots { + display: flex; + align-items: center; + gap: 2px; +} + +.dot { + width: 4px; + height: 4px; + border-radius: 50%; + background-color: var(--text-primary); + animation: typingBounce 1.4s infinite ease-in-out; +} + +.dot:nth-child(1) { animation-delay: 0s; } +.dot:nth-child(2) { animation-delay: 0.2s; } +.dot:nth-child(3) { animation-delay: 0.4s; } + +@keyframes typingBounce { + 0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); } + 40% { opacity: 1; transform: scale(1); } +} + +.text { + color: var(--text-secondary); + font-weight: 500; +} diff --git a/packages/shared/src/components/channel/TypingUsers.tsx b/packages/shared/src/components/channel/TypingUsers.tsx new file mode 100644 index 0000000..62afb3e --- /dev/null +++ b/packages/shared/src/components/channel/TypingUsers.tsx @@ -0,0 +1,44 @@ +import { useQuery } from 'convex/react'; +import { api } from '../../../../../convex/_generated/api'; +import styles from './TypingUsers.module.css'; + +interface TypingUsersProps { + channelId: string; +} + +export function TypingUsers({ channelId }: TypingUsersProps) { + const userId = typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const typing = useQuery( + api.typing.getTyping, + channelId ? { channelId: channelId as any } : 'skip', + ); + const users = useQuery(api.auth.getPublicKeys) ?? []; + + const others = (typing ?? []).filter((t: any) => t.userId !== userId); + if (others.length === 0) return null; + + const names = others + .map((t: any) => { + const u = users.find((x) => x.id === t.userId); + return u?.displayName || u?.username || 'Someone'; + }) + .slice(0, 3); + + const text = + names.length === 1 + ? `${names[0]} is typing…` + : names.length === 2 + ? `${names[0]} and ${names[1]} are typing…` + : 'Several people are typing…'; + + return ( + <div className={styles.container}> + <span className={styles.dots}> + <span className={styles.dot} /> + <span className={styles.dot} /> + <span className={styles.dot} /> + </span> + <span className={styles.text}>{text}</span> + </div> + ); +} diff --git a/packages/shared/src/components/channel/UploadErrorModal.tsx b/packages/shared/src/components/channel/UploadErrorModal.tsx new file mode 100644 index 0000000..8a14b9b --- /dev/null +++ b/packages/shared/src/components/channel/UploadErrorModal.tsx @@ -0,0 +1,46 @@ +/** + * UploadErrorModal — shown when a background upload (shift-to-instant + * drop) fails for any reason other than a size limit. Size-limit + * failures get their own dedicated modal with more explanatory + * copy; this one is for generic errors like 520 from the homeserver's + * media CDN, 413, network drops, or auth issues. + * + * Only a Cancel (OK) button — retrying a silent background upload + * doesn't have a great UX. The user can just drag the file again. + */ +import { Modal, Button } from '@brycord/ui'; +import { WarningCircle } from '@phosphor-icons/react'; +import styles from './FileSizeLimitModal.module.css'; + +interface UploadErrorModalProps { + isOpen: boolean; + onClose: () => void; + fileName: string; + message: string; +} + +export function UploadErrorModal({ isOpen, onClose, fileName, message }: UploadErrorModalProps) { + if (!isOpen) return null; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="small"> + <Modal.Header title="Upload failed" onClose={onClose} /> + <Modal.Content className={styles.content}> + <div className={styles.iconRow}> + <div className={styles.iconBadge}> + <WarningCircle size={28} weight="fill" /> + </div> + </div> + <p className={styles.body}> + Failed to upload <strong>{fileName}</strong>. + </p> + <p className={styles.hint}>{message}</p> + </Modal.Content> + <Modal.Footer> + <Button variant="primary" onClick={onClose}> + OK + </Button> + </Modal.Footer> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/channel/index.ts b/packages/shared/src/components/channel/index.ts new file mode 100644 index 0000000..e8f9dc2 --- /dev/null +++ b/packages/shared/src/components/channel/index.ts @@ -0,0 +1,11 @@ +export { ChannelView } from './ChannelView'; +export { ChannelHeader } from './ChannelHeader'; +export { ChannelChatLayout } from './ChannelChatLayout'; +export { Messages } from './Messages'; +export { MessageGroup } from './MessageGroup'; +export { MessageActionBar } from './MessageActionBar'; +export { ReplyBar } from './ReplyBar'; +export { ChannelTextarea } from './ChannelTextarea'; +export { TypingUsers } from './TypingUsers'; +export { ChannelWelcomeSection } from './ChannelWelcomeSection'; +export { SearchPanel } from './SearchPanel'; diff --git a/packages/shared/src/components/dm/AddFriendsSheet.module.css b/packages/shared/src/components/dm/AddFriendsSheet.module.css new file mode 100644 index 0000000..b835143 --- /dev/null +++ b/packages/shared/src/components/dm/AddFriendsSheet.module.css @@ -0,0 +1,323 @@ +/* ── AddFriendsSheet ───────────────────────────────────────────────── + Tabbed bottom sheet reached from the "Add Friends" button in the + DM sidebar. Two tabs — Add (username input + pending strangers) and + Ignored Friends (review / unignore). Mirrors the chrome we already + use in MobileServerSettings and ChannelDetailsDrawer so both + settings surfaces feel consistent. */ + +.root { + display: flex; + flex-direction: column; + height: 100%; +} + +/* ── Top bar (drag handle + title + close) ───────────────────────── */ + +.topBar { + position: relative; + flex-shrink: 0; + display: grid; + grid-template-columns: 48px 1fr 48px; + align-items: center; + height: 56px; + padding: 0 8px; + border-bottom: 1px solid var(--background-header-secondary); +} + +.topBarTitle { + font-size: 17px; + font-weight: 700; + color: var(--text-primary); + text-align: center; + margin: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.topBarButton { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + border: none; + background: transparent; + color: var(--text-primary); + cursor: pointer; + transition: background-color 0.15s; + -webkit-tap-highlight-color: transparent; + margin: 0 auto; +} + +.topBarButton:active { + background-color: var(--background-modifier-hover); +} + +/* ── Tabs (Add / Ignored Friends) ─────────────────────────────────── */ + +.tabBar { + position: relative; + display: flex; + align-items: stretch; + padding: 0; + border-bottom: 1px solid var(--background-header-secondary); + flex-shrink: 0; +} + +.tabButton { + position: relative; + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 14px 8px; + background: none; + border: none; + color: var(--text-secondary, var(--text-primary-muted)); + cursor: pointer; + font: inherit; + font-size: 0.9375rem; + font-weight: 600; + transition: color 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.tabButtonActive { + color: var(--brand-primary-light); +} + +/* Active tab underline — 2px bar overlapping the row divider. */ +.tabButtonActive::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: -1px; + height: 2px; + background-color: var(--brand-primary-light); + border-radius: 2px 2px 0 0; +} + +.tabBadge { + display: inline-flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + min-width: 20px; + height: 20px; + padding: 0 6px; + border-radius: 999px; + background-color: hsl(0, calc(80% * var(--saturation-factor)), 55%); + color: #fff; + font-size: 11px; + font-weight: 700; + line-height: 1; +} + +/* ── Body (scrolling area) ────────────────────────────────────────── */ + +.body { + flex: 1; + min-height: 0; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + padding: 16px; +} + +/* ── Username input row (Add tab) ────────────────────────────────── */ + +.usernameRow { + display: flex; + flex-direction: column; + gap: 10px; + margin-bottom: 20px; +} + +.usernameInput { + width: 100%; + padding: 14px 16px; + background-color: var(--background-secondary-alt); + border: 1px solid transparent; + border-radius: 0.75rem; + color: var(--text-primary); + font: inherit; + font-size: 15px; + font-weight: 500; + outline: none; + box-shadow: none; + -webkit-appearance: none; + appearance: none; + box-sizing: border-box; +} + +.usernameInput:focus, +.usernameInput:focus-visible { + outline: none; + border-color: var(--brand-primary); +} + +.usernameInput::placeholder { + color: var(--text-tertiary-muted, var(--text-tertiary)); +} + +.primaryButton { + width: 100%; + padding: 14px 16px; + background-color: var(--brand-primary); + border: none; + border-radius: 0.75rem; + color: #fff; + font: inherit; + font-size: 15px; + font-weight: 700; + cursor: pointer; + transition: filter 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.primaryButton:disabled { + opacity: 0.5; + cursor: default; +} + +.primaryButton:active:not(:disabled) { + filter: brightness(0.92); +} + +/* ── Error / status banners ───────────────────────────────────────── */ + +.statusError { + padding: 10px 14px; + border-radius: 8px; + background-color: hsl(0, calc(60% * var(--saturation-factor)), 22%); + color: hsl(0, calc(80% * var(--saturation-factor)), 85%); + font-size: 13px; + margin-top: 4px; +} + +.statusSuccess { + padding: 10px 14px; + border-radius: 8px; + background-color: hsl(139, calc(47.3% * var(--saturation-factor)), 20%); + color: hsl(139, calc(47.3% * var(--saturation-factor)), 85%); + font-size: 13px; + margin-top: 4px; +} + +/* ── Section label ────────────────────────────────────────────────── */ + +.sectionLabel { + font-size: 11px; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--text-primary-muted); + padding: 0 4px 8px; +} + +/* ── Row list (pending + ignored) ────────────────────────────────── */ + +.rowList { + background-color: var(--background-secondary-alt); + border-radius: 0.75rem; + overflow: hidden; +} + +.row { + position: relative; + display: grid; + grid-template-columns: 40px 1fr auto; + align-items: center; + gap: 12px; + padding: 12px 16px; +} + +.row:not(:last-child)::after { + content: ''; + position: absolute; + left: calc(16px + 40px + 12px); + right: 16px; + bottom: 0; + height: 1px; + background-color: var(--background-header-secondary); + pointer-events: none; +} + +.rowText { + min-width: 0; + display: flex; + flex-direction: column; + gap: 2px; +} + +.rowName { + font-size: 14px; + font-weight: 600; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.rowSubtitle { + font-size: 12px; + color: var(--text-primary-muted); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.rowActions { + display: flex; + align-items: center; + gap: 6px; +} + +.pillButton { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 6px 12px; + border: none; + border-radius: 999px; + background-color: var(--background-secondary); + color: var(--text-primary); + font: inherit; + font-size: 12px; + font-weight: 700; + cursor: pointer; + transition: filter 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.pillButton:active:not(:disabled) { + filter: brightness(1.2); +} + +.pillButton:disabled { + opacity: 0.5; + cursor: default; +} + +.pillButtonPrimary { + background-color: var(--brand-primary); + color: #fff; +} + +.pillButtonDanger { + background-color: transparent; + color: hsl(350, calc(90% * var(--saturation-factor)), 65%); + border: 1px solid hsla(350, calc(90% * var(--saturation-factor)), 65%, 0.35); +} + +/* ── Empty state ──────────────────────────────────────────────────── */ + +.empty { + padding: 32px 16px; + text-align: center; + color: var(--text-primary-muted); + font-size: 14px; +} diff --git a/packages/shared/src/components/dm/AddFriendsSheet.tsx b/packages/shared/src/components/dm/AddFriendsSheet.tsx new file mode 100644 index 0000000..7ad3dd3 --- /dev/null +++ b/packages/shared/src/components/dm/AddFriendsSheet.tsx @@ -0,0 +1,224 @@ +/** + * AddFriendsSheet — bottom sheet reached from the "Add Friends" button + * in the DM sidebar. Discord Clone has no friends/pending-request + * backend (it stores DMs directly via `api.dms.openDM`), so this sheet + * functions as a username search that opens a DM with the matched user. + * + * The new UI's Pending/Ignored tabs are preserved as placeholders with + * an empty state. They will light up when the Convex schema grows a + * friends table. + */ +import { useEffect, useMemo, useState } from 'react'; +import { useMutation, useQuery } from 'convex/react'; +import { useNavigate } from 'react-router-dom'; +import { X } from '@phosphor-icons/react'; +import { Avatar, BottomSheet } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import styles from './AddFriendsSheet.module.css'; + +interface AddFriendsSheetProps { + isOpen: boolean; + onClose: () => void; +} + +type Tab = 'add' | 'pending'; + +interface PublicUser { + id: string; + username?: string; + displayName?: string | null; + avatarUrl?: string | null; +} + +export function AddFriendsSheet({ isOpen, onClose }: AddFriendsSheetProps) { + const navigate = useNavigate(); + const [activeTab, setActiveTab] = useState<Tab>('add'); + const [username, setUsername] = useState(''); + const [busy, setBusy] = useState(false); + const [status, setStatus] = useState< + { type: 'error' | 'success'; message: string } | null + >(null); + + const allUsers = (useQuery(api.auth.getPublicKeys) ?? []) as PublicUser[]; + const openDM = useMutation(api.dms.openDM); + + const myUserId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + + // Filter candidates by the search query (case-insensitive on + // username OR display name), excluding the current user. + const matches = useMemo<PublicUser[]>(() => { + const q = username.trim().toLowerCase(); + if (!q) return []; + return allUsers + .filter((u) => u.id !== myUserId) + .filter((u) => { + const uname = (u.username || '').toLowerCase(); + const display = (u.displayName || '').toLowerCase(); + return uname.includes(q) || display.includes(q); + }) + .slice(0, 20); + }, [allUsers, username, myUserId]); + + useEffect(() => { + if (isOpen) { + setStatus(null); + setUsername(''); + setBusy(false); + setActiveTab('add'); + } + }, [isOpen]); + + const handleStartDM = async (target: PublicUser) => { + if (busy || !myUserId) return; + setBusy(true); + setStatus(null); + try { + const result = await openDM({ + userId: myUserId as Id<'userProfiles'>, + targetUserId: target.id as Id<'userProfiles'>, + }); + navigate(`/channels/@me/${result.channelId}`); + onClose(); + } catch (err: any) { + setStatus({ + type: 'error', + message: err?.message || 'Failed to start DM.', + }); + } finally { + setBusy(false); + } + }; + + return ( + <BottomSheet isOpen={isOpen} onClose={onClose} disableDefaultHeader showHandle> + <div className={styles.root}> + <div className={styles.topBar}> + <div /> + <h2 className={styles.topBarTitle}>Add Friend</h2> + <button + type="button" + className={styles.topBarButton} + onClick={onClose} + aria-label="Close" + > + <X size={20} weight="bold" /> + </button> + </div> + + <div className={styles.tabBar} role="tablist"> + <button + type="button" + role="tab" + aria-selected={activeTab === 'add'} + className={`${styles.tabButton} ${ + activeTab === 'add' ? styles.tabButtonActive : '' + }`} + onClick={() => setActiveTab('add')} + > + <span>Add</span> + </button> + <button + type="button" + role="tab" + aria-selected={activeTab === 'pending'} + className={`${styles.tabButton} ${ + activeTab === 'pending' ? styles.tabButtonActive : '' + }`} + onClick={() => setActiveTab('pending')} + > + Pending + </button> + </div> + + <div className={styles.body}> + {activeTab === 'add' ? ( + <> + <div className={styles.usernameRow}> + <input + type="text" + className={styles.usernameInput} + placeholder="Search by username or display name" + value={username} + onChange={(e) => setUsername(e.target.value)} + autoCapitalize="off" + autoCorrect="off" + spellCheck={false} + disabled={busy} + /> + {status && ( + <div + className={ + status.type === 'error' + ? styles.statusError + : styles.statusSuccess + } + > + {status.message} + </div> + )} + </div> + + <div className={styles.sectionLabel}> + {username.trim() ? `Results — ${matches.length}` : 'Start typing to search'} + </div> + {username.trim() && matches.length === 0 ? ( + <div className={styles.empty}>No users match "{username}".</div> + ) : ( + <div className={styles.rowList}> + {matches.map((u) => ( + <UserRow + key={u.id} + user={u} + onStartDM={() => handleStartDM(u)} + disabled={busy} + /> + ))} + </div> + )} + </> + ) : ( + <> + <div className={styles.sectionLabel}>Pending requests</div> + <div className={styles.empty}> + Pending friend requests aren't supported yet. + </div> + </> + )} + </div> + </div> + </BottomSheet> + ); +} + +interface UserRowProps { + user: PublicUser; + onStartDM: () => void; + disabled?: boolean; +} + +function UserRow({ user, onStartDM, disabled }: UserRowProps) { + const name = user.displayName || user.username || 'User'; + return ( + <div className={styles.row}> + <Avatar src={user.avatarUrl ?? undefined} fallback={name} size={40} /> + <div className={styles.rowText}> + <span className={styles.rowName}>{name}</span> + {user.username && ( + <span className={styles.rowSubtitle}>@{user.username}</span> + )} + </div> + <div className={styles.rowActions}> + <button + type="button" + className={`${styles.pillButton} ${styles.pillButtonPrimary}`} + onClick={onStartDM} + disabled={disabled} + > + Start DM + </button> + </div> + </div> + ); +} diff --git a/packages/shared/src/components/dm/DMLayout.module.css b/packages/shared/src/components/dm/DMLayout.module.css new file mode 100644 index 0000000..321a0d8 --- /dev/null +++ b/packages/shared/src/components/dm/DMLayout.module.css @@ -0,0 +1,124 @@ +.container { + display: flex; + flex-direction: column; + height: 100%; + background-color: var(--background-secondary); +} + +/* ── Header ───────────────────────────────────────────────────────── + "Messages" title on the left, search icon + "Add Friends" pill + button on the right. Replaces the old full-width search input with + a layout that matches the Fluxer mobile DM home view. */ + +.header { + display: flex; + align-items: center; + gap: 8px; + padding: 12px 12px; + flex-shrink: 0; +} + +.headerTitle { + flex: 1; + min-width: 0; + margin: 0; + font-size: 1.125rem; + font-weight: 800; + color: var(--text-primary); + letter-spacing: -0.01em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.headerActions { + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; +} + +.iconButton { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: 50%; + border: none; + background-color: var(--guild-list-foreground); + color: var(--text-primary); + cursor: pointer; + transition: filter 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.iconButton:hover, +.iconButton:active { + filter: brightness(1.15); +} + +.addFriendsButton { + position: relative; + display: flex; + align-items: center; + gap: 8px; + padding: 8px 14px; + background-color: var(--guild-list-foreground); + border: none; + border-radius: 999px; + color: var(--text-primary); + font: inherit; + font-size: 14px; + font-weight: 700; + cursor: pointer; + transition: filter 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.addFriendsButton:hover, +.addFriendsButton:active { + filter: brightness(1.15); +} + +/* Red pending-request badge in the top-right corner of the button, + same style as the navbar unread dot. Hidden when count is 0 — + we simply don't render the element in JSX. */ +.badge { + position: absolute; + top: -6px; + right: -6px; + box-sizing: border-box; + min-width: 22px; + height: 22px; + padding: 0 6px; + display: flex; + align-items: center; + justify-content: center; + /* Perfect circle for single-digit counts (min-width == height), + stretches horizontally into a rounded pill for "99+". The + full radius keeps both ends fully round at any width. */ + border-radius: var(--radius-full); + background-color: var(--status-danger); + color: #fff; + font-size: 12px; + font-weight: 700; + line-height: 1; +} + +/* ── List ───────────────────────────────────────────────────────────*/ + +.list { + flex: 1; + overflow-y: auto; + padding: 0 0.5rem; +} + +.empty { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + color: var(--text-muted); + font-size: 0.875rem; +} diff --git a/packages/shared/src/components/dm/DMLayout.tsx b/packages/shared/src/components/dm/DMLayout.tsx new file mode 100644 index 0000000..2eca4e1 --- /dev/null +++ b/packages/shared/src/components/dm/DMLayout.tsx @@ -0,0 +1,43 @@ +import { useQuery } from 'convex/react'; +import { MagnifyingGlass } from '@phosphor-icons/react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { api } from '../../../../../convex/_generated/api'; +import { DMListItem } from './DMListItem'; +import styles from './DMLayout.module.css'; + +export function DMLayout() { + const navigate = useNavigate(); + const params = useParams<{ channelId?: string }>(); + const userId = typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const dms = useQuery(api.dms.listDMs, userId ? { userId: userId as any } : 'skip') ?? []; + + return ( + <div className={styles.container}> + <div className={styles.header}> + <span className={styles.headerTitle}>Messages</span> + <div className={styles.headerActions}> + <button type="button" className={styles.iconButton} aria-label="Search"> + <MagnifyingGlass size={18} weight="regular" /> + </button> + {/* Add Friends button is removed — this build auto-friends + every user on the server, so there's nothing the button + could do besides open a no-op sheet. */} + </div> + </div> + <div className={styles.list}> + {dms.length === 0 ? ( + <div className={styles.empty}>No direct messages yet</div> + ) : ( + dms.map((dm) => ( + <DMListItem + key={dm.channel_id} + dm={dm} + selected={params.channelId === dm.channel_id} + onClick={() => navigate(`/channels/@me/${dm.channel_id}`)} + /> + )) + )} + </div> + </div> + ); +} diff --git a/packages/shared/src/components/dm/DMListItem.module.css b/packages/shared/src/components/dm/DMListItem.module.css new file mode 100644 index 0000000..10246c4 --- /dev/null +++ b/packages/shared/src/components/dm/DMListItem.module.css @@ -0,0 +1,58 @@ +.item { + display: flex; + align-items: center; + gap: 12px; + width: 100%; + height: 3.25rem; + padding: 0 0.5rem; + border-radius: var(--radius-md); + background: none; + border: none; + cursor: pointer; + transition: background-color 0.15s; + text-align: left; +} + +.item:hover { + background-color: var(--background-modifier-hover); +} + +.itemSelected { + /* 35% mix of the interactive-selected surface with transparent — + matches the Fluxer selection highlight on the DM sidebar, a + soft translucent wash that stays visible on top of the + secondary surface behind it. */ + background-color: color-mix( + in srgb, + var(--surface-interactive-selected-bg) 35%, + transparent + ); +} + +.itemSelected .name { + color: var(--text-primary); +} + +.info { + flex: 1; + min-width: 0; +} + +.name { + font-size: 1rem; + font-weight: 500; + color: var(--channel-icon); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.item:hover .name { + color: var(--interactive-hover); +} + +.time { + font-size: 0.75rem; + color: var(--text-muted); + flex-shrink: 0; +} diff --git a/packages/shared/src/components/dm/DMListItem.tsx b/packages/shared/src/components/dm/DMListItem.tsx new file mode 100644 index 0000000..8f82633 --- /dev/null +++ b/packages/shared/src/components/dm/DMListItem.tsx @@ -0,0 +1,50 @@ +import { Avatar } from '@discord-clone/ui'; +import { useOnlineUsers } from '../../contexts/PresenceContext'; +import styles from './DMListItem.module.css'; + +interface DMRow { + channel_id: string; + channel_name: string; + other_user_id: string; + other_username: string; + other_displayName: string | null; + other_user_status?: string; + other_user_avatar_url?: string | null; +} + +interface DMListItemProps { + dm: DMRow; + selected: boolean; + onClick: () => void; +} + +export function DMListItem({ dm, selected, onClick }: DMListItemProps) { + // Always show the real `username` in the DM list — it's the + // stable identifier across the whole app. Display names / server + // nicknames go in member lists where context makes them + // unambiguous, but in the DM sidebar we want users to see the + // account they're talking to. + const name = dm.other_username || dm.other_displayName || 'User'; + const { resolveStatus } = useOnlineUsers(); + const liveStatus = resolveStatus( + dm.other_user_status || 'offline', + dm.other_user_id, + ); + return ( + <button + type="button" + className={`${styles.item} ${selected ? styles.itemSelected : ''}`} + onClick={onClick} + > + <Avatar + src={dm.other_user_avatar_url || null} + size={32} + fallback={name} + status={liveStatus as any} + /> + <div className={styles.info}> + <span className={styles.name}>{name}</span> + </div> + </button> + ); +} diff --git a/packages/shared/src/components/dm/PersonalNotesRow.module.css b/packages/shared/src/components/dm/PersonalNotesRow.module.css new file mode 100644 index 0000000..5b21071 --- /dev/null +++ b/packages/shared/src/components/dm/PersonalNotesRow.module.css @@ -0,0 +1,74 @@ +/* Personal Notes — pinned row at the very top of the DM sidebar. + Matches DMListItem's row dimensions (3.25rem height, 0.5rem + horizontal padding) so the rounded square avatar + label sits + flush with the rest of the DM list. The avatar is a brand-primary + square (not a circle like regular DM avatars) to visually mark it + as a special "pinned" slot, matching the Fluxer reference. */ + +.item { + display: flex; + align-items: center; + gap: 12px; + width: 100%; + height: 3.25rem; + padding: 0 0.5rem; + border-radius: var(--radius-md); + background: none; + border: none; + cursor: pointer; + transition: background-color 0.15s; + text-align: left; + -webkit-tap-highlight-color: transparent; +} + +.item:hover { + background-color: var(--background-modifier-hover); +} + +.itemSelected { + /* Same translucent wash DMListItem uses so selection reads as + consistent between the pinned row and the rest of the list. */ + background-color: color-mix( + in srgb, + var(--surface-interactive-selected-bg) 35%, + transparent + ); +} + +.icon { + width: 40px; + height: 40px; + border-radius: 50%; + /* Default state matches the empty server-list icon in GuildList + (`--guild-list-foreground`) so a non-selected Personal Notes + row reads as a quiet neutral bubble. Selected state switches + to the brand color to match the active state of other DM + rows. */ + background-color: var(--guild-list-foreground); + color: var(--text-primary); + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + transition: background-color 0.15s, color 0.15s; +} + +.itemSelected .icon { + background-color: var(--brand-primary); + color: #fff; +} + +.label { + flex: 1; + min-width: 0; + font-size: 1rem; + font-weight: 600; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.itemSelected .label { + color: var(--text-primary); +} diff --git a/packages/shared/src/components/dm/PersonalNotesRow.tsx b/packages/shared/src/components/dm/PersonalNotesRow.tsx new file mode 100644 index 0000000..d5b8e5f --- /dev/null +++ b/packages/shared/src/components/dm/PersonalNotesRow.tsx @@ -0,0 +1,40 @@ +/** + * PersonalNotesRow — pinned sidebar row for the local user's private + * Personal Notes room. Rendered at the top of DMLayout above the + * friends DMs. + * + * Purely presentational: the row pulls its channel from ChannelStore + * (whose contents come from RoomManager.getRoomAsChannel, which + * classifies the room as `type='personal_notes'`), then renders a + * brand-primary rounded square with a NotePencil icon on the left + * and "Personal Notes" on the right. + */ +import { observer } from 'mobx-react-lite'; +import { NotePencil } from '@phosphor-icons/react'; +import type { Channel } from '@brycord/matrix-client'; +import styles from './PersonalNotesRow.module.css'; + +interface PersonalNotesRowProps { + channel: Channel; + selected: boolean; + onClick: () => void; +} + +export const PersonalNotesRow = observer(function PersonalNotesRow({ + channel, + selected, + onClick, +}: PersonalNotesRowProps) { + return ( + <button + type="button" + className={`${styles.item} ${selected ? styles.itemSelected : ''}`} + onClick={onClick} + > + <div className={styles.icon}> + <NotePencil size={22} weight="fill" /> + </div> + <span className={styles.label}>{channel.name || 'Personal Notes'}</span> + </button> + ); +}); diff --git a/packages/shared/src/components/dm/index.ts b/packages/shared/src/components/dm/index.ts new file mode 100644 index 0000000..df0d2d9 --- /dev/null +++ b/packages/shared/src/components/dm/index.ts @@ -0,0 +1,2 @@ +export { DMLayout } from './DMLayout'; +export { DMListItem } from './DMListItem'; diff --git a/packages/shared/src/components/layout/AppLayout.tsx b/packages/shared/src/components/layout/AppLayout.tsx new file mode 100644 index 0000000..4934509 --- /dev/null +++ b/packages/shared/src/components/layout/AppLayout.tsx @@ -0,0 +1,166 @@ +import { useEffect, useState } from 'react'; +import { Navigate, Outlet, useNavigate } from 'react-router-dom'; +import { PresenceProvider } from '../../contexts/PresenceContext'; +import { KeybindProvider } from '../../contexts/KeybindContext'; +import { UserSettingsModal } from '../settings/UserSettingsModal'; +import { ServerSettingsModal } from '../settings/ServerSettingsModal'; +import { GuildsLayout } from './GuildsLayout'; +import { CreateChannelModal } from './CreateChannelModal'; +import { CreateCategoryModal } from './CategorySettingsModal'; +import { InviteModal } from '../modals/InviteModal'; +import { CreateServerModal } from '../modals/CreateServerModal'; +import { PiPOverlay } from '../voice/PiPOverlay'; +import { ChannelSettingsModal } from '../channel/ChannelSettingsModal'; + +/** + * AppLayout — checks session via sessionStorage (matches App.tsx AuthGuard), + * then renders the 3-column GuildsLayout with the active route's content + * flowing through `<Outlet />`. Hosts the global user settings modal and + * the server-level create/invite/settings modals, and listens for + * `brycord:open-*` events to open them. + */ +export function AppLayout() { + const navigate = useNavigate(); + const [settingsOpen, setSettingsOpen] = useState(false); + // Optional tab request from the dispatcher — e.g. Edit Profile + // buttons pass `{ tab: 'account' }` to jump straight in, while the + // gear button passes nothing so mobile lands on the category list. + const [settingsInitialTab, setSettingsInitialTab] = useState< + 'account' | 'appearance' | 'voice' | 'keybinds' | 'notifications' | 'security' | undefined + >(undefined); + const [createChannelOpen, setCreateChannelOpen] = useState(false); + const [createCategoryOpen, setCreateCategoryOpen] = useState(false); + const [inviteOpen, setInviteOpen] = useState(false); + const [serverSettingsOpen, setServerSettingsOpen] = useState(false); + const [createServerOpen, setCreateServerOpen] = useState(false); + const [channelSettingsId, setChannelSettingsId] = useState<string | null>(null); + + // Keybind / cross-component nav bridge + useEffect(() => { + const onNavigate = (e: Event) => { + const detail = (e as CustomEvent<{ path?: string }>).detail; + if (detail?.path) navigate(detail.path); + }; + window.addEventListener('brycord:navigate', onNavigate); + return () => window.removeEventListener('brycord:navigate', onNavigate); + }, [navigate]); + + // User settings trigger — fired by UserArea's gear button + other entry points + useEffect(() => { + const onOpenSettings = (e: Event) => { + const detail = (e as CustomEvent<{ tab?: string }>).detail; + const tab = detail?.tab as typeof settingsInitialTab; + setSettingsInitialTab(tab); + setSettingsOpen(true); + }; + window.addEventListener('brycord:open-user-settings', onOpenSettings); + return () => window.removeEventListener('brycord:open-user-settings', onOpenSettings); + }, []); + + // Server-level modal triggers dispatched from GuildHeaderDropdown etc. + useEffect(() => { + const handlers: Record<string, () => void> = { + 'brycord:open-create-channel': () => setCreateChannelOpen(true), + 'brycord:open-create-category': () => setCreateCategoryOpen(true), + 'brycord:open-invite': () => setInviteOpen(true), + 'brycord:open-server-settings': () => setServerSettingsOpen(true), + 'brycord:open-create-server': () => setCreateServerOpen(true), + }; + Object.entries(handlers).forEach(([k, h]) => window.addEventListener(k, h)); + return () => { + Object.entries(handlers).forEach(([k, h]) => window.removeEventListener(k, h)); + }; + }, []); + + // Channel settings — fired by the sidebar gear icon with a channelId + // payload. Single state holds the target id; null closes the modal. + useEffect(() => { + const onOpen = (e: Event) => { + const detail = (e as CustomEvent<{ channelId?: string }>).detail; + if (detail?.channelId) setChannelSettingsId(detail.channelId); + }; + window.addEventListener('brycord:open-channel-settings', onOpen); + return () => + window.removeEventListener('brycord:open-channel-settings', onOpen); + }, []); + + // Global keybind handlers. Voice bindings are wired from UserArea + // via its own `useVoice()` access so the dispatch arrives where the + // mute / deafen state already lives. + useEffect(() => { + const goHome = () => navigate('/channels/@me'); + const focusSearch = () => { + const input = document.querySelector<HTMLInputElement>( + 'input[placeholder="Search" i], input[aria-label="Search" i]', + ); + input?.focus(); + }; + const openSettings = () => setSettingsOpen(true); + window.addEventListener('brycord:keybind:navigation.goToDMs', goHome); + window.addEventListener('brycord:keybind:navigation.focusSearch', focusSearch); + window.addEventListener('brycord:keybind:popouts.openUserSettings', openSettings); + return () => { + window.removeEventListener('brycord:keybind:navigation.goToDMs', goHome); + window.removeEventListener( + 'brycord:keybind:navigation.focusSearch', + focusSearch, + ); + window.removeEventListener( + 'brycord:keybind:popouts.openUserSettings', + openSettings, + ); + }; + }, [navigate]); + + const hasSession = + typeof sessionStorage !== 'undefined' && + !!(sessionStorage.getItem('privateKey') && sessionStorage.getItem('signingKey')); + + const myUserId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + + if (!hasSession) { + return <Navigate to="/login" replace />; + } + + return ( + <PresenceProvider userId={myUserId}> + <KeybindProvider> + <GuildsLayout> + <Outlet /> + </GuildsLayout> + <UserSettingsModal + isOpen={settingsOpen} + onClose={() => { + setSettingsOpen(false); + setSettingsInitialTab(undefined); + }} + initialTab={settingsInitialTab} + /> + <ServerSettingsModal + isOpen={serverSettingsOpen} + onClose={() => setServerSettingsOpen(false)} + /> + <CreateChannelModal + isOpen={createChannelOpen} + onClose={() => setCreateChannelOpen(false)} + /> + <CreateCategoryModal + isOpen={createCategoryOpen} + onClose={() => setCreateCategoryOpen(false)} + /> + <InviteModal isOpen={inviteOpen} onClose={() => setInviteOpen(false)} /> + <CreateServerModal + isOpen={createServerOpen} + onClose={() => setCreateServerOpen(false)} + /> + <ChannelSettingsModal + isOpen={!!channelSettingsId} + onClose={() => setChannelSettingsId(null)} + channelId={channelSettingsId} + /> + <PiPOverlay /> + </KeybindProvider> + </PresenceProvider> + ); +} diff --git a/packages/shared/src/components/layout/CategorySettingsModal.module.css b/packages/shared/src/components/layout/CategorySettingsModal.module.css new file mode 100644 index 0000000..1d1d483 --- /dev/null +++ b/packages/shared/src/components/layout/CategorySettingsModal.module.css @@ -0,0 +1,120 @@ +.body { + display: flex; + flex-direction: column; + gap: 18px; + padding: 20px; +} + +.header { + display: flex; + align-items: center; + gap: 12px; +} + +.headerIcon { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 8px; + background-color: var(--background-tertiary); + color: var(--text-secondary); + flex-shrink: 0; +} + +.headerText { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} + +.headerName { + font-size: 1rem; + font-weight: 600; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.headerSubtitle { + font-size: 0.75rem; + color: var(--text-tertiary); + text-transform: uppercase; + letter-spacing: 0.03em; +} + +.permissionWarning { + padding: 10px 14px; + background-color: color-mix(in srgb, var(--status-warning, #faa61a) 15%, transparent); + color: var(--status-warning, #faa61a); + border-radius: 8px; + font-size: 0.8125rem; + font-weight: 500; + margin: 0; +} + +.field { + display: flex; + flex-direction: column; + gap: 8px; + border: none; + padding: 0; + margin: 0; +} + +.field:disabled { + opacity: 0.6; +} + +.fieldLabel { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.02em; + color: var(--text-tertiary); +} + +.fieldInput { + height: 40px; + padding: 0 12px; + border-radius: var(--radius-md, 6px); + border: none; + background-color: var(--background-tertiary); + color: var(--text-primary); + font-size: 1rem; + font-family: inherit; + outline: none; +} + +.fieldInput:focus { + outline: 2px solid var(--brand-primary); + outline-offset: -2px; +} + +.fieldHintRow { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.fieldCounter { + font-size: 0.75rem; + color: var(--text-tertiary); +} + +.status { + font-size: 0.8125rem; + margin: 0; +} + +.statusSuccess { + color: var(--status-positive, #23a55a); +} + +.statusError { + color: var(--status-danger, #da373c); +} diff --git a/packages/shared/src/components/layout/CategorySettingsModal.tsx b/packages/shared/src/components/layout/CategorySettingsModal.tsx new file mode 100644 index 0000000..330543b --- /dev/null +++ b/packages/shared/src/components/layout/CategorySettingsModal.tsx @@ -0,0 +1,99 @@ +/** + * CreateCategoryModal — Convex-backed category creation. + * + * (File historically named CategorySettingsModal; repurposed for the + * minimal "create category" flow. The export is CreateCategoryModal.) + */ +import { useEffect, useState } from 'react'; +import { useMutation } from 'convex/react'; +import { Modal, Button } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import styles from './CategorySettingsModal.module.css'; + +const NAME_MAX = 100; + +interface CreateCategoryModalProps { + isOpen: boolean; + onClose: () => void; +} + +export function CreateCategoryModal({ isOpen, onClose }: CreateCategoryModalProps) { + const createCategory = useMutation(api.categories.create); + + const [name, setName] = useState(''); + const [saving, setSaving] = useState(false); + const [error, setError] = useState<string | null>(null); + + useEffect(() => { + if (isOpen) { + setName(''); + setSaving(false); + setError(null); + } + }, [isOpen]); + + const trimmed = name.trim(); + const canSave = trimmed.length > 0 && trimmed.length <= NAME_MAX && !saving; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!canSave) return; + setSaving(true); + setError(null); + try { + await createCategory({ name: trimmed }); + onClose(); + } catch (err: any) { + setError(err?.message || 'Failed to create category.'); + } finally { + setSaving(false); + } + }; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="small"> + <Modal.Header title="Create Category" onClose={onClose} /> + <Modal.Content> + <form id="create-category-form" onSubmit={handleSubmit} className={styles.body}> + <fieldset className={styles.field} disabled={saving}> + <label className={styles.fieldLabel} htmlFor="create-category-name"> + Category Name + </label> + <input + id="create-category-name" + type="text" + className={styles.fieldInput} + value={name} + onChange={(e) => setName(e.target.value)} + maxLength={NAME_MAX} + placeholder="New Category" + autoFocus + /> + <div className={styles.fieldHintRow}> + <span className={styles.fieldCounter}> + {trimmed.length}/{NAME_MAX} + </span> + </div> + </fieldset> + + {error && <p className={`${styles.status} ${styles.statusError}`}>{error}</p>} + </form> + </Modal.Content> + <Modal.Footer> + <Button variant="secondary" size="sm" onClick={onClose} type="button"> + Cancel + </Button> + <Button + variant="primary" + size="sm" + type="submit" + form="create-category-form" + loading={saving} + disabled={!canSave} + > + Create Category + </Button> + </Modal.Footer> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/layout/ChannelListContextMenu.module.css b/packages/shared/src/components/layout/ChannelListContextMenu.module.css new file mode 100644 index 0000000..b5909b0 --- /dev/null +++ b/packages/shared/src/components/layout/ChannelListContextMenu.module.css @@ -0,0 +1,53 @@ +.overlay { + position: fixed; + inset: 0; + z-index: var(--z-index-contextmenu); +} + +.menu { + position: fixed; + min-width: 188px; + padding: 6px 8px; + background-color: var(--background-floating, var(--background-primary)); + border-radius: var(--radius-md); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.24); + z-index: var(--z-index-contextmenu); +} + +.menuItem { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + padding: 6px 8px; + border-radius: var(--radius-sm); + background: none; + border: none; + cursor: pointer; + color: var(--text-secondary); + font-size: 0.875rem; + font-weight: 500; + font-family: inherit; + text-align: left; + transition: background-color 0.1s, color 0.1s; +} + +.menuItem:hover { + background-color: var(--brand-primary); + color: #fff; +} + +.menuItemDanger { + color: var(--status-danger, #da373c); +} + +.menuItemDanger:hover { + background-color: var(--status-danger, #da373c); + color: #fff; +} + +.separator { + height: 1px; + margin: 4px 8px; + background-color: var(--background-modifier-accent); +} diff --git a/packages/shared/src/components/layout/ChannelListContextMenu.tsx b/packages/shared/src/components/layout/ChannelListContextMenu.tsx new file mode 100644 index 0000000..62fb92f --- /dev/null +++ b/packages/shared/src/components/layout/ChannelListContextMenu.tsx @@ -0,0 +1,134 @@ +import { FolderPlus, Gear, Hash, PencilSimple, SignOut, Trash } from '@phosphor-icons/react'; +import { useEffect, useRef } from 'react'; +import styles from './ChannelListContextMenu.module.css'; + +interface ChannelListContextMenuProps { + x: number; + y: number; + /** Set if right-clicking a category header */ + categoryId?: string; + /** Set if right-clicking a channel (text or voice). Takes priority over categoryId. */ + channelId?: string; + /** Label shown next to the Rename/Delete buttons, e.g. 'Channel' or 'Voice Channel' */ + channelTypeLabel?: string; + onCreateChannel: () => void; + onCreateCategory: () => void; + onRenameCategory?: () => void; + onCategorySettings?: () => void; + onDeleteCategory?: () => void; + onRenameChannel?: () => void; + onDeleteChannel?: () => void; + /** + * Shown when the user can't delete a channel (either because they lack + * the power level or because the channel is already an orphan in the + * local store). Leaves the room locally and removes the ChannelStore + * entry so the user can always nuke a phantom from their sidebar. + */ + onLeaveChannel?: () => void; + onClose: () => void; +} + +export function ChannelListContextMenu({ + x, + y, + categoryId, + channelId, + channelTypeLabel = 'Channel', + onCreateChannel, + onCreateCategory, + onRenameCategory, + onCategorySettings, + onDeleteCategory, + onRenameChannel, + onDeleteChannel, + onLeaveChannel, + onClose, +}: ChannelListContextMenuProps) { + const ref = useRef<HTMLDivElement>(null); + + useEffect(() => { + const handleClick = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) { + onClose(); + } + }; + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + document.addEventListener('mousedown', handleClick); + document.addEventListener('keydown', handleEscape); + return () => { + document.removeEventListener('mousedown', handleClick); + document.removeEventListener('keydown', handleEscape); + }; + }, [onClose]); + + // Clamp position to viewport + const style: React.CSSProperties = { + top: Math.min(y, window.innerHeight - 120), + left: Math.min(x, window.innerWidth - 200), + }; + + return ( + <div className={styles.overlay}> + <div className={styles.menu} ref={ref} style={style}> + {channelId ? ( + <> + {onRenameChannel && ( + <button className={styles.menuItem} onClick={onRenameChannel}> + <PencilSimple size={18} /> + <span>Rename {channelTypeLabel}</span> + </button> + )} + {onDeleteChannel && ( + <button className={`${styles.menuItem} ${styles.menuItemDanger}`} onClick={onDeleteChannel}> + <Trash size={18} /> + <span>Delete {channelTypeLabel}</span> + </button> + )} + {/* Fallback when the user can't delete — always lets them + remove the channel from their own sidebar. */} + {!onDeleteChannel && onLeaveChannel && ( + <button className={`${styles.menuItem} ${styles.menuItemDanger}`} onClick={onLeaveChannel}> + <SignOut size={18} /> + <span>Leave {channelTypeLabel}</span> + </button> + )} + </> + ) : categoryId ? ( + <> + {onRenameCategory && ( + <button className={styles.menuItem} onClick={onRenameCategory}> + <PencilSimple size={18} /> + <span>Rename Category</span> + </button> + )} + {onCategorySettings && ( + <button className={styles.menuItem} onClick={onCategorySettings}> + <Gear size={18} /> + <span>Category Settings</span> + </button> + )} + {onDeleteCategory && ( + <button className={`${styles.menuItem} ${styles.menuItemDanger}`} onClick={onDeleteCategory}> + <Trash size={18} /> + <span>Delete Category</span> + </button> + )} + </> + ) : ( + <> + <button className={styles.menuItem} onClick={onCreateChannel}> + <Hash size={18} weight="bold" /> + <span>Create Channel</span> + </button> + <button className={styles.menuItem} onClick={onCreateCategory}> + <FolderPlus size={18} /> + <span>Create Category</span> + </button> + </> + )} + </div> + </div> + ); +} diff --git a/packages/shared/src/components/layout/CreateChannelModal.module.css b/packages/shared/src/components/layout/CreateChannelModal.module.css new file mode 100644 index 0000000..39b2b71 --- /dev/null +++ b/packages/shared/src/components/layout/CreateChannelModal.module.css @@ -0,0 +1,141 @@ +.backdrop { + position: fixed; + inset: 0; + z-index: var(--z-index-modal); + background-color: rgba(0, 0, 0, 0.35); + backdrop-filter: blur(8px); + display: flex; + align-items: center; + justify-content: center; +} + +.modal { + width: 440px; + max-width: 90vw; + background-color: var(--background-secondary); + border-radius: 8px; + box-shadow: 0 8px 24px -4px rgba(0, 0, 0, 0.25); + overflow: hidden; +} + +.header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px; + background-color: var(--background-tertiary); +} + +.title { + font-size: 1.25rem; + font-weight: 600; + color: var(--text-primary); + margin: 0; +} + +.closeButton { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: var(--radius-md); + background: none; + border: none; + cursor: pointer; + color: var(--text-tertiary-muted); + transition: color 0.15s; +} + +.closeButton:hover { + color: var(--text-primary); +} + +.body { + padding: 16px; + display: flex; + flex-direction: column; + gap: 16px; +} + +.label { + display: block; + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + color: var(--text-tertiary); + letter-spacing: 0.02em; + margin-bottom: 8px; +} + +.input { + width: 100%; + padding: 10px 12px; + border: 1px solid var(--background-modifier-accent); + border-radius: var(--radius-md); + background-color: var(--background-tertiary); + color: var(--text-primary); + font-size: 1rem; + font-family: inherit; + outline: none; + box-sizing: border-box; + transition: border-color 0.15s; +} + +.input:focus { + border-color: var(--brand-primary); +} + +.input::placeholder { + color: var(--text-tertiary-muted); +} + +.field { + display: flex; + flex-direction: column; +} + +.typeSelector { + display: flex; + flex-direction: column; +} + +.typeOptions { + display: flex; + gap: 8px; +} + +.typeOption { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 10px; + border-radius: var(--radius-md); + border: 2px solid var(--background-modifier-accent); + background: none; + color: var(--text-secondary); + font-size: 0.875rem; + font-weight: 500; + font-family: inherit; + cursor: pointer; + transition: border-color 0.15s, color 0.15s; +} + +.typeOption:hover { + border-color: var(--text-tertiary-muted); +} + +.typeOptionActive { + border-color: var(--brand-primary); + color: var(--text-primary); + background-color: color-mix(in srgb, var(--brand-primary) 10%, transparent); +} + +.footer { + display: flex; + justify-content: flex-end; + gap: 8px; + padding-top: 8px; +} diff --git a/packages/shared/src/components/layout/CreateChannelModal.tsx b/packages/shared/src/components/layout/CreateChannelModal.tsx new file mode 100644 index 0000000..6e7990c --- /dev/null +++ b/packages/shared/src/components/layout/CreateChannelModal.tsx @@ -0,0 +1,134 @@ +import { useEffect, useState } from 'react'; +import { useMutation } from 'convex/react'; +import { Hash, SpeakerHigh } from '@phosphor-icons/react'; +import { Modal, Button } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import styles from './CreateChannelModal.module.css'; + +interface CreateChannelModalProps { + isOpen: boolean; + onClose: () => void; +} + +/** + * CreateChannelModal — Convex-backed channel creation. + * Collects a name, a text/voice type, and an optional topic, then calls + * `api.channels.create` and closes. + */ +export function CreateChannelModal({ isOpen, onClose }: CreateChannelModalProps) { + const createChannel = useMutation(api.channels.create); + + const [name, setName] = useState(''); + const [channelType, setChannelType] = useState<'text' | 'voice'>('text'); + const [topic, setTopic] = useState(''); + const [loading, setLoading] = useState(false); + const [error, setError] = useState<string | null>(null); + + useEffect(() => { + if (isOpen) { + setName(''); + setChannelType('text'); + setTopic(''); + setLoading(false); + setError(null); + } + }, [isOpen]); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + const trimmed = name.trim(); + if (!trimmed) return; + + setLoading(true); + setError(null); + try { + await createChannel({ + name: trimmed, + type: channelType, + topic: topic.trim() || undefined, + }); + onClose(); + } catch (err: any) { + setError(err?.message || 'Failed to create channel'); + } finally { + setLoading(false); + } + }; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="small"> + <Modal.Header title="Create Channel" onClose={onClose} /> + <Modal.Content> + <form id="create-channel-form" onSubmit={handleSubmit} className={styles.body}> + <div className={styles.typeSelector}> + <label className={styles.label}>CHANNEL TYPE</label> + <div className={styles.typeOptions}> + <button + type="button" + className={`${styles.typeOption} ${ + channelType === 'text' ? styles.typeOptionActive : '' + }`} + onClick={() => setChannelType('text')} + > + <Hash size={20} weight="bold" /> + <span>Text</span> + </button> + <button + type="button" + className={`${styles.typeOption} ${ + channelType === 'voice' ? styles.typeOptionActive : '' + }`} + onClick={() => setChannelType('voice')} + > + <SpeakerHigh size={20} /> + <span>Voice</span> + </button> + </div> + </div> + + <div className={styles.field}> + <label className={styles.label}>CHANNEL NAME</label> + <input + className={styles.input} + value={name} + onChange={(e) => setName(e.target.value)} + placeholder="new-channel" + autoFocus + /> + </div> + + <div className={styles.field}> + <label className={styles.label}>TOPIC (OPTIONAL)</label> + <input + className={styles.input} + value={topic} + onChange={(e) => setTopic(e.target.value)} + placeholder="What's this channel about?" + /> + </div> + + {error && ( + <div className={styles.field} style={{ color: 'var(--status-danger)', fontSize: '0.875rem' }}> + {error} + </div> + )} + </form> + </Modal.Content> + <Modal.Footer> + <Button variant="secondary" size="sm" onClick={onClose} type="button"> + Cancel + </Button> + <Button + variant="primary" + size="sm" + type="submit" + form="create-channel-form" + loading={loading} + disabled={!name.trim()} + > + Create Channel + </Button> + </Modal.Footer> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/layout/GuildHeaderDropdown.module.css b/packages/shared/src/components/layout/GuildHeaderDropdown.module.css new file mode 100644 index 0000000..4ace9dc --- /dev/null +++ b/packages/shared/src/components/layout/GuildHeaderDropdown.module.css @@ -0,0 +1,95 @@ +.overlay { + position: fixed; + inset: 0; + z-index: var(--z-index-contextmenu, 1000); + pointer-events: none; +} + +.menu { + position: fixed; + pointer-events: auto; + display: flex; + flex-direction: column; + gap: 2px; + padding: 8px 6px; + background-color: var(--background-floating, var(--background-primary)); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); + border-radius: 8px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); + animation: menuIn 140ms cubic-bezier(0.2, 0.9, 0.2, 1); +} + +@keyframes menuIn { + from { + opacity: 0; + transform: translateY(-4px) scale(0.98); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +.item { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 10px; + background: transparent; + border: none; + border-radius: 6px; + color: var(--text-primary); + font: inherit; + font-size: 0.875rem; + font-weight: 500; + text-align: left; + cursor: pointer; + transition: background-color 0.1s ease, color 0.1s ease; + width: 100%; +} + +.item:hover { + background-color: var(--brand-primary, #5865f2); + color: #ffffff; +} + +.item:hover .itemIcon { + color: #ffffff; +} + +.itemDanger { + color: var(--status-danger, #f23f43); +} + +.itemDanger .itemIcon { + color: var(--status-danger, #f23f43); +} + +.itemDanger:hover { + background-color: var(--status-danger, #f23f43); + color: #ffffff; +} + +.itemDanger:hover .itemIcon { + color: #ffffff; +} + +.itemIcon { + flex-shrink: 0; + color: var(--text-secondary); + transition: color 0.1s ease; +} + +.itemLabel { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.divider { + height: 1px; + margin: 4px 8px; + background-color: var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); +} diff --git a/packages/shared/src/components/layout/GuildHeaderDropdown.tsx b/packages/shared/src/components/layout/GuildHeaderDropdown.tsx new file mode 100644 index 0000000..f5b9629 --- /dev/null +++ b/packages/shared/src/components/layout/GuildHeaderDropdown.tsx @@ -0,0 +1,97 @@ +import { Gear, Plus, SignOut, UserPlus } from '@phosphor-icons/react'; +import { useEffect } from 'react'; +import { createPortal } from 'react-dom'; +import styles from './GuildHeaderDropdown.module.css'; + +interface GuildHeaderDropdownProps { + anchorRect: DOMRect; + onClose: () => void; + onInviteMembers?: () => void; + onCreateChannel?: () => void; + onCreateCategory?: () => void; + onServerSettings?: () => void; + onLeaveServer?: () => void; +} + +/** + * Simple portal-based dropdown menu shown below the server header + * button at the top of the channel sidebar. Escape closes it. + */ +export function GuildHeaderDropdown({ + anchorRect, + onClose, + onInviteMembers, + onCreateChannel, + onCreateCategory, + onServerSettings, + onLeaveServer, +}: GuildHeaderDropdownProps) { + useEffect(() => { + const onKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + document.addEventListener('keydown', onKey); + return () => document.removeEventListener('keydown', onKey); + }, [onClose]); + + const top = anchorRect.bottom + 8; + const left = anchorRect.left; + + const wrap = (handler?: () => void) => () => { + handler?.(); + onClose(); + }; + + return createPortal( + <div className={styles.overlay} onClick={onClose}> + <div + className={styles.menu} + style={{ position: 'fixed', top, left }} + onClick={(e) => e.stopPropagation()} + > + <button type="button" className={styles.item} onClick={wrap(onInviteMembers)}> + <span className={styles.itemIcon}> + <UserPlus size={18} /> + </span> + <span className={styles.itemLabel}>Invite People</span> + </button> + <div className={styles.divider} /> + <button type="button" className={styles.item} onClick={wrap(onCreateChannel)}> + <span className={styles.itemIcon}> + <Plus size={18} weight="bold" /> + </span> + <span className={styles.itemLabel}>Create Channel</span> + </button> + <button type="button" className={styles.item} onClick={wrap(onCreateCategory)}> + <span className={styles.itemIcon}> + <Plus size={18} weight="bold" /> + </span> + <span className={styles.itemLabel}>Create Category</span> + </button> + <div className={styles.divider} /> + <button type="button" className={styles.item} onClick={wrap(onServerSettings)}> + <span className={styles.itemIcon}> + <Gear size={18} /> + </span> + <span className={styles.itemLabel}>Server Settings</span> + </button> + {onLeaveServer && ( + <> + <div className={styles.divider} /> + <button + type="button" + className={`${styles.item} ${styles.itemDanger}`} + onClick={wrap(onLeaveServer)} + > + <span className={styles.itemIcon}> + <SignOut size={18} /> + </span> + <span className={styles.itemLabel}>Leave Server</span> + </button> + </> + )} + </div> + </div>, + document.body, + ); +} diff --git a/packages/shared/src/components/layout/GuildList.module.css b/packages/shared/src/components/layout/GuildList.module.css new file mode 100644 index 0000000..1270235 --- /dev/null +++ b/packages/shared/src/components/layout/GuildList.module.css @@ -0,0 +1,168 @@ +.list { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + padding-bottom: var(--spacing-2); + width: 100%; +} + +.guildItem { + position: relative; + display: flex; + height: 48px; + width: 100%; + align-items: center; + justify-content: center; + padding: calc((48px - var(--guild-icon-size)) / 2); + box-sizing: border-box; +} + +.guildIcon { + width: var(--guild-icon-size); + height: var(--guild-icon-size); + border-radius: var(--radius-full); + border: none; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: border-radius 70ms ease-out, background-color 70ms ease-out, color 70ms ease-out; + background-color: var(--guild-list-foreground); + color: var(--text-primary); + overflow: hidden; + position: relative; + flex-shrink: 0; + font-weight: 600; + font-size: 1.25rem; + background-size: cover; + background-position: center; + container-type: size; +} + +.guildIcon:hover { + border-radius: 30%; + background-color: var(--brand-primary); + color: #fff; +} + +.guildIcon.selected { + border-radius: 30%; + background-color: var(--brand-primary); + color: #fff; +} + +.homeIcon { + background-color: var(--guild-list-foreground); + color: var(--text-primary); +} + +.homeIcon:hover, +.homeIcon.selected { + background-color: var(--brand-primary); + color: #fff; +} + +.guildImage { + width: 100%; + height: 100%; + object-fit: cover; +} + +.guildInitials { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + display: block; + width: 100%; + text-align: center; + line-height: 1; + color: inherit; + font-weight: 600; + font-size: clamp(0.85rem, 45cqi, 1.35rem); + letter-spacing: 0.06em; + user-select: none; +} + +.addServer { + border-radius: var(--radius-full); + border: 2px dashed var(--background-modifier-accent); + background-color: transparent; + color: var(--text-primary); + transition: border-radius 70ms ease-out, border-color 70ms ease-out; +} + +.addServer:hover { + border-color: var(--text-primary); + border-radius: 30%; + background-color: transparent; + color: var(--text-primary); +} + +.divider { + width: 2rem; + height: 0.125rem; + background-color: var(--background-modifier-hover); + border-radius: 1px; + flex-shrink: 0; + margin-top: 6px; + margin-bottom: 6px; +} + +/* Guild selection indicator — left pill */ +.guildIndicator { + position: absolute; + left: 0; + top: 0; + bottom: 0; + display: flex; + align-items: center; + width: 0.5rem; + pointer-events: none; +} + +.guildIndicatorBar { + display: block; + width: 0.3rem; + border-radius: 0 var(--radius-full) var(--radius-full) 0; + background-color: var(--text-primary); +} + +/* Show a small indicator on hover for unselected items */ +.guildItem:hover .guildIndicatorBar { + height: 20px !important; +} + +/* Unread / pending count badge overlaid on a guild icon. Currently + used on the DM home bubble to surface pending stranger DMs, but + the class is generic enough to drop onto any server icon later + (e.g. for unread counts). Positioned at the bottom-right corner + of the 48px guildItem so it hangs off the icon edge, with a + border cut-out from the sidebar background so it reads cleanly + against darker icons. */ +.guildBadge { + position: absolute; + bottom: -4px; + right: 4px; + box-sizing: border-box; + min-width: 1.25rem; + height: 1.25rem; + padding: 0 4px; + display: flex; + align-items: center; + justify-content: center; + /* Rounded square, not pill — matches the Fluxer server-list + badge treatment. The outer 3px ring is drawn as a box-shadow + instead of a border so it doesn't affect the badge's hitbox + or layout calculations. The --guild-badge-surface custom + property lets future theming override the cut-out color. */ + border-radius: var(--radius-md); + background-color: var(--status-danger); + box-shadow: 0 0 0 3px var(--guild-badge-surface, var(--background-secondary)); + color: #fff; + font-size: 11px; + font-weight: 700; + line-height: 1; + pointer-events: none; + z-index: 1; +} diff --git a/packages/shared/src/components/layout/GuildList.tsx b/packages/shared/src/components/layout/GuildList.tsx new file mode 100644 index 0000000..269ec28 --- /dev/null +++ b/packages/shared/src/components/layout/GuildList.tsx @@ -0,0 +1,117 @@ +import { useQuery } from 'convex/react'; +import { Tooltip } from '@discord-clone/ui'; +import { ChatCircleDots } from '@phosphor-icons/react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { api } from '../../../../../convex/_generated/api'; +import { useIsMobile } from '../../hooks/useIsMobile'; +import styles from './GuildList.module.css'; + +interface GuildListProps { + onCreateServer?: () => void; +} + +/** + * Discord Clone is a single-server app, so the GuildList shows: + * - Home (DMs) button + * - A single "server" icon sourced from `serverSettings` + * The new UI's multi-server affordances (add-server, drag-to-reorder) are + * hidden until the Convex schema supports multiple servers. + */ +export function GuildList(_props: GuildListProps) { + const navigate = useNavigate(); + const location = useLocation(); + const isMobile = useIsMobile(); + const segments = location.pathname.split('/').filter(Boolean); + const topLevel = segments[0] === 'channels' ? segments[1] : undefined; + const serverSettings = useQuery(api.serverSettings.get); + const channels = useQuery(api.channels.list); + + const isHomeSelected = topLevel === '@me' || !topLevel; + const isServerSelected = !!topLevel && topLevel !== '@me' && topLevel !== 'you'; + + const handleHomeClick = () => navigate('/channels/@me'); + + const handleServerClick = () => { + // On mobile, land on the channel list view first so the user + // sees what's in the server before being dropped into a chat. + // On desktop we auto-open the first text channel because the + // full 3-column layout already shows the channel list beside + // the chat so it's not a jarring jump. + if (isMobile) { + navigate('/channels/home'); + return; + } + const firstText = channels?.find((c) => c.type === 'text'); + if (firstText) navigate(`/channels/home/${firstText._id}`); + else navigate('/channels/home'); + }; + + const serverName = serverSettings?.serverName || 'Server'; + const serverIconUrl: string | null = serverSettings?.iconUrl || null; + const initials = serverName + .split(/\s+/) + .map((w) => w[0]) + .join('') + .slice(0, 2) + .toUpperCase(); + + return ( + <div className={styles.list}> + <div className={styles.guildItem}> + <GuildIndicator active={isHomeSelected} /> + <Tooltip content="Direct Messages" placement="right"> + <button + className={`${styles.guildIcon} ${styles.homeIcon} ${isHomeSelected ? styles.selected : ''}`} + onClick={handleHomeClick} + > + <ChatCircleDots size={28} weight="fill" /> + </button> + </Tooltip> + </div> + + <div className={styles.divider} /> + + <div className={styles.guildItem}> + <GuildIndicator active={isServerSelected} /> + <Tooltip content={serverName} placement="right"> + <div + className={`${styles.guildIcon} ${isServerSelected ? styles.selected : ''}`} + role="button" + tabIndex={0} + onClick={handleServerClick} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + handleServerClick(); + } + }} + > + {serverIconUrl ? ( + <img src={serverIconUrl} alt={serverName} className={styles.guildImage} /> + ) : ( + <span className={styles.guildInitials}>{initials}</span> + )} + </div> + </Tooltip> + </div> + + {/* Add-a-server button removed — this build is a single-server + deployment so there's nothing to add. Drop the trailing + divider + Plus tile entirely. */} + </div> + ); +} + +function GuildIndicator({ active }: { active: boolean }) { + return ( + <div className={styles.guildIndicator}> + <span + className={styles.guildIndicatorBar} + style={{ + height: active ? '40px' : '0px', + transition: 'height 0.15s ease', + }} + /> + </div> + ); +} diff --git a/packages/shared/src/components/layout/GuildNavbar.module.css b/packages/shared/src/components/layout/GuildNavbar.module.css new file mode 100644 index 0000000..8f19f0d --- /dev/null +++ b/packages/shared/src/components/layout/GuildNavbar.module.css @@ -0,0 +1,463 @@ +.navbar { + display: grid; + grid-template-rows: auto 1fr; + height: 100%; + min-height: 0; + background: var(--background-secondary); + overflow: hidden; + user-select: none; + -webkit-user-select: none; +} + +.header { + display: flex; + align-items: center; + gap: var(--spacing-1); + width: 100%; + min-height: var(--layout-header-height, 3.5rem); + padding: 0 var(--spacing-4); + font-weight: 600; + font-size: 1rem; + font-family: inherit; + text-align: left; + border: none; + border-bottom: 1px solid var(--user-area-divider-color); + background: var(--background-secondary); + cursor: pointer; + color: var(--text-primary); + flex-shrink: 0; + transition: background-color var(--transition-normal); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.header:hover { + background-color: var(--background-modifier-hover); +} + +.serverName { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + line-height: 1.25rem; + max-height: 1.25rem; +} + +.headerCaret { + margin-left: auto; + flex-shrink: 0; + height: 1rem; + width: 1rem; + transition: transform 0.15s ease; +} + +.headerCaretOpen { + transform: rotate(180deg); +} + +.header { + cursor: pointer; + transition: background-color 0.15s ease; +} + +.header:hover { + background-color: color-mix(in srgb, var(--text-primary) 3%, transparent); +} + +.headerOpen { + background-color: color-mix(in srgb, var(--text-primary) 5%, transparent); +} + +.channelList { + flex: 1; + overflow-y: auto; + padding-right: var(--spacing-2); + background-color: var(--background-secondary); + /* Hide the scrollbar but keep scrolling. Firefox + IE first, + then the WebKit pseudo-element so it disappears in Chrome / + Safari / Edge / Electron without losing wheel + drag scroll. */ + scrollbar-width: none; + -ms-overflow-style: none; +} + +.channelList::-webkit-scrollbar { + display: none; +} + +/* ── Categories ──────────────────────────────────────────────────────── */ + +.category { + display: flex; + flex-direction: column; + gap: 1px; +} + +.category + .category { + margin-top: var(--spacing-1); +} + +.categoryRow { + display: flex; + align-items: center; + margin-left: 0.5rem; + margin-right: 0; + margin-top: 0.25rem; +} + +.categoryHeader { + position: relative; + display: flex; + align-items: center; + gap: 0.25rem; + flex: 1; + min-width: 0; + padding: 0.375rem 0.5rem; + background: none; + border: none; + cursor: pointer; + color: var(--text-tertiary-muted, var(--text-muted)); + font-size: 0.875rem; + font-weight: 600; + line-height: 1.25rem; + text-align: left; + font-family: inherit; + border-radius: 0.375rem; + transition: color 0.15s ease; +} + +.categoryHeader:hover { + color: var(--text-primary); +} + +.categoryAddButton { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + padding: 0; + margin-right: 0.5rem; + background: none; + border: none; + border-radius: 4px; + cursor: pointer; + color: var(--text-tertiary-muted, var(--text-muted)); + opacity: 0; + transition: opacity 0.15s, color 0.15s, background-color 0.15s; +} + +.categoryRow:hover .categoryAddButton { + opacity: 1; +} + +.categoryAddButton:hover { + color: var(--text-primary); + background-color: color-mix(in srgb, var(--text-primary) 10%, transparent); +} + +.categoryName { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.categoryChevron { + height: 0.75rem; + width: 0.75rem; + flex-shrink: 0; + transition: transform 0.2s ease; +} + +.categoryCollapsed .categoryChevron, +.categoryChevronCollapsed { + transform: rotate(-90deg); +} + +/* ── Channel Items ───────────────────────────────────────────────────── */ + +.channelItem { + position: relative; + margin-left: 0.5rem; + margin-right: 0; + display: flex; + align-items: center; + gap: 0.375rem; + width: calc(100% - 0.5rem); + padding: 0.375rem 0.5rem; + border-radius: 0.375rem; + background: none; + border: none; + cursor: pointer; + color: var(--text-tertiary-muted, var(--text-muted)); + font-size: 1rem; + line-height: 1.25rem; + text-align: left; + font-family: inherit; + transition: background-color 0.1s ease, color 0.1s ease; +} + +.channelItem:hover { + background-color: var(--background-modifier-hover); + color: var(--text-chat, var(--text-secondary)); +} + +.channelItem:hover .channelName { + color: var(--text-chat, var(--text-secondary)); +} + +.channelSelected { + background-color: var(--background-modifier-selected) !important; + color: var(--text-primary) !important; +} + +.channelSelected .channelName, +.channelSelected .channelIcon { + color: var(--text-primary) !important; +} + +.channelIcon { + width: 1.25rem; + height: 1.25rem; + flex-shrink: 0; + color: var(--text-tertiary-muted); +} + +.channelName { + flex: 1 1 auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-weight: 500; + font-size: 1rem; + line-height: 1.25rem; + max-height: 1.25rem; + min-width: 0; +} + +.channelUnread { + color: var(--text-primary) !important; + font-weight: 600; +} + +/* ── Unread Indicators ───────────────────────────────────────────────── */ + +.channelItemUnread { + position: relative; +} + +.channelItemUnread::before { + content: ""; + position: absolute; + left: -0.25rem; + top: 50%; + transform: translateY(-50%); + height: 0.5rem; + width: 0.5rem; + border-radius: 0 9999px 9999px 0; + background-color: var(--text-primary); +} + +.unreadBadge { + flex-shrink: 0; + width: 8px; + height: 8px; + border-radius: 50%; + background-color: var(--text-primary); + margin-left: auto; +} + +.mentionBadge { + flex-shrink: 0; + min-width: 16px; + height: 16px; + padding: 0 4px; + border-radius: 8px; + background-color: var(--status-danger); + color: #fff; + font-size: 0.75rem; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + margin-left: auto; +} + +/* ── Hover-reveal channel settings gear ────────────────────────────── + + The wrapper holds both the channel button and the gear button as + siblings (nesting interactive elements inside a button is invalid + HTML). The gear is absolute-positioned at the right edge of the row + and hidden by default. + + Visibility rules, mirroring Fluxer's ChannelItem.module.css: + - display: none by default + - display: flex when the wrapper is hovered on desktop (hover: hover) + - display: flex when the channel is selected (data-selected='true') + + On hover or select, the unread/mention badges are hidden so the gear + takes their place — same pattern Discord uses. +*/ +.channelItemWrapper { + position: relative; + min-width: 0; + overflow: hidden; +} + +.channelSettingsGear { + display: none; + position: absolute; + right: 10px; + top: 6px; + width: 24px; + height: 24px; + align-items: center; + justify-content: center; + padding: 0; + background: transparent; + border: none; + border-radius: 4px; + color: var(--text-tertiary-muted, var(--text-muted)); + cursor: pointer; + transition: color 0.15s ease, background-color 0.15s ease; + z-index: 2; +} + +.channelSettingsGear:hover { + color: var(--text-primary); + background-color: color-mix(in srgb, var(--text-primary) 10%, transparent); +} + +.channelItemWrapper[data-selected="true"] .channelSettingsGear { + display: flex; + color: var(--text-primary); +} + +.channelItemWrapper[data-selected="true"] .channelItem { + padding-right: 2.25rem; +} + +@media (hover: hover) and (pointer: fine) { + .channelItemWrapper:hover .channelSettingsGear { + display: flex; + } + + .channelItemWrapper:hover .channelItem { + padding-right: 2.25rem; + } + + .channelItemWrapper:hover .mentionBadge, + .channelItemWrapper:hover .unreadBadge { + display: none; + } +} + +.channelItemWrapper[data-selected="true"] .mentionBadge, +.channelItemWrapper[data-selected="true"] .unreadBadge { + display: none; +} + +/* ── Voice Channel Styles ────────────────────────────────────────────── */ + +.voiceUsers { + display: flex; + flex-direction: column; + gap: 1px; + padding-left: 2rem; + margin-top: 2px; +} + +.voiceUser { + display: flex; + align-items: center; + gap: 0.375rem; + padding: 0.25rem 0.5rem; + border-radius: 0.375rem; + font-size: 0.875rem; + font-weight: 500; + line-height: 1.25rem; + color: var(--text-primary-muted); + cursor: pointer; + transition: background-color 150ms, color 150ms; +} + +.voiceUser:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +.voiceUserAvatar { + width: 20px; + height: 20px; + border-radius: 50%; + flex-shrink: 0; +} + +.voiceUserName { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* ── Drag-and-drop: drop indicator + overlay ─────────────────── */ + +.dropIndicator { + height: 2px; + margin: 0 8px; + border-radius: 1px; + background: var(--brand-primary); + position: relative; +} + +.dropIndicator::before, +.dropIndicator::after { + content: ""; + position: absolute; + top: -3px; + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--brand-primary); +} + +.dropIndicator::before { + left: -4px; +} + +.dropIndicator::after { + right: -4px; +} + +.dragOverlay { + display: flex; + align-items: center; + gap: 6px; + padding: 6px 10px; + border-radius: 6px; + background: var(--background-secondary); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + color: var(--text-primary); + font-size: 0.9375rem; + font-weight: 500; + pointer-events: none; + white-space: nowrap; +} + +/* ── Light theme overrides ──────────────────────────────────── + In light theme every channel name and category name reads as + --text-primary across default / hover / selected / unread so + the sidebar labels stay high-contrast against the near-white + background instead of fading to the muted tertiary shade. */ +[data-theme="light"] .channelItem, +[data-theme="light"] .channelItem:hover, +[data-theme="light"] .channelItem:hover .channelName, +[data-theme="light"] .channelItem .channelIcon, +[data-theme="light"] .categoryHeader, +[data-theme="light"] .categoryHeader .categoryChevron { + color: var(--text-primary); +} diff --git a/packages/shared/src/components/layout/GuildNavbar.tsx b/packages/shared/src/components/layout/GuildNavbar.tsx new file mode 100644 index 0000000..43c1185 --- /dev/null +++ b/packages/shared/src/components/layout/GuildNavbar.tsx @@ -0,0 +1,811 @@ +import { + DndContext, + type DragEndEvent, + PointerSensor, + closestCenter, + useSensor, + useSensors, +} from '@dnd-kit/core'; +import { + SortableContext, + useSortable, + verticalListSortingStrategy, +} from '@dnd-kit/sortable'; +import { CSS } from '@dnd-kit/utilities'; +import { useMutation, useQuery } from 'convex/react'; +import { + CaretDown, + Gear, + Hash, + MicrophoneSlash, + Monitor, + Plus, + SpeakerHigh, + SpeakerSlash, +} from '@phosphor-icons/react'; +import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { Avatar } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import { useVoice } from '../../contexts/VoiceContext'; +import { GuildHeaderDropdown } from './GuildHeaderDropdown'; +import { ChannelListContextMenu } from './ChannelListContextMenu'; +import { MobileServerActionsSheet } from './MobileServerActionsSheet'; +import { useIsMobile } from '../../hooks/useIsMobile'; +import { VoiceUserContextMenu } from '../voice/VoiceUserContextMenu'; +import { MemberProfileModal } from '../member/MemberProfileModal'; +import styles from './GuildNavbar.module.css'; + +type VoiceParticipant = { + userId: string; + username: string; + avatarUrl?: string | null; + isMuted?: boolean; + isDeafened?: boolean; + isScreenSharing?: boolean; +}; + +type ChannelDoc = { + _id: string; + name: string; + type: string; + categoryId?: string | null; + position?: number | null; +}; + +type CategoryDoc = { + _id: string; + name: string; + position: number; +}; + +export function GuildNavbar() { + const navigate = useNavigate(); + const location = useLocation(); + const segments = location.pathname.split('/').filter(Boolean); + const selectedChannelId = segments[0] === 'channels' ? segments[2] || null : null; + const serverSettings = useQuery(api.serverSettings.get); + const categoriesRaw = useQuery(api.categories.list); + const channelsRaw = useQuery(api.channels.list); + + const voice = useVoice(); + const voiceStates: Record<string, VoiceParticipant[]> = + ((voice as any)?.voiceStates ?? {}) as Record<string, VoiceParticipant[]>; + + const reorderChannels = useMutation(api.channels.reorderChannels); + const reorderCategories = useMutation(api.categories.reorder); + const renameChannel = useMutation(api.channels.rename); + const deleteChannel = useMutation(api.channels.remove); + const renameCategory = useMutation(api.categories.rename); + const deleteCategory = useMutation(api.categories.remove); + + type ContextMenuState = + | { + x: number; + y: number; + kind: 'channel'; + channelId: string; + channelName: string; + channelType: string; + } + | { + x: number; + y: number; + kind: 'category'; + categoryId: string; + categoryName: string; + }; + const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null); + + // ── Unread tracking ───────────────────────────────────────────── + // The readState API stores lastReadTimestamp per (user, channel). + // We compare that to the latest message timestamp per channel to + // derive an unread flag. There is no mentionCount tracking on the + // backend yet, so the mention pill is wired up but will only render + // if a future version of the API starts returning one. + const userId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const readStates = useQuery( + api.readState.getAllReadStates, + userId ? { userId: userId as any } : 'skip', + ); + const channelIdList = useMemo( + () => (channelsRaw ?? []).map((c: any) => c._id as any), + [channelsRaw], + ); + const latestTimestamps = useQuery( + api.readState.getLatestMessageTimestamps, + channelIdList.length > 0 ? { channelIds: channelIdList } : 'skip', + ); + + const unreadMap = useMemo(() => { + const readMap = new Map<string, number>(); + for (const r of readStates ?? []) { + readMap.set(r.channelId as unknown as string, r.lastReadTimestamp); + } + const latestMap = new Map<string, number>(); + for (const l of latestTimestamps ?? []) { + latestMap.set(l.channelId as unknown as string, l.latestTimestamp); + } + const out = new Map< + string, + { unreadCount: number; mentionCount: number } + >(); + for (const [chId, latest] of latestMap) { + const lastRead = readMap.get(chId) ?? 0; + const hasUnread = latest > lastRead; + out.set(chId, { + unreadCount: hasUnread ? 1 : 0, + mentionCount: 0, + }); + } + return out; + }, [readStates, latestTimestamps]); + + const [collapsedCategories, setCollapsedCategories] = useState<Set<string>>(new Set()); + const headerRef = useRef<HTMLButtonElement>(null); + const [headerDropdownRect, setHeaderDropdownRect] = useState<DOMRect | null>(null); + const [mobileServerSheetOpen, setMobileServerSheetOpen] = useState(false); + const [voiceMenu, setVoiceMenu] = useState< + { x: number; y: number; userId: string; username: string } | null + >(null); + const [voiceProfileFor, setVoiceProfileFor] = useState<string | null>(null); + const isMobile = useIsMobile(); + + const categories = useMemo<CategoryDoc[]>( + () => + (categoriesRaw ?? []) + .slice() + .sort((a: any, b: any) => (a.position ?? 0) - (b.position ?? 0)) + .map((c: any) => ({ _id: c._id, name: c.name, position: c.position ?? 0 })), + [categoriesRaw], + ); + const channels = useMemo<ChannelDoc[]>( + () => + (channelsRaw ?? []) + .slice() + .sort((a: any, b: any) => (a.position ?? 0) - (b.position ?? 0)) + .map((c: any) => ({ + _id: c._id, + name: c.name, + type: c.type, + categoryId: c.categoryId ?? null, + position: c.position ?? 0, + })), + [channelsRaw], + ); + + const serverName = serverSettings?.serverName || 'Server'; + + const toggleCategory = (id: string) => { + setCollapsedCategories((prev) => { + const next = new Set(prev); + if (next.has(id)) next.delete(id); + else next.add(id); + return next; + }); + }; + + // Alt+Left / Alt+Right collapse / expand the category containing the + // currently selected channel. Matches the new UI's keyboard shortcut. + useEffect(() => { + const onKeyDown = (e: KeyboardEvent) => { + if (!e.altKey) return; + if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return; + if (!selectedChannelId) return; + const target = e.target as HTMLElement | null; + if ( + target && + (target.tagName === 'INPUT' || + target.tagName === 'TEXTAREA' || + target.isContentEditable) + ) { + return; + } + const channel = (channelsRaw ?? []).find( + (c: any) => c._id === selectedChannelId, + ); + const catId = channel?.categoryId; + if (!catId) return; + e.preventDefault(); + setCollapsedCategories((prev) => { + const next = new Set(prev); + const isCollapsed = next.has(catId); + if (e.key === 'ArrowLeft' && !isCollapsed) { + next.add(catId); + } else if (e.key === 'ArrowRight' && isCollapsed) { + next.delete(catId); + } + return next; + }); + }; + window.addEventListener('keydown', onKeyDown); + return () => window.removeEventListener('keydown', onKeyDown); + }, [channelsRaw, selectedChannelId]); + + const handleChannelClick = (channelId: string) => { + // Voice channels auto-connect on click (desktop) when the user + // isn't already in that specific channel — matches the new UI. + // Mobile still just navigates; the user has to tap Join on the + // channel view to start capturing audio. + if (!isMobile) { + const channel = (channelsRaw ?? []).find((c: any) => c._id === channelId); + if ( + channel?.type === 'voice' && + (voice as any)?.activeChannelId !== channelId + ) { + const myId = + typeof localStorage !== 'undefined' + ? localStorage.getItem('userId') + : null; + if (myId) { + void (voice as any)?.connectToVoice?.( + channelId, + channel.name, + myId, + false, + ); + } + } + } + navigate(`/channels/home/${channelId}`); + }; + + const handleCreateChannelInCategory = (categoryId: string) => { + window.dispatchEvent( + new CustomEvent('brycord:open-create-channel', { detail: { categoryId } }), + ); + }; + + const handleOpenChannelSettings = (channelId: string) => { + window.dispatchEvent( + new CustomEvent('brycord:open-channel-settings', { detail: { channelId } }), + ); + }; + + const toggleHeader = () => { + if (isMobile) { + setMobileServerSheetOpen((v) => !v); + return; + } + if (headerDropdownRect) { + setHeaderDropdownRect(null); + return; + } + const rect = headerRef.current?.getBoundingClientRect(); + if (rect) setHeaderDropdownRect(rect); + }; + + const uncategorized = channels.filter((c) => !c.categoryId); + const byCategory = new Map<string, ChannelDoc[]>(); + for (const c of channels) { + if (c.categoryId) { + if (!byCategory.has(c.categoryId)) byCategory.set(c.categoryId, []); + byCategory.get(c.categoryId)!.push(c); + } + } + + // Drag-and-drop: pointer sensor with 4px activation distance so plain + // clicks still select a channel. Categories are in one sortable list; + // each category's channel list + the uncategorized list are separate + // sortable contexts. + const sensors = useSensors( + useSensor(PointerSensor, { activationConstraint: { distance: 4 } }), + ); + + const handleDragEndCategories = (event: DragEndEvent) => { + const { active, over } = event; + if (!over || active.id === over.id) return; + const activeIdx = categories.findIndex((c) => `cat:${c._id}` === active.id); + const overIdx = categories.findIndex((c) => `cat:${c._id}` === over.id); + if (activeIdx === -1 || overIdx === -1) return; + const next = categories.slice(); + const [moved] = next.splice(activeIdx, 1); + next.splice(overIdx, 0, moved); + const updates = next.map((c, i) => ({ id: c._id as any, position: i })); + void reorderCategories({ updates }); + }; + + const handleDragEndChannels = ( + event: DragEndEvent, + categoryId: string | null, + ) => { + const { active, over } = event; + if (!over || active.id === over.id) return; + const list = categoryId ? (byCategory.get(categoryId) ?? []) : uncategorized; + const activeIdx = list.findIndex((c) => `ch:${c._id}` === active.id); + const overIdx = list.findIndex((c) => `ch:${c._id}` === over.id); + if (activeIdx === -1 || overIdx === -1) return; + const next = list.slice(); + const [moved] = next.splice(activeIdx, 1); + next.splice(overIdx, 0, moved); + const updates = next.map((c, i) => ({ + id: c._id as any, + categoryId: (categoryId ?? undefined) as any, + position: i, + })); + void reorderChannels({ updates }); + }; + + return ( + <div className={styles.navbar}> + <button + ref={headerRef} + type="button" + className={`${styles.header} ${headerDropdownRect ? styles.headerOpen : ''}`} + onClick={toggleHeader} + > + <span className={styles.serverName}>{serverName}</span> + <CaretDown + size={18} + weight="bold" + className={`${styles.headerCaret} ${headerDropdownRect ? styles.headerCaretOpen : ''}`} + /> + </button> + + {headerDropdownRect && ( + <GuildHeaderDropdown + anchorRect={headerDropdownRect} + onClose={() => setHeaderDropdownRect(null)} + onServerSettings={() => { + window.dispatchEvent(new CustomEvent('brycord:open-server-settings')); + }} + onInviteMembers={() => { + window.dispatchEvent(new CustomEvent('brycord:open-invite')); + }} + onCreateChannel={() => { + window.dispatchEvent(new CustomEvent('brycord:open-create-channel')); + }} + onCreateCategory={() => { + window.dispatchEvent(new CustomEvent('brycord:open-create-category')); + }} + /> + )} + + <div className={styles.channelList}> + <DndContext + sensors={sensors} + collisionDetection={closestCenter} + onDragEnd={(e) => handleDragEndChannels(e, null)} + > + <SortableContext + items={uncategorized.map((c) => `ch:${c._id}`)} + strategy={verticalListSortingStrategy} + > + {uncategorized.map((ch) => { + const unread = unreadMap.get(ch._id); + const participants = voiceStates[ch._id] ?? []; + return ( + <SortableChannelRow + key={ch._id} + id={ch._id} + name={ch.name} + type={ch.type} + selected={selectedChannelId === ch._id} + unreadCount={unread?.unreadCount ?? 0} + mentionCount={unread?.mentionCount ?? 0} + participants={participants} + onClick={() => handleChannelClick(ch._id)} + onOpenSettings={() => handleOpenChannelSettings(ch._id)} + onVoiceUserContextMenu={(p, e) => { + setVoiceMenu({ + x: e.clientX, + y: e.clientY, + userId: p.userId, + username: p.username, + }); + }} + onContextMenu={(e) => { + e.preventDefault(); + e.stopPropagation(); + setContextMenu({ + x: e.clientX, + y: e.clientY, + kind: 'channel', + channelId: ch._id, + channelName: ch.name, + channelType: ch.type, + }); + }} + /> + ); + })} + </SortableContext> + </DndContext> + + <DndContext + sensors={sensors} + collisionDetection={closestCenter} + onDragEnd={handleDragEndCategories} + > + <SortableContext + items={categories.map((c) => `cat:${c._id}`)} + strategy={verticalListSortingStrategy} + > + {categories.map((category) => { + const catChannels = byCategory.get(category._id) ?? []; + const isCollapsed = collapsedCategories.has(category._id); + // Collapsed categories still show the currently + // selected channel row so the user never loses + // their place. Matches the new UI. + const visibleChannels = isCollapsed + ? catChannels.filter((c) => c._id === selectedChannelId) + : catChannels; + return ( + <SortableCategory + key={category._id} + id={category._id} + name={category.name} + collapsed={isCollapsed} + onToggle={() => toggleCategory(category._id)} + onAddChannel={() => handleCreateChannelInCategory(category._id)} + onContextMenu={(e) => { + e.preventDefault(); + e.stopPropagation(); + setContextMenu({ + x: e.clientX, + y: e.clientY, + kind: 'category', + categoryId: category._id, + categoryName: category.name, + }); + }} + > + {visibleChannels.length > 0 && ( + <DndContext + sensors={sensors} + collisionDetection={closestCenter} + onDragEnd={(e) => handleDragEndChannels(e, category._id)} + > + <SortableContext + items={visibleChannels.map((c) => `ch:${c._id}`)} + strategy={verticalListSortingStrategy} + > + {visibleChannels.map((ch) => { + const unread = unreadMap.get(ch._id); + const participants = voiceStates[ch._id] ?? []; + return ( + <SortableChannelRow + key={ch._id} + id={ch._id} + name={ch.name} + type={ch.type} + selected={selectedChannelId === ch._id} + unreadCount={unread?.unreadCount ?? 0} + mentionCount={unread?.mentionCount ?? 0} + participants={participants} + onClick={() => handleChannelClick(ch._id)} + onOpenSettings={() => + handleOpenChannelSettings(ch._id) + } + onVoiceUserContextMenu={(p, e) => { + setVoiceMenu({ + x: e.clientX, + y: e.clientY, + userId: p.userId, + username: p.username, + }); + }} + onContextMenu={(e) => { + e.preventDefault(); + e.stopPropagation(); + setContextMenu({ + x: e.clientX, + y: e.clientY, + kind: 'channel', + channelId: ch._id, + channelName: ch.name, + channelType: ch.type, + }); + }} + /> + ); + })} + </SortableContext> + </DndContext> + )} + </SortableCategory> + ); + })} + </SortableContext> + </DndContext> + </div> + + <MobileServerActionsSheet + isOpen={mobileServerSheetOpen} + onClose={() => setMobileServerSheetOpen(false)} + /> + + {voiceMenu && ( + <VoiceUserContextMenu + x={voiceMenu.x} + y={voiceMenu.y} + userId={voiceMenu.userId} + username={voiceMenu.username} + onViewProfile={() => { + setVoiceProfileFor(voiceMenu.userId); + setVoiceMenu(null); + }} + onClose={() => setVoiceMenu(null)} + /> + )} + + <MemberProfileModal + isOpen={!!voiceProfileFor} + onClose={() => setVoiceProfileFor(null)} + member={voiceProfileFor ? { userId: voiceProfileFor } : null} + /> + + {contextMenu && ( + <ChannelListContextMenu + x={contextMenu.x} + y={contextMenu.y} + channelId={contextMenu.kind === 'channel' ? contextMenu.channelId : undefined} + categoryId={ + contextMenu.kind === 'category' ? contextMenu.categoryId : undefined + } + channelTypeLabel={ + contextMenu.kind === 'channel' && contextMenu.channelType === 'voice' + ? 'Voice Channel' + : 'Channel' + } + onCreateChannel={() => + window.dispatchEvent(new CustomEvent('brycord:open-create-channel')) + } + onCreateCategory={() => + window.dispatchEvent(new CustomEvent('brycord:open-create-category')) + } + onRenameChannel={ + contextMenu.kind === 'channel' + ? () => { + const next = window.prompt( + 'Rename channel', + contextMenu.channelName, + ); + setContextMenu(null); + if (next && next.trim() && next.trim() !== contextMenu.channelName) { + void renameChannel({ + id: contextMenu.channelId as any, + name: next.trim(), + }); + } + } + : undefined + } + onDeleteChannel={ + contextMenu.kind === 'channel' + ? () => { + const confirmed = window.confirm( + `Delete #${contextMenu.channelName}? This cannot be undone.`, + ); + setContextMenu(null); + if (confirmed) { + void deleteChannel({ id: contextMenu.channelId as any }); + } + } + : undefined + } + onRenameCategory={ + contextMenu.kind === 'category' + ? () => { + const next = window.prompt( + 'Rename category', + contextMenu.categoryName, + ); + setContextMenu(null); + if (next && next.trim() && next.trim() !== contextMenu.categoryName) { + void renameCategory({ + id: contextMenu.categoryId as any, + name: next.trim(), + }); + } + } + : undefined + } + onDeleteCategory={ + contextMenu.kind === 'category' + ? () => { + const confirmed = window.confirm( + `Delete category "${contextMenu.categoryName}"? Channels in it will become uncategorized.`, + ); + setContextMenu(null); + if (confirmed) { + void deleteCategory({ id: contextMenu.categoryId as any }); + } + } + : undefined + } + onClose={() => setContextMenu(null)} + /> + )} + </div> + ); +} + +interface SortableChannelRowProps { + id: string; + name: string; + type: string; + selected: boolean; + unreadCount?: number; + mentionCount?: number; + participants?: VoiceParticipant[]; + onClick: () => void; + onOpenSettings: () => void; + onContextMenu?: (e: React.MouseEvent) => void; + onVoiceUserContextMenu?: ( + participant: VoiceParticipant, + e: React.MouseEvent, + ) => void; +} + +function SortableChannelRow({ + id, + name, + type, + selected, + unreadCount = 0, + mentionCount = 0, + participants = [], + onClick, + onOpenSettings, + onContextMenu, + onVoiceUserContextMenu, +}: SortableChannelRowProps) { + const { attributes, listeners, setNodeRef, transform, transition, isDragging } = + useSortable({ id: `ch:${id}` }); + const style = { + transform: CSS.Transform.toString(transform), + transition, + opacity: isDragging ? 0.4 : 1, + }; + const isVoice = type === 'voice'; + const hasUnread = unreadCount > 0 && !selected; + const hasMention = mentionCount > 0; + const showParticipants = isVoice && participants.length > 0; + return ( + <> + <div + ref={setNodeRef} + style={style} + className={`${styles.channelItemWrapper} ${hasUnread ? styles.channelItemUnread : ''}`} + data-selected={selected ? 'true' : undefined} + data-unread={hasUnread ? 'true' : undefined} + onContextMenu={onContextMenu} + {...attributes} + {...listeners} + > + <button + type="button" + className={`${styles.channelItem} ${selected ? styles.channelSelected : ''}`} + onClick={onClick} + > + {isVoice ? ( + <SpeakerHigh size={20} className={styles.channelIcon} /> + ) : ( + <Hash size={20} weight="bold" className={styles.channelIcon} /> + )} + <span + className={`${styles.channelName} ${hasUnread ? styles.channelUnread : ''}`} + > + {name} + </span> + {hasMention && ( + <span className={styles.mentionBadge}> + {mentionCount > 99 ? '99+' : mentionCount} + </span> + )} + </button> + <button + type="button" + className={styles.channelSettingsGear} + onClick={(e) => { + e.stopPropagation(); + onOpenSettings(); + }} + aria-label="Edit Channel" + title="Edit Channel" + > + <Gear size={16} /> + </button> + </div> + {showParticipants && ( + <div + className={styles.voiceUsers} + onPointerDown={(e) => e.stopPropagation()} + > + {participants.map((p) => ( + <div + key={p.userId} + className={styles.voiceUser} + title={p.username} + onContextMenu={(e) => { + if (!onVoiceUserContextMenu) return; + e.preventDefault(); + e.stopPropagation(); + onVoiceUserContextMenu(p, e); + }} + > + <Avatar + src={p.avatarUrl ?? null} + size={20} + fallback={p.username} + className={styles.voiceUserAvatar} + /> + <span className={styles.voiceUserName}>{p.username}</span> + {p.isMuted && ( + <MicrophoneSlash size={12} weight="fill" /> + )} + {p.isDeafened && ( + <SpeakerSlash size={12} weight="fill" /> + )} + {p.isScreenSharing && <Monitor size={12} weight="fill" />} + </div> + ))} + </div> + )} + </> + ); +} + +interface SortableCategoryProps { + id: string; + name: string; + collapsed: boolean; + onToggle: () => void; + onAddChannel: () => void; + onContextMenu?: (e: React.MouseEvent) => void; + children?: ReactNode; +} + +function SortableCategory({ + id, + name, + collapsed, + onToggle, + onAddChannel, + onContextMenu, + children, +}: SortableCategoryProps) { + const { attributes, listeners, setNodeRef, transform, transition, isDragging } = + useSortable({ id: `cat:${id}` }); + const style = { + transform: CSS.Transform.toString(transform), + transition, + opacity: isDragging ? 0.4 : 1, + }; + return ( + <div ref={setNodeRef} style={style} className={styles.category}> + <div + className={styles.categoryRow} + onContextMenu={onContextMenu} + {...attributes} + {...listeners} + > + <button type="button" className={styles.categoryHeader} onClick={onToggle}> + <CaretDown + size={12} + weight="bold" + className={`${styles.categoryChevron} ${ + collapsed ? styles.categoryChevronCollapsed : '' + }`} + /> + <span className={styles.categoryName}>{name}</span> + </button> + <button + type="button" + className={styles.categoryAddButton} + onClick={(e) => { + e.stopPropagation(); + onAddChannel(); + }} + aria-label="Create Channel" + title="Create Channel" + > + <Plus size={16} weight="bold" /> + </button> + </div> + {children} + </div> + ); +} diff --git a/packages/shared/src/components/layout/GuildsLayout.module.css b/packages/shared/src/components/layout/GuildsLayout.module.css new file mode 100644 index 0000000..0c59259 --- /dev/null +++ b/packages/shared/src/components/layout/GuildsLayout.module.css @@ -0,0 +1,197 @@ +.wrapper { + display: flex; + flex-direction: column; + /* Prefer dvh so the mobile browser chrome (address bar showing/hiding) + doesn't crop the app below the keyboard. Falls back to vh on older + browsers that don't support the dynamic viewport unit. */ + height: 100vh; + height: 100dvh; + width: 100vw; + overflow: hidden; +} + +.container { + display: flex; + flex: 1; + min-height: 0; + position: relative; + /* Matches the guild list + TitleBar background so the sidebar's + rounded top-left corner has the right colour to cut into. The + content column paints its own bg on top so this only peeks + through the 8px corner radius. */ + background-color: var(--background-secondary); +} + +.guildList { + width: var(--layout-guild-list-width); + min-width: var(--layout-guild-list-width); + background-color: var(--background-secondary); + display: flex; + flex-direction: column; + align-items: center; + padding-top: var(--spacing-1); + padding-bottom: calc(var(--layout-user-area-height) + var(--spacing-2)); + overflow-y: auto; + overflow-x: hidden; + scrollbar-width: none; +} + +.guildList::-webkit-scrollbar { + display: none; +} + +.sidebar { + width: var(--layout-sidebar-width); + min-width: var(--layout-sidebar-width); + background-color: var(--background-secondary); + display: flex; + flex-direction: column; + position: relative; + /* Reserve space at bottom for the UserArea overlay */ + padding-bottom: var(--layout-user-area-height); + border-left: 1px solid var(--user-area-divider-color); +} + +/* Frameless Electron: the TitleBar sits flush with the guild list + (same background, no separator) but the sidebar + content cards + "hang" below it with a hairline top border. The sidebar also + rounds its top-left corner so the join with the title bar reads + as an inset panel, matching the Fluxer reference. */ +:global(body.has-window-controls) .sidebar { + border-top: 1px solid var(--background-tertiary); + border-top-left-radius: 8px; +} + +:global(body.has-window-controls) .content { + border-top: 1px solid var(--background-tertiary); +} + +/* Inset the vertical divider so it starts below the horizontal + hairline instead of crossing over it. */ +:global(body.has-window-controls) .sidebarDivider { + top: 1px; +} + +/* Divider between sidebar and content */ +.sidebarDivider { + position: absolute; + top: 0; + bottom: 0; + left: calc(var(--layout-guild-list-width) + var(--layout-sidebar-width)); + width: 1px; + background: var(--user-area-divider-color); + pointer-events: none; + z-index: 2; +} + +.content { + flex: 1; + min-width: 0; + background-color: var(--background-secondary-lighter); + display: flex; + flex-direction: column; + overflow: hidden; +} + +/* UserArea wrapper — absolutely positioned across guild list + sidebar */ +.userAreaWrapper { + position: absolute; + bottom: 0; + left: 0; + width: calc(var(--layout-guild-list-width) + var(--layout-sidebar-width)); + display: flex; + flex-direction: column; + justify-content: flex-end; + pointer-events: none; + z-index: var(--z-index-elevated-1, 10); +} + +.userAreaWrapper > * { + pointer-events: auto; +} + +/* ── Mobile layout (≤768px) ────────────────────────────────────────── + + Fluxer/Discord-style single-column mobile: EITHER the nav column + (guild list rail + channel list) OR the chat column is visible, never + both. Which one shows is decided in GuildsLayout.tsx by parsing the + current URL — a channel in the URL means chat mode, no channel means + nav mode. The data-mobile-mode attribute switches between them. + + The bottom user area (avatar + mute + deafen + settings gear) is + hidden in both modes on mobile; its functionality is reachable via + the avatar popout from the top of the channel list instead. +*/ +@media (max-width: 768px) { + /* Common: no divider, no bottom user area strip, no padding reserved + for it. Guild list rail shrinks to an icon column. */ + .sidebarDivider { + display: none; + } + + .userAreaWrapper { + display: none; + } + + /* Guild list keeps its default 72px layout width on mobile too — + the previous 56px override made the icon column feel cramped + and pushed the icons off-center relative to their tooltips. */ + .guildList { + padding-bottom: var(--spacing-2); + } + + .sidebar { + padding-bottom: 0; + } + + /* Nav mode: guild rail + full-width sidebar (channel list / DM list), + chat area hidden. */ + .wrapper[data-mobile-mode='nav'] .sidebar { + display: flex; + flex: 1 1 auto; + width: auto; + min-width: 0; + } + + .wrapper[data-mobile-mode='nav'] .content { + display: none; + } + + /* Chat mode: hide nav columns entirely, chat fills the viewport. + The bottom nav is also hidden — the chat has its own back button + in the header, so the tab bar would be redundant and would just + take vertical space away from the input. */ + .wrapper[data-mobile-mode='chat'] .guildList { + display: none; + } + + .wrapper[data-mobile-mode='chat'] .sidebar { + display: none; + } + + .wrapper[data-mobile-mode='chat'] .content { + flex: 1 1 auto; + min-width: 0; + } + + .wrapper[data-mobile-mode='chat'] > nav { + display: none; + } + + /* You mode: identical column-hiding to chat mode (guild rail + + sidebar hidden, content fills), but the MobileBottomNav stays + visible underneath — that's the whole point of the tab. */ + .wrapper[data-mobile-mode='you'] .guildList { + display: none; + } + + .wrapper[data-mobile-mode='you'] .sidebar { + display: none; + } + + .wrapper[data-mobile-mode='you'] .content { + flex: 1 1 auto; + min-width: 0; + background-color: var(--background-primary); + } +} diff --git a/packages/shared/src/components/layout/GuildsLayout.tsx b/packages/shared/src/components/layout/GuildsLayout.tsx new file mode 100644 index 0000000..7c79f36 --- /dev/null +++ b/packages/shared/src/components/layout/GuildsLayout.tsx @@ -0,0 +1,85 @@ +import { useQuery } from 'convex/react'; +import type { ReactNode } from 'react'; +import { useLocation } from 'react-router-dom'; +import { api } from '../../../../../convex/_generated/api'; +import { FileUploadDropZone } from '../channel/FileUploadDropZone'; +import { DMLayout } from '../dm/DMLayout'; +import { GuildList } from './GuildList'; +import { GuildNavbar } from './GuildNavbar'; +import { MobileBottomNav } from './MobileBottomNav'; +import { UserArea } from './UserArea'; +import styles from './GuildsLayout.module.css'; + +interface GuildsLayoutProps { + children: ReactNode; +} + +/** + * Parse the current pathname to decide which mobile view to show. At + * ≤768px exactly one of the three columns is visible at a time. + * /channels/@me → nav (DM list) + * /channels/@me/:channelId → chat + * /channels/:serverId → nav (channel list) + * /channels/:serverId/:channelId → chat + * /channels/you → you (mobile profile tab) + */ +function detectMobileMode(pathname: string): 'nav' | 'chat' | 'you' { + const segments = pathname.split('/').filter(Boolean); + if (segments[0] !== 'channels') return 'nav'; + if (segments[1] === 'you') return 'you'; + return segments.length >= 3 && !!segments[2] ? 'chat' : 'nav'; +} + +export function GuildsLayout({ children }: GuildsLayoutProps) { + const location = useLocation(); + const segments = location.pathname.split('/').filter(Boolean); + // /channels/:serverId/... — serverId is segments[1], '@me' for DMs + const serverId = segments[0] === 'channels' ? segments[1] : undefined; + const channelId = segments[0] === 'channels' ? segments[2] : undefined; + const hasServer = !!serverId && serverId !== '@me' && serverId !== 'you'; + const mobileMode = detectMobileMode(location.pathname); + + // Look up the current channel's name + type so the drop overlay + // reads "Upload to #channel-name". Drops are disabled when there's + // no channel in the URL (DM home, `/you` profile) and on voice + // channels — they have no composer to attach to. + const channel = useQuery( + api.channels.get, + channelId ? { id: channelId as any } : 'skip', + ); + const channelName = (channel && (channel as any).name) || 'channel'; + const channelType = (channel && (channel as any).type) || undefined; + const dropDisabled = !channelId || channelType === 'voice' || channelType === 'category'; + + return ( + <FileUploadDropZone + channelName={channelName} + disabled={dropDisabled} + onDropFiles={(files) => { + if (!channelId) return; + window.dispatchEvent( + new CustomEvent('brycord:drop-files', { + detail: { channelId, files }, + }), + ); + }} + > + <div className={styles.wrapper} data-mobile-mode={mobileMode}> + <div className={styles.container}> + <div className={styles.guildList}> + <GuildList /> + </div> + <div className={styles.sidebar}> + {hasServer ? <GuildNavbar /> : <DMLayout />} + </div> + <div className={styles.sidebarDivider} /> + <div className={styles.content}>{children}</div> + <div className={styles.userAreaWrapper}> + <UserArea /> + </div> + </div> + <MobileBottomNav /> + </div> + </FileUploadDropZone> + ); +} diff --git a/packages/shared/src/components/layout/InviteMembersModal.module.css b/packages/shared/src/components/layout/InviteMembersModal.module.css new file mode 100644 index 0000000..3228b4d --- /dev/null +++ b/packages/shared/src/components/layout/InviteMembersModal.module.css @@ -0,0 +1,112 @@ +.body { + display: flex; + flex-direction: column; + gap: 20px; + padding-top: 20px; +} + +.section { + display: flex; + flex-direction: column; + gap: 10px; +} + +.sectionLabel { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.02em; + color: var(--text-tertiary); +} + +.sectionDescription { + font-size: 0.8125rem; + color: var(--text-secondary); + line-height: 1.4; +} + +.sectionDescription code { + padding: 1px 5px; + border-radius: 4px; + background-color: var(--background-tertiary, rgba(255, 255, 255, 0.05)); + font-family: 'Consolas', 'Monaco', monospace; + font-size: 0.75rem; +} + +/* ── Invite by MXID input row ────────────────────────────────────────── */ + +.inviteInputRow { + display: flex; + gap: 8px; + align-items: stretch; +} + +.inviteInput { + flex: 1; + min-width: 0; + padding: 10px 12px; + background-color: var(--input-background, var(--background-tertiary)); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.08)); + border-radius: 8px; + color: var(--text-primary); + font: inherit; + font-size: 0.9375rem; + outline: none; + box-sizing: border-box; + transition: border-color 150ms ease; +} + +.inviteInput::placeholder { + color: var(--text-tertiary); +} + +.inviteInput:focus { + border-color: var(--background-modifier-accent-focus, var(--brand-primary, #5865f2)); +} + +.status { + font-size: 0.8125rem; + margin: 0; + line-height: 1.4; +} + +.statusSuccess { + color: var(--status-positive, #23a55a); +} + +.statusError { + color: var(--status-danger, #da373c); +} + +/* ── Copy address row ────────────────────────────────────────────────── */ + +.copyRow { + display: flex; + gap: 8px; + align-items: stretch; +} + +.copyInput { + flex: 1; + min-width: 0; + padding: 10px 12px; + background-color: var(--background-tertiary, rgba(255, 255, 255, 0.04)); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.08)); + border-radius: 8px; + color: var(--text-primary); + font: inherit; + font-family: 'Consolas', 'Monaco', monospace; + font-size: 0.8125rem; + outline: none; + box-sizing: border-box; + cursor: text; +} + +.copyInput:focus { + border-color: var(--background-modifier-accent-focus, var(--brand-primary, #5865f2)); +} + +.divider { + height: 1px; + background-color: var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); +} diff --git a/packages/shared/src/components/layout/InviteMembersModal.tsx b/packages/shared/src/components/layout/InviteMembersModal.tsx new file mode 100644 index 0000000..3192bb9 --- /dev/null +++ b/packages/shared/src/components/layout/InviteMembersModal.tsx @@ -0,0 +1,187 @@ +/** + * InviteMembersModal — the Matrix-native invite flow for a space. + * + * Fluxer's equivalent has shareable web invite links; since Brycord is a + * Matrix client we show the Matrix primitives instead: the space's + * canonical alias (or room ID fallback) as a copyable string people can + * enter into their own client, plus a direct "Invite by Matrix ID" text + * field that calls m.room.invite for a specific user. + */ +import { observer } from 'mobx-react-lite'; +import { useEffect, useState } from 'react'; +import { Copy, PaperPlaneTilt } from '@phosphor-icons/react'; +import { Modal, Button } from '@brycord/ui'; +import { MemberManager, MatrixClientManager } from '@brycord/matrix-client'; +import ServerStore from '@app/stores/ServerStore'; +import styles from './InviteMembersModal.module.css'; + +interface InviteMembersModalProps { + isOpen: boolean; + onClose: () => void; + serverId: string; +} + +// Quick sanity check for "@localpart:server.tld" — not exhaustive but +// enough to catch obvious typos before hitting the homeserver. +const MXID_REGEX = /^@[^:\s]+:[^:\s]+$/; + +export const InviteMembersModal = observer(function InviteMembersModal({ + isOpen, + onClose, + serverId, +}: InviteMembersModalProps) { + const server = ServerStore.getServer(serverId); + const [mxid, setMxid] = useState(''); + const [inviting, setInviting] = useState(false); + const [copied, setCopied] = useState(false); + const [status, setStatus] = useState<{ type: 'success' | 'error'; message: string } | null>(null); + + // Resolve the room's canonical alias if the homeserver has one set; + // otherwise fall back to the raw Matrix room ID. Both are valid + // "addresses" that another Matrix user can paste into their client's + // "Join a room" dialog to join the space. + const [inviteAddress, setInviteAddress] = useState<string>(serverId); + + useEffect(() => { + if (!isOpen) return; + setStatus(null); + setMxid(''); + setCopied(false); + + try { + const client = MatrixClientManager.getInstance().getClient(); + const room = client.getRoom(serverId); + const canonicalAlias = room?.getCanonicalAlias(); + setInviteAddress(canonicalAlias || serverId); + } catch { + setInviteAddress(serverId); + } + }, [isOpen, serverId]); + + const handleCopy = async () => { + try { + await navigator.clipboard.writeText(inviteAddress); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + } catch { + // Silent fail — clipboard API can be blocked in some contexts. + } + }; + + const handleInvite = async () => { + const trimmed = mxid.trim(); + if (!MXID_REGEX.test(trimmed)) { + setStatus({ + type: 'error', + message: 'Enter a valid Matrix ID (e.g. @alice:matrix.org).', + }); + return; + } + setInviting(true); + setStatus(null); + try { + await MemberManager.getInstance().inviteMember(serverId, trimmed); + setStatus({ type: 'success', message: `Invited ${trimmed}.` }); + setMxid(''); + } catch (err: any) { + setStatus({ + type: 'error', + message: err?.message || 'Failed to send invite.', + }); + } finally { + setInviting(false); + } + }; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="medium"> + <Modal.Header + title={`Invite People to ${server?.name || 'Community'}`} + onClose={onClose} + /> + <Modal.Content> + <div className={styles.body}> + <div className={styles.section}> + <div className={styles.sectionLabel}>Invite by Matrix ID</div> + <div className={styles.sectionDescription}> + Send an invite directly to another Matrix user by their + full address (e.g. <code>@alice:matrix.org</code>). + </div> + <div className={styles.inviteInputRow}> + <input + type="text" + className={styles.inviteInput} + placeholder="@alice:matrix.org" + value={mxid} + onChange={(e) => setMxid(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault(); + handleInvite(); + } + }} + autoFocus + /> + <Button + variant="primary" + size="sm" + loading={inviting} + disabled={!mxid.trim()} + icon={<PaperPlaneTilt size={16} />} + onClick={handleInvite} + > + Send Invite + </Button> + </div> + {status && ( + <p + className={`${styles.status} ${ + status.type === 'success' + ? styles.statusSuccess + : styles.statusError + }`} + > + {status.message} + </p> + )} + </div> + + <div className={styles.divider} /> + + <div className={styles.section}> + <div className={styles.sectionLabel}> + Or share this community's address + </div> + <div className={styles.sectionDescription}> + Anyone can join by entering this in their Matrix client's + "Join a room" dialog (subject to the community's privacy + settings). + </div> + <div className={styles.copyRow}> + <input + type="text" + className={styles.copyInput} + value={inviteAddress} + readOnly + onClick={(e) => e.currentTarget.select()} + /> + <Button + variant="secondary" + size="sm" + icon={<Copy size={16} />} + onClick={handleCopy} + > + {copied ? 'Copied!' : 'Copy'} + </Button> + </div> + </div> + </div> + </Modal.Content> + <Modal.Footer> + <Button variant="primary" size="sm" onClick={onClose}> + Done + </Button> + </Modal.Footer> + </Modal.Root> + ); +}); diff --git a/packages/shared/src/components/layout/LeaveServerModal.module.css b/packages/shared/src/components/layout/LeaveServerModal.module.css new file mode 100644 index 0000000..24192cd --- /dev/null +++ b/packages/shared/src/components/layout/LeaveServerModal.module.css @@ -0,0 +1,77 @@ +.body { + display: flex; + flex-direction: column; + gap: 16px; + padding: 16px; +} + +.description { + font-size: 0.9375rem; + color: var(--text-secondary); + line-height: 1.4; + margin: 0; +} + +.description strong { + color: var(--text-primary); + font-weight: 600; +} + +.error { + font-size: 0.875rem; + color: var(--status-danger); + padding: 8px 12px; + background-color: color-mix(in srgb, var(--status-danger) 10%, transparent); + border-radius: var(--radius-md, 6px); +} + +.actions { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 8px; +} + +.secondaryButton { + padding: 10px 16px; + font-size: 0.875rem; + font-weight: 500; + font-family: inherit; + border-radius: var(--radius-md, 6px); + border: none; + background: transparent; + color: var(--text-primary); + cursor: pointer; + transition: background-color 0.1s; +} + +.secondaryButton:hover:not(:disabled) { + background-color: color-mix(in srgb, var(--text-primary) 8%, transparent); +} + +.secondaryButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.dangerButton { + padding: 10px 16px; + font-size: 0.875rem; + font-weight: 600; + font-family: inherit; + border-radius: var(--radius-md, 6px); + border: none; + background-color: var(--status-danger); + color: #fff; + cursor: pointer; + transition: background-color 0.1s; +} + +.dangerButton:hover:not(:disabled) { + background-color: color-mix(in srgb, var(--status-danger) 85%, black); +} + +.dangerButton:disabled { + opacity: 0.6; + cursor: not-allowed; +} diff --git a/packages/shared/src/components/layout/LeaveServerModal.tsx b/packages/shared/src/components/layout/LeaveServerModal.tsx new file mode 100644 index 0000000..1210a8b --- /dev/null +++ b/packages/shared/src/components/layout/LeaveServerModal.tsx @@ -0,0 +1,73 @@ +import SelectionStore from '@app/stores/SelectionStore'; +import ServerStore from '@app/stores/ServerStore'; +import { SpaceManager } from '@brycord/matrix-client'; +import { Modal } from '@brycord/ui'; +/** + * LeaveServerModal — confirmation dialog for the "Leave Server" + * action in the GuildHeaderDropdown. Calls `SpaceManager.leaveServer` + * on confirm, which walks every linked channel and leaves them all + * before leaving the space itself. On success, navigates home and + * optimistically removes the server from ServerStore so the sidebar + * updates instantly without waiting for the sync echo. + */ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import styles from './LeaveServerModal.module.css'; + +interface LeaveServerModalProps { + isOpen: boolean; + onClose: () => void; + serverId: string; + serverName: string; +} + +export function LeaveServerModal({ isOpen, onClose, serverId, serverName }: LeaveServerModalProps) { + const navigate = useNavigate(); + const [busy, setBusy] = useState(false); + const [error, setError] = useState<string | null>(null); + + const handleConfirm = async () => { + if (busy) return; + setBusy(true); + setError(null); + try { + await SpaceManager.getInstance().leaveServer(serverId); + // Optimistic local cleanup — the sync echo will also + // remove the server but the sidebar shouldn't wait for + // it. Clearing the selection prevents the app from + // rendering a now-stale channel view. + ServerStore.handleServerDelete(serverId); + SelectionStore.selectServer(null); + onClose(); + navigate('/channels/@me'); + } catch (err: any) { + setError(err?.message || 'Failed to leave server.'); + setBusy(false); + } + }; + + return ( + <Modal.Root isOpen={isOpen} onClose={busy ? () => {} : onClose} size="small"> + <Modal.Header title="Leave Server" onClose={busy ? () => {} : onClose} /> + <Modal.Content> + <div className={styles.body}> + <p className={styles.description}> + Are you sure you want to leave <strong>{serverName}</strong>? You'll need a new invite to rejoin, and you'll + leave every channel in this server. + </p> + + {error && <div className={styles.error}>{error}</div>} + + <div className={styles.actions}> + <button type="button" className={styles.secondaryButton} onClick={onClose} disabled={busy}> + Cancel + </button> + <button type="button" className={styles.dangerButton} onClick={handleConfirm} disabled={busy}> + {busy ? 'Leaving…' : 'Leave Server'} + </button> + </div> + </div> + </Modal.Content> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/layout/MobileBottomNav.module.css b/packages/shared/src/components/layout/MobileBottomNav.module.css new file mode 100644 index 0000000..cabd2d3 --- /dev/null +++ b/packages/shared/src/components/layout/MobileBottomNav.module.css @@ -0,0 +1,75 @@ +.nav { + display: none; + height: var(--mobile-bottom-nav-height); + background-color: var(--background-secondary); + border-top: 1px solid var(--background-header-secondary); + flex-shrink: 0; + padding-bottom: env(safe-area-inset-bottom, 0px); +} + +@media (max-width: 768px) { + .nav { + display: flex; + align-items: center; + justify-content: space-around; + } +} + +.tab { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + background: none; + border: none; + cursor: pointer; + color: var(--interactive-normal); + padding: 8px 16px; + transition: color 0.15s; +} + +.tab:hover { + color: var(--interactive-hover); +} + +.active { + color: var(--interactive-active) !important; +} + +.label { + font-size: 0.625rem; + font-weight: 600; +} + +/* The "You" tab uses the current user's avatar instead of a generic User + icon. The avatar wrapper reserves the same 24px square the other icons + take, so the tabs line up vertically. */ +.youTab { + padding-top: 6px; +} + +.youAvatar { + display: flex; + align-items: center; + justify-content: center; + height: 24px; + width: 24px; + line-height: 0; +} + +.tabIconWrap { + position: relative; + display: inline-flex; +} + +.unreadDot { + position: absolute; + top: -2px; + right: -3px; + width: 10px; + height: 10px; + border-radius: 50%; + background-color: var(--status-danger, #ed4245); + border: 2px solid var(--background-tertiary, #1e1f22); + box-sizing: content-box; +} diff --git a/packages/shared/src/components/layout/MobileBottomNav.tsx b/packages/shared/src/components/layout/MobileBottomNav.tsx new file mode 100644 index 0000000..6d9c773 --- /dev/null +++ b/packages/shared/src/components/layout/MobileBottomNav.tsx @@ -0,0 +1,130 @@ +/** + * MobileBottomNav — the 3-tab bottom navigation bar used on mobile + * layouts. Tabs: Home, Notifications, You. Shows an unread dot on the + * Home tab when any channel has new messages since the last read + * timestamp. The You tab shows the current user's avatar. + */ +import { useEffect, useMemo } from 'react'; +import { useQuery } from 'convex/react'; +import { useNavigate, useLocation } from 'react-router-dom'; +import { House, Bell } from '@phosphor-icons/react'; +import { Avatar } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import styles from './MobileBottomNav.module.css'; + +// sessionStorage key for the last-visited "home" path. Used to make +// the Home tab resume wherever the user was — server channel list +// or DM — instead of always bouncing to the DM root. +const LAST_HOME_PATH_KEY = 'mobileHomePath'; + +function isHomePath(pathname: string): boolean { + if (pathname === '/channels/you') return false; + return pathname.startsWith('/channels'); +} + +export function MobileBottomNav() { + const navigate = useNavigate(); + const location = useLocation(); + + // Remember the last non-You /channels/* path so the Home tab can + // resume where the user left off. We persist to sessionStorage so + // the choice survives page navigation but not a full tab close. + useEffect(() => { + if (isHomePath(location.pathname)) { + try { + sessionStorage.setItem(LAST_HOME_PATH_KEY, location.pathname); + } catch { + /* ignore storage errors */ + } + } + }, [location.pathname]); + + const userId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + + const allUsers = useQuery(api.auth.getPublicKeys) ?? []; + const me = allUsers.find((u) => u.id === userId); + + const channelsRaw = useQuery(api.channels.list); + const readStates = useQuery( + api.readState.getAllReadStates, + userId ? { userId: userId as any } : 'skip', + ); + const channelIdList = useMemo( + () => (channelsRaw ?? []).map((c: any) => c._id as any), + [channelsRaw], + ); + const latestTimestamps = useQuery( + api.readState.getLatestMessageTimestamps, + channelIdList.length > 0 ? { channelIds: channelIdList } : 'skip', + ); + + const hasUnread = useMemo(() => { + const readMap = new Map<string, number>(); + for (const r of readStates ?? []) { + readMap.set(r.channelId as unknown as string, r.lastReadTimestamp); + } + for (const l of latestTimestamps ?? []) { + const last = readMap.get(l.channelId as unknown as string) ?? 0; + if (l.latestTimestamp > last) return true; + } + return false; + }, [readStates, latestTimestamps]); + + const isYou = location.pathname === '/channels/you'; + const isHome = location.pathname.startsWith('/channels') && !isYou; + const isNotifications = location.pathname === '/notifications'; + + const displayName = me?.displayName || me?.username || 'You'; + const avatarUrl = me?.avatarUrl ?? null; + const status = (me?.status as any) ?? 'online'; + + const handleHomeClick = () => { + // Pick up the last remembered home path; fall back to the DM + // home when nothing's been visited yet this session. + let target = '/channels/@me'; + try { + const stored = sessionStorage.getItem(LAST_HOME_PATH_KEY); + if (stored && isHomePath(stored)) target = stored; + } catch { + /* ignore */ + } + navigate(target); + }; + + return ( + <nav className={styles.nav}> + <button + className={`${styles.tab} ${isHome ? styles.active : ''}`} + onClick={handleHomeClick} + > + <div className={styles.tabIconWrap}> + <House size={24} weight="fill" /> + {hasUnread && <span className={styles.unreadDot} />} + </div> + <span className={styles.label}>Home</span> + </button> + <button + className={`${styles.tab} ${isNotifications ? styles.active : ''}`} + onClick={() => navigate('/notifications')} + > + <Bell size={24} weight="fill" /> + <span className={styles.label}>Notifications</span> + </button> + <button + className={`${styles.tab} ${styles.youTab} ${isYou ? styles.active : ''}`} + onClick={() => navigate('/channels/you')} + > + <div className={styles.youAvatar}> + <Avatar + src={avatarUrl} + fallback={displayName} + size={24} + status={status} + /> + </div> + <span className={styles.label}>You</span> + </button> + </nav> + ); +} diff --git a/packages/shared/src/components/layout/MobileServerActionsSheet.module.css b/packages/shared/src/components/layout/MobileServerActionsSheet.module.css new file mode 100644 index 0000000..e591a5d --- /dev/null +++ b/packages/shared/src/components/layout/MobileServerActionsSheet.module.css @@ -0,0 +1,121 @@ +.body { + display: flex; + flex-direction: column; + gap: 16px; + padding: 16px 16px 32px; +} + +/* Server header — big icon + name + member counts. Sits above the + action cards and mirrors the "quick reactions row" slot in the + message actions sheet. */ + +.serverHeader { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + padding: 4px 0 12px; +} + +.serverIcon { + width: 72px; + height: 72px; + border-radius: 18px; + object-fit: cover; + background-color: var(--background-modifier-hover); +} + +.serverInitials { + width: 72px; + height: 72px; + border-radius: 18px; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--background-modifier-hover); + color: var(--text-primary, #fff); + font-size: 26px; + font-weight: 700; +} + +.serverName { + font-size: 18px; + font-weight: 700; + color: var(--text-primary, #fff); + text-align: center; + margin: 0; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.memberCounts { + display: flex; + align-items: center; + gap: 6px; + font-size: 13px; + color: var(--text-primary-muted, #a0a3a8); +} + +.memberDot { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: hsl(139, calc(47.3% * var(--saturation-factor)), 43.9%); +} + +.memberSeparator { + opacity: 0.5; +} + +/* Action list — same rounded-card pattern as MobileMessageActionsSheet. */ + +.actionList { + display: flex; + flex-direction: column; + gap: 0; + border-radius: 12px; + overflow: hidden; +} + +.actionItem { + position: relative; + display: flex; + align-items: center; + gap: 14px; + width: 100%; + padding: 16px 16px; + background-color: var(--background-secondary-alt); + border: none; + border-radius: 0; + color: var(--text-primary, #fff); + font: inherit; + font-size: 15px; + font-weight: 600; + text-align: left; + cursor: pointer; + transition: background-color 0.1s; + -webkit-tap-highlight-color: transparent; +} + +.actionItem:active { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.08)); +} + +.actionItem svg { + flex-shrink: 0; + color: var(--text-primary-muted, #a0a3a8); +} + +/* Inset divider between adjacent items within a single card. */ +.actionItem:not(:last-child)::after { + content: ''; + position: absolute; + left: 1rem; + right: 1rem; + bottom: 0; + height: 1px; + background-color: var(--background-header-secondary); + pointer-events: none; +} diff --git a/packages/shared/src/components/layout/MobileServerActionsSheet.tsx b/packages/shared/src/components/layout/MobileServerActionsSheet.tsx new file mode 100644 index 0000000..1b540fa --- /dev/null +++ b/packages/shared/src/components/layout/MobileServerActionsSheet.tsx @@ -0,0 +1,145 @@ +/** + * MobileServerActionsSheet — bottom sheet opened when the user taps + * the server name header on mobile. Ported from the new UI's layout: + * server identity block at the top, then three grouped action lists + * (Invite / Create / Settings) using the `.serverHeader`, `.actionList`, + * and `.actionItem` classes from the new UI's module CSS. + */ +import { useQuery } from 'convex/react'; +import { + FolderPlus, + Gear, + Hash, + ShieldCheck, + UserPlus, +} from '@phosphor-icons/react'; +import { BottomSheet } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import { useOnlineUsers } from '../../contexts/PresenceContext'; +import styles from './MobileServerActionsSheet.module.css'; + +interface MobileServerActionsSheetProps { + isOpen: boolean; + onClose: () => void; +} + +function getInitials(name: string): string { + return name + .split(/\s+/) + .map((w) => w[0]) + .join('') + .slice(0, 2) + .toUpperCase(); +} + +export function MobileServerActionsSheet({ + isOpen, + onClose, +}: MobileServerActionsSheetProps) { + const serverSettings = useQuery(api.serverSettings.get); + const allUsers = useQuery(api.auth.getPublicKeys) ?? []; + const { resolveStatus } = useOnlineUsers(); + + const serverName = serverSettings?.serverName || 'Server'; + const iconUrl = serverSettings?.iconUrl || null; + + // Online count runs through resolveStatus so it matches the live + // heartbeat set the sidebar uses, not the stale stored status. + let online = 0; + for (const u of allUsers) { + const live = resolveStatus(u.status ?? 'offline', u.id); + if (live !== 'offline' && live !== 'invisible') online++; + } + const total = allUsers.length; + + // Closes the sheet after every action so the user doesn't have + // to dismiss the drawer manually after making a selection. + const wrap = (eventName: string) => () => { + window.dispatchEvent(new CustomEvent(eventName)); + onClose(); + }; + + return ( + <BottomSheet + isOpen={isOpen} + onClose={onClose} + disableDefaultHeader + showHandle + > + <div className={styles.body}> + <div className={styles.serverHeader}> + {iconUrl ? ( + <img + src={iconUrl} + alt={serverName} + className={styles.serverIcon} + draggable={false} + /> + ) : ( + <div className={styles.serverInitials}> + {getInitials(serverName)} + </div> + )} + <h2 className={styles.serverName}>{serverName}</h2> + <div className={styles.memberCounts}> + <span className={styles.memberDot} /> + <span>{online} Online</span> + <span className={styles.memberSeparator}>·</span> + <span> + {total} Member{total === 1 ? '' : 's'} + </span> + </div> + </div> + + <div className={styles.actionList}> + <button + type="button" + className={styles.actionItem} + onClick={wrap('brycord:open-invite')} + > + <UserPlus size={20} weight="fill" /> + <span>Invite Members</span> + </button> + </div> + + <div className={styles.actionList}> + <button + type="button" + className={styles.actionItem} + onClick={wrap('brycord:open-create-channel')} + > + <Hash size={20} weight="bold" /> + <span>Create Channel</span> + </button> + <button + type="button" + className={styles.actionItem} + onClick={wrap('brycord:open-create-category')} + > + <FolderPlus size={20} weight="fill" /> + <span>Create Category</span> + </button> + </div> + + <div className={styles.actionList}> + <button + type="button" + className={styles.actionItem} + onClick={wrap('brycord:open-server-settings')} + > + <Gear size={20} weight="fill" /> + <span>Server Settings</span> + </button> + <button + type="button" + className={styles.actionItem} + onClick={wrap('brycord:open-server-settings')} + > + <ShieldCheck size={20} weight="fill" /> + <span>Privacy Settings</span> + </button> + </div> + </div> + </BottomSheet> + ); +} diff --git a/packages/shared/src/components/layout/MobileSetStatusSheet.module.css b/packages/shared/src/components/layout/MobileSetStatusSheet.module.css new file mode 100644 index 0000000..c6b68cb --- /dev/null +++ b/packages/shared/src/components/layout/MobileSetStatusSheet.module.css @@ -0,0 +1,166 @@ +/* ── MobileSetStatusSheet ──────────────────────────────────────────── + Bottom sheet opened by tapping the avatar on the MobileYouPage. + Lets the user flip between Online / Idle / DND / Invisible with + the same rounded-card styling we use for the other mobile + settings sheets. */ + +.root { + display: flex; + flex-direction: column; + padding: 4px 16px 32px; + gap: 12px; +} + +.topBar { + display: grid; + grid-template-columns: 48px 1fr 48px; + align-items: center; + padding: 0 0 12px; + border-bottom: 1px solid var(--background-header-secondary); + margin-bottom: 4px; +} + +.topBarTitle { + font-size: 17px; + font-weight: 700; + color: var(--text-primary); + text-align: center; + margin: 0; +} + +.topBarButton { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + border: none; + background: transparent; + color: var(--text-primary); + cursor: pointer; + transition: background-color 0.15s; + -webkit-tap-highlight-color: transparent; + margin: 0 auto; +} + +.topBarButton:active { + background-color: var(--background-modifier-hover); +} + +.sectionLabel { + font-size: 11px; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--text-primary-muted); + padding: 0 4px; +} + +/* Rounded card holding the list of status options — same pattern + as MobileMessageActionsSheet and the other mobile settings cards. + Uses `--background-secondary-alt` (not `--background-secondary`) + so the card visually lifts off the BottomSheet surface, which is + itself `--background-secondary`. */ + +.statusList { + background-color: var(--background-secondary-alt); + border-radius: 0.75rem; + overflow: hidden; +} + +.statusItem { + position: relative; + display: grid; + grid-template-columns: 20px 1fr 24px; + align-items: center; + gap: 14px; + width: 100%; + padding: 16px; + background: transparent; + border: none; + color: var(--text-primary); + font: inherit; + text-align: left; + cursor: pointer; + transition: background-color 0.1s; + -webkit-tap-highlight-color: transparent; +} + +.statusItem:active { + background-color: var(--background-modifier-hover); +} + +.statusItem:not(:last-child)::after { + content: ''; + position: absolute; + left: calc(16px + 20px + 14px); + right: 16px; + bottom: 0; + height: 1px; + background-color: var(--background-header-secondary); + pointer-events: none; +} + +.statusDot { + width: 14px; + height: 14px; + border-radius: 50%; + flex-shrink: 0; +} + +.statusDotOnline { + background-color: var(--status-online); +} + +.statusDotIdle { + background-color: var(--status-idle); +} + +.statusDotDnd { + background-color: var(--status-dnd); +} + +.statusDotOffline { + border: 2px solid var(--status-offline); + box-sizing: border-box; +} + +.statusText { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} + +.statusName { + font-size: 15px; + font-weight: 700; + color: var(--text-primary); +} + +.statusSubtitle { + font-size: 12px; + color: var(--text-primary-muted); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* Right-side indicator: a brand-primary checkmark circle on the + currently-selected status row, nothing on the others. */ + +.statusCheck { + display: flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + border-radius: 50%; + background-color: var(--brand-primary); + color: #fff; +} + +.statusCheckHidden { + visibility: hidden; +} diff --git a/packages/shared/src/components/layout/MobileSetStatusSheet.tsx b/packages/shared/src/components/layout/MobileSetStatusSheet.tsx new file mode 100644 index 0000000..6155f07 --- /dev/null +++ b/packages/shared/src/components/layout/MobileSetStatusSheet.tsx @@ -0,0 +1,130 @@ +/** + * MobileSetStatusSheet — bottom sheet opened by tapping your own + * avatar on the MobileYouPage (or the status row below the name). + * Lets you flip between Online / Idle / Do Not Disturb / Invisible. + * + * Writes flow through `api.auth.updateStatus({ userId, status })`. + * The Convex query at `api.auth.getPublicKeys` is reactive, so all + * subscribers update automatically once the mutation completes. + */ +import { useState } from 'react'; +import { useMutation, useQuery } from 'convex/react'; +import { Check, X } from '@phosphor-icons/react'; +import { BottomSheet } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import styles from './MobileSetStatusSheet.module.css'; + +export type UiPresence = 'online' | 'idle' | 'dnd' | 'invisible'; + +interface MobileSetStatusSheetProps { + isOpen: boolean; + onClose: () => void; +} + +interface StatusOption { + id: UiPresence; + label: string; + subtitle?: string; + dotClass: string; +} + +const STATUS_OPTIONS: StatusOption[] = [ + { + id: 'online', + label: 'Online', + dotClass: styles.statusDotOnline, + }, + { + id: 'idle', + label: 'Idle', + dotClass: styles.statusDotIdle, + }, + { + id: 'dnd', + label: 'Do Not Disturb', + subtitle: "You won't receive notifications on desktop", + dotClass: styles.statusDotDnd, + }, + { + id: 'invisible', + label: 'Invisible', + subtitle: "You'll appear offline", + dotClass: styles.statusDotOffline, + }, +]; + +export function MobileSetStatusSheet({ isOpen, onClose }: MobileSetStatusSheetProps) { + const userId = typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const allUsers = useQuery(api.auth.getPublicKeys) ?? []; + const me = allUsers.find((u) => u.id === userId); + const current = (me?.status ?? 'online') as UiPresence; + const updateStatus = useMutation(api.auth.updateStatus); + const [busy, setBusy] = useState<UiPresence | null>(null); + + const handleSelect = async (next: UiPresence) => { + if (busy || !userId) return; + if (next === current) { + onClose(); + return; + } + setBusy(next); + try { + await updateStatus({ userId: userId as Id<'userProfiles'>, status: next }); + onClose(); + } catch (err) { + console.warn('Failed to set presence:', err); + } finally { + setBusy(null); + } + }; + + return ( + <BottomSheet isOpen={isOpen} onClose={onClose} disableDefaultHeader showHandle> + <div className={styles.root}> + <div className={styles.topBar}> + <div /> + <h2 className={styles.topBarTitle}>Set Status</h2> + <button + type="button" + className={styles.topBarButton} + onClick={onClose} + aria-label="Close" + > + <X size={20} weight="bold" /> + </button> + </div> + + <div className={styles.sectionLabel}>Online Status</div> + + <div className={styles.statusList}> + {STATUS_OPTIONS.map((option) => { + const selected = option.id === current; + return ( + <button + key={option.id} + type="button" + className={styles.statusItem} + onClick={() => handleSelect(option.id)} + disabled={busy !== null} + > + <span className={`${styles.statusDot} ${option.dotClass}`} /> + <span className={styles.statusText}> + <span className={styles.statusName}>{option.label}</span> + {option.subtitle && ( + <span className={styles.statusSubtitle}>{option.subtitle}</span> + )} + </span> + <span + className={`${styles.statusCheck} ${selected ? '' : styles.statusCheckHidden}`} + > + <Check size={13} weight="bold" /> + </span> + </button> + ); + })} + </div> + </div> + </BottomSheet> + ); +} diff --git a/packages/shared/src/components/layout/MobileYouPage.module.css b/packages/shared/src/components/layout/MobileYouPage.module.css new file mode 100644 index 0000000..f1d04fb --- /dev/null +++ b/packages/shared/src/components/layout/MobileYouPage.module.css @@ -0,0 +1,233 @@ +/* ── Mobile You page ───────────────────────────────────────────────── + Full-screen profile view shown when the mobile bottom nav's "You" + tab is tapped. Layout mirrors `MobileMemberProfileSheet` but without + the BottomSheet wrapper — the page fills the content area between + the top of the viewport and the MobileBottomNav at the bottom. */ + +.root { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + background-color: var(--background-primary); + overflow-y: auto; + -webkit-overflow-scrolling: touch; +} + +.banner { + position: relative; + flex-shrink: 0; + height: 180px; + width: 100%; + background-color: var(--brand-primary); + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} + +/* Row under the banner — avatar lifts up to overlap the banner by + ~48px, settings gear sits at the right end of the row. */ + +.headerRow { + display: flex; + align-items: flex-end; + justify-content: space-between; + padding: 0 1rem; + margin-top: -48px; + flex-shrink: 0; +} + +/* Transparent button wrapper around the avatar — lets the whole + 96px circle act as the tap target for opening the status sheet + without adding any visible chrome of its own. Zeroes out the + default button padding/background/border so the avatar looks + identical to the previous static div. */ +.avatarButton { + padding: 0; + background: transparent; + border: none; + cursor: pointer; + -webkit-tap-highlight-color: transparent; + display: inline-flex; + align-items: center; + justify-content: center; +} + +/* Avatar wrapper — a bigger `--background-primary` circle sitting + behind the 96px Avatar. Rendered as a `::before` pseudo so it's + isolated from the Avatar's own layout and the absolutely- + positioned status badge doesn't end up repositioned by a parent + border / padding. The pseudo is 12px wider than the Avatar + (6px on each side) and is visually the stroke the Fluxer + reference shows around the profile picture. */ +.avatarWrap { + position: relative; + display: inline-flex; + flex-shrink: 0; + /* Match the Avatar status dot's cut-out ring to the page's + `--background-primary` surface instead of the default + `--background-secondary`, so the dot looks at home on the + You page. */ + --avatar-status-border: var(--background-primary); +} + +.avatarWrap::before { + content: ''; + position: absolute; + top: -6px; + left: -6px; + right: -6px; + bottom: -6px; + border-radius: 50%; + background-color: var(--background-primary); + z-index: 0; + pointer-events: none; +} + +.avatarWrap > * { + position: relative; + z-index: 1; +} + +.settingsButton { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + border: none; + background-color: var(--background-secondary-alt); + color: var(--text-primary); + cursor: pointer; + transition: background-color 0.15s; + -webkit-tap-highlight-color: transparent; + margin-bottom: 6px; +} + +.settingsButton:hover { + background-color: var(--background-modifier-hover); +} + +.settingsButton:active { + background-color: var(--background-modifier-hover); +} + +/* ── Name block ─────────────────────────────────────────────────────*/ + +.nameBlock { + display: flex; + flex-direction: column; + gap: 2px; + padding: 12px 1rem 0; + flex-shrink: 0; +} + +.displayName { + font-size: 26px; + font-weight: 800; + line-height: 1.1; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.handle { + font-size: 15px; + font-weight: 600; + color: var(--text-primary-muted, #a0a3a8); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* ── Edit Profile button ────────────────────────────────────────────*/ + +.editProfileButton { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + margin: 16px 1rem 0; + padding: 14px 16px; + background-color: var(--brand-primary); + color: #fff; + border: none; + border-radius: 0.75rem; + font: inherit; + font-size: 16px; + font-weight: 700; + cursor: pointer; + transition: filter 0.15s; + -webkit-tap-highlight-color: transparent; + flex-shrink: 0; +} + +.editProfileButton:active { + filter: brightness(0.92); +} + +/* ── Card stack ─────────────────────────────────────────────────────*/ + +.cardStack { + display: flex; + flex-direction: column; + gap: 12px; + padding: 16px 1rem 32px; +} + +.card { + background-color: var(--background-secondary); + border-radius: 0.75rem; + padding: 14px 16px; + display: flex; + flex-direction: column; + gap: 6px; +} + +.cardTitleRow { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.cardTitle { + font-size: 15px; + font-weight: 700; + color: var(--text-primary); +} + +.cardValue { + font-size: 14px; + font-weight: 500; + color: var(--text-primary); +} + +.cardAction { + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + border-radius: 50%; + border: none; + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.06)); + color: var(--text-primary-muted); + cursor: pointer; + transition: background-color 0.15s, color 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.cardAction:active { + background-color: var(--background-modifier-accent, rgba(255, 255, 255, 0.1)); + color: var(--text-primary); +} + +.cardSubtitle { + font-size: 13px; + font-weight: 500; + color: var(--text-primary-muted); + font-style: italic; +} diff --git a/packages/shared/src/components/layout/MobileYouPage.tsx b/packages/shared/src/components/layout/MobileYouPage.tsx new file mode 100644 index 0000000..ce09ddb --- /dev/null +++ b/packages/shared/src/components/layout/MobileYouPage.tsx @@ -0,0 +1,136 @@ +/** + * MobileYouPage — full-screen profile view rendered when the user + * taps the "You" tab in the MobileBottomNav. Mirrors the reference + * layout: big accent banner at the top, avatar overlapping the banner + * on the left (tap to change status), settings gear on the right, + * name + handle, Edit Profile button, optional bio card, and a + * status row that opens `MobileSetStatusSheet`. + * + * Unlike a BottomSheet, this is a regular page that mounts inside the + * GuildsLayout content area so MobileBottomNav stays visible below. + * + * Edit Profile + settings gear both open the shared user settings + * modal via the `brycord:open-user-settings` window event. + */ +import { useState } from 'react'; +import { useQuery } from 'convex/react'; +import { Gear, PencilSimple } from '@phosphor-icons/react'; +import { Avatar } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import { MobileSetStatusSheet } from './MobileSetStatusSheet'; +import styles from './MobileYouPage.module.css'; + +function openUserSettings(tab?: 'account') { + window.dispatchEvent( + new CustomEvent('brycord:open-user-settings', { + detail: tab ? { tab } : {}, + }), + ); +} + +type PresenceStatus = 'online' | 'idle' | 'dnd' | 'offline' | 'invisible'; + +function presenceLabel(status: string): string { + switch (status) { + case 'online': + return 'Online'; + case 'idle': + return 'Idle'; + case 'dnd': + return 'Do Not Disturb'; + case 'invisible': + return 'Invisible'; + default: + return 'Offline'; + } +} + +export function MobileYouPage() { + const userId = typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const username = typeof localStorage !== 'undefined' ? localStorage.getItem('username') : null; + const allUsers = useQuery(api.auth.getPublicKeys) ?? []; + const me = allUsers.find((u) => u.id === userId); + const [statusSheetOpen, setStatusSheetOpen] = useState(false); + + const displayName = me?.displayName || username || 'User'; + const realName = me?.username || username || displayName; + const hasCustomDisplayName = displayName !== realName; + const handleText = hasCustomDisplayName ? realName : `@${realName}`; + const avatarUrl = me?.avatarUrl ?? null; + const rawStatus = (me?.status || 'online') as PresenceStatus; + // Avatar only accepts online/idle/dnd/offline — map 'invisible' → 'offline' + const avatarStatus: 'online' | 'idle' | 'dnd' | 'offline' = + rawStatus === 'invisible' ? 'offline' : (rawStatus as 'online' | 'idle' | 'dnd' | 'offline'); + const bio = me?.aboutMe || ''; + + return ( + <div className={styles.root}> + {/* Banner — solid accent color. */} + <div + className={styles.banner} + style={{ + backgroundColor: + (me as any)?.accentColor || 'var(--brand-primary)', + }} + /> + + <div className={styles.headerRow}> + <button + type="button" + className={styles.avatarButton} + onClick={() => setStatusSheetOpen(true)} + aria-label="Change online status" + > + <div className={styles.avatarWrap}> + <Avatar src={avatarUrl} fallback={displayName} size={96} status={avatarStatus} /> + </div> + </button> + <button + type="button" + className={styles.settingsButton} + onClick={() => openUserSettings()} + aria-label="Open settings" + > + <Gear size={20} weight="fill" /> + </button> + </div> + + <div className={styles.nameBlock}> + <span className={styles.displayName}>{displayName}</span> + <span className={styles.handle}>{handleText}</span> + </div> + + <button + type="button" + className={styles.editProfileButton} + onClick={() => openUserSettings('account')} + > + <PencilSimple size={18} weight="bold" /> + Edit Profile + </button> + + <div className={styles.cardStack}> + <button + type="button" + className={styles.card} + onClick={() => setStatusSheetOpen(true)} + style={{ cursor: 'pointer', border: 'none', textAlign: 'left', font: 'inherit' }} + > + <div className={styles.cardTitleRow}> + <span className={styles.cardTitle}>Status</span> + <span className={styles.cardValue}>{presenceLabel(rawStatus)}</span> + </div> + </button> + + {bio && ( + <div className={styles.card}> + <span className={styles.cardTitle}>About Me</span> + <span className={styles.cardValue}>{bio}</span> + </div> + )} + </div> + + <MobileSetStatusSheet isOpen={statusSheetOpen} onClose={() => setStatusSheetOpen(false)} /> + </div> + ); +} diff --git a/packages/shared/src/components/layout/PrivacySettingsModal.module.css b/packages/shared/src/components/layout/PrivacySettingsModal.module.css new file mode 100644 index 0000000..45c1aaa --- /dev/null +++ b/packages/shared/src/components/layout/PrivacySettingsModal.module.css @@ -0,0 +1,155 @@ +.body { + display: flex; + flex-direction: column; + gap: 12px; + padding-top: 20px; +} + +.permissionWarning { + padding: 10px 14px; + background-color: color-mix(in srgb, var(--status-warning, #faa61a) 15%, transparent); + color: var(--status-warning, #faa61a); + border-radius: 8px; + font-size: 0.8125rem; + font-weight: 500; +} + +.sectionLabel { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.02em; + color: var(--text-tertiary); +} + +.sectionDescription { + font-size: 0.8125rem; + color: var(--text-secondary); + line-height: 1.4; + margin: 0; +} + +.optionList { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 4px; +} + +.option { + display: flex; + align-items: flex-start; + gap: 12px; + padding: 14px; + background-color: var(--background-tertiary, rgba(255, 255, 255, 0.03)); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); + border-radius: 10px; + color: var(--text-primary); + font: inherit; + text-align: left; + cursor: pointer; + transition: background-color 0.15s ease, border-color 0.15s ease; +} + +.option:hover:not(:disabled) { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.05)); +} + +.option:disabled { + opacity: 0.55; + cursor: not-allowed; +} + +.optionSelected { + border-color: var(--brand-primary, #5865f2); + background-color: color-mix(in srgb, var(--brand-primary, #5865f2) 10%, transparent); +} + +.optionSelected:hover:not(:disabled) { + background-color: color-mix(in srgb, var(--brand-primary, #5865f2) 15%, transparent); +} + +.optionIcon { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + flex-shrink: 0; + border-radius: 50%; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.05)); + color: var(--text-secondary); +} + +.optionSelected .optionIcon { + background-color: var(--brand-primary, #5865f2); + color: #ffffff; +} + +.optionBody { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 4px; +} + +.optionTitle { + font-size: 0.9375rem; + font-weight: 600; + color: var(--text-primary); +} + +.optionDescription { + font-size: 0.8125rem; + line-height: 1.35; + color: var(--text-secondary); +} + +.optionCheck { + display: flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + flex-shrink: 0; + border-radius: 50%; + background-color: var(--brand-primary, #5865f2); + color: #ffffff; + align-self: center; +} + +.status { + font-size: 0.8125rem; + margin: 6px 0 0; + line-height: 1.4; +} + +.statusSuccess { + color: var(--status-positive, #23a55a); +} + +.statusError { + color: var(--status-danger, #da373c); +} + +/* ── Channel Access repair section ─────────────────────────────────── */ + +.repairSection { + display: flex; + flex-direction: column; + gap: 10px; + margin-top: 12px; + padding: 14px; + border-radius: 10px; + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); + background-color: var(--background-tertiary, rgba(255, 255, 255, 0.02)); +} + +.repairSection .sectionLabel { + margin: 0; +} + +.repairSection .sectionDescription { + margin: 0; +} diff --git a/packages/shared/src/components/layout/PrivacySettingsModal.tsx b/packages/shared/src/components/layout/PrivacySettingsModal.tsx new file mode 100644 index 0000000..50c0738 --- /dev/null +++ b/packages/shared/src/components/layout/PrivacySettingsModal.tsx @@ -0,0 +1,274 @@ +/** + * PrivacySettingsModal — controls who can join the space by writing the + * m.room.join_rules state event. Fluxer's privacy modal is DM-related + * (controls whether other members can DM you); in Matrix the equivalent + * server-level privacy setting is the join rule, which is more useful + * to expose in a Brycord community. + */ +import { observer } from 'mobx-react-lite'; +import { useEffect, useState } from 'react'; +import { Globe, Lock, Check, ShieldCheck } from '@phosphor-icons/react'; +import { Modal, Button } from '@brycord/ui'; +import { RoomManager, MatrixClientManager } from '@brycord/matrix-client'; +import ServerStore from '@app/stores/ServerStore'; +import styles from './PrivacySettingsModal.module.css'; + +interface PrivacySettingsModalProps { + isOpen: boolean; + onClose: () => void; + serverId: string; +} + +type JoinRule = 'public' | 'invite'; + +/** + * Same power-level check as the channel settings modal / sidebar gear — + * state-event power required. Writing m.room.join_rules is gated at + * state_default (typically 50) or higher. + */ +function canModifyRoom(roomId: string): boolean { + const client = MatrixClientManager.getInstance().getClient(); + const room = client.getRoom(roomId); + if (!room) return false; + const userId = client.getUserId(); + if (!userId) return false; + const myPower = room.getMember(userId)?.powerLevel ?? 0; + const plEvent = room.currentState.getStateEvents('m.room.power_levels', ''); + const stateDefault = (plEvent?.getContent()?.state_default ?? 50) as number; + return myPower >= stateDefault; +} + +export const PrivacySettingsModal = observer(function PrivacySettingsModal({ + isOpen, + onClose, + serverId, +}: PrivacySettingsModalProps) { + const server = ServerStore.getServer(serverId); + const [currentRule, setCurrentRule] = useState<JoinRule>('invite'); + const [saving, setSaving] = useState(false); + const [status, setStatus] = useState<{ type: 'success' | 'error'; message: string } | null>(null); + const [repairing, setRepairing] = useState(false); + const [repairStatus, setRepairStatus] = useState<{ + type: 'success' | 'error'; + message: string; + } | null>(null); + + useEffect(() => { + if (!isOpen) return; + setStatus(null); + setRepairStatus(null); + const rule = RoomManager.getInstance().getJoinRule(serverId); + // Only 'public' and 'invite' are selectable in this UI; anything + // else (knock, restricted, etc.) shows as "Invite Only" by default + // to keep the UX simple. Advanced rules can be set via direct + // Matrix state events if needed. + setCurrentRule(rule === 'public' ? 'public' : 'invite'); + }, [isOpen, serverId]); + + const canModify = canModifyRoom(serverId); + + const handleSelect = (rule: JoinRule) => { + if (!canModify) return; + setCurrentRule(rule); + setStatus(null); + }; + + const handleSave = async () => { + if (!canModify) return; + setSaving(true); + setStatus(null); + try { + await RoomManager.getInstance().setJoinRule(serverId, currentRule); + setStatus({ type: 'success', message: 'Privacy settings updated.' }); + } catch (err: any) { + setStatus({ + type: 'error', + message: err?.message || 'Failed to update privacy settings.', + }); + } finally { + setSaving(false); + } + }; + + const handleRepair = async () => { + if (!canModify) return; + setRepairing(true); + setRepairStatus(null); + try { + const result = await RoomManager.getInstance().repairChannelJoinRules(serverId); + const parts: string[] = []; + if (result.updated > 0) { + parts.push( + `${result.updated} channel${result.updated === 1 ? '' : 's'} updated`, + ); + } + if (result.autoJoinUpdated > 0) { + parts.push( + `${result.autoJoinUpdated} marked for auto-join`, + ); + } + if (result.skipped > 0) { + parts.push(`${result.skipped} already OK`); + } + if (result.failed > 0) { + parts.push( + `${result.failed} couldn't be updated (old room version)`, + ); + } + setRepairStatus({ + type: result.failed > 0 && result.updated === 0 ? 'error' : 'success', + message: + parts.length > 0 + ? parts.join(' · ') + : 'No channels needed updating.', + }); + } catch (err: any) { + setRepairStatus({ + type: 'error', + message: err?.message || 'Failed to repair channel access.', + }); + } finally { + setRepairing(false); + } + }; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="medium"> + <Modal.Header + title={`Privacy Settings — ${server?.name || 'Community'}`} + onClose={onClose} + /> + <Modal.Content> + <div className={styles.body}> + {!canModify && ( + <div className={styles.permissionWarning}> + You don't have permission to change this community's + privacy settings. + </div> + )} + + <div className={styles.sectionLabel}>Who Can Join</div> + <div className={styles.sectionDescription}> + Choose who is allowed to enter this community. + </div> + + <div className={styles.optionList}> + <button + type="button" + className={`${styles.option} ${ + currentRule === 'invite' ? styles.optionSelected : '' + }`} + onClick={() => handleSelect('invite')} + disabled={!canModify} + > + <div className={styles.optionIcon}> + <Lock size={22} weight="regular" /> + </div> + <div className={styles.optionBody}> + <div className={styles.optionTitle}>Invite Only</div> + <div className={styles.optionDescription}> + Only people you invite (or who are invited by + members with permission) can join this community. + Recommended for private groups. + </div> + </div> + {currentRule === 'invite' && ( + <div className={styles.optionCheck}> + <Check size={16} weight="bold" /> + </div> + )} + </button> + + <button + type="button" + className={`${styles.option} ${ + currentRule === 'public' ? styles.optionSelected : '' + }`} + onClick={() => handleSelect('public')} + disabled={!canModify} + > + <div className={styles.optionIcon}> + <Globe size={22} weight="regular" /> + </div> + <div className={styles.optionBody}> + <div className={styles.optionTitle}>Public</div> + <div className={styles.optionDescription}> + Anyone who has this community's address can join + without an invite. Use this for open communities + where discovery matters. + </div> + </div> + {currentRule === 'public' && ( + <div className={styles.optionCheck}> + <Check size={16} weight="bold" /> + </div> + )} + </button> + </div> + + {status && ( + <p + className={`${styles.status} ${ + status.type === 'success' + ? styles.statusSuccess + : styles.statusError + }`} + > + {status.message} + </p> + )} + + {/* Channel Access repair section — one-shot fix for legacy + channels created before restricted join rules were + the default. */} + {canModify && ( + <div className={styles.repairSection}> + <div className={styles.sectionLabel}>Channel Access</div> + <div className={styles.sectionDescription}> + New channels let any community member join automatically. + If members joining this community can't see existing + channels, run this to update the older channels' access + rules so anyone in the community can join them without + a separate invite. + </div> + <Button + variant="secondary" + size="sm" + icon={<ShieldCheck size={16} />} + loading={repairing} + onClick={handleRepair} + > + Repair Channel Access + </Button> + {repairStatus && ( + <p + className={`${styles.status} ${ + repairStatus.type === 'success' + ? styles.statusSuccess + : styles.statusError + }`} + > + {repairStatus.message} + </p> + )} + </div> + )} + </div> + </Modal.Content> + <Modal.Footer> + <Button variant="secondary" size="sm" onClick={onClose}> + Cancel + </Button> + <Button + variant="primary" + size="sm" + disabled={!canModify || saving} + loading={saving} + onClick={handleSave} + > + Save Changes + </Button> + </Modal.Footer> + </Modal.Root> + ); +}); diff --git a/packages/shared/src/components/layout/TitleBar.module.css b/packages/shared/src/components/layout/TitleBar.module.css new file mode 100644 index 0000000..4c2445f --- /dev/null +++ b/packages/shared/src/components/layout/TitleBar.module.css @@ -0,0 +1,52 @@ +/* Thin frameless-window chrome. The bar itself is a drag handle; + the action buttons explicitly opt out so clicks land on them + instead of starting a window drag. + + Uses `position: fixed` so the flex/height math of page layouts + (GuildsLayout's `height: 100vh`, AuthLayout's `position: fixed` + background pattern, etc.) can't displace or cover it. The app + body reserves space via the `body.has-window-controls` padding + rule applied from TitleBar.tsx when we know we're in Electron. */ +.bar { + position: fixed; + top: 0; + left: 0; + right: 0; + height: 32px; + display: flex; + align-items: stretch; + justify-content: flex-end; + background-color: var(--background-secondary); + -webkit-app-region: drag; + user-select: none; + z-index: 20000; +} + +.buttons { + display: flex; + align-items: stretch; + -webkit-app-region: no-drag; +} + +.button { + width: 46px; + display: flex; + align-items: center; + justify-content: center; + padding: 0; + border: none; + background: transparent; + color: var(--text-secondary); + cursor: pointer; + transition: background-color 0.12s, color 0.12s; +} + +.button:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +.closeButton:hover { + background-color: #e81123; + color: #ffffff; +} diff --git a/packages/shared/src/components/layout/TitleBar.tsx b/packages/shared/src/components/layout/TitleBar.tsx new file mode 100644 index 0000000..eba894b --- /dev/null +++ b/packages/shared/src/components/layout/TitleBar.tsx @@ -0,0 +1,64 @@ +import { useEffect } from 'react'; +import { Minus, Square, X } from '@phosphor-icons/react'; +import { usePlatform } from '../../platform'; +import styles from './TitleBar.module.css'; + +/** + * TitleBar — thin 32px draggable bar shown only on frameless Electron + * windows. Returns `null` on every other platform so the web and + * Android builds render as if it didn't exist. + * + * The bar itself gets `-webkit-app-region: drag` via CSS; the three + * action buttons use `no-drag` so clicks go to them instead of the + * OS's window drag handler. While mounted, sets `.has-window-controls` + * on <body> so the global stylesheet can pad `#root` to reserve the + * 32px strip — avoids every top-level layout needing its own offset. + */ +export function TitleBar() { + const { features, windowControls } = usePlatform(); + const enabled = !!features?.hasWindowControls && !!windowControls; + + useEffect(() => { + if (!enabled) return; + document.body.classList.add('has-window-controls'); + return () => { + document.body.classList.remove('has-window-controls'); + }; + }, [enabled]); + + if (!enabled) return null; + + return ( + <div className={styles.bar}> + <div className={styles.buttons}> + <button + type="button" + className={styles.button} + onClick={() => windowControls.minimize()} + aria-label="Minimize" + title="Minimize" + > + <Minus size={14} weight="bold" /> + </button> + <button + type="button" + className={styles.button} + onClick={() => windowControls.maximize()} + aria-label="Maximize" + title="Maximize" + > + <Square size={12} weight="bold" /> + </button> + <button + type="button" + className={`${styles.button} ${styles.closeButton}`} + onClick={() => windowControls.close()} + aria-label="Close" + title="Close" + > + <X size={14} weight="bold" /> + </button> + </div> + </div> + ); +} diff --git a/packages/shared/src/components/layout/UserArea.module.css b/packages/shared/src/components/layout/UserArea.module.css new file mode 100644 index 0000000..8dc0d4c --- /dev/null +++ b/packages/shared/src/components/layout/UserArea.module.css @@ -0,0 +1,147 @@ +/* ── UserArea — matches Fluxer's panel-control pattern ──────────────── */ + +.wrapper { + display: flex; + flex-direction: column; + width: 100%; + background-color: var(--panel-control-bg); + position: relative; + flex-shrink: 0; + /* ChannelTextarea.mainWrapper ends up 2px taller than + --input-container-min-height because .uploadColumn's + --textarea-side-button-padding adds content that overflows the + intended min-height. Rather than fight that calculation (which + would shift the plus-button alignment with avatar rows), we + match the user area to the textarea's actual rendered height + so they're pixel-perfect identical on the same horizontal line. */ + min-height: calc(var(--input-container-min-height) + 2px); + box-sizing: border-box; + box-shadow: inset 0 1px 0 var(--user-area-divider-color); +} + +.separator { + display: none; +} + +.container { + display: flex; + align-items: center; + gap: var(--spacing-3); + padding: var(--user-area-padding-y) var(--user-area-padding-x); + box-sizing: border-box; + background-color: transparent; + width: 100%; + flex: 1; +} + +.userInfo { + display: flex; + align-items: center; + gap: var(--spacing-2); + flex: 1; + min-width: 0; + cursor: pointer; + padding: 0 var(--spacing-2); + margin: 0; + border-radius: var(--radius-md); + height: var(--user-area-content-height); + position: relative; + transition: color var(--transition-normal); + outline: none; + background: none; + border: none; + color: inherit; + font: inherit; + text-align: left; +} + +.userInfo::before { + content: ''; + position: absolute; + inset: calc(var(--spacing-1) * -1); + border-radius: calc(var(--radius-md) + var(--spacing-1)); + background-color: transparent; + z-index: -1; + transition: background-color var(--transition-normal); +} + +.userInfo:hover::before { + background-color: color-mix(in srgb, var(--text-primary) 3%, transparent); +} + +.nameBlock { + display: flex; + flex-direction: column; + flex: 1; + min-width: 0; + user-select: text; + -webkit-user-select: text; + gap: 0.0625rem; +} + +.displayName { + font-weight: 500; + font-size: 0.875rem; + line-height: 1.125rem; + color: var(--text-primary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; +} + +.status { + font-size: 0.6875rem; + line-height: 1rem; + color: var(--text-primary-muted); + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 100%; + margin-top: -0.0625rem; + opacity: 0.85; +} + +.controls { + display: flex; + align-items: center; + gap: var(--spacing-1); + flex-shrink: 0; + padding-left: var(--spacing-3); +} + +.controlButton { + display: flex; + align-items: center; + justify-content: center; + height: 32px; + width: 32px; + background-color: transparent; + color: var(--control-button-normal-text); + border: none; + border-radius: var(--radius-md); + cursor: pointer; + position: relative; + padding: 0; +} + +.controlButton:hover { + background-color: color-mix(in srgb, var(--control-button-normal-text) 10%, transparent); + color: var(--control-button-hover-text, var(--text-primary)); +} + +.controlButtonActive { + background-color: color-mix(in srgb, var(--control-button-danger-text) 10%, transparent); + color: var(--control-button-danger-text); +} + +.controlButtonActive:hover { + background-color: color-mix(in srgb, var(--control-button-danger-text) 20%, transparent); + color: var(--control-button-danger-text); +} + +.controlIcon { + height: 20px; + width: 20px; +} diff --git a/packages/shared/src/components/layout/UserArea.tsx b/packages/shared/src/components/layout/UserArea.tsx new file mode 100644 index 0000000..f6bc19c --- /dev/null +++ b/packages/shared/src/components/layout/UserArea.tsx @@ -0,0 +1,176 @@ +import { useEffect, useRef, useState } from 'react'; +import { useQuery } from 'convex/react'; +import { Gear, Microphone, MicrophoneSlash, SpeakerHigh, SpeakerSlash } from '@phosphor-icons/react'; +import { Avatar, Tooltip } from '@discord-clone/ui'; +import { useVoice } from '../../contexts/VoiceContext'; +import { useOnlineUsers } from '../../contexts/PresenceContext'; +import { useKeybinds } from '../../contexts/KeybindContext'; +import { api } from '../../../../../convex/_generated/api'; +import { UserAreaProfilePopout } from './UserAreaProfilePopout'; +import { VoiceConnectionStatus } from '../voice/VoiceConnectionStatus'; +import styles from './UserArea.module.css'; + +/** + * UserArea — bottom of the sidebar. Shows the signed-in user's avatar, + * display name, and mic/speaker/settings controls. Clicking the user + * info area opens the UserAreaProfilePopout. The gear button dispatches + * `brycord:open-user-settings` (the settings modal is mounted higher + * up in the tree). + */ +export function UserArea() { + const userId = typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const username = typeof localStorage !== 'undefined' ? localStorage.getItem('username') : null; + const allUsers = useQuery(api.auth.getPublicKeys) ?? []; + const me = allUsers.find((u) => u.id === userId); + const voice = useVoice(); + const keybinds = useKeybinds(); + + const userInfoRef = useRef<HTMLButtonElement>(null); + const [popoutAnchor, setPopoutAnchor] = useState<DOMRect | null>(null); + + const displayName = me?.displayName || username || 'User'; + const avatarUrl = me?.avatarUrl || null; + // The current user is always "online" to themselves unless they + // have a stored status — invisible users should see their own dot + // as invisible so the setting reflects their actual choice. + const { resolveStatus } = useOnlineUsers(); + const storedStatus = me?.status || 'online'; + const status = userId ? resolveStatus(storedStatus, userId) : storedStatus; + + const isMuted = voice?.isMuted ?? false; + const isDeafened = voice?.isDeafened ?? false; + + const handleToggleMute = () => voice?.toggleMute?.(); + const handleToggleDeafen = () => voice?.toggleDeafen?.(); + const openUserSettings = () => { + window.dispatchEvent(new CustomEvent('brycord:open-user-settings')); + }; + + // Wire the voice keybinds — they're dispatched globally by + // KeybindProvider and picked up here where the mute/deafen state + // actually lives. + useEffect(() => { + const onMute = () => voice?.toggleMute?.(); + const onDeafen = () => voice?.toggleDeafen?.(); + const onDisconnect = () => voice?.disconnect?.(); + window.addEventListener('brycord:keybind:voice.toggleMute', onMute); + window.addEventListener('brycord:keybind:voice.toggleDeafen', onDeafen); + window.addEventListener('brycord:keybind:voice.disconnect', onDisconnect); + return () => { + window.removeEventListener('brycord:keybind:voice.toggleMute', onMute); + window.removeEventListener('brycord:keybind:voice.toggleDeafen', onDeafen); + window.removeEventListener( + 'brycord:keybind:voice.disconnect', + onDisconnect, + ); + }; + }, [voice]); + + const handleOpenPopout = () => { + if (userInfoRef.current) { + setPopoutAnchor(userInfoRef.current.getBoundingClientRect()); + } + }; + + return ( + <div className={styles.wrapper}> + <VoiceConnectionStatus /> + <div className={styles.container}> + <button + ref={userInfoRef} + className={styles.userInfo} + type="button" + onClick={handleOpenPopout} + > + <Avatar src={avatarUrl} size={32} fallback={displayName} status={mapAvatarStatus(status)} /> + <div className={styles.nameBlock}> + <span className={styles.displayName}>{displayName}</span> + <span className={styles.status}>{presenceLabel(status)}</span> + </div> + </button> + + <div className={styles.controls}> + <Tooltip + content={isMuted ? 'Unmute' : 'Mute'} + shortcut={keybinds.getCombo('voice.toggleMute')} + placement="top" + > + <button + type="button" + className={`${styles.controlButton} ${isMuted ? styles.controlButtonActive : ''}`} + onClick={handleToggleMute} + aria-label={isMuted ? 'Unmute' : 'Mute'} + > + {isMuted ? <MicrophoneSlash size={20} /> : <Microphone size={20} />} + </button> + </Tooltip> + <Tooltip + content={isDeafened ? 'Undeafen' : 'Deafen'} + shortcut={keybinds.getCombo('voice.toggleDeafen')} + placement="top" + > + <button + type="button" + className={`${styles.controlButton} ${isDeafened ? styles.controlButtonActive : ''}`} + onClick={handleToggleDeafen} + aria-label={isDeafened ? 'Undeafen' : 'Deafen'} + > + {isDeafened ? <SpeakerSlash size={20} /> : <SpeakerHigh size={20} />} + </button> + </Tooltip> + <Tooltip + content="User Settings" + shortcut={keybinds.getCombo('popouts.openUserSettings')} + placement="top" + > + <button + type="button" + className={styles.controlButton} + onClick={openUserSettings} + aria-label="User Settings" + > + <Gear size={20} /> + </button> + </Tooltip> + </div> + </div> + + {popoutAnchor && ( + <UserAreaProfilePopout + anchorRect={popoutAnchor} + onClose={() => setPopoutAnchor(null)} + onEditProfile={openUserSettings} + /> + )} + </div> + ); +} + +function mapAvatarStatus(status: string): 'online' | 'idle' | 'dnd' | 'offline' { + switch (status) { + case 'online': + return 'online'; + case 'idle': + return 'idle'; + case 'dnd': + return 'dnd'; + default: + return 'offline'; + } +} + +function presenceLabel(presence: string): string { + switch (presence) { + case 'online': + return 'Online'; + case 'idle': + return 'Idle'; + case 'dnd': + return 'Do Not Disturb'; + case 'offline': + case 'invisible': + return 'Invisible'; + default: + return 'Online'; + } +} diff --git a/packages/shared/src/components/layout/UserAreaProfilePopout.module.css b/packages/shared/src/components/layout/UserAreaProfilePopout.module.css new file mode 100644 index 0000000..f012039 --- /dev/null +++ b/packages/shared/src/components/layout/UserAreaProfilePopout.module.css @@ -0,0 +1,275 @@ +/* ── UserAreaProfilePopout ───────────────────────────────────────────── */ + +.overlay { + position: fixed; + inset: 0; + z-index: var(--z-index-contextmenu, 1000); + pointer-events: none; +} + +.card { + position: fixed; + pointer-events: auto; + display: flex; + flex-direction: column; + background-color: var(--background-floating, var(--background-secondary)); + /* 3px border painted inline with the user's accent color — matches + the settings preview and the MemberProfileModal. box-shadow is + also set inline so we can layer an accent glow over the default + drop shadow. */ + border: 3px solid transparent; + border-radius: 12px; + overflow: hidden; + animation: popoutIn 140ms cubic-bezier(0.2, 0.9, 0.2, 1); +} + +@keyframes popoutIn { + from { + opacity: 0; + transform: translateY(8px) scale(0.98); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +/* ── Header (banner + avatar) ────────────────────────────────────────── */ + +.header { + position: relative; + height: 130px; + flex-shrink: 0; +} + +.banner { + width: 100%; + height: 90px; + background-color: var(--brand-primary, #5865f2); + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} + +.avatarWrap { + position: absolute; + top: 50px; + left: 16px; + padding: 4px; + background-color: var(--background-floating, var(--background-secondary)); + border-radius: 9999px; + line-height: 0; +} + +/* ── Identity block ──────────────────────────────────────────────────── */ + +.identity { + display: flex; + flex-direction: column; + gap: 0.1875rem; + padding: 0.25rem 1rem 0.75rem; +} + +.displayName { + font-size: 1.125rem; + font-weight: 600; + line-height: 1.375rem; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.handle { + font-size: 0.75rem; + font-weight: 500; + line-height: 1rem; + color: var(--text-tertiary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-family: 'Consolas', 'Monaco', monospace; +} + +.bio { + margin-top: 0.5rem; + font-size: 0.8125rem; + line-height: 1.125rem; + color: var(--text-secondary); + white-space: pre-wrap; + word-wrap: break-word; + overflow-wrap: break-word; + max-height: 4.5rem; + overflow: hidden; +} + +/* ── Action group (Status / Copy User ID) ────────────────────────────── */ + +.actionGroup { + display: flex; + flex-direction: column; + margin: 0 1rem; + border-radius: 0.5rem; + background-color: var(--background-tertiary, rgba(255, 255, 255, 0.04)); + overflow: hidden; +} + +.statusRow { + display: flex; + flex-direction: column; +} + +.actionButton { + display: flex; + align-items: center; + gap: 0.625rem; + width: 100%; + padding: 0.625rem 0.75rem; + border: none; + background: transparent; + color: var(--text-primary); + text-align: left; + font: inherit; + font-weight: 500; + font-size: 0.875rem; + cursor: pointer; + transition: background-color 0.1s ease; +} + +.actionButton:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.04)); +} + +.actionIcon { + color: var(--text-tertiary); + flex-shrink: 0; +} + +.actionLabel { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.chevron { + color: var(--text-tertiary); + flex-shrink: 0; + transition: transform 150ms ease; +} + +.chevronOpen { + transform: rotate(90deg); +} + +.divider { + height: 1px; + margin: 0 0.75rem; + background-color: var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); +} + +/* ── Status dot (in the collapsed row) ───────────────────────────────── */ + +.statusDot { + width: 10px; + height: 10px; + border-radius: 9999px; + flex-shrink: 0; + margin-left: 0.125rem; + box-shadow: 0 0 0 2px var(--background-tertiary, rgba(0, 0, 0, 0.2)); +} + +.statusOnline { + background-color: var(--status-positive, #23a55a); + color: var(--status-positive, #23a55a); +} + +.statusIdle { + background-color: var(--status-warning, #f0b232); + color: var(--status-warning, #f0b232); +} + +.statusDnd { + background-color: var(--status-danger, #f23f42); + color: var(--status-danger, #f23f42); +} + +.statusOffline { + background-color: var(--text-tertiary, #80848e); + color: var(--text-tertiary, #80848e); +} + +/* ── Status submenu (expanded picker) ────────────────────────────────── */ + +.statusMenu { + display: flex; + flex-direction: column; + padding: 0.25rem 0.25rem 0.5rem; + border-top: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); +} + +.statusMenuItem { + display: flex; + align-items: center; + gap: 0.625rem; + width: 100%; + padding: 0.5rem 0.625rem; + border: none; + background: transparent; + color: var(--text-primary); + text-align: left; + font: inherit; + font-size: 0.875rem; + border-radius: 0.375rem; + cursor: pointer; + transition: background-color 0.1s ease; +} + +.statusMenuItem:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.06)); +} + +.statusMenuIcon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + flex-shrink: 0; +} + +.statusMenuText { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + line-height: 1.125rem; +} + +.statusMenuLabel { + font-weight: 500; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.statusMenuDescription { + font-size: 0.6875rem; + color: var(--text-tertiary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.statusMenuCheck { + color: var(--brand-primary, #5865f2); + flex-shrink: 0; +} + +/* ── Footer (Edit Profile CTA) ───────────────────────────────────────── */ + +.footer { + padding: 0.75rem 1rem 1rem; +} diff --git a/packages/shared/src/components/layout/UserAreaProfilePopout.tsx b/packages/shared/src/components/layout/UserAreaProfilePopout.tsx new file mode 100644 index 0000000..7c5cf61 --- /dev/null +++ b/packages/shared/src/components/layout/UserAreaProfilePopout.tsx @@ -0,0 +1,331 @@ +/** + * UserAreaProfilePopout — Discord-style card that opens above the + * bottom-left user area when the user clicks their own avatar/name. + * + * Contents: + * - Banner (accent color) with the user's avatar overlapping + * - Display name + username + bio + * - Status selector (Online / Idle / DND / Invisible) wired to + * `api.auth.updateStatus` + * - Copy User ID button + * - Edit Profile button (fires `brycord:open-user-settings` via the + * `onEditProfile` callback) + * + * Rendered via createPortal so it escapes the sidebar clipping. + * Position is fixed and computed from the trigger's + * getBoundingClientRect() when the popout opens. + */ +import { useEffect, useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { useMutation, useQuery } from 'convex/react'; +import { + Circle, + Moon, + MinusCircle, + XCircle, + Copy, + PencilSimple, + CaretRight, + Check, +} from '@phosphor-icons/react'; +import { Avatar, Button } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import styles from './UserAreaProfilePopout.module.css'; + +type StatusId = 'online' | 'idle' | 'dnd' | 'invisible'; + +interface UserAreaProfilePopoutProps { + anchorRect: DOMRect; + onClose: () => void; + onEditProfile: () => void; +} + +const STATUS_OPTIONS: Array<{ + id: StatusId; + label: string; + description?: string; + icon: React.ReactNode; + iconClass: string; +}> = [ + { + id: 'online', + label: 'Online', + icon: <Circle size={12} weight="fill" />, + iconClass: 'statusOnline', + }, + { + id: 'idle', + label: 'Idle', + description: 'Shown as away', + icon: <Moon size={12} weight="fill" />, + iconClass: 'statusIdle', + }, + { + id: 'dnd', + label: 'Do Not Disturb', + description: 'Notifications suppressed', + icon: <MinusCircle size={12} weight="fill" />, + iconClass: 'statusDnd', + }, + { + id: 'invisible', + label: 'Invisible', + description: 'Appear offline', + icon: <XCircle size={12} weight="fill" />, + iconClass: 'statusOffline', + }, +]; + +function getStatusLabel(status: string): string { + switch (status) { + case 'online': + return 'Online'; + case 'idle': + return 'Idle'; + case 'dnd': + return 'Do Not Disturb'; + case 'invisible': + case 'offline': + return 'Invisible'; + default: + return 'Online'; + } +} + +function getStatusClassName(status: string): string { + switch (status) { + case 'online': + return styles.statusOnline; + case 'idle': + return styles.statusIdle; + case 'dnd': + return styles.statusDnd; + case 'invisible': + case 'offline': + return styles.statusOffline; + default: + return styles.statusOnline; + } +} + +function mapAvatarStatus( + status: string, +): 'online' | 'idle' | 'dnd' | 'offline' { + switch (status) { + case 'online': + return 'online'; + case 'idle': + return 'idle'; + case 'dnd': + return 'dnd'; + default: + return 'offline'; + } +} + +const DEFAULT_ACCENT_COLOR = '#4641D9'; +const POPOUT_WIDTH = 300; +const GAP = 12; +const VIEWPORT_MARGIN = 8; + +export function UserAreaProfilePopout({ + anchorRect, + onClose, + onEditProfile, +}: UserAreaProfilePopoutProps) { + const ref = useRef<HTMLDivElement>(null); + const [statusPickerOpen, setStatusPickerOpen] = useState(false); + const [copiedId, setCopiedId] = useState(false); + + const userId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const allUsers = useQuery(api.auth.getPublicKeys) ?? []; + const me = allUsers.find((u) => u.id === userId); + const updateStatus = useMutation(api.auth.updateStatus); + + // Close on click-outside / Escape. + useEffect(() => { + const handleClick = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) { + onClose(); + } + }; + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + document.addEventListener('mousedown', handleClick); + document.addEventListener('keydown', handleEscape); + return () => { + document.removeEventListener('mousedown', handleClick); + document.removeEventListener('keydown', handleEscape); + }; + }, [onClose]); + + if (!me || !userId) return null; + + // Pull the current user's saved accent color from their profile + // row. Reactive via `useQuery` so the popout updates instantly + // when the value changes in Settings → My Account. + const accentColor = (me as any)?.accentColor || DEFAULT_ACCENT_COLOR; + const presence = me.status || 'online'; + const bio = me.aboutMe?.trim(); + const displayName = me.displayName || me.username || 'User'; + + // Position: bottom of popout is anchored to top of trigger with a + // small gap. Left-align to trigger's left edge, clamp to viewport. + const left = Math.max( + VIEWPORT_MARGIN, + Math.min(anchorRect.left, window.innerWidth - POPOUT_WIDTH - VIEWPORT_MARGIN), + ); + const bottom = Math.max(VIEWPORT_MARGIN, window.innerHeight - anchorRect.top + GAP); + + const style: React.CSSProperties = { + left, + bottom, + width: POPOUT_WIDTH, + borderColor: accentColor, + boxShadow: `0 16px 32px rgba(0, 0, 0, 0.5), 0 0 0 1px ${accentColor}55, 0 0 28px ${accentColor}30`, + }; + + const handleStatusSelect = async (status: StatusId) => { + try { + await updateStatus({ userId: userId as any, status }); + } catch (err) { + console.error('Failed to update status:', err); + } + setStatusPickerOpen(false); + }; + + const handleCopyUserId = async () => { + try { + await navigator.clipboard.writeText(userId); + setCopiedId(true); + setTimeout(() => setCopiedId(false), 1500); + } catch { + // Clipboard API can be blocked in some contexts — silently fail. + } + }; + + return createPortal( + <div className={styles.overlay}> + <div className={styles.card} ref={ref} style={style}> + {/* Banner + avatar header */} + <div className={styles.header}> + <div + className={styles.banner} + style={{ backgroundColor: accentColor }} + /> + <div className={styles.avatarWrap}> + <Avatar + src={me.avatarUrl ?? null} + fallback={displayName} + size={72} + status={mapAvatarStatus(presence)} + /> + </div> + </div> + + {/* Name + handle */} + <div className={styles.identity}> + <div className={styles.displayName}>{displayName}</div> + <div className={styles.handle}>{me.username}</div> + {bio && bio.length > 0 && <div className={styles.bio}>{bio}</div>} + </div> + + {/* Action group */} + <div className={styles.actionGroup}> + <div className={styles.statusRow}> + <button + type="button" + className={styles.actionButton} + onClick={() => setStatusPickerOpen((v) => !v)} + > + <span + className={`${styles.statusDot} ${getStatusClassName(presence)}`} + /> + <span className={styles.actionLabel}> + {getStatusLabel(presence)} + </span> + <CaretRight + size={14} + weight="bold" + className={`${styles.chevron} ${statusPickerOpen ? styles.chevronOpen : ''}`} + /> + </button> + + {statusPickerOpen && ( + <div className={styles.statusMenu}> + {STATUS_OPTIONS.map((opt) => { + const isActive = + presence === opt.id || + (opt.id === 'invisible' && presence === 'offline'); + return ( + <button + key={opt.id} + type="button" + className={styles.statusMenuItem} + onClick={() => handleStatusSelect(opt.id)} + > + <span + className={`${styles.statusMenuIcon} ${styles[opt.iconClass]}`} + > + {opt.icon} + </span> + <span className={styles.statusMenuText}> + <span className={styles.statusMenuLabel}> + {opt.label} + </span> + {opt.description && ( + <span className={styles.statusMenuDescription}> + {opt.description} + </span> + )} + </span> + {isActive && ( + <Check + size={14} + weight="bold" + className={styles.statusMenuCheck} + /> + )} + </button> + ); + })} + </div> + )} + </div> + + <div className={styles.divider} /> + + <button + type="button" + className={styles.actionButton} + onClick={handleCopyUserId} + > + <Copy size={16} weight="regular" className={styles.actionIcon} /> + <span className={styles.actionLabel}> + {copiedId ? 'Copied!' : 'Copy User ID'} + </span> + </button> + </div> + + {/* Edit profile CTA */} + <div className={styles.footer}> + <Button + variant="primary" + size="sm" + fitContainer + icon={<PencilSimple size={16} />} + onClick={() => { + onEditProfile(); + onClose(); + }} + > + Edit Profile + </Button> + </div> + </div> + </div>, + document.body, + ); +} diff --git a/packages/shared/src/components/layout/index.ts b/packages/shared/src/components/layout/index.ts new file mode 100644 index 0000000..e63b61b --- /dev/null +++ b/packages/shared/src/components/layout/index.ts @@ -0,0 +1,6 @@ +export { AppLayout } from './AppLayout'; +export { GuildsLayout } from './GuildsLayout'; +export { GuildList } from './GuildList'; +export { GuildNavbar } from './GuildNavbar'; +export { UserArea } from './UserArea'; +export { MobileBottomNav } from './MobileBottomNav'; diff --git a/packages/shared/src/components/member/ManageRolesPopout.module.css b/packages/shared/src/components/member/ManageRolesPopout.module.css new file mode 100644 index 0000000..3cdb23f --- /dev/null +++ b/packages/shared/src/components/member/ManageRolesPopout.module.css @@ -0,0 +1,112 @@ +.content { + display: flex; + flex-direction: column; + gap: 12px; + padding: 16px 20px 8px; +} + +.subtitle { + font-size: 14px; + color: var(--text-secondary, #a0a3a8); +} + +.subtitle strong { + color: var(--text-primary, #fff); + font-weight: 600; +} + +.empty { + padding: 20px 12px; + text-align: center; + color: var(--text-primary-muted, #a0a3a8); + font-size: 13px; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.03)); + border-radius: 6px; +} + +.list { + display: flex; + flex-direction: column; + gap: 2px; + max-height: 360px; + overflow-y: auto; +} + +.row { + display: flex; + align-items: center; + gap: 10px; + width: 100%; + padding: 8px 12px; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.03)); + border: 1px solid transparent; + border-radius: 6px; + cursor: pointer; + color: var(--text-primary, #fff); + font: inherit; + text-align: left; + transition: background-color 0.1s, border-color 0.1s; +} + +.row:hover:not(.rowDisabled) { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.06)); +} + +.rowChecked { + border-color: var(--brand-primary, #5865f2); +} + +.rowDisabled { + cursor: not-allowed; + opacity: 0.5; +} + +.roleDot { + width: 12px; + height: 12px; + border-radius: 50%; + flex-shrink: 0; +} + +.roleName { + flex: 1 1 auto; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 14px; + font-weight: 500; +} + +.rolePower { + flex-shrink: 0; + font-size: 11px; + color: var(--text-primary-muted, #a0a3a8); + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; +} + +.check { + flex-shrink: 0; + width: 18px; + height: 18px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + background-color: transparent; + color: var(--brand-primary, #5865f2); +} + +.rowChecked .check { + background-color: var(--brand-primary, #5865f2); + color: #fff; +} + +.error { + padding: 8px 12px; + background-color: rgba(234, 80, 80, 0.15); + border: 1px solid rgba(234, 80, 80, 0.4); + border-radius: 6px; + color: var(--text-primary, #fff); + font-size: 13px; +} diff --git a/packages/shared/src/components/member/ManageRolesPopout.tsx b/packages/shared/src/components/member/ManageRolesPopout.tsx new file mode 100644 index 0000000..4bca355 --- /dev/null +++ b/packages/shared/src/components/member/ManageRolesPopout.tsx @@ -0,0 +1,189 @@ +/** + * ManageRolesPopout — opened from a member profile popout's "Manage + * Roles" button. Shows a checklist of every role in the server with + * the target member's current assignments pre-checked. The user + * toggles rows then clicks Save to commit — the save handler diffs + * the pending set against the previously-assigned set and fires + * `api.roles.assign` / `api.roles.unassign` mutations for each + * added / removed role. + * + * `@everyone` is not a real row in the Convex `roles` table, so + * nothing extra needs filtering here. + */ +import { useEffect, useMemo, useState } from 'react'; +import { useMutation, useQuery } from 'convex/react'; +import { Check } from '@phosphor-icons/react'; +import { Modal, Button } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import styles from './ManageRolesPopout.module.css'; + +interface ManageRolesPopoutMember { + userId: Id<'userProfiles'>; + displayName: string; +} + +interface RoleDoc { + _id: Id<'roles'>; + name: string; + color?: string; + position?: number; +} + +interface ManageRolesPopoutProps { + isOpen: boolean; + onClose: () => void; + member: ManageRolesPopoutMember | null; +} + +export function ManageRolesPopout({ + isOpen, + onClose, + member, +}: ManageRolesPopoutProps) { + // Owner is non-assignable — filter it out of the selectable list + // so it can't be checked, and the backend mutations refuse it + // anyway if somebody tries to craft a request manually. + const allRoles = ( + (useQuery(api.roles.list, {}) ?? []) as RoleDoc[] + ).filter((r) => r.name !== 'Owner'); + const listMembers = useQuery(api.roles.listMembers, {}) as + | Array<{ id: Id<'userProfiles'>; roles: Array<{ _id: Id<'roles'> }> }> + | undefined; + + const assignMutation = useMutation(api.roles.assign); + const unassignMutation = useMutation(api.roles.unassign); + + // The set of role IDs currently assigned on the server (source of + // truth), and the user's pending selection (draft). Reset whenever + // the popout is opened for a new member. + const currentAssignedIds = useMemo<Set<string>>(() => { + if (!member || !listMembers) return new Set(); + const row = listMembers.find((r) => r.id === member.userId); + if (!row) return new Set(); + return new Set(row.roles.map((r) => r._id as unknown as string)); + }, [listMembers, member]); + + const [pendingIds, setPendingIds] = useState<Set<string>>(new Set()); + const [saving, setSaving] = useState(false); + const [error, setError] = useState<string | null>(null); + + useEffect(() => { + if (isOpen) { + setPendingIds(new Set(currentAssignedIds)); + setError(null); + } + }, [isOpen, currentAssignedIds]); + + if (!member) return null; + + // Show roles highest-position first so the editor reads like the + // rest of the role UI (sidebar hierarchy). + const sortedRoles = [...allRoles].sort( + (a, b) => (b.position || 0) - (a.position || 0), + ); + + const togglePending = (roleId: string) => { + if (saving) return; + setPendingIds((prev) => { + const next = new Set(prev); + if (next.has(roleId)) next.delete(roleId); + else next.add(roleId); + return next; + }); + }; + + const handleSave = async () => { + if (saving) return; + setSaving(true); + setError(null); + + // Diff: every role that's checked now but wasn't before → assign; + // every role that was checked before but isn't now → unassign. + const toAssign: string[] = []; + const toRemove: string[] = []; + for (const id of pendingIds) { + if (!currentAssignedIds.has(id)) toAssign.push(id); + } + for (const id of currentAssignedIds) { + if (!pendingIds.has(id)) toRemove.push(id); + } + + try { + await Promise.all([ + ...toAssign.map((roleId) => + assignMutation({ + roleId: roleId as Id<'roles'>, + userId: member.userId, + }), + ), + ...toRemove.map((roleId) => + unassignMutation({ + roleId: roleId as Id<'roles'>, + userId: member.userId, + }), + ), + ]); + onClose(); + } catch (err: any) { + setError(err?.message || 'Failed to update roles.'); + } finally { + setSaving(false); + } + }; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="small"> + <Modal.Header title="Manage Roles" onClose={onClose} /> + <Modal.Content className={styles.content}> + <div className={styles.subtitle}> + Editing roles for <strong>{member.displayName}</strong> + </div> + {sortedRoles.length === 0 ? ( + <div className={styles.empty}> + No roles have been created in this server yet. Open Server + Settings → Roles to create one. + </div> + ) : ( + <div className={styles.list}> + {sortedRoles.map((role) => { + const roleIdStr = role._id as unknown as string; + const assigned = pendingIds.has(roleIdStr); + return ( + <button + key={roleIdStr} + type="button" + className={`${styles.row} ${assigned ? styles.rowChecked : ''}`} + onClick={() => togglePending(roleIdStr)} + disabled={saving} + > + <span + className={styles.roleDot} + style={{ + backgroundColor: + role.color || + 'var(--text-primary-muted, #a0a3a8)', + }} + /> + <span className={styles.roleName}>{role.name}</span> + <span className={styles.check}> + {assigned && <Check size={14} weight="bold" />} + </span> + </button> + ); + })} + </div> + )} + {error && <div className={styles.error}>{error}</div>} + </Modal.Content> + <Modal.Footer> + <Button variant="secondary" onClick={onClose} disabled={saving}> + Cancel + </Button> + <Button variant="primary" onClick={handleSave} disabled={saving}> + {saving ? 'Saving…' : 'Save'} + </Button> + </Modal.Footer> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/member/MemberContextMenu.tsx b/packages/shared/src/components/member/MemberContextMenu.tsx new file mode 100644 index 0000000..d957d21 --- /dev/null +++ b/packages/shared/src/components/member/MemberContextMenu.tsx @@ -0,0 +1,85 @@ +/** + * MemberContextMenu — right-click menu for a user in the member list. + * Offers View Profile, Mention, and (for admins) Manage Roles. Matches + * the new UI's layout, reusing `ChannelListContextMenu.module.css` so + * both context menus share a consistent visual system. + */ +import { useEffect, useRef } from 'react'; +import { createPortal } from 'react-dom'; +import { At, PencilSimple, ShieldStar, User as UserIcon } from '@phosphor-icons/react'; +import styles from '../layout/ChannelListContextMenu.module.css'; + +interface MemberContextMenuProps { + x: number; + y: number; + onViewProfile: () => void; + onMention: () => void; + onChangeNickname?: () => void; + onManageRoles?: () => void; + onClose: () => void; +} + +export function MemberContextMenu({ + x, + y, + onViewProfile, + onMention, + onChangeNickname, + onManageRoles, + onClose, +}: MemberContextMenuProps) { + const ref = useRef<HTMLDivElement>(null); + + useEffect(() => { + const handleClick = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) { + onClose(); + } + }; + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + document.addEventListener('mousedown', handleClick); + document.addEventListener('keydown', handleEscape); + return () => { + document.removeEventListener('mousedown', handleClick); + document.removeEventListener('keydown', handleEscape); + }; + }, [onClose]); + + const style: React.CSSProperties = { + top: Math.min(y, window.innerHeight - 140), + left: Math.min(x, window.innerWidth - 200), + }; + + return createPortal( + <div className={styles.overlay}> + <div className={styles.menu} ref={ref} style={style}> + <button className={styles.menuItem} onClick={onViewProfile}> + <UserIcon size={18} weight="fill" /> + <span>View Profile</span> + </button> + <button className={styles.menuItem} onClick={onMention}> + <At size={18} weight="bold" /> + <span>Mention</span> + </button> + {(onChangeNickname || onManageRoles) && ( + <div className={styles.separator} /> + )} + {onChangeNickname && ( + <button className={styles.menuItem} onClick={onChangeNickname}> + <PencilSimple size={18} weight="fill" /> + <span>Change Nickname</span> + </button> + )} + {onManageRoles && ( + <button className={styles.menuItem} onClick={onManageRoles}> + <ShieldStar size={18} weight="fill" /> + <span>Manage Roles</span> + </button> + )} + </div> + </div>, + document.body, + ); +} diff --git a/packages/shared/src/components/member/MemberListContainer.module.css b/packages/shared/src/components/member/MemberListContainer.module.css new file mode 100644 index 0000000..60a2f06 --- /dev/null +++ b/packages/shared/src/components/member/MemberListContainer.module.css @@ -0,0 +1,209 @@ +/* ── Member List — matches Fluxer ─────────────────────────────────────── */ + +.container { + width: var(--layout-member-list-width); + min-width: var(--layout-member-list-width); + background-color: var(--background-secondary-lighter); + overflow-y: auto; + padding-top: 0.625rem; + padding-left: var(--spacing-2); + padding-right: var(--spacing-2); + padding-bottom: var(--spacing-4); + /* Hide the scrollbar but keep wheel + drag scrolling so the + member list stays clean against the lighter sidebar surface. */ + scrollbar-width: none; + -ms-overflow-style: none; +} + +.container::-webkit-scrollbar { + display: none; +} + +/* Fallback for tablet/narrow viewports — the useIsNarrowScreen hook in + ChannelView.tsx already prevents the panel from rendering below + 1024px, but this CSS-only guard ensures that if it ever does leak + into the DOM (e.g. a different surface, drawer variant excluded) it + won't squish the chat column on a narrow window. The drawer variant + is explicitly opted back in because it intentionally displays inside + a mobile sheet. */ +@media (max-width: 1024px) { + .container:not(.containerDrawer) { + display: none; + } +} + +/* Drawer variant — used inside ChannelDetailsDrawer on mobile. Removes + the panel background and width constraint, then wraps each section's + member list in a rounded `--background-secondary-alt` card so the + layout matches the Fluxer mobile reference screenshot. */ + +.containerDrawer { + width: 100%; + min-width: 0; + background-color: transparent; + overflow-y: visible; + padding: 0; +} + +.containerDrawer .section + .section { + margin-top: 0.75rem; +} + +.containerDrawer .sectionHeader { + padding: 0 0.25rem 0.5rem; +} + +.containerDrawer .membersList { + background-color: var(--background-secondary-alt); + border-radius: 0.75rem; + padding: 0; + gap: 0; +} + +.containerDrawer .memberItem { + padding: 0.75rem 1rem; + border-radius: 0.5rem; +} + +/* Drawer variant uses a 40px avatar, so the default 32px fixed row + height clips it. Zero the grid padding (the parent .memberItem + already owns horizontal/vertical padding) and let the row grow + to fit the avatar naturally. */ +.containerDrawer .memberGrid { + height: auto; + padding: 0; +} + +/* ── Section / Group ─────────────────────────────────────────────────── */ + +.section { + display: flex; + flex-direction: column; +} + +.section + .section { + margin-top: 0.25rem; +} + +.sectionHeader { + display: flex; + align-items: flex-end; + gap: 0.375rem; + padding: 0.75rem 0.5rem 0.25rem; + box-sizing: border-box; + font-size: 0.8125rem; + font-weight: 600; + line-height: 1rem; + color: var(--text-primary-muted); +} + +.sectionLabel { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.sectionCount { + flex-shrink: 0; +} + +/* ── Member Item ─────────────────────────────────────────────────────── */ + +.membersList { + display: flex; + flex-direction: column; + gap: 2px; +} + +.memberItem { + position: relative; + padding-top: 4px; + padding-bottom: 4px; + cursor: pointer; + border-radius: 0.375rem; + color: var(--text-chat, var(--text-secondary)); + display: block; + width: 100%; + text-align: left; + background: none; + border: none; + font-family: inherit; + transition: background-color 0.15s; +} + +.memberItem:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +.memberItemOffline { + opacity: 0.3; +} + +.memberItemOffline:hover { + opacity: 1; +} + +.memberGrid { + display: grid; + height: 32px; + min-width: 0; + grid-template-columns: 1fr auto; + align-items: center; + gap: 0.25rem; + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.memberContent { + display: flex; + min-width: 0; + align-items: center; + gap: 0.625rem; + font-weight: 500; +} + +.memberAvatar { + flex-shrink: 0; +} + +.memberInfo { + display: flex; + align-items: center; + gap: 4px; + min-width: 0; + flex-grow: 1; +} + +.memberName { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: inherit; + font-weight: 500; + line-height: 1.25rem; + max-height: 1.25rem; + color: inherit; +} + +.crownIcon { + flex-shrink: 0; + color: hsl(39, 57%, 64%); +} + +.memberItem:hover .memberName { + color: var(--text-primary); +} + +.memberStatus { + max-width: 100%; + color: var(--text-primary-muted); + font-size: 0.6875rem; + line-height: 0.875rem; + font-weight: 500; + opacity: 0.85; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/packages/shared/src/components/member/MemberListContainer.tsx b/packages/shared/src/components/member/MemberListContainer.tsx new file mode 100644 index 0000000..8c472c6 --- /dev/null +++ b/packages/shared/src/components/member/MemberListContainer.tsx @@ -0,0 +1,412 @@ +import { useEffect, useState } from 'react'; +import { useMutation, useQuery } from 'convex/react'; +import { Avatar, Button, Modal } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import { useIsMobile } from '../../hooks/useIsMobile'; +import { useOnlineUsers } from '../../contexts/PresenceContext'; +import { MobileMemberProfileSheet } from './MobileMemberProfileSheet'; +import { MemberProfilePopout } from './MemberProfilePopout'; +import { MemberProfileModal } from './MemberProfileModal'; +import { MemberContextMenu } from './MemberContextMenu'; +import { ManageRolesPopout } from './ManageRolesPopout'; +import styles from './MemberListContainer.module.css'; + +interface MemberListContainerProps { + members?: unknown[]; + channelId: string; + serverId?: string; + variant?: 'panel' | 'drawer'; +} + +interface MemberRow { + userId: string; + displayName: string; + avatarUrl: string | null; + status: string; + roleColor: string | null; +} + +const DEFAULT_ROLE_COLOR = '#99aab5'; + +/** Highest-position role that has a non-default colour. Matches the + * server-side enrichMessage helper so chat + member list pick the + * same tint for the same user. Owner is deliberately skipped so + * owners fall through to the next coloured role instead of showing + * the bootstrap pink on every server. */ +function pickRoleColor(roles: any[] | undefined): string | null { + if (!Array.isArray(roles)) return null; + let best: { position: number; color: string } | null = null; + for (const r of roles) { + if (r?.name === 'Owner') continue; + const color = r?.color as string | undefined; + const position = (r?.position ?? 0) as number; + if (!color || color.toLowerCase() === DEFAULT_ROLE_COLOR) continue; + if (!best || position > best.position) best = { position, color }; + } + return best?.color ?? null; +} + +interface PopoutState { + anchorRect: DOMRect; + member: MemberRow; +} + +export function MemberListContainer({ channelId, variant = 'panel' }: MemberListContainerProps) { + const result = useQuery( + api.members.getChannelMembers, + channelId ? { channelId: channelId as any } : 'skip', + ); + const { resolveStatus } = useOnlineUsers(); + const isMobile = useIsMobile(); + + const [popout, setPopout] = useState<PopoutState | null>(null); + const [mobileProfileFor, setMobileProfileFor] = useState<MemberRow | null>(null); + // Right-click context menu position + target member. + const [contextMenu, setContextMenu] = useState< + { x: number; y: number; member: MemberRow } | null + >(null); + // Big profile modal opened from the context menu's "View Profile". + const [profileModalFor, setProfileModalFor] = useState<MemberRow | null>(null); + const [manageRolesFor, setManageRolesFor] = useState<MemberRow | null>(null); + // Inline nickname editor — replaces the previous window.prompt + // alert with a real Modal + input + Save/Cancel buttons. + const [nicknameTarget, setNicknameTarget] = useState<MemberRow | null>(null); + const [nicknameDraft, setNicknameDraft] = useState(''); + const [nicknameSaving, setNicknameSaving] = useState(false); + const [nicknameError, setNicknameError] = useState<string | null>(null); + + // Resolve each member's status against the live presence heartbeat + // set. A user with stored status `online` but no heartbeat gets + // shown as offline; an invisible user also collapses to offline. + const members: MemberRow[] = Array.isArray(result) + ? result.map((m: any) => { + const storedStatus = m.status || 'offline'; + const userId = m.userId || m.id; + return { + userId, + displayName: m.displayName || m.username || 'User', + avatarUrl: m.avatarUrl ?? null, + status: resolveStatus(storedStatus, userId), + roleColor: pickRoleColor(m.roles), + }; + }) + : []; + + const online = members.filter((m) => m.status !== 'offline' && m.status !== 'invisible'); + const offline = members.filter((m) => m.status === 'offline' || m.status === 'invisible'); + + const containerClass = variant === 'drawer' ? styles.containerDrawer : styles.container; + + const handleOpenPopout = (member: MemberRow, rect: DOMRect) => { + // On mobile we open the full-screen profile modal instead of + // the anchored desktop popout so touch layouts get the same + // surface they already know from the DM header path. + if (isMobile) { + setMobileProfileFor(member); + return; + } + setPopout({ anchorRect: rect, member }); + }; + + const handleOpenSettings = () => { + window.dispatchEvent(new CustomEvent('brycord:open-user-settings')); + }; + + const handleMemberContextMenu = ( + member: MemberRow, + e: React.MouseEvent, + ) => { + e.preventDefault(); + e.stopPropagation(); + setContextMenu({ x: e.clientX, y: e.clientY, member }); + }; + + const setNickname = useMutation(api.auth.setNickname); + + const openNicknameEditor = (member: MemberRow) => { + setNicknameTarget(member); + setNicknameDraft(member.displayName); + setNicknameError(null); + setNicknameSaving(false); + setContextMenu(null); + }; + + // Reset the draft whenever a different target is loaded. + useEffect(() => { + if (nicknameTarget) { + setNicknameDraft(nicknameTarget.displayName); + setNicknameError(null); + } + }, [nicknameTarget?.userId]); + + const handleSaveNickname = async () => { + if (!nicknameTarget || nicknameSaving) return; + const localUserId = + typeof localStorage !== 'undefined' + ? localStorage.getItem('userId') + : null; + if (!localUserId) return; + setNicknameSaving(true); + setNicknameError(null); + try { + await setNickname({ + actorUserId: localUserId as any, + targetUserId: nicknameTarget.userId as any, + displayName: nicknameDraft.trim(), + }); + setNicknameTarget(null); + } catch (err: any) { + setNicknameError(err?.message ?? 'Failed to set nickname.'); + } finally { + setNicknameSaving(false); + } + }; + + const handleMentionMember = (member: MemberRow) => { + // Broadcast an insert-mention event — ChannelTextarea listens for + // this and pastes `@username` at the caret. Falling back to a + // silent no-op if no composer is mounted is fine. + window.dispatchEvent( + new CustomEvent('brycord:mention-user', { + detail: { userId: member.userId, displayName: member.displayName }, + }), + ); + setContextMenu(null); + }; + + return ( + <div className={containerClass}> + {online.length > 0 && ( + <MemberSection + label="Online" + count={online.length} + members={online} + onMemberClick={handleOpenPopout} + onMemberContextMenu={handleMemberContextMenu} + /> + )} + {offline.length > 0 && ( + <MemberSection + label="Offline" + count={offline.length} + members={offline} + muted + onMemberClick={handleOpenPopout} + onMemberContextMenu={handleMemberContextMenu} + /> + )} + + {popout && ( + <MemberProfilePopout + anchorRect={popout.anchorRect} + member={popout.member} + onClose={() => setPopout(null)} + onOpenProfile={handleOpenSettings} + onOpenRoles={() => setManageRolesFor(popout.member)} + /> + )} + + <MobileMemberProfileSheet + isOpen={!!mobileProfileFor} + onClose={() => setMobileProfileFor(null)} + member={mobileProfileFor ? { userId: mobileProfileFor.userId } : null} + /> + + <MemberProfileModal + isOpen={!!profileModalFor} + onClose={() => setProfileModalFor(null)} + member={profileModalFor ? { userId: profileModalFor.userId } : null} + /> + + {contextMenu && ( + <MemberContextMenu + x={contextMenu.x} + y={contextMenu.y} + onViewProfile={() => { + setProfileModalFor(contextMenu.member); + setContextMenu(null); + }} + onMention={() => handleMentionMember(contextMenu.member)} + onChangeNickname={() => openNicknameEditor(contextMenu.member)} + onManageRoles={() => { + setManageRolesFor(contextMenu.member); + setContextMenu(null); + }} + onClose={() => setContextMenu(null)} + /> + )} + + <ManageRolesPopout + isOpen={!!manageRolesFor} + onClose={() => setManageRolesFor(null)} + member={ + manageRolesFor + ? { + userId: manageRolesFor.userId as Id<'userProfiles'>, + displayName: manageRolesFor.displayName, + } + : null + } + /> + + <Modal.Root + isOpen={!!nicknameTarget} + onClose={() => { + if (nicknameSaving) return; + setNicknameTarget(null); + }} + size="small" + > + <Modal.Header + title={`Change Nickname${nicknameTarget ? ` for ${nicknameTarget.displayName}` : ''}`} + onClose={() => { + if (nicknameSaving) return; + setNicknameTarget(null); + }} + /> + <Modal.Content> + <div + style={{ + display: 'flex', + flexDirection: 'column', + gap: 10, + padding: '4px 4px 8px', + }} + > + <label + htmlFor="nickname-input" + style={{ + fontSize: 12, + fontWeight: 700, + letterSpacing: '0.04em', + textTransform: 'uppercase', + color: 'var(--text-tertiary)', + }} + > + Nickname + </label> + <input + id="nickname-input" + type="text" + value={nicknameDraft} + onChange={(e) => setNicknameDraft(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault(); + void handleSaveNickname(); + } + }} + placeholder="Enter a nickname" + autoFocus + maxLength={64} + disabled={nicknameSaving} + style={{ + padding: '10px 12px', + borderRadius: 6, + border: '1px solid var(--background-modifier-accent)', + background: 'var(--background-tertiary)', + color: 'var(--text-primary)', + font: 'inherit', + fontSize: 14, + outline: 'none', + }} + /> + <div style={{ fontSize: 12, color: 'var(--text-tertiary)' }}> + Leave empty to clear the nickname and fall back to the + user's original display name. + </div> + {nicknameError && ( + <div + style={{ + fontSize: 12, + color: 'var(--status-danger, #ed4245)', + }} + > + {nicknameError} + </div> + )} + </div> + </Modal.Content> + <Modal.Footer> + <Button + variant="secondary" + size="sm" + onClick={() => setNicknameTarget(null)} + disabled={nicknameSaving} + > + Cancel + </Button> + <Button + variant="primary" + size="sm" + loading={nicknameSaving} + onClick={handleSaveNickname} + > + Save + </Button> + </Modal.Footer> + </Modal.Root> + </div> + ); +} + +function MemberSection({ + label, + count, + members, + muted, + onMemberClick, + onMemberContextMenu, +}: { + label: string; + count: number; + members: MemberRow[]; + muted?: boolean; + onMemberClick: (member: MemberRow, rect: DOMRect) => void; + onMemberContextMenu?: (member: MemberRow, e: React.MouseEvent) => void; +}) { + return ( + <div className={styles.section}> + <div className={styles.sectionHeader}> + <span className={styles.sectionLabel}>{label}</span> + <span className={styles.sectionCount}>{count}</span> + </div> + <div className={styles.membersList}> + {members.map((m) => ( + <button + key={m.userId} + type="button" + className={`${styles.memberItem} ${muted ? styles.memberItemOffline : ''}`} + onClick={(e) => { + const rect = e.currentTarget.getBoundingClientRect(); + onMemberClick(m, rect); + }} + onContextMenu={(e) => onMemberContextMenu?.(m, e)} + > + <div className={styles.memberGrid}> + <div className={styles.memberContent}> + <div className={styles.memberAvatar}> + <Avatar + src={m.avatarUrl} + size={32} + fallback={m.displayName} + status={m.status as any} + /> + </div> + <div className={styles.memberInfo}> + <span + className={styles.memberName} + style={{ color: m.roleColor ?? undefined }} + > + {m.displayName} + </span> + </div> + </div> + </div> + </button> + ))} + </div> + </div> + ); +} diff --git a/packages/shared/src/components/member/MemberProfileModal.module.css b/packages/shared/src/components/member/MemberProfileModal.module.css new file mode 100644 index 0000000..a153301 --- /dev/null +++ b/packages/shared/src/components/member/MemberProfileModal.module.css @@ -0,0 +1,389 @@ +.backdrop { + position: fixed; + inset: 0; + z-index: 15000; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba(0, 0, 0, 0.55); + backdrop-filter: blur(2px); + padding: 20px; + box-sizing: border-box; +} + +/* Fixed 600px wide, shrinks vertically with the viewport up to a + 780px ceiling. The 3px border is painted with the member's accent + color inline so the accent bleeds "all around" the card. */ +.cardWrap { + position: relative; + width: 600px; + max-width: calc(100vw - 40px); + height: min(780px, calc(100svh - 40px)); + max-height: 780px; + background-color: var(--background-primary, #111214); + border: 3px solid transparent; + border-radius: 12px; + overflow: hidden; + display: flex; + flex-direction: column; +} + +.closeButton { + position: absolute; + top: 12px; + right: 12px; + z-index: 2; + display: flex; + align-items: center; + justify-content: center; + width: 30px; + height: 30px; + border: none; + border-radius: 50%; + background-color: rgba(0, 0, 0, 0.5); + color: #fff; + cursor: pointer; + transition: background-color 0.1s, transform 0.1s; +} + +.closeButton:hover { + background-color: rgba(0, 0, 0, 0.75); + transform: scale(1.05); +} + +.banner { + flex: 0 0 auto; + height: 210px; + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} + +/* Header row — holds the pulled-up avatar on the left and the + action buttons (Edit Profile / Message / Add Friend / More) + flush to the right. `align-items: flex-end` sits the button + row at the bottom of the avatar so the primary button lines + up with the avatar's lower third, matching the Fluxer + reference. */ +.headerRow { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 12px; + min-height: 52px; + padding: 0 24px; + flex-shrink: 0; +} + +/* Avatar wrap — pulls up over the banner so the bottom third of + the avatar overlaps into the body. The dark ring around the + avatar is NOT an accent-colored border — it's the card's + background color bleeding through the wrapper's padding, + creating a Fluxer/Discord-style cutout against the banner + above. */ +.avatarWrap { + position: relative; + margin-top: -56px; + padding: 6px; + border-radius: 50%; + background-color: var(--background-primary, #111214); + box-sizing: content-box; + flex-shrink: 0; + display: inline-flex; + align-items: center; + justify-content: center; +} + +/* ── Action row (Edit Profile / Message / Add Friend / More) ── + Sits flush against the right edge of the header row, bottom- + aligned with the avatar so the primary button baseline lines + up with the lower third of the avatar. */ +.actionRow { + display: flex; + align-items: center; + gap: 8px; + padding-bottom: 4px; +} + +.primaryButton { + display: inline-flex; + align-items: center; + gap: 6px; + height: 36px; + padding: 0 14px; + border: none; + border-radius: 6px; + background-color: var(--brand-primary, #4641d9); + color: #fff; + font: inherit; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: background-color 0.1s, filter 0.1s; +} + +.primaryButton:hover { + filter: brightness(1.08); +} + +.primaryButton:active { + filter: brightness(0.92); +} + +.iconButton { + display: inline-flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + padding: 0; + border: none; + border-radius: 6px; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.06)); + color: var(--text-primary, #fff); + cursor: pointer; + transition: background-color 0.1s; +} + +.iconButton:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.1)); +} + +/* ── More dropdown ─────────────────────────────────────────── + Anchored to the three-dots button via relative positioning + on the wrapper. Sized to the reference popup (~200px wide), + background matches the floating surfaces elsewhere in the + app. Opens downward + left-aligned so it never clips the + card's right edge. */ +.moreWrap { + position: relative; +} + +.moreMenu { + position: absolute; + top: calc(100% + 6px); + right: 0; + z-index: 3; + min-width: 200px; + padding: 6px; + background-color: var(--background-floating, #18191c); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.08)); + border-radius: 8px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35); + display: flex; + flex-direction: column; + gap: 2px; +} + +.moreMenuItem { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 8px 10px; + background: transparent; + border: none; + border-radius: 4px; + color: var(--text-primary, #fff); + font: inherit; + font-size: 14px; + font-weight: 500; + text-align: left; + cursor: pointer; + transition: background-color 0.08s; +} + +.moreMenuItem > span { + flex: 1; + text-align: left; +} + +.moreMenuItem:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.08)); +} + +.moreMenuItemDanger { + color: var(--status-danger, #ed4245); +} + +.moreMenuItemDanger:hover { + background-color: var(--status-danger, #ed4245); + color: #fff; +} + +.moreMenuSeparator { + height: 1px; + background-color: var(--background-modifier-accent, rgba(255, 255, 255, 0.08)); + margin: 4px 0; +} + +/* Status dot — pinned to the bottom-right of the avatar wrap. The + thick 4px ring blends with the card background so the dot reads + as "cut out of the avatar" the way Discord's does. */ +.statusDot { + position: absolute; + bottom: 2px; + right: 2px; + width: 28px; + height: 28px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + border: 4px solid var(--background-primary, #111214); + color: #fff; + box-sizing: border-box; + z-index: 2; +} + +.statusOnline { + background-color: #3ba55d; + color: #fff; +} + +.statusIdle { + background-color: #faa61a; + color: #fff; +} + +.statusDnd { + background-color: #ed4245; + color: #fff; +} + +.statusOffline { + background-color: #747f8d; + color: #fff; +} + +.body { + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; + padding: 12px 24px 24px; + display: flex; + flex-direction: column; + gap: 16px; +} + +.nameBlock { + display: flex; + flex-direction: column; + gap: 4px; +} + +.displayName { + margin: 0; + font-size: 22px; + font-weight: 700; + color: var(--text-primary, #fff); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.realName { + font-size: 13px; + color: var(--text-primary-muted, #a0a3a8); +} + +.divider { + height: 1px; + background-color: var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); + margin: 4px 0; +} + +.section { + display: flex; + flex-direction: column; + gap: 8px; +} + +.sectionLabel { + font-size: 11px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--text-primary-muted, #a0a3a8); +} + +.bioText { + font-size: 14px; + line-height: 1.5; + color: var(--text-primary, #fff); + white-space: pre-wrap; + word-wrap: break-word; +} + +.rolesList { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.rolePill { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 10px; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.04)); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.08)); + border-radius: 999px; + font-size: 12px; + font-weight: 500; +} + +.roleDot { + width: 10px; + height: 10px; + border-radius: 50%; + flex-shrink: 0; +} + +.roleName { + color: var(--text-primary, #fff); + max-width: 180px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.idRow { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 14px; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.03)); + border-radius: 6px; +} + +.idValue { + flex: 1 1 auto; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 13px; + color: var(--text-primary, #fff); +} + +.copyButton { + flex-shrink: 0; + width: 28px; + height: 28px; + display: inline-flex; + align-items: center; + justify-content: center; + background-color: transparent; + border: none; + border-radius: 4px; + color: var(--text-primary-muted, #a0a3a8); + cursor: pointer; + transition: background-color 0.1s, color 0.1s; +} + +.copyButton:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.06)); + color: var(--text-primary, #fff); +} diff --git a/packages/shared/src/components/member/MemberProfileModal.tsx b/packages/shared/src/components/member/MemberProfileModal.tsx new file mode 100644 index 0000000..8fe7519 --- /dev/null +++ b/packages/shared/src/components/member/MemberProfileModal.tsx @@ -0,0 +1,352 @@ +/** + * MemberProfileModal — full-screen "View Profile" card used from the + * DM header and (optionally) from member list rows. Ported from the + * new UI's MemberProfileModal, rebuilt against Convex: + * + * - Accent, banner, avatar, bio, status all come from + * `api.auth.getPublicKeys` for the target user (live — `useQuery`). + * - Presence is reconciled through `PresenceContext.resolveStatus` + * so the dot matches the live heartbeat set. + * - "Message" opens / creates the 1:1 DM via `api.dms.openDM`. + * - "Edit Profile" fires the global `brycord:open-user-settings` + * event when the viewer is themselves. + * - "Rotate Encryption Key" is shown when the caller passes + * `onRotateKey` (i.e. the modal was opened from a DM header). + */ +import { useEffect, useMemo, useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { useNavigate } from 'react-router-dom'; +import { AnimatePresence, motion } from 'framer-motion'; +import { useMutation, useQuery } from 'convex/react'; +import { + CheckCircle, + ChatCircleDots, + Copy, + DotsThree, + Key, + PencilSimple, + X, +} from '@phosphor-icons/react'; +import { Avatar } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import { useOnlineUsers } from '../../contexts/PresenceContext'; +import styles from './MemberProfileModal.module.css'; + +interface MemberProfileModalMember { + userId: string; +} + +interface MemberProfileModalProps { + isOpen: boolean; + onClose: () => void; + member: MemberProfileModalMember | null; + /** + * Optional handler for the "Rotate Encryption Key" button. When + * provided (and the viewer is not themselves), an icon button next + * to Message fires it. A resolved promise shows a success line; a + * rejected one shows the error message. + */ + onRotateKey?: () => Promise<void>; +} + +const DEFAULT_ACCENT = '#4641D9'; + +interface StatusDisplay { + label: string; + dotClass: string; +} + +function getStatusDisplay(presence: string): StatusDisplay { + switch (presence) { + case 'online': + return { label: 'Online', dotClass: styles.statusOnline }; + case 'idle': + return { label: 'Idle', dotClass: styles.statusIdle }; + case 'dnd': + return { label: 'Do Not Disturb', dotClass: styles.statusDnd }; + case 'offline': + default: + return { label: 'Offline', dotClass: styles.statusOffline }; + } +} + +export function MemberProfileModal({ + isOpen, + onClose, + member, + onRotateKey, +}: MemberProfileModalProps) { + const navigate = useNavigate(); + const { resolveStatus } = useOnlineUsers(); + + const [copied, setCopied] = useState(false); + const [moreOpen, setMoreOpen] = useState(false); + const [rotating, setRotating] = useState(false); + const [rotateStatus, setRotateStatus] = useState< + { type: 'success' | 'error'; message: string } | null + >(null); + const moreButtonRef = useRef<HTMLButtonElement>(null); + const moreMenuRef = useRef<HTMLDivElement>(null); + + const allUsers = useQuery(api.auth.getPublicKeys, isOpen ? {} : 'skip') ?? []; + const localUserId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + + const profile = useMemo( + () => (member ? allUsers.find((u) => u.id === member.userId) : undefined), + [allUsers, member], + ); + + const openDM = useMutation(api.dms.openDM); + + useEffect(() => { + if (!moreOpen) return; + const handler = (e: MouseEvent) => { + const target = e.target as Node; + if (moreMenuRef.current?.contains(target)) return; + if (moreButtonRef.current?.contains(target)) return; + setMoreOpen(false); + }; + document.addEventListener('mousedown', handler); + return () => document.removeEventListener('mousedown', handler); + }, [moreOpen]); + + useEffect(() => { + if (!isOpen) { + setMoreOpen(false); + setRotateStatus(null); + setRotating(false); + } + }, [isOpen]); + + useEffect(() => { + if (!isOpen) return; + const handler = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + document.addEventListener('keydown', handler); + return () => document.removeEventListener('keydown', handler); + }, [isOpen, onClose]); + + if (!isOpen || !member) return null; + + const displayName = + profile?.displayName || profile?.username || 'Unknown User'; + const username = profile?.username || ''; + const avatarUrl = profile?.avatarUrl ?? null; + const bio = profile?.aboutMe?.trim(); + const accent = (profile as any)?.accentColor || DEFAULT_ACCENT; + const storedStatus = (profile?.status as string | undefined) || 'offline'; + const livePresence = resolveStatus(storedStatus, member.userId); + const statusDisplay = getStatusDisplay(livePresence); + + const isMeLocal = localUserId === member.userId; + + const handleCopy = () => { + void navigator.clipboard.writeText(member.userId).then( + () => { + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }, + () => {}, + ); + }; + + const handleEditProfile = () => { + window.dispatchEvent(new CustomEvent('brycord:open-user-settings')); + onClose(); + }; + + const handleMessage = async () => { + if (!localUserId) return; + try { + const result = await openDM({ + userId: localUserId as any, + targetUserId: member.userId as any, + }); + navigate(`/channels/@me/${result.channelId}`); + onClose(); + } catch (err) { + console.warn('Failed to open DM from profile modal:', err); + } + }; + + const handleRotate = async () => { + if (!onRotateKey || rotating) return; + setRotating(true); + setRotateStatus(null); + try { + await onRotateKey(); + setRotateStatus({ + type: 'success', + message: 'Encryption key rotated.', + }); + } catch (err: any) { + setRotateStatus({ + type: 'error', + message: err?.message ?? 'Rotation failed.', + }); + } finally { + setRotating(false); + } + }; + + return createPortal( + <AnimatePresence> + {isOpen && ( + <motion.div + className={styles.backdrop} + initial={{ opacity: 0 }} + animate={{ opacity: 1 }} + exit={{ opacity: 0 }} + transition={{ duration: 0.15 }} + onClick={onClose} + > + <motion.div + className={styles.cardWrap} + initial={{ opacity: 0, scale: 0.94 }} + animate={{ opacity: 1, scale: 1 }} + exit={{ opacity: 0, scale: 0.94 }} + transition={{ duration: 0.18, ease: 'easeOut' }} + style={{ + borderColor: accent, + boxShadow: `0 10px 30px rgba(0, 0, 0, 0.35), 0 0 0 1px ${accent}55, 0 0 40px ${accent}30`, + }} + onClick={(e) => e.stopPropagation()} + > + <button + type="button" + className={styles.closeButton} + onClick={onClose} + aria-label="Close profile" + > + <X size={18} weight="bold" /> + </button> + + <div + className={styles.banner} + style={{ backgroundColor: accent }} + /> + + <div className={styles.headerRow}> + <div className={styles.avatarWrap}> + <Avatar src={avatarUrl} fallback={displayName} size={96} /> + <div + className={`${styles.statusDot} ${statusDisplay.dotClass}`} + aria-label={statusDisplay.label} + /> + </div> + + <div className={styles.actionRow}> + {isMeLocal ? ( + <button + type="button" + className={styles.primaryButton} + onClick={handleEditProfile} + > + <PencilSimple size={16} weight="fill" /> + <span>Edit Profile</span> + </button> + ) : ( + <> + <button + type="button" + className={styles.primaryButton} + onClick={handleMessage} + > + <ChatCircleDots size={16} weight="fill" /> + <span>Message</span> + </button> + {onRotateKey && ( + <button + type="button" + className={styles.iconButton} + onClick={handleRotate} + disabled={rotating} + aria-label="Rotate Encryption Key" + title="Rotate Encryption Key" + > + <Key size={18} weight="bold" /> + </button> + )} + </> + )} + <div className={styles.moreWrap}> + <button + ref={moreButtonRef} + type="button" + className={styles.iconButton} + onClick={() => setMoreOpen((v) => !v)} + aria-label="More options" + > + <DotsThree size={22} weight="bold" /> + </button> + </div> + </div> + </div> + + <div className={styles.body}> + <div className={styles.nameBlock}> + <h2 className={styles.displayName}>{displayName}</h2> + {username && ( + <div className={styles.realName}>@{username}</div> + )} + </div> + + {bio && ( + <> + <div className={styles.divider} /> + <div className={styles.section}> + <div className={styles.sectionLabel}>About Me</div> + <div className={styles.bioText}>{bio}</div> + </div> + </> + )} + + {rotateStatus && ( + <> + <div className={styles.divider} /> + <div className={styles.section}> + <div + style={{ + fontSize: 13, + color: + rotateStatus.type === 'success' + ? '#3ba55d' + : 'var(--status-danger, #ed4245)', + }} + > + {rotateStatus.message} + </div> + </div> + </> + )} + + <div className={styles.divider} /> + + <div className={styles.section}> + <div className={styles.sectionLabel}>User ID</div> + <div className={styles.idRow}> + <span className={styles.idValue}>{member.userId}</span> + <button + type="button" + className={styles.copyButton} + onClick={handleCopy} + aria-label="Copy user ID" + > + {copied ? ( + <CheckCircle size={16} weight="fill" /> + ) : ( + <Copy size={16} weight="fill" /> + )} + </button> + </div> + </div> + </div> + </motion.div> + </motion.div> + )} + </AnimatePresence>, + document.body, + ); +} diff --git a/packages/shared/src/components/member/MemberProfilePopout.module.css b/packages/shared/src/components/member/MemberProfilePopout.module.css new file mode 100644 index 0000000..b5f81dc --- /dev/null +++ b/packages/shared/src/components/member/MemberProfilePopout.module.css @@ -0,0 +1,171 @@ +.card { + z-index: 15000; + display: flex; + flex-direction: column; + background-color: var(--background-primary, #111214); + border: 3px solid transparent; + border-radius: 10px; + overflow: hidden; + max-height: calc(100vh - 32px); +} + +.banner { + flex: 0 0 auto; + height: 110px; + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} + +/* Avatar wrap — pulls up over the banner, anchored left. The dark + ring around the avatar is the card's background color bleeding + through the wrapper's padding — creates a cutout effect against + the banner above. `align-self: flex-start` keeps it left-aligned + in the flex-column parent. */ +.avatarWrap { + position: relative; + align-self: flex-start; + margin: -40px 0 0 16px; + padding: 5px; + border-radius: 50%; + background-color: var(--background-primary, #111214); + box-sizing: content-box; + flex-shrink: 0; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.body { + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; + padding: 10px 16px 14px; + display: flex; + flex-direction: column; + gap: 10px; +} + +.nameBlock { + display: flex; + flex-direction: column; + gap: 2px; +} + +.displayName { + font-size: 18px; + font-weight: 700; + line-height: 1.2; + color: var(--text-primary, #fff); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.handle { + font-size: 12px; + color: var(--text-primary-muted, #a0a3a8); + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.divider { + height: 1px; + background-color: var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); +} + +.section { + display: flex; + flex-direction: column; + gap: 6px; +} + +.sectionLabel { + font-size: 11px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--text-primary-muted, #a0a3a8); +} + +.bioText { + font-size: 13px; + line-height: 1.45; + color: var(--text-primary, #fff); + white-space: pre-wrap; + word-wrap: break-word; + max-height: 8em; + overflow-y: auto; +} + +.memberSince { + font-size: 13px; + color: var(--text-primary, #fff); +} + +.rolesHeader { + display: flex; + align-items: center; + justify-content: space-between; +} + +.addRoleButton { + display: inline-flex; + align-items: center; + justify-content: center; + width: 18px; + height: 18px; + padding: 0; + background-color: var(--background-tertiary, rgba(255, 255, 255, 0.08)); + border: none; + border-radius: 4px; + color: var(--text-primary-muted, #a0a3a8); + cursor: pointer; + transition: background-color 0.1s, color 0.1s; +} + +.addRoleButton:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.12)); + color: var(--text-primary, #fff); +} + +.noRoles { + font-size: 12px; + font-style: italic; + color: var(--text-primary-muted, #a0a3a8); +} + +.rolesList { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.rolePill { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 3px 8px; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.04)); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.08)); + border-radius: 999px; + font-size: 11px; + font-weight: 500; +} + +.roleDot { + width: 8px; + height: 8px; + border-radius: 50%; + flex-shrink: 0; +} + +.roleName { + color: var(--text-primary, #fff); + max-width: 140px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/packages/shared/src/components/member/MemberProfilePopout.tsx b/packages/shared/src/components/member/MemberProfilePopout.tsx new file mode 100644 index 0000000..8d252a1 --- /dev/null +++ b/packages/shared/src/components/member/MemberProfilePopout.tsx @@ -0,0 +1,278 @@ +/** + * MemberProfilePopout — Discord-style profile card that opens next + * to the member list when the user clicks a member row. Anchored + * against the clicked row's bounding rect and rendered via a portal + * so it escapes sidebar clipping. + */ +import { useEffect, useMemo, useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { useQuery } from 'convex/react'; +import { Key, PencilSimple, ShieldCheck } from '@phosphor-icons/react'; +import { Avatar, Button } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import { useOnlineUsers } from '../../contexts/PresenceContext'; +import styles from './MemberProfilePopout.module.css'; + +export interface MemberProfilePopoutMember { + userId: string; + displayName: string; + avatarUrl: string | null; + status?: string; +} + +interface MemberProfilePopoutProps { + anchorRect: DOMRect; + member: MemberProfilePopoutMember; + onClose: () => void; + onOpenProfile?: () => void; + onOpenRoles?: () => void; + /** + * Optional handler for "Rotate Encryption Key". When provided, + * renders a button that invokes it — currently only passed in by + * ChannelHeader when the open channel is a DM with this user. + * Returning a rejected promise leaves the busy state up to the + * caller; a resolved one closes the popout. + */ + onRotateKey?: () => Promise<void>; +} + +const DEFAULT_ACCENT = '#4641D9'; +const POPOUT_WIDTH = 320; +const POPOUT_GAP = 12; +const VIEWPORT_MARGIN = 16; + +function mapPresence( + status: string | undefined, +): 'online' | 'idle' | 'dnd' | 'offline' { + switch (status) { + case 'online': + return 'online'; + case 'idle': + return 'idle'; + case 'dnd': + return 'dnd'; + default: + return 'offline'; + } +} + +export function MemberProfilePopout({ + anchorRect, + member, + onClose, + onOpenProfile, + onOpenRoles, + onRotateKey, +}: MemberProfilePopoutProps) { + const [rotating, setRotating] = useState(false); + const [rotateStatus, setRotateStatus] = useState< + { type: 'success' | 'error'; message: string } | null + >(null); + const cardRef = useRef<HTMLDivElement>(null); + const allUsers = useQuery(api.auth.getPublicKeys) ?? []; + const localUserId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const myPerms = useQuery( + api.roles.getMyPermissions, + localUserId ? { userId: localUserId as Id<'userProfiles'> } : 'skip', + ); + const canManageRoles = !!myPerms?.manage_roles; + + const fullUser = useMemo( + () => allUsers.find((u) => u.id === member.userId), + [allUsers, member.userId], + ); + + // Close on click-outside / Escape. mousedown intercepts before + // any following click that might re-open the popout on another row. + useEffect(() => { + const handleClick = (e: MouseEvent) => { + if (cardRef.current && !cardRef.current.contains(e.target as Node)) { + onClose(); + } + }; + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + document.addEventListener('mousedown', handleClick); + document.addEventListener('keydown', handleEscape); + return () => { + document.removeEventListener('mousedown', handleClick); + document.removeEventListener('keydown', handleEscape); + }; + }, [onClose]); + + // Pull the target user's accentColor from their public profile + // row so the popout reflects whatever they saved in Settings → + // My Account. Falls back to the brand default when the user + // hasn't picked one yet. + const accent = (fullUser as any)?.accentColor || DEFAULT_ACCENT; + + // Position: anchor to the LEFT of the clicked row. The member + // list sits on the right edge so the popout flows inward. Clamp + // to the viewport so it never clips. + const positionStyle = useMemo<React.CSSProperties>(() => { + const rawLeft = anchorRect.left - POPOUT_WIDTH - POPOUT_GAP; + const left = Math.max( + VIEWPORT_MARGIN, + Math.min(rawLeft, window.innerWidth - POPOUT_WIDTH - VIEWPORT_MARGIN), + ); + const desiredTop = anchorRect.top - 8; + const estimatedHeight = 420; + const top = Math.max( + VIEWPORT_MARGIN, + Math.min(desiredTop, window.innerHeight - estimatedHeight - VIEWPORT_MARGIN), + ); + return { + position: 'fixed', + left, + top, + width: POPOUT_WIDTH, + borderColor: accent, + boxShadow: `0 16px 32px rgba(0, 0, 0, 0.45), 0 0 0 1px ${accent}55, 0 0 28px ${accent}30`, + }; + }, [anchorRect, accent]); + + const displayName = fullUser?.displayName || member.displayName; + const username = fullUser?.username || member.displayName; + const avatarUrl = fullUser?.avatarUrl ?? member.avatarUrl ?? null; + const aboutMe = fullUser?.aboutMe?.trim(); + // Resolve presence against the live heartbeat set so the popout + // matches what the sidebar / member list show for this user. + const { resolveStatus } = useOnlineUsers(); + const storedStatus = fullUser?.status ?? member.status ?? 'offline'; + const liveStatus = resolveStatus(storedStatus, member.userId); + const presence = mapPresence(liveStatus); + const isMe = localUserId === member.userId; + + return createPortal( + <div + ref={cardRef} + className={styles.card} + style={positionStyle} + onClick={(e) => e.stopPropagation()} + > + {/* Banner — solid accent color. */} + <div + className={styles.banner} + style={{ backgroundColor: accent }} + /> + + <div className={styles.avatarWrap}> + <Avatar + src={avatarUrl} + fallback={displayName} + size={80} + status={presence} + /> + </div> + + <div className={styles.body}> + <div className={styles.nameBlock}> + <div className={styles.displayName} title={displayName}> + {displayName} + </div> + <div className={styles.handle} title={username}> + {username} + </div> + </div> + + {aboutMe && ( + <> + <div className={styles.divider} /> + <div className={styles.section}> + <div className={styles.sectionLabel}>About Me</div> + <div className={styles.bioText}>{aboutMe}</div> + </div> + </> + )} + + {isMe && onOpenProfile && ( + <> + <div className={styles.divider} /> + <Button + variant="primary" + size="sm" + fitContainer + icon={<PencilSimple size={16} />} + onClick={() => { + onOpenProfile(); + onClose(); + }} + > + Edit Profile + </Button> + </> + )} + + {!isMe && canManageRoles && onOpenRoles && ( + <> + <div className={styles.divider} /> + <Button + variant="secondary" + size="sm" + fitContainer + icon={<ShieldCheck size={16} />} + onClick={() => { + onOpenRoles(); + onClose(); + }} + > + Manage Roles + </Button> + </> + )} + + {!isMe && onRotateKey && ( + <> + <div className={styles.divider} /> + <Button + variant="secondary" + size="sm" + fitContainer + icon={<Key size={16} />} + loading={rotating} + onClick={async () => { + if (rotating) return; + setRotating(true); + setRotateStatus(null); + try { + await onRotateKey(); + setRotateStatus({ + type: 'success', + message: 'Encryption key rotated.', + }); + } catch (err: any) { + setRotateStatus({ + type: 'error', + message: err?.message ?? 'Rotation failed.', + }); + } finally { + setRotating(false); + } + }} + > + Rotate Encryption Key + </Button> + {rotateStatus && ( + <div + style={{ + marginTop: 8, + fontSize: 12, + color: + rotateStatus.type === 'success' + ? '#3ba55d' + : 'var(--status-danger, #ed4245)', + }} + > + {rotateStatus.message} + </div> + )} + </> + )} + </div> + </div>, + document.body, + ); +} diff --git a/packages/shared/src/components/member/MobileMemberProfileSheet.module.css b/packages/shared/src/components/member/MobileMemberProfileSheet.module.css new file mode 100644 index 0000000..fabaa78 --- /dev/null +++ b/packages/shared/src/components/member/MobileMemberProfileSheet.module.css @@ -0,0 +1,285 @@ +/* ── Mobile member profile sheet ───────────────────────────────────── + Stacks on top of the ChannelDetailsDrawer member list on mobile. + Layout mirrors the Fluxer reference: big banner, avatar overlapping + the banner, name + handle, optional Edit Profile button, then a + vertical stack of rounded cards for Member Since / Roles / Note. */ + +/* Override the default BottomSheet surface colour. The shared + `--background-secondary` feels too light sitting on top of the + members drawer (also secondary) — the profile sheet needs to read + as a distinct layer, so it uses the darker `--background-primary` + instead. Passed via the BottomSheet `className` prop. */ +.sheet { + background-color: var(--background-primary); +} + +.root { + display: flex; + flex-direction: column; + min-height: 100%; + padding-bottom: 32px; +} + +.banner { + position: relative; + flex-shrink: 0; + height: 160px; + width: 100%; + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} + +/* In-banner drag handle — replaces the default BottomSheet handle + row so the drag affordance sits inside the colored banner area + instead of on its own strip above it. White/translucent so it + remains visible on any banner colour or image. */ +.bannerHandle { + position: absolute; + top: 8px; + left: 50%; + transform: translateX(-50%); + width: 40px; + height: 4px; + border-radius: 2px; + background-color: rgba(255, 255, 255, 0.6); + pointer-events: none; +} + +/* Row under the banner that holds the avatar on the left and the + "more" action button on the right. Negative top margin lifts the + avatar to overlap the banner by half its height. */ + +.headerRow { + display: flex; + align-items: flex-end; + justify-content: space-between; + padding: 0 1rem; + margin-top: -44px; +} + +.avatarWrap { + padding: 4px; + border-radius: 50%; + background-color: var(--background-primary); + box-sizing: content-box; + display: inline-flex; + align-items: center; + justify-content: center; + /* The sheet surface is `--background-primary`, so the Avatar's + status dot needs a matching cut-out ring to blend. */ + --avatar-status-border: var(--background-primary); +} + +.moreButton { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: 50%; + border: none; + background-color: var(--background-secondary-alt); + color: var(--text-primary); + cursor: pointer; + transition: background-color 0.15s; + -webkit-tap-highlight-color: transparent; + margin-bottom: 4px; +} + +.moreButton:active { + background-color: var(--background-modifier-hover); +} + +/* ── Name block + edit button ───────────────────────────────────────*/ + +.nameBlock { + display: flex; + align-items: baseline; + gap: 6px; + padding: 12px 1rem 0; + flex-wrap: wrap; +} + +.displayName { + font-size: 22px; + font-weight: 800; + line-height: 1.1; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 100%; +} + +.handle { + font-size: 16px; + font-weight: 600; + color: var(--text-primary-muted, #a0a3a8); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.editProfileButton { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + margin: 14px 1rem 0; + padding: 12px 16px; + background-color: var(--brand-primary); + color: #fff; + border: none; + border-radius: 0.75rem; + font: inherit; + font-size: 15px; + font-weight: 700; + cursor: pointer; + transition: filter 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.editProfileButton:active { + filter: brightness(0.92); +} + +/* ── Card stack ─────────────────────────────────────────────────────*/ + +.cardStack { + display: flex; + flex-direction: column; + gap: 12px; + padding: 16px 1rem 0; +} + +.card { + background-color: var(--background-secondary-alt); + border-radius: 0.75rem; + padding: 14px 16px; + display: flex; + flex-direction: column; + gap: 8px; +} + +.cardTitleRow { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.cardTitle { + font-size: 15px; + font-weight: 700; + color: var(--text-primary); +} + +.cardAction { + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + border-radius: 50%; + border: none; + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.06)); + color: var(--text-primary-muted); + cursor: pointer; + transition: background-color 0.15s, color 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.cardAction:active { + background-color: var(--background-modifier-accent, rgba(255, 255, 255, 0.1)); + color: var(--text-primary); +} + +.cardSubtitle { + font-size: 13px; + font-weight: 500; + color: var(--text-primary-muted); + font-style: italic; +} + +/* ── Member Since row ───────────────────────────────────────────────*/ + +.memberSinceRow { + display: flex; + align-items: center; + gap: 10px; + font-size: 14px; + font-weight: 500; + color: var(--text-primary); +} + +.memberSinceIcon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + border-radius: 50%; + background-color: var(--brand-primary-light); + color: #fff; + font-size: 11px; + font-weight: 700; + flex-shrink: 0; + overflow: hidden; +} + +.memberSinceIcon img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.memberSinceSeparator { + width: 4px; + height: 4px; + border-radius: 50%; + background-color: var(--text-primary-muted); + opacity: 0.5; + flex-shrink: 0; +} + +/* ── Roles ──────────────────────────────────────────────────────────*/ + +.rolesList { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.rolePill { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 10px; + background-color: var(--background-secondary); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.08)); + border-radius: 999px; + font-size: 12px; + font-weight: 500; +} + +.roleDot { + width: 8px; + height: 8px; + border-radius: 50%; + flex-shrink: 0; +} + +.roleName { + color: var(--text-primary); + max-width: 180px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.noRoles { + font-size: 13px; + font-style: italic; + color: var(--text-primary-muted); +} diff --git a/packages/shared/src/components/member/MobileMemberProfileSheet.tsx b/packages/shared/src/components/member/MobileMemberProfileSheet.tsx new file mode 100644 index 0000000..acaef25 --- /dev/null +++ b/packages/shared/src/components/member/MobileMemberProfileSheet.tsx @@ -0,0 +1,313 @@ +/** + * MobileMemberProfileSheet — mobile bottom sheet opened when tapping + * a member row or a DM header. Ported from the new UI's sheet layout + * and rebuilt against Convex: profile pulls from `api.auth.getPublicKeys`, + * presence runs through `PresenceContext.resolveStatus`, and the roles + * card uses `api.roles.listMembers` + `api.roles.list` for the live + * assignment set. + * + * ┌── handle ──┐ + * │ [banner] │ banner = accent color + * │ (avatar) ⋯│ avatar lifts up to overlap banner + * │ Name │ + * │ @username │ + * │ [Edit Prof]│ (self only) + * │ ┌ About Me ┐│ + * │ └──────────┘ + * │ ┌ Roles ┐│ + * │ └──────────┘ + * │ [Rotate Key] (DM only) + * └─────────────┘ + */ +import { useMemo, useState } from 'react'; +import { useMutation, useQuery } from 'convex/react'; +import { useNavigate } from 'react-router-dom'; +import { ChatCircleDots, DotsThree, Key, PencilSimple } from '@phosphor-icons/react'; +import { Avatar, BottomSheet } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import { useOnlineUsers } from '../../contexts/PresenceContext'; +import styles from './MobileMemberProfileSheet.module.css'; + +interface MobileMemberProfileSheetMember { + userId: string; +} + +interface MobileMemberProfileSheetProps { + isOpen: boolean; + onClose: () => void; + member: MobileMemberProfileSheetMember | null; + /** + * Optional — when provided, a "Rotate Encryption Key" button is + * rendered below the action row. Only passed in from DM entry + * points so server-member sheets don't show it. + */ + onRotateKey?: () => Promise<void>; +} + +const DEFAULT_ACCENT = '#4641D9'; + +function mapPresence( + status: string | undefined, +): 'online' | 'idle' | 'dnd' | 'offline' { + switch (status) { + case 'online': + return 'online'; + case 'idle': + return 'idle'; + case 'dnd': + return 'dnd'; + default: + return 'offline'; + } +} + +export function MobileMemberProfileSheet({ + isOpen, + onClose, + member, + onRotateKey, +}: MobileMemberProfileSheetProps) { + const navigate = useNavigate(); + const { resolveStatus } = useOnlineUsers(); + const [rotating, setRotating] = useState(false); + const [rotateStatus, setRotateStatus] = useState< + { type: 'success' | 'error'; message: string } | null + >(null); + + const allUsers = useQuery(api.auth.getPublicKeys, isOpen ? {} : 'skip') ?? []; + const localUserId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + + const profile = useMemo( + () => (member ? allUsers.find((u) => u.id === member.userId) : undefined), + [allUsers, member], + ); + + const roleMembers = useQuery(api.roles.listMembers, isOpen ? {} : 'skip'); + const allRoles = useQuery(api.roles.list, isOpen ? {} : 'skip') ?? []; + + const assignedRoles = useMemo(() => { + if (!member || !roleMembers) return []; + const row = (roleMembers as any[]).find((r) => r.id === member.userId); + if (!row) return []; + const ids = new Set((row.roles as any[]).map((r) => r._id as string)); + return (allRoles as any[]).filter((r) => ids.has(r._id as string)); + }, [member, roleMembers, allRoles]); + + const openDM = useMutation(api.dms.openDM); + + if (!isOpen || !member) { + return ( + <BottomSheet + isOpen={isOpen} + onClose={onClose} + disableDefaultHeader + showHandle={false} + className={styles.sheet} + > + <div className={styles.root} /> + </BottomSheet> + ); + } + + const displayName = + profile?.displayName || profile?.username || 'Unknown User'; + const username = profile?.username || ''; + const avatarUrl = profile?.avatarUrl ?? null; + const bio = profile?.aboutMe?.trim(); + const accent = (profile as any)?.accentColor || DEFAULT_ACCENT; + const storedStatus = (profile?.status as string | undefined) || 'offline'; + const presence = mapPresence(resolveStatus(storedStatus, member.userId)); + + const isMe = localUserId === member.userId; + + const handleEditProfile = () => { + window.dispatchEvent(new CustomEvent('brycord:open-user-settings')); + onClose(); + }; + + const handleMessage = async () => { + if (!localUserId) return; + try { + const result = await openDM({ + userId: localUserId as Id<'userProfiles'>, + targetUserId: member.userId as Id<'userProfiles'>, + }); + navigate(`/channels/@me/${result.channelId}`); + onClose(); + } catch (err) { + console.warn('Failed to open DM from sheet:', err); + } + }; + + const handleRotate = async () => { + if (!onRotateKey || rotating) return; + setRotating(true); + setRotateStatus(null); + try { + await onRotateKey(); + setRotateStatus({ + type: 'success', + message: 'Encryption key rotated.', + }); + } catch (err: any) { + setRotateStatus({ + type: 'error', + message: err?.message ?? 'Rotation failed.', + }); + } finally { + setRotating(false); + } + }; + + return ( + <BottomSheet + isOpen={isOpen} + onClose={onClose} + disableDefaultHeader + showHandle={false} + className={styles.sheet} + > + <div className={styles.root}> + <div + className={styles.banner} + style={{ backgroundColor: accent }} + > + <div className={styles.bannerHandle} /> + </div> + + <div className={styles.headerRow}> + <div className={styles.avatarWrap}> + <Avatar + src={avatarUrl} + fallback={displayName} + size={88} + status={presence} + /> + </div> + <button + type="button" + className={styles.moreButton} + aria-label="More actions" + > + <DotsThree size={22} weight="bold" /> + </button> + </div> + + <div className={styles.nameBlock}> + <span className={styles.displayName}>{displayName}</span> + {username && <span className={styles.handle}>@{username}</span>} + </div> + + {isMe ? ( + <button + type="button" + className={styles.editProfileButton} + onClick={handleEditProfile} + > + <PencilSimple size={18} weight="bold" /> + Edit Profile + </button> + ) : ( + <button + type="button" + className={styles.editProfileButton} + onClick={handleMessage} + > + <ChatCircleDots size={18} weight="bold" /> + Message + </button> + )} + + <div className={styles.cardStack}> + {bio && ( + <div className={styles.card}> + <div className={styles.cardTitleRow}> + <span className={styles.cardTitle}>About Me</span> + </div> + <div + style={{ + fontSize: 13, + color: 'var(--text-secondary)', + whiteSpace: 'pre-wrap', + lineHeight: 1.45, + }} + > + {bio} + </div> + </div> + )} + + {assignedRoles.length > 0 && ( + <div className={styles.card}> + <div className={styles.cardTitleRow}> + <span className={styles.cardTitle}>Roles</span> + </div> + <div className={styles.rolesList}> + {assignedRoles.map((role) => ( + <div key={role._id as string} className={styles.rolePill}> + <span + className={styles.roleDot} + style={{ + backgroundColor: + role.color || 'var(--text-primary-muted)', + }} + /> + <span + className={styles.roleName} + style={role.color ? { color: role.color } : undefined} + > + {role.name} + </span> + </div> + ))} + </div> + </div> + )} + + {!isMe && onRotateKey && ( + <div className={styles.card}> + <div className={styles.cardTitleRow}> + <span className={styles.cardTitle}>Encryption</span> + </div> + <div + style={{ + fontSize: 12, + color: 'var(--text-secondary)', + marginBottom: 10, + }} + > + Replace the key used to encrypt this DM. Existing messages + stay readable; new messages use the new key. + </div> + <button + type="button" + className={styles.editProfileButton} + onClick={handleRotate} + disabled={rotating} + > + <Key size={18} weight="bold" /> + {rotating ? 'Rotating…' : 'Rotate Encryption Key'} + </button> + {rotateStatus && ( + <div + style={{ + marginTop: 8, + fontSize: 12, + color: + rotateStatus.type === 'success' + ? '#3ba55d' + : 'var(--status-danger, #ed4245)', + }} + > + {rotateStatus.message} + </div> + )} + </div> + )} + </div> + </div> + </BottomSheet> + ); +} diff --git a/packages/shared/src/components/member/NicknameModal.module.css b/packages/shared/src/components/member/NicknameModal.module.css new file mode 100644 index 0000000..3029ece --- /dev/null +++ b/packages/shared/src/components/member/NicknameModal.module.css @@ -0,0 +1,73 @@ +.body { + display: flex; + flex-direction: column; + gap: 16px; + padding: 16px 20px 4px; +} + +.description { + font-size: 14px; + line-height: 1.45; + color: var(--text-secondary, #a0a3a8); +} + +.description strong { + color: var(--text-primary, #fff); + font-weight: 600; +} + +.label { + display: flex; + flex-direction: column; + gap: 6px; + position: relative; +} + +.labelText { + font-size: 11px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--text-primary-muted, #a0a3a8); +} + +.input { + width: 100%; + padding: 10px 60px 10px 12px; + background-color: var(--background-tertiary, rgba(0, 0, 0, 0.3)); + border: 1px solid transparent; + border-radius: 6px; + color: var(--text-primary, #fff); + font-size: 14px; + font-family: inherit; + outline: none; + box-sizing: border-box; + transition: border-color 0.1s; +} + +.input:focus { + border-color: var(--brand-primary, #5865f2); +} + +.counter { + position: absolute; + right: 10px; + bottom: 10px; + font-size: 11px; + font-weight: 500; + color: var(--text-primary-muted, #a0a3a8); + pointer-events: none; +} + +.counterBad { + color: var(--status-danger, #da373c); +} + +.error { + padding: 8px 12px; + background-color: rgba(234, 80, 80, 0.15); + border: 1px solid rgba(234, 80, 80, 0.4); + border-radius: 6px; + color: var(--text-primary, #fff); + font-size: 13px; +} diff --git a/packages/shared/src/components/member/NicknameModal.tsx b/packages/shared/src/components/member/NicknameModal.tsx new file mode 100644 index 0000000..fc3a6e6 --- /dev/null +++ b/packages/shared/src/components/member/NicknameModal.tsx @@ -0,0 +1,150 @@ +/** + * NicknameModal — lets the user set or clear a room-scoped nickname + * for a member. Writes a new `m.room.member` state event via + * `MemberManager.setRoomNickname`, which preserves the existing + * membership and avatar and overwrites the displayname. + * + * The nickname is scoped to the Matrix room (i.e. the Brycord + * channel), which in Brycord's model means "the server this channel + * lives in as far as the member panel is concerned". The displayname + * field is the exact field matrix-js-sdk's RoomMember.name reads + * from, so every observer of the member list picks up the change as + * soon as the state event syncs. + */ +import { useEffect, useState } from 'react'; +import { Modal, Button } from '@brycord/ui'; +import { MemberManager } from '@brycord/matrix-client'; +import type { Member } from '@brycord/matrix-client'; +import styles from './NicknameModal.module.css'; + +const MAX_NICKNAME = 80; + +interface NicknameModalProps { + isOpen: boolean; + onClose: () => void; + member: Member | null; + channelId: string; + /** The parent space — when provided the nickname is set server-wide. */ + serverId?: string; +} + +export function NicknameModal({ isOpen, onClose, member, channelId, serverId }: NicknameModalProps) { + const [value, setValue] = useState(''); + const [saving, setSaving] = useState(false); + const [error, setError] = useState<string | null>(null); + + useEffect(() => { + if (!isOpen || !member) return; + // Seed with the current room nickname (if any); blank it out + // when there's no override so the placeholder hints at the + // user's global display name. + setValue(member.nickname || ''); + setError(null); + setSaving(false); + }, [isOpen, member]); + + if (!member) return null; + + const trimmed = value.trim(); + const original = (member.nickname || '').trim(); + const isDirty = trimmed !== original; + const tooLong = trimmed.length > MAX_NICKNAME; + const canSave = !saving && isDirty && !tooLong; + + const setNickname = async (nick: string) => { + const mgr = MemberManager.getInstance(); + if (serverId) { + await mgr.setServerNickname(serverId, member.user.id, nick); + } else { + await mgr.setRoomNickname(channelId, member.user.id, nick); + } + }; + + const handleSave = async () => { + if (!canSave) return; + setSaving(true); + setError(null); + try { + await setNickname(trimmed); + onClose(); + } catch (err: any) { + const code = err?.errcode || err?.data?.errcode; + if (code === 'M_FORBIDDEN') { + setError("You don't have permission to rename this user."); + } else { + setError(err?.message || 'Failed to set nickname.'); + } + } finally { + setSaving(false); + } + }; + + const handleClear = async () => { + setSaving(true); + setError(null); + try { + await setNickname(''); + onClose(); + } catch (err: any) { + const code = err?.errcode || err?.data?.errcode; + if (code === 'M_FORBIDDEN') { + setError("You don't have permission to rename this user."); + } else { + setError(err?.message || 'Failed to clear nickname.'); + } + } finally { + setSaving(false); + } + }; + + const globalName = member.user.displayName || member.user.username; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="small"> + <Modal.Header title="Change Nickname" onClose={onClose} /> + <Modal.Content> + <div className={styles.body}> + <div className={styles.description}> + Set a nickname for <strong>{globalName}</strong> in this server. + Leave blank to restore their normal name. + </div> + <label className={styles.label}> + <span className={styles.labelText}>Nickname</span> + <input + type="text" + className={styles.input} + value={value} + onChange={(e) => setValue(e.target.value)} + placeholder={globalName} + maxLength={MAX_NICKNAME + 10} + autoFocus + onKeyDown={(e) => { + if (e.key === 'Enter' && canSave) { + e.preventDefault(); + handleSave(); + } + }} + /> + <span className={`${styles.counter} ${tooLong ? styles.counterBad : ''}`}> + {trimmed.length}/{MAX_NICKNAME} + </span> + </label> + {error && <div className={styles.error}>{error}</div>} + </div> + </Modal.Content> + <Modal.Footer> + {original && ( + <Button variant="ghost" onClick={handleClear} disabled={saving}> + Reset Nickname + </Button> + )} + <Button variant="secondary" onClick={onClose} disabled={saving}> + Cancel + </Button> + <Button variant="primary" onClick={handleSave} disabled={!canSave}> + {saving ? 'Saving…' : 'Save'} + </Button> + </Modal.Footer> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/member/index.ts b/packages/shared/src/components/member/index.ts new file mode 100644 index 0000000..ae1962d --- /dev/null +++ b/packages/shared/src/components/member/index.ts @@ -0,0 +1 @@ +export { MemberListContainer } from './MemberListContainer'; diff --git a/packages/shared/src/components/modals/CreateServerModal.module.css b/packages/shared/src/components/modals/CreateServerModal.module.css new file mode 100644 index 0000000..38245b3 --- /dev/null +++ b/packages/shared/src/components/modals/CreateServerModal.module.css @@ -0,0 +1,122 @@ +.content { + display: flex; + flex-direction: column; + gap: 16px; +} + +.description { + font-size: 0.9375rem; + color: var(--text-secondary); + margin: 0; + text-align: center; +} + +/* ── Icon upload ─────────────────────────────────────────── */ + +.iconSection { + display: flex; + align-items: center; + gap: 16px; +} + +.iconUpload { + width: 80px; + height: 80px; + min-width: 80px; + border-radius: 50%; + border: 2px dashed var(--text-tertiary); + background: var(--background-tertiary); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + padding: 0; + transition: border-color 0.15s, background-color 0.15s; +} + +.iconUpload:hover { + border-color: var(--text-secondary); + background-color: color-mix(in srgb, var(--background-tertiary) 80%, var(--text-primary) 5%); +} + +.iconImage { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 50%; +} + +.iconPlaceholder { + color: var(--text-tertiary); +} + +.iconHint { + display: flex; + flex-direction: column; + gap: 4px; +} + +.iconHintTitle { + font-size: 0.875rem; + font-weight: 600; + color: var(--text-primary); +} + +.iconHintDetail { + font-size: 0.75rem; + color: var(--text-tertiary); + line-height: 1.25; +} + +.hiddenInput { + display: none; +} + +/* ── Form field ──────────────────────────────────────────── */ + +.field { + display: flex; + flex-direction: column; + gap: 8px; +} + +.label { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + color: var(--text-secondary); + letter-spacing: 0.02em; +} + +.input { + height: 40px; + padding: 0 12px; + border-radius: var(--radius-md); + border: none; + background-color: var(--background-tertiary); + color: var(--text-primary); + font-size: 1rem; + outline: none; + font-family: inherit; +} + +.input:focus { + outline: 2px solid var(--brand-primary); + outline-offset: -2px; +} + +/* ── Error ───────────────────────────────────────────────── */ + +.error { + color: var(--status-danger); + font-size: 0.875rem; +} + +/* ── Actions ─────────────────────────────────────────────── */ + +.actions { + display: flex; + justify-content: space-between; + margin-top: 8px; +} diff --git a/packages/shared/src/components/modals/CreateServerModal.tsx b/packages/shared/src/components/modals/CreateServerModal.tsx new file mode 100644 index 0000000..14e7fb8 --- /dev/null +++ b/packages/shared/src/components/modals/CreateServerModal.tsx @@ -0,0 +1,178 @@ +import { AvatarCropModal } from '@app/components/settings/AvatarCropModal'; +import SelectionStore from '@app/stores/SelectionStore'; +import { SpaceManager } from '@brycord/matrix-client'; +import { Button, Modal } from '@brycord/ui'; +import { Camera } from '@phosphor-icons/react'; +import { useRef, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import styles from './CreateServerModal.module.css'; + +const ICON_MAX_SIZE = 10 * 1024 * 1024; + +interface CreateServerModalProps { + isOpen: boolean; + onClose: () => void; +} + +export function CreateServerModal({ isOpen, onClose }: CreateServerModalProps) { + const navigate = useNavigate(); + const fileInputRef = useRef<HTMLInputElement>(null); + + const [name, setName] = useState(''); + const [isCreating, setIsCreating] = useState(false); + const [error, setError] = useState<string | null>(null); + + const [iconPreview, setIconPreview] = useState<string | null>(null); + const [iconFile, setIconFile] = useState<File | null>(null); + const [cropCandidate, setCropCandidate] = useState<File | null>(null); + + const resetState = () => { + setName(''); + setError(null); + setIsCreating(false); + if (iconPreview) URL.revokeObjectURL(iconPreview); + setIconPreview(null); + setIconFile(null); + setCropCandidate(null); + }; + + const handleClose = () => { + resetState(); + onClose(); + }; + + const handleIconClick = () => { + fileInputRef.current?.click(); + }; + + const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => { + const file = e.target.files?.[0]; + e.target.value = ''; + if (!file) return; + if (!file.type.startsWith('image/')) { + setError('Please select an image file.'); + return; + } + if (file.size > ICON_MAX_SIZE) { + setError('Image must be under 10 MB.'); + return; + } + setError(null); + setCropCandidate(file); + }; + + const handleCropped = (blob: Blob) => { + const croppedFile = new File([blob], 'icon.png', { + type: 'image/png', + lastModified: Date.now(), + }); + if (iconPreview) URL.revokeObjectURL(iconPreview); + const url = URL.createObjectURL(blob); + setIconPreview(url); + setIconFile(croppedFile); + }; + + const handleSkipCrop = () => { + const file = cropCandidate; + if (!file) return; + if (iconPreview) URL.revokeObjectURL(iconPreview); + const url = URL.createObjectURL(file); + setIconPreview(url); + setIconFile(file); + }; + + const handleCreate = async () => { + const trimmed = name.trim(); + if (!trimmed) return; + setIsCreating(true); + setError(null); + + try { + const { spaceId, generalChannelId } = await SpaceManager.getInstance().createServer( + trimmed, + iconFile ?? undefined, + ); + resetState(); + onClose(); + SelectionStore.selectServer(spaceId); + SelectionStore.selectChannel(generalChannelId); + navigate(`/channels/${spaceId}/${generalChannelId}`); + } catch (err: any) { + setError(err?.message || 'Failed to create server'); + } finally { + setIsCreating(false); + } + }; + + return ( + <> + <Modal isOpen={isOpen} onClose={handleClose} title="Create a Community" width={440}> + <div className={styles.content}> + <p className={styles.description}>Create a community for you and your friends to chat.</p> + + <div className={styles.iconSection}> + <button + type="button" + className={styles.iconUpload} + onClick={handleIconClick} + aria-label="Upload community icon" + > + {iconPreview ? ( + <img src={iconPreview} alt="Icon preview" className={styles.iconImage} /> + ) : ( + <Camera size={32} weight="regular" className={styles.iconPlaceholder} /> + )} + </button> + <input + ref={fileInputRef} + type="file" + accept="image/*" + onChange={handleFileSelect} + className={styles.hiddenInput} + /> + <div className={styles.iconHint}> + <span className={styles.iconHintTitle}>Upload Icon</span> + <span className={styles.iconHintDetail}>JPEG, PNG, WebP. Max 10MB. Recommended: 512x512px</span> + </div> + </div> + + <div className={styles.field}> + <label className={styles.label}>COMMUNITY NAME</label> + <input + type="text" + className={styles.input} + value={name} + onChange={(e) => setName(e.target.value)} + placeholder="My Community" + autoFocus + onKeyDown={(e) => { + if (e.key === 'Enter' && name.trim()) { + void handleCreate(); + } + }} + /> + </div> + + {error && <div className={styles.error}>{error}</div>} + + <div className={styles.actions}> + <Button variant="ghost" onClick={handleClose}> + Cancel + </Button> + <Button variant="primary" onClick={handleCreate} loading={isCreating} disabled={!name.trim()}> + Create Community + </Button> + </div> + </div> + </Modal> + + <AvatarCropModal + isOpen={cropCandidate !== null} + onClose={() => setCropCandidate(null)} + file={cropCandidate} + onCropped={handleCropped} + onSkipCrop={handleSkipCrop} + /> + </> + ); +} diff --git a/packages/shared/src/components/modals/InviteModal.module.css b/packages/shared/src/components/modals/InviteModal.module.css new file mode 100644 index 0000000..bc4f898 --- /dev/null +++ b/packages/shared/src/components/modals/InviteModal.module.css @@ -0,0 +1,45 @@ +.content { + display: flex; + flex-direction: column; + gap: 16px; +} + +.field { + display: flex; + flex-direction: column; + gap: 8px; +} + +.label { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + color: var(--text-secondary); + letter-spacing: 0.02em; +} + +.input { + height: 40px; + padding: 0 12px; + border-radius: var(--radius-md); + border: none; + background-color: var(--background-tertiary); + color: var(--text-primary); + font-size: 1rem; + outline: none; +} + +.input:focus { + outline: 2px solid var(--brand-primary); + outline-offset: -2px; +} + +.error { + color: var(--status-danger); + font-size: 0.875rem; +} + +.success { + color: var(--status-success); + font-size: 0.875rem; +} diff --git a/packages/shared/src/components/modals/InviteModal.tsx b/packages/shared/src/components/modals/InviteModal.tsx new file mode 100644 index 0000000..8186694 --- /dev/null +++ b/packages/shared/src/components/modals/InviteModal.tsx @@ -0,0 +1,267 @@ +import { useCallback, useEffect, useState } from 'react'; +import { useMutation, useQuery } from 'convex/react'; +import { ArrowClockwise, Copy } from '@phosphor-icons/react'; +import { Modal, Button } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import { usePlatform } from '../../platform'; +import styles from './InviteModal.module.css'; + +interface InviteModalProps { + isOpen: boolean; + onClose: () => void; +} + +/** + * InviteModal — generates a real E2E-encrypted invite. + * + * Flow: + * 1. Decrypt the creator's own `channelKeys` bundles with their + * RSA private key (held in sessionStorage after login), merging + * everything into a single `{channelId: keyHex}` map. + * 2. Generate a random `inviteSecret` (32 bytes hex) that will act + * as the AES key for the invite payload. This secret lives in + * the invite URL fragment — never sent to the server. + * 3. Encrypt the key map with `inviteSecret` and store the + * resulting blob on the server via `api.invites.create`. + * 4. Build the invite URL as `${origin}/invite/${code}#key=${secret}` + * so the fragment carries the secret client-side only. + * + * The acceptor opens the URL, hits `/invite/:code`, which pulls the + * blob from the server, decrypts with the fragment secret, stashes + * the decoded keys in sessionStorage, then hands off to RegisterPage. + */ +export function InviteModal({ isOpen, onClose }: InviteModalProps) { + const { crypto } = usePlatform(); + const createInvite = useMutation(api.invites.create); + const serverSettings = useQuery(api.serverSettings.get); + const serverName = serverSettings?.serverName ?? 'this server'; + + const userId = + typeof localStorage !== 'undefined' + ? localStorage.getItem('userId') + : null; + const myKeyBundles = useQuery( + api.channelKeys.getKeysForUser, + userId ? { userId: userId as any } : 'skip', + ); + // The invite should only share keys for server channels (text/voice). + // DMs are 1:1 and leaking their keys would let the invitee decrypt + // the inviter's private conversations with everyone else. `channels.list` + // already filters by `type !== 'dm'`, so use it as the allowlist. + const serverChannels = useQuery(api.channels.list); + + const [code, setCode] = useState<string | null>(null); + const [isGenerating, setIsGenerating] = useState(false); + const [copied, setCopied] = useState(false); + const [inviteUrl, setInviteUrl] = useState<string>(''); + const [error, setError] = useState<string | null>(null); + + const origin = + typeof window !== 'undefined' ? window.location.origin : ''; + + const generate = useCallback(async () => { + if (!userId) { + setError('You must be signed in to create an invite.'); + return; + } + if (!myKeyBundles || !serverChannels) { + // Key bundles / channel list haven't loaded yet — surface a + // soft error; the auto-generate effect fires again once both + // queries resolve. + setError('Loading your channel keys…'); + return; + } + + const privateKey = + typeof sessionStorage !== 'undefined' + ? sessionStorage.getItem('privateKey') + : null; + if (!privateKey) { + setError( + 'No decryption key available. Log out and back in to restore your session.', + ); + return; + } + + setIsGenerating(true); + setError(null); + setCopied(false); + + try { + // Build an allowlist of channel ids that belong to the server + // (non-DM). Everything else — especially DM bundles — gets + // dropped on the floor so invitees never see private DM keys. + const allowedChannelIds = new Set<string>( + serverChannels.map((c: any) => c._id as string), + ); + + // Decrypt every bundle we have for allowed channels and merge + // into a single {channelId: keyHex} map. Bundles we can't + // decrypt (old key versions, corrupt rows) are silently + // skipped — they just won't be shared with the invitee. + const mergedKeys: Record<string, string> = {}; + for (const bundle of myKeyBundles) { + const bundleChannelId = bundle.channel_id as unknown as string; + if (!allowedChannelIds.has(bundleChannelId)) continue; + try { + const plaintext = await crypto.privateDecrypt( + privateKey, + bundle.encrypted_key_bundle, + ); + const parsed = JSON.parse(plaintext) as Record<string, string>; + for (const [chId, keyHex] of Object.entries(parsed)) { + // Double-check: an old bundle could theoretically + // contain more than its owning row's channelId. + // Only carry forward the ones on the allowlist. + if (allowedChannelIds.has(chId)) { + mergedKeys[chId] = keyHex; + } + } + } catch { + /* skip unreadable bundle */ + } + } + + const inviteCode = Array.from({ length: 8 }, () => + Math.floor(Math.random() * 36).toString(36), + ).join(''); + const inviteSecret = await crypto.randomBytes(32); + + const payload = JSON.stringify(mergedKeys); + const encrypted = await crypto.encryptData(payload, inviteSecret); + const blob = JSON.stringify({ + c: encrypted.content, + i: encrypted.iv, + t: encrypted.tag, + }); + + await createInvite({ + code: inviteCode, + encryptedPayload: blob, + createdBy: userId as any, + keyVersion: 1, + expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000, + }); + + // Fragment URL — the secret never hits the server. The + // acceptor's client reads it from `window.location.hash`. + setInviteUrl(`${origin}/invite/${inviteCode}#key=${inviteSecret}`); + setCode(inviteCode); + } catch (err: any) { + setError(err?.message ?? 'Unknown error'); + setCode(null); + setInviteUrl(''); + } finally { + setIsGenerating(false); + } + }, [createInvite, crypto, myKeyBundles, origin, userId]); + + useEffect(() => { + if (!isOpen) return; + setCopied(false); + if ( + !code && + !isGenerating && + myKeyBundles !== undefined && + serverChannels !== undefined + ) { + void generate(); + } + }, [isOpen, code, isGenerating, myKeyBundles, serverChannels, generate]); + + const handleCopy = async () => { + if (!inviteUrl) return; + try { + await navigator.clipboard.writeText(inviteUrl); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + } catch { + /* clipboard blocked */ + } + }; + + const handleRegenerate = () => { + setCode(null); + setInviteUrl(''); + void generate(); + }; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="small"> + <Modal.Header title={`Invite People to ${serverName}`} onClose={onClose} /> + <Modal.Content> + <div className={styles.content}> + <p style={{ color: 'var(--text-secondary)', fontSize: 14, margin: 0 }}> + Send an invite link to your friends. The link carries encrypted + channel keys so the invitee can read existing messages. + </p> + + {error ? ( + <div className={styles.error}> + Unable to generate invite: {error} + </div> + ) : ( + <> + <div className={styles.field}> + <label className={styles.label}>SERVER INVITE LINK</label> + <div style={{ display: 'flex', gap: 8 }}> + <input + type="text" + className={styles.input} + value={ + isGenerating || !inviteUrl ? 'Generating…' : inviteUrl + } + readOnly + onClick={(e) => e.currentTarget.select()} + style={{ flex: 1 }} + /> + <Button + variant="primary" + size="sm" + icon={<Copy size={16} />} + onClick={handleCopy} + disabled={!inviteUrl || isGenerating} + > + {copied ? 'Copied!' : 'Copy'} + </Button> + </div> + </div> + + <div + style={{ + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + gap: 12, + }} + > + <span + style={{ + fontSize: 12, + color: 'var(--text-tertiary)', + }} + > + Your invite link expires in 7 days. + </span> + <Button + variant="ghost" + size="sm" + icon={<ArrowClockwise size={14} />} + onClick={handleRegenerate} + disabled={isGenerating} + > + Generate new link + </Button> + </div> + </> + )} + </div> + </Modal.Content> + <Modal.Footer> + <Button variant="primary" size="sm" onClick={onClose}> + Done + </Button> + </Modal.Footer> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/modals/LogoutConfirmModal.module.css b/packages/shared/src/components/modals/LogoutConfirmModal.module.css new file mode 100644 index 0000000..e9d8da7 --- /dev/null +++ b/packages/shared/src/components/modals/LogoutConfirmModal.module.css @@ -0,0 +1,52 @@ +.card { + position: relative; + padding: 22px 24px 20px; + display: flex; + flex-direction: column; + gap: 12px; +} + +.closeButton { + position: absolute; + top: 14px; + right: 14px; + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + border: none; + border-radius: 6px; + background: transparent; + color: var(--text-tertiary); + cursor: pointer; + transition: background-color 0.12s, color 0.12s; +} + +.closeButton:hover { + background: var(--background-modifier-hover); + color: var(--text-primary); +} + +.title { + margin: 0; + padding-right: 32px; + font-size: 1.125rem; + font-weight: 700; + color: var(--text-primary); + letter-spacing: -0.01em; +} + +.body { + margin: 0; + font-size: 0.9375rem; + color: var(--text-secondary); + line-height: 1.4; +} + +.actions { + display: flex; + justify-content: flex-end; + gap: 10px; + margin-top: 6px; +} diff --git a/packages/shared/src/components/modals/LogoutConfirmModal.tsx b/packages/shared/src/components/modals/LogoutConfirmModal.tsx new file mode 100644 index 0000000..f7328a9 --- /dev/null +++ b/packages/shared/src/components/modals/LogoutConfirmModal.tsx @@ -0,0 +1,46 @@ +import { X } from '@phosphor-icons/react'; +import { Button, Modal } from '@discord-clone/ui'; +import styles from './LogoutConfirmModal.module.css'; + +interface LogoutConfirmModalProps { + isOpen: boolean; + onClose: () => void; + onConfirm: () => void; +} + +/** + * Friendly confirm dialog shown before we actually wipe the user's + * session. Uses the shared Modal.Root so the animation + backdrop + * match the rest of the app; the body / buttons are custom to hit + * the playful copy from the new UI ("Leaving so soon?" / "Hit 88mph"). + */ +export function LogoutConfirmModal({ + isOpen, + onClose, + onConfirm, +}: LogoutConfirmModalProps) { + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="small"> + <div className={styles.card}> + <button + type="button" + className={styles.closeButton} + onClick={onClose} + aria-label="Close" + > + <X size={18} weight="bold" /> + </button> + <h2 className={styles.title}>Leaving so soon?</h2> + <p className={styles.body}>Hope to see you back in the future</p> + <div className={styles.actions}> + <Button variant="secondary" size="md" onClick={onClose}> + I changed my mind + </Button> + <Button variant="primary" size="md" onClick={onConfirm}> + Hit 88mph + </Button> + </div> + </div> + </Modal.Root> + ); +} diff --git a/packages/shared/src/components/modals/index.ts b/packages/shared/src/components/modals/index.ts new file mode 100644 index 0000000..f70d605 --- /dev/null +++ b/packages/shared/src/components/modals/index.ts @@ -0,0 +1,2 @@ +export { CreateServerModal } from './CreateServerModal'; +export { InviteModal } from './InviteModal'; diff --git a/packages/shared/src/components/settings/AvatarCropModal.module.css b/packages/shared/src/components/settings/AvatarCropModal.module.css new file mode 100644 index 0000000..8c43825 --- /dev/null +++ b/packages/shared/src/components/settings/AvatarCropModal.module.css @@ -0,0 +1,268 @@ +/* ── Crop Avatar modal body ────────────────────────────────── + Chrome (backdrop, centring, header) comes from Modal.Root. + This module styles the editor viewport, the zoom row, and + the footer actions. */ + +.body { + display: flex; + flex-direction: column; + gap: 16px; + padding: 0; +} + +.description { + margin: 0; + font-size: 0.875rem; + line-height: 1.4; + color: var(--text-secondary); +} + +/* ── Editor viewport ───────────────────────────────────────── + Fixed-size square container. The image is absolutely + positioned inside and transformed via JS state (pan / zoom / + rotate). The circular crop mask is a sibling element that + casts a huge outer box-shadow to dim everything outside the + ring. */ + +.editor { + position: relative; + align-self: center; + background-color: #000; + border-radius: 8px; + overflow: hidden; + cursor: grab; + touch-action: none; + user-select: none; + -webkit-user-select: none; +} + +.editor:active { + cursor: grabbing; +} + +.editorImage { + position: absolute; + left: 50%; + top: 50%; + transform-origin: center center; + pointer-events: none; + max-width: none; + max-height: none; + /* Prevent the browser from smoothing natural-size pixels + into a blur when the user zooms hard — on a 512px output + we'd rather have slightly crunchy pixels than mush. */ + image-rendering: auto; +} + +.cropMask { + position: absolute; + top: 50%; + left: 50%; + width: 100%; + height: 100%; + transform: translate(-50%, -50%); + border-radius: 50%; + border: 2px solid rgba(255, 255, 255, 0.85); + box-sizing: border-box; + pointer-events: none; + /* A massive outer box-shadow acts as the "outside the + circle is dark" overlay. 9999px is hand-wavy but it only + needs to exceed the max viewport dimension to look clean, + which it always will. */ + box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.6); +} + +/* ── Zoom row ────────────────────────────────────────────── */ + +.zoomRow { + display: flex; + align-items: center; + gap: 12px; +} + +.zoomLabel { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--text-primary-muted, #a0a3a8); + flex-shrink: 0; +} + +.zoomSlider { + flex: 1; + height: 4px; + -webkit-appearance: none; + appearance: none; + background-color: var(--background-modifier-accent); + border-radius: 999px; + cursor: pointer; + outline: none; +} + +.zoomSlider:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.zoomSlider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--brand-primary); + cursor: pointer; + border: none; +} + +.zoomSlider::-moz-range-thumb { + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--brand-primary); + cursor: pointer; + border: none; +} + +.iconButton { + display: inline-flex; + align-items: center; + justify-content: center; + width: 34px; + height: 34px; + padding: 0; + background: transparent; + border: none; + border-radius: 6px; + color: var(--text-primary-muted, #a0a3a8); + cursor: pointer; + transition: background-color 0.12s, color 0.12s; +} + +.iconButton:hover:not(:disabled) { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +.iconButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* ── Inline error row ────────────────────────────────────── */ + +.error { + padding: 10px 14px; + background-color: hsl(0, calc(60% * var(--saturation-factor, 1)), 22%); + color: hsl(0, calc(80% * var(--saturation-factor, 1)), 85%); + border-radius: 6px; + font-size: 0.8125rem; +} + +/* ── Footer action row ──────────────────────────────────── + Reset sits hard-left, a flex spacer pushes Cancel / Skip / + Save to the right end. Matches the Fluxer reference image + exactly. */ + +.actions { + display: flex; + align-items: center; + gap: 8px; + margin-top: 4px; +} + +.actionsSpacer { + flex: 1; +} + +.resetButton { + height: 40px; + padding: 0 18px; + background: var(--background-secondary-alt); + border: none; + border-radius: 6px; + color: var(--text-primary); + font: inherit; + font-size: 0.875rem; + font-weight: 700; + cursor: pointer; + transition: background-color 0.12s; +} + +.resetButton:hover:not(:disabled) { + background-color: var(--background-modifier-hover); +} + +.resetButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.secondaryButton { + height: 40px; + padding: 0 18px; + background: transparent; + border: none; + border-radius: 6px; + color: var(--text-primary); + font: inherit; + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + transition: background-color 0.12s; +} + +.secondaryButton:hover:not(:disabled) { + background-color: var(--background-modifier-hover); +} + +.tertiaryButton { + height: 40px; + padding: 0 18px; + background: var(--background-secondary-alt); + border: none; + border-radius: 6px; + color: var(--text-primary); + font: inherit; + font-size: 0.875rem; + font-weight: 700; + cursor: pointer; + transition: background-color 0.12s; +} + +.tertiaryButton:hover:not(:disabled) { + background-color: var(--background-modifier-hover); +} + +.primaryButton { + height: 40px; + padding: 0 20px; + background-color: var(--brand-primary); + border: none; + border-radius: 6px; + color: var(--text-on-brand-primary, #fff); + font: inherit; + font-size: 0.875rem; + font-weight: 700; + cursor: pointer; + transition: filter 0.12s; +} + +.primaryButton:hover:not(:disabled) { + filter: brightness(1.08); +} + +.primaryButton:active:not(:disabled) { + filter: brightness(0.92); +} + +.primaryButton:disabled { + opacity: 0.55; + cursor: not-allowed; +} + +.secondaryButton:disabled, +.tertiaryButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} diff --git a/packages/shared/src/components/settings/AvatarCropModal.tsx b/packages/shared/src/components/settings/AvatarCropModal.tsx new file mode 100644 index 0000000..7aa3e84 --- /dev/null +++ b/packages/shared/src/components/settings/AvatarCropModal.tsx @@ -0,0 +1,468 @@ +/** + * AvatarCropModal — opened after the user picks an avatar file in + * the profile settings. Lets them drag to reposition + zoom the + * image inside a circular crop frame before we upload the result. + * + * Output is a square PNG (default 512×512) of the visible crop + * region. We output PNG instead of re-encoding the source format + * so transparent avatars don't get a white background and so the + * alpha channel (transparent PNG, GIF frame 0) round-trips. + * + * The modal is built on the shared `Modal` primitive, so backdrop + * chrome + Esc handling + portal routing come for free. Drag and + * zoom are handled with pointer events (no external dependency) + * so we can ship this inside the existing Vite bundle without + * adding react-easy-crop or react-image-crop to the dep tree. + */ +import { + useCallback, + useEffect, + useMemo, + useRef, + useState, + type PointerEvent as ReactPointerEvent, + type WheelEvent as ReactWheelEvent, +} from 'react'; +import { Modal } from '@discord-clone/ui'; +import { + ArrowClockwise, + FrameCorners, +} from '@phosphor-icons/react'; +import styles from './AvatarCropModal.module.css'; + +const EDITOR_SIZE = 360; // CSS pixels of the square editor viewport +const OUTPUT_SIZE = 512; // PNG pixels of the exported crop +const ZOOM_MIN = 1; +const ZOOM_MAX = 4; +const ZOOM_STEP = 0.05; + +interface AvatarCropModalProps { + isOpen: boolean; + onClose: () => void; + /** The raw File the user picked — gets decoded into an Image + * and rendered inside the editor. */ + file: File | null; + /** Called with the cropped PNG Blob when the user hits Save. + * The caller should treat it as a fresh upload candidate + * (same path as picking a brand new file). */ + onCropped: (blob: Blob) => void; + /** Called when the user clicks "Skip Cropping" — the raw file + * is accepted as-is without any client-side processing. Used + * for animated GIFs where cropping would flatten to a single + * frame and the user would rather upload the original. */ + onSkipCrop: () => void; +} + +interface Offset { + x: number; + y: number; +} + +export function AvatarCropModal({ + isOpen, + onClose, + file, + onCropped, + onSkipCrop, +}: AvatarCropModalProps) { + const [imageUrl, setImageUrl] = useState<string | null>(null); + const [imageEl, setImageEl] = useState<HTMLImageElement | null>(null); + // Crop transform state — `offset` is the image centre's + // position inside the editor viewport in CSS pixels, `zoom` is + // a multiplier on top of the "cover" scale computed once per + // image. Keeping them separate lets the zoom slider and drag + // pan compose cleanly. + const [offset, setOffset] = useState<Offset>({ x: 0, y: 0 }); + const [zoom, setZoom] = useState(1); + const [rotation, setRotation] = useState(0); + const [isSaving, setIsSaving] = useState(false); + const [error, setError] = useState<string | null>(null); + + const editorRef = useRef<HTMLDivElement>(null); + // Pointer drag bookkeeping — we track the start pointer + the + // offset at the moment the drag began, and compute the live + // offset as (current - start + startOffset). Kept in a ref so + // mid-drag updates don't re-render 60 times a second. + const dragRef = useRef<{ + pointerId: number; + startX: number; + startY: number; + startOffset: Offset; + } | null>(null); + + // ── Source image decoding ───────────────────────────────── + + // Decode the picked File into an <img> element so we know its + // natural dimensions before laying out the cover scale. + useEffect(() => { + if (!isOpen || !file) { + setImageUrl(null); + setImageEl(null); + return; + } + const url = URL.createObjectURL(file); + setImageUrl(url); + const img = new Image(); + img.onload = () => setImageEl(img); + img.onerror = () => setError('Could not read that image.'); + img.src = url; + return () => { + URL.revokeObjectURL(url); + }; + }, [isOpen, file]); + + // Reset transform whenever a new file lands. Otherwise the + // user's last crop would persist across picks which is + // confusing — fresh file should open uncropped. + useEffect(() => { + if (!imageEl) return; + setOffset({ x: 0, y: 0 }); + setZoom(1); + setRotation(0); + setError(null); + }, [imageEl]); + + // ── Cover scale ─────────────────────────────────────────── + // + // The baseline scale that makes the source image exactly cover + // the editor viewport (the `object-fit: cover` equivalent). + // All further zooming is multiplied on top of this so zoom=1 + // always means "source fits edges". + const coverScale = useMemo(() => { + if (!imageEl) return 1; + return Math.max( + EDITOR_SIZE / imageEl.naturalWidth, + EDITOR_SIZE / imageEl.naturalHeight, + ); + }, [imageEl]); + + const effectiveScale = coverScale * zoom; + + const displayWidth = imageEl + ? imageEl.naturalWidth * effectiveScale + : EDITOR_SIZE; + const displayHeight = imageEl + ? imageEl.naturalHeight * effectiveScale + : EDITOR_SIZE; + + // Clamp the drag offset so the image edge can never move + // inside the crop circle's bounding box. Matches the Fluxer + // behaviour where you physically can't pan past "image + // touching the edge of the viewport". + const clampOffset = useCallback( + (raw: Offset): Offset => { + const maxX = Math.max(0, (displayWidth - EDITOR_SIZE) / 2); + const maxY = Math.max(0, (displayHeight - EDITOR_SIZE) / 2); + return { + x: Math.max(-maxX, Math.min(maxX, raw.x)), + y: Math.max(-maxY, Math.min(maxY, raw.y)), + }; + }, + [displayWidth, displayHeight], + ); + + // Re-clamp whenever zoom or image size changes so a zoom-out + // doesn't leave the image stranded off-centre. + useEffect(() => { + setOffset((prev) => clampOffset(prev)); + }, [clampOffset]); + + // ── Pointer drag ────────────────────────────────────────── + + const handlePointerDown = (e: ReactPointerEvent<HTMLDivElement>) => { + if (!imageEl) return; + (e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId); + dragRef.current = { + pointerId: e.pointerId, + startX: e.clientX, + startY: e.clientY, + startOffset: offset, + }; + }; + + const handlePointerMove = (e: ReactPointerEvent<HTMLDivElement>) => { + const drag = dragRef.current; + if (!drag || drag.pointerId !== e.pointerId) return; + const dx = e.clientX - drag.startX; + const dy = e.clientY - drag.startY; + setOffset( + clampOffset({ + x: drag.startOffset.x + dx, + y: drag.startOffset.y + dy, + }), + ); + }; + + const handlePointerUp = (e: ReactPointerEvent<HTMLDivElement>) => { + const drag = dragRef.current; + if (!drag || drag.pointerId !== e.pointerId) return; + try { + (e.currentTarget as HTMLDivElement).releasePointerCapture(e.pointerId); + } catch { + // Already released — non-issue. + } + dragRef.current = null; + }; + + // ── Wheel zoom ──────────────────────────────────────────── + + const handleWheel = (e: ReactWheelEvent<HTMLDivElement>) => { + if (!imageEl) return; + // Negative deltaY = scroll up = zoom in. Convert to the + // same 0.05 steps the slider uses for a consistent feel. + const delta = e.deltaY > 0 ? -ZOOM_STEP : ZOOM_STEP; + setZoom((prev) => Math.max(ZOOM_MIN, Math.min(ZOOM_MAX, prev + delta))); + }; + + // ── Footer actions ──────────────────────────────────────── + + const handleReset = () => { + setOffset({ x: 0, y: 0 }); + setZoom(1); + setRotation(0); + }; + + const handleRotate = () => { + // Clockwise 90° increments. Rotation composes on top of + // the drag/zoom transform at render time. + setRotation((prev) => (prev + 90) % 360); + }; + + const handleFitFrame = () => { + // Snap zoom back to 1 (cover) and recentre. + setZoom(1); + setOffset({ x: 0, y: 0 }); + }; + + const handleSkip = () => { + onSkipCrop(); + onClose(); + }; + + const handleSave = async () => { + if (!imageEl) return; + setIsSaving(true); + setError(null); + try { + const blob = await renderCrop({ + image: imageEl, + editorSize: EDITOR_SIZE, + outputSize: OUTPUT_SIZE, + offset, + scale: effectiveScale, + rotation, + }); + onCropped(blob); + onClose(); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to crop image.'); + setIsSaving(false); + } + }; + + // ── Render ──────────────────────────────────────────────── + + // Compose CSS transform from the three axes. Order matters — + // we translate THEN rotate THEN scale so the pivot is the + // image centre and the user's pan/zoom feel intuitive. + const imageTransform = imageEl + ? `translate(-50%, -50%) translate(${offset.x}px, ${offset.y}px) rotate(${rotation}deg) scale(${effectiveScale})` + : undefined; + + return ( + <Modal.Root isOpen={isOpen} onClose={onClose} size="medium"> + <Modal.Header title="Crop Avatar" onClose={onClose} /> + <Modal.Content> + <div className={styles.body}> + <p className={styles.description}> + Drag to reposition your avatar and use the scroll wheel or + pinch to zoom. The recommended minimum size is 256×256 pixels. + </p> + + <div + ref={editorRef} + className={styles.editor} + style={{ width: EDITOR_SIZE, height: EDITOR_SIZE }} + onPointerDown={handlePointerDown} + onPointerMove={handlePointerMove} + onPointerUp={handlePointerUp} + onPointerCancel={handlePointerUp} + onWheel={handleWheel} + > + {imageUrl && ( + <img + src={imageUrl} + alt="" + className={styles.editorImage} + style={{ + transform: imageTransform, + // The image is rendered at its NATURAL size + // and we scale it with `transform: scale()` + // so the pan math stays in source-pixel + // space no matter the zoom. + width: imageEl?.naturalWidth ?? 0, + height: imageEl?.naturalHeight ?? 0, + }} + draggable={false} + /> + )} + {/* Circular crop mask — absolutely positioned ring + on top of the image with a 9999px outer shadow + that dims everything outside the ring. */} + <div className={styles.cropMask} aria-hidden /> + </div> + + <div className={styles.zoomRow}> + <label className={styles.zoomLabel} htmlFor="avatar-zoom"> + Zoom + </label> + <input + id="avatar-zoom" + type="range" + className={styles.zoomSlider} + min={ZOOM_MIN} + max={ZOOM_MAX} + step={ZOOM_STEP} + value={zoom} + onChange={(e) => setZoom(Number(e.target.value))} + disabled={!imageEl} + /> + <button + type="button" + className={styles.iconButton} + onClick={handleFitFrame} + disabled={!imageEl} + aria-label="Fit to frame" + title="Fit to frame" + > + <FrameCorners size={20} weight="regular" /> + </button> + <button + type="button" + className={styles.iconButton} + onClick={handleRotate} + disabled={!imageEl} + aria-label="Rotate 90°" + title="Rotate 90°" + > + <ArrowClockwise size={20} weight="regular" /> + </button> + </div> + + {error && <div className={styles.error}>{error}</div>} + + <div className={styles.actions}> + <button + type="button" + className={styles.resetButton} + onClick={handleReset} + disabled={!imageEl} + > + Reset + </button> + <div className={styles.actionsSpacer} /> + <button + type="button" + className={styles.secondaryButton} + onClick={onClose} + disabled={isSaving} + > + Cancel + </button> + <button + type="button" + className={styles.tertiaryButton} + onClick={handleSkip} + disabled={isSaving} + title="Upload the original file without cropping" + > + Skip Cropping + </button> + <button + type="button" + className={styles.primaryButton} + onClick={handleSave} + disabled={!imageEl || isSaving} + > + {isSaving ? 'Saving…' : 'Save Avatar'} + </button> + </div> + </div> + </Modal.Content> + </Modal.Root> + ); +} + +// ───────────────────────────────────────────────────────────────── +// Canvas render helper +// ───────────────────────────────────────────────────────────────── + +interface RenderCropArgs { + image: HTMLImageElement; + editorSize: number; + outputSize: number; + offset: Offset; + scale: number; + rotation: number; +} + +/** + * Bakes the current editor transform into a square PNG Blob. The + * math mirrors the DOM transform chain exactly: translate to the + * editor centre, pan by the drag offset, rotate, then draw the + * source image scaled by `scale` centred at (0, 0). + * + * Output is `outputSize × outputSize` pixels regardless of how + * much the user zoomed in — we let the browser's native image + * scaling handle super-sampling from the source bitmap. + */ +function renderCrop(args: RenderCropArgs): Promise<Blob> { + return new Promise((resolve, reject) => { + const { image, editorSize, outputSize, offset, scale, rotation } = args; + const canvas = document.createElement('canvas'); + canvas.width = outputSize; + canvas.height = outputSize; + const ctx = canvas.getContext('2d'); + if (!ctx) { + reject(new Error('Could not get 2D canvas context.')); + return; + } + + // Convert from editor-pixel space (360px) to output-pixel + // space (512px) by uniformly scaling every transform. + const ratio = outputSize / editorSize; + + ctx.save(); + // Move the origin to the centre of the output canvas + // (where the crop circle is centred in the editor). + ctx.translate(outputSize / 2, outputSize / 2); + // Apply the user's pan offset, rescaled to output space. + ctx.translate(offset.x * ratio, offset.y * ratio); + // Rotate around the new origin. + ctx.rotate((rotation * Math.PI) / 180); + // Scale the source image by the effective scale * ratio. + // The image is drawn from its own centre so the origin + // lines up with the editor's crop centre. + const drawScale = scale * ratio; + ctx.drawImage( + image, + (-image.naturalWidth * drawScale) / 2, + (-image.naturalHeight * drawScale) / 2, + image.naturalWidth * drawScale, + image.naturalHeight * drawScale, + ); + ctx.restore(); + + canvas.toBlob( + (blob) => { + if (!blob) { + reject(new Error('Canvas export failed.')); + return; + } + resolve(blob); + }, + 'image/png', + 0.95, + ); + }); +} diff --git a/packages/shared/src/components/settings/CustomEmojisTab.module.css b/packages/shared/src/components/settings/CustomEmojisTab.module.css new file mode 100644 index 0000000..623a37b --- /dev/null +++ b/packages/shared/src/components/settings/CustomEmojisTab.module.css @@ -0,0 +1,323 @@ +.container { + display: flex; + flex-direction: column; + gap: 16px; +} + +/* ── Search row ──────────────────────────────────────────────── */ + +.searchWrap { + position: relative; + width: 100%; +} + +.searchIcon { + position: absolute; + left: 14px; + top: 50%; + transform: translateY(-50%); + color: var(--text-tertiary); + pointer-events: none; +} + +.searchInput { + width: 100%; + height: 44px; + padding: 0 14px 0 40px; + border: 1px solid var(--background-header-secondary); + border-radius: 8px; + background: var(--background-tertiary); + color: var(--text-primary); + font: inherit; + font-size: 0.875rem; + outline: none; + transition: border-color 0.12s; +} + +.searchInput:focus { + border-color: var(--brand-primary); +} + +/* ── Slots card ──────────────────────────────────────────────── */ + +.slotsCard { + display: flex; + flex-direction: column; + gap: 12px; + padding: 18px 20px; + border: 1px solid var(--background-header-secondary); + border-radius: 10px; + background: var(--background-secondary); +} + +.slotsHeader { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; +} + +.slotsHeaderLeft { + display: flex; + flex-direction: column; + gap: 4px; + min-width: 0; +} + +.slotsTitle { + font-size: 1rem; + font-weight: 700; + color: var(--text-primary); + margin: 0; +} + +.slotsCounts { + display: flex; + align-items: center; + gap: 20px; + font-size: 0.8125rem; + font-weight: 500; + color: var(--text-secondary); +} + +.slotsCount strong { + color: var(--text-primary); +} + +.uploadButton { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 10px 16px; + border: none; + border-radius: 6px; + background: var(--brand-primary, #5865f2); + color: #ffffff; + font: inherit; + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + flex-shrink: 0; + transition: filter 0.12s; +} + +.uploadButton:hover:not(:disabled) { + filter: brightness(1.1); +} + +.uploadButton:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.slotsDescription { + font-size: 0.8125rem; + line-height: 1.5; + color: var(--text-tertiary); + margin: 0; +} + +/* ── Drag and drop zone ──────────────────────────────────────── */ + +.dropZone { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 10px; + padding: 40px 20px; + border: 1px dashed var(--background-modifier-accent); + border-radius: 10px; + background: var(--background-secondary); + color: var(--text-tertiary); + font-size: 0.875rem; + font-weight: 500; + cursor: pointer; + transition: background-color 0.12s, border-color 0.12s, color 0.12s; +} + +.dropZone:hover { + background: var(--background-modifier-hover); + color: var(--text-primary); +} + +.dropZoneActive { + border-color: var(--brand-primary, #5865f2); + background: rgba(88, 101, 242, 0.08); + color: var(--text-primary); +} + +.dropZoneIcon { + color: var(--text-tertiary); +} + +.dropZoneActive .dropZoneIcon { + color: var(--brand-primary, #5865f2); +} + +/* ── Emoji sections ──────────────────────────────────────────── */ + +.section { + display: flex; + flex-direction: column; + gap: 8px; +} + +.sectionTitle { + font-size: 0.9375rem; + font-weight: 700; + color: var(--text-primary); + margin: 12px 0 4px; +} + +.tableHeader { + display: grid; + grid-template-columns: 72px 1fr 1fr 40px; + gap: 12px; + padding: 8px 14px; + font-size: 0.6875rem; + font-weight: 700; + color: var(--text-tertiary); + text-transform: uppercase; + letter-spacing: 0.04em; + border-bottom: 1px solid var(--background-modifier-accent); +} + +.tableBody { + display: flex; + flex-direction: column; +} + +.emojiRow { + display: grid; + grid-template-columns: 72px 1fr 1fr 40px; + gap: 12px; + align-items: center; + padding: 10px 14px; + border-bottom: 1px solid var(--background-modifier-accent); + transition: background-color 0.1s; +} + +.emojiRow:last-child { + border-bottom: none; +} + +.emojiRow:hover { + background: var(--background-modifier-hover); +} + +.emojiCell { + display: flex; + align-items: center; +} + +.emojiImage { + width: 40px; + height: 40px; + object-fit: contain; + border-radius: 4px; +} + +.nameCell { + display: flex; + align-items: center; + min-width: 0; +} + +.nameInput { + width: 100%; + padding: 6px 10px; + border: 1px solid transparent; + border-radius: 6px; + background: transparent; + color: var(--text-primary); + font: inherit; + font-size: 0.875rem; + font-family: ui-monospace, Menlo, Consolas, monospace; + outline: none; + transition: background-color 0.12s, border-color 0.12s; +} + +.nameInput:hover, +.nameInput:focus { + background: var(--background-tertiary); + border-color: var(--background-modifier-accent); +} + +.nameInput:focus { + border-color: var(--brand-primary); +} + +.uploaderCell { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; +} + +.uploaderName { + font-size: 0.875rem; + font-weight: 500; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.deleteCell { + display: flex; + align-items: center; + justify-content: flex-end; +} + +.deleteButton { + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + padding: 0; + border: none; + border-radius: 4px; + background: transparent; + color: var(--text-tertiary); + cursor: pointer; + transition: background-color 0.12s, color 0.12s; + opacity: 0; +} + +.emojiRow:hover .deleteButton { + opacity: 1; +} + +.deleteButton:hover { + background: rgba(237, 66, 69, 0.12); + color: var(--status-danger, #ed4245); +} + +/* ── Empty / status ──────────────────────────────────────────── */ + +.emptyState { + padding: 24px 20px; + text-align: center; + font-size: 0.875rem; + color: var(--text-tertiary); +} + +.statusBanner { + padding: 10px 12px; + border-radius: 6px; + font-size: 0.8125rem; +} + +.statusOk { + background: rgba(59, 165, 93, 0.12); + border: 1px solid rgba(59, 165, 93, 0.4); + color: var(--text-primary); +} + +.statusErr { + background: rgba(234, 80, 80, 0.12); + border: 1px solid rgba(234, 80, 80, 0.4); + color: var(--text-primary); +} diff --git a/packages/shared/src/components/settings/CustomEmojisTab.tsx b/packages/shared/src/components/settings/CustomEmojisTab.tsx new file mode 100644 index 0000000..9ccda6c --- /dev/null +++ b/packages/shared/src/components/settings/CustomEmojisTab.tsx @@ -0,0 +1,418 @@ +/** + * CustomEmojisTab — server-settings Custom Emoji surface, ported + * from the Fluxer GuildEmojiTab design. Shows the emoji slot card + * (Static/Animated counts + Upload button + help copy), a drag-and- + * drop zone, and a per-group table (Non-Animated / Animated) of the + * server's current uploads with rename-in-place + delete-on-hover. + * + * Backend: `api.customEmojis.list / upload / rename / remove`. The + * component infers the `animated` flag from the uploaded file's + * MIME type (GIF/APNG → animated). MIME-based classification only, + * no frame inspection. + */ +import { useMutation, useQuery } from 'convex/react'; +import { + MagnifyingGlass, + Trash, + UploadSimple, +} from '@phosphor-icons/react'; +import { useMemo, useRef, useState } from 'react'; +import { Avatar } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import styles from './CustomEmojisTab.module.css'; + +interface CustomEmojiDoc { + _id: Id<'customEmojis'>; + name: string; + src: string; + createdAt: number; + animated: boolean; + uploadedById: Id<'userProfiles'>; + uploadedByUsername: string; + uploadedByDisplayName: string | null; + uploadedByAvatarUrl: string | null; +} + +const MAX_STATIC = 50; +const MAX_ANIMATED = 50; +const ACCEPTED_MIME = + 'image/png,image/gif,image/webp,image/jpeg,image/apng'; + +function isAnimatedMime(mime: string): boolean { + const m = mime.toLowerCase(); + return m === 'image/gif' || m === 'image/apng'; +} + +/** Turn a file name like `"cat face!.gif"` into a valid emoji + * shortcode (`"cat_face"`), matching the server's validation. */ +function sanitizeEmojiName(filename: string): string { + const stem = filename.replace(/\.[^.]+$/, ''); + const clean = stem + .toLowerCase() + .replace(/[^a-z0-9_]/g, '_') + .replace(/_+/g, '_') + .replace(/^_+|_+$/g, '') + .slice(0, 32); + if (clean.length >= 2) return clean; + return 'emoji'; +} + +export function CustomEmojisTab() { + const userId = + typeof localStorage !== 'undefined' + ? (localStorage.getItem('userId') as Id<'userProfiles'> | null) + : null; + + const emojis = (useQuery(api.customEmojis.list, {}) ?? + []) as CustomEmojiDoc[]; + const generateUploadUrl = useMutation(api.files.generateUploadUrl); + const uploadEmoji = useMutation(api.customEmojis.upload); + const renameEmoji = useMutation(api.customEmojis.rename); + const removeEmoji = useMutation(api.customEmojis.remove); + + const fileInputRef = useRef<HTMLInputElement>(null); + const [uploading, setUploading] = useState(false); + const [search, setSearch] = useState(''); + const [status, setStatus] = useState<{ + type: 'ok' | 'err'; + message: string; + } | null>(null); + const [isDraggingFiles, setIsDraggingFiles] = useState(false); + // Inline rename draft per emoji id — `undefined` means "no local + // draft, read the server name" so the query reactively updates + // rows that other clients renamed. + const [nameDrafts, setNameDrafts] = useState<Record<string, string>>({}); + + const filtered = useMemo(() => { + const q = search.trim().toLowerCase(); + if (!q) return emojis; + return emojis.filter((e) => e.name.toLowerCase().includes(q)); + }, [emojis, search]); + + const staticEmojis = useMemo( + () => filtered.filter((e) => !e.animated), + [filtered], + ); + const animatedEmojis = useMemo( + () => filtered.filter((e) => e.animated), + [filtered], + ); + + // Counts use the unfiltered lists so the slot card always + // reflects the true server state, not the current search view. + const totalStatic = emojis.filter((e) => !e.animated).length; + const totalAnimated = emojis.filter((e) => e.animated).length; + + const uploadFiles = async (files: FileList | File[]) => { + if (!userId) { + setStatus({ type: 'err', message: 'You must be logged in.' }); + return; + } + const list = Array.from(files).filter((f) => f.type.startsWith('image/')); + if (list.length === 0) return; + setUploading(true); + setStatus(null); + let okCount = 0; + try { + for (const file of list) { + const animated = isAnimatedMime(file.type); + // Enforce slot limits locally so we don't waste an upload + // round-trip when the server would reject it anyway. + if ( + (animated && totalAnimated + okCount >= MAX_ANIMATED) || + (!animated && totalStatic + okCount >= MAX_STATIC) + ) { + setStatus({ + type: 'err', + message: `Slot limit reached for ${animated ? 'animated' : 'static'} emoji.`, + }); + break; + } + try { + const uploadUrl = await generateUploadUrl({}); + const res = await fetch(uploadUrl, { + method: 'POST', + headers: { 'Content-Type': file.type }, + body: file, + }); + if (!res.ok) throw new Error(`Upload failed (${res.status})`); + const { storageId } = (await res.json()) as { + storageId: Id<'_storage'>; + }; + await uploadEmoji({ + userId, + name: sanitizeEmojiName(file.name), + storageId, + animated, + }); + okCount++; + } catch (err: any) { + setStatus({ + type: 'err', + message: err?.message ?? `Failed to upload ${file.name}`, + }); + } + } + if (okCount > 0 && !status) { + setStatus({ + type: 'ok', + message: `Uploaded ${okCount} ${okCount === 1 ? 'emoji' : 'emojis'}.`, + }); + } + } finally { + setUploading(false); + } + }; + + const handlePickFile = () => fileInputRef.current?.click(); + + const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => { + const files = e.target.files; + if (fileInputRef.current) fileInputRef.current.value = ''; + if (!files || files.length === 0) return; + await uploadFiles(files); + }; + + const handleDrop = async (e: React.DragEvent<HTMLDivElement>) => { + e.preventDefault(); + setIsDraggingFiles(false); + if (!e.dataTransfer.files || e.dataTransfer.files.length === 0) return; + await uploadFiles(e.dataTransfer.files); + }; + + const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => { + e.preventDefault(); + setIsDraggingFiles(true); + }; + + const handleDragLeave = (e: React.DragEvent<HTMLDivElement>) => { + e.preventDefault(); + setIsDraggingFiles(false); + }; + + const handleRename = async (emoji: CustomEmojiDoc, nextName: string) => { + if (!userId) return; + const clean = nextName.trim(); + if (clean === emoji.name) return; + try { + await renameEmoji({ + userId, + emojiId: emoji._id, + name: clean, + }); + setNameDrafts((prev) => { + const next = { ...prev }; + delete next[emoji._id]; + return next; + }); + } catch (err: any) { + setStatus({ type: 'err', message: err?.message ?? 'Rename failed' }); + } + }; + + const handleRemove = async (emoji: CustomEmojiDoc) => { + if (!userId) return; + if (!confirm(`Delete :${emoji.name}:?`)) return; + setStatus(null); + try { + await removeEmoji({ userId, emojiId: emoji._id }); + } catch (err: any) { + setStatus({ type: 'err', message: err?.message ?? 'Delete failed' }); + } + }; + + const renderRows = (rows: CustomEmojiDoc[]) => ( + <> + <div className={styles.tableHeader}> + <span>Emoji</span> + <span>Name</span> + <span>Uploaded By</span> + <span /> + </div> + <div className={styles.tableBody}> + {rows.map((emoji) => { + const draft = + nameDrafts[emoji._id] !== undefined + ? nameDrafts[emoji._id] + : emoji.name; + return ( + <div key={emoji._id} className={styles.emojiRow}> + <div className={styles.emojiCell}> + <img + src={emoji.src} + alt={emoji.name} + className={styles.emojiImage} + draggable={false} + /> + </div> + <div className={styles.nameCell}> + <input + type="text" + className={styles.nameInput} + value={`:${draft}:`} + onChange={(e) => { + // Strip the surrounding colons so only the raw + // shortcode is stored — users edit what reads as + // `:name:` but the backend gets the bare name. + const stripped = e.target.value.replace(/^:|:$/g, ''); + setNameDrafts((prev) => ({ + ...prev, + [emoji._id]: stripped, + })); + }} + onBlur={() => handleRename(emoji, draft)} + onKeyDown={(e) => { + if (e.key === 'Enter') e.currentTarget.blur(); + else if (e.key === 'Escape') { + setNameDrafts((prev) => { + const next = { ...prev }; + delete next[emoji._id]; + return next; + }); + (e.currentTarget as HTMLInputElement).blur(); + } + }} + maxLength={32} + /> + </div> + <div className={styles.uploaderCell}> + <Avatar + src={emoji.uploadedByAvatarUrl ?? undefined} + size={24} + fallback={ + emoji.uploadedByDisplayName || + emoji.uploadedByUsername + } + /> + <span className={styles.uploaderName}> + {emoji.uploadedByDisplayName || emoji.uploadedByUsername} + </span> + </div> + <div className={styles.deleteCell}> + <button + type="button" + className={styles.deleteButton} + onClick={() => handleRemove(emoji)} + aria-label={`Delete ${emoji.name}`} + title="Delete" + > + <Trash size={14} weight="bold" /> + </button> + </div> + </div> + ); + })} + </div> + </> + ); + + return ( + <div className={styles.container}> + <div className={styles.searchWrap}> + <MagnifyingGlass + className={styles.searchIcon} + size={16} + weight="regular" + /> + <input + type="text" + className={styles.searchInput} + placeholder="Search emojis..." + value={search} + onChange={(e) => setSearch(e.target.value)} + /> + </div> + + <div className={styles.slotsCard}> + <div className={styles.slotsHeader}> + <div className={styles.slotsHeaderLeft}> + <h3 className={styles.slotsTitle}>Emoji Slots</h3> + <div className={styles.slotsCounts}> + <span className={styles.slotsCount}> + Static: <strong>{totalStatic}</strong> / {MAX_STATIC} + </span> + <span className={styles.slotsCount}> + Animated: <strong>{totalAnimated}</strong> / {MAX_ANIMATED} + </span> + </div> + </div> + <button + type="button" + className={styles.uploadButton} + onClick={handlePickFile} + disabled={uploading} + > + <UploadSimple size={16} weight="bold" /> + {uploading ? 'Uploading…' : 'Upload Emoji'} + </button> + </div> + <p className={styles.slotsDescription}> + Emoji names must be at least 2 characters long and can only contain + alphanumeric characters and underscores. Allowed file types: JPEG, + PNG, WebP, GIF. We compress images to 128×128 pixels. Maximum size: + 384 KB per emoji. + </p> + </div> + + <div + className={`${styles.dropZone} ${isDraggingFiles ? styles.dropZoneActive : ''}`} + onClick={handlePickFile} + onDragOver={handleDragOver} + onDragLeave={handleDragLeave} + onDrop={handleDrop} + > + <UploadSimple + size={32} + weight="bold" + className={styles.dropZoneIcon} + /> + <span>Drag and drop emoji files here</span> + </div> + + <input + ref={fileInputRef} + type="file" + accept={ACCEPTED_MIME} + multiple + onChange={handleFileChange} + style={{ display: 'none' }} + /> + + {status && ( + <div + className={`${styles.statusBanner} ${status.type === 'ok' ? styles.statusOk : styles.statusErr}`} + > + {status.message} + </div> + )} + + {filtered.length === 0 ? ( + <div className={styles.emptyState}> + {search + ? 'No emojis match your search.' + : 'No custom emojis yet. Upload one to get started.'} + </div> + ) : ( + <> + {staticEmojis.length > 0 && ( + <div className={styles.section}> + <h3 className={styles.sectionTitle}> + Non-Animated Emoji ({staticEmojis.length}) + </h3> + {renderRows(staticEmojis)} + </div> + )} + {animatedEmojis.length > 0 && ( + <div className={styles.section}> + <h3 className={styles.sectionTitle}> + Animated Emoji ({animatedEmojis.length}) + </h3> + {renderRows(animatedEmojis)} + </div> + )} + </> + )} + </div> + ); +} diff --git a/packages/shared/src/components/settings/EmojisTab.module.css b/packages/shared/src/components/settings/EmojisTab.module.css new file mode 100644 index 0000000..d5ef0e1 --- /dev/null +++ b/packages/shared/src/components/settings/EmojisTab.module.css @@ -0,0 +1,225 @@ +.container { + display: flex; + flex-direction: column; + gap: 16px; + max-width: 680px; +} + +.header { + display: flex; + align-items: center; + justify-content: space-between; +} + +.heading { + margin: 0; + font-size: 18px; + font-weight: 700; + color: var(--text-primary, #fff); +} + +.count { + font-size: 11px; + color: var(--text-primary-muted, #a0a3a8); + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; +} + +.description { + margin: 0; + font-size: 13px; + line-height: 1.45; + color: var(--text-secondary, #a0a3a8); +} + +/* ── Upload row ──────────────────────────────────────────────────── */ + +.uploadRow { + display: flex; + align-items: center; + gap: 12px; + padding: 12px; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.03)); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); + border-radius: 8px; +} + +.uploadPreview { + flex: 0 0 auto; + width: 64px; + height: 64px; + border-radius: 8px; + background-color: var(--background-tertiary, rgba(0, 0, 0, 0.3)); + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; +} + +.uploadPreviewImage { + max-width: 100%; + max-height: 100%; + object-fit: contain; +} + +.uploadPreviewEmpty { + color: var(--text-primary-muted, #a0a3a8); +} + +.uploadFields { + flex: 1 1 auto; + display: flex; + flex-direction: column; + gap: 8px; + min-width: 0; +} + +.shortcodeInput { + width: 100%; + padding: 8px 12px; + background-color: var(--background-tertiary, rgba(0, 0, 0, 0.3)); + border: 1px solid transparent; + border-radius: 6px; + color: var(--text-primary, #fff); + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 13px; + outline: none; + box-sizing: border-box; + transition: border-color 0.1s; +} + +.shortcodeInput:focus { + border-color: var(--brand-primary, #5865f2); +} + +.uploadActions { + display: flex; + gap: 8px; +} + +/* ── Status banners ──────────────────────────────────────────────── */ + +.statusSuccess { + padding: 8px 12px; + background-color: rgba(46, 204, 113, 0.15); + border: 1px solid rgba(46, 204, 113, 0.4); + border-radius: 6px; + font-size: 13px; + color: var(--text-primary, #fff); +} + +.statusError { + padding: 8px 12px; + background-color: rgba(234, 80, 80, 0.15); + border: 1px solid rgba(234, 80, 80, 0.4); + border-radius: 6px; + font-size: 13px; + color: var(--text-primary, #fff); +} + +/* ── Existing pack grid ──────────────────────────────────────────── */ + +.empty { + padding: 32px 20px; + text-align: center; + font-size: 13px; + color: var(--text-primary-muted, #a0a3a8); + background-color: var(--background-secondary, rgba(255, 255, 255, 0.03)); + border: 1px dashed var(--background-modifier-accent, rgba(255, 255, 255, 0.08)); + border-radius: 8px; +} + +.grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); + gap: 10px; +} + +.gridItem { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + padding: 10px 8px; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.03)); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); + border-radius: 8px; + transition: border-color 0.1s, background-color 0.1s; +} + +.gridItem:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.06)); + border-color: var(--background-modifier-accent, rgba(255, 255, 255, 0.12)); +} + +.gridPreview { + width: 56px; + height: 56px; + display: flex; + align-items: center; + justify-content: center; +} + +.gridImage { + max-width: 56px; + max-height: 56px; + object-fit: contain; +} + +.gridLabel { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 11px; + color: var(--text-primary-muted, #a0a3a8); + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: center; +} + +.gridRemove { + position: absolute; + top: 4px; + right: 4px; + display: flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + border: none; + border-radius: 4px; + background-color: rgba(0, 0, 0, 0.5); + color: #fff; + cursor: pointer; + opacity: 0; + transition: opacity 0.1s, background-color 0.1s; +} + +.gridItem:hover .gridRemove { + opacity: 1; +} + +.gridRemove:hover { + background-color: var(--status-danger, #da373c); +} + +/* ── Permission gate fallback ────────────────────────────────────── */ + +.gate { + padding: 60px 20px; + text-align: center; +} + +.gateTitle { + margin: 0 0 8px; + font-size: 18px; + font-weight: 700; + color: var(--text-primary, #fff); +} + +.gateBody { + margin: 0 auto; + max-width: 360px; + font-size: 14px; + color: var(--text-secondary, #a0a3a8); +} diff --git a/packages/shared/src/components/settings/EmojisTab.tsx b/packages/shared/src/components/settings/EmojisTab.tsx new file mode 100644 index 0000000..431a905 --- /dev/null +++ b/packages/shared/src/components/settings/EmojisTab.tsx @@ -0,0 +1,296 @@ +/** + * EmojisTab — admin UI for managing the server's MSC2545 image pack. + * Upload PNG / GIF / WebP images, assign shortcodes, delete existing + * entries. Gated behind `EmojiPackManager.canManageEmojis` (native + * Matrix power level check on `im.ponies.room_emotes` state events). + * + * Pattern mirrors `RolesTab`: optimistic store updates, tolerant + * error surfacing, file + shortcode validation before hitting the + * homeserver. + */ +import { observer } from 'mobx-react-lite'; +import { useMemo, useRef, useState } from 'react'; +import { Upload, Trash, Plus } from '@phosphor-icons/react'; +import { Button } from '@brycord/ui'; +import { + EmojiPackManager, + MAX_EMOJIS_PER_PACK, + type CustomEmoji, +} from '@brycord/matrix-client'; +import EmojiPackStore from '@app/stores/EmojiPackStore'; +import SelectionStore from '@app/stores/SelectionStore'; +import { CustomEmojiImage } from '../channel/CustomEmojiImage'; +import { MobileEmojisTab } from './MobileEmojisTab'; +import styles from './EmojisTab.module.css'; + +interface EmojisTabProps { + serverId: string; +} + +const SHORTCODE_REGEX = /^[a-z0-9_]{2,30}$/; + +export const EmojisTab = observer(function EmojisTab({ serverId }: EmojisTabProps) { + // Mobile gets a completely different full-screen layout — see + // MobileEmojisTab. Branch into a thin wrapper component so the + // desktop hooks below don't run on the mobile path. + if (SelectionStore.isMobileViewport) { + return <MobileEmojisTab serverId={serverId} />; + } + return <DesktopEmojisTab serverId={serverId} />; +}); + +const DesktopEmojisTab = observer(function DesktopEmojisTab({ serverId }: EmojisTabProps) { + const [pendingFile, setPendingFile] = useState<File | null>(null); + const [pendingPreviewUrl, setPendingPreviewUrl] = useState<string | null>(null); + const [shortcode, setShortcode] = useState(''); + const [uploading, setUploading] = useState(false); + const [deletingId, setDeletingId] = useState<string | null>(null); + const [status, setStatus] = useState<{ type: 'success' | 'error'; message: string } | null>(null); + const fileInputRef = useRef<HTMLInputElement>(null); + + const canManage = useMemo(() => { + try { + return EmojiPackManager.getInstance().canManageEmojis(serverId); + } catch { + return false; + } + }, [serverId]); + + const pack = EmojiPackStore.getPack(serverId); + const emojis: CustomEmoji[] = pack?.emojis ?? []; + const isFull = emojis.length >= MAX_EMOJIS_PER_PACK; + + if (!canManage) { + return ( + <div className={styles.gate}> + <h2 className={styles.gateTitle}>Emojis</h2> + <p className={styles.gateBody}> + You need permission to manage emojis in this server. Ask an + admin to grant you a role with a higher power level. + </p> + </div> + ); + } + + const handleFilePick = (e: React.ChangeEvent<HTMLInputElement>) => { + const file = e.target.files?.[0]; + // Reset the input so the same file can be re-picked after clearing. + if (fileInputRef.current) fileInputRef.current.value = ''; + if (!file) return; + setStatus(null); + + if (!file.type.startsWith('image/')) { + setStatus({ type: 'error', message: 'Emoji must be a PNG, GIF, or WebP image.' }); + return; + } + if (file.size > 256 * 1024) { + setStatus({ + type: 'error', + message: 'File is too large. Maximum is 256 KB.', + }); + return; + } + + if (pendingPreviewUrl) URL.revokeObjectURL(pendingPreviewUrl); + setPendingFile(file); + setPendingPreviewUrl(URL.createObjectURL(file)); + + // Auto-suggest a shortcode from the filename: strip extension, + // lowercase, replace non-alphanumerics with underscores, clamp. + if (!shortcode) { + const base = file.name.replace(/\.[^.]+$/, '').toLowerCase(); + const cleaned = base.replace(/[^a-z0-9_]/g, '_').slice(0, 30); + if (cleaned.length >= 2) setShortcode(cleaned); + } + }; + + const handleUpload = async () => { + if (!pendingFile) return; + if (uploading) return; + + const trimmed = shortcode.trim(); + if (!SHORTCODE_REGEX.test(trimmed)) { + setStatus({ + type: 'error', + message: 'Shortcode must be 2–30 characters of lowercase letters, digits, or underscores.', + }); + return; + } + if (emojis.some((e) => e.shortcode === trimmed)) { + setStatus({ type: 'error', message: `An emoji named "${trimmed}" already exists.` }); + return; + } + + setUploading(true); + setStatus(null); + try { + await EmojiPackManager.getInstance().addEmoji(serverId, trimmed, pendingFile); + // The RoomState.events listener in MatrixActions will push + // the new pack to EmojiPackStore on the next tick; clear + // the local form state immediately for snappy feedback. + if (pendingPreviewUrl) URL.revokeObjectURL(pendingPreviewUrl); + setPendingFile(null); + setPendingPreviewUrl(null); + setShortcode(''); + setStatus({ type: 'success', message: `Added :${trimmed}:` }); + } catch (err: any) { + setStatus({ type: 'error', message: err?.message || 'Failed to upload emoji.' }); + } finally { + setUploading(false); + } + }; + + const handleRemove = async (emoji: CustomEmoji) => { + if (deletingId) return; + if (!window.confirm(`Remove :${emoji.shortcode}: from this server?`)) return; + setDeletingId(emoji.shortcode); + setStatus(null); + try { + await EmojiPackManager.getInstance().removeEmoji(serverId, emoji.shortcode); + setStatus({ type: 'success', message: `Removed :${emoji.shortcode}:` }); + } catch (err: any) { + setStatus({ type: 'error', message: err?.message || 'Failed to remove emoji.' }); + } finally { + setDeletingId(null); + } + }; + + const handleClearPending = () => { + if (pendingPreviewUrl) URL.revokeObjectURL(pendingPreviewUrl); + setPendingFile(null); + setPendingPreviewUrl(null); + setShortcode(''); + }; + + return ( + <div className={styles.container}> + <div className={styles.header}> + <h2 className={styles.heading}>Server Emojis</h2> + <span className={styles.count}> + {emojis.length}/{MAX_EMOJIS_PER_PACK} + </span> + </div> + <p className={styles.description}> + Upload custom emojis that everyone in this server can use in + messages and reactions. PNG, GIF, or WebP up to 256 KB. + Animated GIFs play automatically. + </p> + + {/* Upload row */} + <div className={styles.uploadRow}> + <div className={styles.uploadPreview}> + {pendingPreviewUrl ? ( + <img + src={pendingPreviewUrl} + alt="pending emoji preview" + className={styles.uploadPreviewImage} + draggable={false} + /> + ) : ( + <div className={styles.uploadPreviewEmpty}> + <Upload size={20} /> + </div> + )} + </div> + <div className={styles.uploadFields}> + <input + type="text" + className={styles.shortcodeInput} + placeholder="shortcode" + value={shortcode} + onChange={(e) => + setShortcode(e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '')) + } + maxLength={30} + /> + <div className={styles.uploadActions}> + {!pendingFile ? ( + <Button + variant="secondary" + size="sm" + icon={<Upload size={14} />} + onClick={() => fileInputRef.current?.click()} + disabled={isFull} + > + Choose File + </Button> + ) : ( + <> + <Button + variant="primary" + size="sm" + icon={<Plus size={14} weight="bold" />} + onClick={handleUpload} + disabled={uploading || !SHORTCODE_REGEX.test(shortcode)} + loading={uploading} + > + {uploading ? 'Uploading…' : 'Add'} + </Button> + <Button + variant="secondary" + size="sm" + onClick={handleClearPending} + disabled={uploading} + > + Cancel + </Button> + </> + )} + </div> + </div> + </div> + <input + ref={fileInputRef} + type="file" + accept="image/png,image/gif,image/webp,image/jpeg" + onChange={handleFilePick} + style={{ display: 'none' }} + /> + + {status && ( + <div + className={ + status.type === 'error' ? styles.statusError : styles.statusSuccess + } + > + {status.message} + </div> + )} + + {/* Existing pack grid */} + {emojis.length === 0 ? ( + <div className={styles.empty}> + No custom emojis yet. Upload one above to get started. + </div> + ) : ( + <div className={styles.grid}> + {emojis.map((emoji) => ( + <div key={emoji.mxcUrl} className={styles.gridItem}> + <div className={styles.gridPreview}> + <CustomEmojiImage + mxc={emoji.mxcUrl} + alt={`:${emoji.shortcode}:`} + title={`:${emoji.shortcode}:`} + className={styles.gridImage} + draggable={false} + /> + </div> + <div className={styles.gridLabel} title={`:${emoji.shortcode}:`}> + :{emoji.shortcode}: + </div> + <button + type="button" + className={styles.gridRemove} + onClick={() => handleRemove(emoji)} + disabled={deletingId === emoji.shortcode} + aria-label={`Remove :${emoji.shortcode}:`} + > + <Trash size={14} weight="fill" /> + </button> + </div> + ))} + </div> + )} + </div> + ); +}); diff --git a/packages/shared/src/components/settings/KeybindsTab.module.css b/packages/shared/src/components/settings/KeybindsTab.module.css new file mode 100644 index 0000000..310d974 --- /dev/null +++ b/packages/shared/src/components/settings/KeybindsTab.module.css @@ -0,0 +1,136 @@ +.header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1.25rem; +} + +.heading { + font-size: 1.125rem; + font-weight: 700; + color: var(--text-primary); + margin: 0; +} + +.description { + margin: 0.25rem 0 0; + font-size: 0.8125rem; + color: var(--text-tertiary); +} + +.categoryTitle { + margin: 1.5rem 0 0.5rem; + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--text-primary-muted, var(--text-muted)); +} + +.row { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.625rem 0.75rem; + border-radius: 0.5rem; + background-color: var(--background-secondary); + margin-bottom: 0.375rem; +} + +.rowLabel { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 0.125rem; +} + +.rowName { + font-size: 0.875rem; + font-weight: 600; + color: var(--text-primary); +} + +.rowDescription { + font-size: 0.75rem; + color: var(--text-tertiary); + overflow: hidden; + text-overflow: ellipsis; +} + +.comboButton { + flex-shrink: 0; + min-width: 10rem; + min-height: 2.25rem; + padding: 0.375rem 0.75rem; + border-radius: 0.375rem; + border: 1px solid var(--background-modifier-accent); + background-color: var(--background-primary); + color: var(--text-primary); + font: inherit; + font-size: 0.8125rem; + font-weight: 600; + text-align: center; + cursor: pointer; + transition: border-color 0.12s, background-color 0.12s; +} + +.comboButton:hover { + border-color: var(--brand-primary, #4641d9); +} + +.comboButtonRecording { + border-color: var(--brand-primary, #4641d9); + background-color: var(--background-modifier-selected); + animation: pulse 1.2s ease-in-out infinite; +} + +.comboButtonEmpty { + color: var(--text-tertiary); + font-style: italic; +} + +.resetButton { + flex-shrink: 0; + padding: 0.375rem 0.625rem; + border-radius: 0.375rem; + border: none; + background-color: transparent; + color: var(--text-tertiary); + font: inherit; + font-size: 0.75rem; + font-weight: 600; + cursor: pointer; + transition: color 0.12s, background-color 0.12s; +} + +.resetButton:hover { + color: var(--text-primary); + background-color: var(--background-modifier-hover); +} + +.resetButton:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.conflict { + margin-top: 0.375rem; + font-size: 0.75rem; + color: var(--status-warning, #f0b232); +} + +.recorderHint { + margin-top: 0.375rem; + font-size: 0.75rem; + color: var(--text-tertiary); +} + +@keyframes pulse { + 0%, 100% { + box-shadow: 0 0 0 0 rgba(70, 65, 217, 0.35); + } + 50% { + box-shadow: 0 0 0 4px rgba(70, 65, 217, 0); + } +} diff --git a/packages/shared/src/components/settings/KeybindsTab.tsx b/packages/shared/src/components/settings/KeybindsTab.tsx new file mode 100644 index 0000000..ce91c15 --- /dev/null +++ b/packages/shared/src/components/settings/KeybindsTab.tsx @@ -0,0 +1,233 @@ +/** + * KeybindsTab — the Keybinds page inside User Settings. Lists every + * action from KeybindContext grouped by category. Each row shows the + * current combo as a button; clicking it opens an inline recorder that + * captures the next keydown and saves the result. + */ +import { useEffect, useRef, useState } from 'react'; +import { Button } from '@discord-clone/ui'; +import { + eventToCombo, + formatCombo, + useKeybinds, + type KeybindAction, + type KeybindCategory, +} from '../../contexts/KeybindContext'; +import styles from './KeybindsTab.module.css'; + +const CATEGORY_ORDER: KeybindCategory[] = [ + 'voice', + 'navigation', + 'popouts', + 'messaging', +]; + +const CATEGORY_LABELS: Record<KeybindCategory, string> = { + voice: 'Voice', + navigation: 'Navigation', + popouts: 'Popouts', + messaging: 'Messaging', +}; + +export function KeybindsTab() { + const keybinds = useKeybinds(); + const [recordingId, setRecordingId] = useState<string | null>(null); + + const actionsByCategory = new Map<KeybindCategory, KeybindAction[]>(); + for (const action of keybinds.actions) { + const bucket = actionsByCategory.get(action.category) ?? []; + bucket.push(action); + actionsByCategory.set(action.category, bucket); + } + + return ( + <div> + <div className={styles.header}> + <div> + <h3 className={styles.heading}>Keybinds</h3> + <p className={styles.description}> + Customize keyboard shortcuts. Click a combo to rebind, or reset + to defaults. + </p> + </div> + <Button + variant="secondary" + size="sm" + onClick={() => keybinds.resetAll()} + > + Reset All + </Button> + </div> + + {CATEGORY_ORDER.map((category) => { + const actions = actionsByCategory.get(category); + if (!actions || actions.length === 0) return null; + return ( + <div key={category}> + <div className={styles.categoryTitle}> + {CATEGORY_LABELS[category]} + </div> + {actions.map((action) => ( + <KeybindRow + key={action.id} + action={action} + isRecording={recordingId === action.id} + onStartRecording={() => setRecordingId(action.id)} + onStopRecording={() => setRecordingId(null)} + /> + ))} + </div> + ); + })} + </div> + ); +} + +interface KeybindRowProps { + action: KeybindAction; + isRecording: boolean; + onStartRecording: () => void; + onStopRecording: () => void; +} + +function KeybindRow({ + action, + isRecording, + onStartRecording, + onStopRecording, +}: KeybindRowProps) { + const keybinds = useKeybinds(); + const currentCombo = keybinds.getCombo(action.id); + const isDefault = currentCombo === action.defaultCombo; + + return ( + <div className={styles.row}> + <div className={styles.rowLabel}> + <span className={styles.rowName}>{action.label}</span> + <span className={styles.rowDescription}>{action.description}</span> + </div> + {isRecording ? ( + <KeybindRecorder + action={action} + onSave={(combo) => { + keybinds.setCombo(action.id, combo); + onStopRecording(); + }} + onCancel={onStopRecording} + /> + ) : ( + <> + <button + type="button" + className={`${styles.comboButton} ${!currentCombo ? styles.comboButtonEmpty : ''}`} + onClick={onStartRecording} + > + {formatCombo(currentCombo)} + </button> + <button + type="button" + className={styles.resetButton} + disabled={isDefault} + onClick={() => keybinds.resetCombo(action.id)} + > + Reset + </button> + </> + )} + </div> + ); +} + +interface KeybindRecorderProps { + action: KeybindAction; + onSave: (combo: string) => void; + onCancel: () => void; +} + +function KeybindRecorder({ action, onSave, onCancel }: KeybindRecorderProps) { + const keybinds = useKeybinds(); + const [captured, setCaptured] = useState<string>(''); + const containerRef = useRef<HTMLDivElement>(null); + + useEffect(() => { + containerRef.current?.focus(); + }, []); + + useEffect(() => { + const onKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + e.preventDefault(); + e.stopPropagation(); + onCancel(); + return; + } + const combo = eventToCombo(e); + if (!combo) return; + e.preventDefault(); + e.stopPropagation(); + setCaptured(combo); + }; + window.addEventListener('keydown', onKeyDown, { capture: true }); + return () => { + window.removeEventListener('keydown', onKeyDown, { + capture: true, + } as EventListenerOptions); + }; + }, [onCancel]); + + const conflictId = captured ? keybinds.findConflict(captured, action.id) : null; + const conflictAction = conflictId + ? keybinds.actions.find((a) => a.id === conflictId) + : null; + + return ( + <div + ref={containerRef} + tabIndex={-1} + style={{ + display: 'flex', + flexDirection: 'column', + alignItems: 'stretch', + gap: 4, + flex: 1, + minWidth: 0, + }} + > + <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}> + <button + type="button" + className={`${styles.comboButton} ${styles.comboButtonRecording}`} + style={{ flex: 1 }} + tabIndex={-1} + > + {captured ? formatCombo(captured) : 'Press a key combination…'} + </button> + <Button + variant="primary" + size="sm" + disabled={!captured} + onClick={() => onSave(captured)} + > + Save + </Button> + <Button variant="secondary" size="sm" onClick={() => onSave('')}> + Clear + </Button> + <Button variant="secondary" size="sm" onClick={onCancel}> + Cancel + </Button> + </div> + {conflictAction && ( + <div className={styles.conflict}> + Conflicts with <strong>{conflictAction.label}</strong> — saving + will clear the other binding. + </div> + )} + {!captured && ( + <div className={styles.recorderHint}> + Press any key or combo. Press Escape to cancel. + </div> + )} + </div> + ); +} diff --git a/packages/shared/src/components/settings/MobileEmojisTab.module.css b/packages/shared/src/components/settings/MobileEmojisTab.module.css new file mode 100644 index 0000000..d1f2f13 --- /dev/null +++ b/packages/shared/src/components/settings/MobileEmojisTab.module.css @@ -0,0 +1,307 @@ +/* ── Mobile Emojis tab ─────────────────────────────────────────────── + Full-screen Fluxer-style layout. Uses the same `--background-primary` + page surface and `--background-secondary` rounded cards as the + mobile Roles tab so the two tabs feel like one system. */ + +.root { + display: flex; + flex-direction: column; + gap: 16px; +} + +/* ── Top "Upload Emoji" CTA ─────────────────────────────────────────*/ + +.uploadButton { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 14px 16px; + background-color: var(--brand-primary); + color: #fff; + border: none; + border-radius: 0.75rem; + font: inherit; + font-size: 16px; + font-weight: 700; + cursor: pointer; + transition: filter 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.uploadButton:active:not(:disabled) { + filter: brightness(0.92); +} + +.uploadButton:disabled { + opacity: 0.5; + cursor: default; +} + +.helperText { + font-size: 13px; + color: var(--text-primary-muted); + line-height: 1.45; + margin: 0 4px; +} + +/* ── Upload requirements card ───────────────────────────────────────*/ + +.sectionLabel { + font-size: 11px; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--text-primary-muted); + padding: 4px 4px 0; +} + +.requirementsCard { + background-color: var(--background-secondary); + border-radius: 0.75rem; + padding: 14px 16px; + display: flex; + flex-direction: column; + gap: 10px; +} + +.requirementsList { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 8px; +} + +.requirementsList li { + font-size: 13px; + color: var(--text-primary); + line-height: 1.45; + padding-left: 16px; + position: relative; +} + +.requirementsList li::before { + content: '•'; + position: absolute; + left: 0; + color: var(--text-primary-muted); +} + +.requirementsList li strong { + font-weight: 700; +} + +/* ── Inline upload form (shown when a file is staged) ──────────────*/ + +.uploadForm { + background-color: var(--background-secondary); + border-radius: 0.75rem; + padding: 14px 16px; + display: flex; + flex-direction: column; + gap: 12px; +} + +.uploadFormRow { + display: flex; + align-items: center; + gap: 12px; +} + +.uploadPreview { + width: 56px; + height: 56px; + border-radius: 0.5rem; + background-color: var(--background-secondary-alt); + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + overflow: hidden; +} + +.uploadPreviewImage { + max-width: 100%; + max-height: 100%; + object-fit: contain; +} + +.shortcodeInput { + flex: 1; + background-color: var(--background-secondary-alt); + border: none; + outline: none; + border-radius: 0.5rem; + padding: 10px 12px; + font: inherit; + font-size: 15px; + color: var(--text-primary); + min-width: 0; +} + +.shortcodeInput:focus, +.shortcodeInput:focus-visible { + outline: none; + box-shadow: none; +} + +.shortcodeInput::placeholder { + color: var(--text-tertiary-muted, var(--text-tertiary)); +} + +.uploadFormActions { + display: flex; + gap: 8px; +} + +.formButton { + flex: 1; + padding: 12px 16px; + border: none; + border-radius: 0.5rem; + font: inherit; + font-size: 14px; + font-weight: 700; + cursor: pointer; + transition: filter 0.15s, background-color 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.formButtonPrimary { + background-color: var(--brand-primary); + color: #fff; +} + +.formButtonPrimary:disabled { + opacity: 0.5; + cursor: default; +} + +.formButtonSecondary { + background-color: var(--background-secondary-alt); + color: var(--text-primary); +} + +/* ── Emoji list card ────────────────────────────────────────────────*/ + +.emojiCard { + background-color: var(--background-secondary); + border-radius: 0.75rem; + overflow: hidden; +} + +.emojiRow { + position: relative; + display: grid; + grid-template-columns: 48px 1fr 36px; + align-items: center; + gap: 12px; + padding: 12px 16px; +} + +.emojiRow:not(:last-child)::after { + content: ''; + position: absolute; + left: calc(16px + 48px + 12px); + right: 16px; + bottom: 0; + height: 1px; + background-color: var(--background-header-secondary); + pointer-events: none; +} + +.emojiThumb { + width: 48px; + height: 48px; + border-radius: 0.375rem; + background-color: var(--background-secondary-alt); + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; +} + +.emojiThumbImage { + max-width: 36px; + max-height: 36px; + object-fit: contain; +} + +.emojiName { + font-size: 14px; + font-weight: 600; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; +} + +.deleteButton { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: 50%; + border: none; + background: transparent; + color: hsl(350, calc(90% * var(--saturation-factor)), 65%); + cursor: pointer; + -webkit-tap-highlight-color: transparent; + transition: background-color 0.15s; +} + +.deleteButton:active { + background-color: var(--background-modifier-hover); +} + +.deleteButton:disabled { + opacity: 0.4; + cursor: default; +} + +/* ── Empty + status states ─────────────────────────────────────────*/ + +.empty { + padding: 24px 16px; + text-align: center; + color: var(--text-primary-muted); + font-size: 14px; +} + +.status { + padding: 10px 14px; + border-radius: 0.5rem; + font-size: 13px; +} + +.statusSuccess { + background-color: hsl(139, calc(47.3% * var(--saturation-factor)), 20%); + color: hsl(139, calc(47.3% * var(--saturation-factor)), 85%); +} + +.statusError { + background-color: hsl(0, calc(60% * var(--saturation-factor)), 22%); + color: hsl(0, calc(80% * var(--saturation-factor)), 85%); +} + +.gate { + padding: 32px 16px; + text-align: center; +} + +.gateTitle { + font-size: 18px; + font-weight: 700; + color: var(--text-primary); + margin: 0 0 8px; +} + +.gateBody { + font-size: 14px; + color: var(--text-primary-muted); + margin: 0; +} diff --git a/packages/shared/src/components/settings/MobileEmojisTab.tsx b/packages/shared/src/components/settings/MobileEmojisTab.tsx new file mode 100644 index 0000000..5c86ec8 --- /dev/null +++ b/packages/shared/src/components/settings/MobileEmojisTab.tsx @@ -0,0 +1,330 @@ +/** + * MobileEmojisTab — full-screen Fluxer-style mobile variant of + * EmojisTab. Layout: + * + * ┌────────── Upload Emoji ──────────┐ ← brand-primary CTA + * Add up to 50 custom emoji… helper text + * UPLOAD REQUIREMENTS section label + * ┌─────── card ──────────────┐ + * │ • File type: PNG, GIF, … │ + * │ • Recommended size: 256KB │ + * │ • Recommended dimensions… │ + * │ • Naming: 2+ chars, … │ + * └────────────────────────────┘ + * EMOJI — N SLOTS AVAILABLE section label + * ┌────────── card ────────────┐ + * │ [img] :hazmat: ⋯ │ + * │ [img] :blank: ⋯ │ + * │ ... │ + * └────────────────────────────┘ + * + * The uploader column from the Fluxer reference is intentionally + * dropped — MSC2545's `images` dict only stores `{shortcode → mxc}` + * with no record of who added each entry, so there's nothing to + * surface. Tapping the trash icon prompts a confirm and removes. + * + * The whole tab is gated behind `EmojiPackManager.canManageEmojis` + * (native power-level check) — same gate as desktop. Non-admins + * see a friendly fallback explaining how to get access. + */ +import { observer } from 'mobx-react-lite'; +import { useMemo, useRef, useState } from 'react'; +import { Trash, UploadSimple } from '@phosphor-icons/react'; +import { + EmojiPackManager, + MAX_EMOJIS_PER_PACK, + type CustomEmoji, +} from '@brycord/matrix-client'; +import EmojiPackStore from '@app/stores/EmojiPackStore'; +import { CustomEmojiImage } from '../channel/CustomEmojiImage'; +import styles from './MobileEmojisTab.module.css'; + +interface MobileEmojisTabProps { + serverId: string; +} + +const SHORTCODE_REGEX = /^[a-z0-9_]{2,30}$/; +const MAX_FILE_BYTES = 256 * 1024; + +export const MobileEmojisTab = observer(function MobileEmojisTab({ + serverId, +}: MobileEmojisTabProps) { + const [pendingFile, setPendingFile] = useState<File | null>(null); + const [pendingPreviewUrl, setPendingPreviewUrl] = useState<string | null>( + null, + ); + const [shortcode, setShortcode] = useState(''); + const [uploading, setUploading] = useState(false); + const [deletingShortcode, setDeletingShortcode] = useState<string | null>( + null, + ); + const [status, setStatus] = useState< + { type: 'success' | 'error'; message: string } | null + >(null); + const fileInputRef = useRef<HTMLInputElement>(null); + + const canManage = useMemo(() => { + try { + return EmojiPackManager.getInstance().canManageEmojis(serverId); + } catch { + return false; + } + }, [serverId]); + + const pack = EmojiPackStore.getPack(serverId); + const emojis: CustomEmoji[] = pack?.emojis ?? []; + const slotsRemaining = MAX_EMOJIS_PER_PACK - emojis.length; + const isFull = slotsRemaining <= 0; + + if (!canManage) { + return ( + <div className={styles.gate}> + <h2 className={styles.gateTitle}>Emojis</h2> + <p className={styles.gateBody}> + You need permission to manage emojis in this server. Ask an + admin to grant you a role with a higher power level. + </p> + </div> + ); + } + + const handleFilePick = (e: React.ChangeEvent<HTMLInputElement>) => { + const file = e.target.files?.[0]; + // Reset input so the same file can be picked again after clearing. + if (fileInputRef.current) fileInputRef.current.value = ''; + if (!file) return; + setStatus(null); + + if (!file.type.startsWith('image/')) { + setStatus({ + type: 'error', + message: 'Emoji must be a PNG, GIF, WebP, or JPEG image.', + }); + return; + } + if (file.size > MAX_FILE_BYTES) { + setStatus({ + type: 'error', + message: 'File is too large. Maximum is 256 KB.', + }); + return; + } + + if (pendingPreviewUrl) URL.revokeObjectURL(pendingPreviewUrl); + setPendingFile(file); + setPendingPreviewUrl(URL.createObjectURL(file)); + + // Auto-suggest a shortcode from the filename, same heuristic + // the desktop tab uses. + if (!shortcode) { + const base = file.name.replace(/\.[^.]+$/, '').toLowerCase(); + const cleaned = base.replace(/[^a-z0-9_]/g, '_').slice(0, 30); + if (cleaned.length >= 2) setShortcode(cleaned); + } + }; + + const handleUpload = async () => { + if (!pendingFile || uploading) return; + const trimmed = shortcode.trim(); + if (!SHORTCODE_REGEX.test(trimmed)) { + setStatus({ + type: 'error', + message: + 'Shortcode must be 2–30 chars (lowercase letters, digits, underscores).', + }); + return; + } + if (emojis.some((e) => e.shortcode === trimmed)) { + setStatus({ + type: 'error', + message: `An emoji named "${trimmed}" already exists.`, + }); + return; + } + + setUploading(true); + setStatus(null); + try { + await EmojiPackManager.getInstance().addEmoji(serverId, trimmed, pendingFile); + if (pendingPreviewUrl) URL.revokeObjectURL(pendingPreviewUrl); + setPendingFile(null); + setPendingPreviewUrl(null); + setShortcode(''); + setStatus({ type: 'success', message: `Added :${trimmed}:` }); + } catch (err: any) { + setStatus({ + type: 'error', + message: err?.message || 'Failed to upload emoji.', + }); + } finally { + setUploading(false); + } + }; + + const handleClearPending = () => { + if (pendingPreviewUrl) URL.revokeObjectURL(pendingPreviewUrl); + setPendingFile(null); + setPendingPreviewUrl(null); + setShortcode(''); + }; + + const handleDelete = async (emoji: CustomEmoji) => { + if (deletingShortcode) return; + if (!window.confirm(`Remove :${emoji.shortcode}: from this server?`)) return; + setDeletingShortcode(emoji.shortcode); + setStatus(null); + try { + await EmojiPackManager.getInstance().removeEmoji(serverId, emoji.shortcode); + setStatus({ type: 'success', message: `Removed :${emoji.shortcode}:` }); + } catch (err: any) { + setStatus({ + type: 'error', + message: err?.message || 'Failed to remove emoji.', + }); + } finally { + setDeletingShortcode(null); + } + }; + + return ( + <div className={styles.root}> + <button + type="button" + className={styles.uploadButton} + onClick={() => fileInputRef.current?.click()} + disabled={isFull || uploading} + > + <UploadSimple size={18} weight="bold" /> + Upload Emoji + </button> + <input + ref={fileInputRef} + type="file" + accept="image/png,image/gif,image/webp,image/jpeg" + onChange={handleFilePick} + style={{ display: 'none' }} + /> + + <p className={styles.helperText}> + Add up to {MAX_EMOJIS_PER_PACK} custom emoji that anyone can use + in this server. Animated GIF and WebP emoji play automatically. + </p> + + <div className={styles.sectionLabel}>Upload Requirements</div> + <div className={styles.requirementsCard}> + <ul className={styles.requirementsList}> + <li> + <strong>File type:</strong> PNG, GIF, WebP, JPEG + </li> + <li> + <strong>Maximum file size:</strong> 256 KB + </li> + <li> + <strong>Recommended dimensions:</strong> 128×128 + </li> + <li> + <strong>Naming:</strong> Emoji names must be at least 2 + characters long and can only contain lowercase letters, + digits, and underscores. + </li> + </ul> + </div> + + {/* Inline upload form — only visible after a file has been + picked. Lets the user name and confirm the upload, or + cancel out without leaving the tab. */} + {pendingFile && ( + <div className={styles.uploadForm}> + <div className={styles.uploadFormRow}> + <div className={styles.uploadPreview}> + {pendingPreviewUrl && ( + <img + src={pendingPreviewUrl} + alt="pending emoji" + className={styles.uploadPreviewImage} + draggable={false} + /> + )} + </div> + <input + type="text" + className={styles.shortcodeInput} + placeholder="shortcode" + value={shortcode} + onChange={(e) => + setShortcode( + e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, ''), + ) + } + maxLength={30} + /> + </div> + <div className={styles.uploadFormActions}> + <button + type="button" + className={`${styles.formButton} ${styles.formButtonSecondary}`} + onClick={handleClearPending} + disabled={uploading} + > + Cancel + </button> + <button + type="button" + className={`${styles.formButton} ${styles.formButtonPrimary}`} + onClick={handleUpload} + disabled={uploading || !SHORTCODE_REGEX.test(shortcode)} + > + {uploading ? 'Uploading…' : 'Add Emoji'} + </button> + </div> + </div> + )} + + {status && ( + <div + className={`${styles.status} ${ + status.type === 'error' ? styles.statusError : styles.statusSuccess + }`} + > + {status.message} + </div> + )} + + <div className={styles.sectionLabel}> + Emoji — {Math.max(0, slotsRemaining)} Slots Available + </div> + + {emojis.length === 0 ? ( + <div className={styles.empty}> + No custom emojis yet. Tap "Upload Emoji" to add one. + </div> + ) : ( + <div className={styles.emojiCard}> + {emojis.map((emoji) => ( + <div key={emoji.mxcUrl} className={styles.emojiRow}> + <div className={styles.emojiThumb}> + <CustomEmojiImage + mxc={emoji.mxcUrl} + alt={`:${emoji.shortcode}:`} + title={`:${emoji.shortcode}:`} + className={styles.emojiThumbImage} + draggable={false} + /> + </div> + <span className={styles.emojiName}>:{emoji.shortcode}:</span> + <button + type="button" + className={styles.deleteButton} + onClick={() => handleDelete(emoji)} + disabled={deletingShortcode === emoji.shortcode} + aria-label={`Remove :${emoji.shortcode}:`} + > + <Trash size={18} weight="fill" /> + </button> + </div> + ))} + </div> + )} + </div> + ); +}); diff --git a/packages/shared/src/components/settings/MobileRolesTab.module.css b/packages/shared/src/components/settings/MobileRolesTab.module.css new file mode 100644 index 0000000..49f1ad2 --- /dev/null +++ b/packages/shared/src/components/settings/MobileRolesTab.module.css @@ -0,0 +1,541 @@ +/* ── Mobile Roles tab ──────────────────────────────────────────────── + Full-screen Fluxer-style layout. Both the list view and the role + editor sit inside the existing MobileServerSettings body area and + share the `--background-primary` page surface. + + Cards use `--background-secondary` as a distinct raised surface + (same treatment the You page uses), with inset dividers in + `--background-header-secondary`. */ + +.root { + display: flex; + flex-direction: column; + gap: 16px; + padding: 0; +} + +/* ── Search input ───────────────────────────────────────────────────*/ + +.searchBar { + position: relative; + display: flex; + align-items: center; + min-height: 40px; + padding: 0 2.25rem 0 2.25rem; + border-radius: 8px; + background-color: var(--background-secondary); + flex-shrink: 0; +} + +.searchIcon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: var(--text-tertiary); + pointer-events: none; +} + +.searchInput { + flex: 1; + background: transparent; + border: none; + outline: none; + box-shadow: none; + -webkit-appearance: none; + appearance: none; + padding: 10px 0; + font: inherit; + font-size: 15px; + color: var(--text-primary); +} + +.searchInput:focus, +.searchInput:focus-visible { + outline: none; + box-shadow: none; +} + +.searchInput::placeholder { + color: var(--text-tertiary-muted, var(--text-tertiary)); +} + +/* ── Helper text between search and everyone card ──────────────────*/ + +.helperText { + font-size: 13px; + color: var(--text-primary-muted); + line-height: 1.4; + padding: 0 4px; +} + +/* ── @everyone single-item card ────────────────────────────────────*/ + +.everyoneCard { + background-color: var(--background-secondary); + border-radius: 0.75rem; + overflow: hidden; +} + +/* ── "Roles — N" section header row ────────────────────────────────*/ + +.rolesHeader { + display: flex; + align-items: center; + justify-content: space-between; + padding: 4px 4px; +} + +.rolesHeaderTitle { + font-size: 13px; + font-weight: 700; + color: var(--text-primary); +} + +.rolesHeaderCreate { + display: flex; + align-items: center; + gap: 4px; + background: transparent; + border: none; + color: var(--brand-primary-light); + font: inherit; + font-size: 13px; + font-weight: 600; + cursor: pointer; + padding: 4px 8px; + border-radius: 6px; + -webkit-tap-highlight-color: transparent; +} + +.rolesHeaderCreate:disabled { + opacity: 0.5; + cursor: default; +} + +/* ── Roles list card ───────────────────────────────────────────────*/ + +.rolesCard { + background-color: var(--background-secondary); + border-radius: 0.75rem; + overflow: hidden; +} + +.roleRow { + position: relative; + display: grid; + grid-template-columns: 36px 1fr auto; + align-items: center; + gap: 12px; + width: 100%; + padding: 14px 16px; + background: transparent; + border: none; + color: var(--text-primary); + font: inherit; + text-align: left; + cursor: pointer; + transition: background-color 0.1s; + -webkit-tap-highlight-color: transparent; +} + +.roleRow:active { + background-color: var(--background-modifier-hover); +} + +.roleRow:not(:last-child)::after { + content: ''; + position: absolute; + left: calc(16px + 36px + 12px); + right: 16px; + bottom: 0; + height: 1px; + background-color: var(--background-header-secondary); + pointer-events: none; +} + +.roleAvatar { + width: 36px; + height: 36px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--background-secondary-alt); + flex-shrink: 0; +} + +/* Small colored dot sitting inside the circle avatar — shows the + role color even when no icon is attached. */ +.roleAvatarDot { + width: 18px; + height: 18px; + border-radius: 50%; +} + +.roleRowText { + min-width: 0; + display: flex; + flex-direction: column; + gap: 2px; +} + +.roleRowName { + font-size: 15px; + font-weight: 600; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.roleRowSubtitle { + font-size: 12px; + color: var(--text-primary-muted); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.roleRowCaret { + color: var(--text-primary-muted); +} + +/* ── Empty state ───────────────────────────────────────────────────*/ + +.emptyState { + padding: 24px; + text-align: center; + color: var(--text-primary-muted); + font-size: 14px; +} + +.error { + padding: 12px 16px; + background-color: hsl(0, calc(60% * var(--saturation-factor)), 20%); + color: hsl(0, calc(80% * var(--saturation-factor)), 85%); + border-radius: 0.5rem; + font-size: 13px; +} + +.gate { + padding: 32px 16px; + text-align: center; +} + +.gateTitle { + font-size: 18px; + font-weight: 700; + color: var(--text-primary); + margin: 0 0 8px; +} + +.gateBody { + font-size: 14px; + color: var(--text-primary-muted); + margin: 0; +} + +/* ── Editor view ───────────────────────────────────────────────────*/ + +.editorRoot { + display: flex; + flex-direction: column; + gap: 16px; +} + +.fieldLabel { + font-size: 13px; + font-weight: 500; + color: var(--text-primary-muted); + padding: 0 4px 6px; +} + +/* Single-row input card (role name textbox wrapped in a rounded + --background-secondary surface). */ +.inputCard { + background-color: var(--background-secondary); + border-radius: 0.75rem; + padding: 14px 16px; + display: flex; + align-items: center; +} + +.inputCard input { + flex: 1; + background: transparent; + border: none; + outline: none; + box-shadow: none; + -webkit-appearance: none; + appearance: none; + font: inherit; + font-size: 16px; + font-weight: 600; + color: var(--text-primary); +} + +.inputCard input:focus, +.inputCard input:focus-visible { + outline: none; + box-shadow: none; +} + +.inputCard input::placeholder { + color: var(--text-tertiary-muted, var(--text-tertiary)); +} + +/* Grouped card with multiple rows (Role Color, Power Level, etc). */ +.groupCard { + background-color: var(--background-secondary); + border-radius: 0.75rem; + overflow: hidden; +} + +.groupRow { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 14px 16px; +} + +.groupRow:not(:last-child)::after { + content: ''; + position: absolute; + left: 16px; + right: 16px; + bottom: 0; + height: 1px; + background-color: var(--background-header-secondary); + pointer-events: none; +} + +.groupRowLeft { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; + flex: 1; +} + +.groupRowLabel { + font-size: 15px; + font-weight: 700; + color: var(--text-primary); +} + +.groupRowSublabel { + font-size: 12px; + color: var(--text-primary-muted); +} + +.colorPreview { + width: 22px; + height: 22px; + border-radius: 6px; + flex-shrink: 0; +} + +/* Stacked row (row + content below it, for swatches / slider) */ +.stackedRow { + position: relative; + display: flex; + flex-direction: column; + gap: 12px; + padding: 14px 16px; +} + +.stackedRow:not(:last-child)::after { + content: ''; + position: absolute; + left: 16px; + right: 16px; + bottom: 0; + height: 1px; + background-color: var(--background-header-secondary); + pointer-events: none; +} + +.swatchRow { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.swatch { + width: 28px; + height: 28px; + border-radius: 50%; + border: 2px solid transparent; + cursor: pointer; + transition: transform 0.1s, border-color 0.1s; + -webkit-tap-highlight-color: transparent; + padding: 0; +} + +.swatchActive { + border-color: #fff; +} + +.swatchNone { + background-color: var(--background-secondary-alt); + color: var(--text-primary-muted); + display: flex; + align-items: center; + justify-content: center; +} + +.hexInput { + margin-top: 4px; + background: var(--background-secondary-alt); + border: none; + outline: none; + padding: 8px 10px; + border-radius: 6px; + color: var(--text-primary); + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 13px; +} + +.powerHeader { + display: flex; + align-items: center; + justify-content: space-between; +} + +.powerValue { + font-size: 15px; + font-weight: 700; + color: var(--brand-primary-light); +} + +.slider { + width: 100%; + accent-color: var(--brand-primary-light); +} + +.powerHint { + font-size: 12px; + color: var(--text-primary-muted); + margin: 0; + line-height: 1.4; +} + +/* ── Abilities readout ─────────────────────────────────────────────*/ + +.abilities { + background-color: var(--background-secondary); + border-radius: 0.75rem; + padding: 14px 16px; + display: flex; + flex-direction: column; + gap: 10px; +} + +.abilitiesHeader { + font-size: 13px; + color: var(--text-primary-muted); +} + +.abilitiesList { + list-style: none; + margin: 0; + padding: 0; + display: grid; + grid-template-columns: 1fr; + gap: 6px; +} + +.abilitiesList li { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; +} + +.abilityOn { + color: var(--text-primary); +} + +.abilityOn svg { + color: hsl(139, calc(47.3% * var(--saturation-factor)), 43.9%); +} + +.abilityOff { + color: var(--text-primary-muted); +} + +.abilityOff svg { + color: var(--text-primary-muted); + opacity: 0.6; +} + +/* ── Delete button ─────────────────────────────────────────────────*/ + +.deleteCard { + background-color: var(--background-secondary); + border-radius: 0.75rem; + overflow: hidden; +} + +.deleteButton { + width: 100%; + padding: 16px; + background: transparent; + border: none; + color: hsl(350, calc(90% * var(--saturation-factor)), 65%); + font: inherit; + font-size: 15px; + font-weight: 700; + cursor: pointer; + text-align: center; + -webkit-tap-highlight-color: transparent; +} + +.deleteButton:active { + background-color: var(--background-modifier-hover); +} + +/* ── Save status strip ─────────────────────────────────────────────*/ + +.status { + padding: 10px 14px; + border-radius: 0.5rem; + font-size: 13px; +} + +.statusSuccess { + background-color: hsl(139, calc(47.3% * var(--saturation-factor)), 20%); + color: hsl(139, calc(47.3% * var(--saturation-factor)), 85%); +} + +.statusError { + background-color: hsl(0, calc(60% * var(--saturation-factor)), 22%); + color: hsl(0, calc(80% * var(--saturation-factor)), 85%); +} + +.saveButton { + width: 100%; + padding: 14px 16px; + background-color: var(--brand-primary); + border: none; + border-radius: 0.75rem; + color: #fff; + font: inherit; + font-size: 15px; + font-weight: 700; + cursor: pointer; + transition: filter 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.saveButton:active:not(:disabled) { + filter: brightness(0.92); +} + +.saveButton:disabled { + opacity: 0.5; + cursor: default; +} diff --git a/packages/shared/src/components/settings/MobileRolesTab.tsx b/packages/shared/src/components/settings/MobileRolesTab.tsx new file mode 100644 index 0000000..1b2872e --- /dev/null +++ b/packages/shared/src/components/settings/MobileRolesTab.tsx @@ -0,0 +1,576 @@ +/** + * MobileRolesTab — mobile full-screen variant of RolesTab. Rendered + * by RolesTab when SelectionStore.isMobileViewport is true. Hosted + * inside MobileServerSettings, so the top bar + body chrome come + * from there — this component only renders the tab contents and + * uses the shared `useMobileSettingsNav` context to push the top + * bar title / back handler when the user opens a specific role's + * editor screen. + * + * Layout follows the Fluxer mobile reference (see reference images + * in chat): + * List view — search, helper text, @everyone card, "Roles — N" + * header with Create shortcut, then a rounded card + * of every custom role with a trailing caret. + * Editor view — role name input, color picker card, power level + * slider card, abilities readout, delete button. + * + * Core CRUD + dirty tracking is intentionally inlined here (instead + * of shared with desktop RoleEditor) because the mobile layout + * rearranges controls enough that a mobile=true prop would bloat + * the desktop JSX. The underlying RoleManager calls are identical. + */ +import { observer } from 'mobx-react-lite'; +import { useEffect, useMemo, useState } from 'react'; +import { + CaretRight, + CheckCircle, + MagnifyingGlass, + Plus, + XCircle, +} from '@phosphor-icons/react'; +import { RoleManager } from '@brycord/matrix-client'; +import type { Role, PowerLevelAbilities } from '@brycord/matrix-client'; +import { MAX_ROLES_PER_SERVER } from '@brycord/constants'; +import RoleStore from '@app/stores/RoleStore'; +import { useMobileSettingsNav } from './MobileServerSettings'; +import styles from './MobileRolesTab.module.css'; + +interface MobileRolesTabProps { + serverId: string; +} + +const COLOR_SWATCHES: Array<{ hex: string; name: string }> = [ + { hex: '#99AAB5', name: 'Default' }, + { hex: '#1ABC9C', name: 'Teal' }, + { hex: '#2ECC71', name: 'Green' }, + { hex: '#3498DB', name: 'Blue' }, + { hex: '#9B59B6', name: 'Purple' }, + { hex: '#E91E63', name: 'Magenta' }, + { hex: '#F1C40F', name: 'Yellow' }, + { hex: '#E67E22', name: 'Orange' }, + { hex: '#E74C3C', name: 'Red' }, + { hex: '#95A5A6', name: 'Gray' }, + { hex: '#607D8B', name: 'Slate' }, +]; + +const HEX_REGEX = /^#[0-9a-fA-F]{6}$/; + +/** + * Build a roleId → member-count map for the given server in one pass + * over `memberRolesBySpace`. Used by the list view to show a + * "3 Members" subtitle on each role row. @everyone is excluded + * because it's never stored in the per-member assignment index. + */ +function useMemberCounts(serverId: string): Record<string, number> { + return useMemo(() => { + const assignments = RoleStore.memberRolesBySpace[serverId] ?? {}; + const counts: Record<string, number> = {}; + for (const roleIds of Object.values(assignments)) { + for (const id of roleIds) { + counts[id] = (counts[id] ?? 0) + 1; + } + } + return counts; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [serverId, RoleStore.memberRolesBySpace[serverId]]); +} + +export const MobileRolesTab = observer(function MobileRolesTab({ + serverId, +}: MobileRolesTabProps) { + const [editingRoleId, setEditingRoleId] = useState<string | null>(null); + const [search, setSearch] = useState(''); + const [error, setError] = useState<string | null>(null); + const [creating, setCreating] = useState(false); + + const canManage = useMemo(() => { + try { + return RoleManager.getInstance().canManageRoles(serverId); + } catch { + return false; + } + }, [serverId]); + + const memberCounts = useMemberCounts(serverId); + + const allRoles = RoleStore.getRoles(serverId); + const editableRoles = allRoles + .filter((r) => r.id !== 'everyone') + .sort((a, b) => b.position - a.position); + const everyone = allRoles.find((r) => r.id === 'everyone'); + + const filteredRoles = useMemo(() => { + if (!search.trim()) return editableRoles; + const q = search.toLowerCase(); + return editableRoles.filter((r) => r.name.toLowerCase().includes(q)); + }, [editableRoles, search]); + + const editingRole: Role | undefined = editingRoleId + ? allRoles.find((r) => r.id === editingRoleId) + : undefined; + + // Fall back to the list view if the role being edited was deleted + // from another session or just now by this component. + useEffect(() => { + if (editingRoleId && !editingRole) setEditingRoleId(null); + }, [editingRoleId, editingRole]); + + // Tell MobileServerSettings to swap the top bar title / back + // button while the editor is open. Cleared on unmount + when the + // editor closes. + const nav = useMobileSettingsNav(); + useEffect(() => { + if (!editingRole) { + nav.setSubTitle(null); + nav.setSubSubtitle(null); + nav.setSubBackHandler(null); + return; + } + nav.setSubTitle(editingRole.name); + nav.setSubSubtitle('Role'); + nav.setSubBackHandler(() => () => setEditingRoleId(null)); + return () => { + nav.setSubTitle(null); + nav.setSubSubtitle(null); + nav.setSubBackHandler(null); + }; + }, [editingRole, nav]); + + if (!canManage) { + return ( + <div className={styles.gate}> + <h2 className={styles.gateTitle}>Manage Roles</h2> + <p className={styles.gateBody}> + You need permission to manage roles in this server. Ask an + admin to grant you a role with a higher power level. + </p> + </div> + ); + } + + const handleCreate = async () => { + if (creating) return; + if (editableRoles.length >= MAX_ROLES_PER_SERVER) { + setError(`A server can have at most ${MAX_ROLES_PER_SERVER} roles.`); + return; + } + setCreating(true); + setError(null); + try { + const role = await RoleManager.getInstance().createRole(serverId, { + name: 'New Role', + powerLevel: 0, + }); + RoleStore.handleRoleCreated(serverId, role); + setEditingRoleId(role.id); + } catch (err: any) { + setError(err?.message || 'Failed to create role.'); + } finally { + setCreating(false); + } + }; + + if (editingRole) { + return ( + <MobileRoleEditor + key={editingRole.id} + serverId={serverId} + role={editingRole} + readOnly={editingRole.id === 'everyone'} + onDeleted={() => setEditingRoleId(null)} + /> + ); + } + + return ( + <div className={styles.root}> + <div className={styles.searchBar}> + <MagnifyingGlass + size={18} + weight="regular" + className={styles.searchIcon} + /> + <input + type="text" + className={styles.searchInput} + placeholder="Search" + value={search} + onChange={(e) => setSearch(e.target.value)} + /> + </div> + + <p className={styles.helperText}> + Use roles to group your server members and assign permissions. + </p> + + {everyone && ( + <div className={styles.everyoneCard}> + <button + type="button" + className={styles.roleRow} + onClick={() => setEditingRoleId('everyone')} + > + <div className={styles.roleAvatar}> + <span + className={styles.roleAvatarDot} + style={{ backgroundColor: 'var(--text-primary-muted)' }} + /> + </div> + <div className={styles.roleRowText}> + <span className={styles.roleRowName}>@everyone</span> + <span className={styles.roleRowSubtitle}> + Default permissions for all server members + </span> + </div> + <CaretRight + size={16} + weight="bold" + className={styles.roleRowCaret} + /> + </button> + </div> + )} + + <div className={styles.rolesHeader}> + <span className={styles.rolesHeaderTitle}> + Roles — {editableRoles.length} + </span> + <button + type="button" + className={styles.rolesHeaderCreate} + onClick={handleCreate} + disabled={creating || editableRoles.length >= MAX_ROLES_PER_SERVER} + > + <Plus size={14} weight="bold" /> + Create + </button> + </div> + + {filteredRoles.length === 0 ? ( + <div className={styles.emptyState}> + {search.trim() + ? 'No roles match your search.' + : 'No roles yet. Tap "Create" to add one.'} + </div> + ) : ( + <div className={styles.rolesCard}> + {filteredRoles.map((role) => { + const count = memberCounts[role.id] ?? 0; + return ( + <button + key={role.id} + type="button" + className={styles.roleRow} + onClick={() => setEditingRoleId(role.id)} + > + <div className={styles.roleAvatar}> + <span + className={styles.roleAvatarDot} + style={{ + backgroundColor: + role.color || 'var(--text-primary-muted)', + }} + /> + </div> + <div className={styles.roleRowText}> + <span + className={styles.roleRowName} + style={role.color ? { color: role.color } : undefined} + > + {role.name} + </span> + <span className={styles.roleRowSubtitle}> + {count} {count === 1 ? 'Member' : 'Members'} · PL{' '} + {role.powerLevel} + </span> + </div> + <CaretRight + size={16} + weight="bold" + className={styles.roleRowCaret} + /> + </button> + ); + })} + </div> + )} + + {error && <div className={styles.error}>{error}</div>} + </div> + ); +}); + +// ── Editor ────────────────────────────────────────────────────────── + +const ABILITY_LABELS: Array<{ key: keyof PowerLevelAbilities; label: string }> = [ + { key: 'canSendMessages', label: 'Send messages' }, + { key: 'canInvite', label: 'Invite members' }, + { key: 'canRenameNicknames', label: 'Rename other members' }, + { key: 'canRedact', label: "Delete others' messages" }, + { key: 'canKick', label: 'Kick members' }, + { key: 'canBan', label: 'Ban members' }, + { key: 'canSendState', label: 'Send room state events' }, + { key: 'canRenameChannels', label: 'Rename channels' }, + { key: 'canManageRoles', label: 'Manage roles' }, + { key: 'canEditPowerLevels', label: 'Edit server power levels' }, +]; + +interface MobileRoleEditorProps { + serverId: string; + role: Role; + readOnly?: boolean; + onDeleted: () => void; +} + +const MobileRoleEditor = observer(function MobileRoleEditor({ + serverId, + role, + readOnly, + onDeleted, +}: MobileRoleEditorProps) { + const [name, setName] = useState(role.name); + const [color, setColor] = useState<string | undefined>(role.color); + const [powerLevel, setPowerLevel] = useState(role.powerLevel); + const [saving, setSaving] = useState(false); + const [deleting, setDeleting] = useState(false); + const [status, setStatus] = useState< + { type: 'success' | 'error'; message: string } | null + >(null); + const [confirmDelete, setConfirmDelete] = useState(false); + + useEffect(() => { + setName(role.name); + setColor(role.color); + setPowerLevel(role.powerLevel); + setStatus(null); + setConfirmDelete(false); + }, [role.id]); + + const abilities: PowerLevelAbilities = useMemo( + () => RoleManager.getInstance().describePowerLevel(serverId, powerLevel), + [serverId, powerLevel], + ); + + const trimmedName = name.trim(); + const isDirty = + trimmedName !== role.name || + (color ?? undefined) !== (role.color ?? undefined) || + powerLevel !== role.powerLevel; + const nameValid = trimmedName.length > 0 && trimmedName.length <= 32; + const canSave = isDirty && nameValid && !saving && !readOnly; + + const handleSave = async () => { + if (!canSave) return; + setSaving(true); + setStatus(null); + try { + await RoleManager.getInstance().updateRole(serverId, role.id, { + name: trimmedName, + color, + powerLevel, + }); + RoleStore.handleRoleUpdated(serverId, { + ...role, + name: trimmedName, + color, + powerLevel, + }); + setStatus({ type: 'success', message: 'Role saved.' }); + } catch (err: any) { + setStatus({ + type: 'error', + message: err?.message || 'Failed to save role.', + }); + } finally { + setSaving(false); + } + }; + + const handleDelete = async () => { + if (readOnly || deleting) return; + if (!confirmDelete) { + setConfirmDelete(true); + return; + } + setDeleting(true); + setStatus(null); + try { + await RoleManager.getInstance().deleteRole(serverId, role.id); + RoleStore.handleRoleDeleted(serverId, role.id); + onDeleted(); + } catch (err: any) { + setStatus({ + type: 'error', + message: err?.message || 'Failed to delete role.', + }); + setDeleting(false); + } + }; + + if (readOnly) { + return ( + <div className={styles.editorRoot}> + <div className={styles.abilities}> + <div className={styles.abilitiesHeader}> + @everyone is the baseline role every member has. Its abilities + come from the server's default power level thresholds, not + from a per-role configuration. + </div> + </div> + <AbilitiesReadout + powerLevel={0} + abilities={RoleManager.getInstance().describePowerLevel(serverId, 0)} + /> + </div> + ); + } + + return ( + <div className={styles.editorRoot}> + <div> + <div className={styles.fieldLabel}>Role name</div> + <div className={styles.inputCard}> + <input + type="text" + value={name} + onChange={(e) => setName(e.target.value)} + maxLength={40} + placeholder="new role" + /> + </div> + </div> + + <div className={styles.groupCard}> + <div className={styles.stackedRow}> + <div className={styles.groupRowLeft}> + <span className={styles.groupRowLabel}>Role Color</span> + <span className={styles.groupRowSublabel}> + {color ? color : 'No color'} + </span> + </div> + <div className={styles.swatchRow}> + <button + type="button" + className={`${styles.swatch} ${styles.swatchNone} ${!color ? styles.swatchActive : ''}`} + onClick={() => setColor(undefined)} + aria-label="No color" + > + <XCircle size={14} weight="bold" /> + </button> + {COLOR_SWATCHES.map((s) => ( + <button + key={s.hex} + type="button" + className={`${styles.swatch} ${color === s.hex ? styles.swatchActive : ''}`} + style={{ backgroundColor: s.hex }} + onClick={() => setColor(s.hex)} + aria-label={s.name} + /> + ))} + </div> + <input + type="text" + className={styles.hexInput} + value={color ?? ''} + placeholder="#5865F2" + onChange={(e) => { + const val = e.target.value; + if (val === '' || HEX_REGEX.test(val)) { + setColor(val || undefined); + } else if (val.startsWith('#') && val.length <= 7) { + setColor(val); + } + }} + /> + </div> + + <div className={styles.stackedRow}> + <div className={styles.powerHeader}> + <span className={styles.groupRowLabel}>Power Level</span> + <span className={styles.powerValue}>{powerLevel}</span> + </div> + <input + type="range" + min={0} + max={100} + value={powerLevel} + onChange={(e) => setPowerLevel(Number(e.target.value))} + className={styles.slider} + /> + <p className={styles.powerHint}> + Power level determines what members with this role can do + at the Matrix protocol layer. Multiple roles can share the + same power level. + </p> + </div> + </div> + + <AbilitiesReadout powerLevel={powerLevel} abilities={abilities} /> + + {status && ( + <div + className={`${styles.status} ${ + status.type === 'error' ? styles.statusError : styles.statusSuccess + }`} + > + {status.message} + </div> + )} + + <button + type="button" + className={styles.saveButton} + onClick={handleSave} + disabled={!canSave} + > + {saving ? 'Saving…' : 'Save Role'} + </button> + + <div className={styles.deleteCard}> + <button + type="button" + className={styles.deleteButton} + onClick={handleDelete} + disabled={deleting} + > + {confirmDelete ? 'Tap again to confirm delete' : 'Delete Role'} + </button> + </div> + </div> + ); +}); + +function AbilitiesReadout({ + powerLevel, + abilities, +}: { + powerLevel: number; + abilities: PowerLevelAbilities; +}) { + return ( + <div className={styles.abilities}> + <div className={styles.abilitiesHeader}> + At power level <strong>{powerLevel}</strong>, members can: + </div> + <ul className={styles.abilitiesList}> + {ABILITY_LABELS.map(({ key, label }) => { + const allowed = abilities[key]; + return ( + <li + key={key} + className={allowed ? styles.abilityOn : styles.abilityOff} + > + {allowed ? ( + <CheckCircle size={14} weight="fill" /> + ) : ( + <XCircle size={14} weight="fill" /> + )} + <span>{label}</span> + </li> + ); + })} + </ul> + </div> + ); +} diff --git a/packages/shared/src/components/settings/MobileServerSettings.module.css b/packages/shared/src/components/settings/MobileServerSettings.module.css new file mode 100644 index 0000000..bcc0af3 --- /dev/null +++ b/packages/shared/src/components/settings/MobileServerSettings.module.css @@ -0,0 +1,223 @@ +/* ── Mobile server settings — full-screen overlay ──────────────────── + Stands in for ServerSettingsModal's desktop Modal at ≤768px. Same + Fluxer-mobile layout: top bar with back/close + title, server icon + + name hero, Settings section heading, and a rounded card stack + with one row per tab. Tapping a row pushes that tab's panel + full-screen with a back arrow header. */ + +.overlay { + position: fixed; + inset: 0; + z-index: var(--z-index-modal, 10000); + display: flex; + flex-direction: column; + background-color: var(--background-primary); + overflow: hidden; +} + +/* ── Top bar (close / back + title) ─────────────────────────────────*/ + +.topBar { + flex-shrink: 0; + display: grid; + grid-template-columns: 48px 1fr 48px; + align-items: center; + height: 56px; + padding: 0 8px; + border-bottom: 1px solid var(--background-header-secondary); +} + +.topBarButton { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + border: none; + background: transparent; + color: var(--text-primary); + cursor: pointer; + transition: background-color 0.15s; + -webkit-tap-highlight-color: transparent; + margin: 0 auto; +} + +.topBarButton:active { + background-color: var(--background-modifier-hover); +} + +.topBarTitleBlock { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-width: 0; +} + +.topBarTitle { + font-size: 17px; + font-weight: 700; + color: var(--text-primary); + text-align: center; + margin: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 100%; +} + +.topBarSubtitle { + font-size: 12px; + font-weight: 500; + color: var(--text-primary-muted); + line-height: 1.2; + margin-top: 2px; +} + +/* ── Body (scrolling content area) ──────────────────────────────────*/ + +.body { + flex: 1; + min-height: 0; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + padding: 16px 16px 32px; +} + +/* Category-list body variant gets extra top space for the server + hero block. The tab-panel variant sits flush so custom panels + control their own spacing. */ +.bodyList { + padding-top: 24px; +} + +.bodyPanel { + padding: 16px; +} + +/* ── Server hero (icon + name) ──────────────────────────────────────*/ + +.hero { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + margin-bottom: 28px; +} + +.heroIcon { + width: 72px; + height: 72px; + border-radius: 18px; + object-fit: cover; + background-color: var(--background-modifier-hover); +} + +.heroInitials { + width: 72px; + height: 72px; + border-radius: 18px; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--background-modifier-hover); + color: var(--text-primary); + font-size: 26px; + font-weight: 700; +} + +.heroName { + font-size: 16px; + font-weight: 600; + color: var(--text-primary); + margin: 0; + text-align: center; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* ── Section label ("Settings") ─────────────────────────────────────*/ + +.sectionLabel { + font-size: 13px; + font-weight: 500; + color: var(--text-primary-muted); + padding: 0 4px 8px; +} + +/* ── Rounded card holding the list of tabs ──────────────────────────*/ + +.tabList { + background-color: var(--background-secondary); + border-radius: 0.75rem; + overflow: hidden; +} + +.tabItem { + position: relative; + display: grid; + grid-template-columns: 32px 1fr auto; + align-items: center; + gap: 12px; + width: 100%; + padding: 16px; + background: transparent; + border: none; + color: var(--text-primary); + font: inherit; + font-size: 15px; + font-weight: 600; + text-align: left; + cursor: pointer; + transition: background-color 0.1s; + -webkit-tap-highlight-color: transparent; +} + +.tabItem:active { + background-color: var(--background-modifier-hover); +} + +.tabItemIcon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 50%; + background-color: var(--background-secondary-alt); + color: var(--text-primary); +} + +.tabItemCaret { + color: var(--text-primary-muted); +} + +/* Divider between adjacent rows within the rounded card. */ +.tabItem:not(:last-child)::after { + content: ''; + position: absolute; + left: calc(16px + 32px + 12px); + right: 16px; + bottom: 0; + height: 1px; + background-color: var(--background-header-secondary); + pointer-events: none; +} + +/* ── Tab panel — stub fallback ──────────────────────────────────────*/ + +.stubTitle { + margin: 0 0 8px; + font-size: 18px; + font-weight: 700; + color: var(--text-primary); +} + +.stubBody { + margin: 0; + font-size: 14px; + color: var(--text-secondary); +} diff --git a/packages/shared/src/components/settings/MobileServerSettings.tsx b/packages/shared/src/components/settings/MobileServerSettings.tsx new file mode 100644 index 0000000..08ca946 --- /dev/null +++ b/packages/shared/src/components/settings/MobileServerSettings.tsx @@ -0,0 +1,235 @@ +/** + * MobileServerSettings — full-screen stand-in for ServerSettingsModal + * on mobile. Ported from the new UI: top bar with X / back button + + * title, a server icon + name hero, then a rounded card listing each + * category. Tapping a category replaces the body with that tab's + * panel and swaps the top bar to a back arrow. + * + * Rendered as a portal overlay (not the shared Modal) so the surface + * is true full-screen on mobile. Reuses the existing `OverviewTab`, + * `RolesTab`, and `EmojisTab` components exported from + * ServerSettingsModal — same Convex-backed logic as desktop. + */ +import { useEffect, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { useQuery } from 'convex/react'; +import { + CaretLeft, + CaretRight, + Gear, + ShieldStar, + Smiley, + X, +} from '@phosphor-icons/react'; +import { api } from '../../../../../convex/_generated/api'; +import { + EmojisTab, + OverviewTab, + type ServerSettingsTab, +} from './ServerSettingsModal'; +import { useRolesView } from './RolesView'; +import rolesStyles from './RolesView.module.css'; +import styles from './MobileServerSettings.module.css'; + +interface MobileServerSettingsProps { + isOpen: boolean; + onClose: () => void; + initialTab?: ServerSettingsTab; +} + +const TABS: Array<{ + id: ServerSettingsTab; + label: string; + icon: React.ComponentType<{ + size?: number; + weight?: 'regular' | 'fill' | 'bold'; + }>; +}> = [ + { id: 'overview', label: 'Overview', icon: Gear }, + { id: 'roles', label: 'Roles & Permissions', icon: ShieldStar }, + { id: 'emojis', label: 'Custom Emoji', icon: Smiley }, +]; + +function getInitials(name: string): string { + return name + .split(/\s+/) + .map((w) => w[0]) + .join('') + .slice(0, 2) + .toUpperCase(); +} + +export function MobileServerSettings({ + isOpen, + onClose, + initialTab, +}: MobileServerSettingsProps) { + // `null` means we're on the category list. A non-null tab id + // shows that tab's panel full-screen with a back arrow in the + // top bar. + const [activeTab, setActiveTab] = useState<ServerSettingsTab | null>( + initialTab ?? null, + ); + + // Roles & Permissions uses a two-screen flow on mobile: the + // role list view, then the editor when a role is tapped. The + // hook owns the selected-id state so desktop and mobile share + // the same drafts map behind the scenes. + const rolesView = useRolesView({ + onBack: () => setActiveTab(null), + }); + + useEffect(() => { + if (isOpen) setActiveTab(initialTab ?? null); + }, [isOpen, initialTab]); + + // Lock body scroll while the overlay is open. + useEffect(() => { + if (!isOpen) return; + const prev = document.body.style.overflow; + document.body.style.overflow = 'hidden'; + return () => { + document.body.style.overflow = prev; + }; + }, [isOpen]); + + // Escape / top-bar back — pop one level at a time. + // roles editor → roles list → category list → close modal + const popOneLevel = () => { + if (activeTab === 'roles' && rolesView.selectedId) { + rolesView.clearSelection(); + return; + } + if (activeTab !== null) { + setActiveTab(null); + return; + } + onClose(); + }; + + useEffect(() => { + if (!isOpen) return; + const handler = (e: KeyboardEvent) => { + if (e.key !== 'Escape') return; + popOneLevel(); + }; + document.addEventListener('keydown', handler); + return () => document.removeEventListener('keydown', handler); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isOpen, activeTab, rolesView.selectedId, onClose]); + + const serverSettings = useQuery(api.serverSettings.get, isOpen ? {} : 'skip'); + + if (!isOpen) return null; + + const serverName = serverSettings?.serverName || 'Server'; + const serverIcon = serverSettings?.iconUrl || null; + + const activeTabMeta = activeTab + ? TABS.find((t) => t.id === activeTab) + : undefined; + + const backIsClose = activeTab === null; + const barTitle = activeTabMeta ? activeTabMeta.label : 'Server Settings'; + + return createPortal( + <div className={styles.overlay} role="dialog" aria-modal="true"> + {/* Top bar — X at the root, back arrow whenever a tab is pushed. */} + <div className={styles.topBar}> + <button + type="button" + className={styles.topBarButton} + onClick={popOneLevel} + aria-label={backIsClose ? 'Close' : 'Back'} + > + {backIsClose ? ( + <X size={22} weight="bold" /> + ) : ( + <CaretLeft size={22} weight="bold" /> + )} + </button> + <div className={styles.topBarTitleBlock}> + <h2 className={styles.topBarTitle}>{barTitle}</h2> + </div> + <div /> + </div> + + {activeTab === null ? ( + <div className={`${styles.body} ${styles.bodyList}`}> + <div className={styles.hero}> + {serverIcon ? ( + <img + src={serverIcon} + alt={serverName} + className={styles.heroIcon} + draggable={false} + /> + ) : ( + <div className={styles.heroInitials}> + {getInitials(serverName)} + </div> + )} + <h3 className={styles.heroName}>{serverName}</h3> + </div> + + <div className={styles.sectionLabel}>Settings</div> + <div className={styles.tabList}> + {TABS.map((tab) => { + const Icon = tab.icon; + return ( + <button + key={tab.id} + type="button" + className={styles.tabItem} + onClick={() => setActiveTab(tab.id)} + > + <span className={styles.tabItemIcon}> + <Icon size={18} weight="fill" /> + </span> + <span>{tab.label}</span> + <CaretRight + size={16} + weight="bold" + className={styles.tabItemCaret} + /> + </button> + ); + })} + </div> + </div> + ) : activeTab === 'roles' ? ( + // Roles & Permissions gets a dedicated two-screen flow: + // - no selection → full-screen role list (Create Role + + // rows with chevrons) + // - role selected → editor content with a "Back to + // Roles" row on top for the in-panel pop-back + <div className={`${styles.body} ${styles.bodyPanel}`}> + {rolesView.selectedId ? ( + <> + <div className={rolesStyles.mobileBackRow}> + <button + type="button" + className={rolesStyles.mobileBackButton} + onClick={rolesView.clearSelection} + > + <CaretLeft size={14} weight="bold" /> + Back to Roles + </button> + </div> + {rolesView.header} + {rolesView.content} + </> + ) : ( + rolesView.mobileList + )} + </div> + ) : ( + <div className={`${styles.body} ${styles.bodyPanel}`}> + {activeTab === 'overview' && <OverviewTab />} + {activeTab === 'emojis' && <EmojisTab />} + </div> + )} + </div>, + document.body, + ); +} diff --git a/packages/shared/src/components/settings/MobileUserSettings.module.css b/packages/shared/src/components/settings/MobileUserSettings.module.css new file mode 100644 index 0000000..6aa61da --- /dev/null +++ b/packages/shared/src/components/settings/MobileUserSettings.module.css @@ -0,0 +1,243 @@ +/* ── Mobile user settings — full-screen overlay ────────────────────── + Mirrors `MobileServerSettings.module.css` so the two settings + surfaces feel consistent on mobile. Top bar with X / back arrow, + user-avatar hero on the category list, then a stack of grouped + rounded cards (Your Account / App Settings / Log Out). Tapping a + row pushes the tab content full-screen with a back arrow. */ + +.overlay { + position: fixed; + inset: 0; + z-index: var(--z-index-modal, 10000); + display: flex; + flex-direction: column; + background-color: var(--background-secondary); + overflow: hidden; +} + +/* ── Top bar ────────────────────────────────────────────────────────*/ + +.topBar { + flex-shrink: 0; + display: grid; + grid-template-columns: 48px 1fr 48px; + align-items: center; + height: 56px; + padding: 0 8px; + border-bottom: 1px solid var(--background-header-secondary); +} + +.topBarButton { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + border: none; + background: transparent; + color: var(--text-primary); + cursor: pointer; + transition: background-color 0.15s; + -webkit-tap-highlight-color: transparent; + margin: 0 auto; +} + +.topBarButton:active { + background-color: var(--background-modifier-hover); +} + +.topBarTitleBlock { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-width: 0; +} + +.topBarTitle { + font-size: 17px; + font-weight: 700; + color: var(--text-primary); + text-align: center; + margin: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 100%; +} + +/* ── Body ───────────────────────────────────────────────────────────*/ + +.body { + flex: 1; + min-height: 0; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + padding: 16px 16px 32px; +} + +.bodyList { + padding-top: 24px; +} + +.bodyPanel { + padding: 16px; +} + +/* ── User hero ─────────────────────────────────────────────────────*/ + +.hero { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + margin-bottom: 28px; +} + +.heroName { + font-size: 18px; + font-weight: 700; + color: var(--text-primary); + margin: 0; + text-align: center; +} + +.heroHandle { + font-size: 13px; + color: var(--text-primary-muted); + margin: 0; + text-align: center; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + padding: 0 16px; +} + +/* ── Section / cards ───────────────────────────────────────────────*/ + +.section + .section { + margin-top: 18px; +} + +.sectionLabel { + font-size: 13px; + font-weight: 500; + color: var(--text-primary-muted); + padding: 0 4px 8px; +} + +.tabList { + background-color: var(--background-secondary-alt); + border-radius: 0.75rem; + overflow: hidden; +} + +.tabItem { + position: relative; + display: grid; + grid-template-columns: 32px 1fr auto; + align-items: center; + gap: 12px; + width: 100%; + padding: 16px; + background: transparent; + border: none; + color: var(--text-primary); + font: inherit; + font-size: 15px; + font-weight: 600; + text-align: left; + cursor: pointer; + transition: background-color 0.1s; + -webkit-tap-highlight-color: transparent; +} + +.tabItem:active { + background-color: var(--background-modifier-hover); +} + +.tabItemIcon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 50%; + background-color: var(--background-secondary); + color: var(--text-primary); +} + +.tabItemCaret { + color: var(--text-primary-muted); +} + +.tabItem:not(:last-child)::after { + content: ''; + position: absolute; + left: calc(16px + 32px + 12px); + right: 16px; + bottom: 0; + height: 1px; + background-color: var(--background-header-secondary); + pointer-events: none; +} + +/* ── Log Out button (danger card) ───────────────────────────────────*/ + +.dangerCard { + background-color: var(--background-secondary-alt); + border-radius: 0.75rem; + overflow: hidden; +} + +.dangerItem { + display: grid; + grid-template-columns: 32px 1fr; + align-items: center; + gap: 12px; + width: 100%; + padding: 16px; + background: transparent; + border: none; + font: inherit; + font-size: 15px; + font-weight: 600; + text-align: left; + cursor: pointer; + color: hsl(350, calc(90% * var(--saturation-factor)), 65%); + -webkit-tap-highlight-color: transparent; + transition: background-color 0.1s; +} + +.dangerItem:active { + background-color: var(--background-modifier-hover); +} + +.dangerItemIcon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 50%; + background-color: var(--background-secondary); + color: hsl(350, calc(90% * var(--saturation-factor)), 65%); +} + +/* ── Tab panel stub fallback ────────────────────────────────────────*/ + +.stubTitle { + margin: 0 0 8px; + font-size: 18px; + font-weight: 700; + color: var(--text-primary); +} + +.stubBody { + margin: 0; + font-size: 14px; + color: var(--text-primary-muted); +} diff --git a/packages/shared/src/components/settings/MobileUserSettings.tsx b/packages/shared/src/components/settings/MobileUserSettings.tsx new file mode 100644 index 0000000..e786976 --- /dev/null +++ b/packages/shared/src/components/settings/MobileUserSettings.tsx @@ -0,0 +1,289 @@ +/** + * MobileUserSettings — full-screen stand-in for UserSettingsModal on + * mobile. Ported from the new UI: top bar with X / back button, a + * user-avatar hero on the category list, then grouped rounded cards + * listing each tab. Tapping a row replaces the body with that tab's + * content and the top bar's X swaps for a back arrow. + * + * Reuses the existing tab components (`AccountTab`, `AppearanceTab`, + * `VoiceTab`, `SecurityTab`, plus `KeybindsTab`) exported from + * UserSettingsModal so the underlying form logic — profile editing, + * avatar upload, accent color, voice test, recovery key — is + * identical to desktop. + */ +import { useEffect, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { useQuery } from 'convex/react'; +import { + Bell, + CaretLeft, + CaretRight, + Microphone, + PaintBrush, + ShieldCheck, + SignOut, + User as UserIcon, + X, +} from '@phosphor-icons/react'; +import { Avatar } from '@discord-clone/ui'; +import { api } from '../../../../../convex/_generated/api'; +import { useOnlineUsers } from '../../contexts/PresenceContext'; +import { useLogout } from '../../hooks/useLogout'; +import { LogoutConfirmModal } from '../modals/LogoutConfirmModal'; +import { + AccountTab, + AppearanceTab, + SecurityTab, + VoiceTab, +} from './UserSettingsModal'; +import styles from './MobileUserSettings.module.css'; + +// Keybinds are deliberately omitted from the mobile settings — mobile +// doesn't have a physical keyboard workflow, so the whole tab would +// be dead weight. Desktop still shows it via UserSettingsModal. +type TabId = + | 'account' + | 'appearance' + | 'voice' + | 'notifications' + | 'security'; + +interface MobileUserSettingsProps { + isOpen: boolean; + onClose: () => void; + initialTab?: TabId; +} + +interface MobileSettingsTabMeta { + id: TabId; + label: string; + icon: React.ComponentType<{ + size?: number; + weight?: 'regular' | 'fill' | 'bold'; + }>; +} + +interface MobileSettingsGroup { + category: string; + tabs: MobileSettingsTabMeta[]; +} + +const TAB_GROUPS: MobileSettingsGroup[] = [ + { + category: 'Your Account', + tabs: [ + { id: 'account', label: 'My Account', icon: UserIcon }, + { id: 'security', label: 'Security & Login', icon: ShieldCheck }, + ], + }, + { + category: 'App Settings', + tabs: [ + { id: 'appearance', label: 'Appearance', icon: PaintBrush }, + { id: 'voice', label: 'Voice & Video', icon: Microphone }, + { id: 'notifications', label: 'Notifications', icon: Bell }, + ], + }, +]; + +function mapPresence( + status: string | undefined, +): 'online' | 'idle' | 'dnd' | 'offline' { + switch (status) { + case 'online': + return 'online'; + case 'idle': + return 'idle'; + case 'dnd': + return 'dnd'; + default: + return 'offline'; + } +} + +export function MobileUserSettings({ + isOpen, + onClose, + initialTab, +}: MobileUserSettingsProps) { + // `null` means we're on the category list. A non-null tab id + // shows that tab's panel full-screen with a back arrow in the + // top bar. + const [activeTab, setActiveTab] = useState<TabId | null>( + initialTab ?? null, + ); + const logout = useLogout(); + const [showLogoutConfirm, setShowLogoutConfirm] = useState(false); + + useEffect(() => { + if (isOpen) setActiveTab(initialTab ?? null); + }, [isOpen, initialTab]); + + // Lock body scroll while the overlay is open. + useEffect(() => { + if (!isOpen) return; + const prev = document.body.style.overflow; + document.body.style.overflow = 'hidden'; + return () => { + document.body.style.overflow = prev; + }; + }, [isOpen]); + + // Escape — pop one level (panel → list → close). + useEffect(() => { + if (!isOpen) return; + const handler = (e: KeyboardEvent) => { + if (e.key !== 'Escape') return; + if (activeTab !== null) setActiveTab(null); + else onClose(); + }; + document.addEventListener('keydown', handler); + return () => document.removeEventListener('keydown', handler); + }, [isOpen, activeTab, onClose]); + + const localUserId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const allUsers = useQuery(api.auth.getPublicKeys, isOpen ? {} : 'skip') ?? []; + const me = allUsers.find((u) => u.id === localUserId); + const { resolveStatus } = useOnlineUsers(); + + if (!isOpen) return null; + + const displayName = me?.displayName || me?.username || 'You'; + const username = me?.username || ''; + const avatarUrl = me?.avatarUrl ?? null; + const presence = mapPresence( + localUserId + ? resolveStatus(me?.status ?? 'offline', localUserId) + : 'offline', + ); + + const allTabs = TAB_GROUPS.flatMap((g) => g.tabs); + const activeMeta = activeTab ? allTabs.find((t) => t.id === activeTab) : undefined; + + const goBack = () => setActiveTab(null); + const backIsClose = activeTab === null; + + const renderPanel = () => { + switch (activeTab) { + case 'account': + return <AccountTab />; + case 'security': + return <SecurityTab />; + case 'voice': + return <VoiceTab />; + case 'appearance': + return <AppearanceTab />; + case 'notifications': + return ( + <div> + <h2 className={styles.stubTitle}>Notifications</h2> + <p className={styles.stubBody}> + Notification settings coming soon. + </p> + </div> + ); + default: + return null; + } + }; + + return createPortal( + <> + <div className={styles.overlay} role="dialog" aria-modal="true"> + <div className={styles.topBar}> + <button + type="button" + className={styles.topBarButton} + onClick={backIsClose ? onClose : goBack} + aria-label={backIsClose ? 'Close' : 'Back'} + > + {backIsClose ? ( + <X size={22} weight="bold" /> + ) : ( + <CaretLeft size={22} weight="bold" /> + )} + </button> + <div className={styles.topBarTitleBlock}> + <h2 className={styles.topBarTitle}> + {activeMeta ? activeMeta.label : 'Settings'} + </h2> + </div> + <div /> + </div> + + {activeTab === null ? ( + <div className={`${styles.body} ${styles.bodyList}`}> + <div className={styles.hero}> + <Avatar + src={avatarUrl} + fallback={displayName} + size={88} + status={presence} + /> + <h3 className={styles.heroName}>{displayName}</h3> + {username && <p className={styles.heroHandle}>@{username}</p>} + </div> + + {TAB_GROUPS.map((group) => ( + <div key={group.category} className={styles.section}> + <div className={styles.sectionLabel}>{group.category}</div> + <div className={styles.tabList}> + {group.tabs.map((tab) => { + const Icon = tab.icon; + return ( + <button + key={tab.id} + type="button" + className={styles.tabItem} + onClick={() => setActiveTab(tab.id)} + > + <span className={styles.tabItemIcon}> + <Icon size={18} weight="fill" /> + </span> + <span>{tab.label}</span> + <CaretRight + size={16} + weight="bold" + className={styles.tabItemCaret} + /> + </button> + ); + })} + </div> + </div> + ))} + + <div className={styles.section}> + <div className={styles.dangerCard}> + <button + type="button" + className={styles.dangerItem} + onClick={() => setShowLogoutConfirm(true)} + > + <span className={styles.dangerItemIcon}> + <SignOut size={18} weight="fill" /> + </span> + <span>Log Out</span> + </button> + </div> + </div> + </div> + ) : ( + <div className={`${styles.body} ${styles.bodyPanel}`}> + {renderPanel()} + </div> + )} + </div> + <LogoutConfirmModal + isOpen={showLogoutConfirm} + onClose={() => setShowLogoutConfirm(false)} + onConfirm={() => { + setShowLogoutConfirm(false); + void logout(); + }} + /> + </>, + document.body, + ); +} diff --git a/packages/shared/src/components/settings/OverviewTab.module.css b/packages/shared/src/components/settings/OverviewTab.module.css new file mode 100644 index 0000000..d89cad8 --- /dev/null +++ b/packages/shared/src/components/settings/OverviewTab.module.css @@ -0,0 +1,316 @@ +/* ── OverviewTab — server name + icon editor ───────────────────────── + Shared between desktop ServerSettingsModal and mobile MobileServerSettings. + The component branches its layout off `mobile` so the same form + logic powers both styles. Mobile variant uses the now-familiar + rounded-card pattern; desktop uses the existing inline form look. */ + +.root { + display: flex; + flex-direction: column; + gap: 18px; +} + +.heading { + font-size: 20px; + font-weight: 700; + color: var(--text-primary); + margin: 0; +} + +/* ── Icon picker ────────────────────────────────────────────────────*/ + +.iconBlock { + display: flex; + align-items: center; + gap: 16px; +} + +.iconPreviewWrap { + position: relative; + flex-shrink: 0; +} + +.iconPreview { + width: 96px; + height: 96px; + border-radius: 24px; + object-fit: cover; + background-color: var(--background-secondary-alt); + display: block; +} + +.iconInitials { + width: 96px; + height: 96px; + border-radius: 24px; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--background-secondary-alt); + color: var(--text-primary); + font-size: 32px; + font-weight: 700; +} + +.iconBadge { + position: absolute; + right: -2px; + bottom: -2px; + width: 28px; + height: 28px; + border-radius: 50%; + background-color: var(--brand-primary); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + border: 3px solid var(--background-secondary); + pointer-events: none; +} + +.iconActions { + display: flex; + flex-direction: column; + gap: 8px; + min-width: 0; +} + +.iconActionsHelp { + font-size: 12px; + color: var(--text-primary-muted); + margin: 0; +} + +.iconButton { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 14px; + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.1)); + border-radius: 8px; + background-color: var(--background-secondary-alt); + color: var(--text-primary); + font: inherit; + font-size: 13px; + font-weight: 600; + cursor: pointer; + transition: background-color 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.iconButton:active { + background-color: var(--background-modifier-hover); +} + +.iconButtonDanger { + color: hsl(350, calc(90% * var(--saturation-factor)), 65%); + border-color: hsla(350, calc(90% * var(--saturation-factor)), 65%, 0.3); +} + +/* ── Name field ─────────────────────────────────────────────────────*/ + +.fieldLabel { + font-size: 12px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--text-primary-muted); + margin: 0 0 4px; +} + +.nameInput { + width: 100%; + padding: 12px 14px; + background-color: var(--background-secondary-alt); + border: 1px solid transparent; + border-radius: 8px; + color: var(--text-primary); + font: inherit; + font-size: 15px; + font-weight: 500; + outline: none; + box-shadow: none; + -webkit-appearance: none; + appearance: none; +} + +.nameInput:focus, +.nameInput:focus-visible { + border-color: var(--brand-primary); + outline: none; +} + +/* ── Save / status row ──────────────────────────────────────────────*/ + +.actions { + display: flex; + gap: 8px; + justify-content: flex-end; + margin-top: 4px; +} + +.saveButton { + padding: 10px 18px; + background-color: var(--brand-primary); + border: none; + border-radius: 8px; + color: #fff; + font: inherit; + font-size: 14px; + font-weight: 700; + cursor: pointer; + transition: filter 0.15s; + -webkit-tap-highlight-color: transparent; +} + +.saveButton:active:not(:disabled) { + filter: brightness(0.92); +} + +.saveButton:disabled { + opacity: 0.5; + cursor: default; +} + +.status { + padding: 10px 14px; + border-radius: 0.5rem; + font-size: 13px; +} + +.statusSuccess { + background-color: hsl(139, calc(47.3% * var(--saturation-factor)), 20%); + color: hsl(139, calc(47.3% * var(--saturation-factor)), 85%); +} + +.statusError { + background-color: hsl(0, calc(60% * var(--saturation-factor)), 22%); + color: hsl(0, calc(80% * var(--saturation-factor)), 85%); +} + +.gate { + padding: 32px 16px; + text-align: center; +} + +.gateTitle { + font-size: 18px; + font-weight: 700; + color: var(--text-primary); + margin: 0 0 8px; +} + +.gateBody { + font-size: 14px; + color: var(--text-primary-muted); + margin: 0; +} + +/* ── Mobile variant overrides ─────────────────────────────────────── + When `mobile` is true the component renders the same widgets but + with the rounded-card chrome the rest of the mobile settings tabs + use. The inline overrides below tweak spacing / colors so it sits + inside MobileServerSettings comfortably. */ + +.mobileRoot { + gap: 16px; +} + +.mobileIconBlock { + flex-direction: column; + align-items: center; + gap: 12px; +} + +.mobileNameCard { + background-color: var(--background-secondary); + border-radius: 0.75rem; + padding: 14px 16px; +} + +.mobileNameCard .nameInput { + background: transparent; + padding: 0; + font-size: 16px; + font-weight: 600; + border: none; +} + +.mobileNameCard .nameInput:focus, +.mobileNameCard .nameInput:focus-visible { + border: none; +} + +.mobileFieldLabel { + padding: 0 4px 6px; + margin: 0; +} + +.mobileSaveButton { + width: 100%; + padding: 14px; + font-size: 15px; + border-radius: 0.75rem; +} + +.mobileActions { + margin-top: 0; +} + +/* ── Idle / AFK settings ─────────────────────────────────────── */ + +.idleSection { + display: flex; + flex-direction: column; + gap: 8px; + padding-top: 12px; + border-top: 1px solid var(--background-modifier-accent); +} + +.idleHeading { + font-size: 15px; + font-weight: 700; + color: var(--text-primary); +} + +.idleDescription { + font-size: 13px; + color: var(--text-secondary); + margin: 0 0 4px 0; +} + +.idleRow { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 16px; +} + +@media (max-width: 768px) { + .idleRow { + grid-template-columns: 1fr; + } +} + +.idleField { + display: flex; + flex-direction: column; + gap: 4px; +} + +.idleSelect { + height: 40px; + padding: 0 12px; + border-radius: var(--radius-md, 6px); + background-color: var(--background-tertiary); + color: var(--text-primary); + border: none; + font-size: 0.9375rem; + font-family: inherit; + outline: none; + cursor: pointer; +} + +.idleSelect:focus { + outline: 2px solid var(--brand-primary); + outline-offset: -2px; +} diff --git a/packages/shared/src/components/settings/OverviewTab.tsx b/packages/shared/src/components/settings/OverviewTab.tsx new file mode 100644 index 0000000..82e89c2 --- /dev/null +++ b/packages/shared/src/components/settings/OverviewTab.tsx @@ -0,0 +1,369 @@ +import ChannelStore from '@app/stores/ChannelStore'; +import ServerStore from '@app/stores/ServerStore'; +import { MatrixClientManager, SpaceManager } from '@brycord/matrix-client'; +import { PencilSimple, Trash, UploadSimple } from '@phosphor-icons/react'; +/** + * OverviewTab — Server Settings → Overview. Lets an admin rename + * the space and change its avatar (icon). Both writes go through + * `SpaceManager`, which sends the canonical Matrix `m.room.name` + * and `m.room.avatar` state events on the space room — so the + * change is interoperable with Element / Cinny / any other Matrix + * client viewing the same space. + * + * Renders the same form on desktop and mobile; the `mobile` prop + * just swaps the chrome to the rounded-card style the rest of the + * mobile settings tabs use. Logic is identical across both. + * + * Permission gate uses `SpaceManager.canManageSpaceProfile`, which + * checks the local user's power level against the room's + * `state_default` (and any per-event overrides) for both + * `m.room.name` and `m.room.avatar`. + */ +import { observer } from 'mobx-react-lite'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import styles from './OverviewTab.module.css'; + +const AFK_TIMEOUT_OPTIONS: { label: string; value: number }[] = [ + { label: '1 minute', value: 60 * 1000 }, + { label: '5 minutes', value: 5 * 60 * 1000 }, + { label: '15 minutes', value: 15 * 60 * 1000 }, + { label: '30 minutes', value: 30 * 60 * 1000 }, + { label: '1 hour', value: 60 * 60 * 1000 }, +]; + +interface OverviewTabProps { + serverId: string; + mobile?: boolean; +} + +function getInitials(name: string): string { + return name + .split(/\s+/) + .map((w) => w[0]) + .join('') + .slice(0, 2) + .toUpperCase(); +} + +export const OverviewTab = observer(function OverviewTab({ serverId, mobile = false }: OverviewTabProps) { + const server = ServerStore.getServer(serverId); + + const canManage = useMemo(() => { + try { + return SpaceManager.getInstance().canManageSpaceProfile(serverId); + } catch { + return false; + } + }, [serverId]); + + // Form state — seeded from the live server snapshot. We track a + // "pending" file separately from the committed avatar so the user + // can preview their pick before tapping Save. + const [name, setName] = useState(server?.name ?? ''); + const [pendingIconFile, setPendingIconFile] = useState<File | null>(null); + const [pendingPreviewUrl, setPendingPreviewUrl] = useState<string | null>(null); + // `removeIcon` is a flag set when the user clicks "Remove Icon" so + // we can send an empty `m.room.avatar` content on save without + // also wiping a freshly-picked file. + const [removeIcon, setRemoveIcon] = useState(false); + // AFK settings. `null` means "no AFK channel configured". + // `initialAfk*` tracks the committed values so we can diff on save. + const [afkChannelId, setAfkChannelId] = useState<string | null>(null); + const [afkTimeoutMs, setAfkTimeoutMs] = useState<number>(5 * 60 * 1000); + const [initialAfkChannelId, setInitialAfkChannelId] = useState<string | null>(null); + const [initialAfkTimeoutMs, setInitialAfkTimeoutMs] = useState<number>(5 * 60 * 1000); + const [saving, setSaving] = useState(false); + const [status, setStatus] = useState<{ type: 'success' | 'error'; message: string } | null>(null); + const fileInputRef = useRef<HTMLInputElement>(null); + + // Reset state whenever the server name in the store changes + // underneath us (e.g. another client renamed it). + useEffect(() => { + setName(server?.name ?? ''); + }, [server?.name]); + + // Load the currently-configured AFK settings when the server + // switches. Reads synchronously from the space's in-memory + // state event — no network round-trip. + useEffect(() => { + const current = SpaceManager.getInstance().getAfkSettings(serverId); + const channelId = current?.channelId ?? null; + const timeoutMs = current?.timeoutMs ?? 5 * 60 * 1000; + setAfkChannelId(channelId); + setAfkTimeoutMs(timeoutMs); + setInitialAfkChannelId(channelId); + setInitialAfkTimeoutMs(timeoutMs); + }, [serverId]); + + // Cleanup the temporary preview URL when the component unmounts + // or the user picks a different file. + useEffect(() => { + return () => { + if (pendingPreviewUrl) URL.revokeObjectURL(pendingPreviewUrl); + }; + }, [pendingPreviewUrl]); + + if (!server) { + return ( + <div className={styles.gate}> + <h2 className={styles.gateTitle}>Server not found</h2> + <p className={styles.gateBody}>This server is no longer available.</p> + </div> + ); + } + + if (!canManage) { + return ( + <div className={styles.gate}> + <h2 className={styles.gateTitle}>Overview</h2> + <p className={styles.gateBody}> + You need permission to manage this server. Ask an admin to grant you a role with a higher power level. + </p> + </div> + ); + } + + const trimmedName = name.trim(); + const nameDirty = trimmedName !== (server.name ?? ''); + const iconDirty = pendingIconFile !== null || removeIcon; + const afkDirty = afkChannelId !== initialAfkChannelId || afkTimeoutMs !== initialAfkTimeoutMs; + const isDirty = nameDirty || iconDirty || afkDirty; + const nameValid = trimmedName.length > 0 && trimmedName.length <= 100; + const canSave = isDirty && nameValid && !saving; + + // List of voice channels in this server for the AFK dropdown. + // Filtered live from ChannelStore so newly-created voice channels + // appear without needing a remount. + const voiceChannels = ChannelStore.getVoiceChannels(serverId); + + const handlePickFile = () => { + fileInputRef.current?.click(); + }; + + const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { + const file = e.target.files?.[0]; + if (fileInputRef.current) fileInputRef.current.value = ''; + if (!file) return; + setStatus(null); + + if (!file.type.startsWith('image/')) { + setStatus({ + type: 'error', + message: 'Server icon must be an image file.', + }); + return; + } + if (file.size > 10 * 1024 * 1024) { + setStatus({ + type: 'error', + message: 'Server icon must be smaller than 10 MB.', + }); + return; + } + + if (pendingPreviewUrl) URL.revokeObjectURL(pendingPreviewUrl); + setPendingIconFile(file); + setPendingPreviewUrl(URL.createObjectURL(file)); + // Picking a new file overrides any pending "remove" flag. + setRemoveIcon(false); + }; + + const handleRemoveIcon = () => { + if (pendingPreviewUrl) URL.revokeObjectURL(pendingPreviewUrl); + setPendingIconFile(null); + setPendingPreviewUrl(null); + setRemoveIcon(true); + setStatus(null); + }; + + const handleSave = async () => { + if (!canSave) return; + setSaving(true); + setStatus(null); + try { + const sm = SpaceManager.getInstance(); + // Avatar mutation first so the optimistic store update at + // the end has both fields in their final state. + let newIconHttp: string | undefined = server.icon; + if (pendingIconFile) { + const mxc = await sm.setSpaceAvatar(serverId, pendingIconFile); + if (mxc) { + // Match the same dimensioned thumbnail SpaceManager + // produces so the local server entry stays in sync + // with what a fresh sync would compute. + const client = MatrixClientManager.getInstance().getClient(); + newIconHttp = client.mxcUrlToHttp(mxc, 128, 128, 'crop') ?? undefined; + } + } else if (removeIcon) { + await sm.setSpaceAvatar(serverId, null); + newIconHttp = undefined; + } + + if (nameDirty) { + await sm.renameSpace(serverId, trimmedName); + } + + if (afkDirty) { + await sm.setAfkSettings(serverId, afkChannelId, afkTimeoutMs); + setInitialAfkChannelId(afkChannelId); + setInitialAfkTimeoutMs(afkTimeoutMs); + } + + // Optimistic local update so the sidebar / hero refresh + // instantly instead of waiting for the next sync tick. + ServerStore.handleServerUpdate({ + ...server, + name: nameDirty ? trimmedName : server.name, + icon: newIconHttp, + }); + + if (pendingPreviewUrl) URL.revokeObjectURL(pendingPreviewUrl); + setPendingIconFile(null); + setPendingPreviewUrl(null); + setRemoveIcon(false); + setStatus({ type: 'success', message: 'Saved.' }); + } catch (err: any) { + setStatus({ + type: 'error', + message: err?.message || 'Failed to save changes.', + }); + } finally { + setSaving(false); + } + }; + + // Decide which preview src to render: pending file > current + // server icon > nothing (initials fallback). When the user has + // queued an icon removal, force the initials path. + const showInitials = removeIcon || (!pendingPreviewUrl && !server.icon); + const previewSrc = pendingPreviewUrl ?? server.icon; + + const rootClass = mobile ? `${styles.root} ${styles.mobileRoot}` : styles.root; + const iconBlockClass = mobile ? `${styles.iconBlock} ${styles.mobileIconBlock}` : styles.iconBlock; + const fieldLabelClass = mobile ? `${styles.fieldLabel} ${styles.mobileFieldLabel}` : styles.fieldLabel; + const saveButtonClass = mobile ? `${styles.saveButton} ${styles.mobileSaveButton}` : styles.saveButton; + const actionsClass = mobile ? `${styles.actions} ${styles.mobileActions}` : styles.actions; + + return ( + <div className={rootClass}> + {!mobile && <h2 className={styles.heading}>Overview</h2>} + + <div className={iconBlockClass}> + <div className={styles.iconPreviewWrap}> + {showInitials ? ( + <div className={styles.iconInitials}>{getInitials(server.name)}</div> + ) : ( + <img src={previewSrc} alt={server.name} className={styles.iconPreview} draggable={false} /> + )} + <div className={styles.iconBadge}> + <PencilSimple size={12} weight="bold" /> + </div> + </div> + <div className={styles.iconActions}> + <p className={styles.iconActionsHelp}> + We recommend an image of at least 512×512 — JPEG, PNG, GIF, or WebP, up to 10 MB. + </p> + <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}> + <button type="button" className={styles.iconButton} onClick={handlePickFile} disabled={saving}> + <UploadSimple size={14} weight="bold" /> + {server.icon || pendingPreviewUrl ? 'Change Icon' : 'Upload Icon'} + </button> + {(server.icon || pendingPreviewUrl) && ( + <button + type="button" + className={`${styles.iconButton} ${styles.iconButtonDanger}`} + onClick={handleRemoveIcon} + disabled={saving} + > + <Trash size={14} weight="bold" /> + Remove + </button> + )} + </div> + </div> + </div> + <input + ref={fileInputRef} + type="file" + accept="image/png,image/jpeg,image/gif,image/webp" + onChange={handleFileChange} + style={{ display: 'none' }} + /> + + <div> + <div className={fieldLabelClass}>Server Name</div> + {mobile ? ( + <div className={styles.mobileNameCard}> + <input + type="text" + className={styles.nameInput} + value={name} + onChange={(e) => setName(e.target.value)} + maxLength={100} + placeholder="My Server" + /> + </div> + ) : ( + <input + type="text" + className={styles.nameInput} + value={name} + onChange={(e) => setName(e.target.value)} + maxLength={100} + placeholder="My Server" + /> + )} + </div> + + <div className={styles.idleSection}> + <div className={styles.idleHeading}>Idle Settings</div> + <p className={styles.idleDescription}> + Move members to an AFK voice channel when they go idle. They'll be automatically muted on move. + </p> + <div className={styles.idleRow}> + <div className={styles.idleField}> + <div className={fieldLabelClass}>AFK / Idle Channel</div> + <select + className={styles.idleSelect} + value={afkChannelId ?? ''} + onChange={(e) => setAfkChannelId(e.target.value || null)} + > + <option value="">No AFK Channel</option> + {voiceChannels.map((ch) => ( + <option key={ch.id} value={ch.id}> + {ch.name} + </option> + ))} + </select> + </div> + <div className={styles.idleField}> + <div className={fieldLabelClass}>AFK Timeout</div> + <select + className={styles.idleSelect} + value={afkTimeoutMs} + onChange={(e) => setAfkTimeoutMs(Number(e.target.value))} + > + {AFK_TIMEOUT_OPTIONS.map((opt) => ( + <option key={opt.value} value={opt.value}> + {opt.label} + </option> + ))} + </select> + </div> + </div> + </div> + + {status && ( + <div className={`${styles.status} ${status.type === 'error' ? styles.statusError : styles.statusSuccess}`}> + {status.message} + </div> + )} + + <div className={actionsClass}> + <button type="button" className={saveButtonClass} onClick={handleSave} disabled={!canSave}> + {saving ? 'Saving…' : 'Save Changes'} + </button> + </div> + </div> + ); +}); diff --git a/packages/shared/src/components/settings/RoleEditor.module.css b/packages/shared/src/components/settings/RoleEditor.module.css new file mode 100644 index 0000000..c3ce7b3 --- /dev/null +++ b/packages/shared/src/components/settings/RoleEditor.module.css @@ -0,0 +1,244 @@ +.container { + display: flex; + flex-direction: column; + gap: 18px; + max-width: 560px; +} + +.header { + display: flex; + align-items: center; + justify-content: space-between; +} + +.heading { + margin: 0; + font-size: 18px; + font-weight: 700; + color: var(--text-primary, #fff); +} + +.field { + display: flex; + flex-direction: column; + gap: 6px; + position: relative; +} + +.label { + display: block; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--text-primary-muted, #a0a3a8); +} + +.input { + width: 100%; + padding: 10px 12px; + background-color: var(--background-tertiary, rgba(0, 0, 0, 0.3)); + border: 1px solid transparent; + border-radius: 6px; + color: var(--text-primary, #fff); + font-size: 14px; + font-family: inherit; + outline: none; + box-sizing: border-box; + transition: border-color 0.1s; +} + +.input:focus { + border-color: var(--brand-primary, #5865f2); +} + +.counter { + position: absolute; + top: 0; + right: 0; + font-size: 11px; + color: var(--text-primary-muted, #a0a3a8); +} + +.counterBad { + color: var(--status-danger, #da373c); +} + +.swatchRow { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.swatch { + width: 28px; + height: 28px; + border-radius: 50%; + border: 2px solid transparent; + cursor: pointer; + padding: 0; + display: inline-flex; + align-items: center; + justify-content: center; + transition: border-color 0.1s, transform 0.1s; +} + +.swatch:hover { + transform: scale(1.1); +} + +.swatchActive { + border-color: var(--text-primary, #fff); +} + +.swatchNone { + background-color: var(--background-tertiary, rgba(0, 0, 0, 0.3)); + color: var(--text-primary-muted, #a0a3a8); +} + +.hexInput { + width: 120px; + padding: 6px 10px; + background-color: var(--background-tertiary, rgba(0, 0, 0, 0.3)); + border: 1px solid transparent; + border-radius: 6px; + color: var(--text-primary, #fff); + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 12px; + outline: none; + box-sizing: border-box; +} + +.hexInput:focus { + border-color: var(--brand-primary, #5865f2); +} + +.powerHeader { + display: flex; + align-items: center; + justify-content: space-between; +} + +.powerValue { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 14px; + font-weight: 600; + color: var(--text-primary, #fff); +} + +.slider { + width: 100%; + accent-color: var(--brand-primary, #5865f2); +} + +.powerHint { + margin: 0; + font-size: 12px; + line-height: 1.45; + color: var(--text-primary-muted, #a0a3a8); +} + +.abilities { + padding: 12px 14px; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.03)); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); + border-radius: 6px; +} + +.abilitiesHeader { + font-size: 13px; + color: var(--text-secondary, #a0a3a8); + margin-bottom: 8px; +} + +.abilitiesHeader strong { + color: var(--text-primary, #fff); +} + +.abilitiesList { + list-style: none; + margin: 0; + padding: 0; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4px 14px; +} + +.abilitiesList li { + display: flex; + align-items: center; + gap: 6px; + font-size: 12.5px; +} + +.abilityOn { + color: var(--text-primary, #fff); +} + +.abilityOn svg { + color: #2ecc71; +} + +.abilityOff { + color: var(--text-primary-muted, #a0a3a8); +} + +.abilityOff svg { + color: var(--text-primary-muted, #a0a3a8); +} + +.status { + padding: 8px 12px; + border-radius: 6px; + font-size: 13px; +} + +.statusSuccess { + background-color: rgba(46, 204, 113, 0.15); + border: 1px solid rgba(46, 204, 113, 0.4); + color: var(--text-primary, #fff); +} + +.statusError { + background-color: rgba(234, 80, 80, 0.15); + border: 1px solid rgba(234, 80, 80, 0.4); + color: var(--text-primary, #fff); +} + +.actions { + display: flex; + justify-content: space-between; + gap: 12px; + padding-top: 4px; +} + +.readOnlyNote { + padding: 14px 16px; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.03)); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); + border-radius: 6px; + font-size: 13px; + line-height: 1.5; + color: var(--text-secondary, #a0a3a8); +} + +.readOnlyNote p { + margin: 0; +} + +.readOnlyNote p + p { + margin-top: 10px; +} + +.readOnlyNote strong { + color: var(--text-primary, #fff); +} + +.readOnlyNote code { + padding: 1px 4px; + background-color: var(--background-tertiary, rgba(0, 0, 0, 0.3)); + border-radius: 3px; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 11.5px; + color: var(--text-primary, #fff); +} diff --git a/packages/shared/src/components/settings/RoleEditor.tsx b/packages/shared/src/components/settings/RoleEditor.tsx new file mode 100644 index 0000000..5367a7a --- /dev/null +++ b/packages/shared/src/components/settings/RoleEditor.tsx @@ -0,0 +1,304 @@ +/** + * RoleEditor — right-hand panel of the Roles tab. Lets the admin + * edit a single role's name, color, and power level. Below the PL + * slider there's a live "abilities readout" driven by + * RoleManager.describePowerLevel, so admins see exactly which + * homeserver-enforced actions the chosen PL grants. + * + * Changes are saved via an explicit Save button — the form is dirty- + * tracked so Save is disabled until the user has made a real change. + * Deletion is its own button at the bottom with a confirm prompt. + * + * The @everyone entry passes `readOnly` and gets a short explanation + * instead of an editable form, since @everyone is synthetic and its + * permissions come from the room's baseline power level defaults. + */ +import { observer } from 'mobx-react-lite'; +import { useEffect, useMemo, useState } from 'react'; +import { Trash, CheckCircle, XCircle } from '@phosphor-icons/react'; +import { Button } from '@brycord/ui'; +import { RoleManager } from '@brycord/matrix-client'; +import type { Role, PowerLevelAbilities } from '@brycord/matrix-client'; +import RoleStore from '@app/stores/RoleStore'; +import styles from './RoleEditor.module.css'; + +interface RoleEditorProps { + serverId: string; + role: Role; + readOnly?: boolean; +} + +// A small, curated palette of Discord-like role colors plus a "no +// color" swatch. Admins can always type a custom hex too. +const COLOR_SWATCHES: Array<{ hex: string; name: string }> = [ + { hex: '#99AAB5', name: 'Default' }, + { hex: '#1ABC9C', name: 'Teal' }, + { hex: '#2ECC71', name: 'Green' }, + { hex: '#3498DB', name: 'Blue' }, + { hex: '#9B59B6', name: 'Purple' }, + { hex: '#E91E63', name: 'Magenta' }, + { hex: '#F1C40F', name: 'Yellow' }, + { hex: '#E67E22', name: 'Orange' }, + { hex: '#E74C3C', name: 'Red' }, + { hex: '#95A5A6', name: 'Gray' }, + { hex: '#607D8B', name: 'Slate' }, +]; + +const HEX_REGEX = /^#[0-9a-fA-F]{6}$/; + +export const RoleEditor = observer(function RoleEditor({ serverId, role, readOnly }: RoleEditorProps) { + const [name, setName] = useState(role.name); + const [color, setColor] = useState<string | undefined>(role.color); + const [powerLevel, setPowerLevel] = useState(role.powerLevel); + const [saving, setSaving] = useState(false); + const [deleting, setDeleting] = useState(false); + const [status, setStatus] = useState<{ type: 'success' | 'error'; message: string } | null>(null); + const [confirmDelete, setConfirmDelete] = useState(false); + + // Reset local form state whenever the selected role changes. + useEffect(() => { + setName(role.name); + setColor(role.color); + setPowerLevel(role.powerLevel); + setStatus(null); + setConfirmDelete(false); + }, [role.id]); + + // Compute what abilities the current slider PL unlocks — reads the + // space's live `m.room.power_levels` via RoleManager so the list + // reflects reality (admins may have customized thresholds). + const abilities: PowerLevelAbilities = useMemo( + () => RoleManager.getInstance().describePowerLevel(serverId, powerLevel), + [serverId, powerLevel], + ); + + const trimmedName = name.trim(); + const isDirty = + trimmedName !== role.name || + (color ?? undefined) !== (role.color ?? undefined) || + powerLevel !== role.powerLevel; + const nameValid = trimmedName.length > 0 && trimmedName.length <= 32; + const canSave = isDirty && nameValid && !saving && !readOnly; + + const handleSave = async () => { + if (!canSave) return; + setSaving(true); + setStatus(null); + try { + await RoleManager.getInstance().updateRole(serverId, role.id, { + name: trimmedName, + color: color, + powerLevel, + }); + // Optimistically update the store so the left-column list + // reflects the new values immediately. + RoleStore.handleRoleUpdated(serverId, { + ...role, + name: trimmedName, + color, + powerLevel, + }); + setStatus({ type: 'success', message: 'Role saved.' }); + } catch (err: any) { + setStatus({ type: 'error', message: err?.message || 'Failed to save role.' }); + } finally { + setSaving(false); + } + }; + + const handleDelete = async () => { + if (readOnly || deleting) return; + if (!confirmDelete) { + setConfirmDelete(true); + return; + } + setDeleting(true); + setStatus(null); + try { + await RoleManager.getInstance().deleteRole(serverId, role.id); + RoleStore.handleRoleDeleted(serverId, role.id); + } catch (err: any) { + setStatus({ type: 'error', message: err?.message || 'Failed to delete role.' }); + setDeleting(false); + } + }; + + if (readOnly) { + return ( + <div className={styles.container}> + <div className={styles.header}> + <h2 className={styles.heading}>{role.name}</h2> + </div> + <div className={styles.readOnlyNote}> + <p> + <strong>@everyone</strong> is the baseline role every member + has. Its abilities come from the server's default power + level thresholds in <code>m.room.power_levels</code>, not + from a per-role configuration. + </p> + <p> + To change what @everyone can do, edit the baseline power + level values directly (channel-level permission editing + will arrive in a future update). + </p> + </div> + <AbilitiesReadout powerLevel={0} abilities={ + RoleManager.getInstance().describePowerLevel(serverId, 0) + } /> + </div> + ); + } + + return ( + <div className={styles.container}> + <div className={styles.header}> + <h2 className={styles.heading}>Edit Role</h2> + </div> + + <div className={styles.field}> + <label className={styles.label}> + Role Name + <input + type="text" + className={styles.input} + value={name} + onChange={(e) => setName(e.target.value)} + maxLength={40} + placeholder="new role" + /> + </label> + <span className={`${styles.counter} ${trimmedName.length > 32 ? styles.counterBad : ''}`}> + {trimmedName.length}/32 + </span> + </div> + + <div className={styles.field}> + <span className={styles.label}>Role Color</span> + <div className={styles.swatchRow}> + <button + type="button" + className={`${styles.swatch} ${!color ? styles.swatchActive : ''} ${styles.swatchNone}`} + onClick={() => setColor(undefined)} + aria-label="No color" + > + <XCircle size={16} weight="bold" /> + </button> + {COLOR_SWATCHES.map((s) => ( + <button + key={s.hex} + type="button" + className={`${styles.swatch} ${color === s.hex ? styles.swatchActive : ''}`} + style={{ backgroundColor: s.hex }} + onClick={() => setColor(s.hex)} + aria-label={s.name} + /> + ))} + </div> + <input + type="text" + className={styles.hexInput} + value={color ?? ''} + placeholder="#5865F2" + onChange={(e) => { + const val = e.target.value; + if (val === '' || HEX_REGEX.test(val)) { + setColor(val || undefined); + } else if (val.startsWith('#') && val.length <= 7) { + // Let the user type hex chars without immediate validation + setColor(val); + } + }} + /> + </div> + + <div className={styles.field}> + <div className={styles.powerHeader}> + <span className={styles.label}>Power Level</span> + <span className={styles.powerValue}>{powerLevel}</span> + </div> + <input + type="range" + min={0} + max={100} + value={powerLevel} + onChange={(e) => setPowerLevel(Number(e.target.value))} + className={styles.slider} + /> + <p className={styles.powerHint}> + Power level determines what members with this role can do + at the Matrix protocol layer. Multiple roles can share the + same power level — they'll have identical abilities but + different names and colors. + </p> + </div> + + <AbilitiesReadout powerLevel={powerLevel} abilities={abilities} /> + + {status && ( + <div className={`${styles.status} ${status.type === 'error' ? styles.statusError : styles.statusSuccess}`}> + {status.message} + </div> + )} + + <div className={styles.actions}> + <Button variant="primary" onClick={handleSave} disabled={!canSave}> + {saving ? 'Saving…' : 'Save Role'} + </Button> + <Button + variant="danger" + onClick={handleDelete} + disabled={deleting} + > + <Trash size={14} weight="fill" /> + {confirmDelete ? 'Click again to confirm' : 'Delete Role'} + </Button> + </div> + </div> + ); +}); + +// ── Abilities readout ────────────────────────────────────────────── + +const ABILITY_LABELS: Array<{ key: keyof PowerLevelAbilities; label: string }> = [ + { key: 'canSendMessages', label: 'Send messages' }, + { key: 'canInvite', label: 'Invite members' }, + { key: 'canRenameNicknames', label: 'Rename other members' }, + { key: 'canRedact', label: "Delete others' messages" }, + { key: 'canKick', label: 'Kick members' }, + { key: 'canBan', label: 'Ban members' }, + { key: 'canSendState', label: 'Send room state events' }, + { key: 'canRenameChannels', label: 'Rename channels' }, + { key: 'canManageRoles', label: 'Manage roles' }, + { key: 'canEditPowerLevels', label: 'Edit server power levels' }, +]; + +function AbilitiesReadout({ + powerLevel, + abilities, +}: { + powerLevel: number; + abilities: PowerLevelAbilities; +}) { + return ( + <div className={styles.abilities}> + <div className={styles.abilitiesHeader}> + At power level <strong>{powerLevel}</strong>, members with this role can: + </div> + <ul className={styles.abilitiesList}> + {ABILITY_LABELS.map(({ key, label }) => { + const allowed = abilities[key]; + return ( + <li key={key} className={allowed ? styles.abilityOn : styles.abilityOff}> + {allowed ? ( + <CheckCircle size={14} weight="fill" /> + ) : ( + <XCircle size={14} weight="fill" /> + )} + <span>{label}</span> + </li> + ); + })} + </ul> + </div> + ); +} diff --git a/packages/shared/src/components/settings/RolesTab.module.css b/packages/shared/src/components/settings/RolesTab.module.css new file mode 100644 index 0000000..cb02f69 --- /dev/null +++ b/packages/shared/src/components/settings/RolesTab.module.css @@ -0,0 +1,143 @@ +.container { + display: flex; + gap: 20px; + height: 100%; + min-height: 440px; +} + +.list { + flex: 0 0 240px; + display: flex; + flex-direction: column; + gap: 10px; + min-height: 0; +} + +.listHeader { + display: flex; + align-items: center; + justify-content: space-between; +} + +.listTitle { + font-size: 11px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--text-primary-muted, #a0a3a8); +} + +.listCount { + font-size: 11px; + color: var(--text-primary-muted, #a0a3a8); + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; +} + +.rolesList { + display: flex; + flex-direction: column; + gap: 2px; + overflow-y: auto; + min-height: 0; + flex: 1 1 auto; +} + +.roleItem { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 12px; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.03)); + border: 1px solid transparent; + border-radius: 6px; + cursor: pointer; + user-select: none; + transition: background-color 0.1s, border-color 0.1s; +} + +.roleItem:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.06)); +} + +.roleItemSelected { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.08)); + border-color: var(--brand-primary, #5865f2); +} + +.roleItemDragOver { + border-color: var(--brand-primary, #5865f2); + background-color: rgba(88, 101, 242, 0.12); +} + +.roleItemEveryone { + opacity: 0.7; + margin-top: 8px; + border-top: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); + padding-top: 12px; +} + +.roleDot { + width: 12px; + height: 12px; + border-radius: 50%; + flex-shrink: 0; +} + +.roleName { + flex: 1 1 auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 14px; + font-weight: 500; + color: var(--text-primary, #fff); +} + +.rolePower { + flex-shrink: 0; + font-size: 11px; + color: var(--text-primary-muted, #a0a3a8); + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; +} + +.editor { + flex: 1 1 auto; + min-width: 0; +} + +.placeholder { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + color: var(--text-primary-muted, #a0a3a8); + font-size: 14px; +} + +.gate { + padding: 60px 20px; + text-align: center; +} + +.gateTitle { + margin: 0 0 8px; + font-size: 18px; + font-weight: 700; + color: var(--text-primary, #fff); +} + +.gateBody { + margin: 0 auto; + max-width: 360px; + font-size: 14px; + color: var(--text-secondary, #a0a3a8); +} + +.error { + padding: 8px 12px; + background-color: rgba(234, 80, 80, 0.15); + border: 1px solid rgba(234, 80, 80, 0.4); + border-radius: 6px; + color: var(--text-primary, #fff); + font-size: 12px; +} diff --git a/packages/shared/src/components/settings/RolesTab.tsx b/packages/shared/src/components/settings/RolesTab.tsx new file mode 100644 index 0000000..bdfc681 --- /dev/null +++ b/packages/shared/src/components/settings/RolesTab.tsx @@ -0,0 +1,218 @@ +/** + * RolesTab — left column lists every role in the server (with + * @everyone pinned at the bottom), right column is the RoleEditor for + * the currently selected role. The create button at the top spawns a + * new role with default values and immediately selects it. + * + * Role reordering is via native HTML5 drag-and-drop — no new deps. + * `@everyone` is non-draggable, non-selectable, non-deletable, and + * has its own "read-only" notice in the editor. + */ +import { observer } from 'mobx-react-lite'; +import { useEffect, useMemo, useState } from 'react'; +import { Plus } from '@phosphor-icons/react'; +import { Button } from '@brycord/ui'; +import { RoleManager } from '@brycord/matrix-client'; +import type { Role } from '@brycord/matrix-client'; +import { MAX_ROLES_PER_SERVER } from '@brycord/constants'; +import RoleStore from '@app/stores/RoleStore'; +import SelectionStore from '@app/stores/SelectionStore'; +import { RoleEditor } from './RoleEditor'; +import { MobileRolesTab } from './MobileRolesTab'; +import styles from './RolesTab.module.css'; + +interface RolesTabProps { + serverId: string; +} + +export const RolesTab = observer(function RolesTab({ serverId }: RolesTabProps) { + // Mobile gets a completely different full-screen layout — a + // Fluxer-style list of rounded cards that pushes the role editor + // as a sub-screen. Delegates to a separate component so the + // desktop hooks below don't run on the mobile path (and vice + // versa), which keeps React's rules-of-hooks happy. + if (SelectionStore.isMobileViewport) { + return <MobileRolesTab serverId={serverId} />; + } + return <DesktopRolesTab serverId={serverId} />; +}); + +const DesktopRolesTab = observer(function DesktopRolesTab({ serverId }: RolesTabProps) { + const [selectedRoleId, setSelectedRoleId] = useState<string | null>(null); + const [error, setError] = useState<string | null>(null); + const [creating, setCreating] = useState(false); + const [dragOverId, setDragOverId] = useState<string | null>(null); + + const canManage = useMemo(() => { + try { + return RoleManager.getInstance().canManageRoles(serverId); + } catch { + return false; + } + }, [serverId]); + + // All roles including synthetic @everyone. Everyone is at index 0 + // in the store; split it out for rendering. + const allRoles = RoleStore.getRoles(serverId); + const editableRoles = allRoles + .filter((r) => r.id !== 'everyone') + .sort((a, b) => b.position - a.position); // show highest at the top + const everyone = allRoles.find((r) => r.id === 'everyone'); + + // Auto-select the first editable role when one exists and nothing + // is currently selected. + useEffect(() => { + if (selectedRoleId) return; + if (editableRoles.length > 0) setSelectedRoleId(editableRoles[0].id); + }, [editableRoles, selectedRoleId]); + + // If the currently selected role was deleted, fall back. + useEffect(() => { + if (!selectedRoleId) return; + if (!allRoles.some((r) => r.id === selectedRoleId)) { + setSelectedRoleId(editableRoles[0]?.id ?? null); + } + }, [allRoles, editableRoles, selectedRoleId]); + + if (!canManage) { + return ( + <div className={styles.gate}> + <h2 className={styles.gateTitle}>Manage Roles</h2> + <p className={styles.gateBody}> + You need permission to manage roles in this server. Ask an + admin to grant you a role with a higher power level. + </p> + </div> + ); + } + + const handleCreate = async () => { + if (creating) return; + if (editableRoles.length >= MAX_ROLES_PER_SERVER) { + setError(`A server can have at most ${MAX_ROLES_PER_SERVER} roles.`); + return; + } + setCreating(true); + setError(null); + try { + const role = await RoleManager.getInstance().createRole(serverId, { + name: 'New Role', + powerLevel: 0, + }); + RoleStore.handleRoleCreated(serverId, role); + setSelectedRoleId(role.id); + } catch (err: any) { + setError(err?.message || 'Failed to create role.'); + } finally { + setCreating(false); + } + }; + + const handleDragStart = (e: React.DragEvent, roleId: string) => { + e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.setData('text/plain', roleId); + }; + + const handleDragOver = (e: React.DragEvent, overId: string) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + setDragOverId(overId); + }; + + const handleDrop = async (e: React.DragEvent, dropOnId: string) => { + e.preventDefault(); + setDragOverId(null); + const draggedId = e.dataTransfer.getData('text/plain'); + if (!draggedId || draggedId === dropOnId) return; + const order = editableRoles.map((r) => r.id); + const fromIdx = order.indexOf(draggedId); + const toIdx = order.indexOf(dropOnId); + if (fromIdx === -1 || toIdx === -1) return; + const next = [...order]; + next.splice(fromIdx, 1); + next.splice(toIdx, 0, draggedId); + // Since the list is displayed with highest position first, + // reverse to match the RoleManager's ascending position order. + const orderedIds = [...next].reverse(); + try { + await RoleManager.getInstance().reorderRoles(serverId, orderedIds); + } catch (err: any) { + setError(err?.message || 'Failed to reorder roles.'); + } + }; + + const selectedRole: Role | undefined = selectedRoleId + ? allRoles.find((r) => r.id === selectedRoleId) + : undefined; + + return ( + <div className={styles.container}> + <div className={styles.list}> + <div className={styles.listHeader}> + <span className={styles.listTitle}>Roles</span> + <span className={styles.listCount}> + {editableRoles.length}/{MAX_ROLES_PER_SERVER} + </span> + </div> + <Button + variant="primary" + onClick={handleCreate} + disabled={creating || editableRoles.length >= MAX_ROLES_PER_SERVER} + > + <Plus size={14} weight="bold" /> Create Role + </Button> + + <div className={styles.rolesList}> + {editableRoles.map((role) => { + const selected = role.id === selectedRoleId; + return ( + <div + key={role.id} + draggable + onDragStart={(e) => handleDragStart(e, role.id)} + onDragOver={(e) => handleDragOver(e, role.id)} + onDragLeave={() => setDragOverId((id) => (id === role.id ? null : id))} + onDrop={(e) => handleDrop(e, role.id)} + onClick={() => setSelectedRoleId(role.id)} + className={`${styles.roleItem} ${selected ? styles.roleItemSelected : ''} ${dragOverId === role.id ? styles.roleItemDragOver : ''}`} + > + <span + className={styles.roleDot} + style={{ backgroundColor: role.color || 'var(--text-primary-muted, #a0a3a8)' }} + /> + <span className={styles.roleName}>{role.name}</span> + <span className={styles.rolePower}>PL {role.powerLevel}</span> + </div> + ); + })} + {everyone && ( + <div + className={`${styles.roleItem} ${styles.roleItemEveryone} ${selectedRoleId === 'everyone' ? styles.roleItemSelected : ''}`} + onClick={() => setSelectedRoleId('everyone')} + > + <span className={styles.roleDot} style={{ backgroundColor: 'var(--text-primary-muted, #a0a3a8)' }} /> + <span className={styles.roleName}>@everyone</span> + <span className={styles.rolePower}>PL 0</span> + </div> + )} + </div> + + {error && <div className={styles.error}>{error}</div>} + </div> + + <div className={styles.editor}> + {selectedRole ? ( + <RoleEditor + serverId={serverId} + role={selectedRole} + readOnly={selectedRole.id === 'everyone'} + /> + ) : ( + <div className={styles.placeholder}> + Select a role on the left to edit, or create a new one. + </div> + )} + </div> + </div> + ); +}); diff --git a/packages/shared/src/components/settings/RolesView.module.css b/packages/shared/src/components/settings/RolesView.module.css new file mode 100644 index 0000000..d549701 --- /dev/null +++ b/packages/shared/src/components/settings/RolesView.module.css @@ -0,0 +1,696 @@ +/* ── Custom sidebar (replaces the default tab list) ──────────── */ + +.sidebarInner { + display: flex; + flex-direction: column; + gap: 8px; + padding: 16px 12px 24px; +} + +.backButton { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + border: none; + border-radius: 6px; + background: transparent; + color: var(--text-secondary); + font: inherit; + font-size: 0.8125rem; + font-weight: 600; + cursor: pointer; + transition: background-color 0.12s, color 0.12s; +} + +.backButton:hover { + background: var(--background-modifier-hover); + color: var(--text-primary); +} + +.sidebarSectionTitle { + padding: 6px 12px 2px; + font-size: 0.6875rem; + font-weight: 700; + color: var(--text-tertiary); + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.createButton { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 8px 12px; + border: 1px solid var(--background-header-secondary); + border-radius: 6px; + background: var(--background-tertiary); + color: var(--text-primary); + font: inherit; + font-size: 0.8125rem; + font-weight: 600; + cursor: pointer; + transition: background-color 0.12s, border-color 0.12s; +} + +.createButton:hover { + background: var(--background-modifier-hover); + border-color: var(--background-modifier-accent); +} + +.createButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.roleList { + display: flex; + flex-direction: column; + gap: 2px; +} + +.roleItem { + display: flex; + align-items: center; + gap: 10px; + width: 100%; + padding: 8px 12px; + border: 1px solid transparent; + border-radius: 6px; + background: transparent; + color: var(--text-primary); + font: inherit; + font-size: 0.875rem; + font-weight: 500; + text-align: left; + cursor: pointer; + transition: background-color 0.12s, border-color 0.12s; +} + +.roleItem:hover { + background: var(--background-modifier-hover); +} + +.roleItemActive, +.roleItemActive:hover { + background: var(--background-modifier-selected, var(--background-modifier-hover)); + border-color: var(--brand-primary, #5865f2); +} + +.roleDot { + width: 10px; + height: 10px; + border-radius: 50%; + flex-shrink: 0; +} + +.roleName { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.emptyNote { + padding: 8px 12px; + font-size: 0.8125rem; + color: var(--text-tertiary); +} + +/* ── Main editor column ──────────────────────────────────────── */ + +.editorEmpty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + gap: 8px; + color: var(--text-tertiary); + font-size: 0.9375rem; +} + +.headerRow { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; + margin-bottom: 24px; +} + +.headerText { + display: flex; + flex-direction: column; + gap: 4px; + min-width: 0; +} + +.headerTitle { + font-size: 1.25rem; + font-weight: 700; + color: var(--text-primary); + margin: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.headerSubtitle { + font-size: 0.8125rem; + color: var(--text-tertiary); + margin: 0; +} + +.deleteButton { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 14px; + border: 1px solid var(--background-header-secondary); + border-radius: 6px; + background: var(--background-tertiary); + color: var(--text-primary); + font: inherit; + font-size: 0.8125rem; + font-weight: 600; + cursor: pointer; + flex-shrink: 0; + transition: background-color 0.12s, border-color 0.12s, color 0.12s; +} + +.deleteButton:hover { + background: rgba(237, 66, 69, 0.12); + border-color: rgba(237, 66, 69, 0.4); + color: var(--status-danger, #ed4245); +} + +.deleteButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* ── Section headings ────────────────────────────────────────── */ + +.sectionHeading { + font-size: 0.6875rem; + font-weight: 700; + color: var(--text-tertiary); + text-transform: uppercase; + letter-spacing: 0.04em; + margin: 24px 0 10px; +} + +.sectionHeading:first-of-type { + margin-top: 0; +} + +/* ── Display row ─────────────────────────────────────────────── */ + +.displayRow { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 16px; + margin-bottom: 24px; +} + +.fieldGroup { + display: flex; + flex-direction: column; + gap: 6px; +} + +.fieldLabel { + font-size: 0.75rem; + font-weight: 700; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.02em; +} + +.textInput { + width: 100%; + padding: 10px 12px; + border: 1px solid var(--background-header-secondary); + border-radius: 6px; + background: var(--background-tertiary); + color: var(--text-primary); + font: inherit; + font-size: 0.9375rem; + outline: none; + transition: border-color 0.12s; +} + +.textInput:focus { + border-color: var(--brand-primary); +} + +.textInput:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +/* Color field wraps the hex input + a native color picker button so + the user can either type a colour or pick one. */ + +.colorWrap { + position: relative; + display: flex; + align-items: center; +} + +.colorInput { + padding-right: 42px; +} + +.colorSwatchButton { + position: absolute; + right: 6px; + top: 50%; + transform: translateY(-50%); + width: 30px; + height: 30px; + padding: 0; + border: 1px solid var(--background-header-secondary); + border-radius: 6px; + background: transparent; + cursor: pointer; + overflow: hidden; +} + +.colorSwatchInner { + display: block; + width: 100%; + height: 100%; +} + +.hiddenColorInput { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + opacity: 0; + cursor: pointer; + border: none; + padding: 0; + background: none; +} + +.fieldHelp { + font-size: 0.75rem; + color: var(--text-tertiary); + margin-top: 2px; +} + +/* ── Toggle row (hoist + mentionable) ────────────────────────── */ + +.toggleRow { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 14px 0; + border-top: 1px solid var(--background-modifier-accent); +} + +.toggleRow:first-of-type { + border-top: none; +} + +.toggleText { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 3px; +} + +.toggleTitle { + font-size: 0.875rem; + font-weight: 600; + color: var(--text-primary); +} + +.toggleDescription { + font-size: 0.8125rem; + color: var(--text-tertiary); +} + +.toggle { + position: relative; + width: 40px; + height: 22px; + border: none; + border-radius: 999px; + background: var(--background-header-secondary); + cursor: pointer; + flex-shrink: 0; + transition: background-color 0.15s; +} + +.toggleOn { + background: var(--brand-primary, #5865f2); +} + +.toggleThumb { + position: absolute; + top: 3px; + left: 3px; + width: 16px; + height: 16px; + border-radius: 50%; + background: #ffffff; + transition: transform 0.15s ease; +} + +.toggleOn .toggleThumb { + transform: translateX(18px); +} + +.toggle:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* ── Clear permissions row ───────────────────────────────────── */ + +.clearRow { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; + margin: 16px 0 8px; + padding: 14px 0 0; + border-top: 1px solid var(--background-modifier-accent); +} + +.clearText { + flex: 1; + font-size: 0.8125rem; + color: var(--text-tertiary); + margin: 0; + line-height: 1.4; +} + +.clearButton { + padding: 8px 14px; + border: 1px solid var(--background-header-secondary); + border-radius: 6px; + background: var(--background-tertiary); + color: var(--text-primary); + font: inherit; + font-size: 0.8125rem; + font-weight: 600; + cursor: pointer; + flex-shrink: 0; + transition: background-color 0.12s, border-color 0.12s; +} + +.clearButton:hover { + background: var(--background-modifier-hover); +} + +.clearButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* ── Permission search row ───────────────────────────────────── */ + +.searchRow { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 12px; +} + +.searchWrap { + position: relative; + flex: 1; + min-width: 0; +} + +.searchIcon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: var(--text-tertiary); + pointer-events: none; +} + +.searchInput { + width: 100%; + padding: 10px 12px 10px 36px; + border: 1px solid var(--background-header-secondary); + border-radius: 6px; + background: var(--background-tertiary); + color: var(--text-primary); + font: inherit; + font-size: 0.875rem; + outline: none; + transition: border-color 0.12s; +} + +.searchInput:focus { + border-color: var(--brand-primary); +} + +/* ── Permissions list ────────────────────────────────────────── */ + +.permissionList { + display: flex; + flex-direction: column; +} + +.permissionRow { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; + padding: 14px 0; + border-top: 1px solid var(--background-modifier-accent); +} + +.permissionRow:first-of-type { + border-top: none; +} + +.permissionInfo { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 4px; +} + +.permissionTitle { + font-size: 0.9375rem; + font-weight: 600; + color: var(--text-primary); +} + +.permissionDescription { + font-size: 0.8125rem; + color: var(--text-tertiary); + line-height: 1.4; +} + +.permissionEmpty { + padding: 24px 0; + text-align: center; + font-size: 0.875rem; + color: var(--text-tertiary); +} + +.errorBanner { + padding: 10px 12px; + margin-bottom: 16px; + background: rgba(234, 80, 80, 0.12); + border: 1px solid rgba(234, 80, 80, 0.4); + border-radius: 6px; + color: var(--text-primary); + font-size: 0.8125rem; +} + +/* ── Unsaved-changes bar (replaces the modal content header) ── */ + +.unsavedBar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + width: 100%; + padding: 0; +} + +.unsavedText { + font-size: 0.9375rem; + font-weight: 700; + color: var(--text-primary); + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.unsavedActions { + display: flex; + align-items: center; + gap: 10px; + flex-shrink: 0; +} + +.resetButton { + padding: 8px 18px; + border: none; + border-radius: 6px; + background: var(--background-tertiary); + color: var(--text-primary); + font: inherit; + font-size: 0.8125rem; + font-weight: 600; + cursor: pointer; + transition: background-color 0.12s; +} + +.resetButton:hover:not(:disabled) { + background: var(--background-modifier-hover); +} + +.resetButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.saveButton { + padding: 8px 18px; + border: none; + border-radius: 6px; + background: var(--brand-primary, #5865f2); + color: #ffffff; + font: inherit; + font-size: 0.8125rem; + font-weight: 600; + cursor: pointer; + transition: filter 0.12s; +} + +.saveButton:hover:not(:disabled) { + filter: brightness(1.1); +} + +.saveButton:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +/* ── Mobile full-screen role list ────────────────────────────── */ + +.mobileList { + display: flex; + flex-direction: column; + gap: 10px; + padding: 16px; +} + +.mobileListHeader { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-bottom: 6px; +} + +.mobileListTitle { + font-size: 1.125rem; + font-weight: 700; + color: var(--text-primary); + margin: 0; +} + +.mobileCreateButton { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 10px 14px; + border: none; + border-radius: 8px; + background: var(--background-tertiary); + color: var(--text-primary); + font: inherit; + font-size: 0.8125rem; + font-weight: 600; + cursor: pointer; + flex-shrink: 0; + transition: background-color 0.12s; +} + +.mobileCreateButton:hover { + background: var(--background-modifier-hover); +} + +.mobileRoleRow { + display: flex; + align-items: center; + gap: 12px; + width: 100%; + padding: 14px 16px; + border: 1px solid var(--background-header-secondary); + border-radius: 10px; + background: var(--background-secondary); + color: var(--text-primary); + font: inherit; + font-size: 0.9375rem; + font-weight: 600; + text-align: left; + cursor: pointer; + transition: background-color 0.12s; +} + +.mobileRoleRow:hover, +.mobileRoleRow:active { + background: var(--background-modifier-hover); +} + +.mobileRoleRowName { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.mobileRoleRowCaret { + color: var(--text-tertiary); + flex-shrink: 0; +} + +/* ── Mobile editor back-to-roles row ─────────────────────────── */ + +.mobileBackRow { + display: flex; + margin-bottom: 12px; +} + +.mobileBackButton { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 14px; + border: 1px solid var(--background-header-secondary); + border-radius: 6px; + background: var(--background-tertiary); + color: var(--text-primary); + font: inherit; + font-size: 0.8125rem; + font-weight: 600; + cursor: pointer; + transition: background-color 0.12s; +} + +.mobileBackButton:hover { + background: var(--background-modifier-hover); +} diff --git a/packages/shared/src/components/settings/RolesView.tsx b/packages/shared/src/components/settings/RolesView.tsx new file mode 100644 index 0000000..875f6e7 --- /dev/null +++ b/packages/shared/src/components/settings/RolesView.tsx @@ -0,0 +1,735 @@ +/** + * RolesView — full "Roles & Permissions" editor used inside + * ServerSettingsModal. Takes over the whole settings surface: the + * sidebar shows a back-to-settings button + create-role + role list + * (not the tab list), and the main column shows the editor for the + * selected role (display + toggles + permissions). + * + * Layout / visual language is ported from the Fluxer GuildRolesTab + * screenshot the user shared, adapted to our Convex role schema: + * - Single server, no mentionable field in our schema (omitted) + * - Owner role is immutable and never shows here (filtered out) + * - Saves are direct `api.roles.update` calls — no explicit Save + * button / unsaved-changes banner; reactive queries update the + * other UI surfaces instantly. + */ +import { useMemo, useState, type ReactNode } from 'react'; +import { useMutation, useQuery } from 'convex/react'; +import { + DndContext, + type DragEndEvent, + PointerSensor, + closestCenter, + useSensor, + useSensors, +} from '@dnd-kit/core'; +import { + SortableContext, + useSortable, + verticalListSortingStrategy, + arrayMove, +} from '@dnd-kit/sortable'; +import { CSS } from '@dnd-kit/utilities'; +import { + CaretLeft, + CaretRight, + MagnifyingGlass, + Plus, + Trash, +} from '@phosphor-icons/react'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import styles from './RolesView.module.css'; + +interface RoleDoc { + _id: Id<'roles'>; + name: string; + color: string; + position?: number; + permissions?: Record<string, boolean>; + isHoist?: boolean; +} + +interface UseRolesViewOptions { + onBack: () => void; +} + +interface RolesViewSlots { + sidebar: ReactNode; + content: ReactNode; + /** + * Optional override for the modal's content header. When the + * user has uncommitted edits across any role, this renders a + * yellow "unsaved changes" banner with Reset / Save Changes + * buttons (replacing the default title + close button). When + * there are no drafts, it's `null` and the caller falls back + * to its default header row. + */ + header: ReactNode | null; + hasChanges: boolean; + /** + * Full-screen mobile role list — shown by `MobileServerSettings` + * instead of the desktop sidebar when `activeTab === 'roles'` + * and no role is yet selected. Tapping a row flips `selectedId` + * via the hook's own state, so the mobile caller only needs to + * check `selectedId` to decide whether to show this or the + * editor. + */ + mobileList: ReactNode; + /** Currently selected role id (null if nothing selected). */ + selectedId: string | null; + /** Clear the current selection — used by the mobile "Back to + * Roles" control to pop back to the list view. */ + clearSelection: () => void; +} + +/** + * Pending edits for a single role. Only fields the user touched + * are present; a field set to `undefined` (or missing) means "use + * the server value". Saving walks the Map and only sends the + * defined fields to `api.roles.update`. + */ +interface RoleDraft { + name?: string; + color?: string; + isHoist?: boolean; + permissions?: Record<string, boolean>; +} + +interface PermissionMeta { + key: string; + label: string; + description: string; +} + +// Our backend currently ships a small permission set — labels and +// descriptions are local metadata, not part of the schema, so new +// permissions added server-side must also be added here to be +// editable in the UI. +const PERMISSIONS: PermissionMeta[] = [ + { + key: 'manage_channels', + label: 'Manage Channels', + description: 'Create, edit, or delete channels and categories.', + }, + { + key: 'manage_roles', + label: 'Manage Roles', + description: 'Create, edit, or delete roles below your highest role.', + }, + { + key: 'manage_messages', + label: 'Manage Messages', + description: 'Delete messages by other members and pin any message.', + }, + { + key: 'create_invite', + label: 'Create Invite', + description: 'Invite other people to the server.', + }, + { + key: 'embed_links', + label: 'Embed Links', + description: 'Render link previews for URLs sent in messages.', + }, + { + key: 'attach_files', + label: 'Attach Files', + description: 'Upload images, videos, audio, and other files.', + }, + { + key: 'move_members', + label: 'Move Members', + description: 'Move members between voice channels.', + }, + { + key: 'mute_members', + label: 'Mute Members', + description: 'Server-mute other members in voice channels.', + }, + { + key: 'manage_nicknames', + label: 'Manage Nicknames', + description: 'Change the nicknames of other members.', + }, +]; + +const DEFAULT_COLOR = '#99aab5'; + +function isValidHex(value: string): boolean { + return /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value); +} + +/** + * Sortable sidebar row — wraps the visual role button with the + * dnd-kit sortable bindings so pointer drags move the row in-place. + * The click handler still fires for normal taps because the sensor + * only activates after a 6px drag (configured on the parent). + */ +function SortableRoleRow({ + role, + isActive, + onClick, +}: { + role: RoleDoc; + isActive: boolean; + onClick: () => void; +}) { + const { attributes, listeners, setNodeRef, transform, transition, isDragging } = + useSortable({ id: role._id }); + const style = { + transform: CSS.Transform.toString(transform), + transition, + opacity: isDragging ? 0.6 : undefined, + }; + return ( + <button + ref={setNodeRef} + type="button" + className={`${styles.roleItem} ${isActive ? styles.roleItemActive : ''}`} + style={style} + onClick={onClick} + {...attributes} + {...listeners} + > + <span + className={styles.roleDot} + style={{ background: role.color || DEFAULT_COLOR }} + /> + <span className={styles.roleName}>{role.name}</span> + </button> + ); +} + +/** + * Hook — returns the two JSX slots the caller renders inside the + * settings modal shell. Not a component because it returns a plain + * object, which React components are not allowed to do. + */ +export function useRolesView({ onBack }: UseRolesViewOptions): RolesViewSlots { + const allRoles = (useQuery(api.roles.list, {}) ?? []) as RoleDoc[]; + // Owner is permanently frozen — see convex/roles.ts. It's filtered + // out here so the sidebar never shows it and the editor never + // targets it. Backend mutations reject any Owner edit as a second + // line of defence. + // + // The sidebar renders two groups: + // - `draggableRoles` — everything except Owner and @everyone, in + // descending position order. These are the rows the user can + // drag to reorder. + // - `everyoneRole` — pinned to the bottom of the list regardless + // of its stored position so it always reads as the fallback + // bucket. + const { draggableRoles, everyoneRole } = useMemo(() => { + const everyone = allRoles.find((r) => r.name === '@everyone') ?? null; + const others = allRoles + .filter((r) => r.name !== 'Owner' && r.name !== '@everyone') + .sort((a, b) => (b.position ?? 0) - (a.position ?? 0)); + return { draggableRoles: others, everyoneRole: everyone }; + }, [allRoles]); + + // Flat list used by the editor lookup (by selected id). Keeps + // the @everyone row targetable from the editor without dragging. + const roles = useMemo( + () => (everyoneRole ? [...draggableRoles, everyoneRole] : draggableRoles), + [draggableRoles, everyoneRole], + ); + + const createRole = useMutation(api.roles.create); + const updateRole = useMutation(api.roles.update); + const removeRole = useMutation(api.roles.remove); + const reorderRoles = useMutation(api.roles.reorder); + + // dnd-kit sensors — `distance: 6` means a 6px drag is required + // before a drop is triggered, so a normal click still selects a + // role without accidentally starting a drag. + const sensors = useSensors( + useSensor(PointerSensor, { activationConstraint: { distance: 6 } }), + ); + + const handleDragEnd = async (event: DragEndEvent) => { + const { active, over } = event; + if (!over || active.id === over.id) return; + const oldIndex = draggableRoles.findIndex((r) => r._id === active.id); + const newIndex = draggableRoles.findIndex((r) => r._id === over.id); + if (oldIndex === -1 || newIndex === -1) return; + const nextOrder = arrayMove(draggableRoles, oldIndex, newIndex); + // Rewrite positions top-down. Higher array index = lower + // position number, matching the descending-position sort + // everywhere else. Leaves 10 unit gaps between rows so new + // rows can be inserted later without a full renumber. + const updates = nextOrder.map((r, idx) => ({ + id: r._id, + position: (nextOrder.length - idx) * 10, + })); + setError(null); + try { + await reorderRoles({ updates }); + } catch (err: any) { + setError(err?.message ?? 'Failed to reorder roles'); + } + }; + + const [selectedId, setSelectedId] = useState<Id<'roles'> | null>(null); + const [permissionSearch, setPermissionSearch] = useState(''); + const [drafts, setDrafts] = useState<Map<string, RoleDraft>>(new Map()); + const [saving, setSaving] = useState(false); + const [error, setError] = useState<string | null>(null); + + const selected = useMemo( + () => roles.find((r) => r._id === selectedId) ?? null, + [roles, selectedId], + ); + + /** Merge any pending draft for the given role on top of the + * server state so the editor shows the in-progress values. */ + const getEffectiveRole = (role: RoleDoc): RoleDoc => { + const draft = drafts.get(role._id); + if (!draft) return role; + return { + ...role, + name: draft.name ?? role.name, + color: draft.color ?? role.color, + isHoist: draft.isHoist ?? role.isHoist, + permissions: draft.permissions ?? role.permissions, + }; + }; + + const selectedWithDraft = selected ? getEffectiveRole(selected) : null; + const hasChanges = drafts.size > 0; + + const updateDraft = (roleId: string, changes: Partial<RoleDraft>) => { + setError(null); + setDrafts((prev) => { + const next = new Map(prev); + const existing = next.get(roleId) ?? {}; + next.set(roleId, { ...existing, ...changes }); + return next; + }); + }; + + const handleCreate = async () => { + setError(null); + try { + const created: any = await createRole({ + name: 'new role', + color: DEFAULT_COLOR, + permissions: {}, + isHoist: false, + position: 0, + }); + if (created?._id) setSelectedId(created._id); + } catch (err: any) { + setError(err?.message ?? 'Failed to create role'); + } + }; + + const handleDelete = async () => { + if (!selected) return; + if (!confirm(`Delete role "${selected.name}"?`)) return; + setError(null); + try { + await removeRole({ id: selected._id }); + // Drop any pending draft for this role — it no longer exists. + setDrafts((prev) => { + const next = new Map(prev); + next.delete(selected._id); + return next; + }); + setSelectedId(null); + } catch (err: any) { + setError(err?.message ?? 'Failed to delete role'); + } + }; + + const handleReset = () => { + setDrafts(new Map()); + setError(null); + }; + + const handleSaveChanges = async () => { + if (drafts.size === 0 || saving) return; + setError(null); + setSaving(true); + try { + // Persist every dirty role in parallel. For each draft we + // only forward the fields that were actually touched — an + // invalid hex colour is silently skipped so the user can + // still save the rest of their edits. + await Promise.all( + Array.from(drafts.entries()).map(([id, draft]) => { + const role = allRoles.find((r) => r._id === id); + if (!role) return undefined; + const updates: Record<string, unknown> = { + id: id as Id<'roles'>, + }; + if (draft.name !== undefined) { + const clean = draft.name.trim(); + if (clean) updates.name = clean; + } + if (draft.color !== undefined && isValidHex(draft.color)) { + updates.color = draft.color; + } + if (draft.isHoist !== undefined) { + updates.isHoist = draft.isHoist; + } + if (draft.permissions !== undefined) { + updates.permissions = draft.permissions; + } + return updateRole(updates as any); + }), + ); + setDrafts(new Map()); + } catch (err: any) { + setError(err?.message ?? 'Failed to save changes'); + } finally { + setSaving(false); + } + }; + + const filteredPermissions = useMemo(() => { + const q = permissionSearch.trim().toLowerCase(); + if (!q) return PERMISSIONS; + return PERMISSIONS.filter( + (p) => + p.label.toLowerCase().includes(q) || + p.description.toLowerCase().includes(q), + ); + }, [permissionSearch]); + + const currentPerms = selectedWithDraft?.permissions ?? {}; + + // Render the sidebar + main content. The caller is responsible for + // wrapping these in the modal shell — RolesView returns a fragment + // with two children via the `sidebar` / `content` render props. + return { + sidebar: ( + <div className={styles.sidebarInner}> + <button type="button" className={styles.backButton} onClick={onBack}> + <CaretLeft size={14} weight="bold" /> + Back to Settings + </button> + <div className={styles.sidebarSectionTitle}>Roles</div> + <button + type="button" + className={styles.createButton} + onClick={handleCreate} + > + <Plus size={14} weight="bold" /> + Create Role + </button> + <DndContext + sensors={sensors} + collisionDetection={closestCenter} + onDragEnd={handleDragEnd} + > + <SortableContext + items={draggableRoles.map((r) => r._id)} + strategy={verticalListSortingStrategy} + > + <div className={styles.roleList}> + {draggableRoles.length === 0 && !everyoneRole && ( + <div className={styles.emptyNote}>No custom roles yet.</div> + )} + {draggableRoles.map((role) => ( + <SortableRoleRow + key={role._id} + role={role} + isActive={selectedId === role._id} + onClick={() => setSelectedId(role._id)} + /> + ))} + {everyoneRole && ( + <button + key={everyoneRole._id} + type="button" + className={`${styles.roleItem} ${selectedId === everyoneRole._id ? styles.roleItemActive : ''}`} + onClick={() => setSelectedId(everyoneRole._id)} + > + <span + className={styles.roleDot} + style={{ background: everyoneRole.color || DEFAULT_COLOR }} + /> + <span className={styles.roleName}>{everyoneRole.name}</span> + </button> + )} + </div> + </SortableContext> + </DndContext> + </div> + ), + content: !selected || !selectedWithDraft ? ( + <div className={styles.editorEmpty}> + Select a role on the left to edit it, or create a new one. + </div> + ) : ( + <> + {error && <div className={styles.errorBanner}>{error}</div>} + + <div className={styles.headerRow}> + <div className={styles.headerText}> + <h2 className={styles.headerTitle}> + Edit "{selectedWithDraft.name}" + </h2> + <p className={styles.headerSubtitle}> + Configure role settings and permissions + </p> + </div> + {selected.name !== '@everyone' && ( + <button + type="button" + className={styles.deleteButton} + onClick={handleDelete} + > + <Trash size={14} weight="bold" /> + Delete Role + </button> + )} + </div> + + <div className={styles.sectionHeading}>Display</div> + <div className={styles.displayRow}> + <div className={styles.fieldGroup}> + <label className={styles.fieldLabel} htmlFor="role-name-input"> + Role Name + </label> + <input + id="role-name-input" + className={styles.textInput} + type="text" + value={selectedWithDraft.name} + onChange={(e) => + updateDraft(selected._id, { name: e.target.value }) + } + maxLength={100} + /> + </div> + <div className={styles.fieldGroup}> + <label className={styles.fieldLabel} htmlFor="role-color-input"> + Role Color + </label> + <div className={styles.colorWrap}> + <input + id="role-color-input" + className={`${styles.textInput} ${styles.colorInput}`} + type="text" + value={selectedWithDraft.color || ''} + onChange={(e) => + updateDraft(selected._id, { color: e.target.value }) + } + placeholder="#99aab5" + /> + <button + type="button" + className={styles.colorSwatchButton} + aria-label="Pick a color" + > + <span + className={styles.colorSwatchInner} + style={{ + background: isValidHex(selectedWithDraft.color || '') + ? selectedWithDraft.color + : DEFAULT_COLOR, + }} + /> + <input + className={styles.hiddenColorInput} + type="color" + value={ + isValidHex(selectedWithDraft.color || '') + ? selectedWithDraft.color + : DEFAULT_COLOR + } + onChange={(e) => + updateDraft(selected._id, { color: e.target.value }) + } + /> + </button> + </div> + <p className={styles.fieldHelp}> + Type a color (hex) or use the picker. + </p> + </div> + </div> + + <div className={styles.toggleRow}> + <div className={styles.toggleText}> + <span className={styles.toggleTitle}> + Show this role separately + </span> + <span className={styles.toggleDescription}> + Lists members with this role in their own section in the member + list. + </span> + </div> + <button + type="button" + className={`${styles.toggle} ${selectedWithDraft.isHoist ? styles.toggleOn : ''}`} + onClick={() => + updateDraft(selected._id, { + isHoist: !selectedWithDraft.isHoist, + }) + } + aria-pressed={!!selectedWithDraft.isHoist} + aria-label="Show this role separately" + > + <span className={styles.toggleThumb} /> + </button> + </div> + + <div className={styles.clearRow}> + <p className={styles.clearText}> + Use this button to quickly clear all permissions. + </p> + <button + type="button" + className={styles.clearButton} + onClick={() => updateDraft(selected._id, { permissions: {} })} + > + Clear Permissions + </button> + </div> + + <div className={styles.sectionHeading}>Permissions</div> + <div className={styles.searchRow}> + <div className={styles.searchWrap}> + <MagnifyingGlass + className={styles.searchIcon} + size={16} + weight="regular" + /> + <input + className={styles.searchInput} + type="text" + placeholder="Search Permissions..." + value={permissionSearch} + onChange={(e) => setPermissionSearch(e.target.value)} + /> + </div> + </div> + <div className={styles.permissionList}> + {filteredPermissions.length === 0 ? ( + <div className={styles.permissionEmpty}> + No permissions match your search. + </div> + ) : ( + filteredPermissions.map((perm) => { + const enabled = !!currentPerms[perm.key]; + return ( + <div key={perm.key} className={styles.permissionRow}> + <div className={styles.permissionInfo}> + <span className={styles.permissionTitle}> + {perm.label} + </span> + <span className={styles.permissionDescription}> + {perm.description} + </span> + </div> + <button + type="button" + className={`${styles.toggle} ${enabled ? styles.toggleOn : ''}`} + onClick={() => + updateDraft(selected._id, { + permissions: { ...currentPerms, [perm.key]: !enabled }, + }) + } + aria-pressed={enabled} + aria-label={perm.label} + > + <span className={styles.toggleThumb} /> + </button> + </div> + ); + }) + )} + </div> + </> + ), + header: hasChanges ? ( + <div className={styles.unsavedBar}> + <span className={styles.unsavedText}> + Careful! You have unsaved changes. + </span> + <div className={styles.unsavedActions}> + <button + type="button" + className={styles.resetButton} + onClick={handleReset} + disabled={saving} + > + Reset + </button> + <button + type="button" + className={styles.saveButton} + onClick={handleSaveChanges} + disabled={saving} + > + {saving ? 'Saving…' : 'Save Changes'} + </button> + </div> + </div> + ) : null, + hasChanges, + selectedId, + clearSelection: () => setSelectedId(null), + mobileList: ( + <div className={styles.mobileList}> + {error && <div className={styles.errorBanner}>{error}</div>} + <div className={styles.mobileListHeader}> + <h3 className={styles.mobileListTitle}>Roles</h3> + <button + type="button" + className={styles.mobileCreateButton} + onClick={handleCreate} + > + <Plus size={14} weight="bold" /> + Create Role + </button> + </div> + {draggableRoles.length === 0 && !everyoneRole && ( + <div className={styles.emptyNote}>No custom roles yet.</div> + )} + {draggableRoles.map((role) => ( + <button + key={role._id} + type="button" + className={styles.mobileRoleRow} + onClick={() => setSelectedId(role._id)} + > + <span + className={styles.roleDot} + style={{ background: role.color || DEFAULT_COLOR }} + /> + <span className={styles.mobileRoleRowName}>{role.name}</span> + <CaretRight + size={18} + weight="bold" + className={styles.mobileRoleRowCaret} + /> + </button> + ))} + {everyoneRole && ( + <button + type="button" + className={styles.mobileRoleRow} + onClick={() => setSelectedId(everyoneRole._id)} + > + <span + className={styles.roleDot} + style={{ background: everyoneRole.color || DEFAULT_COLOR }} + /> + <span className={styles.mobileRoleRowName}> + {everyoneRole.name} + </span> + <CaretRight + size={18} + weight="bold" + className={styles.mobileRoleRowCaret} + /> + </button> + )} + </div> + ), + }; +} diff --git a/packages/shared/src/components/settings/ServerSettingsModal.module.css b/packages/shared/src/components/settings/ServerSettingsModal.module.css new file mode 100644 index 0000000..4f4ace7 --- /dev/null +++ b/packages/shared/src/components/settings/ServerSettingsModal.module.css @@ -0,0 +1,84 @@ +.content { + padding: 0; + min-height: 520px; + max-height: 80vh; + overflow: hidden; +} + +.layout { + display: flex; + min-height: 520px; + max-height: 80vh; +} + +.sidebar { + flex: 0 0 180px; + display: flex; + flex-direction: column; + gap: 2px; + padding: 16px 8px; + border-right: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); + background-color: var(--background-secondary, rgba(0, 0, 0, 0.15)); +} + +.tab { + display: flex; + align-items: center; + gap: 10px; + width: 100%; + padding: 8px 12px; + background-color: transparent; + border: none; + border-radius: 6px; + color: var(--text-secondary, #a0a3a8); + font: inherit; + font-size: 14px; + font-weight: 500; + cursor: pointer; + text-align: left; + transition: background-color 0.1s, color 0.1s; +} + +.tab:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.06)); + color: var(--text-primary, #fff); +} + +.tabActive { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.08)); + color: var(--text-primary, #fff); +} + +.tabIcon { + display: inline-flex; + width: 16px; + height: 16px; +} + +.panel { + flex: 1 1 auto; + min-width: 0; + padding: 20px 24px; + overflow-y: auto; +} + +.stub { + padding: 40px 0; + text-align: center; +} + +.stubTitle { + margin: 0 0 8px; + font-size: 18px; + font-weight: 700; + color: var(--text-primary, #fff); +} + +.stubBody { + margin: 0; + font-size: 14px; + color: var(--text-secondary, #a0a3a8); + max-width: 360px; + margin-left: auto; + margin-right: auto; +} diff --git a/packages/shared/src/components/settings/ServerSettingsModal.tsx b/packages/shared/src/components/settings/ServerSettingsModal.tsx new file mode 100644 index 0000000..cbb5e7f --- /dev/null +++ b/packages/shared/src/components/settings/ServerSettingsModal.tsx @@ -0,0 +1,878 @@ +/** + * ServerSettingsModal — full-screen settings overlay (same pattern as + * UserSettingsModal) with Overview / Roles / Emojis tabs, wired to the + * Convex backend. + * + * Opens via the `brycord:open-server-settings` window event dispatched + * from GuildHeaderDropdown / GuildNavbar. All three tabs are inlined + * here so the file is self-contained — the sibling *Tab.tsx files + * still hold the old Matrix-based code and are not imported. + */ +import { useMutation, useQuery } from 'convex/react'; +import { Gear, Plus, ShieldStar, Smiley, Trash, UploadSimple, X } from '@phosphor-icons/react'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import { useIsMobile } from '../../hooks/useIsMobile'; +import { CustomEmojisTab } from './CustomEmojisTab'; +import { MobileServerSettings } from './MobileServerSettings'; +import { useRolesView } from './RolesView'; +import userStyles from './UserSettingsModal.module.css'; + +export type ServerSettingsTab = 'overview' | 'roles' | 'emojis'; + +interface ServerSettingsModalProps { + isOpen: boolean; + onClose: () => void; + initialTab?: ServerSettingsTab; +} + +const TABS: Array<{ id: ServerSettingsTab; label: string; icon: typeof Gear }> = [ + { id: 'overview', label: 'Overview', icon: Gear }, + { id: 'roles', label: 'Roles', icon: ShieldStar }, + { id: 'emojis', label: 'Custom Emoji', icon: Smiley }, +]; + +export function ServerSettingsModal({ isOpen, onClose, initialTab = 'overview' }: ServerSettingsModalProps) { + const [activeTab, setActiveTab] = useState<ServerSettingsTab>(initialTab); + const isMobile = useIsMobile(); + + useEffect(() => { + if (isOpen) setActiveTab(initialTab); + }, [isOpen, initialTab]); + + useEffect(() => { + if (!isOpen) return; + const onKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + document.addEventListener('keydown', onKey); + return () => document.removeEventListener('keydown', onKey); + }, [isOpen, onClose]); + + // Mobile gets the full-screen overlay with a category list ↔ panel + // flow, desktop gets the two-column modal below. + if (isMobile) { + return ( + <MobileServerSettings + isOpen={isOpen} + onClose={onClose} + initialTab={initialTab} + /> + ); + } + + // Roles view takes over the whole settings surface — its own + // sidebar (back + create + role list) and its own main column + // (role editor). We still delegate the back button to flipping + // activeTab back to 'overview' so the outer modal stays open. + const rolesView = useRolesView({ onBack: () => setActiveTab('overview') }); + + if (!isOpen) return null; + + const active = TABS.find((t) => t.id === activeTab) ?? TABS[0]; + const inRolesView = activeTab === 'roles'; + + return createPortal( + <div className={userStyles.overlay} onClick={onClose}> + <div className={userStyles.modal} onClick={(e) => e.stopPropagation()}> + <nav className={userStyles.sidebar}> + {inRolesView ? ( + rolesView.sidebar + ) : ( + <div className={userStyles.sidebarInner}> + <div className={userStyles.sidebarCategoryTitle}>Server Settings</div> + <div className={userStyles.sidebarGroup}> + {TABS.map((tab) => { + const Icon = tab.icon; + const isActive = activeTab === tab.id; + return ( + <button + key={tab.id} + type="button" + className={`${userStyles.sidebarItem} ${isActive ? userStyles.sidebarItemActive : ''}`} + onClick={() => setActiveTab(tab.id)} + > + <span className={userStyles.sidebarItemIcon}> + <Icon size={18} /> + </span> + <span className={userStyles.sidebarItemLabel}>{tab.label}</span> + </button> + ); + })} + </div> + </div> + )} + </nav> + + <div className={userStyles.contentColumn}> + <div className={userStyles.contentHeader}> + {inRolesView && rolesView.header ? ( + // Dirty roles editor takes over the header with a + // Reset / Save Changes bar. The close X is hidden + // until the user either saves or resets their drafts. + rolesView.header + ) : ( + <> + <h1 className={userStyles.contentTitle}> + {inRolesView ? 'Roles & Permissions' : active.label} + </h1> + <button + type="button" + className={userStyles.closeButton} + onClick={onClose} + aria-label="Close settings" + > + <X size={22} weight="bold" /> + </button> + </> + )} + </div> + <div className={userStyles.contentScroll}> + <div className={userStyles.contentInner}> + {activeTab === 'overview' && <OverviewTab />} + {inRolesView && rolesView.content} + {activeTab === 'emojis' && <CustomEmojisTab />} + </div> + </div> + </div> + </div> + </div>, + document.body, + ); +} + +/* ------------------------------------------------------------------- */ +/* Overview */ +/* ------------------------------------------------------------------- */ + +export function OverviewTab() { + const userId = + typeof localStorage !== 'undefined' ? (localStorage.getItem('userId') as Id<'userProfiles'> | null) : null; + const settings = useQuery(api.serverSettings.get, {}) as + | { + serverName?: string; + iconUrl?: string | null; + afkChannelId?: Id<'channels'> | null; + afkTimeout?: number; + } + | null + | undefined; + const channels = useQuery(api.channels.list, {}) ?? []; + const updateSettings = useMutation(api.serverSettings.update); + + const voiceChannels = useMemo( + () => channels.filter((c: any) => c.type === 'voice'), + [channels], + ); + + const [afkChannelId, setAfkChannelId] = useState<string>(''); + const [afkTimeout, setAfkTimeout] = useState<number>(300); + const [status, setStatus] = useState<{ type: 'ok' | 'err'; message: string } | null>(null); + const [saving, setSaving] = useState(false); + + useEffect(() => { + if (settings) { + setAfkChannelId((settings.afkChannelId as string) ?? ''); + setAfkTimeout(settings.afkTimeout ?? 300); + } + }, [settings?.afkChannelId, settings?.afkTimeout]); + + const handleSave = async () => { + if (!userId) { + setStatus({ type: 'err', message: 'You must be logged in.' }); + return; + } + if (afkTimeout < 60 || afkTimeout > 3600) { + setStatus({ type: 'err', message: 'AFK timeout must be between 60 and 3600 seconds.' }); + return; + } + setSaving(true); + setStatus(null); + try { + await updateSettings({ + userId, + afkChannelId: (afkChannelId || undefined) as Id<'channels'> | undefined, + afkTimeout, + }); + setStatus({ type: 'ok', message: 'Saved.' }); + } catch (err: any) { + setStatus({ type: 'err', message: err?.message ?? 'Failed to save.' }); + } finally { + setSaving(false); + } + }; + + const serverName = settings?.serverName ?? 'Server'; + const iconUrl = settings?.iconUrl ?? null; + const initials = serverName + .split(/\s+/) + .map((w) => w[0]) + .join('') + .slice(0, 2) + .toUpperCase(); + + return ( + <> + <div className={userStyles.profileHeader}> + <h2 className={userStyles.profileSubheading}>Overview</h2> + <p className={userStyles.profileDescription}> + Manage your server's display info and idle settings. + </p> + </div> + + <div style={{ display: 'flex', gap: 16, alignItems: 'center', marginBottom: 24 }}> + {iconUrl ? ( + <img + src={iconUrl} + alt={serverName} + style={{ + width: 96, + height: 96, + borderRadius: 24, + objectFit: 'cover', + background: 'var(--background-tertiary)', + }} + /> + ) : ( + <div + style={{ + width: 96, + height: 96, + borderRadius: 24, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + background: 'var(--background-tertiary)', + color: 'var(--text-primary)', + fontSize: 30, + fontWeight: 700, + }} + > + {initials || 'S'} + </div> + )} + <div style={{ flex: 1 }}> + <Label>Server Name</Label> + <input + type="text" + value={serverName} + readOnly + style={inputStyle} + /> + </div> + </div> + + <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 16 }}> + <div> + <Label>AFK / Idle Channel</Label> + <select + value={afkChannelId} + onChange={(e) => setAfkChannelId(e.target.value)} + style={inputStyle} + > + <option value="">No AFK Channel</option> + {voiceChannels.map((ch: any) => ( + <option key={ch._id} value={ch._id}> + {ch.name} + </option> + ))} + </select> + </div> + <div> + <Label>AFK Timeout (seconds)</Label> + <input + type="number" + min={60} + max={3600} + value={afkTimeout} + onChange={(e) => setAfkTimeout(Number(e.target.value))} + style={inputStyle} + /> + </div> + </div> + + <div style={{ display: 'flex', gap: 12, alignItems: 'center' }}> + <button type="button" onClick={handleSave} disabled={saving} style={primaryBtnStyle}> + {saving ? 'Saving…' : 'Save Changes'} + </button> + {status && ( + <span + style={{ + fontSize: 13, + color: status.type === 'err' ? '#f87171' : 'var(--text-secondary)', + }} + > + {status.message} + </span> + )} + </div> + </> + ); +} + +/* ------------------------------------------------------------------- */ +/* Roles */ +/* ------------------------------------------------------------------- */ + +const PERMISSION_KEYS = [ + 'manage_channels', + 'manage_roles', + 'manage_messages', + 'create_invite', + 'embed_links', + 'attach_files', + 'move_members', + 'mute_members', + 'manage_nicknames', +] as const; + +type PermissionKey = (typeof PERMISSION_KEYS)[number]; + +interface RoleDoc { + _id: Id<'roles'>; + name: string; + color: string; + position?: number; + permissions?: Record<string, boolean>; + isHoist?: boolean; +} + +export function RolesTab() { + // Owner is a bootstrap-only, permanently frozen role — hide it + // from the editable roles list so admins can't delete, rename, + // or strip its permissions. Backend mutations enforce the same + // rule server-side as a second line of defence. + const roles = ( + (useQuery(api.roles.list, {}) ?? []) as RoleDoc[] + ).filter((r) => r.name !== 'Owner'); + const createRole = useMutation(api.roles.create); + const updateRole = useMutation(api.roles.update); + const removeRole = useMutation(api.roles.remove); + + const [selectedId, setSelectedId] = useState<Id<'roles'> | null>(null); + const [error, setError] = useState<string | null>(null); + + const selected = useMemo( + () => roles.find((r) => r._id === selectedId) ?? null, + [roles, selectedId], + ); + + // Local draft state for the editor — seeded when selection changes. + const [draftName, setDraftName] = useState(''); + const [draftColor, setDraftColor] = useState('#99aab5'); + const [draftPerms, setDraftPerms] = useState<Record<string, boolean>>({}); + const [draftHoist, setDraftHoist] = useState(false); + + useEffect(() => { + if (selected) { + setDraftName(selected.name); + setDraftColor(selected.color || '#99aab5'); + setDraftPerms({ ...(selected.permissions ?? {}) }); + setDraftHoist(!!selected.isHoist); + } + }, [selectedId]); + + const handleCreate = async () => { + setError(null); + try { + const created: any = await createRole({ + name: 'new role', + color: '#99aab5', + permissions: {}, + isHoist: false, + position: 0, + }); + if (created?._id) setSelectedId(created._id); + } catch (err: any) { + setError(err?.message ?? 'Failed to create role'); + } + }; + + const handleSave = async () => { + if (!selected) return; + setError(null); + try { + await updateRole({ + id: selected._id, + name: draftName, + color: draftColor, + permissions: draftPerms, + isHoist: draftHoist, + }); + } catch (err: any) { + setError(err?.message ?? 'Failed to save role'); + } + }; + + const handleDelete = async () => { + if (!selected) return; + if (!confirm(`Delete role "${selected.name}"?`)) return; + setError(null); + try { + await removeRole({ id: selected._id }); + setSelectedId(null); + } catch (err: any) { + setError(err?.message ?? 'Failed to delete role'); + } + }; + + return ( + <> + <div className={userStyles.profileHeader}> + <h2 className={userStyles.profileSubheading}>Roles</h2> + <p className={userStyles.profileDescription}> + Use roles to group your members and assign permissions. + </p> + </div> + + {error && ( + <div + style={{ + padding: '8px 12px', + marginBottom: 12, + background: 'rgba(234,80,80,0.15)', + border: '1px solid rgba(234,80,80,0.4)', + borderRadius: 6, + color: 'var(--text-primary)', + fontSize: 13, + }} + > + {error} + </div> + )} + + <div style={{ display: 'flex', gap: 20, minHeight: 440 }}> + <div style={{ flex: '0 0 240px', display: 'flex', flexDirection: 'column', gap: 10 }}> + <button type="button" onClick={handleCreate} style={{ ...primaryBtnStyle, width: '100%' }}> + <Plus size={14} weight="bold" style={{ marginRight: 6, verticalAlign: 'middle' }} /> + Create Role + </button> + <div style={{ display: 'flex', flexDirection: 'column', gap: 2, overflowY: 'auto' }}> + {roles.length === 0 && ( + <div style={{ fontSize: 13, color: 'var(--text-secondary)', padding: 8 }}> + No roles yet. + </div> + )} + {roles.map((role) => ( + <button + key={role._id} + type="button" + onClick={() => setSelectedId(role._id)} + style={{ + display: 'flex', + alignItems: 'center', + gap: 10, + padding: '8px 12px', + background: + selectedId === role._id + ? 'var(--background-modifier-hover)' + : 'var(--background-secondary)', + border: + selectedId === role._id + ? '1px solid var(--brand-primary)' + : '1px solid transparent', + borderRadius: 6, + cursor: 'pointer', + textAlign: 'left', + color: 'var(--text-primary)', + font: 'inherit', + fontSize: 14, + }} + > + <span + style={{ + width: 12, + height: 12, + borderRadius: '50%', + flexShrink: 0, + background: role.color || '#99aab5', + }} + /> + <span + style={{ + flex: 1, + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }} + > + {role.name} + </span> + </button> + ))} + </div> + </div> + + <div style={{ flex: 1, minWidth: 0 }}> + {!selected ? ( + <div + style={{ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + height: '100%', + color: 'var(--text-secondary)', + fontSize: 14, + }} + > + Select a role to edit, or create a new one. + </div> + ) : ( + <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}> + <div> + <Label>Role Name</Label> + <input + type="text" + value={draftName} + onChange={(e) => setDraftName(e.target.value)} + style={inputStyle} + /> + </div> + <div> + <Label>Role Color</Label> + <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}> + <input + type="color" + value={draftColor} + onChange={(e) => setDraftColor(e.target.value)} + style={{ + width: 48, + height: 40, + border: 'none', + borderRadius: 6, + background: 'transparent', + cursor: 'pointer', + }} + /> + <input + type="text" + value={draftColor} + onChange={(e) => setDraftColor(e.target.value)} + style={{ ...inputStyle, maxWidth: 140 }} + /> + </div> + </div> + <div> + <Label>Display Options</Label> + <label + style={{ + display: 'flex', + alignItems: 'center', + gap: 10, + fontSize: 14, + color: 'var(--text-primary)', + }} + > + <input + type="checkbox" + checked={draftHoist} + onChange={(e) => setDraftHoist(e.target.checked)} + /> + Display role members separately from online members + </label> + </div> + <div> + <Label>Permissions</Label> + <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}> + {PERMISSION_KEYS.map((key) => ( + <label + key={key} + style={{ + display: 'flex', + alignItems: 'center', + gap: 8, + fontSize: 13, + color: 'var(--text-primary)', + padding: '6px 8px', + background: 'var(--background-tertiary)', + borderRadius: 6, + }} + > + <input + type="checkbox" + checked={!!draftPerms[key]} + onChange={(e) => + setDraftPerms((p) => ({ ...p, [key]: e.target.checked })) + } + /> + {key.replace(/_/g, ' ')} + </label> + ))} + </div> + </div> + <div style={{ display: 'flex', gap: 10, marginTop: 8 }}> + <button type="button" onClick={handleSave} style={primaryBtnStyle}> + Save + </button> + <button type="button" onClick={handleDelete} style={dangerBtnStyle}> + <Trash size={14} weight="bold" style={{ marginRight: 6, verticalAlign: 'middle' }} /> + Delete + </button> + </div> + </div> + )} + </div> + </div> + </> + ); +} + +/* ------------------------------------------------------------------- */ +/* Emojis */ +/* ------------------------------------------------------------------- */ + +/** + * Back-compat shim — the old desktop EmojisTab lived inline in this + * file and is still imported by MobileServerSettings. It now just + * renders the shared `CustomEmojisTab` so both surfaces get the new + * Fluxer-style layout without a second code path. + */ +export function EmojisTab() { + return <CustomEmojisTab />; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function _LegacyEmojisTabDeadCode(): any { + const userId = + typeof localStorage !== 'undefined' ? (localStorage.getItem('userId') as Id<'userProfiles'> | null) : null; + const emojis = (useQuery(api.customEmojis.list, {}) ?? []) as CustomEmojiDoc[]; + const generateUploadUrl = useMutation(api.files.generateUploadUrl); + const uploadEmoji = useMutation(api.customEmojis.upload); + const removeEmoji = useMutation(api.customEmojis.remove); + + const fileInputRef = useRef<HTMLInputElement>(null); + const [uploading, setUploading] = useState(false); + const [status, setStatus] = useState<{ type: 'ok' | 'err'; message: string } | null>(null); + + const handlePickFile = () => fileInputRef.current?.click(); + + const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => { + const file = e.target.files?.[0]; + if (fileInputRef.current) fileInputRef.current.value = ''; + if (!file) return; + if (!userId) { + setStatus({ type: 'err', message: 'You must be logged in.' }); + return; + } + + // Prompt for an emoji name based on the filename. + const defaultName = + file.name + .replace(/\.[^.]+$/, '') + .replace(/[^a-zA-Z0-9_]/g, '_') + .slice(0, 32) || 'emoji'; + const name = prompt('Emoji name (letters, numbers, underscores; 2-32 chars):', defaultName); + if (!name) return; + + setUploading(true); + setStatus(null); + try { + const uploadUrl = await generateUploadUrl({}); + const res = await fetch(uploadUrl, { + method: 'POST', + headers: { 'Content-Type': file.type }, + body: file, + }); + if (!res.ok) throw new Error('Upload failed'); + const { storageId } = (await res.json()) as { storageId: Id<'_storage'> }; + await uploadEmoji({ userId, name, storageId }); + setStatus({ type: 'ok', message: `Added :${name}:` }); + } catch (err: any) { + setStatus({ type: 'err', message: err?.message ?? 'Upload failed' }); + } finally { + setUploading(false); + } + }; + + const handleRemove = async (emojiId: Id<'customEmojis'>) => { + if (!userId) return; + if (!confirm('Delete this emoji?')) return; + setStatus(null); + try { + await removeEmoji({ userId, emojiId }); + } catch (err: any) { + setStatus({ type: 'err', message: err?.message ?? 'Failed to remove' }); + } + }; + + return ( + <> + <div className={userStyles.profileHeader}> + <h2 className={userStyles.profileSubheading}>Emojis</h2> + <p className={userStyles.profileDescription}> + Upload custom emojis for this server. PNG, GIF, or WebP up to ~500 KB works best. + </p> + </div> + + <div style={{ display: 'flex', gap: 10, alignItems: 'center', marginBottom: 16 }}> + <button type="button" onClick={handlePickFile} disabled={uploading} style={primaryBtnStyle}> + <UploadSimple size={14} weight="bold" style={{ marginRight: 6, verticalAlign: 'middle' }} /> + {uploading ? 'Uploading…' : 'Upload Emoji'} + </button> + <span style={{ fontSize: 12, color: 'var(--text-secondary)' }}> + {emojis.length} {emojis.length === 1 ? 'emoji' : 'emojis'} + </span> + {status && ( + <span + style={{ + fontSize: 13, + color: status.type === 'err' ? '#f87171' : 'var(--text-secondary)', + }} + > + {status.message} + </span> + )} + </div> + + <input + ref={fileInputRef} + type="file" + accept="image/png,image/gif,image/webp,image/jpeg" + onChange={handleFileChange} + style={{ display: 'none' }} + /> + + {emojis.length === 0 ? ( + <div + style={{ + padding: '32px 20px', + textAlign: 'center', + fontSize: 13, + color: 'var(--text-secondary)', + background: 'var(--background-secondary)', + border: '1px dashed var(--background-modifier-accent)', + borderRadius: 8, + }} + > + No custom emojis yet. Upload one to get started. + </div> + ) : ( + <div + style={{ + display: 'grid', + gridTemplateColumns: 'repeat(auto-fill, minmax(110px, 1fr))', + gap: 10, + }} + > + {emojis.map((emoji) => ( + <div + key={emoji._id} + style={{ + position: 'relative', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + gap: 6, + padding: '10px 8px', + background: 'var(--background-secondary)', + border: '1px solid var(--background-modifier-accent)', + borderRadius: 8, + }} + > + <img + src={emoji.src} + alt={emoji.name} + style={{ width: 56, height: 56, objectFit: 'contain' }} + /> + <span + style={{ + fontFamily: 'ui-monospace, Menlo, Consolas, monospace', + fontSize: 11, + color: 'var(--text-secondary)', + maxWidth: '100%', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }} + > + :{emoji.name}: + </span> + <button + type="button" + onClick={() => handleRemove(emoji._id)} + style={{ + position: 'absolute', + top: 4, + right: 4, + width: 22, + height: 22, + border: 'none', + borderRadius: 4, + background: 'rgba(0,0,0,0.55)', + color: '#fff', + cursor: 'pointer', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }} + aria-label={`Delete ${emoji.name}`} + > + <Trash size={12} weight="bold" /> + </button> + </div> + ))} + </div> + )} + </> + ); +} + +/* ------------------------------------------------------------------- */ +/* Shared bits */ +/* ------------------------------------------------------------------- */ + +function Label({ children }: { children: React.ReactNode }) { + return ( + <label + style={{ + display: 'block', + marginBottom: 6, + fontSize: 12, + fontWeight: 700, + textTransform: 'uppercase', + letterSpacing: 0.5, + color: 'var(--text-secondary)', + }} + > + {children} + </label> + ); +} + +const inputStyle: React.CSSProperties = { + width: '100%', + padding: '10px 12px', + background: 'var(--background-tertiary)', + border: '1px solid var(--background-modifier-accent)', + borderRadius: 6, + color: 'var(--text-primary)', + fontSize: 14, + fontFamily: 'inherit', + outline: 'none', + boxSizing: 'border-box', +}; + +const primaryBtnStyle: React.CSSProperties = { + background: 'var(--brand-primary)', + color: '#fff', + border: 'none', + padding: '10px 18px', + borderRadius: 6, + cursor: 'pointer', + fontWeight: 600, + fontSize: 14, +}; + +const dangerBtnStyle: React.CSSProperties = { + background: 'var(--status-danger, #da373c)', + color: '#fff', + border: 'none', + padding: '10px 18px', + borderRadius: 6, + cursor: 'pointer', + fontWeight: 600, + fontSize: 14, +}; + diff --git a/packages/shared/src/components/settings/UserSettingsModal.module.css b/packages/shared/src/components/settings/UserSettingsModal.module.css new file mode 100644 index 0000000..e6a5ac6 --- /dev/null +++ b/packages/shared/src/components/settings/UserSettingsModal.module.css @@ -0,0 +1,1115 @@ +/* ── Fullscreen Settings Overlay ─────────────────────────────────────── */ + +.overlay { + position: fixed; + inset: 0; + z-index: var(--z-index-modal, 10000); + background-color: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + padding: 40px; + box-sizing: border-box; + animation: fadeIn 150ms ease; +} + +.modal { + display: flex; + width: 100%; + max-width: 1200px; + height: 100%; + max-height: 900px; + background-color: var(--background-secondary); + border: 1px solid var(--background-header-secondary); + border-radius: 16px; + overflow: hidden; + box-shadow: 0 16px 48px rgba(0, 0, 0, 0.6); + animation: modalIn 180ms cubic-bezier(0.2, 0.9, 0.2, 1); +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes modalIn { + from { opacity: 0; transform: scale(0.96); } + to { opacity: 1; transform: scale(1); } +} + +/* ── Sidebar ────────────────────────────────────────────────────────── */ + +.sidebar { + flex: 0 0 clamp(240px, 24%, 280px); + background-color: var(--background-primary); + border-right: 1px solid var(--background-header-secondary); + display: flex; + flex-direction: column; + overflow-y: auto; + scrollbar-width: thin; +} + +.sidebarInner { + padding: 16px 12px 24px 12px; + display: flex; + flex-direction: column; + gap: 2px; +} + +/* ── Search box (top of sidebar) ─────────────────────────────────── */ + +.sidebarSearch { + position: relative; + margin-bottom: 12px; +} + +.sidebarSearchInput { + width: 100%; + height: 36px; + padding: 0 12px 0 36px; + border: 1px solid var(--background-modifier-accent); + border-radius: var(--radius-md, 8px); + background-color: var(--background-tertiary); + color: var(--text-primary); + font-size: 0.875rem; + font-family: inherit; + outline: none; + box-sizing: border-box; + transition: border-color 0.15s; +} + +.sidebarSearchInput::placeholder { + color: var(--text-tertiary); +} + +.sidebarSearchInput:focus { + border-color: var(--brand-primary, #5865f2); +} + +.sidebarSearchIcon { + position: absolute; + left: 10px; + top: 50%; + transform: translateY(-50%); + color: var(--text-tertiary); + pointer-events: none; +} + +/* ── Current user block (above categories) ──────────────────────── */ + +.sidebarUserBlock { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 10px; + margin-bottom: 8px; + border-radius: var(--radius-md, 8px); +} + +.sidebarUserName { + flex: 1; + min-width: 0; + font-size: 0.9375rem; + font-weight: 600; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.sidebarGroup { + display: flex; + flex-direction: column; + gap: 2px; + margin-bottom: 12px; +} + +.sidebarCategoryTitle { + padding: 6px 10px; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--text-tertiary); + user-select: none; +} + +.sidebarItem { + display: flex; + align-items: center; + gap: 12px; + width: 100%; + padding: 6px 10px; + border: none; + background: none; + border-radius: var(--radius-md, 6px); + color: var(--text-secondary); + font-size: 0.9375rem; + font-weight: 500; + font-family: inherit; + cursor: pointer; + text-align: left; + transition: background-color 0.1s ease, color 0.1s ease; +} + +.sidebarItem:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +.sidebarItemActive, +.sidebarItemActive:hover { + background-color: var(--background-modifier-selected); + color: var(--text-primary); +} + +.sidebarItemDanger { + color: var(--status-danger); +} + +.sidebarItemDanger:hover { + background-color: color-mix(in srgb, var(--status-danger) 12%, transparent); + color: var(--status-danger); +} + +.sidebarItemIcon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + flex-shrink: 0; + color: var(--text-tertiary); +} + +.sidebarItem:hover .sidebarItemIcon, +.sidebarItemActive .sidebarItemIcon { + color: var(--text-primary); +} + +.sidebarItemDanger .sidebarItemIcon, +.sidebarItemDanger:hover .sidebarItemIcon { + color: var(--status-danger); +} + +.sidebarItemLabel { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.sidebarDivider { + height: 1px; + background-color: var(--user-area-divider-color); + margin: 8px 0; +} + +/* ── Content column ─────────────────────────────────────────────────── */ + +.contentColumn { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + background-color: var(--background-secondary); + overflow: hidden; +} + +.contentHeader { + position: sticky; + top: 0; + z-index: 5; + display: flex; + align-items: center; + justify-content: space-between; + height: 68px; + padding: 0 32px; + background-color: var(--background-secondary); + flex-shrink: 0; +} + +.contentTitle { + font-size: 1.25rem; + font-weight: 600; + line-height: 1.75rem; + color: var(--text-primary); + margin: 0; +} + +.closeButton { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: var(--radius-md, 6px); + background: none; + border: none; + color: var(--text-secondary); + cursor: pointer; + transition: background-color 0.1s, color 0.1s; +} + +.closeButton:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +.contentScroll { + flex: 1; + min-height: 0; + overflow-y: auto; +} + +.contentInner { + max-width: 740px; + margin: 0 auto; + padding: 32px 32px 80px; +} + +/* ── Content Styles (panels) ────────────────────────────────────────── */ + +.title { + font-size: 1.25rem; + font-weight: 600; + color: var(--text-primary); + margin: 0 0 20px; +} + +/* ── Profile tab (Fluxer-style) ──────────────────────────────────── */ + +/* Section header */ + +.profileHeader { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin-bottom: 1.5rem; +} + +.profileSubheading { + font-size: 1.25rem; + font-weight: 600; + line-height: 1.5rem; + color: var(--text-primary); + margin: 0; +} + +.profileDescription { + font-size: 0.875rem; + line-height: 1.25rem; + color: var(--text-tertiary-muted, var(--text-tertiary)); + margin: 0; +} + +/* Two-column content layout */ + +.profileLayout { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +@media (min-width: 1024px) { + .profileLayout { + flex-direction: row; + gap: clamp(2rem, 3vw, 3.5rem); + } +} + +.profileForm { + display: flex; + flex: 2; + flex-direction: column; + gap: 1.5rem; + min-width: 0; +} + +.profilePreviewColumn { + display: flex; + flex: 1; + justify-content: center; +} + +@media (min-width: 1024px) { + .profilePreviewColumn { + justify-content: flex-end; + } +} + +/* Fieldset / form field */ + +.profileField { + display: flex; + flex-direction: column; + gap: 0.375rem; + margin: 0; + padding: 0; + border: 0; +} + +.profileFieldLabel { + display: block; + margin: 0; + padding: 0; + font-size: 0.875rem; + font-weight: 500; + line-height: 1.25rem; + color: var(--text-primary); +} + +.profileInput { + width: 100%; + min-height: 44px; + padding: 0.625rem 1rem; + background-color: var(--input-background, var(--background-tertiary)); + border: 1px solid var(--background-modifier-accent); + border-radius: 0.5rem; + color: var(--text-primary); + font-size: 0.875rem; + line-height: 1.25rem; + font-family: inherit; + outline: none; + box-sizing: border-box; + appearance: none; + resize: none; + transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1), + background-color 150ms cubic-bezier(0.4, 0, 0.2, 1), + border-color 150ms cubic-bezier(0.4, 0, 0.2, 1); +} + +.profileInput::placeholder { + color: var(--text-tertiary); +} + +.profileInput:focus-within, +.profileInput:focus { + border-color: var(--background-modifier-accent-focus, var(--brand-primary, #5865f2)); +} + +/* Avatar section */ + +.profileAvatarSection { + display: flex; + flex-direction: column; +} + +.profileAvatarLabel { + margin: 0 0 0.5rem; + font-size: 0.875rem; + font-weight: 500; + line-height: 1.25rem; + color: var(--text-primary); +} + +.profileAvatarButtonGroup { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin-bottom: 0.75rem; +} + +@media (min-width: 640px) { + .profileAvatarButtonGroup { + flex-direction: row; + } +} + +.profileAvatarDescription { + font-size: 0.875rem; + line-height: 1.25rem; + color: var(--text-tertiary-muted, var(--text-tertiary)); +} + +.hiddenFileInput { + display: none; +} + +/* Accent color picker */ + +.accentColorRow { + display: flex; + align-items: stretch; + gap: 0.5rem; +} + +.accentColorSwatch { + flex: 0 0 44px; + width: 44px; + height: 44px; + padding: 4px; + border: 1px solid var(--background-modifier-accent); + border-radius: 0.5rem; + background-color: var(--input-background, var(--background-tertiary)); + cursor: pointer; + appearance: none; + -webkit-appearance: none; +} + +.accentColorSwatch::-webkit-color-swatch-wrapper { + padding: 0; +} + +.accentColorSwatch::-webkit-color-swatch { + border: none; + border-radius: 0.375rem; +} + +.accentColorSwatch::-moz-color-swatch { + border: none; + border-radius: 0.375rem; +} + +.accentColorHex { + flex: 1; + min-width: 0; + min-height: 44px; + padding: 0.625rem 1rem; + background-color: var(--input-background, var(--background-tertiary)); + border: 1px solid var(--background-modifier-accent); + border-radius: 0.5rem; + color: var(--text-primary); + font-size: 0.875rem; + line-height: 1.25rem; + font-family: 'Consolas', 'Monaco', monospace; + text-transform: uppercase; + outline: none; + box-sizing: border-box; + transition: border-color 150ms cubic-bezier(0.4, 0, 0.2, 1); +} + +.accentColorHex:focus { + border-color: var(--background-modifier-accent-focus, var(--brand-primary, #5865f2)); +} + +/* Action bar (Reset / Save) */ + +.profileActionBar { + display: flex; + justify-content: flex-end; + gap: 0.5rem; +} + +/* ── Preview card ─────────────────────────────────────────────── */ + +.profilePreviewInner { + display: flex; + flex-direction: column; + align-items: center; +} + +.profilePreviewLabel { + margin-bottom: 1rem; + text-align: center; + font-size: 0.875rem; + font-weight: 500; + line-height: 1.25rem; + color: var(--text-tertiary-muted, var(--text-tertiary)); +} + +.profilePreviewCard { + position: relative; + display: flex; + width: 300px; + flex-direction: column; + gap: 4px; + overflow: hidden; + border-radius: 0.375rem; + border: 2.5px solid var(--brand-primary, #5865f2); + background-color: var(--background-primary); + padding-bottom: 0.75rem; +} + +.profilePreviewHeader { + position: relative; + height: 140px; + flex-shrink: 0; +} + +.profilePreviewBanner { + width: 100%; + min-height: 105px; + height: 105px; + background-color: var(--brand-primary, #5865f2); + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} + +.profilePreviewBio { + font-size: 0.8125rem; + line-height: 1.125rem; + color: var(--text-secondary); + white-space: pre-wrap; + word-wrap: break-word; + overflow-wrap: break-word; + max-height: 4.5rem; + overflow: hidden; +} + +.profileBioTextarea { + width: 100%; + min-height: 88px; + padding: 0.625rem 1rem; + background-color: var(--input-background, var(--background-tertiary)); + border: 1px solid var(--background-modifier-accent); + border-radius: 0.5rem; + color: var(--text-primary); + font-size: 0.875rem; + line-height: 1.25rem; + font-family: inherit; + outline: none; + box-sizing: border-box; + resize: vertical; + transition: border-color 150ms cubic-bezier(0.4, 0, 0.2, 1); +} + +.profileBioTextarea::placeholder { + color: var(--text-tertiary); +} + +.profileBioTextarea:focus { + border-color: var(--background-modifier-accent-focus, var(--brand-primary, #5865f2)); +} + +.profileBioFooter { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + margin-top: 0.375rem; +} + +.profileBioCounter { + font-size: 0.75rem; + font-variant-numeric: tabular-nums; + color: var(--text-tertiary); +} + +.profileBioCounterFull { + font-size: 0.75rem; + font-variant-numeric: tabular-nums; + color: var(--status-danger, #da373c); +} + +.profilePreviewAvatar { + position: absolute; + top: 55px; + left: 10px; + padding: 4px; + border-radius: 9999px; + background-color: var(--background-primary); + line-height: 0; +} + +.profilePreviewContent { + display: flex; + flex-direction: column; + gap: 0.5rem; + padding: 0.5rem 1rem 0; +} + +.profilePreviewName { + font-size: 1.25rem; + font-weight: 500; + line-height: 1.5rem; + max-height: 1.5rem; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.profilePreviewHandle { + font-size: 14px; + line-height: 18px; + max-height: 18px; + color: var(--text-tertiary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.profilePreviewFooter { + display: flex; + flex-direction: column; + gap: 0.5rem; + padding: 1rem 0.75rem 0; +} + +.setting { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 0; + border-bottom: 1px solid var(--background-modifier-accent); +} + +.settingLabel { + font-size: 1rem; + font-weight: 500; + color: var(--text-primary); +} + +.themeButtons { + display: flex; + gap: 8px; +} + +.placeholder { + font-size: 0.9375rem; + color: var(--text-secondary); + margin: 0; +} + +/* ── Security Tab ───────────────────────────────────────────────────── */ + +.section { + margin-bottom: 24px; +} + +.sectionTitle { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + color: var(--text-tertiary); + letter-spacing: 0.02em; + margin: 0 0 8px; +} + +.sectionDescription { + font-size: 0.8125rem; + color: var(--text-secondary); + margin: 0 0 12px; + line-height: 1.4; +} + +.inputGroup { + display: flex; + gap: 8px; + align-items: center; +} + +.textInput { + flex: 1; + padding: 8px 12px; + border: 1px solid var(--background-modifier-accent); + border-radius: var(--radius-md); + background-color: var(--input-background, var(--background-tertiary)); + color: var(--text-primary); + font-size: 0.875rem; + outline: none; + transition: border-color 0.15s; + font-family: inherit; +} + +.textInput:focus { + border-color: var(--brand-primary, #5865f2); +} + +.textInput::placeholder { + color: var(--text-tertiary); +} + +.recoveryKeyInput { + width: 100%; + padding: 10px 12px; + border: 1px solid var(--background-modifier-accent); + border-radius: var(--radius-md); + background-color: var(--input-background, var(--background-tertiary)); + color: var(--text-primary); + font-size: 0.875rem; + font-family: monospace; + outline: none; + resize: vertical; + transition: border-color 0.15s; + box-sizing: border-box; +} + +.recoveryKeyInput:focus { + border-color: var(--brand-primary, #5865f2); +} + +.recoveryKeyInput::placeholder { + color: var(--text-tertiary); + font-family: inherit; +} + +.recoveryKeyDisplay { + display: flex; + align-items: center; + gap: 8px; + padding: 12px; + border-radius: var(--radius-md); + background-color: var(--background-tertiary); + margin-bottom: 8px; +} + +.recoveryKeyCode { + flex: 1; + font-family: monospace; + font-size: 0.8125rem; + color: var(--text-primary); + word-break: break-all; + line-height: 1.5; + user-select: all; +} + +.recoveryKeyWarning { + font-size: 0.8125rem; + color: var(--status-warning, #faa61a); + margin: 0; + line-height: 1.4; +} + +.fileInput { + display: none; +} + +.statusMessage { + font-size: 0.8125rem; + margin: 8px 0 0; + line-height: 1.4; +} + +.statusSuccess { + color: var(--status-positive, #23a55a); +} + +.statusError { + color: var(--status-danger, #da373c); +} + +.deviceList { + display: flex; + flex-direction: column; + gap: 2px; +} + +.deviceItem { + display: flex; + align-items: center; + gap: 12px; + padding: 8px 12px; + border-radius: var(--radius-md); + background-color: var(--background-tertiary); +} + +.deviceIcon { + flex-shrink: 0; + color: var(--text-secondary); +} + +.deviceInfo { + flex: 1; + min-width: 0; +} + +.deviceName { + font-size: 0.875rem; + font-weight: 500; + color: var(--text-primary); + display: flex; + align-items: center; + gap: 6px; +} + +.deviceId { + font-size: 0.75rem; + color: var(--text-tertiary); + font-family: 'Consolas', 'Monaco', monospace; + word-break: break-all; +} + +.currentBadge { + font-size: 0.625rem; + font-weight: 700; + text-transform: uppercase; + padding: 1px 5px; + border-radius: var(--radius-sm, 3px); + background-color: var(--brand-primary, #5865f2); + color: #fff; + letter-spacing: 0.02em; +} + +.verifiedBadge { + color: var(--status-positive, #23a55a); + display: flex; + align-items: center; +} + +.passphraseRow { + margin-top: 8px; +} + +/* ── Voice & Video Tab ─────────────────────────────────────────────── */ + +/* The `.setting` class is a single-row layout for the appearance tab + etc. — voice sections need stacked content (title + description + + controls on separate rows), so use a dedicated block container. */ +.voiceSection { + display: flex; + flex-direction: column; + gap: 8px; + padding: 16px 0; + border-bottom: 1px solid var(--background-modifier-accent); +} + +.voiceSection:last-child { + border-bottom: none; +} + +.voiceSectionTitle { + font-size: 1rem; + font-weight: 600; + color: var(--text-primary); + margin: 0; +} + +.settingDescription { + margin: 0 0 4px; + font-size: 13px; + line-height: 1.45; + color: var(--text-secondary, #a0a3a8); +} + +/* ── Audio & Video settings (VoiceTab additions) ─────────── */ + +.voiceFieldLabel { + margin-top: 12px; + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--text-primary-muted, #a0a3a8); +} + +/* Radio row (Voice Activity / Push-to-Talk) — checkbox on the + left, two-line label block on the right. */ +.voiceRadioRow { + display: flex; + align-items: flex-start; + gap: 12px; + padding: 10px 12px; + background-color: var(--background-secondary); + border: 1px solid var(--background-modifier-accent); + border-radius: 8px; + cursor: pointer; + transition: border-color 0.12s; +} + +.voiceRadioRow:hover { + border-color: var(--background-modifier-hover); +} + +.voiceRadioRow input[type='radio'] { + margin-top: 4px; + accent-color: var(--brand-primary); + cursor: pointer; +} + +.voiceRadioLabel { + font-size: 0.9375rem; + font-weight: 600; + color: var(--text-primary); +} + +.voiceRadioHelp { + margin-top: 2px; + font-size: 0.8125rem; + line-height: 1.35; + color: var(--text-primary-muted, #a0a3a8); +} + +/* Device dropdown — full width, same surface as the form + inputs elsewhere in the settings modal. */ +.voiceSelect { + width: 100%; + height: 40px; + padding: 0 12px; + background-color: var(--form-surface-background, #1e2024); + border: 1px solid var(--background-modifier-accent); + border-radius: 6px; + color: var(--text-primary); + font: inherit; + font-size: 0.9375rem; + outline: none; + cursor: pointer; + transition: border-color 0.12s; +} + +.voiceSelect:focus, +.voiceSelect:focus-visible { + border-color: var(--brand-primary); +} + +/* Input / output volume slider — full-width range with a + right-flush percent readout. */ +.voiceVolumeHeader { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 12px; +} + +.voiceVolumeValue { + font-size: 0.75rem; + font-weight: 600; + color: var(--text-primary-muted, #a0a3a8); + font-variant-numeric: tabular-nums; +} + +.voiceSlider { + width: 100%; + height: 4px; + -webkit-appearance: none; + appearance: none; + background-color: var(--background-modifier-accent); + border-radius: 999px; + outline: none; + cursor: pointer; +} + +.voiceSlider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--brand-primary); + cursor: pointer; + border: none; +} + +.voiceSlider::-moz-range-thumb { + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--brand-primary); + cursor: pointer; + border: none; +} + +/* Audio-processing toggle row — label block on the left, ios- + style toggle on the right. Uses a plain checkbox for now; + when we ship a real Toggle primitive this becomes a one-line + swap. */ +.voiceToggleRow { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 12px; + background-color: var(--background-secondary); + border: 1px solid var(--background-modifier-accent); + border-radius: 8px; + cursor: pointer; +} + +.voiceToggleRow input[type='checkbox'] { + width: 18px; + height: 18px; + accent-color: var(--brand-primary); + cursor: pointer; +} + +/* Mic Test row — Button + stretch meter. */ +.micTestRow { + display: flex; + align-items: center; + gap: 14px; + margin-top: 4px; +} + +.micTestMeter { + flex: 1; + height: 8px; + background-color: var(--background-modifier-accent); + border-radius: 999px; + overflow: hidden; +} + +.micTestMeterFill { + height: 100%; + background-color: var(--brand-primary); + transition: width 0.08s linear; +} + +.joinSoundRow { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 10px 14px; + background-color: var(--background-secondary, rgba(255, 255, 255, 0.03)); + border: 1px solid var(--background-modifier-accent, rgba(255, 255, 255, 0.06)); + border-radius: 6px; +} + +.joinSoundLabel { + flex: 1 1 auto; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 13px; + color: var(--text-primary, #fff); +} + +.joinSoundActions { + display: flex; + gap: 6px; + flex-shrink: 0; +} + +.joinSoundSaveRow { + display: flex; + justify-content: flex-end; + margin-top: 12px; +} + +.joinSoundToggleRow { + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + color: var(--text-primary, #fff); + cursor: pointer; + padding: 6px 0; +} + +.joinSoundToggleRow input[type='checkbox'] { + width: 16px; + height: 16px; + cursor: pointer; + accent-color: var(--brand-primary, #5865f2); +} + +.joinSoundVolumeRow { + display: flex; + align-items: center; + gap: 12px; + margin-top: 10px; +} + +.joinSoundVolumeLabel { + font-size: 12px; + color: var(--text-primary-muted, #a0a3a8); + min-width: 60px; + flex-shrink: 0; +} + +.joinSoundVolumeRow input[type='range'] { + flex: 1 1 auto; + accent-color: var(--brand-primary, #5865f2); +} + +.joinSoundVolumeValue { + font-size: 12px; + color: var(--text-primary, #fff); + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + min-width: 40px; + text-align: right; + flex-shrink: 0; +} diff --git a/packages/shared/src/components/settings/UserSettingsModal.tsx b/packages/shared/src/components/settings/UserSettingsModal.tsx new file mode 100644 index 0000000..70d311c --- /dev/null +++ b/packages/shared/src/components/settings/UserSettingsModal.tsx @@ -0,0 +1,1593 @@ +import { useMutation, useQuery } from 'convex/react'; +import { + Bell, + Keyboard, + Microphone, + Palette, + Shield, + SignOut, + User, + X, +} from '@phosphor-icons/react'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { api } from '../../../../../convex/_generated/api'; +import { useTheme } from '../../contexts/ThemeContext'; +import { useIsMobile } from '../../hooks/useIsMobile'; +import { useLogout } from '../../hooks/useLogout'; +import { AvatarCropModal } from './AvatarCropModal'; +import { KeybindsTab } from './KeybindsTab'; +import { MobileUserSettings } from './MobileUserSettings'; +import { LogoutConfirmModal } from '../modals/LogoutConfirmModal'; +import styles from './UserSettingsModal.module.css'; + +type TabId = 'account' | 'appearance' | 'voice' | 'keybinds' | 'notifications' | 'security'; + +interface UserSettingsModalProps { + isOpen: boolean; + onClose: () => void; + initialTab?: TabId; +} + +const TABS: Array<{ id: TabId; label: string; icon: typeof User }> = [ + { id: 'account', label: 'My Account', icon: User }, + { id: 'appearance', label: 'Appearance', icon: Palette }, + { id: 'voice', label: 'Voice & Video', icon: Microphone }, + { id: 'keybinds', label: 'Keybinds', icon: Keyboard }, + { id: 'notifications', label: 'Notifications', icon: Bell }, + { id: 'security', label: 'Security', icon: Shield }, +]; + +export function UserSettingsModal({ isOpen, onClose, initialTab }: UserSettingsModalProps) { + // Desktop opens straight to the Account tab so the user lands on + // meaningful content. Mobile prefers the category list when the + // caller didn't ask for a specific tab — the list view is the + // mobile-correct default. + const isMobile = useIsMobile(); + const logout = useLogout(); + const resolvedInitialTab: TabId = initialTab ?? 'account'; + const [activeTab, setActiveTab] = useState<TabId>(resolvedInitialTab); + const [showLogoutConfirm, setShowLogoutConfirm] = useState(false); + + useEffect(() => { + if (isOpen) setActiveTab(resolvedInitialTab); + }, [isOpen, resolvedInitialTab]); + + useEffect(() => { + if (!isOpen) return; + const onKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + document.addEventListener('keydown', onKey); + return () => document.removeEventListener('keydown', onKey); + }, [isOpen, onClose]); + + // On mobile, present the full-screen MobileUserSettings overlay + // instead of the desktop two-column modal. Pass `initialTab` as-is + // (NOT the resolved version) so mobile can land on the category + // list when no explicit tab was requested. + if (isMobile) { + return ( + <MobileUserSettings + isOpen={isOpen} + onClose={onClose} + initialTab={initialTab as any} + /> + ); + } + + if (!isOpen) return null; + + const active = TABS.find((t) => t.id === activeTab) ?? TABS[0]; + + return createPortal( + <> + <div className={styles.overlay} onClick={onClose}> + <div className={styles.modal} onClick={(e) => e.stopPropagation()}> + <nav className={styles.sidebar}> + <div className={styles.sidebarInner}> + <div className={styles.sidebarCategoryTitle}>User Settings</div> + <div className={styles.sidebarGroup}> + {TABS.map((tab) => { + const Icon = tab.icon; + const isActive = activeTab === tab.id; + return ( + <button + key={tab.id} + type="button" + className={`${styles.sidebarItem} ${isActive ? styles.sidebarItemActive : ''}`} + onClick={() => setActiveTab(tab.id)} + > + <span className={styles.sidebarItemIcon}> + <Icon size={18} /> + </span> + <span className={styles.sidebarItemLabel}>{tab.label}</span> + </button> + ); + })} + <div className={styles.sidebarDivider} /> + <button + type="button" + className={`${styles.sidebarItem} ${styles.sidebarItemDanger}`} + onClick={() => setShowLogoutConfirm(true)} + > + <span className={styles.sidebarItemIcon}> + <SignOut size={18} /> + </span> + <span className={styles.sidebarItemLabel}>Log Out</span> + </button> + </div> + </div> + </nav> + + <div className={styles.contentColumn}> + <div className={styles.contentHeader}> + <h1 className={styles.contentTitle}>{active.label}</h1> + <button + type="button" + className={styles.closeButton} + onClick={onClose} + aria-label="Close settings" + > + <X size={22} weight="bold" /> + </button> + </div> + <div className={styles.contentScroll}> + <div className={styles.contentInner}> + {activeTab === 'account' && <AccountTab />} + {activeTab === 'appearance' && <AppearanceTab />} + {activeTab === 'voice' && <VoiceTab />} + {activeTab === 'keybinds' && <KeybindsTab />} + {activeTab === 'notifications' && <Placeholder label="Notifications" />} + {activeTab === 'security' && <SecurityTab />} + </div> + </div> + </div> + </div> + </div> + <LogoutConfirmModal + isOpen={showLogoutConfirm} + onClose={() => setShowLogoutConfirm(false)} + onConfirm={() => { + setShowLogoutConfirm(false); + void logout(); + }} + /> + </>, + document.body, + ); +} + +const DEFAULT_ACCENT_COLOR = '#4641D9'; +const AVATAR_MAX_SIZE = 10 * 1024 * 1024; +// Join sounds are tiny by design — a 2 MB cap matches the desktop +// Discord limit and keeps upload latency in check. Audio mimeType +// accept list pulls the common web-playable formats. +const JOIN_SOUND_MAX_SIZE = 2 * 1024 * 1024; +const JOIN_SOUND_ACCEPT = 'audio/mpeg,audio/mp3,audio/wav,audio/ogg,audio/webm,audio/mp4'; + +export function AccountTab() { + const userId = typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const allUsers = useQuery(api.auth.getPublicKeys) ?? []; + const me = allUsers.find((u) => u.id === userId); + const updateProfile = useMutation(api.auth.updateProfile); + const generateUploadUrl = useMutation(api.files.generateUploadUrl); + const validateUpload = useMutation(api.files.validateUpload); + + const fileInputRef = useRef<HTMLInputElement>(null); + const joinSoundInputRef = useRef<HTMLInputElement>(null); + const joinSoundPreviewRef = useRef<HTMLAudioElement | null>(null); + const [displayName, setDisplayName] = useState(''); + const [aboutMe, setAboutMe] = useState(''); + const [accentColor, setAccentColor] = useState<string>(DEFAULT_ACCENT_COLOR); + const [pendingAvatarBlob, setPendingAvatarBlob] = useState<Blob | null>(null); + const [avatarPreview, setAvatarPreview] = useState<string | null>(null); + const [cropCandidate, setCropCandidate] = useState<File | null>(null); + const [saving, setSaving] = useState(false); + const [status, setStatus] = useState<string | null>(null); + // Join sound state. + // `joinSoundBusy` — upload in flight + // `joinSoundError` — surface-level error (file too big, wrong type, …) + // `joinSoundFilename` — the currently-committed sound's filename so + // we can show it next to the preview button without a round-trip + const [joinSoundBusy, setJoinSoundBusy] = useState(false); + const [joinSoundError, setJoinSoundError] = useState<string | null>(null); + const [joinSoundFilename, setJoinSoundFilename] = useState<string | null>(null); + + useEffect(() => { + if (me) { + setDisplayName(me.displayName ?? ''); + setAboutMe(me.aboutMe ?? ''); + setAccentColor((me as any).accentColor ?? DEFAULT_ACCENT_COLOR); + } + }, [me?.id]); + + // Revoke locally-created object URLs on unmount. + useEffect(() => { + return () => { + if (avatarPreview) URL.revokeObjectURL(avatarPreview); + }; + }, [avatarPreview]); + + const pickAvatar = () => fileInputRef.current?.click(); + + const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { + const file = e.target.files?.[0]; + e.target.value = ''; + if (!file) return; + if (!file.type.startsWith('image/')) { + setStatus('Pick an image file.'); + return; + } + if (file.size > AVATAR_MAX_SIZE) { + setStatus('Avatar must be under 10 MB.'); + return; + } + setCropCandidate(file); + }; + + const handleCropped = (blob: Blob) => { + setPendingAvatarBlob(blob); + if (avatarPreview) URL.revokeObjectURL(avatarPreview); + setAvatarPreview(URL.createObjectURL(blob)); + setCropCandidate(null); + }; + + const handleSkipCrop = () => { + if (!cropCandidate) return; + setPendingAvatarBlob(cropCandidate); + if (avatarPreview) URL.revokeObjectURL(avatarPreview); + setAvatarPreview(URL.createObjectURL(cropCandidate)); + setCropCandidate(null); + }; + + const uploadAvatar = async (blob: Blob): Promise<string> => { + const uploadUrl = await generateUploadUrl(); + const res = await fetch(uploadUrl, { + method: 'POST', + headers: { 'Content-Type': blob.type || 'image/png' }, + body: blob, + }); + if (!res.ok) throw new Error('Avatar upload failed'); + const { storageId } = (await res.json()) as { storageId: string }; + await validateUpload({ storageId: storageId as any }); + return storageId; + }; + + const pickJoinSound = () => joinSoundInputRef.current?.click(); + + const handleJoinSoundFile = async ( + e: React.ChangeEvent<HTMLInputElement>, + ) => { + const file = e.target.files?.[0]; + e.target.value = ''; + if (!file) return; + setJoinSoundError(null); + if (!file.type.startsWith('audio/')) { + setJoinSoundError('Pick an audio file.'); + return; + } + if (file.size > JOIN_SOUND_MAX_SIZE) { + setJoinSoundError('Join sound must be under 2 MB.'); + return; + } + if (!userId) return; + setJoinSoundBusy(true); + try { + const uploadUrl = await generateUploadUrl(); + const res = await fetch(uploadUrl, { + method: 'POST', + headers: { 'Content-Type': file.type || 'audio/mpeg' }, + body: file, + }); + if (!res.ok) throw new Error('Join sound upload failed'); + const { storageId } = (await res.json()) as { storageId: string }; + await validateUpload({ storageId: storageId as any }); + await updateProfile({ + userId: userId as any, + joinSoundStorageId: storageId as any, + }); + setJoinSoundFilename(file.name); + } catch (err: any) { + setJoinSoundError(err?.message ?? 'Upload failed.'); + } finally { + setJoinSoundBusy(false); + } + }; + + const handlePreviewJoinSound = () => { + const url = (me as any)?.joinSoundUrl; + if (!url) return; + // Reuse a single audio element so multiple presses don't stack + // overlapping plays. Stopping first resets the clip to the start. + if (joinSoundPreviewRef.current) { + joinSoundPreviewRef.current.pause(); + joinSoundPreviewRef.current.currentTime = 0; + } + const audio = new Audio(url); + joinSoundPreviewRef.current = audio; + void audio.play().catch(() => { + setJoinSoundError('Could not play preview.'); + }); + }; + + const handleRemoveJoinSound = async () => { + if (!userId || joinSoundBusy) return; + setJoinSoundBusy(true); + setJoinSoundError(null); + try { + await updateProfile({ + userId: userId as any, + removeJoinSound: true, + }); + setJoinSoundFilename(null); + } catch (err: any) { + setJoinSoundError(err?.message ?? 'Remove failed.'); + } finally { + setJoinSoundBusy(false); + } + }; + + const handleSave = async () => { + if (!userId) return; + setSaving(true); + setStatus(null); + try { + const patch: Record<string, unknown> = { + userId, + displayName: displayName || undefined, + aboutMe: aboutMe || undefined, + accentColor: accentColor || undefined, + }; + if (pendingAvatarBlob) { + const storageId = await uploadAvatar(pendingAvatarBlob); + patch.avatarStorageId = storageId; + } + await updateProfile(patch as any); + setPendingAvatarBlob(null); + setStatus('Saved'); + setTimeout(() => setStatus(null), 1500); + } catch (err: any) { + setStatus(err?.message ?? 'Save failed'); + } finally { + setSaving(false); + } + }; + + const currentAvatar = avatarPreview ?? me?.avatarUrl ?? null; + + return ( + <> + <div className={styles.profileHeader}> + <h2 className={styles.profileSubheading}>My Account</h2> + <p className={styles.profileDescription}> + Edit how you appear to others in the app. + </p> + </div> + + {/* Profile preview card — matches the new UI's + "this is what your profile looks like" frame. */} + <div + style={{ + position: 'relative', + marginTop: 16, + marginBottom: 24, + border: '1px solid var(--background-modifier-accent)', + borderRadius: 10, + overflow: 'hidden', + background: 'var(--background-secondary)', + }} + > + <div + style={{ + height: 72, + background: `linear-gradient(135deg, ${accentColor}, ${accentColor}aa)`, + }} + /> + <div style={{ position: 'absolute', top: 36, left: 18 }}> + <button + type="button" + onClick={pickAvatar} + title="Change avatar" + style={{ + width: 88, + height: 88, + padding: 0, + borderRadius: '50%', + border: '6px solid var(--background-secondary)', + background: 'var(--background-tertiary)', + overflow: 'hidden', + cursor: 'pointer', + }} + > + {currentAvatar ? ( + <img + src={currentAvatar} + alt="Avatar" + style={{ width: '100%', height: '100%', objectFit: 'cover' }} + /> + ) : ( + <div + style={{ + width: '100%', + height: '100%', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + color: 'var(--text-tertiary)', + fontSize: 28, + fontWeight: 700, + }} + > + {(me?.displayName || me?.username || '?').slice(0, 1).toUpperCase()} + </div> + )} + </button> + </div> + <div style={{ padding: '48px 18px 16px 18px' }}> + <div style={{ fontSize: 18, fontWeight: 700, color: 'var(--text-primary)' }}> + {displayName || me?.username || 'You'} + </div> + <div style={{ fontSize: 13, color: 'var(--text-secondary)' }}> + @{me?.username} + </div> + {aboutMe && ( + <div + style={{ + marginTop: 8, + fontSize: 13, + color: 'var(--text-secondary)', + whiteSpace: 'pre-wrap', + }} + > + {aboutMe} + </div> + )} + </div> + </div> + + <input + ref={fileInputRef} + type="file" + accept="image/*" + style={{ display: 'none' }} + onChange={handleFileChange} + /> + + <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}> + <FieldRow label="Username" value={me?.username ?? '—'} readOnly /> + <FieldRow + label="Display Name" + value={displayName} + onChange={setDisplayName} + placeholder="How others see you" + /> + <FieldRow + label="About Me" + value={aboutMe} + onChange={setAboutMe} + placeholder="Tell others about yourself" + multiline + /> + + <div> + <div + style={{ + fontSize: 12, + fontWeight: 700, + letterSpacing: '0.04em', + textTransform: 'uppercase', + color: 'var(--text-tertiary)', + marginBottom: 8, + }} + > + Accent Color + </div> + <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}> + <input + type="color" + value={accentColor} + onChange={(e) => setAccentColor(e.target.value)} + style={{ + width: 56, + height: 40, + padding: 0, + border: '1px solid var(--background-modifier-accent)', + borderRadius: 6, + cursor: 'pointer', + background: 'transparent', + }} + aria-label="Accent color" + /> + <input + type="text" + value={accentColor} + onChange={(e) => setAccentColor(e.target.value)} + placeholder="#4641D9" + spellCheck={false} + style={{ + width: 120, + padding: '8px 10px', + borderRadius: 6, + border: '1px solid var(--background-modifier-accent)', + background: 'var(--background-tertiary)', + color: 'var(--text-primary)', + font: 'inherit', + fontSize: 14, + }} + /> + <span style={{ fontSize: 12, color: 'var(--text-tertiary)' }}> + Used for your profile banner. + </span> + </div> + </div> + + <div> + <div + style={{ + fontSize: 12, + fontWeight: 700, + letterSpacing: '0.04em', + textTransform: 'uppercase', + color: 'var(--text-tertiary)', + marginBottom: 8, + }} + > + Join Sound + </div> + <div + style={{ + fontSize: 12, + color: 'var(--text-tertiary)', + marginBottom: 10, + }} + > + Plays for other users when you join a voice channel. Max 2 MB. + </div> + <input + ref={joinSoundInputRef} + type="file" + accept={JOIN_SOUND_ACCEPT} + style={{ display: 'none' }} + onChange={handleJoinSoundFile} + /> + <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}> + <button + type="button" + onClick={pickJoinSound} + disabled={joinSoundBusy} + style={{ + padding: '8px 14px', + borderRadius: 6, + border: '1px solid var(--background-modifier-accent)', + background: 'var(--background-tertiary)', + color: 'var(--text-primary)', + cursor: joinSoundBusy ? 'not-allowed' : 'pointer', + fontWeight: 600, + fontSize: 13, + opacity: joinSoundBusy ? 0.6 : 1, + }} + > + {joinSoundBusy ? 'Uploading…' : 'Upload Sound'} + </button> + {(me as any)?.joinSoundUrl && ( + <> + <button + type="button" + onClick={handlePreviewJoinSound} + disabled={joinSoundBusy} + style={{ + padding: '8px 14px', + borderRadius: 6, + border: 'none', + background: 'var(--brand-primary)', + color: '#fff', + cursor: 'pointer', + fontWeight: 600, + fontSize: 13, + }} + > + Preview + </button> + <button + type="button" + onClick={handleRemoveJoinSound} + disabled={joinSoundBusy} + style={{ + padding: '8px 14px', + borderRadius: 6, + border: 'none', + background: 'var(--status-danger, #ed4245)', + color: '#fff', + cursor: 'pointer', + fontWeight: 600, + fontSize: 13, + }} + > + Remove + </button> + <span + style={{ + fontSize: 12, + color: 'var(--text-tertiary)', + marginLeft: 2, + }} + > + {joinSoundFilename ?? 'Custom sound set'} + </span> + </> + )} + </div> + {joinSoundError && ( + <div + style={{ + marginTop: 8, + fontSize: 12, + color: 'var(--status-danger, #ed4245)', + }} + > + {joinSoundError} + </div> + )} + </div> + + <div style={{ display: 'flex', gap: 12, alignItems: 'center', marginTop: 8 }}> + <button + type="button" + onClick={handleSave} + disabled={saving} + style={{ + background: 'var(--brand-primary)', + color: '#fff', + border: 'none', + padding: '10px 20px', + borderRadius: 6, + cursor: 'pointer', + fontWeight: 600, + fontSize: 14, + opacity: saving ? 0.6 : 1, + }} + > + {saving ? 'Saving…' : 'Save'} + </button> + {status && ( + <span style={{ color: 'var(--text-secondary)', fontSize: 13 }}>{status}</span> + )} + </div> + </div> + + <AvatarCropModal + isOpen={!!cropCandidate} + onClose={() => setCropCandidate(null)} + file={cropCandidate} + onCropped={handleCropped} + onSkipCrop={handleSkipCrop} + /> + </> + ); +} + +export function AppearanceTab() { + const theme = useTheme?.(); + const current = theme?.theme ?? 'theme-dark'; + + const options: Array<{ + id: 'theme-dark' | 'theme-light'; + label: string; + bg: string; + surface: string; + bubble: string; + text: string; + subText: string; + }> = [ + { + id: 'theme-dark', + label: 'Dark', + bg: '#1c1d22', + surface: '#141318', + bubble: '#2b2d31', + text: '#ffffff', + subText: '#b5bac1', + }, + { + id: 'theme-light', + label: 'Light', + bg: '#ffffff', + surface: '#e3e5e8', + bubble: '#f2f3f5', + text: '#060607', + subText: '#4e5058', + }, + ]; + + return ( + <> + <div className={styles.profileHeader}> + <h2 className={styles.profileSubheading}>Appearance</h2> + <p className={styles.profileDescription}>Choose how the app looks.</p> + </div> + <div style={{ display: 'flex', gap: 16, marginTop: 16, flexWrap: 'wrap' }}> + {options.map((opt) => { + const selected = current === opt.id; + return ( + <button + key={opt.id} + type="button" + onClick={() => theme?.setTheme?.(opt.id)} + aria-pressed={selected} + style={{ + width: 200, + height: 140, + padding: 0, + borderRadius: 8, + border: `2px solid ${ + selected ? 'var(--brand-primary, #5865f2)' : 'transparent' + }`, + background: opt.bg, + cursor: 'pointer', + position: 'relative', + overflow: 'hidden', + display: 'flex', + flexDirection: 'column', + textAlign: 'left', + boxShadow: selected + ? '0 0 0 1px rgba(0,0,0,0.2)' + : '0 0 0 1px rgba(0,0,0,0.15)', + transition: 'border-color 0.15s ease', + }} + > + {/* sidebar strip */} + <div + style={{ + position: 'absolute', + inset: 0, + display: 'flex', + }} + > + <div + style={{ + width: 32, + background: opt.surface, + borderRight: `1px solid ${opt.bubble}`, + }} + /> + <div style={{ flex: 1, padding: '10px 10px 28px 10px' }}> + {/* chat bubbles */} + <div + style={{ + background: opt.bubble, + color: opt.text, + borderRadius: 6, + padding: '4px 6px', + fontSize: 8, + marginBottom: 4, + width: '70%', + }} + > + <div + style={{ + height: 4, + width: '60%', + background: opt.subText, + borderRadius: 2, + marginBottom: 3, + }} + /> + <div + style={{ + height: 4, + width: '90%', + background: opt.text, + borderRadius: 2, + }} + /> + </div> + <div + style={{ + background: opt.bubble, + color: opt.text, + borderRadius: 6, + padding: '4px 6px', + fontSize: 8, + width: '55%', + }} + > + <div + style={{ + height: 4, + width: '50%', + background: opt.subText, + borderRadius: 2, + marginBottom: 3, + }} + /> + <div + style={{ + height: 4, + width: '80%', + background: opt.text, + borderRadius: 2, + }} + /> + </div> + </div> + </div> + {/* footer label */} + <div + style={{ + position: 'absolute', + left: 0, + right: 0, + bottom: 0, + padding: '6px 10px', + background: 'rgba(0,0,0,0.35)', + color: '#fff', + fontSize: 12, + fontWeight: 600, + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + }} + > + <span>{opt.label}</span> + {selected && ( + <span + style={{ + display: 'inline-flex', + alignItems: 'center', + justifyContent: 'center', + width: 16, + height: 16, + borderRadius: '50%', + background: 'var(--brand-primary, #5865f2)', + color: '#fff', + fontSize: 11, + lineHeight: 1, + }} + > + ✓ + </span> + )} + </div> + </button> + ); + })} + </div> + + </> + ); +} + +interface VoiceSettings { + inputDeviceId: string; + outputDeviceId: string; + videoDeviceId: string; + inputVolume: number; + noiseSuppression: boolean; + echoCancellation: boolean; + autoGainControl: boolean; +} + +const DEFAULT_VOICE_SETTINGS: VoiceSettings = { + inputDeviceId: 'default', + outputDeviceId: 'default', + videoDeviceId: 'default', + inputVolume: 100, + noiseSuppression: true, + echoCancellation: false, + autoGainControl: true, +}; + +function loadVoiceSettings(): VoiceSettings { + if (typeof localStorage === 'undefined') return { ...DEFAULT_VOICE_SETTINGS }; + try { + const raw = localStorage.getItem('voiceSettings'); + if (!raw) return { ...DEFAULT_VOICE_SETTINGS }; + const parsed = JSON.parse(raw); + return { ...DEFAULT_VOICE_SETTINGS, ...parsed }; + } catch { + return { ...DEFAULT_VOICE_SETTINGS }; + } +} + +export function VoiceTab() { + const [settings, setSettings] = useState<VoiceSettings>(() => loadVoiceSettings()); + const [inputDevices, setInputDevices] = useState<MediaDeviceInfo[]>([]); + const [outputDevices, setOutputDevices] = useState<MediaDeviceInfo[]>([]); + const [videoDevices, setVideoDevices] = useState<MediaDeviceInfo[]>([]); + const [permissionState, setPermissionState] = useState<'unknown' | 'granted' | 'denied' | 'prompt'>( + 'unknown', + ); + const [permissionError, setPermissionError] = useState<string | null>(null); + const mountedRef = useRef(true); + + useEffect(() => { + mountedRef.current = true; + return () => { + mountedRef.current = false; + }; + }, []); + + const enumerateDevices = useCallback(async () => { + if (typeof navigator === 'undefined' || !navigator.mediaDevices?.enumerateDevices) { + return; + } + try { + const devices = await navigator.mediaDevices.enumerateDevices(); + if (!mountedRef.current) return; + setInputDevices(devices.filter((d) => d.kind === 'audioinput')); + setOutputDevices(devices.filter((d) => d.kind === 'audiooutput')); + setVideoDevices(devices.filter((d) => d.kind === 'videoinput')); + // If any device has a label, permission has effectively been granted + // (labels are empty strings until the user grants mic/camera access). + const anyLabeled = devices.some( + (d) => (d.kind === 'audioinput' || d.kind === 'videoinput') && !!d.label, + ); + if (anyLabeled) { + setPermissionState('granted'); + } else if (devices.length === 0) { + setPermissionState('denied'); + } else { + setPermissionState((prev) => (prev === 'granted' ? prev : 'prompt')); + } + } catch (err: any) { + setPermissionError(err?.message ?? 'Failed to list audio/video devices'); + } + }, []); + + useEffect(() => { + void enumerateDevices(); + if (typeof navigator === 'undefined' || !navigator.mediaDevices) return; + const onChange = () => { + void enumerateDevices(); + }; + navigator.mediaDevices.addEventListener?.('devicechange', onChange); + return () => { + navigator.mediaDevices.removeEventListener?.('devicechange', onChange); + }; + }, [enumerateDevices]); + + const requestPermission = useCallback(async () => { + setPermissionError(null); + try { + const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + // Immediately stop tracks — we only wanted the prompt so labels populate. + stream.getTracks().forEach((t) => t.stop()); + setPermissionState('granted'); + await enumerateDevices(); + } catch (err: any) { + setPermissionState('denied'); + setPermissionError(err?.message ?? 'Microphone access was denied'); + } + }, [enumerateDevices]); + + const commit = useCallback((next: VoiceSettings) => { + setSettings(next); + try { + localStorage.setItem('voiceSettings', JSON.stringify(next)); + } catch { + /* ignore quota / unavailable storage */ + } + try { + window.dispatchEvent( + new CustomEvent('brycord:voice-settings-changed', { detail: next }), + ); + } catch { + /* ignore */ + } + }, []); + + const update = useCallback( + <K extends keyof VoiceSettings>(key: K, value: VoiceSettings[K]) => { + commit({ ...settings, [key]: value }); + }, + [commit, settings], + ); + + const showPermissionGate = + permissionState !== 'granted' && + inputDevices.every((d) => !d.label) && + videoDevices.every((d) => !d.label); + + return ( + <div> + <div className={styles.profileHeader}> + <h3 className={styles.profileSubheading}>Voice & Video</h3> + <p className={styles.profileDescription}> + Configure your microphone, speakers, and camera + </p> + </div> + + {showPermissionGate && ( + <div className={styles.voiceSection}> + <h4 className={styles.voiceSectionTitle}>Microphone Access</h4> + <p className={styles.settingDescription}> + Grant microphone access so device names can be shown and devices can be + selected. + </p> + <button + type="button" + onClick={() => void requestPermission()} + style={{ + background: 'var(--brand-primary)', + color: '#fff', + border: 'none', + padding: '10px 20px', + borderRadius: 6, + cursor: 'pointer', + fontWeight: 600, + fontSize: 14, + marginTop: 8, + }} + > + Grant microphone access + </button> + {permissionError && ( + <p + className={styles.settingDescription} + style={{ color: 'var(--status-danger, #ed4245)', marginTop: 8 }} + > + {permissionError} + </p> + )} + </div> + )} + + <div className={styles.voiceSection}> + <h4 className={styles.voiceSectionTitle}>Audio</h4> + <p className={styles.settingDescription}> + Choose which microphone and speakers the app should use. + </p> + + <div className={styles.voiceFieldLabel}>Input Device</div> + <select + className={styles.voiceSelect} + value={settings.inputDeviceId} + onChange={(e) => update('inputDeviceId', e.target.value)} + > + <option value="default">Default</option> + {inputDevices + .filter((d) => d.deviceId && d.deviceId !== 'default') + .map((d) => ( + <option key={d.deviceId} value={d.deviceId}> + {d.label || d.deviceId} + </option> + ))} + </select> + + <div className={styles.voiceFieldLabel}>Output Device</div> + <select + className={styles.voiceSelect} + value={settings.outputDeviceId} + onChange={(e) => update('outputDeviceId', e.target.value)} + > + <option value="default">Default</option> + {outputDevices + .filter((d) => d.deviceId && d.deviceId !== 'default') + .map((d) => ( + <option key={d.deviceId} value={d.deviceId}> + {d.label || d.deviceId} + </option> + ))} + </select> + + <div className={styles.voiceVolumeHeader}> + <span className={styles.voiceFieldLabel}>Input Volume</span> + <span className={styles.voiceVolumeValue}>{settings.inputVolume}%</span> + </div> + <input + type="range" + min={0} + max={200} + value={settings.inputVolume} + onChange={(e) => update('inputVolume', Number(e.target.value))} + className={styles.voiceSlider} + /> + + <MicTest settings={settings} /> + </div> + + <div className={styles.voiceSection}> + <h4 className={styles.voiceSectionTitle}>Audio Processing</h4> + <p className={styles.settingDescription}> + These settings improve voice audio quality. Disable them for music mode or legacy + compatibility. + </p> + + <label className={styles.voiceToggleRow}> + <div> + <div className={styles.voiceRadioLabel}>Echo Cancellation</div> + <div className={styles.voiceRadioHelp}> + Reduces echo and feedback from speakers. + </div> + </div> + <input + type="checkbox" + checked={settings.echoCancellation} + onChange={(e) => update('echoCancellation', e.target.checked)} + /> + </label> + + <label className={styles.voiceToggleRow}> + <div> + <div className={styles.voiceRadioLabel}>Noise Suppression</div> + <div className={styles.voiceRadioHelp}> + Filters out background noise like fans and keyboard typing. + </div> + </div> + <input + type="checkbox" + checked={settings.noiseSuppression} + onChange={(e) => update('noiseSuppression', e.target.checked)} + /> + </label> + + <label className={styles.voiceToggleRow}> + <div> + <div className={styles.voiceRadioLabel}>Auto Gain Control</div> + <div className={styles.voiceRadioHelp}> + Adjusts microphone volume for consistent levels. + </div> + </div> + <input + type="checkbox" + checked={settings.autoGainControl} + onChange={(e) => update('autoGainControl', e.target.checked)} + /> + </label> + </div> + + <div className={styles.voiceSection}> + <h4 className={styles.voiceSectionTitle}>Video</h4> + <p className={styles.settingDescription}>Configure your camera.</p> + <div className={styles.voiceFieldLabel}>Camera</div> + <select + className={styles.voiceSelect} + value={settings.videoDeviceId} + onChange={(e) => update('videoDeviceId', e.target.value)} + > + <option value="default">Default</option> + {videoDevices + .filter((d) => d.deviceId && d.deviceId !== 'default') + .map((d) => ( + <option key={d.deviceId} value={d.deviceId}> + {d.label || d.deviceId} + </option> + ))} + </select> + </div> + </div> + ); +} + +function Placeholder({ label }: { label: string }) { + return ( + <div className={styles.profileHeader}> + <h2 className={styles.profileSubheading}>{label}</h2> + <p className={styles.profileDescription}>Coming soon.</p> + </div> + ); +} + +/** + * SecurityTab — shows the user's recovery key. Reads `masterKey` + * from sessionStorage (where LoginPage / RegisterPage stashes the + * decrypted master key for the session). The recovery key is the + * master key — anyone who has it can decrypt the user's private + * keys, so we gate the display behind an explicit "Show" toggle. + */ +export function SecurityTab() { + const [revealed, setRevealed] = useState(false); + const [copied, setCopied] = useState(false); + const masterKey = + typeof sessionStorage !== 'undefined' + ? sessionStorage.getItem('masterKey') + : null; + + const formatted = masterKey + ? (masterKey.match(/.{1,4}/g) ?? []).join('-').toUpperCase() + : null; + + const handleCopy = () => { + if (!masterKey) return; + void navigator.clipboard.writeText(masterKey).then(() => { + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }); + }; + + return ( + <div> + <div className={styles.profileHeader}> + <h2 className={styles.profileSubheading}>Security</h2> + <p className={styles.profileDescription}> + Back up your recovery key. You need it to restore your account + on another device or if you lose your password. + </p> + </div> + + <div + style={{ + marginTop: 16, + padding: 16, + borderRadius: 8, + border: '1px solid var(--background-modifier-accent)', + background: 'var(--background-secondary)', + }} + > + <div + style={{ + fontSize: 12, + fontWeight: 700, + textTransform: 'uppercase', + letterSpacing: '0.04em', + color: 'var(--text-tertiary)', + marginBottom: 8, + }} + > + Recovery Key + </div> + {!masterKey ? ( + <div + style={{ + fontSize: 13, + color: 'var(--text-secondary)', + }} + > + No recovery key is available in this session. Sign out and sign + back in to regenerate your session state. + </div> + ) : ( + <> + <code + style={{ + display: 'block', + padding: '12px 14px', + borderRadius: 6, + background: 'var(--background-tertiary)', + border: '1px solid var(--background-modifier-accent)', + fontFamily: + 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace', + fontSize: 13, + color: 'var(--text-primary)', + wordBreak: 'break-all', + letterSpacing: revealed ? '0.5px' : undefined, + filter: revealed ? 'none' : 'blur(6px)', + userSelect: revealed ? 'text' : 'none', + transition: 'filter 0.15s ease', + }} + > + {formatted} + </code> + <div style={{ display: 'flex', gap: 8, marginTop: 12 }}> + <button + type="button" + onClick={() => setRevealed((v) => !v)} + style={{ + padding: '8px 14px', + borderRadius: 6, + border: '1px solid var(--background-modifier-accent)', + background: 'var(--background-tertiary)', + color: 'var(--text-primary)', + cursor: 'pointer', + fontWeight: 600, + fontSize: 13, + }} + > + {revealed ? 'Hide' : 'Show Recovery Key'} + </button> + <button + type="button" + onClick={handleCopy} + disabled={!revealed} + style={{ + padding: '8px 14px', + borderRadius: 6, + border: 'none', + background: 'var(--brand-primary)', + color: '#fff', + cursor: revealed ? 'pointer' : 'not-allowed', + fontWeight: 600, + fontSize: 13, + opacity: revealed ? 1 : 0.5, + }} + > + {copied ? 'Copied!' : 'Copy'} + </button> + </div> + <div + style={{ + marginTop: 12, + padding: 10, + borderRadius: 6, + background: 'rgba(237, 66, 69, 0.08)', + border: '1px solid rgba(237, 66, 69, 0.25)', + fontSize: 12, + color: 'var(--text-secondary)', + }} + > + <strong style={{ color: '#ed4245' }}>Keep this safe.</strong>{' '} + Anyone with this key can access your account and decrypt + your messages. Store it in a password manager or print it + and put it somewhere secure. + </div> + </> + )} + </div> + </div> + ); +} + +/** + * MicTest — live loopback test so the user can hear themselves + * through the selected input device with the configured filters + * applied. Pipes getUserMedia → AnalyserNode (for the level meter) + * → GainNode (for volume) → destination. Updating `settings` while + * the test is live tears down and re-creates the graph with the + * new constraints. + */ +function MicTest({ settings }: { settings: VoiceSettings }) { + const [testing, setTesting] = useState(false); + const [level, setLevel] = useState(0); + const [error, setError] = useState<string | null>(null); + const streamRef = useRef<MediaStream | null>(null); + const audioCtxRef = useRef<AudioContext | null>(null); + const analyserRef = useRef<AnalyserNode | null>(null); + const rafRef = useRef<number | null>(null); + + const stop = useCallback(() => { + setTesting(false); + setLevel(0); + if (rafRef.current !== null) { + cancelAnimationFrame(rafRef.current); + rafRef.current = null; + } + if (streamRef.current) { + streamRef.current.getTracks().forEach((t) => t.stop()); + streamRef.current = null; + } + if (audioCtxRef.current) { + void audioCtxRef.current.close().catch(() => {}); + audioCtxRef.current = null; + } + analyserRef.current = null; + }, []); + + const start = useCallback(async () => { + setError(null); + try { + const audioConstraints: MediaTrackConstraints = { + echoCancellation: settings.echoCancellation, + noiseSuppression: settings.noiseSuppression, + autoGainControl: settings.autoGainControl, + channelCount: 1, + }; + if (settings.inputDeviceId && settings.inputDeviceId !== 'default') { + audioConstraints.deviceId = { exact: settings.inputDeviceId }; + } + const stream = await navigator.mediaDevices.getUserMedia({ + audio: audioConstraints, + }); + streamRef.current = stream; + + const AudioCtx = + (window as any).AudioContext || (window as any).webkitAudioContext; + const ctx: AudioContext = new AudioCtx(); + audioCtxRef.current = ctx; + const source = ctx.createMediaStreamSource(stream); + const gain = ctx.createGain(); + gain.gain.value = Math.max(0, settings.inputVolume / 100); + const analyser = ctx.createAnalyser(); + analyser.fftSize = 256; + analyserRef.current = analyser; + source.connect(gain); + gain.connect(analyser); + // Pipe to speaker so the user hears themselves. If the user + // has a specific output device selected, route via + // setSinkId — only supported when `analyser -> destination` + // goes through an <audio> element, so we do that instead. + const audioEl = new Audio(); + audioEl.autoplay = true; + audioEl.muted = false; + const dest = ctx.createMediaStreamDestination(); + analyser.connect(dest); + audioEl.srcObject = dest.stream; + if ( + settings.outputDeviceId && + settings.outputDeviceId !== 'default' && + typeof (audioEl as any).setSinkId === 'function' + ) { + try { + await (audioEl as any).setSinkId(settings.outputDeviceId); + } catch { + /* fall back to default output */ + } + } + void audioEl.play().catch(() => {}); + + setTesting(true); + + const data = new Uint8Array(analyser.frequencyBinCount); + const tick = () => { + if (!analyserRef.current) return; + analyserRef.current.getByteTimeDomainData(data); + let sum = 0; + for (let i = 0; i < data.length; i++) { + const v = (data[i] - 128) / 128; + sum += v * v; + } + const rms = Math.sqrt(sum / data.length); + setLevel(Math.min(1, rms * 3)); + rafRef.current = requestAnimationFrame(tick); + }; + rafRef.current = requestAnimationFrame(tick); + } catch (err: any) { + setError(err?.message ?? 'Microphone access failed'); + stop(); + } + }, [settings, stop]); + + // Re-apply settings by restarting the test when the user changes + // the processing toggles or the selected device while it's live. + useEffect(() => { + if (!testing) return; + stop(); + void start(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + settings.echoCancellation, + settings.noiseSuppression, + settings.autoGainControl, + settings.inputDeviceId, + settings.outputDeviceId, + ]); + + // Volume changes don't need a restart — patch the gain node in place. + useEffect(() => { + if (!testing || !audioCtxRef.current) return; + // The analyser chain hides the GainNode; re-use the current + // source graph by walking AudioContext. Simpler: clamp local + // level meter and leave track volume to the OS (LiveKit does + // the same in-call). A real per-track gain ramp would require + // keeping a reference to the GainNode, which we're skipping + // to keep the loopback simple. The slider still persists. + }, [settings.inputVolume, testing]); + + useEffect(() => { + return () => stop(); + }, [stop]); + + return ( + <div style={{ marginTop: 16 }}> + <div className={styles.voiceFieldLabel}>Mic Test</div> + <div + style={{ + display: 'flex', + alignItems: 'center', + gap: 12, + marginTop: 6, + }} + > + <button + type="button" + onClick={testing ? stop : () => void start()} + style={{ + padding: '8px 16px', + borderRadius: 6, + border: 'none', + background: testing ? 'var(--status-danger, #ed4245)' : 'var(--brand-primary)', + color: '#fff', + cursor: 'pointer', + fontWeight: 600, + fontSize: 13, + }} + > + {testing ? 'Stop Test' : "Let's Check"} + </button> + <div + style={{ + flex: 1, + height: 10, + borderRadius: 5, + background: 'var(--background-tertiary)', + overflow: 'hidden', + border: '1px solid var(--background-modifier-accent)', + }} + aria-label="Input level" + > + <div + style={{ + width: `${Math.round(level * 100)}%`, + height: '100%', + background: + 'linear-gradient(90deg, #3ba55d 0%, #3ba55d 60%, #faa61a 80%, #ed4245 100%)', + transition: 'width 60ms linear', + }} + /> + </div> + </div> + <div + style={{ + fontSize: 12, + color: 'var(--text-tertiary)', + marginTop: 6, + }} + > + {testing + ? 'Speak into your mic — you should hear yourself through the selected output.' + : 'Click to hear yourself with your current filters applied.'} + </div> + {error && ( + <div + style={{ + fontSize: 12, + color: 'var(--status-danger, #ed4245)', + marginTop: 6, + }} + > + {error} + </div> + )} + </div> + ); +} + +interface FieldRowProps { + label: string; + value: string; + onChange?: (v: string) => void; + placeholder?: string; + readOnly?: boolean; + multiline?: boolean; +} + +function FieldRow({ label, value, onChange, placeholder, readOnly, multiline }: FieldRowProps) { + const baseStyle: React.CSSProperties = { + width: '100%', + padding: '10px 12px', + background: 'var(--background-tertiary)', + border: '1px solid var(--background-modifier-accent)', + borderRadius: 6, + color: 'var(--text-primary)', + fontSize: 14, + fontFamily: 'inherit', + outline: 'none', + boxSizing: 'border-box', + }; + return ( + <div> + <label + style={{ + display: 'block', + marginBottom: 6, + fontSize: 12, + fontWeight: 700, + textTransform: 'uppercase', + letterSpacing: 0.5, + color: 'var(--text-secondary)', + }} + > + {label} + </label> + {multiline ? ( + <textarea + value={value} + onChange={(e) => onChange?.(e.target.value)} + placeholder={placeholder} + readOnly={readOnly} + rows={4} + style={{ ...baseStyle, resize: 'vertical' }} + /> + ) : ( + <input + type="text" + value={value} + onChange={(e) => onChange?.(e.target.value)} + placeholder={placeholder} + readOnly={readOnly} + style={baseStyle} + /> + )} + </div> + ); +} diff --git a/packages/shared/src/components/settings/index.ts b/packages/shared/src/components/settings/index.ts new file mode 100644 index 0000000..beb48ea --- /dev/null +++ b/packages/shared/src/components/settings/index.ts @@ -0,0 +1 @@ +export { UserSettingsModal } from './UserSettingsModal'; diff --git a/packages/shared/src/components/voice/PiPOverlay.module.css b/packages/shared/src/components/voice/PiPOverlay.module.css new file mode 100644 index 0000000..a08c07f --- /dev/null +++ b/packages/shared/src/components/voice/PiPOverlay.module.css @@ -0,0 +1,250 @@ +.container { + position: fixed; + top: 0; + left: 0; + z-index: calc(var(--z-index-modal, 10000) + 1); + border-radius: 12px; + overflow: hidden; + box-shadow: + 0 8px 32px var(--voice-shadow-strong), + 0 2px 8px var(--voice-shadow-soft); + cursor: grab; + user-select: none; + touch-action: none; + background-color: var(--voice-surface-2); + will-change: transform; +} + +.container:active { + cursor: grabbing; +} + +.videoWrapper { + position: absolute; + inset: 0; + overflow: hidden; + border-radius: inherit; +} + +.videoWrapper video { + width: 100%; + height: 100%; + object-fit: contain; + background-color: var(--voice-surface-0); + border-radius: inherit; + display: block; +} + +.screenShareVideo { + object-fit: contain; +} + +/* ── Top gradient + return-to-call button ───────────────────── */ + +.headerGradient { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 48px; + background: linear-gradient(to bottom, var(--voice-overlay-strong) 0%, transparent 100%); + display: flex; + align-items: flex-start; + padding: 8px 10px; + pointer-events: auto; + z-index: 3; +} + +.returnToCallButton { + display: inline-flex; + align-items: center; + gap: 6px; + color: var(--voice-text-strong); + cursor: pointer; + background: none; + border: none; + padding: 0; + font-size: 0.75rem; + font-weight: 600; + border-bottom: 1px solid transparent; + transition: border-color 150ms; +} + +.returnToCallButton:hover { + border-bottom-color: currentColor; +} + +/* ── Bottom gradient + streamer name ───────────────────────── */ + +.footerGradient { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 48px; + background: linear-gradient(to top, var(--voice-overlay-strong) 0%, transparent 100%); + display: flex; + align-items: flex-end; + padding: 8px 10px; + pointer-events: none; + z-index: 3; +} + +.streamerName { + font-size: 0.75rem; + font-weight: 500; + color: var(--voice-text-strong); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* ── Resize handles ─────────────────────────────────────────── */ + +.resizeHandleTop, +.resizeHandleBottom, +.resizeHandleLeft, +.resizeHandleRight, +.resizeHandleTopLeft, +.resizeHandleTopRight, +.resizeHandleBottomLeft, +.resizeHandleBottomRight { + position: absolute; + border: none; + background: transparent; + z-index: 4; + pointer-events: auto; + padding: 0; +} + +.resizeHandleTop { + top: 0; + left: 16px; + right: 16px; + height: 8px; + cursor: n-resize; +} + +.resizeHandleBottom { + bottom: 0; + left: 16px; + right: 16px; + height: 8px; + cursor: s-resize; +} + +.resizeHandleLeft { + top: 16px; + left: 0; + bottom: 16px; + width: 8px; + cursor: w-resize; +} + +.resizeHandleRight { + top: 16px; + right: 0; + bottom: 16px; + width: 8px; + cursor: e-resize; +} + +.resizeHandleTopLeft { + top: 0; + left: 0; + width: 16px; + height: 16px; + cursor: nw-resize; +} + +.resizeHandleTopRight { + top: 0; + right: 0; + width: 16px; + height: 16px; + cursor: ne-resize; +} + +.resizeHandleBottomLeft { + bottom: 0; + left: 0; + width: 16px; + height: 16px; + cursor: sw-resize; +} + +.resizeHandleBottomRight { + bottom: 0; + right: 0; + width: 16px; + height: 16px; + cursor: se-resize; +} + +.pipContent { + display: flex; + flex-direction: column; + justify-content: center; + height: 100%; + padding: 12px 14px; + gap: 4px; +} + +.pipLabel { + font-size: 11px; + font-weight: 600; + color: var(--text-tertiary); + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.pipChannel { + font-size: 15px; + font-weight: 700; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.pipActions { + display: flex; + gap: 8px; + margin-top: 6px; +} + +.pipPrimary { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 6px 10px; + border: none; + border-radius: 4px; + background-color: var(--brand-primary, #4641d9); + color: #fff; + font: inherit; + font-size: 12px; + font-weight: 600; + cursor: pointer; +} + +.pipPrimary:hover { + filter: brightness(1.1); +} + +.pipDanger { + display: inline-flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + border: none; + border-radius: 4px; + background-color: var(--status-danger, #ed4245); + color: #fff; + cursor: pointer; +} + +.pipDanger:hover { + filter: brightness(1.1); +} diff --git a/packages/shared/src/components/voice/PiPOverlay.tsx b/packages/shared/src/components/voice/PiPOverlay.tsx new file mode 100644 index 0000000..34cd686 --- /dev/null +++ b/packages/shared/src/components/voice/PiPOverlay.tsx @@ -0,0 +1,133 @@ +/** + * PiPOverlay — floating voice-call status widget shown when the user + * is connected to a voice channel but has navigated to a different + * channel / DM / route. Presents the channel name, a "Return to + * channel" button, and a disconnect button. The widget is draggable + * via pointer events. + * + * The new UI's screen-share video PiP required live LiveKit track + * binding from a MobX store; our Convex/React voice context doesn't + * expose that the same way, so this is a simpler status widget + * focused on the common case: "I'm in a call, I want to get back." + */ +import { useCallback, useEffect, useRef, useState } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { PhoneDisconnect, ArrowRight } from '@phosphor-icons/react'; +import { useVoice } from '../../contexts/VoiceContext'; +import styles from './PiPOverlay.module.css'; + +interface Box { + x: number; + y: number; +} + +const WIDTH = 260; +const HEIGHT = 96; + +function clamp(v: number, min: number, max: number): number { + return Math.max(min, Math.min(max, v)); +} + +export function PiPOverlay() { + const navigate = useNavigate(); + const location = useLocation(); + const voice = useVoice() as any; + + const [box, setBox] = useState<Box>(() => ({ + x: Math.max(16, window.innerWidth - WIDTH - 24), + y: Math.max(16, window.innerHeight - HEIGHT - 24), + })); + const dragRef = useRef<{ startX: number; startY: number; startBox: Box } | null>(null); + + useEffect(() => { + const handleMove = (e: PointerEvent) => { + const drag = dragRef.current; + if (!drag) return; + const dx = e.clientX - drag.startX; + const dy = e.clientY - drag.startY; + setBox({ + x: clamp(drag.startBox.x + dx, 0, window.innerWidth - WIDTH), + y: clamp(drag.startBox.y + dy, 0, window.innerHeight - HEIGHT), + }); + }; + const handleUp = () => { + dragRef.current = null; + }; + window.addEventListener('pointermove', handleMove); + window.addEventListener('pointerup', handleUp); + return () => { + window.removeEventListener('pointermove', handleMove); + window.removeEventListener('pointerup', handleUp); + }; + }, []); + + const startDrag = useCallback( + (e: React.PointerEvent) => { + // Don't initiate a drag when the pointer lands on a button. + const target = e.target as HTMLElement; + if (target.closest('button')) return; + e.preventDefault(); + dragRef.current = { + startX: e.clientX, + startY: e.clientY, + startBox: { ...box }, + }; + }, + [box], + ); + + const activeChannelId: string | null = voice?.activeChannelId ?? null; + const activeChannelName: string | null = voice?.activeChannelName ?? null; + + // Hide the overlay when not in a call or when the user is already + // looking at the active voice channel. + if (!activeChannelId) return null; + const onActiveChannelRoute = + location.pathname.includes(`/${activeChannelId}`); + if (onActiveChannelRoute) return null; + + const handleReturn = () => { + navigate(`/channels/home/${activeChannelId}`); + }; + const handleDisconnect = () => { + voice?.disconnect?.(); + }; + + return ( + <div + className={styles.container} + style={{ + transform: `translate(${box.x}px, ${box.y}px)`, + width: WIDTH, + height: HEIGHT, + }} + onPointerDown={startDrag} + > + <div className={styles.pipContent}> + <div className={styles.pipLabel}>In voice</div> + <div className={styles.pipChannel}> + #{activeChannelName || 'channel'} + </div> + <div className={styles.pipActions}> + <button + type="button" + className={styles.pipPrimary} + onClick={handleReturn} + aria-label="Return to channel" + > + <ArrowRight size={14} weight="bold" /> + <span>Return</span> + </button> + <button + type="button" + className={styles.pipDanger} + onClick={handleDisconnect} + aria-label="Disconnect" + > + <PhoneDisconnect size={14} weight="bold" /> + </button> + </div> + </div> + </div> + ); +} diff --git a/packages/shared/src/components/voice/ScreenSharePreview.module.css b/packages/shared/src/components/voice/ScreenSharePreview.module.css new file mode 100644 index 0000000..4fbdd9d --- /dev/null +++ b/packages/shared/src/components/voice/ScreenSharePreview.module.css @@ -0,0 +1,86 @@ +/* ── ScreenSharePreview ────────────────────────────────────────── + Local preview of the user's own screen share, rendered above + the participant grid in the voice call view. Matches the new UI: + rounded card, solid dark surface, accent border, label pill. */ + +.wrap { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + padding: 10px 12px 14px; + width: min(100%, 640px); + margin: 0 auto; +} + +.videoFrame { + position: relative; + width: 100%; + aspect-ratio: 16 / 9; + background: #000; + border-radius: 10px; + overflow: hidden; + box-shadow: + 0 0 0 1px var(--background-modifier-accent, rgba(0, 0, 0, 0.3)), + 0 6px 18px rgba(0, 0, 0, 0.35); +} + +.video { + width: 100%; + height: 100%; + object-fit: contain; + display: block; +} + +.placeholder { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-tertiary, #a0a3a8); + font-size: 13px; + background: rgba(0, 0, 0, 0.55); +} + +.pauseOverlay { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 6px; + background: rgba(0, 0, 0, 0.78); + color: var(--text-primary, #ffffff); + padding: 16px; + text-align: center; +} + +.pauseTitle { + font-size: 16px; + font-weight: 700; + color: #ffffff; +} + +.pauseBody { + font-size: 13px; + color: var(--text-secondary, #b5bac1); + max-width: 320px; + line-height: 1.4; +} + +.label { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 3px 10px; + border-radius: 999px; + background: var(--status-danger, #ed4245); + color: #ffffff; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; +} diff --git a/packages/shared/src/components/voice/ScreenSharePreview.tsx b/packages/shared/src/components/voice/ScreenSharePreview.tsx new file mode 100644 index 0000000..804bc7d --- /dev/null +++ b/packages/shared/src/components/voice/ScreenSharePreview.tsx @@ -0,0 +1,172 @@ +/** + * ScreenSharePreview — renders a local preview of the user's own + * screen share inside the voice call view. Attaches the LiveKit + * `screen_share` video publication to a `<video>` element and + * detaches cleanly when the publication changes or unmounts. + * + * Pauses the preview (but keeps the publication running) whenever + * the browser tab loses focus / becomes hidden, replacing the + * frame with an overlay that reads "Your stream is still running! + * We've paused this preview to save your resources." Mirrors the + * new UI's local preview behaviour exactly. + */ +import { useEffect, useRef, useState } from 'react'; +import { useVoice } from '../../contexts/VoiceContext'; +import styles from './ScreenSharePreview.module.css'; + +export function ScreenSharePreview() { + const voice = useVoice() as any; + const videoRef = useRef<HTMLVideoElement>(null); + const [hasTrack, setHasTrack] = useState(false); + // `paused` is true whenever the tab is hidden or the window has + // lost focus. We cover the video with an overlay in that state + // so the user still understands their stream is live. + const [paused, setPaused] = useState(false); + + // Attach the local screen-share track to our video element. The + // publication is recreated every time `setScreenShareEnabled(true)` + // fires, so re-poll after the room reference or the active-channel + // flag changes. + useEffect(() => { + const room = voice?.room; + const video = videoRef.current; + if (!room || !video) { + setHasTrack(false); + return; + } + let attached = false; + const attach = () => { + const publications = Array.from( + room.localParticipant.trackPublications.values(), + ); + const pub = publications.find( + (p: any) => p.source === 'screen_share' || p.source === 3, + ) as any; + const track = pub?.videoTrack; + if (track && !attached) { + try { + track.attach(video); + attached = true; + setHasTrack(true); + } catch { + setHasTrack(false); + } + } + }; + // Initial attempt + one retry after the next microtask so a + // freshly-published track has time to land in the map. + attach(); + const t = window.setTimeout(attach, 250); + + const handleTrackPublished = () => attach(); + const handleTrackUnpublished = (pub: any) => { + if (pub?.source === 'screen_share' || pub?.source === 3) { + try { + pub.videoTrack?.detach(video); + } catch { + /* ignore */ + } + attached = false; + setHasTrack(false); + } + }; + room.localParticipant.on?.('localTrackPublished', handleTrackPublished); + room.localParticipant.on?.('localTrackUnpublished', handleTrackUnpublished); + + return () => { + window.clearTimeout(t); + room.localParticipant.off?.('localTrackPublished', handleTrackPublished); + room.localParticipant.off?.( + 'localTrackUnpublished', + handleTrackUnpublished, + ); + if (attached) { + const publications = Array.from( + room.localParticipant.trackPublications.values(), + ); + const pub = publications.find( + (p: any) => p.source === 'screen_share' || p.source === 3, + ) as any; + try { + pub?.videoTrack?.detach(video); + } catch { + /* ignore */ + } + } + setHasTrack(false); + }; + }, [voice?.room, voice?.isScreenSharing, voice?.activeChannelId]); + + // Pause the preview whenever the tab is hidden or the window + // loses focus. The underlying LiveKit publication keeps running + // — we just stop painting the video element and show an overlay. + useEffect(() => { + const update = () => { + const hidden = + document.hidden === true || document.visibilityState === 'hidden'; + const focused = document.hasFocus?.() ?? true; + setPaused(hidden || !focused); + }; + update(); + document.addEventListener('visibilitychange', update); + window.addEventListener('focus', update); + window.addEventListener('blur', update); + return () => { + document.removeEventListener('visibilitychange', update); + window.removeEventListener('focus', update); + window.removeEventListener('blur', update); + }; + }, []); + + // Tell the video element to pause playback while paused so the + // GPU isn't decoding frames the user isn't looking at. + useEffect(() => { + const video = videoRef.current; + if (!video) return; + if (paused) { + try { + video.pause(); + } catch { + /* ignore */ + } + } else { + try { + void video.play(); + } catch { + /* ignore */ + } + } + }, [paused]); + + if (!voice?.isScreenSharing) return null; + + return ( + <div className={styles.wrap}> + <div className={styles.videoFrame}> + <video + ref={videoRef} + className={styles.video} + autoPlay + playsInline + muted + /> + {!hasTrack && ( + <div className={styles.placeholder}> + Starting your stream preview… + </div> + )} + {paused && hasTrack && ( + <div className={styles.pauseOverlay}> + <div className={styles.pauseTitle}> + Your stream is still running! + </div> + <div className={styles.pauseBody}> + We've paused this preview to save your resources. + </div> + </div> + )} + </div> + <div className={styles.label}>You are live</div> + </div> + ); +} diff --git a/packages/shared/src/components/voice/SignalStrengthIcon.tsx b/packages/shared/src/components/voice/SignalStrengthIcon.tsx new file mode 100644 index 0000000..c4c0380 --- /dev/null +++ b/packages/shared/src/components/voice/SignalStrengthIcon.tsx @@ -0,0 +1,20 @@ +import { CellSignalFull, CellSignalMedium, CellSignalLow, CellSignalSlash } from '@phosphor-icons/react'; +import { ConnectionQuality } from 'livekit-client'; + +interface SignalStrengthIconProps { + quality: ConnectionQuality; + size?: number; +} + +export function SignalStrengthIcon({ quality, size = 18 }: SignalStrengthIconProps) { + if (quality === ConnectionQuality.Excellent) { + return <CellSignalFull size={size} weight="bold" color="var(--voice-status-success)" />; + } + if (quality === ConnectionQuality.Good) { + return <CellSignalMedium size={size} weight="bold" color="var(--voice-status-warning)" />; + } + if (quality === ConnectionQuality.Poor) { + return <CellSignalLow size={size} weight="bold" color="var(--voice-status-danger)" />; + } + return <CellSignalSlash size={size} weight="bold" color="var(--voice-text-subtle)" />; +} diff --git a/packages/shared/src/components/voice/VoiceCallLayoutContent.module.css b/packages/shared/src/components/voice/VoiceCallLayoutContent.module.css new file mode 100644 index 0000000..ae025aa --- /dev/null +++ b/packages/shared/src/components/voice/VoiceCallLayoutContent.module.css @@ -0,0 +1,55 @@ +.focusLayoutContent { + position: relative; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: flex-start; + gap: 0; + --carousel-row-height: 180px; +} + +.focusLayoutMain { + position: relative; + width: 100%; + flex: 1 1 auto; + max-height: min(76dvh, calc(100% - 3rem)); + overflow: hidden; + z-index: 1; + display: flex; + align-items: center; + justify-content: center; +} + +.focusLayoutMain > * { + width: 100%; + height: 100%; +} + +.carouselWrapper { + flex: 0 0 auto; + min-width: 0; + overflow: hidden; + position: relative; + width: 100%; + max-height: var(--carousel-row-height); + padding: 0 12px 12px; + box-sizing: border-box; +} + +.carousel { + display: flex; + gap: 12px; + overflow-x: auto; + overflow-y: hidden; + scrollbar-width: thin; + padding: 4px 0; + height: 100%; +} + +.carouselItem { + flex: 0 0 auto; + height: calc(var(--carousel-row-height) - 24px); + aspect-ratio: 16 / 9; +} diff --git a/packages/shared/src/components/voice/VoiceCallLayoutContent.tsx b/packages/shared/src/components/voice/VoiceCallLayoutContent.tsx new file mode 100644 index 0000000..7c8ee42 --- /dev/null +++ b/packages/shared/src/components/voice/VoiceCallLayoutContent.tsx @@ -0,0 +1,94 @@ +import { useEffect } from 'react'; +import { observer } from 'mobx-react-lite'; +import type { VoiceParticipantSnapshot } from '@brycord/matrix-client'; +import VoiceStore from '@app/stores/VoiceStore'; +import { VoiceGridLayout } from './VoiceGridLayout'; +import { VoiceParticipantTile } from './VoiceParticipantTile'; +import styles from './VoiceCallLayoutContent.module.css'; + +interface VoiceCallLayoutContentProps { + participants: VoiceParticipantSnapshot[]; +} + +/** + * Switches between grid layout (no screen share currently being + * WATCHED) and focus layout (the local user is actively watching + * a screen share — large primary tile + carousel of everyone else). + * + * The trigger changed with the opt-in streaming rework: previously + * any peer screen-sharing auto-flipped us into focus mode. Now we + * require the local user to explicitly click "Watch Stream" first + * — `p.hasScreenPublication` means the peer is sharing, but + * `p.screenSubscribed` is only true once the user opts in. This + * keeps the layout calm when multiple peers share simultaneously + * and lets the user pick which one to focus. + */ +export const VoiceCallLayoutContent = observer(function VoiceCallLayoutContent({ + participants, +}: VoiceCallLayoutContentProps) { + const focusedScreenSharer = participants.find( + (p) => p.hasScreenPublication && p.screenSubscribed, + ); + + // Flip the focused screen share to HIGH quality whenever it + // enters focus mode, and implicitly leave it alone (or get + // downgraded by leaving the watch set entirely) when the user + // unwatches. Runs only when the focused identity actually + // changes so we don't re-call setVideoQuality on every render. + const focusedIdentity = focusedScreenSharer?.identity; + useEffect(() => { + if (!focusedIdentity) return; + VoiceStore.setStreamQuality(focusedIdentity, 'screen', 'high'); + }, [focusedIdentity]); + + if (!focusedScreenSharer) { + return <VoiceGridLayout participants={participants} />; + } + + // Build the carousel items the same way the grid does: a camera + // tile per participant, plus an extra screen-share tile for any + // other peer who is also sharing their screen. The currently- + // focused screen share is excluded from the carousel since it's + // already rendering in the main slot — otherwise it would show + // up twice. + const carouselItems: { + key: string; + participant: VoiceParticipantSnapshot; + showScreenShare: boolean; + }[] = []; + for (const p of participants) { + carouselItems.push({ + key: `${p.identity}|camera`, + participant: p, + showScreenShare: false, + }); + if (p.hasScreenPublication && p.identity !== focusedScreenSharer.identity) { + carouselItems.push({ + key: `${p.identity}|screen`, + participant: p, + showScreenShare: true, + }); + } + } + + // Focus mode — large screen tile + carousel of all participants + return ( + <div className={styles.focusLayoutContent}> + <div className={styles.focusLayoutMain}> + <VoiceParticipantTile participant={focusedScreenSharer} showScreenShare /> + </div> + <div className={styles.carouselWrapper}> + <div className={styles.carousel}> + {carouselItems.map((item) => ( + <div key={item.key} className={styles.carouselItem}> + <VoiceParticipantTile + participant={item.participant} + showScreenShare={item.showScreenShare} + /> + </div> + ))} + </div> + </div> + </div> + ); +}); diff --git a/packages/shared/src/components/voice/VoiceCallView.module.css b/packages/shared/src/components/voice/VoiceCallView.module.css new file mode 100644 index 0000000..bb67378 --- /dev/null +++ b/packages/shared/src/components/voice/VoiceCallView.module.css @@ -0,0 +1,159 @@ +.root { + display: grid; + grid-template-rows: auto minmax(0, 1fr) auto; + grid-template-areas: + 'header' + 'main' + 'footer'; + height: 100%; + width: 100%; + background-color: #000; + color: var(--voice-text-strong); + position: relative; + overflow: hidden; + --voice-hud-opacity: 0; + --voice-hud-pointer-events: none; + --voice-hud-transition-duration: 0ms; +} + +.root.pointerActive { + --voice-hud-opacity: 1; + --voice-hud-pointer-events: auto; + --voice-hud-transition-duration: 180ms; +} + +/* ── Header ─────────────────────────────────────────────────── */ + +.voiceHeader { + grid-area: header; + position: relative; + z-index: 30; + display: flex; + align-items: center; + justify-content: space-between; + padding: 14px 18px; + opacity: var(--voice-hud-opacity); + pointer-events: var(--voice-hud-pointer-events); + transition: opacity var(--voice-hud-transition-duration) cubic-bezier(0.2, 0, 0, 1); +} + +.voiceHeader::before { + content: ''; + position: absolute; + inset: 0; + background: var(--voice-header-gradient); + pointer-events: none; + z-index: -1; +} + +.headerLeftSection, +.headerRightSection { + display: flex; + align-items: center; + gap: 12px; +} + +.channelInfoContainer { + display: flex; + align-items: center; + gap: 6px; + color: var(--voice-text-strong); +} + +.channelIcon { + color: var(--voice-text-muted); +} + +.channelName { + font-size: 0.9375rem; + font-weight: 600; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.headerButton { + display: inline-flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: 9999px; + background: var(--voice-overlay-light); + color: var(--voice-text-strong); + border: none; + cursor: pointer; + transition: background-color 150ms; +} + +.headerButton:hover { + background: var(--voice-overlay-light-strong); +} + +/* ── Main content ────────────────────────────────────────────── */ + +.mainContent { + grid-area: main; + display: flex; + flex-direction: column; + min-height: 0; + min-width: 0; +} + +/* ── Control bar container ───────────────────────────────────── */ + +.controlBarContainer { + grid-area: footer; + position: relative; + z-index: 30; + display: flex; + justify-content: center; + padding: 28px 24px calc(20px + env(safe-area-inset-bottom, 0px)); + pointer-events: none; + opacity: var(--voice-hud-opacity); + transition: opacity var(--voice-hud-transition-duration) cubic-bezier(0.2, 0, 0, 1); +} + +.controlBarContainer::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 64px; + background: var(--voice-footer-gradient); + pointer-events: none; + transform: translateY(-100%); +} + +.controlBarContainer > * { + pointer-events: auto; +} + +/* ── Stats overlay ───────────────────────────────────────────── */ + +.statsOverlay { + position: absolute; + top: 64px; + right: 18px; + z-index: 40; + background: var(--voice-overlay-strong); + color: var(--voice-text-strong); + padding: 12px 14px; + border-radius: 8px; + min-width: 180px; + font-size: 0.8125rem; + box-shadow: 0 8px 24px var(--voice-shadow-strong); +} + +.statsHeader { + font-weight: 700; + margin-bottom: 6px; +} + +.statsRow { + display: flex; + justify-content: space-between; + gap: 12px; + padding: 2px 0; +} diff --git a/packages/shared/src/components/voice/VoiceCallView.tsx b/packages/shared/src/components/voice/VoiceCallView.tsx new file mode 100644 index 0000000..48f0d5a --- /dev/null +++ b/packages/shared/src/components/voice/VoiceCallView.tsx @@ -0,0 +1,63 @@ +import { Hash } from '@phosphor-icons/react'; +import { useVoice } from '../../contexts/VoiceContext'; +import { ScreenSharePreview } from './ScreenSharePreview'; +import { VoiceGridLayout } from './VoiceGridLayout'; +import { VoiceControlBar } from './VoiceControlBar'; +import type { VoiceParticipantTileData } from './VoiceParticipantTile'; +import styles from './VoiceCallView.module.css'; + +interface VoiceCallViewProps { + channelId: string; +} + +interface RawParticipant { + userId: string; + username?: string; + avatarUrl?: string; + isMuted?: boolean; + isDeafened?: boolean; + isScreenSharing?: boolean; + [key: string]: any; +} + +export function VoiceCallView({ channelId }: VoiceCallViewProps) { + const voice = useVoice(); + + const rawParticipants: RawParticipant[] = + (voice?.voiceStates && voice.voiceStates[channelId]) || []; + const activeSpeakers: Set<string> = voice?.activeSpeakers || new Set(); + + const participants: VoiceParticipantTileData[] = rawParticipants.map((p) => ({ + userId: p.userId, + username: p.username || 'Unknown', + avatarUrl: p.avatarUrl, + isMuted: !!p.isMuted, + isSpeaking: activeSpeakers.has(p.userId), + isScreenSharing: !!p.isScreenSharing, + })); + + const channelName = voice?.activeChannelName || 'Voice Channel'; + + return ( + <div className={`${styles.root} ${styles.pointerActive}`} data-voice-call-root> + <div className={styles.voiceHeader}> + <div className={styles.headerLeftSection}> + <div className={styles.channelInfoContainer}> + <Hash size={20} weight="bold" className={styles.channelIcon} /> + <span className={styles.channelName}>{channelName}</span> + </div> + </div> + <div className={styles.headerRightSection} /> + </div> + + <div className={styles.mainContent}> + <ScreenSharePreview /> + <VoiceGridLayout participants={participants} /> + </div> + + <div className={styles.controlBarContainer}> + <VoiceControlBar /> + </div> + </div> + ); +} diff --git a/packages/shared/src/components/voice/VoiceConnectionStatus.module.css b/packages/shared/src/components/voice/VoiceConnectionStatus.module.css new file mode 100644 index 0000000..dabf822 --- /dev/null +++ b/packages/shared/src/components/voice/VoiceConnectionStatus.module.css @@ -0,0 +1,276 @@ +.voiceConnectionContainer { + display: flex; + flex-direction: column; + gap: 12px; + margin: 0; + padding: 8px 8px; + background-color: var(--panel-control-bg, var(--background-secondary)); + border: none; + width: 100%; + min-width: 0; + flex-shrink: 0; + box-sizing: border-box; + box-shadow: inset 0 1px 0 var(--user-area-divider-color); +} + +/* ── Status row ───────────────────────────────────────────────────────── */ + +.statusRow { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; +} + +.signalIcon { + display: flex; + align-items: center; + justify-content: center; + height: 24px; + width: 24px; + color: var(--status-online, #23a55a); + flex-shrink: 0; +} + +.statusButton { + cursor: pointer; + border: none; + background: transparent; + padding: 0; + text-align: left; + font-weight: 600; + font-size: 0.875rem; + line-height: 1.125rem; + color: var(--status-online, #23a55a); + user-select: none; + -webkit-user-select: none; + flex: 1; + min-width: 0; + font-family: inherit; +} + +.statusConnecting { + color: var(--status-warning, #f0b232); +} + +.statusReconnecting { + color: var(--status-warning, #f0b232); +} + +.statusFailed { + color: var(--status-danger, #f23f43); +} + +.statusDisconnected { + color: var(--status-danger, #f23f43); +} + +.controls { + display: flex; + align-items: center; + gap: 4px; + flex-shrink: 0; +} + +.controlButton { + display: flex; + align-items: center; + justify-content: center; + height: 32px; + width: 32px; + background-color: transparent; + color: var(--interactive-normal, var(--text-secondary)); + border: none; + border-radius: var(--radius-md, 6px); + cursor: pointer; + position: relative; + padding: 0; + flex-shrink: 0; + transition: background-color 0.15s, color 0.15s; +} + +.controlButton:hover { + background-color: color-mix(in srgb, var(--text-primary) 10%, transparent); + color: var(--text-primary); +} + +.controlButton.selected { + background-color: var(--background-modifier-selected, color-mix(in srgb, var(--text-primary) 12%, transparent)); + color: var(--text-primary); +} + +.controlButtonDanger:hover { + background-color: color-mix(in srgb, var(--status-danger, #f23f43) 15%, transparent); + color: var(--status-danger, #f23f43); +} + +.icon { + height: 20px; + width: 20px; +} + +/* ── Connection info ─────────────────────────────────────────────────── */ + +.connectionInfo { + display: flex; + flex-direction: column; + gap: 8px; +} + +.channelSourceRow { + display: flex; + align-items: center; + min-width: 0; +} + +.channelSourceLink { + display: inline-flex; + align-items: center; + min-width: 0; + max-width: 100%; + border: none; + background: transparent; + padding: 0; + font-size: 0.75rem; + line-height: 1rem; + color: var(--text-secondary); + text-decoration: none; + cursor: pointer; + font-family: inherit; + text-align: left; +} + +.channelSourceLink:hover { + color: var(--text-secondary); + text-decoration: underline; +} + +.channelSourceText { + display: inline-flex; + align-items: center; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: inherit; +} + +.channelSourceChannel { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; + color: inherit; +} + +.channelSourceSeparator { + margin: 0 2px; + flex-shrink: 0; + color: inherit; +} + +.channelSourceGuild { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: inherit; +} + +.connectionIdRow { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; + font-size: 0.75rem; + line-height: 1rem; + color: var(--text-secondary); +} + +.connectionIdValue { + flex: 1 1 auto; + min-width: 0; + display: flex; + align-items: center; +} + +.connectionIdValueText { + display: inline-block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.connectionIdIcon { + height: 16px; + width: 16px; + color: var(--text-tertiary); + flex-shrink: 0; +} + +/* ── Media section ───────────────────────────────────────────────────── */ + +.mediaSection { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 4px; +} + +.mediaButton { + display: flex; + align-items: center; + justify-content: center; + height: 32px; + width: 100%; + background-color: color-mix(in srgb, var(--background-modifier-hover) 70%, transparent); + color: var(--interactive-normal, var(--text-secondary)); + border: 1px solid var(--background-modifier-hover); + border-radius: var(--radius-md, 6px); + cursor: pointer; + position: relative; + padding: 0; + transition: background-color 0.15s, color 0.15s, border-color 0.15s; + font-family: inherit; +} + +.mediaButton:hover:not(:disabled) { + background-color: color-mix(in srgb, var(--text-primary) 10%, transparent); + color: var(--text-primary); + border-color: var(--background-modifier-selected, var(--background-modifier-hover)); +} + +.mediaButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.mediaButton.cameraActive, +.mediaButton.screenShareActive { + background-color: color-mix(in srgb, var(--status-online, #23a55a) 15%, transparent); + color: var(--status-online, #23a55a); + border-color: color-mix(in srgb, var(--status-online, #23a55a) 30%, transparent); +} + +.mediaButton.cameraActive:hover:not(:disabled), +.mediaButton.screenShareActive:hover:not(:disabled) { + background-color: color-mix(in srgb, var(--status-online, #23a55a) 20%, transparent); + color: var(--status-online, #23a55a); +} + +.mediaIcon { + height: 20px; + width: 20px; +} + +/* ── Active state for the noise-suppression toggle ───────────────── + Added so the button reads as "on" at rest, not just on hover. Uses + the same hover palette the other controls pick up when moused over, + so a user who's never hovered the button can still see it's active. */ +.controlButton.controlButtonActive { + color: var(--brand-primary, #5865f2); + background-color: var(--background-modifier-hover, rgba(79, 84, 92, 0.35)); +} +.controlButton.controlButtonActive:hover { + color: var(--brand-primary, #5865f2); + background-color: var(--background-modifier-selected, rgba(79, 84, 92, 0.55)); +} diff --git a/packages/shared/src/components/voice/VoiceConnectionStatus.tsx b/packages/shared/src/components/voice/VoiceConnectionStatus.tsx new file mode 100644 index 0000000..cbe1da6 --- /dev/null +++ b/packages/shared/src/components/voice/VoiceConnectionStatus.tsx @@ -0,0 +1,259 @@ +/** + * VoiceConnectionStatus — sticky widget above the UserArea showing the + * current LiveKit connection state when the user is in a voice call. + * Ported 1:1 from the new UI's layout, adapted for our Convex + * `VoiceContext`: + * + * ┌─────────────────────────────────────┐ + * │ 📶 Voice Connected ⊘ 📞× │ + * │ #channel-name │ + * │ 🖥 <participant id> │ + * │ [📷] [🖥] │ + * └─────────────────────────────────────┘ + * + * Missing pieces (camera toggle, retry/rejoin flow, per-participant + * signal quality) are stubbed with safe fallbacks since our voice + * context doesn't model them yet. The screen-share button still + * works because `VoiceContext.setScreenSharing` is wired. + */ +import { useEffect, useState } from 'react'; +import { + Camera, + CameraSlash, + Desktop, + MonitorPlay, + PhoneX, + Waveform, +} from '@phosphor-icons/react'; +import { useNavigate } from 'react-router-dom'; +import { Tooltip } from '@discord-clone/ui'; +import { useVoice } from '../../contexts/VoiceContext'; +import styles from './VoiceConnectionStatus.module.css'; + +/** + * Read the user's saved voice settings (Settings → Voice & Video) + * from localStorage so we can show a persistent active state on the + * noise-suppression button. Also re-reads on the change event fired + * by UserSettingsModal so toggling the setting updates the widget + * without a reload. + */ +function useVoiceSettingsSnapshot() { + const [settings, setSettings] = useState<{ noiseSuppression: boolean }>( + () => readVoiceSettings(), + ); + useEffect(() => { + const onChange = (e: Event) => { + const detail = (e as CustomEvent<{ noiseSuppression?: boolean }>).detail; + if (detail) setSettings((prev) => ({ ...prev, ...detail })); + else setSettings(readVoiceSettings()); + }; + window.addEventListener('brycord:voice-settings-changed', onChange); + return () => + window.removeEventListener('brycord:voice-settings-changed', onChange); + }, []); + return settings; +} + +function readVoiceSettings(): { noiseSuppression: boolean } { + try { + const raw = localStorage.getItem('voiceSettings'); + if (!raw) return { noiseSuppression: true }; + const parsed = JSON.parse(raw); + return { + noiseSuppression: + typeof parsed?.noiseSuppression === 'boolean' + ? parsed.noiseSuppression + : true, + }; + } catch { + return { noiseSuppression: true }; + } +} + +interface StatusDescriptor { + text: string; + className: string; +} + +function describeStatus(state: string): StatusDescriptor { + switch (state) { + case 'connecting': + return { text: 'Connecting…', className: styles.statusConnecting }; + case 'reconnecting': + return { text: 'Reconnecting…', className: styles.statusReconnecting }; + case 'disconnecting': + return { text: 'Disconnecting…', className: styles.statusConnecting }; + case 'error': + case 'failed': + return { text: 'Call failed', className: styles.statusFailed }; + case 'connected': + return { text: 'Voice Connected', className: '' }; + default: + return { text: 'Disconnected', className: styles.statusFailed }; + } +} + +export function VoiceConnectionStatus() { + const voice = useVoice() as any; + const navigate = useNavigate(); + const voiceSettings = useVoiceSettingsSnapshot(); + + if (!voice?.activeChannelId) return null; + const state: string = voice.connectionState || 'disconnected'; + if (state === 'disconnected') return null; + + const channelName: string = voice.activeChannelName || 'Voice Channel'; + const channelId: string = voice.activeChannelId; + const status = describeStatus(state); + const isConnected = state === 'connected'; + + const screenShareActive: boolean = !!voice.isScreenSharing; + const cameraActive: boolean = !!voice.isCameraOn; + const noiseSuppressionActive = voiceSettings.noiseSuppression; + + // Derived participant identity for the connection-id row. Our + // LiveKit identity is just the Convex user id (no `@user:domain` + // prefix), so show the trailing 12 chars for a compact display. + const localIdentity: string | undefined = + voice.room?.localParticipant?.identity ?? undefined; + const connectionId = localIdentity ? localIdentity.slice(-12) : null; + + const handleJump = () => { + navigate(`/channels/home/${channelId}`); + }; + + const handleDisconnect = () => { + voice.disconnectVoice?.(); + }; + + const handleToggleScreenShare = () => { + voice.setScreenSharing?.(!screenShareActive); + }; + + const handleToggleCamera = () => { + voice.toggleCamera?.(); + }; + + const handleOpenVoiceSettings = () => { + window.dispatchEvent( + new CustomEvent('brycord:open-user-settings', { + detail: { tab: 'voice' }, + }), + ); + }; + + return ( + <div className={styles.voiceConnectionContainer}> + <div className={styles.statusRow}> + {isConnected && ( + <div className={styles.signalIcon} title="Connection quality"> + <Waveform size={16} weight="fill" /> + </div> + )} + <button + type="button" + className={`${styles.statusButton} ${status.className}`} + onClick={handleJump} + title={status.text} + > + {status.text} + </button> + <div className={styles.controls}> + <Tooltip + content={ + noiseSuppressionActive + ? 'Noise Suppression · On' + : 'Noise Suppression · Off' + } + placement="top" + > + <button + type="button" + className={`${styles.controlButton} ${ + noiseSuppressionActive ? styles.controlButtonActive : '' + }`} + onClick={handleOpenVoiceSettings} + aria-label="Noise Suppression" + aria-pressed={noiseSuppressionActive} + > + <Waveform weight="fill" className={styles.icon} /> + </button> + </Tooltip> + <Tooltip content="Disconnect" placement="top"> + <button + type="button" + className={`${styles.controlButton} ${styles.controlButtonDanger}`} + onClick={handleDisconnect} + aria-label="Disconnect" + > + <PhoneX weight="fill" className={styles.icon} /> + </button> + </Tooltip> + </div> + </div> + + <div className={styles.connectionInfo}> + <div className={styles.channelSourceRow}> + <button + type="button" + className={styles.channelSourceLink} + onClick={handleJump} + aria-label={`Jump to ${channelName}`} + > + <span className={styles.channelSourceText}> + <span className={styles.channelSourceChannel}>{channelName}</span> + </span> + </button> + </div> + {connectionId && ( + <div className={styles.connectionIdRow}> + <Desktop weight="regular" className={styles.connectionIdIcon} /> + <div className={styles.connectionIdValue}> + <span + className={styles.connectionIdValueText} + title={localIdentity} + > + {connectionId} + </span> + </div> + </div> + )} + </div> + + <div className={styles.mediaSection}> + <Tooltip + content={cameraActive ? 'Turn Off Camera' : 'Turn On Camera'} + placement="top" + > + <button + type="button" + className={`${styles.mediaButton} ${cameraActive ? styles.cameraActive : ''}`} + onClick={handleToggleCamera} + aria-label={cameraActive ? 'Turn Off Camera' : 'Turn On Camera'} + aria-pressed={cameraActive} + > + {cameraActive ? ( + <Camera weight="fill" className={styles.mediaIcon} /> + ) : ( + <CameraSlash weight="fill" className={styles.mediaIcon} /> + )} + </button> + </Tooltip> + <Tooltip + content={screenShareActive ? 'Stop Sharing' : 'Share Your Screen'} + placement="top" + > + <button + type="button" + className={`${styles.mediaButton} ${screenShareActive ? styles.screenShareActive : ''}`} + onClick={handleToggleScreenShare} + aria-label={screenShareActive ? 'Stop Sharing' : 'Share Your Screen'} + aria-pressed={screenShareActive} + > + <MonitorPlay weight="fill" className={styles.mediaIcon} /> + </button> + </Tooltip> + </div> + </div> + ); +} diff --git a/packages/shared/src/components/voice/VoiceControlBar.module.css b/packages/shared/src/components/voice/VoiceControlBar.module.css new file mode 100644 index 0000000..6f2b2b5 --- /dev/null +++ b/packages/shared/src/components/voice/VoiceControlBar.module.css @@ -0,0 +1,84 @@ +.container { + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; +} + +.button { + display: flex; + align-items: center; + justify-content: center; + width: 56px; + height: 56px; + border-radius: 9999px; + border: none; + cursor: pointer; + background: none; + transition-duration: 150ms; + transition-property: background-color, color; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + color: var(--voice-text-strong); + position: relative; +} + +.icon { + width: 28px; + height: 28px; +} + +.buttonUnmuted { + background-color: var(--voice-surface-4); + color: var(--voice-text-strong); +} + +.buttonUnmuted:hover { + background-color: var(--voice-surface-5); +} + +.buttonMuted { + background-color: var(--voice-status-danger-bg-solid); + color: var(--voice-status-danger); +} + +.buttonMuted:hover { + background-color: var(--voice-status-danger-bg-strong-solid); +} + +.buttonCameraOn, +.buttonScreenShareOn { + background-color: var(--voice-status-success-bg-solid); + color: var(--voice-status-success); +} + +.buttonCameraOn:hover, +.buttonScreenShareOn:hover { + background-color: color-mix(in srgb, var(--voice-status-success) 30%, var(--voice-surface-2)); +} + +.buttonDisconnect { + background-color: var(--voice-status-danger-bg-solid); + color: var(--voice-status-danger); +} + +.buttonDisconnect:hover { + background-color: var(--voice-status-danger-bg-strong-solid); +} + +/* Settings cog overlay (top-right of mic/camera buttons) */ +.settingsButton { + position: absolute; + top: -4px; + right: -4px; + display: flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + border-radius: 9999px; + background-color: var(--background-tertiary); + color: var(--text-primary); + box-shadow: 0 0 0 2px var(--background-primary); + border: none; + cursor: pointer; +} diff --git a/packages/shared/src/components/voice/VoiceControlBar.tsx b/packages/shared/src/components/voice/VoiceControlBar.tsx new file mode 100644 index 0000000..dad6a7f --- /dev/null +++ b/packages/shared/src/components/voice/VoiceControlBar.tsx @@ -0,0 +1,108 @@ +import { + Microphone, + MicrophoneSlash, + SpeakerHigh, + SpeakerSlash, + VideoCamera, + VideoCameraSlash, + Monitor, + MonitorPlay, + PhoneDisconnect, +} from '@phosphor-icons/react'; +import { useVoice } from '../../contexts/VoiceContext'; +import styles from './VoiceControlBar.module.css'; + +export function VoiceControlBar() { + const voice = useVoice() as any; + + const isMuted = !!voice?.isMuted; + const isDeafened = !!voice?.isDeafened; + const isScreenSharing = !!voice?.isScreenSharing; + const cameraOn = !!voice?.isCameraOn; + + const handleMute = () => { + void voice?.toggleMute?.(); + }; + const handleDeafen = () => { + void voice?.toggleDeafen?.(); + }; + const handleCamera = () => { + void voice?.toggleCamera?.(); + }; + const handleScreenShare = () => { + void voice?.setScreenSharing?.(!isScreenSharing); + }; + const handleDisconnect = () => { + void voice?.disconnectVoice?.(); + }; + + return ( + <div className={styles.container}> + {/* Microphone */} + <button + type="button" + className={`${styles.button} ${isMuted ? styles.buttonMuted : styles.buttonUnmuted}`} + onClick={handleMute} + aria-label={isMuted ? 'Unmute' : 'Mute'} + > + {isMuted ? ( + <MicrophoneSlash className={styles.icon} weight="fill" /> + ) : ( + <Microphone className={styles.icon} weight="fill" /> + )} + </button> + + {/* Deafen */} + <button + type="button" + className={`${styles.button} ${isDeafened ? styles.buttonMuted : styles.buttonUnmuted}`} + onClick={handleDeafen} + aria-label={isDeafened ? 'Undeafen' : 'Deafen'} + > + {isDeafened ? ( + <SpeakerSlash className={styles.icon} weight="fill" /> + ) : ( + <SpeakerHigh className={styles.icon} weight="fill" /> + )} + </button> + + {/* Camera (stub) */} + <button + type="button" + className={`${styles.button} ${cameraOn ? styles.buttonCameraOn : styles.buttonUnmuted}`} + onClick={handleCamera} + aria-label={cameraOn ? 'Disable camera' : 'Enable camera'} + > + {cameraOn ? ( + <VideoCamera className={styles.icon} weight="fill" /> + ) : ( + <VideoCameraSlash className={styles.icon} weight="fill" /> + )} + </button> + + {/* Screen share */} + <button + type="button" + className={`${styles.button} ${isScreenSharing ? styles.buttonScreenShareOn : styles.buttonUnmuted}`} + onClick={handleScreenShare} + aria-label={isScreenSharing ? 'Stop sharing screen' : 'Share screen'} + > + {isScreenSharing ? ( + <Monitor className={styles.icon} weight="fill" /> + ) : ( + <MonitorPlay className={styles.icon} weight="fill" /> + )} + </button> + + {/* Disconnect */} + <button + type="button" + className={`${styles.button} ${styles.buttonDisconnect}`} + onClick={handleDisconnect} + aria-label="Disconnect" + > + <PhoneDisconnect className={styles.icon} weight="fill" /> + </button> + </div> + ); +} diff --git a/packages/shared/src/components/voice/VoiceGridLayout.module.css b/packages/shared/src/components/voice/VoiceGridLayout.module.css new file mode 100644 index 0000000..a83061a --- /dev/null +++ b/packages/shared/src/components/voice/VoiceGridLayout.module.css @@ -0,0 +1,89 @@ +.gridContainer { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + container-type: size; + container-name: voice-grid; + overflow: hidden; +} + +.grid { + --voice-grid-columns: 1; + --voice-grid-gap: 12px; + --voice-grid-side-padding: 12px; + --voice-grid-vertical-padding: 14px; + display: flex; + flex-wrap: wrap; + justify-content: center; + align-content: center; + width: 100%; + height: 100%; + padding: var(--voice-grid-vertical-padding) var(--voice-grid-side-padding); + gap: var(--voice-grid-gap); + box-sizing: border-box; +} + +.gridItem { + width: calc( + (100% - (var(--voice-grid-columns) - 1) * var(--voice-grid-gap)) / var(--voice-grid-columns) + ); + max-width: calc( + (100% - (var(--voice-grid-columns) - 1) * var(--voice-grid-gap)) / var(--voice-grid-columns) + ); + aspect-ratio: 16 / 9; + min-width: 0; +} + +/* Single tile sizing — fill the container while preserving aspect ratio */ +.grid[data-tile-count='1'] { + align-content: center; +} + +.grid[data-tile-count='1'] .gridItem { + width: min( + calc(100cqw - (2 * var(--voice-grid-side-padding))), + calc((100cqh - (2 * var(--voice-grid-vertical-padding))) * 16 / 9) + ); + max-width: min( + calc(100cqw - (2 * var(--voice-grid-side-padding))), + calc((100cqh - (2 * var(--voice-grid-vertical-padding))) * 16 / 9) + ); +} + +/* Responsive column counts based on container size + participant count */ +@container voice-grid (min-width: 520px) and (min-height: 260px) { + .grid:has(> :nth-child(2)) { + --voice-grid-columns: 2; + } +} + +@container voice-grid (min-width: 860px) and (min-height: 360px) { + .grid:has(> :nth-child(5)) { + --voice-grid-columns: 3; + } +} + +@container voice-grid (min-width: 1180px) and (min-height: 460px) { + .grid:has(> :nth-child(10)) { + --voice-grid-columns: 4; + } +} + +/* Tighter gaps for crowded calls */ +.grid:has(> :nth-child(6)) { + --voice-grid-gap: 10px; +} + +.grid:has(> :nth-child(12)) { + --voice-grid-gap: 8px; +} + +.grid:has(> :nth-child(24)) { + --voice-grid-gap: 6px; +} + +.grid:has(> :nth-child(40)) { + --voice-grid-gap: 4px; +} diff --git a/packages/shared/src/components/voice/VoiceGridLayout.tsx b/packages/shared/src/components/voice/VoiceGridLayout.tsx new file mode 100644 index 0000000..7864f57 --- /dev/null +++ b/packages/shared/src/components/voice/VoiceGridLayout.tsx @@ -0,0 +1,22 @@ +import { VoiceParticipantTile, type VoiceParticipantTileData } from './VoiceParticipantTile'; +import styles from './VoiceGridLayout.module.css'; + +interface VoiceGridLayoutProps { + participants: VoiceParticipantTileData[]; +} + +export function VoiceGridLayout({ participants }: VoiceGridLayoutProps) { + const tileCount = participants.length; + + return ( + <div className={styles.gridContainer}> + <div className={styles.grid} data-tile-count={tileCount}> + {participants.map((p) => ( + <div key={p.userId} className={styles.gridItem}> + <VoiceParticipantTile participant={p} /> + </div> + ))} + </div> + </div> + ); +} diff --git a/packages/shared/src/components/voice/VoiceParticipantContextMenu.module.css b/packages/shared/src/components/voice/VoiceParticipantContextMenu.module.css new file mode 100644 index 0000000..fe2b0a3 --- /dev/null +++ b/packages/shared/src/components/voice/VoiceParticipantContextMenu.module.css @@ -0,0 +1,28 @@ +.volumeRow { + display: flex; + flex-direction: column; + gap: 6px; + padding: 8px 8px 10px; +} + +.volumeLabel { + display: flex; + align-items: center; + gap: 6px; + color: var(--text-secondary); + font-size: 0.8125rem; + font-weight: 500; +} + +.volumeValue { + margin-left: auto; + color: var(--text-tertiary); + font-variant-numeric: tabular-nums; +} + +.volumeSlider { + width: 100%; + height: 4px; + accent-color: var(--brand-primary); + cursor: pointer; +} diff --git a/packages/shared/src/components/voice/VoiceParticipantContextMenu.tsx b/packages/shared/src/components/voice/VoiceParticipantContextMenu.tsx new file mode 100644 index 0000000..431dd27 --- /dev/null +++ b/packages/shared/src/components/voice/VoiceParticipantContextMenu.tsx @@ -0,0 +1,162 @@ +import { + At, + MicrophoneSlash, + PencilSimple, + PhoneX, + SpeakerSimpleHigh, + SpeakerSimpleSlash, + User as UserIcon, +} from '@phosphor-icons/react'; +/** + * VoiceParticipantContextMenu — right-click menu for users in the + * voice channel sidebar. Includes the standard profile/message + * actions plus voice-specific ones (volume, server mute, server + * deafen, disconnect). Admin-only items are gated by the caller + * passing the corresponding callback — missing callbacks hide the + * row, same pattern MemberContextMenu uses. + */ +import { useEffect, useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; +import baseStyles from '../layout/ChannelListContextMenu.module.css'; +import styles from './VoiceParticipantContextMenu.module.css'; + +interface VoiceParticipantContextMenuProps { + x: number; + y: number; + onViewProfile: () => void; + onMessage: () => void; + /** Optional — only shown when in the same call as the target. */ + onVolumeChange?: (level: number) => void; + /** Current volume (0..2) for the slider. */ + currentVolume?: number; + /** Optional — admin-only actions. */ + onChangeNickname?: () => void; + onToggleServerMute?: () => void; + onToggleServerDeafen?: () => void; + onDisconnect?: () => void; + /** Current moderation state for labels. */ + isServerMuted?: boolean; + isServerDeafened?: boolean; + onClose: () => void; +} + +export function VoiceParticipantContextMenu({ + x, + y, + onViewProfile, + onMessage, + onVolumeChange, + currentVolume = 1, + onChangeNickname, + onToggleServerMute, + onToggleServerDeafen, + onDisconnect, + isServerMuted = false, + isServerDeafened = false, + onClose, +}: VoiceParticipantContextMenuProps) { + const ref = useRef<HTMLDivElement>(null); + // Local slider state so dragging doesn't clobber onClose's + // click-outside detection. Applied to the store on change. + const [volume, setVolume] = useState(Math.round((currentVolume ?? 1) * 100)); + + useEffect(() => { + const handleClick = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) { + onClose(); + } + }; + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + document.addEventListener('mousedown', handleClick); + document.addEventListener('keydown', handleEscape); + return () => { + document.removeEventListener('mousedown', handleClick); + document.removeEventListener('keydown', handleEscape); + }; + }, [onClose]); + + const style: React.CSSProperties = { + top: Math.min(y, window.innerHeight - 320), + left: Math.min(x, window.innerWidth - 220), + }; + + const hasAdminSection = !!(onChangeNickname || onToggleServerMute || onToggleServerDeafen || onDisconnect); + + return createPortal( + <div className={baseStyles.overlay}> + <div className={baseStyles.menu} ref={ref} style={style}> + <button type="button" className={baseStyles.menuItem} onClick={onViewProfile}> + <UserIcon size={18} weight="fill" /> + <span>View Profile</span> + </button> + <button type="button" className={baseStyles.menuItem} onClick={onMessage}> + <At size={18} weight="bold" /> + <span>Message</span> + </button> + + {onVolumeChange && ( + <> + <div className={baseStyles.separator} /> + <div className={styles.volumeRow}> + <div className={styles.volumeLabel}> + <SpeakerSimpleHigh size={16} weight="fill" /> + <span>User Volume</span> + <span className={styles.volumeValue}>{volume}%</span> + </div> + <input + type="range" + min={0} + max={200} + step={1} + value={volume} + onChange={(e) => { + const next = Number(e.target.value); + setVolume(next); + onVolumeChange(next / 100); + }} + className={styles.volumeSlider} + /> + </div> + </> + )} + + {hasAdminSection && <div className={baseStyles.separator} />} + + {onChangeNickname && ( + <button type="button" className={baseStyles.menuItem} onClick={onChangeNickname}> + <PencilSimple size={18} weight="fill" /> + <span>Change Nickname</span> + </button> + )} + + {onToggleServerMute && ( + <button type="button" className={baseStyles.menuItem} onClick={onToggleServerMute}> + <MicrophoneSlash size={18} weight="fill" /> + <span>{isServerMuted ? 'Unmute' : 'Server Mute'}</span> + </button> + )} + + {onToggleServerDeafen && ( + <button type="button" className={baseStyles.menuItem} onClick={onToggleServerDeafen}> + <SpeakerSimpleSlash size={18} weight="fill" /> + <span>{isServerDeafened ? 'Undeafen' : 'Server Deafen'}</span> + </button> + )} + + {onDisconnect && ( + <button + type="button" + className={`${baseStyles.menuItem} ${baseStyles.menuItemDanger}`} + onClick={onDisconnect} + > + <PhoneX size={18} weight="fill" /> + <span>Disconnect</span> + </button> + )} + </div> + </div>, + document.body, + ); +} diff --git a/packages/shared/src/components/voice/VoiceParticipantItem.module.css b/packages/shared/src/components/voice/VoiceParticipantItem.module.css new file mode 100644 index 0000000..7945312 --- /dev/null +++ b/packages/shared/src/components/voice/VoiceParticipantItem.module.css @@ -0,0 +1,74 @@ +.item { + display: flex; + align-items: center; + gap: 0.375rem; + padding: 0.25rem 0.5rem; + border-radius: 0.375rem; + cursor: pointer; + transition: background-color 150ms, color 150ms; + color: var(--text-tertiary-muted, var(--text-muted)); +} + +.item:hover { + background-color: var(--background-modifier-hover); + color: var(--text-primary); +} + +/* Discord / Fluxer-style speaking indicator: a green ring around + the avatar. Box-shadow (not outline) so it follows the wrapper's + circular shape on every browser without needing outline-offset + tricks, and doesn't affect layout. `border-radius: 50%` is set + here because the Avatar wrapper itself doesn't round — only the + inner image/fallback do — so the shadow would render as a square + otherwise. */ +.avatarSpeaking { + border-radius: 50%; + box-shadow: 0 0 0 2px var(--status-online, #23a55a); + transition: box-shadow 100ms ease-out; +} + +.name { + flex: 1; + min-width: 0; + font-size: 0.875rem; + font-weight: 500; + line-height: 1.25rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.icons { + display: flex; + align-items: center; + gap: 4px; + flex-shrink: 0; +} + +.muteIcon { + color: var(--control-button-normal-text); +} + +.deafenIcon { + color: var(--control-button-normal-text); +} + +.streamIcon { + color: var(--brand-primary); +} + +.liveBadge { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0 5px; + height: 14px; + border-radius: 3px; + background-color: var(--status-danger, #f23f43); + color: #fff; + font-size: 0.625rem; + font-weight: 700; + letter-spacing: 0.03em; + line-height: 1; + text-transform: uppercase; +} diff --git a/packages/shared/src/components/voice/VoiceParticipantItem.tsx b/packages/shared/src/components/voice/VoiceParticipantItem.tsx new file mode 100644 index 0000000..441a86b --- /dev/null +++ b/packages/shared/src/components/voice/VoiceParticipantItem.tsx @@ -0,0 +1,203 @@ +import VoiceStore from '@app/stores/VoiceStore'; +import { + FriendManager, + MatrixClientManager, + type Member, + MemberManager, + VoiceModerationManager, + type VoiceParticipantSnapshot, + parseMatrixUserFromIdentity, +} from '@brycord/matrix-client'; +import { Avatar } from '@brycord/ui'; +import { MicrophoneSlash, SpeakerSlash } from '@phosphor-icons/react'; +import { observer } from 'mobx-react-lite'; +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { MemberProfileModal } from '../member/MemberProfileModal'; +import { NicknameModal } from '../member/NicknameModal'; +import { VoiceParticipantContextMenu } from './VoiceParticipantContextMenu'; +import styles from './VoiceParticipantItem.module.css'; + +interface VoiceParticipantItemProps { + participant: VoiceParticipantSnapshot; +} + +interface ContextMenuState { + x: number; + y: number; +} + +export const VoiceParticipantItem = observer(function VoiceParticipantItem({ participant }: VoiceParticipantItemProps) { + const navigate = useNavigate(); + const { displayName, avatar, isMuted, isSpeaking, isScreenSharing, isLocal, identity } = participant; + + // For the local user we read VoiceStore directly so every + // toggle shows instant feedback without waiting for our own + // state-event round-trip. Remote participants read + // `participant.isDeafened` which the snapshot pipeline has + // already merged from the `io.brycord.voice_state` state event + // published by that user. Pre-join and in-call use the same + // source of truth. + const isDeafened = isLocal ? VoiceStore.isDeafened : participant.isDeafened; + + const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null); + const [profileMember, setProfileMember] = useState<Member | null>(null); + const [nicknameMember, setNicknameMember] = useState<Member | null>(null); + + const matrixUserId = parseMatrixUserFromIdentity(identity); + + // Is the target participant in the same voice call as us right + // now? Needed for Volume + admin actions (mute/deafen/disconnect + // require the LiveKit data channel for disconnect, and + // moderation state events need the voice channel context). + const sameCallAsLocal = + VoiceStore.connectedChannelId !== null && VoiceStore.participants.some((p) => p.identity === identity); + + const handleContextMenu = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + setContextMenu({ x: e.clientX, y: e.clientY }); + }; + + const closeMenu = () => setContextMenu(null); + + // Resolve the Member object for this participant. Looks up the + // currently-connected voice channel first (since that's where + // the participant is visible), falling back to any room the + // user shares with the local user via MemberManager. + const getMember = (): Member | null => { + try { + const channelId = VoiceStore.connectedChannelId; + if (!channelId) return null; + const serverId = VoiceStore.connectedServerId ?? undefined; + const members = MemberManager.getInstance().getRoomMembers(channelId, serverId); + return members.find((m) => m.user.id === matrixUserId) ?? null; + } catch { + return null; + } + }; + + const handleViewProfile = () => { + const member = getMember(); + closeMenu(); + if (member) setProfileMember(member); + }; + + const handleMessage = async () => { + closeMenu(); + try { + const roomId = await FriendManager.getInstance().getOrCreateDM(matrixUserId); + navigate(`/channels/@me/${roomId}`); + } catch { + // Silent — the user can retry from the profile modal. + } + }; + + const handleVolumeChange = (level: number) => { + VoiceStore.setParticipantVolume(identity, level); + }; + + const handleChangeNickname = () => { + const member = getMember(); + closeMenu(); + if (member) setNicknameMember(member); + }; + + const handleToggleServerMute = async () => { + closeMenu(); + const channelId = VoiceStore.connectedChannelId; + if (!channelId) return; + const current = VoiceModerationManager.getInstance().getVoiceModeration(channelId, matrixUserId); + await VoiceStore.setTargetServerMuted(channelId, matrixUserId, !(current?.muted === true)); + }; + + const handleToggleServerDeafen = async () => { + closeMenu(); + const channelId = VoiceStore.connectedChannelId; + if (!channelId) return; + const current = VoiceModerationManager.getInstance().getVoiceModeration(channelId, matrixUserId); + await VoiceStore.setTargetServerDeafened(channelId, matrixUserId, !(current?.deafened === true)); + }; + + const handleDisconnect = async () => { + closeMenu(); + await VoiceStore.disconnectTarget(identity); + }; + + // Permission checks — recomputed fresh every render so they + // stay in sync with power level / membership changes. + const myUserId = (() => { + try { + return MatrixClientManager.getInstance().getClient().getUserId(); + } catch { + return null; + } + })(); + const isSelf = matrixUserId === myUserId; + + const connectedChannelId = VoiceStore.connectedChannelId; + const canModerate = connectedChannelId + ? VoiceModerationManager.getInstance().canModerateVoice(connectedChannelId) + : false; + const canRename = connectedChannelId ? MemberManager.getInstance().canSetOtherNicknames(connectedChannelId) : false; + + // Self-protection: admins can't moderate themselves via the + // context menu (renaming self is fine — you can always rename + // yourself, it's a separate power level). + const showChangeNickname = !isSelf && canRename; + const showAdminActions = !isSelf && canModerate && sameCallAsLocal; + const showVolume = !isSelf && sameCallAsLocal; + + const targetModeration = connectedChannelId + ? VoiceModerationManager.getInstance().getVoiceModeration(connectedChannelId, matrixUserId) + : null; + + return ( + <> + <button type="button" className={styles.item} onContextMenu={handleContextMenu}> + <Avatar + src={avatar} + fallback={displayName} + size={24} + className={isSpeaking ? styles.avatarSpeaking : undefined} + /> + <span className={styles.name}>{displayName}</span> + <div className={styles.icons}> + {isMuted && <MicrophoneSlash size={14} className={styles.muteIcon} />} + {isDeafened && <SpeakerSlash size={14} className={styles.deafenIcon} />} + {isScreenSharing && <span className={styles.liveBadge}>LIVE</span>} + </div> + </button> + + {contextMenu && ( + <VoiceParticipantContextMenu + x={contextMenu.x} + y={contextMenu.y} + onClose={closeMenu} + onViewProfile={handleViewProfile} + onMessage={handleMessage} + onVolumeChange={showVolume ? handleVolumeChange : undefined} + currentVolume={showVolume ? VoiceStore.getParticipantVolume(identity) : 1} + onChangeNickname={showChangeNickname ? handleChangeNickname : undefined} + onToggleServerMute={showAdminActions ? handleToggleServerMute : undefined} + onToggleServerDeafen={showAdminActions ? handleToggleServerDeafen : undefined} + onDisconnect={showAdminActions ? handleDisconnect : undefined} + isServerMuted={targetModeration?.muted === true} + isServerDeafened={targetModeration?.deafened === true} + /> + )} + + {profileMember && <MemberProfileModal isOpen member={profileMember} onClose={() => setProfileMember(null)} />} + + {nicknameMember && connectedChannelId && ( + <NicknameModal + isOpen + member={nicknameMember} + channelId={connectedChannelId} + serverId={VoiceStore.connectedServerId ?? undefined} + onClose={() => setNicknameMember(null)} + /> + )} + </> + ); +}); diff --git a/packages/shared/src/components/voice/VoiceParticipantTile.module.css b/packages/shared/src/components/voice/VoiceParticipantTile.module.css new file mode 100644 index 0000000..a8d2996 --- /dev/null +++ b/packages/shared/src/components/voice/VoiceParticipantTile.module.css @@ -0,0 +1,231 @@ +.lkParticipantTile { + position: relative; + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + aspect-ratio: 16 / 9; + border-radius: var(--radius-lg, 12px); + background-color: var(--voice-surface-2); + overflow: hidden; +} + +.video { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: var(--radius-lg, 12px); + display: block; +} + +.lkParticipantTile[data-source='screen_share'] { + background-color: var(--voice-surface-0); +} + +.lkParticipantTile[data-source='screen_share'] .video { + object-fit: contain; +} + +/* Avatar placeholder when no video */ +.lkParticipantPlaceholder { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--voice-surface-3); +} + +/* Speaking indicator — green border */ +.lkParticipantTile::after { + content: ''; + position: absolute; + inset: 0; + border-radius: var(--radius-lg, 12px); + border: 0px solid var(--voice-status-success); + transition: + border-width 0.4s, + border-color 0.4s; + transition-delay: 0.5s; + pointer-events: none; + box-sizing: border-box; +} + +.lkParticipantTile[data-speaking='true']::after { + border-width: 3.5px; + transition-delay: 0s; + transition-duration: 0.2s; +} + +/* Bottom metadata strip — name + mute icons */ +.lkParticipantMetadata { + position: absolute; + right: 0.5rem; + bottom: 0.5rem; + left: 0.5rem; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + z-index: 10; + pointer-events: none; +} + +.lkParticipantMetadataItem { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 0.375rem 0.5rem; + background-color: color-mix(in srgb, var(--voice-surface-1) 80%, transparent); + border-radius: var(--radius-md, 8px); + color: var(--voice-text-strong); + font-size: 0.875rem; + font-weight: 500; + max-width: 100%; + min-width: 0; +} + +.name { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 240px; +} + +.mutedIcon { + color: var(--voice-status-danger); +} + +/* Dim matrix-only participants (users known via MatrixRTC state events but + not yet visible on our LiveKit room — usually Element/Commet users who + have announced membership but haven't finished connecting their media). */ +.lkParticipantTile[data-connection-source='matrix'] .lkParticipantPlaceholder { + opacity: 0.55; +} + +.joiningLabel { + font-size: 0.75rem; + font-weight: 500; + font-style: italic; + color: var(--voice-text-muted, var(--voice-text-strong)); + opacity: 0.75; +} + +/* ── Stream opt-in placeholder ───────────────────────────────── + Rendered when a remote peer has published a camera or screen + track but the local user hasn't clicked Watch yet. Avatar fills + the tile just like the normal placeholder, with a LIVE pill + in the top-left and a brand-primary Watch button centered in + the bottom third. */ +.streamPlaceholder { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; + background-color: var(--voice-surface-3); +} + +.liveIndicator { + position: absolute; + top: 10px; + left: 10px; + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 8px; + background-color: rgba(0, 0, 0, 0.65); + border-radius: 999px; + color: #fff; + font-size: 0.6875rem; + font-weight: 800; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.liveDot { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: hsl(0, calc(80% * var(--saturation-factor, 1)), 60%); + box-shadow: 0 0 6px hsl(0, calc(80% * var(--saturation-factor, 1)), 60%); + animation: voiceLiveDotPulse 1.6s ease-in-out infinite; +} + +@keyframes voiceLiveDotPulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + +.watchStreamButton { + display: inline-flex; + align-items: center; + gap: 8px; + height: 40px; + padding: 0 18px; + background-color: var(--brand-primary); + border: none; + border-radius: 999px; + color: var(--text-on-brand-primary, #fff); + font: inherit; + font-size: 0.875rem; + font-weight: 700; + cursor: pointer; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.35); + transition: filter 0.12s, transform 0.12s; + -webkit-tap-highlight-color: transparent; +} + +.watchStreamButton:hover { + filter: brightness(1.08); +} + +.watchStreamButton:active { + filter: brightness(0.92); + transform: scale(0.98); +} + +/* ── Unwatch affordance ──────────────────────────────────────── + Small X pinned to the top-right of an actively subscribed + remote tile. Hidden by default, fades in on hover / focus so + it doesn't clutter the tile during normal viewing but is one + click away when the user wants to drop bandwidth. */ +.unwatchButton { + position: absolute; + top: 10px; + right: 10px; + display: inline-flex; + align-items: center; + justify-content: center; + width: 26px; + height: 26px; + padding: 0; + background-color: rgba(0, 0, 0, 0.65); + border: none; + border-radius: 50%; + color: #fff; + cursor: pointer; + opacity: 0; + visibility: hidden; + transition: opacity 0.12s ease, visibility 0.12s ease, background-color 0.12s; + z-index: 20; + -webkit-tap-highlight-color: transparent; +} + +.lkParticipantTile:hover .unwatchButton, +.lkParticipantTile:focus-within .unwatchButton { + opacity: 1; + visibility: visible; +} + +.unwatchButton:hover { + background-color: hsl(350, calc(80% * var(--saturation-factor, 1)), 55%); +} diff --git a/packages/shared/src/components/voice/VoiceParticipantTile.tsx b/packages/shared/src/components/voice/VoiceParticipantTile.tsx new file mode 100644 index 0000000..d5629a0 --- /dev/null +++ b/packages/shared/src/components/voice/VoiceParticipantTile.tsx @@ -0,0 +1,46 @@ +import { Avatar } from '@discord-clone/ui'; +import { Microphone, MicrophoneSlash } from '@phosphor-icons/react'; +import styles from './VoiceParticipantTile.module.css'; + +export interface VoiceParticipantTileData { + userId: string; + username: string; + avatarUrl?: string; + isMuted?: boolean; + isSpeaking?: boolean; + isScreenSharing?: boolean; +} + +interface VoiceParticipantTileProps { + participant: VoiceParticipantTileData; +} + +export function VoiceParticipantTile({ participant }: VoiceParticipantTileProps) { + const { username, avatarUrl, isMuted, isSpeaking } = participant; + + return ( + <div + className={styles.lkParticipantTile} + data-source="avatar" + data-speaking={isSpeaking ? 'true' : 'false'} + data-muted={isMuted ? 'true' : 'false'} + > + <div className={styles.lkParticipantPlaceholder}> + <Avatar src={avatarUrl} fallback={username} size={96} /> + </div> + + <div className={styles.lkParticipantMetadata}> + <div className={styles.lkParticipantMetadataItem}> + <span className={styles.name}>{username}</span> + </div> + <div className={styles.lkParticipantMetadataItem}> + {isMuted ? ( + <MicrophoneSlash size={16} weight="fill" className={styles.mutedIcon} /> + ) : ( + <Microphone size={16} weight="fill" /> + )} + </div> + </div> + </div> + ); +} diff --git a/packages/shared/src/components/voice/VoiceParticipantsList.module.css b/packages/shared/src/components/voice/VoiceParticipantsList.module.css new file mode 100644 index 0000000..4dae28a --- /dev/null +++ b/packages/shared/src/components/voice/VoiceParticipantsList.module.css @@ -0,0 +1,8 @@ +.container { + display: flex; + flex-direction: column; + gap: 0.125rem; + margin-top: 0.25rem; + margin-left: 1.5rem; + margin-right: 0.5rem; +} diff --git a/packages/shared/src/components/voice/VoiceParticipantsList.tsx b/packages/shared/src/components/voice/VoiceParticipantsList.tsx new file mode 100644 index 0000000..340bb3e --- /dev/null +++ b/packages/shared/src/components/voice/VoiceParticipantsList.tsx @@ -0,0 +1,30 @@ +import { observer } from 'mobx-react-lite'; +import VoiceStore from '@app/stores/VoiceStore'; +import { VoiceParticipantItem } from './VoiceParticipantItem'; +import styles from './VoiceParticipantsList.module.css'; + +interface VoiceParticipantsListProps { + channelId: string; +} + +export const VoiceParticipantsList = observer(function VoiceParticipantsList({ channelId }: VoiceParticipantsListProps) { + // If we're connected to this specific channel, show the live LiveKit + // participant list (full data: mute state, speaking, tracks). Otherwise + // fall back to MatrixRTC memberships so the sidebar can show who's + // currently in this voice channel — including users joined via Element + // or Commet — before you click join. + const participants = + VoiceStore.connectedChannelId === channelId + ? VoiceStore.participants + : VoiceStore.getParticipantsForChannel(channelId); + + if (participants.length === 0) return null; + + return ( + <div className={styles.container}> + {participants.map((participant) => ( + <VoiceParticipantItem key={participant.identity} participant={participant} /> + ))} + </div> + ); +}); diff --git a/packages/shared/src/components/voice/VoiceUserContextMenu.tsx b/packages/shared/src/components/voice/VoiceUserContextMenu.tsx new file mode 100644 index 0000000..55c1660 --- /dev/null +++ b/packages/shared/src/components/voice/VoiceUserContextMenu.tsx @@ -0,0 +1,223 @@ +/** + * VoiceUserContextMenu — right-click menu for a participant in the + * voice channel rows on the sidebar. Surfaces: + * + * - Personal volume slider (local, doesn't affect other clients) + * - Personal mute toggle (local only) + * - Server mute toggle (admins only) + * - Disconnect user from the call (admins only) + * - View profile (opens the big MemberProfileModal) + * + * Reuses `ChannelListContextMenu.module.css` for visual parity with + * the other context menus in the app. Permissions come from + * `api.roles.getMyPermissions` so only admins with the right power + * level see the destructive server-side actions. + */ +import { useEffect, useRef } from 'react'; +import { createPortal } from 'react-dom'; +import { useQuery } from 'convex/react'; +import { + MicrophoneSlash, + PhoneDisconnect, + ShieldCheck, + SpeakerSlash, + User as UserIcon, +} from '@phosphor-icons/react'; +import { api } from '../../../../../convex/_generated/api'; +import type { Id } from '../../../../../convex/_generated/dataModel'; +import { useVoice } from '../../contexts/VoiceContext'; +import menuStyles from '../layout/ChannelListContextMenu.module.css'; + +interface VoiceUserContextMenuProps { + x: number; + y: number; + userId: string; + username: string; + onViewProfile: () => void; + onClose: () => void; +} + +export function VoiceUserContextMenu({ + x, + y, + userId, + username, + onViewProfile, + onClose, +}: VoiceUserContextMenuProps) { + const ref = useRef<HTMLDivElement>(null); + const voice = useVoice() as any; + + const localUserId = + typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null; + const myPerms = useQuery( + api.roles.getMyPermissions, + localUserId ? { userId: localUserId as Id<'userProfiles'> } : 'skip', + ); + // Server-side mute / disconnect are gated on `move_members`. If + // your deployment doesn't expose that flag fall back to `manage_channels` + // so admins still get the actions — matches the previous behaviour. + const canModerate = + !!myPerms?.move_members || !!myPerms?.manage_channels; + + const isSelf = userId === localUserId; + const personalVolume: number = + voice?.getUserVolume?.(userId) ?? 100; + const isPersonallyMuted: boolean = !!voice?.isPersonallyMuted?.(userId); + const isServerMuted: boolean = !!voice?.isServerMuted?.(userId); + + useEffect(() => { + const handleClick = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) onClose(); + }; + const handleEsc = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + document.addEventListener('mousedown', handleClick); + document.addEventListener('keydown', handleEsc); + return () => { + document.removeEventListener('mousedown', handleClick); + document.removeEventListener('keydown', handleEsc); + }; + }, [onClose]); + + // Wider than the default channel-list menu so the volume slider + // doesn't feel cramped. Clamp to viewport to avoid clipping. + const WIDTH = 240; + const HEIGHT_ESTIMATE = 220; + const style: React.CSSProperties = { + top: Math.min(y, window.innerHeight - HEIGHT_ESTIMATE), + left: Math.min(x, window.innerWidth - WIDTH), + width: WIDTH, + }; + + const handlePersonalMute = () => { + voice?.togglePersonalMute?.(userId); + }; + const handleServerMute = async () => { + await voice?.serverMute?.(userId, !isServerMuted); + onClose(); + }; + const handleDisconnect = async () => { + await voice?.disconnectUser?.(userId); + onClose(); + }; + + return createPortal( + <div className={menuStyles.overlay}> + <div className={menuStyles.menu} ref={ref} style={style}> + {/* Username label — read-only, mirrors the "{Member} / Member" + header the new UI puts on its context menus. */} + <div + style={{ + padding: '10px 14px 4px', + fontSize: 12, + fontWeight: 700, + color: 'var(--text-tertiary, #a0a3a8)', + textTransform: 'uppercase', + letterSpacing: '0.04em', + }} + > + {username} + </div> + + <button + className={menuStyles.menuItem} + type="button" + onClick={() => { + onViewProfile(); + onClose(); + }} + > + <UserIcon size={18} weight="fill" /> + <span>View Profile</span> + </button> + + {!isSelf && ( + <> + <div className={menuStyles.separator} /> + + <button + className={menuStyles.menuItem} + type="button" + onClick={handlePersonalMute} + > + <MicrophoneSlash size={18} weight="fill" /> + <span>{isPersonallyMuted ? 'Unmute' : 'Mute'}</span> + </button> + + <div + style={{ + padding: '8px 14px 12px', + display: 'flex', + flexDirection: 'column', + gap: 6, + }} + > + <label + style={{ + fontSize: 12, + color: 'var(--text-secondary, #b5bac1)', + }} + > + User Volume · {personalVolume}% + </label> + <input + type="range" + min={0} + max={200} + value={personalVolume} + onChange={(e) => + voice?.setUserVolume?.(userId, Number(e.target.value)) + } + style={{ width: '100%' }} + /> + </div> + + {canModerate && ( + <> + <div className={menuStyles.separator} /> + <button + className={menuStyles.menuItem} + type="button" + onClick={handleServerMute} + > + <SpeakerSlash size={18} weight="fill" /> + <span> + {isServerMuted ? 'Unmute on Server' : 'Server Mute'} + </span> + </button> + <button + className={`${menuStyles.menuItem} ${menuStyles.menuItemDanger ?? ''}`} + type="button" + onClick={handleDisconnect} + style={{ color: 'var(--status-danger, #ed4245)' }} + > + <PhoneDisconnect size={18} weight="fill" /> + <span>Disconnect</span> + </button> + </> + )} + + {!canModerate && ( + <> + <div className={menuStyles.separator} /> + <button + className={menuStyles.menuItem} + type="button" + disabled + title="Requires Move Members permission" + style={{ opacity: 0.5, cursor: 'not-allowed' }} + > + <ShieldCheck size={18} weight="fill" /> + <span>Server Controls</span> + </button> + </> + )} + </> + )} + </div> + </div>, + document.body, + ); +} diff --git a/packages/shared/src/components/voice/index.ts b/packages/shared/src/components/voice/index.ts new file mode 100644 index 0000000..711f4dd --- /dev/null +++ b/packages/shared/src/components/voice/index.ts @@ -0,0 +1,3 @@ +export { VoiceConnectionStatus } from './VoiceConnectionStatus'; +export { VoiceParticipantsList } from './VoiceParticipantsList'; +export { VoiceParticipantItem } from './VoiceParticipantItem'; diff --git a/packages/shared/src/contexts/KeybindContext.tsx b/packages/shared/src/contexts/KeybindContext.tsx new file mode 100644 index 0000000..c924209 --- /dev/null +++ b/packages/shared/src/contexts/KeybindContext.tsx @@ -0,0 +1,316 @@ +/** + * KeybindContext — minimal localStorage-backed keybind registry. + * + * Defines a small set of built-in actions, each with a default combo. + * Stores user overrides in localStorage under `keybinds.combos`. + * Installs a single window keydown listener that, on a matching combo, + * dispatches `brycord:keybind:<actionId>` as a window CustomEvent — + * components listen for those events to execute the action. + * + * The context is the single source of truth for combo strings, so + * KeybindsTab can rebind while the listener picks up the change via + * `useEffect` re-subscription. + */ +import { + createContext, + useCallback, + useContext, + useEffect, + useMemo, + useState, + type ReactNode, +} from 'react'; + +export type KeybindCategory = 'voice' | 'navigation' | 'popouts' | 'messaging'; + +export interface KeybindAction { + id: string; + label: string; + description: string; + category: KeybindCategory; + defaultCombo: string; +} + +const DEFAULT_ACTIONS: KeybindAction[] = [ + { + id: 'voice.toggleMute', + label: 'Toggle Mute', + description: 'Mute or unmute your microphone while in a voice call.', + category: 'voice', + defaultCombo: 'Ctrl+Shift+M', + }, + { + id: 'voice.toggleDeafen', + label: 'Toggle Deafen', + description: 'Deafen or undeafen — stops all incoming audio.', + category: 'voice', + defaultCombo: 'Ctrl+Shift+D', + }, + { + id: 'voice.disconnect', + label: 'Disconnect from Call', + description: 'Leave the current voice call.', + category: 'voice', + defaultCombo: '', + }, + { + id: 'navigation.goToDMs', + label: 'Go to Direct Messages', + description: 'Jump to the direct-messages home view.', + category: 'navigation', + defaultCombo: 'Ctrl+Alt+H', + }, + { + id: 'navigation.focusSearch', + label: 'Focus Search', + description: 'Focus the channel search box.', + category: 'navigation', + defaultCombo: 'Ctrl+K', + }, + { + id: 'popouts.openUserSettings', + label: 'Open User Settings', + description: 'Open the user settings modal.', + category: 'popouts', + defaultCombo: 'Ctrl+,', + }, + { + id: 'popouts.openPins', + label: 'Toggle Pins', + description: 'Open or close the pinned messages popover.', + category: 'popouts', + defaultCombo: 'Ctrl+P', + }, + { + id: 'popouts.toggleMembers', + label: 'Toggle Member List', + description: 'Show or hide the channel members panel.', + category: 'popouts', + defaultCombo: 'Ctrl+U', + }, + { + id: 'popouts.openEmojiPicker', + label: 'Open Emoji Picker', + description: 'Open the emoji picker in the composer.', + category: 'popouts', + defaultCombo: 'Ctrl+E', + }, + { + id: 'popouts.openGifPicker', + label: 'Open GIF Picker', + description: 'Open the expression picker on the GIFs tab.', + category: 'popouts', + defaultCombo: 'Ctrl+G', + }, + { + id: 'messaging.markAllRead', + label: 'Mark Channel as Read', + description: 'Clear the unread indicator for the current channel.', + category: 'messaging', + defaultCombo: 'Escape', + }, + { + id: 'messaging.focusComposer', + label: 'Focus Composer', + description: 'Jump the caret to the message input box.', + category: 'messaging', + defaultCombo: 'Tab', + }, +]; + +const STORAGE_KEY = 'keybinds.combos'; + +function loadOverrides(): Record<string, string> { + if (typeof localStorage === 'undefined') return {}; + try { + const raw = localStorage.getItem(STORAGE_KEY); + if (!raw) return {}; + const parsed = JSON.parse(raw); + return typeof parsed === 'object' && parsed ? parsed : {}; + } catch { + return {}; + } +} + +function saveOverrides(overrides: Record<string, string>) { + if (typeof localStorage === 'undefined') return; + try { + localStorage.setItem(STORAGE_KEY, JSON.stringify(overrides)); + } catch { + /* ignore quota errors */ + } +} + +export function eventToCombo(e: KeyboardEvent): string | null { + // Pure modifier press — wait for a real key. + const key = e.key; + if (key === 'Control' || key === 'Shift' || key === 'Alt' || key === 'Meta') { + return null; + } + const parts: string[] = []; + if (e.ctrlKey) parts.push('Ctrl'); + if (e.shiftKey) parts.push('Shift'); + if (e.altKey) parts.push('Alt'); + if (e.metaKey) parts.push('Meta'); + // Normalise letters to uppercase; punctuation stays as-is. + const normalised = key.length === 1 ? key.toUpperCase() : key; + parts.push(normalised); + return parts.join('+'); +} + +export function formatCombo(combo: string): string { + if (!combo) return 'Unbound'; + return combo; +} + +interface KeybindContextValue { + actions: KeybindAction[]; + getCombo: (actionId: string) => string; + setCombo: (actionId: string, combo: string) => void; + resetCombo: (actionId: string) => void; + resetAll: () => void; + findConflict: (combo: string, ignoreActionId?: string) => string | null; +} + +const KeybindCtx = createContext<KeybindContextValue | null>(null); + +export function KeybindProvider({ children }: { children: ReactNode }) { + const [overrides, setOverrides] = useState<Record<string, string>>(() => loadOverrides()); + + const combos = useMemo<Record<string, string>>(() => { + const out: Record<string, string> = {}; + for (const action of DEFAULT_ACTIONS) { + out[action.id] = overrides[action.id] ?? action.defaultCombo; + } + return out; + }, [overrides]); + + const getCombo = useCallback( + (actionId: string): string => combos[actionId] ?? '', + [combos], + ); + + const findConflict = useCallback( + (combo: string, ignoreActionId?: string): string | null => { + if (!combo) return null; + for (const action of DEFAULT_ACTIONS) { + if (action.id === ignoreActionId) continue; + if ((combos[action.id] ?? '') === combo) return action.id; + } + return null; + }, + [combos], + ); + + const setCombo = useCallback( + (actionId: string, combo: string) => { + setOverrides((prev) => { + const next: Record<string, string> = { ...prev, [actionId]: combo }; + // Clear conflicting binding: last-write-wins, matches the + // new UI's "other binding loses" behaviour. + if (combo) { + for (const action of DEFAULT_ACTIONS) { + if (action.id === actionId) continue; + const current = next[action.id] ?? action.defaultCombo; + if (current === combo) { + next[action.id] = ''; + } + } + } + saveOverrides(next); + return next; + }); + }, + [], + ); + + const resetCombo = useCallback((actionId: string) => { + setOverrides((prev) => { + const next = { ...prev }; + delete next[actionId]; + saveOverrides(next); + return next; + }); + }, []); + + const resetAll = useCallback(() => { + setOverrides({}); + saveOverrides({}); + }, []); + + // Install the global keydown dispatcher. Uses capture phase so it + // wins against components that use keydown for their own shortcuts + // — rebinding in settings disables the default behaviour cleanly. + useEffect(() => { + const onKeyDown = (e: KeyboardEvent) => { + // Never intercept keys typed into inputs / contenteditable — + // a bare `Escape` would otherwise cancel active composition. + const target = e.target as HTMLElement | null; + if (target) { + const tag = target.tagName; + if ( + tag === 'INPUT' || + tag === 'TEXTAREA' || + target.isContentEditable + ) { + // Allow Ctrl- / Ctrl+Shift- combinations through — + // those are deliberate shortcuts, never accidental + // typing. Plain keys still wait for focus to leave. + if (!(e.ctrlKey || e.metaKey)) { + return; + } + } + } + const combo = eventToCombo(e); + if (!combo) return; + for (const action of DEFAULT_ACTIONS) { + if ((combos[action.id] ?? '') === combo) { + e.preventDefault(); + e.stopPropagation(); + window.dispatchEvent( + new CustomEvent(`brycord:keybind:${action.id}`), + ); + return; + } + } + }; + window.addEventListener('keydown', onKeyDown, { capture: true }); + return () => { + window.removeEventListener('keydown', onKeyDown, { + capture: true, + } as EventListenerOptions); + }; + }, [combos]); + + const value = useMemo<KeybindContextValue>( + () => ({ + actions: DEFAULT_ACTIONS, + getCombo, + setCombo, + resetCombo, + resetAll, + findConflict, + }), + [getCombo, setCombo, resetCombo, resetAll, findConflict], + ); + + return <KeybindCtx.Provider value={value}>{children}</KeybindCtx.Provider>; +} + +export function useKeybinds(): KeybindContextValue { + const ctx = useContext(KeybindCtx); + if (!ctx) { + // Fall back to a read-only default when used outside the + // provider (e.g. during SSR or in isolated tests). + return { + actions: DEFAULT_ACTIONS, + getCombo: (id) => + DEFAULT_ACTIONS.find((a) => a.id === id)?.defaultCombo ?? '', + setCombo: () => {}, + resetCombo: () => {}, + resetAll: () => {}, + findConflict: () => null, + }; + } + return ctx; +} diff --git a/packages/shared/src/contexts/ThemeContext.jsx b/packages/shared/src/contexts/ThemeContext.jsx index ef5abbf..4bd0d5e 100644 --- a/packages/shared/src/contexts/ThemeContext.jsx +++ b/packages/shared/src/contexts/ThemeContext.jsx @@ -6,44 +6,54 @@ const STORAGE_KEY = 'discord-theme'; export const THEMES = { LIGHT: 'theme-light', DARK: 'theme-dark', - ASH: 'theme-darker', - ONYX: 'theme-midnight', }; export const THEME_LABELS = { [THEMES.LIGHT]: 'Light', [THEMES.DARK]: 'Dark', - [THEMES.ASH]: 'Ash', - [THEMES.ONYX]: 'Onyx', }; +const VALID_THEMES = Object.values(THEMES); + +function normalize(value) { + return VALID_THEMES.includes(value) ? value : THEMES.DARK; +} + const ThemeContext = createContext(); export function ThemeProvider({ children }) { const { settings } = usePlatform(); - const [theme, setTheme] = useState(() => { - return localStorage.getItem(STORAGE_KEY) || THEMES.DARK; + const [theme, setThemeState] = useState(() => { + return normalize(localStorage.getItem(STORAGE_KEY)); }); + const setTheme = (next) => setThemeState(normalize(next)); + const toggleTheme = () => + setThemeState((prev) => (prev === THEMES.DARK ? THEMES.LIGHT : THEMES.DARK)); + // On mount, check settings.json as fallback when localStorage is empty useEffect(() => { if (!localStorage.getItem(STORAGE_KEY) && settings) { settings.get('theme').then((saved) => { - if (saved && Object.values(THEMES).includes(saved)) { - setTheme(saved); + if (saved && VALID_THEMES.includes(saved)) { + setThemeState(saved); } }); } }, []); useEffect(() => { + // global.css uses [data-theme='dark'] / [data-theme='light'] + const short = theme === THEMES.LIGHT ? 'light' : 'dark'; + document.documentElement.setAttribute('data-theme', short); + // keep className in sync for any legacy selectors that rely on it document.documentElement.className = theme; localStorage.setItem(STORAGE_KEY, theme); settings?.set('theme', theme); }, [theme]); return ( - <ThemeContext.Provider value={{ theme, setTheme, THEMES, THEME_LABELS }}> + <ThemeContext.Provider value={{ theme, setTheme, toggleTheme, THEMES, THEME_LABELS }}> {children} </ThemeContext.Provider> ); diff --git a/packages/shared/src/contexts/VoiceContext.jsx b/packages/shared/src/contexts/VoiceContext.jsx index 88cc779..ba62daa 100644 --- a/packages/shared/src/contexts/VoiceContext.jsx +++ b/packages/shared/src/contexts/VoiceContext.jsx @@ -15,6 +15,10 @@ import deafenSound from '../assets/sounds/deafen.mp3'; import undeafenSound from '../assets/sounds/undeafen.mp3'; import viewerJoinSound from '../assets/sounds/screenshare_viewer_join.mp3'; import viewerLeaveSound from '../assets/sounds/screenshare_viewer_leave.mp3'; +import screenshareStartSound from '../assets/sounds/screenshare_start.mp3'; +import screenshareStopSound from '../assets/sounds/screenshare_stop.mp3'; +import cameraOnSound from '../assets/sounds/camera_on.mp3'; +import cameraOffSound from '../assets/sounds/camera_off.mp3'; const soundMap = { join: joinSound, @@ -25,6 +29,10 @@ const soundMap = { undeafen: undeafenSound, viewer_join: viewerJoinSound, viewer_leave: viewerLeaveSound, + screenshare_start: screenshareStartSound, + screenshare_stop: screenshareStopSound, + camera_on: cameraOnSound, + camera_off: cameraOffSound, }; const VoiceContext = createContext(); @@ -270,10 +278,33 @@ export const VoiceProvider = ({ children }) => { setToken(lkToken); - const storedInputDevice = localStorage.getItem('voiceInputDevice'); - const storedOutputDevice = localStorage.getItem('voiceOutputDevice'); - - const noiseSuppression = localStorage.getItem('voiceNoiseSuppression') !== 'false'; // default true + // Voice Settings tab (settings/UserSettingsModal.tsx) writes + // everything to a single `voiceSettings` JSON blob. Read it + // here so user choices actually flow through to LiveKit. + // Defaults: echoCancellation off (user preference — can be + // re-enabled in settings), everything else on. + let voiceSettings = { + inputDeviceId: 'default', + outputDeviceId: 'default', + videoDeviceId: 'default', + inputVolume: 100, + noiseSuppression: true, + echoCancellation: false, + autoGainControl: true, + }; + try { + const raw = localStorage.getItem('voiceSettings'); + if (raw) { + const parsed = JSON.parse(raw); + if (parsed && typeof parsed === 'object') { + voiceSettings = { ...voiceSettings, ...parsed }; + } + } + } catch { + /* fall through to defaults */ + } + const storedInputDevice = voiceSettings.inputDeviceId; + const storedOutputDevice = voiceSettings.outputDeviceId; const isMobile = /Android|iPhone|iPad/i.test(navigator.userAgent); @@ -289,9 +320,9 @@ export const VoiceProvider = ({ children }) => { ], }, audioCaptureDefaults: { - autoGainControl: true, - echoCancellation: true, - noiseSuppression, + autoGainControl: voiceSettings.autoGainControl, + echoCancellation: voiceSettings.echoCancellation, + noiseSuppression: voiceSettings.noiseSuppression, channelCount: 1, sampleRate: 48000, ...(storedInputDevice && storedInputDevice !== 'default' ? { deviceId: { exact: storedInputDevice } } : {}), @@ -790,6 +821,48 @@ export const VoiceProvider = ({ children }) => { prevViewersRef.current = currentViewers; }, [voiceStates, watchingStreamOf]); + // Detect screen-share publications starting / stopping across the + // active voice channel (including the local user) and play a + // "stream started" / "stream stopped" SFX on transitions. Reuses + // the viewer_join / viewer_leave clips since they're already + // loaded and sound right for the event. + const prevSharersRef = useRef(new Set()); + const sharerDetectionInitRef = useRef(false); + useEffect(() => { + if (!activeChannelId) { + prevSharersRef.current = new Set(); + sharerDetectionInitRef.current = false; + return; + } + const channelUsers = voiceStates[activeChannelId] || []; + const currentSharers = new Set(); + for (const u of channelUsers) { + if (u.isScreenSharing) currentSharers.add(u.userId); + } + if (!sharerDetectionInitRef.current) { + sharerDetectionInitRef.current = true; + prevSharersRef.current = currentSharers; + return; + } + const prev = prevSharersRef.current; + // Stream started — someone in the channel is now sharing + // who wasn't before. + for (const uid of currentSharers) { + if (!prev.has(uid)) { + playSound('screenshare_start'); + break; + } + } + // Stream stopped — someone who was sharing isn't anymore. + for (const uid of prev) { + if (!currentSharers.has(uid)) { + playSound('screenshare_stop'); + break; + } + } + prevSharersRef.current = currentSharers; + }, [voiceStates, activeChannelId]); + const disconnectVoice = () => { console.log('User manually disconnected voice'); isDMCallRef.current = false; @@ -824,11 +897,53 @@ export const VoiceProvider = ({ children }) => { await updateVoiceState({ isDeafened: nextState }); }; + // Actually flip the LiveKit screen-share publication on/off. The + // earlier implementation only toggled local React state + the + // voiceStates row, so the Share button lit up but no track ever + // got published. `setScreenShareEnabled` is LiveKit's one-call + // helper that prompts for `getDisplayMedia`, publishes the + // resulting track, and tears it down on false. const setScreenSharing = async (active) => { + if (!room) return; + try { + await room.localParticipant.setScreenShareEnabled(active, { + audio: true, + }); + } catch (e) { + console.warn('Failed to toggle screen share:', e); + // User cancelled the picker or permission was denied — + // keep local state in sync with whatever actually happened + // on the LiveKit side. + const published = !!room.localParticipant.getTrackPublication?.( + 'screen_share', + ); + setIsScreenSharingLocal(published); + await updateVoiceState({ isScreenSharing: published }); + return; + } setIsScreenSharingLocal(active); await updateVoiceState({ isScreenSharing: active }); }; + // Camera toggle — publishes / unpublishes the local webcam track + // via LiveKit's `setCameraEnabled` helper. Mirrors the mic and + // screen-share code paths so the UI can show a single consistent + // "active" state for each media type. + const [isCameraOn, setIsCameraOn] = useState(false); + const setCamera = async (active) => { + if (!room) return; + try { + await room.localParticipant.setCameraEnabled(active); + setIsCameraOn(active); + playSound(active ? 'camera_on' : 'camera_off'); + } catch (e) { + console.warn('Failed to toggle camera:', e); + const published = !!room.localParticipant.isCameraEnabled; + setIsCameraOn(published); + } + }; + const toggleCamera = () => setCamera(!isCameraOn); + const switchDevice = useCallback(async (kind, deviceId) => { if (!room) return; try { @@ -856,6 +971,9 @@ export const VoiceProvider = ({ children }) => { toggleDeafen, isScreenSharing, setScreenSharing, + isCameraOn, + setCamera, + toggleCamera, personallyMutedUsers, togglePersonalMute, isPersonallyMuted, diff --git a/packages/shared/src/css-modules.d.ts b/packages/shared/src/css-modules.d.ts new file mode 100644 index 0000000..5b5c1c7 --- /dev/null +++ b/packages/shared/src/css-modules.d.ts @@ -0,0 +1,4 @@ +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} diff --git a/packages/shared/src/global.css b/packages/shared/src/global.css new file mode 100644 index 0000000..c008717 --- /dev/null +++ b/packages/shared/src/global.css @@ -0,0 +1,1051 @@ +/* ── CSS Reset ─────────────────────────────────────────────────────────── */ + +a, +abbr, +acronym, +address, +applet, +big, +blockquote, +body, +caption, +cite, +code, +dd, +del, +dfn, +div, +dl, +dt, +em, +fieldset, +form, +h1, +h2, +h3, +h4, +h5, +h6, +html, +iframe, +img, +ins, +kbd, +label, +legend, +li, +object, +ol, +p, +pre, +q, +s, +samp, +small, +span, +strike, +strong, +table, +tbody, +td, +tfoot, +th, +thead, +tr, +tt, +ul, +var { + border: 0; + font-family: inherit; + font-size: 100%; + font-style: inherit; + font-weight: inherit; + margin: 0; + padding: 0; + vertical-align: baseline; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html, +body, +#root { + height: 100%; + width: 100%; +} + +/* Frameless Electron: reserve 32px at the top for the TitleBar. + The class is flipped on/off at runtime by TitleBar.tsx so web + and Android builds don't reserve any space. We pad #root (not + body) so children that use `height: 100vh` start below the bar; + the bottom 32px of such children will overflow the viewport but + most top-level layouts use `height: 100%` which fits cleanly. */ +body.has-window-controls #root { + padding-top: 32px; +} + +/* ── Design Tokens ────────────────────────────────────────────────────── */ + +:root { + --saturation-factor: 1; + --user-select: auto; + + /* ── Voice / video stage tokens ─────────────────────────────── */ + --voice-surface-0: var(--background-primary); + --voice-surface-1: var(--background-secondary); + --voice-surface-2: var(--background-secondary-alt, var(--background-secondary)); + --voice-surface-3: var(--background-tertiary); + --voice-surface-4: var(--background-header-secondary, var(--background-tertiary)); + --voice-surface-5: var(--background-header-primary-hover, var(--background-modifier-hover)); + + --voice-overlay-base: color-mix(in srgb, black 72%, var(--background-primary)); + --voice-overlay-strong: color-mix(in srgb, var(--voice-overlay-base) 78%, transparent); + --voice-overlay: color-mix(in srgb, var(--voice-overlay-base) 62%, transparent); + --voice-overlay-soft: color-mix(in srgb, var(--voice-overlay-base) 46%, transparent); + --voice-overlay-subtle: color-mix(in srgb, var(--voice-overlay-base) 32%, transparent); + --voice-overlay-light: color-mix(in srgb, var(--text-primary) 12%, transparent); + --voice-overlay-light-strong: color-mix(in srgb, var(--text-primary) 20%, transparent); + --voice-overlay-light-bold: color-mix(in srgb, var(--text-primary) 48%, transparent); + + --voice-text-strong: var(--text-primary); + --voice-text-muted: var(--text-secondary); + --voice-text-subtle: var(--text-tertiary); + + --voice-status-success: var(--status-online, #23a55a); + --voice-status-success-bg-solid: color-mix(in srgb, var(--voice-status-success) 20%, var(--voice-surface-2)); + + --voice-status-warning: var(--status-warning, #f0b232); + --voice-status-warning-alt: var(--status-idle, #f0b232); + + --voice-status-danger: var(--status-danger, #f23f43); + --voice-status-danger-bg-solid: color-mix(in srgb, var(--voice-status-danger) 20%, var(--voice-surface-2)); + --voice-status-danger-bg-strong-solid: color-mix(in srgb, var(--voice-status-danger) 30%, var(--voice-surface-2)); + + --voice-shadow-base: color-mix(in srgb, var(--background-primary) 12%, black); + --voice-shadow-strong: color-mix(in srgb, var(--voice-shadow-base) 40%, transparent); + --voice-shadow-medium: color-mix(in srgb, var(--voice-shadow-base) 30%, transparent); + --voice-shadow-soft: color-mix(in srgb, var(--voice-shadow-base) 20%, transparent); + + --voice-header-gradient: linear-gradient( + to bottom, + var(--voice-overlay-strong) 0%, + var(--voice-overlay) 46%, + transparent 100% + ); + --voice-footer-gradient: linear-gradient( + to top, + var(--voice-overlay-strong) 0%, + var(--voice-overlay-soft) 46%, + transparent 100% + ); + + /* Z-index layers */ + --z-index-base: 0; + --z-index-elevated-1: 10; + --z-index-elevated-2: 20; + --z-index-elevated-3: 30; + --z-index-modal: 10000; + --z-index-popout: 15000; + --z-index-modal-swap: 25000; + --z-index-popout-above-swap: 30000; + --z-index-overlay: 40000; + --z-index-tooltip: 45000; + --z-index-toast: 50000; + --z-index-titlebar: 100000; + --z-index-contextmenu: 2147483647; + --native-titlebar-height: 32px; + + /* Border radii */ + --radius-sm: 0.25rem; + --radius-md: 0.375rem; + --radius-lg: 0.5rem; + --radius-xl: 0.75rem; + --radius-2xl: 1rem; + --radius-full: 9999px; + --media-border-radius: 4px; + + /* Spacing scale */ + --spacing-0: 0; + --spacing-1: 0.25rem; + --spacing-1-5: 0.375rem; + --spacing-2: 0.5rem; + --spacing-3: 0.75rem; + --spacing-4: 1rem; + --spacing-5: 1.25rem; + --spacing-6: 1.5rem; + --spacing-8: 2rem; + --spacing-10: 2.5rem; + --spacing-12: 3rem; + --spacing-16: 4rem; + --spacing-20: 5rem; + --spacing-24: 6rem; + + /* Layout dimensions */ + --layout-guild-list-width: 4.5rem; + --layout-sidebar-width: 16.875rem; + --layout-header-height: 3.5rem; + --layout-user-area-height: var(--input-container-min-height); + --layout-user-area-reserved-height: 0px; + --layout-mobile-bottom-nav-reserved-height: 0px; + --layout-member-list-width: 265px; + --guild-icon-size: 44px; + --guild-icon-gap: var(--spacing-2); + --mobile-bottom-nav-height: 60px; + + --layout-gap: var(--spacing-4); + --layout-gap-sm: var(--spacing-2); + --layout-gap-lg: var(--spacing-6); + + --content-padding: var(--spacing-4); + --content-padding-sm: var(--spacing-3); + --content-padding-lg: var(--spacing-6); + --layout-header-popout-width: calc(var(--layout-sidebar-width) - (var(--spacing-4) * 2)); + + /* Input / textarea */ + --input-height: 40px; + --input-container-padding: 0.625rem; + --footer-row-height: 72px; + --input-container-min-height: var(--footer-row-height); + --input-wrapper-padding-x: 0.5rem; + --input-wrapper-padding-bottom: 0.5rem; + --textarea-top-bar-height: 40px; + --textarea-line-height: 1.375rem; + --textarea-content-offset: calc((var(--user-area-content-height) - var(--textarea-line-height)) / 2); + + /* Typing indicator */ + --typing-indicator-height: 16px; + --typing-pill-height: 20px; + --slowmode-indicator-height: var(--typing-pill-height); + --scroller-spacer-height: 28px; + --typing-avatar-size: 12px; + --typing-indicator-animation-size: 20px; + --typing-indicator-gap: 0px; + --typing-upload-column-width: calc( + var(--user-area-content-height) + + (var(--textarea-side-button-padding, 0.34375rem) * 2) + ); + + /* User area */ + --user-area-content-height: 36px; + --user-area-padding-y: calc((var(--layout-user-area-height) - var(--user-area-content-height)) / 2); + --user-area-padding-x: var(--spacing-4); + --voice-connection-padding-y: var(--spacing-2); + --footer-row-padding-y: var(--user-area-padding-y); + + /* Message layout */ + --message-avatar-size: 40px; + --message-gutter: 16px; + --message-line-height: 1.375rem; + + /* Spoiler */ + --spoiler-border-radius: 6px; + + /* Typography */ + --font-sans: 'IBM Plex Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --font-mono: 'Consolas', 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace; + --font-emoji: 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji', var(--font-sans, system-ui), sans-serif; + --font-size-xs: 0.75rem; + --font-size-sm: 0.8125rem; + --font-size-md: 0.875rem; + --font-size-lg: 1rem; + --font-size-xl: 1.25rem; + --font-size-2xl: 1.5rem; + + /* Emoji */ + --custom-emoji-size-emoji: 1.375em; + --custom-emoji-size-jumbo-emoji: 3rem; + --emoji-size: var(--custom-emoji-size-emoji); + --emoji-jumbo-size: var(--custom-emoji-size-jumbo-emoji); + + /* Focus */ + --focus-primary: #00b0f4; +} + +/* ── Dark Theme (default) ─────────────────────────────────────────────── */ + +:root, +[data-theme='dark'] { + /* Backgrounds — HSL with saturation factor (neutralDark: hue=220, sat=13%) */ + --background-primary: hsl(220, calc(13% * var(--saturation-factor)), 5%); + --background-secondary: hsl(220, calc(13% * var(--saturation-factor)), 11.18%); + --background-secondary-lighter: hsl(220, calc(13% * var(--saturation-factor)), 13.22%); + --background-secondary-alt: hsl(220, calc(13% * var(--saturation-factor)), 15.08%); + --background-tertiary: hsl(220, calc(13% * var(--saturation-factor)), 17.78%); + --background-channel-header: hsl(220, calc(13% * var(--saturation-factor)), 16.46%); + --background-floating: hsl(220, calc(13% * var(--saturation-factor)), 3%); + --background-textarea: hsl(220, calc(13% * var(--saturation-factor)), 21.78%); + --background-header-secondary: hsl(220, calc(13% * var(--saturation-factor)), 20.75%); + --background-header-primary: hsl(220, calc(13% * var(--saturation-factor)), 19.5%); + --background-accent: hsl(220, calc(13% * var(--saturation-factor)), 40%); + + --background-modifier-hover: hsla(220, calc(13% * var(--saturation-factor)), 100%, 0.05); + --background-modifier-active: hsla(220, calc(13% * var(--saturation-factor)), 100%, 0.08); + --background-modifier-selected: hsla(220, calc(13% * var(--saturation-factor)), 100%, 0.1); + --background-modifier-accent: hsla(220, calc(13% * var(--saturation-factor)), 80%, 0.15); + --background-modifier-accent-focus: hsla(220, calc(13% * var(--saturation-factor)), 80%, 0.22); + + /* Code blocks */ + --bg-code: hsla(220, calc(13% * var(--saturation-factor)), 15%, 0.8); + --bg-code-block: var(--background-secondary-alt); + + /* Text — HSL (neutralDark hue=220, sat=13%, darkText range [52, 96]) */ + --text-primary: hsl(220, calc(13% * var(--saturation-factor)), 96%); + --text-chat: hsl(220, calc(13% * var(--saturation-factor)), 90.56%); + --text-secondary: hsl(220, calc(13% * var(--saturation-factor)), 83.68%); + --text-primary-muted: hsl(220, calc(13% * var(--saturation-factor)), 76.2%); + --text-tertiary: hsl(220, calc(13% * var(--saturation-factor)), 68.72%); + --text-tertiary-secondary: hsl(220, calc(13% * var(--saturation-factor)), 52%); + --text-tertiary-muted: hsl(220, calc(13% * var(--saturation-factor)), 60.8%); + --text-muted: var(--text-tertiary-muted); + + /* Brand */ + --brand-primary: hsl(242, calc(70% * var(--saturation-factor)), 55%); + --brand-secondary: hsl(242, calc(60% * var(--saturation-factor)), 49%); + --brand-primary-light: hsl(242, calc(100% * var(--saturation-factor)), 84%); + --brand-primary-fill: hsl(0, 0%, 100%); + --brand-primary-hover: var(--brand-secondary); + --brand-primary-active: hsl(242, calc(60% * var(--saturation-factor)), 42%); + + /* Links */ + --text-link: hsl(210, calc(100% * var(--saturation-factor)), 70%); + --text-on-brand-primary: hsl(0, 0%, 98%); + + /* Status */ + --status-online: hsl(142, calc(76% * var(--saturation-factor)), 40%); + --status-idle: hsl(45, calc(93% * var(--saturation-factor)), 50%); + --status-dnd: hsl(0, calc(84% * var(--saturation-factor)), 60%); + --status-offline: hsl(218, calc(11% * var(--saturation-factor)), 65%); + --status-danger: hsl(1, calc(77% * var(--saturation-factor)), 55%); + --status-warning: var(--status-idle); + --status-success: var(--status-online); + --accent-success: var(--status-online); + + /* Interactive */ + --interactive-normal: var(--text-secondary); + --interactive-hover: var(--text-primary); + --interactive-active: color-mix(in oklab, hsl(0, 0%, 100%) 100%, hsl(245, calc(100% * var(--saturation-factor)), 80%) 40%); + --interactive-muted: color-mix(in oklab, hsl(228, calc(10% * var(--saturation-factor)), 35%) 100%, hsl(245, calc(100% * var(--saturation-factor)), 80%) 40%); + + /* Buttons */ + --button-secondary-fill: hsla(0, 0%, 100%, 0.1); + --button-secondary-active-fill: hsla(0, 0%, 100%, 0.15); + --button-secondary-text: hsl(0, 0%, 100%); + --button-secondary-active-text: var(--button-secondary-text); + --button-ghost-text: hsl(0, 0%, 100%); + --button-danger-fill: hsl(359, calc(70% * var(--saturation-factor)), 54%); + --button-danger-text: hsl(0, 0%, 100%); + --button-danger-active-fill: hsl(359, calc(65% * var(--saturation-factor)), 45%); + --button-danger-outline-text: hsl(0, 0%, 100%); + --button-inverted-fill: hsl(0, 0%, 100%); + --button-inverted-text: hsl(0, 0%, 0%); + + /* Control buttons */ + --control-button-normal-text: var(--text-primary-muted); + --control-button-hover-bg: hsl(220, calc(13% * var(--saturation-factor)), 22%); + --control-button-hover-text: var(--text-primary); + --control-button-danger-text: hsl(1, calc(77% * var(--saturation-factor)), 60%); + + /* Channel */ + --channel-icon: var(--text-tertiary-muted); + + /* Surfaces */ + --form-surface-background: var(--background-tertiary); + --surface-interactive-hover-bg: var(--background-modifier-hover); + --surface-interactive-selected-bg: var(--background-modifier-selected); + --surface-interactive-selected-color: var(--text-primary); + + /* Scrollbar — matches Fluxer's dark-theme palette so the chat + scroller (and any other `.scroller`-styled element) reads the + same as Fluxer. The thumb is a muted mix of the textarea + surface and the tertiary text colour; hover bumps the mix + toward text-secondary for a subtle hi-light. */ + --scrollbar-thumb-bg: color-mix(in srgb, var(--background-textarea) 35%, var(--text-tertiary) 65%); + --scrollbar-thumb-bg-hover: color-mix(in srgb, var(--background-textarea) 25%, var(--text-secondary) 75%); + --scrollbar-track-bg: color-mix(in srgb, var(--background-secondary) 80%, transparent); + + /* Panel controls (UserArea, voice panels) */ + --panel-control-bg: color-mix( + in srgb, + var(--background-secondary-alt) 80%, + hsl(220, calc(13% * var(--saturation-factor)), 2%) 20% + ); + --panel-control-border: hsla(220, calc(30% * var(--saturation-factor)), 65%, 0.45); + --panel-control-divider: hsla(220, calc(30% * var(--saturation-factor)), 55%, 0.35); + --panel-control-highlight: hsla(0, 0%, 100%, 0.04); + + /* Guild list */ + --guild-list-foreground: hsl(220, calc(13% * var(--saturation-factor)), 19.5%); + + /* User area */ + --user-area-divider-color: color-mix(in srgb, var(--background-modifier-hover) 70%, transparent); + + /* Shadows */ + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.1); + --shadow-md: 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 4px 8px rgba(0, 0, 0, 0.15), 0 2px 4px rgba(0, 0, 0, 0.1); + + /* Transitions */ + --transition-fast: 100ms ease; + --transition-normal: 200ms ease; + --transition-slow: 300ms ease; + + /* Border */ + --border-color: hsla(220, calc(13% * var(--saturation-factor)), 50%, 0.2); + + color-scheme: dark; +} + +/* ── Light Theme ──────────────────────────────────────────────────────── */ + +[data-theme='light'] { + /* Backgrounds — HSL with saturation factor (neutralLight: hue=220, sat=10%) */ + /* lightSurface scale: easeIn curve, range [86, 98.5] */ + --background-primary: hsl(220, calc(10% * var(--saturation-factor)), 98.5%); + --background-secondary: hsl(220, calc(10% * var(--saturation-factor)), 92.85%); + --background-secondary-lighter: hsl(220, calc(10% * var(--saturation-factor)), 94.61%); + --background-secondary-alt: hsl(220, calc(10% * var(--saturation-factor)), 90.96%); + --background-tertiary: hsl(220, calc(10% * var(--saturation-factor)), 88.21%); + --background-channel-header: hsl(220, calc(10% * var(--saturation-factor)), 89.13%); + --background-floating: hsl(220, calc(10% * var(--saturation-factor)), 98.5%); + --background-accent: hsl(220, calc(10% * var(--saturation-factor)), 88.21%); + --background-textarea: hsl(220, calc(10% * var(--saturation-factor)), 95.68%); + --background-header-secondary: hsl(220, calc(10% * var(--saturation-factor)), 86.5%); + --background-header-primary: hsl(220, calc(10% * var(--saturation-factor)), 86.18%); + --background-header-primary-hover: hsl(220, calc(10% * var(--saturation-factor)), 86%); + --guild-list-foreground: hsl(220, calc(10% * var(--saturation-factor)), 87.53%); + + /* Modifiers — light theme uses dark base with alpha */ + --background-modifier-hover: hsla(220, calc(10% * var(--saturation-factor)), 10%, 0.05); + --background-modifier-active: hsla(220, calc(10% * var(--saturation-factor)), 10%, 0.08); + --background-modifier-selected: hsla(220, calc(10% * var(--saturation-factor)), 10%, 0.1); + --background-modifier-accent: hsla(220, calc(10% * var(--saturation-factor)), 40%, 0.22); + --background-modifier-accent-focus: hsla(220, calc(10% * var(--saturation-factor)), 40%, 0.32); + + /* Code */ + --bg-code: hsla(220, calc(22% * var(--saturation-factor)), 90%, 0.9); + --bg-code-block: var(--background-primary); + + /* Text — HSL (neutralLight hue=220, sat=10%, lightText range [15, 60]) */ + --text-primary: hsl(220, calc(10% * var(--saturation-factor)), 15%); + --text-chat: hsl(220, calc(10% * var(--saturation-factor)), 21.91%); + --text-secondary: hsl(220, calc(10% * var(--saturation-factor)), 36.67%); + --text-primary-muted: hsl(220, calc(10% * var(--saturation-factor)), 46.39%); + --text-tertiary: hsl(220, calc(10% * var(--saturation-factor)), 52.8%); + --text-tertiary-secondary: hsl(220, calc(10% * var(--saturation-factor)), 57.19%); + --text-tertiary-muted: hsl(220, calc(10% * var(--saturation-factor)), 58.99%); + --text-muted: var(--text-tertiary-muted); + --text-link: hsl(210, 100%, 45%); + --text-code: hsl(340, 50%, 45%); + + /* Brand */ + --brand-primary: hsl(242, calc(70% * var(--saturation-factor)), 55%); + --brand-secondary: hsl(242, calc(60% * var(--saturation-factor)), 49%); + --brand-primary-hover: var(--brand-secondary); + --brand-primary-active: hsl(242, calc(60% * var(--saturation-factor)), 42%); + --brand-primary-fill: hsl(0, 0%, 100%); + --text-on-brand-primary: hsl(0, 0%, 98%); + + /* Status */ + --status-online: hsl(142, calc(70% * var(--saturation-factor)), 40%); + --status-idle: hsl(45, calc(90% * var(--saturation-factor)), 45%); + --status-dnd: hsl(359, calc(70% * var(--saturation-factor)), 50%); + --status-offline: hsl(210, calc(10% * var(--saturation-factor)), 55%); + --status-danger: hsl(359, calc(70% * var(--saturation-factor)), 50%); + --status-warning: var(--status-idle); + --status-success: var(--status-online); + --accent-success: var(--status-online); + + /* Buttons */ + --button-secondary-fill: hsla(220, calc(10% * var(--saturation-factor)), 10%, 0.1); + --button-secondary-active-fill: hsla(220, calc(10% * var(--saturation-factor)), 10%, 0.15); + --button-secondary-text: hsl(220, calc(10% * var(--saturation-factor)), 15%); + --button-secondary-active-text: hsl(220, calc(10% * var(--saturation-factor)), 10%); + --button-ghost-text: hsl(220, calc(10% * var(--saturation-factor)), 20%); + --button-inverted-fill: hsl(0, 0%, 100%); + --button-inverted-text: hsl(0, 0%, 10%); + --button-danger-fill: hsl(359, calc(70% * var(--saturation-factor)), 54%); + --button-danger-text: hsl(0, 0%, 100%); + --button-danger-active-fill: hsl(359, calc(65% * var(--saturation-factor)), 45%); + --button-danger-outline-text: hsl(359, calc(70% * var(--saturation-factor)), 45%); + + /* Control buttons */ + --control-button-normal-bg: transparent; + --control-button-normal-text: hsl(220, calc(10% * var(--saturation-factor)), 50%); + --control-button-hover-bg: hsl(220, calc(10% * var(--saturation-factor)), 88%); + --control-button-hover-text: hsl(220, calc(10% * var(--saturation-factor)), 20%); + --control-button-active-bg: hsl(220, calc(10% * var(--saturation-factor)), 85%); + --control-button-active-text: hsl(220, calc(10% * var(--saturation-factor)), 15%); + --control-button-danger-text: hsl(359, calc(70% * var(--saturation-factor)), 50%); + --control-button-danger-hover-bg: hsl(359, calc(70% * var(--saturation-factor)), 95%); + + /* Panel controls */ + --panel-control-bg: color-mix(in srgb, var(--background-secondary) 65%, hsl(0, 0%, 100%) 35%); + --panel-control-border: hsla(220, calc(25% * var(--saturation-factor)), 45%, 0.25); + --panel-control-divider: hsla(220, calc(30% * var(--saturation-factor)), 35%, 0.2); + --panel-control-highlight: hsla(0, 0%, 100%, 0.65); + + /* Guild list */ + --guild-list-foreground: hsl(220, calc(10% * var(--saturation-factor)), 87.53%); + + /* Surfaces */ + --form-surface-background: var(--background-primary); + --surface-interactive-hover-bg: var(--background-modifier-hover); + --surface-interactive-selected-bg: var(--background-modifier-selected); + --surface-interactive-selected-color: var(--text-primary); + + /* Scrollbar */ + --scrollbar-thumb-bg: color-mix(in srgb, var(--background-header-secondary) 40%, var(--text-secondary) 60%); + --scrollbar-thumb-bg-hover: color-mix(in srgb, var(--background-header-secondary) 30%, var(--text-primary) 70%); + --scrollbar-track-bg: color-mix(in srgb, var(--background-secondary) 50%, transparent); + + /* Border */ + --border-color: hsla(220, calc(10% * var(--saturation-factor)), 40%, 0.15); + --border-color-hover: hsla(220, calc(10% * var(--saturation-factor)), 40%, 0.25); + --border-color-focus: hsla(210, calc(90% * var(--saturation-factor)), 50%, 0.4); + + /* User area */ + --user-area-divider-color: hsla(220, calc(10% * var(--saturation-factor)), 40%, 0.2); + + /* Syntax highlighting (light theme) */ + --hljs-light-text: #24292e; + --hljs-light-keyword: #d73a49; + --hljs-light-entity: #6f42c1; + --hljs-light-constant: #005cc5; + --hljs-light-string: #032f62; + --hljs-light-variable: #e36209; + --hljs-light-comment: #6a737d; + --hljs-light-tag: #22863a; + --hljs-light-list: #735c0f; + --hljs-light-addition: #22863a; + --hljs-light-addition-bg: #f0fff4; + --hljs-light-deletion: #b31d28; + --hljs-light-deletion-bg: #ffeef0; + + color-scheme: light; +} + +/* ── macOS native titlebar adjustment ─────────────────────────────────── */ + +html.platform-native.platform-macos { + --layout-guild-list-width: 4.75rem; +} + +/* ── Base Styles ──────────────────────────────────────────────────────── */ + +html { + background-color: var(--background-secondary); + color: var(--text-primary); + overflow: hidden; + overscroll-behavior: none; + user-select: var(--user-select); + -webkit-user-select: var(--user-select); + font-family: var(--font-sans); + font-size: 16px; + line-height: 1.5; + font-optical-sizing: auto; +} + +html, +body { + touch-action: pan-y; +} + +body { + font-family: var(--font-sans), var(--font-emoji); + font-size: var(--font-size-lg); + line-height: 1.375; + color: var(--text-primary); + background-color: var(--background-tertiary); + scrollbar-color: var(--scrollbar-thumb-bg) var(--scrollbar-track-bg); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + user-select: var(--user-select); + overflow: hidden; + margin: 0; + padding: 0; +} + +button, +input, +select, +textarea, +a { + touch-action: manipulation; + outline: none; +} + +[role='button'], +[tabindex] { + outline: none; +} + +input, +textarea, +select, +button { + font-family: inherit; + font-size: inherit; +} + +input { + color-scheme: dark; +} + +[data-theme='light'] input { + color-scheme: light; +} + +b, +strong { + font-weight: 600; +} + +a { + color: var(--text-link); + text-decoration: none; +} + +@media (hover: hover) { + a:hover { + color: var(--text-link); + text-decoration: underline; + } +} + +@media (hover: none) { + a, + a:hover, + a:active { + text-decoration: none !important; + } +} + +code { + font-family: var(--font-mono); + font-size: 0.9em; + line-height: 1.5; + padding: 0.2em 0.4em; + border-radius: var(--radius-sm); + background-color: var(--bg-code); +} + +pre { + padding: var(--pre-padding, 1em); + overflow: auto; + border-radius: var(--radius-md); + background-color: var(--bg-code-block); +} + +pre code { + padding: 0; + background-color: transparent; +} + +blockquote { + margin: 1em 0; + padding: 0; + border: none; + background-color: transparent; +} + +table { + border-collapse: collapse; + margin: 1em 0; +} + +table th { + text-align: left; + font-weight: bold; +} + +table td { + padding: 0.5em; +} + +img[alt] { + text-indent: -9999px; +} + +/* ── Scrollbar (WebKit) ───────────────────────────────────────────────── */ + +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: var(--scrollbar-track-bg); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb { + background: var(--scrollbar-thumb-bg); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--scrollbar-thumb-bg-hover); +} + +/* ── Syntax Highlighting (light theme) ────────────────────────────────── */ + +[data-theme='light'] .hljs { + color: var(--hljs-light-text); + background: transparent; +} + +[data-theme='light'] .hljs-doctag, +[data-theme='light'] .hljs-keyword, +[data-theme='light'] .hljs-meta .hljs-keyword, +[data-theme='light'] .hljs-template-tag, +[data-theme='light'] .hljs-template-variable, +[data-theme='light'] .hljs-type, +[data-theme='light'] .hljs-variable.language_ { + color: var(--hljs-light-keyword); +} + +[data-theme='light'] .hljs-title, +[data-theme='light'] .hljs-title.class_, +[data-theme='light'] .hljs-title.class_.inherited__, +[data-theme='light'] .hljs-title.function_ { + color: var(--hljs-light-entity); +} + +[data-theme='light'] .hljs-attr, +[data-theme='light'] .hljs-attribute, +[data-theme='light'] .hljs-literal, +[data-theme='light'] .hljs-meta, +[data-theme='light'] .hljs-number, +[data-theme='light'] .hljs-operator, +[data-theme='light'] .hljs-variable, +[data-theme='light'] .hljs-selector-attr, +[data-theme='light'] .hljs-selector-class, +[data-theme='light'] .hljs-selector-id { + color: var(--hljs-light-constant); +} + +[data-theme='light'] .hljs-regexp, +[data-theme='light'] .hljs-string, +[data-theme='light'] .hljs-meta .hljs-string { + color: var(--hljs-light-string); +} + +[data-theme='light'] .hljs-built_in, +[data-theme='light'] .hljs-symbol { + color: var(--hljs-light-variable); +} + +[data-theme='light'] .hljs-comment, +[data-theme='light'] .hljs-code, +[data-theme='light'] .hljs-formula { + color: var(--hljs-light-comment); +} + +[data-theme='light'] .hljs-name, +[data-theme='light'] .hljs-quote, +[data-theme='light'] .hljs-selector-tag, +[data-theme='light'] .hljs-selector-pseudo { + color: var(--hljs-light-tag); +} + +[data-theme='light'] .hljs-subst, +[data-theme='light'] .hljs-emphasis, +[data-theme='light'] .hljs-strong { + color: var(--hljs-light-text); +} + +[data-theme='light'] .hljs-section { + color: var(--hljs-light-constant); + font-weight: 600; +} + +[data-theme='light'] .hljs-bullet { + color: var(--hljs-light-list); +} + +[data-theme='light'] .hljs-emphasis { + font-style: italic; +} + +[data-theme='light'] .hljs-strong { + font-weight: 600; +} + +[data-theme='light'] .hljs-addition { + color: var(--hljs-light-addition); + background-color: var(--hljs-light-addition-bg); +} + +[data-theme='light'] .hljs-deletion { + color: var(--hljs-light-deletion); + background-color: var(--hljs-light-deletion-bg); +} + +/* ── Animations ───────────────────────────────────────────────────────── */ + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes slideUp { + from { opacity: 0; transform: translateY(8px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes searchFlash { + 0% { background-color: rgba(250, 168, 26, 0.2); } + 100% { background-color: transparent; } +} + +.searchHighlight { + animation: searchFlash 2s ease-out; +} + +/* ── Utility Classes ──────────────────────────────────────────────────── */ + +.text-smol { + font-size: 14px; + font-weight: 400; + line-height: 1.2857142857; +} + +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; +} + +.clamp { + --clamp-lines: 3; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: var(--clamp-lines); + line-clamp: var(--clamp-lines); + overflow: hidden; +} + +.zalgo-contain { + --zalgo-line-height: 1.25em; + line-height: var(--zalgo-line-height); + max-height: var(--zalgo-line-height); + overflow: hidden; + display: block; +} + +.zalgo-contain-inline { + --zalgo-line-height: 1.25em; + line-height: var(--zalgo-line-height); + overflow: clip; + overflow-clip-margin: 0px; + display: inline-block; + vertical-align: bottom; + max-height: var(--zalgo-line-height); +} + +.user-text { + --user-text-line-height: 1.25em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; + line-height: var(--user-text-line-height); + max-height: var(--user-text-line-height); + display: block; +} + +.user-text-inline { + --user-text-line-height: 1.25em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + line-height: var(--user-text-line-height); + max-height: var(--user-text-line-height); + display: inline-block; + vertical-align: bottom; + max-width: 100%; +} + +.user-text-flex { + --user-text-line-height: 1.25em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; + line-height: var(--user-text-line-height); + max-height: var(--user-text-line-height); + flex: 1 1 auto; +} + +.emoji { + font-family: var(--font-emoji); + display: inline-block; + width: var(--custom-emoji-size-emoji); + height: var(--custom-emoji-size-emoji); + object-fit: contain; + vertical-align: bottom; +} + +.emoji img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.emoji.jumboable { + width: var(--custom-emoji-size-jumbo-emoji); + height: var(--custom-emoji-size-jumbo-emoji); + min-height: var(--custom-emoji-size-jumbo-emoji); +} + +.emoji:not(img) { + font-size: var(--custom-emoji-size-emoji); + line-height: 1; + width: 1em; + height: 1em; +} + +.emoji.jumboable:not(img) { + font-size: var(--custom-emoji-size-jumbo-emoji); + width: 1em; + height: 1em; + min-height: 1em; +} + +/* Twemoji inside contenteditable input */ +.twemoji-inline { + width: 1.375em; + height: 1.375em; + vertical-align: -0.3em; + display: inline; + object-fit: contain; + pointer-events: none; +} + +/* Custom MSC2545 server emoji inside the contenteditable input — + sized to match .twemoji-inline so custom emojis and unicode + emojis are visually identical in the composer. Without this rule + the img renders at the source file's natural pixel dimensions + (e.g. 128×128), making it tower over unicode emojis. */ +.customEmojiInline { + width: auto; + height: 1.375em; + max-width: 2.75em; + vertical-align: -0.3em; + display: inline; + object-fit: contain; + pointer-events: none; +} + +/* Inline emoji images inserted into ChannelTextarea's contenteditable + by the EmojiPicker. Both unicode (twemoji) and custom server emoji + use these dimensions so they render identically in the composer. */ +.customEmoji { + display: inline-block; + vertical-align: -0.125em; + width: 1.375em; + height: 1.375em; + margin: 0 0.05em; + user-select: text; +} + +/* ── Focus Visible ────────────────────────────────────────────────────── */ + +:focus-visible { + outline: 2px solid var(--focus-primary); + outline-offset: 2px; +} + +textarea:focus-visible, +textarea:focus { + outline: none; +} + +/* ── Selection ────────────────────────────────────────────────────────── */ + +::selection { + background-color: var(--brand-primary); + color: #fff; +} + +/* ── Mobile Keyboard Handling ─────────────────────────────────────────── */ + +body.keyboard-visible { + --mobile-bottom-nav-height: 0px; +} + +body.keyboard-visible .mobile-bottom-nav { + display: none; +} + +/* ── Capacitor Safe Areas ─────────────────────────────────────────────── */ + +@supports (padding: env(safe-area-inset-top)) { + :root { + --safe-area-top: env(safe-area-inset-top, 0px); + --safe-area-bottom: env(safe-area-inset-bottom, 0px); + --safe-area-left: env(safe-area-inset-left, 0px); + --safe-area-right: env(safe-area-inset-right, 0px); + } +} + +/* ── Accessibility: High Contrast ─────────────────────────────────────── */ + +@media (prefers-contrast: more) { + :root { + --text-primary: hsl(0, 0%, 100%); + --text-secondary: hsl(0, 0%, 90%); + --text-tertiary: hsl(0, 0%, 80%); + --text-link: hsl(210, 100%, 70%); + --border-color: hsl(0, 0%, 50%); + } + [data-theme='light'] { + --text-primary: hsl(0, 0%, 0%); + --text-secondary: hsl(0, 0%, 10%); + --text-tertiary: hsl(0, 0%, 20%); + --text-link: hsl(210, 100%, 40%); + --border-color: hsl(0, 0%, 40%); + } +} + +/* ── Mobile: Disable Text Selection ───────────────────────────────────── */ + +@media (max-width: 840px) { + :root { + --user-select: none; + } + *, + .select-text { + user-select: none !important; + -webkit-user-select: none !important; + -moz-user-select: none !important; + -ms-user-select: none !important; + } + input, + textarea { + user-select: auto !important; + -webkit-user-select: auto !important; + -moz-user-select: auto !important; + -ms-user-select: auto !important; + } +} + +/* ── Reduced Motion ───────────────────────────────────────────────────── */ + +html.reduced-motion * { + animation-duration: 0.01ms; + animation-iteration-count: 1; + transition-duration: 0.01ms; + scroll-behavior: auto; +} diff --git a/packages/shared/src/hooks/useIsNarrowScreen.ts b/packages/shared/src/hooks/useIsNarrowScreen.ts new file mode 100644 index 0000000..b88543f --- /dev/null +++ b/packages/shared/src/hooks/useIsNarrowScreen.ts @@ -0,0 +1,23 @@ +import { useEffect, useState } from 'react'; + +// Matches the Brycord New UI MEMBER_LIST_MIN_WIDTH — below this viewport +// width the members panel is force-hidden so it doesn't squish the chat +// column. Kept in sync with the SelectionStore implementation we ported +// from: `Brycord New UI/apps/web/src/stores/SelectionStore.ts`. +const NARROW_BREAKPOINT = 1024; // px — members panel auto-hide threshold + +export function useIsNarrowScreen(breakpoint = NARROW_BREAKPOINT): boolean { + const [isNarrow, setIsNarrow] = useState(() => + typeof window !== 'undefined' ? window.innerWidth < breakpoint : false, + ); + + useEffect(() => { + if (typeof window === 'undefined') return; + const onResize = () => setIsNarrow(window.innerWidth < breakpoint); + onResize(); + window.addEventListener('resize', onResize); + return () => window.removeEventListener('resize', onResize); + }, [breakpoint]); + + return isNarrow; +} diff --git a/packages/shared/src/hooks/useLogout.ts b/packages/shared/src/hooks/useLogout.ts new file mode 100644 index 0000000..abcec3b --- /dev/null +++ b/packages/shared/src/hooks/useLogout.ts @@ -0,0 +1,45 @@ +import { useCallback } from 'react'; +import { usePlatform } from '../platform'; + +/** + * useLogout — wipes all client-side auth state and hard-reloads the + * app so AuthGuard lands on /login with a clean React tree (no stale + * Convex subscriptions, no lingering in-memory keys). Callers don't + * need to navigate themselves — the reload does it. + * + * Storage cleared (mirrors what AuthGuard hydrates on restore): + * - localStorage: userId, username, publicKey, userPrefs_<userId> + * - sessionStorage: privateKey, signingKey, masterKey, searchDbKey + * - platform.session: full encrypted blob (Electron safeStorage / web localStorage) + */ +export function useLogout(): () => Promise<void> { + const { session } = usePlatform(); + + return useCallback(async () => { + try { + await session?.clear?.(); + } catch (err) { + console.warn('Failed to clear platform session:', err); + } + + try { + const userId = localStorage.getItem('userId'); + localStorage.removeItem('userId'); + localStorage.removeItem('username'); + localStorage.removeItem('publicKey'); + if (userId) localStorage.removeItem(`userPrefs_${userId}`); + + sessionStorage.removeItem('privateKey'); + sessionStorage.removeItem('signingKey'); + sessionStorage.removeItem('masterKey'); + sessionStorage.removeItem('searchDbKey'); + } catch (err) { + console.warn('Failed to clear auth storage:', err); + } + + // Hard reload: dumps the React tree, LiveKit rooms, Convex + // client, and any open audio/video contexts. AuthGuard then + // mounts fresh with empty storage and redirects to /login. + window.location.reload(); + }, [session]); +} diff --git a/packages/shared/src/index.css b/packages/shared/src/index.css deleted file mode 100644 index e096abb..0000000 --- a/packages/shared/src/index.css +++ /dev/null @@ -1,5775 +0,0 @@ -/* Grouping under one family name: 'gg sans' */ - -@font-face { - font-family: 'gg sans'; - src: local('gg sans'), url('./assets/font/gg sans Regular.woff') format('woff'); - font-weight: 400; /* Normal */ - font-style: normal; -} - -@font-face { - font-family: 'gg sans'; - src: local('gg sans Medium'), url('./assets/font/gg sans Medium.woff') format('woff'); - font-weight: 500; /* Medium */ - font-style: normal; -} - -@font-face { - font-family: 'gg sans'; - src: local('gg sans Semibold'), url('./assets/font/gg sans Semibold.woff') format('woff'); - font-weight: 600; /* Semibold */ - font-style: normal; -} - -@font-face { - font-family: 'gg sans'; - src: local('gg sans Bold'), url('./assets/font/gg sans Bold.woff') format('woff'); - font-weight: 700; /* Bold */ - font-style: normal; -} - -html { - height: 100%; -} - -body { - margin: 0; - padding: 0; - height: 100%; - font-family: "gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; - background-color: var(--bg-primary); - color: var(--text-normal); - -webkit-font-smoothing: antialiased; - overflow: hidden; -} - -.auth-container { - display: flex; - align-items: center; - justify-content: center; - height: 100vh; - background-image: url('https://discord.com/assets/f9e794909795f472.svg'); - background-size: cover; - background-position: center; -} - -.auth-box { - background-color: var(--bg-secondary); - padding: 32px; - border-radius: 5px; - width: 480px; - box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.2); -} - -.auth-header { - text-align: center; - margin-bottom: 20px; -} - -.auth-header h2 { - color: var(--header-primary); - font-size: 24px; - font-weight: 600; - margin-bottom: 8px; -} - -.auth-header p { - color: var(--header-secondary); - font-size: 16px; -} - -.form-group { - margin-bottom: 20px; -} - -.form-group label { - display: block; - color: var(--header-secondary); - font-size: 12px; - font-weight: 700; - text-transform: uppercase; - margin-bottom: 8px; -} - -.form-group input { - width: 100%; - padding: 10px; - background-color: var(--input-background); - border: 1px solid rgba(0, 0, 0, 0.3); - border-radius: 3px; - color: var(--text-normal); - font-size: 16px; - box-sizing: border-box; - transition: border-color 0.2s; -} - -.form-group input:focus { - border-color: var(--brand-experiment); - outline: none; -} - -.auth-button { - width: 100%; - padding: 12px; - background-color: var(--brand-experiment); - color: white; - border: none; - border-radius: 3px; - font-size: 16px; - font-weight: 500; - cursor: pointer; - transition: background-color 0.2s; -} - -.auth-button:hover { - background-color: var(--brand-experiment-hover); -} - -.auth-footer { - margin-top: 16px; - font-size: 14px; - color: var(--text-muted); -} - -.auth-footer a { - color: var(--brand-experiment); - text-decoration: none; -} - -/* Sidebar */ -.sidebar { - width: 312px; - min-width: 312px; - background-color: var(--bg-tertiary); - display: flex; - flex-direction: row; - flex-shrink: 0; -} - -.server-list { - width: 72px; - border-right: 1px solid var(--div-border); - background-color: var(--bg-tertiary); - display: flex; - flex-direction: column; - align-items: center; - flex-shrink: 0; -} - -.ownerIcon { - color: var(--text-feedback-warning); - margin-inline-start: 4px; -} - -.server-icon { - width: 48px; - height: 48px; - background-color: var(--bg-primary); - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - color: var(--text-normal); - cursor: pointer; - transition: border-radius 0.15s ease, background-color 0.15s ease; - overflow: hidden; - font-weight: 600; - font-size: 18px; -} - -.server-icon:hover, -.server-icon.active { - border-radius: 16px; - background-color: var(--brand-experiment); - color: white; -} - -.channel-list { - flex: 1; - background-color: var(--bg-secondary); -} - -.channel-list::-webkit-scrollbar { - width: 8px; - background-color: var(--bg-primary); -} - -.channel-list::-webkit-scrollbar-thumb { - background-color: #666770; - border-radius: 4px; -} - -.channel-header { - padding: 0 8px; - margin-bottom: 16px; - font-weight: 700; - color: var(--header-primary); - text-transform: uppercase; - font-size: 12px; -} - -.channel-item { - padding: 8px; - margin: 4px; - border-radius: 8px; - color: var(--interactive-normal); - font-weight: 500; - cursor: pointer; -} - -.channel-item:hover { - background-color: var(--background-modifier-hover); - color: var(--interactive-hover); -} - -.channel-item.active { - background-color: var(--background-modifier-selected); - color: var(--interactive-active); -} - -#root { - display: flex; - flex-direction: column; - height: 100vh; -} - -/* Chat Area */ -.app-container { - display: flex; - flex: 1; - min-height: 0; - width: 100vw; - overflow: hidden; -} - -.chat-container { - display: flex; - flex-direction: column; - flex: 1; - min-width: 0; -} - -.chat-content { - display: flex; - flex: 1; - min-height: 0; - position: relative; -} - -.chat-area { - flex: 1; - background-color: var(--bg-primary); - display: flex; - flex-direction: column; - position: relative; - min-width: 0; - /* CRITICAL: Allows flex item to shrink below content size */ -} - -.messages-list { - flex: 1; - overflow: hidden; - position: relative; -} - -.messages-list::-webkit-scrollbar { - width: 8px; - background-color: var(--bg-primary); -} - -.messages-list::-webkit-scrollbar-thumb { - background-color: #666770; - border-radius: 4px; -} - -.message-item { - display: flex; - flex-wrap: wrap; - padding: 2px 16px; - margin-top: 17px; -} - -.message-item:hover { - background-color: rgba(0, 0, 0, 0.06); -} - -.message-grouped { - margin-top: 2px; -} - -.grouped-timestamp-wrapper { - display: flex; - align-items: center; - justify-content: center; - margin-top: 0; - width: 40px; - margin-right: 16px; -} - -.grouped-timestamp { - display: none; - font-size: 0.6875rem; - color: var(--text-muted); - white-space: nowrap; - line-height: 1.375rem; -} - -.message-grouped:hover .grouped-timestamp { - display: block; -} - -.message-avatar-wrapper { - width: 40px; - margin-right: 16px; - margin-top: 2px; -} - -.message-avatar { - width: 40px; - height: 40px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-weight: 500; - color: white; - font-size: 18px; - user-select: none; -} - -.message-body { - flex: 1; - min-width: 0; -} - -.message-header { - display: flex; - align-items: center; - margin-bottom: 2px; -} - -.username { - font-size: 1rem; - font-weight: 500; - margin-right: 0.25rem; - cursor: pointer; -} - -.username:hover { - text-decoration: underline; -} - -.timestamp { - color: var(--text-muted); - font-size: 0.75rem; - margin-left: 0.25rem; - font-weight: 400; -} - -.message-content { - color: var(--text-normal); - font-size: 1rem; - line-height: 1.375rem; - white-space: pre-wrap; - word-wrap: break-word; -} - -.message-content img { - max-width: 100%; - height: auto; -} - -/* Markdown Styles Tweaks */ -.message-content strong { - font-weight: 700; -} - -.message-content h1, -.message-content h2, -.message-content h3 { - border-bottom: none; - font-weight: 700; - color: var(--header-primary); -} - -.message-content ul, -.message-content ol { - list-style-type: disc; -} - -.message-content a { - color: var(--text-link); - text-decoration: none; -} - -.message-content a:hover { - text-decoration: underline; -} - -.message-content blockquote { - border-left: 4px solid var(--text-muted); - margin: 4px 0 4px 0; - padding: 0 8px 0 12px; - color: var(--header-secondary); - background-color: transparent; -} - -.message-content code { - background-color: var(--embed-background); - padding: 2px 4px; - border-radius: 3px; - font-family: Consolas, 'Courier New', monospace; - font-size: 0.875rem; -} - -.message-content pre { - background-color: var(--embed-background) !important; - border-radius: 4px; - border: 1px solid var(--bg-tertiary); - padding: 8px !important; - margin: 8px 0 !important; - max-width: 100%; - overflow-x: auto; -} - -.chat-input-form { - padding: 0 8px 8px; - background-color: var(--bg-primary); -} - -.chat-input-wrapper { - background-color: var(--channeltextarea-background); - border-radius: 8px; - display: flex; - align-items: center; - padding: 0 16px; - min-height: 56px; -} - -.chat-input-file-btn { - background: none; - border: none; - color: var(--header-secondary); - cursor: pointer; - padding: 0 16px 0 0; - display: flex; - align-items: center; - height: 24px; - align-self: center; -} - -.chat-input-file-btn:hover { - color: var(--text-normal); -} - -.chat-input-form textarea { - flex: 1; - padding: 11px 0; - background-color: transparent; - border: none; - color: var(--text-default); - font-size: 16px; - font-family: inherit; - height: 44px; - resize: none; - overflow: hidden; - line-height: 1.375rem; - box-sizing: border-box; -} - -.chat-input-form textarea:focus { - outline: none; -} - -.chat-input-form textarea::placeholder { - color: var(--text-muted); -} - -.chat-input-icons { - display: flex; - align-items: center; - padding: 0 0 0 10px; - align-self: center; - height: 100%; -} - -.chat-input-icon-btn { - background: none; - border: none; - color: var(--header-secondary); - cursor: pointer; - padding: 4px; - margin-left: 4px; - display: flex; - align-items: center; -} - -.chat-input-icon-btn:hover { - color: var(--text-normal); -} - -/* Link Previews */ -.link-preview { - display: flex; - background-color: var(--embed-background); - border-left: 4px solid var(--bg-tertiary); - border-radius: 4px; - padding: 10px; - margin-top: 8px; - max-width: 520px; - width: 100%; - gap: 16px; - overflow: hidden; - /* Ensure content stays inside */ - box-sizing: border-box; -} - -.preview-content { - flex: 1; - display: flex; - flex-direction: column; - justify-content: center; - min-width: 0; -} - -.preview-site-name { - font-size: 12px; - color: var(--header-secondary); - margin-bottom: 4px; -} - -.preview-title { - font-size: 16px; - font-weight: 600; - color: var(--text-link); - text-decoration: none; - margin-bottom: 4px; - display: block; - word-wrap: break-word; - /* Ensure long words don't overflow */ -} - -.preview-title:hover { - text-decoration: underline; -} - -/* Virtuoso scroller scrollbar styles */ -.messages-list [data-virtuoso-scroller] { - overflow-y: auto !important; - overflow-anchor: none; - overscroll-behavior: contain; - will-change: scroll-position; -} -.messages-list [data-virtuoso-scroller]::-webkit-scrollbar { - width: 8px; - background-color: var(--bg-primary); -} -.messages-list [data-virtuoso-scroller]::-webkit-scrollbar-thumb { - background-color: #666770; - border-radius: 4px; -} - -/* Attachment & link preview loading skeletons */ -.attachment-skeleton { - background-color: var(--embed-background, #2f3136); - display: flex; - align-items: center; - justify-content: center; - overflow: hidden; -} - -.link-preview-skeleton { - background-color: var(--embed-background, #2f3136); - border-left: 4px solid #202225; - overflow: hidden; -} - -.skeleton-bar { - background: linear-gradient(90deg, rgba(255,255,255,0.04) 25%, rgba(255,255,255,0.08) 50%, rgba(255,255,255,0.04) 75%); - background-size: 200% 100%; - animation: skeleton-shimmer 1.5s ease-in-out infinite; - border-radius: 4px; -} - -@keyframes skeleton-shimmer { - 0% { background-position: 200% 0; } - 100% { background-position: -200% 0; } -} - -.preview-description { - font-size: 14px; - color: var(--text-normal); - line-height: 1.4; - display: -webkit-box; - -webkit-line-clamp: 3; - line-clamp: 3; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.preview-image-container { - flex-shrink: 0; - position: relative; -} - -.preview-image { - max-width: 150px; - max-height: 150px; - border-radius: 4px; - object-fit: cover; -} - -.play-icon { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - color: white; - background: rgba(0, 0, 0, 0.6); - border-radius: 50%; - width: 40px; - height: 40px; - display: flex; - align-items: center; - justify-content: center; - font-size: 20px; -} - -/* Preview author line */ -.preview-author { - font-size: 13px; - color: var(--header-primary); - font-weight: 500; - margin-bottom: 2px; -} - -/* Provider-branded previews */ -.twitter-preview { - border-left-color: #1da1f2 !important; -} - -.twitter-preview .preview-description { - -webkit-line-clamp: 6; - line-clamp: 6; -} - -.spotify-preview { - border-left-color: #1db954 !important; -} - -.reddit-preview { - border-left-color: #ff4500 !important; -} - -/* Large image layout: image below content at full width */ -.large-image-layout { - flex-direction: column; - gap: 8px; -} - -.large-image-layout .preview-image-container.large-image { - width: 100%; - max-width: 400px; -} - -.large-image-layout .preview-image-container.large-image .preview-image { - width: 100%; - max-width: 100%; - max-height: 300px; - object-fit: cover; -} - -.youtube-preview { - flex-direction: column; - align-items: flex-start; - gap: 8px; -} - -.youtube-preview .preview-image-container { - width: 100%; - max-width: 400px; - position: relative; -} - -.youtube-preview .preview-image { - width: 100%; - max-width: 100%; - max-height: none; - aspect-ratio: 16 / 9; -} - -.youtube-video-wrapper { - margin-top: 8px; - position: relative; - width: 100%; - max-width: 560px; - /* Standard YouTube embed width or max for chat */ - padding-bottom: 56.25%; - /* 16:9 Aspect Ratio */ - height: 0; - overflow: hidden; - border-radius: 4px; - background-color: black; -} - -.youtube-video-wrapper iframe { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - border: 0; -} - -.date-divider { - display: flex; - align-items: center; - justify-content: center; - margin: 24px 16px 8px 16px; - position: relative; -} - -.date-divider::before { - content: ""; - position: absolute; - top: 50%; - left: 0; - right: 0; - height: 1px; - background-color: var(--border-subtle); - z-index: 1; -} - -.date-divider span { - /* background-color: var(--bg-primary); */ - padding: 0 8px; - color: var(--text-muted); - font-size: 12px; - font-weight: 600; - position: relative; - z-index: 2; -} - -.verification-failed { - display: flex; - align-items: center; - margin-right: 0.25rem; - cursor: help; -} - -/* Loading spinner */ -@keyframes spin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } -} - -.loading-spinner { - width: 32px; - height: 32px; - border-radius: 50%; - border: 3px solid rgba(255, 255, 255, 0.1); - border-top-color: var(--brand-experiment); - animation: spin 0.8s linear infinite; -} - -/* Skeleton message loading placeholders */ -@keyframes skeleton-pulse { - 0%, 100% { opacity: 0.06; } - 50% { opacity: 0.12; } -} - -.skeleton-message { - display: flex; - padding: 4px 48px 4px 16px; - gap: 16px; - align-items: flex-start; -} - -.skeleton-avatar { - width: 40px; - height: 40px; - border-radius: 50%; - flex-shrink: 0; - background: var(--text-normal); - animation: skeleton-pulse 1.5s ease-in-out infinite; -} - -.skeleton-name { - height: 16px; - border-radius: 8px; - margin-bottom: 8px; - margin-top: 4px; - background: var(--text-normal); - animation: skeleton-pulse 1.5s ease-in-out infinite; -} - -.skeleton-line { - height: 16px; - border-radius: 8px; - margin-bottom: 4px; - background: var(--text-normal); - animation: skeleton-pulse 1.5s ease-in-out infinite; -} - -/* Channel beginning indicator */ -.channel-beginning { - padding: 16px 16px 8px; - margin-bottom: 8px; -} - -.channel-beginning-icon { - width: 68px; - height: 68px; - border-radius: 50%; - background-color: var(--background-surface-highest); - display: flex; - align-items: center; - justify-content: center; - font-size: 36px; - font-weight: 600; - color: var(--header-primary); - margin-bottom: 8px; -} - -.channel-beginning-title { - font-size: 32px; - font-weight: 700; - color: var(--header-primary); - margin: 8px 0 4px; -} - -.channel-beginning-subtitle { - font-size: 15px; - color: var(--text-muted); - margin: 0; -} - -/* Utility to hide scrollbar but allow scrolling */ -.no-scrollbar { - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ -} -.no-scrollbar::-webkit-scrollbar { - display: none; -} - -/* ============================================ - TITLEBAR - ============================================ */ -.titlebar { - height: 32px; - background-color: var(--bg-tertiary); - display: flex; - align-items: center; - justify-content: center; - position: relative; - -webkit-app-region: drag; - z-index: 10000; - flex-shrink: 0; -} - -.titlebar-drag-region { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; -} - -.titlebar-title { - font-size: 12px; - font-weight: 600; - color: var(--text-muted); - pointer-events: none; - user-select: none; -} - -.titlebar-buttons { - position: absolute; - right: 0; - top: 0; - height: 100%; - display: flex; - -webkit-app-region: no-drag; -} - -.titlebar-btn { - width: 46px; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - background: none; - border: none; - color: var(--header-secondary); - cursor: pointer; - transition: background-color 0.1s; -} - -.titlebar-btn:hover { - background-color: rgba(255, 255, 255, 0.06); -} - -.titlebar-close:hover { - background-color: var(--danger); - color: var(--header-primary); -} - -/* ============================================ - CHAT HEADER - ============================================ */ -.chat-header { - height: 48px; - min-height: 48px; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 16px; - border-bottom: 1px solid var(--border-subtle); - flex-shrink: 0; - background-color: var(--bg-primary); -} - -.chat-header-left { - display: flex; - align-items: center; - gap: 8px; -} - -.chat-header-icon { - color: var(--text-muted); - font-size: 20px; - font-weight: 700; -} - -.chat-header-name { - color: var(--header-primary); - font-weight: 700; - font-size: 16px; -} - -.chat-header-status-text { - color: var(--text-muted); - font-size: 13px; - margin-left: 8px; -} - -.chat-header-right { - display: flex; - align-items: center; - gap: 4px; -} - -.chat-header-btn { - background: none; - border: none; - color: var(--header-secondary); - cursor: pointer; - padding: 6px; - border-radius: 4px; - display: flex; - align-items: center; - transition: color 0.1s; -} - -.chat-header-btn:hover { - color: var(--text-normal); -} - -.chat-header-btn.active { - color: var(--header-primary); -} - -.chat-header-search-wrapper { - margin-left: 4px; - position: relative; - display: flex; - align-items: center; -} - -.chat-header-search-icon { - position: absolute; - left: 8px; - color: var(--text-muted); - pointer-events: none; - z-index: 1; - display: flex; - align-items: center; -} - -.chat-header-search { - width: 214px; - height: 28px; - background-color: #17171a; - border: 1px solid color-mix(in oklab,hsl(240 calc(1*4%) 60.784% /0.2) 100%,hsl(0 0% 0% /0.2) 0%); - border-radius: 4px; - color: color-mix(in oklab, hsl(240 calc(1*6.667%) 94.118% /1) 100%, #000 0%); - padding: 0 28px 0 28px; - font-size: 13px; - outline: none; - transition: width 0.25s ease; - font-family: inherit; -} - -.chat-header-search::placeholder { - color: var(--text-muted); -} - -.chat-header-search.focused { - width: 240px; -} - -.chat-header-search-clear { - position: absolute; - right: 4px; - background: none; - border: none; - color: var(--text-muted); - cursor: pointer; - padding: 2px; - display: flex; - align-items: center; - border-radius: 2px; -} - -.chat-header-search-clear:hover { - color: var(--header-primary); -} - -/* ============================================ - MEMBERS LIST - ============================================ */ -.members-list { - width: 240px; - min-width: 240px; - background-color: var(--bg-primary); - border-left: 1px solid var(--border-subtle); - overflow-y: auto; - padding: 16px 8px; - flex-shrink: 0; -} - -.members-list::-webkit-scrollbar { - width: 6px; -} - -.members-list::-webkit-scrollbar-thumb { - background-color: var(--scrollbar-auto-thumb); - border-radius: 3px; -} - -.members-role-header { - font-size: 12px; - font-weight: 600; - color: var(--text-muted); - text-transform: uppercase; - padding: 16px 8px 4px; -} - -.member-item { - display: flex; - align-items: center; - padding: 6px 8px; - border-radius: 4px; - cursor: pointer; - transition: background-color 0.1s; -} - -.member-item:hover { - background-color: rgba(255, 255, 255, 0.04); -} - -.member-avatar-wrapper { - position: relative; - margin-right: 10px; - flex-shrink: 0; - width: 32px; - height: 32px; -} - -.member-avatar { - width: 32px; - height: 32px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-weight: 600; - font-size: 14px; -} - -.avatar-decoration { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 44px; - height: 44px; - pointer-events: none; - z-index: 1; -} - -.avatar-decoration-static { display: block; } -.avatar-decoration-animated { display: none; } - -.member-item:hover .avatar-decoration-static { display: none; } -.member-item:hover .avatar-decoration-animated { display: block; } - -.member-status-dot { - position: absolute; - bottom: -1px; - right: -1px; - width: 10px; - height: 10px; - border-radius: 50%; - border: 2px solid var(--bg-secondary); - z-index: 2; -} - -.member-info { - min-width: 0; -} - -.member-name { - font-size: 14px; - font-weight: 500; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.member-voice-indicator { - display: flex; - align-items: center; - gap: 4px; - color: #3ba55c; - font-size: 12px; - font-weight: 500; -} - -.member-voice-indicator svg { - width: 14px; - height: 14px; - flex-shrink: 0; -} - -.member-screen-sharing-indicator { - display: flex; - align-items: center; - gap: 4px; - color: #3ba55c; - font-size: 12px; - font-weight: 500; -} - -.member-screen-sharing-indicator img { - width: 14px; - height: 14px; - flex-shrink: 0; -} - -/* ============================================ - REPLY SYSTEM - ============================================ */ -.reply-preview-bar { - display: flex; - align-items: center; - justify-content: space-between; - background-color: var(--channeltextarea-background); - border-left: 4px solid var(--brand-experiment); - padding: 8px 12px; - margin: 0 8px; - border-radius: 4px 4px 0 0; -} - -.reply-preview-content { - font-size: 14px; - color: var(--header-secondary); - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} - -.reply-preview-content strong { - color: var(--header-primary); -} - -.reply-preview-text { - margin-left: 8px; - color: var(--text-muted); - font-size: 13px; -} - -.reply-preview-close { - background: none; - border: none; - color: var(--header-secondary); - cursor: pointer; - font-size: 20px; - padding: 0 4px; - line-height: 1; -} - -.reply-preview-close:hover { - color: var(--header-primary); -} - -.message-reply-context { - width: 100%; - display: flex; - align-items: center; - gap: 4px; - font-size: 0.75rem; - color: var(--text-muted); - cursor: pointer; - margin-left: 32px; - padding: 2px 0; - position: relative; -} - -.reply-spine { - position: absolute; - top: 50%; - left: -16px; - width: 33px; - height: 13px; - border-left: 2px solid var(--text-muted); - border-top: 2px solid var(--text-muted); - border-top-left-radius: 6px; - box-sizing: border-box; -} - -.reply-avatar { - flex-shrink: 0; - border-radius: 50%; - margin-left: 20px; -} - -.message-reply-context .reply-author { - font-weight: 600; - font-size: 0.75rem; - flex-shrink: 0; - cursor: pointer; -} - -.message-reply-context .reply-author:hover { - text-decoration: underline; -} - -.message-reply-context .reply-text { - color: var(--text-muted); - font-size: 0.75rem; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} - -.message-reply-context:hover .reply-text { - color: var(--text-normal); -} - -/* ============================================ - MESSAGE EDIT - ============================================ */ -.message-editing { - margin: 4px 0; -} - -.message-edit-textarea { - width: 100%; - background-color: var(--channeltextarea-background); - border: none; - border-radius: 8px; - color: var(--text-normal); - font-family: inherit; - font-size: 1rem; - line-height: 1.375rem; - padding: 11px 16px; - resize: none; - outline: none; - min-height: 44px; - box-sizing: border-box; -} - -.message-edit-hint { - font-size: 11px; - color: var(--text-muted); - margin-top: 4px; -} - -.message-edit-hint span { - color: var(--text-link); - cursor: pointer; -} - -.message-edit-hint span:hover { - text-decoration: underline; -} - -.edited-indicator { - color: var(--text-muted); - font-size: 0.625rem; - margin-left: 4px; - user-select: none; -} - -/* ============================================ - MESSAGE TOOLBAR - ============================================ */ -.message-toolbar { - position: absolute; - top: -40px; - right: 16px; - background-color: var(--bg-secondary); - border: 1px solid var(--bg-tertiary); - border-radius: 8px; - box-shadow: 0 1px 5px 0 rgba(0,0,0,0.28); - display: flex; - align-items: center; - z-index: 10; - padding: 2px; -} - -.icon-button:hover { - background-color: var(--background-modifier-hover); - border-radius: 4px; -} - -/* ============================================ - CONTEXT MENU - ============================================ */ -.context-menu { - position: fixed; - background-color: var(--panel-bg); - border-radius: 8px; - border: 1px solid var(--app-frame-border); - box-shadow: 0 8px 16px rgba(0,0,0,0.24); - z-index: 9999; - min-width: 188px; - padding: 6px 8px; - display: flex; - flex-direction: column; - gap: 2px; - animation: contextMenuFadeIn 0.1s ease; -} - -@keyframes contextMenuFadeIn { - from { opacity: 0; transform: scale(0.95); } - to { opacity: 1; transform: scale(1); } -} - -.context-menu-item { - display: flex; - align-items: center; - padding: 10px 12px; - cursor: pointer; - font-size: 14px; - color: var(--text-normal); - justify-content: space-between; - white-space: nowrap; - border-radius: 8px; - transition: background-color 0.1s; -} - -.context-menu-item:hover { - background-color: hsla(240, 4%, 60.784%, 0.078); -} - -.context-menu-item-danger { - color: hsl(1.353, 82.609%, 68.431%); -} - -.context-menu-item-danger:hover { - background-color: hsla(355.636, 64.706%, 50%, 0.078); -} - -.context-menu-checkbox-item { - display: flex; - align-items: center; - justify-content: space-between; -} - -.context-menu-checkbox { - display: flex; - align-items: center; - margin-left: 8px; -} - -.context-menu-checkbox-indicator { - width: 20px; - height: 20px; - border-radius: 4px; - border: 2px solid var(--header-secondary); - display: flex; - align-items: center; - justify-content: center; - transition: all 0.15s; -} - -.context-menu-checkbox-indicator.checked { - background-color: hsl(235, 86%, 65%); - border-color: hsl(235, 86%, 65%); -} - -.context-menu-separator { - height: 1px; - background-color: var(--bg-primary); - margin: 4px 0; -} - -/* ============================================ - CONTEXT MENU VOLUME SLIDER - ============================================ */ -.context-menu-volume { - padding: 8px 12px; -} - -.context-menu-volume-label { - display: flex; - align-items: center; - justify-content: space-between; - font-size: 12px; - font-weight: 600; - color: var(--header-secondary); - text-transform: uppercase; - margin-bottom: 8px; -} - -.context-menu-volume-slider { - -webkit-appearance: none; - appearance: none; - width: 100%; - height: 4px; - border-radius: 2px; - outline: none; - cursor: pointer; -} - -.context-menu-volume-slider::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 14px; - height: 14px; - border-radius: 50%; - background: white; - cursor: pointer; - border: none; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); -} - -.context-menu-volume-slider::-moz-range-thumb { - width: 14px; - height: 14px; - border-radius: 50%; - background: white; - cursor: pointer; - border: none; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); -} - -.context-menu-volume-slider::-moz-range-track { - height: 4px; - border-radius: 2px; - background: transparent; -} - -/* ============================================ - MENTION MENU - ============================================ */ -.mention-menu { - position: absolute; - bottom: calc(100% + 4px); - left: 0; - right: 0; - background-color: var(--background-surface-high, var(--embed-background)); - border-radius: 5px; - box-shadow: 0 10px 16px rgba(0,0,0,0.24); - z-index: 100; - margin: 8px; -} - -.mention-menu-header { - padding: 12px 12px 4px; - font-size: 12px; - font-weight: 600; - text-transform: uppercase; - color: var(--header-secondary); -} - -.mention-menu-section-header { - padding: 8px 12px 4px; - font-size: 12px; - font-weight: 600; - text-transform: uppercase; - color: var(--header-secondary); -} - -.mention-menu-role-icon { - width: 24px; - height: 24px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-weight: 700; - font-size: 14px; - flex-shrink: 0; -} - -.mention-menu-scroller { - max-height: 490px; - overflow-y: auto; - padding-bottom: 8px; -} - -.mention-menu-scroller::-webkit-scrollbar { - width: 6px; -} - -.mention-menu-scroller::-webkit-scrollbar-thumb { - background-color: var(--scrollbar-auto-thumb, var(--bg-tertiary)); - border-radius: 3px; -} - -.mention-menu-row { - display: flex; - align-items: center; - padding: 8px 12px; - gap: 10px; - cursor: pointer; -} - -.mention-menu-row:hover, -.mention-menu-row.selected { - background-color: var(--interactive-background-hover, rgba(255,255,255,0.06)); -} - -.mention-menu-row-primary { - font-size: 14px; - font-weight: 500; - color: var(--text-normal); -} - -.mention-menu-row-secondary { - font-size: 12px; - margin-left: auto; - color: var(--interactive-text-default, var(--text-muted)); -} - -/* ============================================ - TOOLTIP - ============================================ */ -.tooltip { - background-color: var(--background-base-lowest); - color: var(--text-normal); - padding: 6px 10px; - border-radius: 4px; - font-size: 14px; - font-weight: 500; - white-space: nowrap; - pointer-events: none; - box-shadow: 0 2px 10px rgba(0,0,0,0.2); -} - -.tooltip-arrow { - position: absolute; - width: 8px; - height: 8px; - background-color: var(--background-base-lowest); - transform: rotate(45deg); -} - -.tooltip-arrow-top { - bottom: -4px; - left: calc(50% - 4px); -} - -.tooltip-arrow-bottom { - top: -4px; - left: calc(50% - 4px); -} - -.tooltip-arrow-left { - right: -4px; - top: calc(50% - 4px); -} - -.tooltip-arrow-right { - left: -4px; - top: calc(50% - 4px); -} - -/* ============================================ - SERVER STRIP - ============================================ */ -.server-separator { - width: 32px; - height: 2px; - background-color: var(--bg-primary); - border-radius: 1px; - margin: 4px 0 8px; -} - -.server-item-wrapper { - position: relative; - display: flex; - align-items: center; - justify-content: center; - width: 100%; - margin-top: 8px; - margin-bottom: 8px; -} - -.server-pill { - position: absolute; - left: 0; - width: 4px; - border-radius: 0 4px 4px 0; - background-color: var(--header-primary); - height: 0; - transition: height 0.2s ease; -} - -.server-item-wrapper:hover .server-pill { - height: 20px; -} - -.server-pill.active { - height: 40px; -} - -/* ============================================ - STATUS MENU - ============================================ */ -.status-menu { - position: absolute; - bottom: 100%; - left: 0; - right: 0; - background-color: var(--background-base-lowest); - border-radius: 4px; - box-shadow: 0 2px 10px rgba(0,0,0,0.3); - padding: 6px 8px; - z-index: 100; - margin-bottom: 4px; -} - -.status-menu-item { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 10px; - cursor: pointer; - color: var(--text-normal); - font-size: 14px; - border-radius: 2px; - transition: background-color 0.1s; -} - -.status-menu-item:hover { - background-color: rgba(255,255,255,0.06); -} - -.status-menu-dot { - width: 10px; - height: 10px; - border-radius: 50%; - flex-shrink: 0; -} - -.user-control-info { - display: flex; - align-items: center; - margin-right: auto; - padding: 4px; - border-radius: 4px; - cursor: pointer; - transition: background-color 0.1s; - overflow: hidden; -} - -.user-control-info:hover { - background-color: rgba(255,255,255,0.05); -} - -/* ============================================ - PINNED MESSAGES PANEL - ============================================ */ -.pinned-panel { - position: absolute; - top: 48px; - right: 0; - width: 420px; - max-height: calc(100% - 48px); - background-color: var(--embed-background); - box-shadow: 0 2px 10px rgba(0,0,0,0.3); - z-index: 100; - display: flex; - flex-direction: column; - border-radius: 0 0 0 8px; -} - -.pinned-panel-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 16px; - border-bottom: 1px solid var(--border-subtle); -} - -.pinned-panel-header h3 { - margin: 0; - color: var(--header-primary); - font-size: 16px; -} - -.pinned-panel-close { - background: none; - border: none; - color: var(--header-secondary); - font-size: 24px; - cursor: pointer; - line-height: 1; -} - -.pinned-panel-close:hover { - color: var(--header-primary); -} - -.pinned-panel-content { - overflow-y: auto; - padding: 8px; - flex: 1; -} - -.pinned-panel-empty { - text-align: center; - color: var(--text-muted); - padding: 32px 16px; -} - -.pinned-message-card { - background-color: var(--bg-primary); - border-radius: 8px; - margin-bottom: 8px; - overflow: hidden; -} - -.pinned-message-card .message-item { - padding: 8px 12px 4px; -} - -.pinned-message-card .message-toolbar { - display: none !important; -} - -.pinned-message-card .message-content img, -.pinned-message-card .message-content video { - max-width: 100%; -} - -.pinned-message-actions { - display: flex; - gap: 8px; - padding: 4px 12px 10px 68px; -} - -.pinned-action-btn { - background: none; - border: 1px solid var(--text-muted); - color: var(--header-secondary); - cursor: pointer; - padding: 4px 12px; - border-radius: 3px; - font-size: 13px; - transition: all 0.1s; -} - -.pinned-action-btn:hover { - background-color: rgba(255,255,255,0.06); - color: var(--header-primary); -} - -.pinned-action-danger { - color: var(--danger); - border-color: var(--danger); -} - -.pinned-action-danger:hover { - background-color: rgba(237, 66, 69, 0.1); -} - -/* ============================================ - USER PROFILE POPUP - ============================================ */ -.user-profile-popup { - width: 300px; - background-color: var(--background-base-lowest); - border-radius: 8px; - box-shadow: 0 4px 20px rgba(0,0,0,0.4); - overflow: hidden; -} - -.user-profile-banner { - height: 60px; -} - -.user-profile-body { - padding: 0 16px 16px; -} - -.user-profile-avatar-wrapper { - position: relative; - margin-top: -32px; - margin-bottom: 8px; - width: fit-content; -} - -.user-profile-avatar { - width: 64px; - height: 64px; - border-radius: 50%; - border: 4px solid var(--background-base-lowest); - display: flex; - align-items: center; - justify-content: center; - color: white; - font-weight: 700; - font-size: 24px; -} - -.user-profile-status-dot { - position: absolute; - bottom: 2px; - right: 2px; - width: 14px; - height: 14px; - border-radius: 50%; - border: 3px solid var(--background-base-lowest); -} - -.user-profile-name { - color: var(--header-primary); - font-size: 20px; - font-weight: 700; - margin-bottom: 2px; -} - -.user-profile-status-text { - color: var(--header-secondary); - font-size: 14px; - margin-bottom: 12px; -} - -.user-profile-divider { - height: 1px; - background-color: var(--bg-primary); - margin: 8px 0; -} - -.user-profile-section-header { - font-size: 12px; - font-weight: 700; - color: var(--header-primary); - text-transform: uppercase; - margin-bottom: 4px; -} - -.user-profile-about { - color: var(--header-secondary); - font-size: 14px; - margin-bottom: 12px; -} - -.user-profile-message-btn { - width: 100%; - background-color: var(--brand-experiment); - color: var(--header-primary); - border: none; - border-radius: 3px; - padding: 8px; - font-size: 14px; - cursor: pointer; - transition: background-color 0.1s; -} - -.user-profile-message-btn:hover { - background-color: var(--brand-experiment-hover); -} - -/* ============================================ - TOAST NOTIFICATIONS - ============================================ */ -.toast-container { - position: fixed; - bottom: 24px; - right: 24px; - z-index: 10002; - display: flex; - flex-direction: column; - gap: 8px; -} - -.toast { - display: flex; - align-items: center; - background-color: var(--embed-background); - border-radius: 8px; - padding: 12px 16px; - min-width: 300px; - box-shadow: 0 4px 12px rgba(0,0,0,0.3); - gap: 12px; -} - -.toast-enter { - animation: toastSlideIn 0.3s ease; -} - -.toast-exit { - animation: toastSlideOut 0.3s ease forwards; -} - -@keyframes toastSlideIn { - from { transform: translateX(100%); opacity: 0; } - to { transform: translateX(0); opacity: 1; } -} - -@keyframes toastSlideOut { - from { transform: translateX(0); opacity: 1; } - to { transform: translateX(100%); opacity: 0; } -} - -.toast-avatar { - width: 32px; - height: 32px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-weight: 600; - font-size: 14px; - flex-shrink: 0; -} - -.toast-body { - flex: 1; - min-width: 0; -} - -.toast-title { - color: var(--text-normal); - font-size: 14px; -} - -.toast-preview { - color: var(--text-muted); - font-size: 13px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.toast-close { - background: none; - border: none; - color: var(--text-muted); - font-size: 18px; - cursor: pointer; - padding: 0 4px; -} - -.toast-close:hover { - color: var(--text-normal); -} - -/* ============================================ - MESSAGE HIGHLIGHT - ============================================ */ -@keyframes messageHighlight { - 0% { background-color: rgba(88, 101, 242, 0.2); } - 100% { background-color: transparent; } -} - -.message-highlight { - animation: messageHighlight 2s ease; -} - -/* ============================================ - JUMP TO PRESENT BUTTON - ============================================ */ -.jump-to-present-btn { - position: absolute; - bottom: 80px; - left: 50%; - transform: translateX(-50%); - z-index: 10; - display: flex; - align-items: center; - padding: 6px 16px; - border-radius: 24px; - background-color: var(--brand-experiment, #5865f2); - color: #ffffff; - font-size: 14px; - font-weight: 500; - border: none; - cursor: pointer; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4); - transition: background-color 0.15s ease; - font-family: inherit; -} - -.jump-to-present-btn:hover { - background-color: #4752c4; -} - -/* ============================================ - SYSTEM MESSAGES - ============================================ */ -.system-message-row { - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - padding: 8px 16px; -} - -.system-message-text { - color: var(--text-muted); - font-size: 14px; -} - -.system-message-time { - color: var(--text-muted); - font-size: 12px; - opacity: 0.7; -} - -/* ============================================ - FRIENDS VIEW - ============================================ */ -.friend-item { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 8px; - border-top: 1px solid var(--border-subtle); - cursor: pointer; - border-radius: 4px; - transition: background-color 0.1s; -} - -.friend-item:hover { - background-color: rgba(255,255,255,0.04); -} - -.friend-action-btn { - padding: 8px; - background-color: var(--embed-background); - border-radius: 50%; - cursor: pointer; - color: var(--header-secondary); - display: flex; - align-items: center; - justify-content: center; - transition: background-color 0.1s, color 0.1s; -} - -.friend-action-btn:hover { - background-color: var(--bg-primary); - color: var(--text-normal); -} - -.friends-tab { - transition: background-color 0.1s; -} - -.friends-tab:hover { - background-color: rgba(255,255,255,0.04) !important; -} - -/* ============================================ - DM LIST - ============================================ */ - -.dm-search-wrapper { - height: 48px; - display: flex; - align-items: center; - padding: 0 16px; - border-bottom: 1px solid var(--app-frame-border); - flex-shrink: 0; - font-weight: 600; - font-size: 15px; - color: var(--header-primary); - gap: 8px; - margin-bottom: 8px; -} - -.dm-search-input { - width: 100%; - height: 28px; - background-color: var(--bg-tertiary); - border: none; - border-radius: 4px; - color: var(--text-normal); - padding: 0 8px; - font-size: 13px; - outline: none; - box-sizing: border-box; -} - -.dm-search-input::placeholder { - color: var(--text-muted); -} - -.dm-search-dropdown { - position: absolute; - top: 100%; - left: 0; - right: 0; - background-color: var(--embed-background); - border-radius: 0 0 4px 4px; - box-shadow: 0 4px 12px rgba(0,0,0,0.3); - z-index: 10; - max-height: 240px; - overflow-y: auto; - padding: 4px; -} - -.dm-search-result { - display: flex; - align-items: center; - padding: 6px 8px; - border-radius: 4px; - cursor: pointer; - color: var(--text-normal); - font-size: 14px; -} - -.dm-search-result:hover { - background-color: rgba(255,255,255,0.06); -} - -.dm-picker-user { - display: flex; - align-items: center; - padding: 8px; - border-radius: 4px; - cursor: pointer; - color: var(--text-normal); - transition: background-color 0.1s; -} - -.dm-picker-user:hover { - background-color: rgba(255,255,255,0.06); -} - -.dm-friends-btn { - margin: 8px; - display: flex; - align-items: center; - padding: 10px 8px; - border-radius: 8px; - color: var(--text-muted); - cursor: pointer; - margin-bottom: 16px; - transition: background-color 0.1s; -} - -.dm-friends-btn:hover { - background-color: rgba(255,255,255,0.02); -} - -.dm-friends-btn.active { - background-color: rgba(255,255,255,0.04); - color: var(--header-primary); -} - -.dm-item { - display: flex; - align-items: center; - padding: 8px 8px 8px 8px; - border-radius: 8px; - cursor: pointer; - color: var(--text-muted); - transition: background-color 0.1s; - position: relative; - margin-bottom: 4px; -} - -.dm-item:hover { - background-color: rgba(255,255,255,0.04); -} - -.dm-item-active { - background-color: rgba(255,255,255,0.06); - color: var(--header-primary); -} - -.dm-item-status { - font-size: 12px; - color: var(--header-secondary); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.dm-close-btn { - opacity: 0; - color: var(--header-secondary); - cursor: pointer; - padding: 2px; - border-radius: 4px; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - transition: opacity 0.1s; -} - -.dm-item:hover .dm-close-btn { - opacity: 1; -} - -.dm-close-btn:hover { - color: var(--text-normal); -} - -/* ============================================ - MODAL SHADOWS - ============================================ */ -.auth-box, -.pinned-panel, -.context-menu, -.user-profile-popup, -.message-toolbar { - box-shadow: 0 2px 10px 0 rgba(0,0,0,0.2); -} - -/* ============================================ - CHANNEL ITEM HOVER TRANSITIONS - ============================================ */ -.channel-item { - transition: background-color 0.1s ease; -} - -.channel-settings-icon { - transition: opacity 0.2s; - opacity: 0; -} - -.channel-item:hover .channel-settings-icon, -.channel-item.active .channel-settings-icon { - opacity: 1; -} - -/* ============================================ - SERVER HEADER - ============================================ */ -.server-header { - height: 48px; - display: flex; - align-items: center; - padding: 0 8px; - border-bottom: 1px solid var(--app-frame-border); - flex-shrink: 0; - font-weight: 600; - font-size: 15px; - color: var(--header-primary); - gap: 8px; -} - -.server-header-name { - flex: 1; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - cursor: pointer; - border-radius: 4px; - padding: 4px 4px; - transition: background-color 0.1s; - font-size: 16px; - line-height: 1.25; - font-weight: 600; - display: flex; - align-items: center; -} - -.server-header-name:hover { - background-color: var(--background-modifier-hover); -} - -.mobile-server-chevron { - color: var(--text-muted); - margin-left: 2px; - flex-shrink: 0; -} - -.server-header-invite { - flex-shrink: 0; - background: none; - border: none; - padding: 4px; - border-radius: 4px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - color: var(--interactive-normal); - transition: color 0.15s, background-color 0.15s; -} - -.server-header-invite:hover { - color: var(--interactive-hover); - background-color: var(--background-modifier-hover); -} - -.server-header-invite img { - width: 20px; - height: 20px; - filter: brightness(0) invert(0.7); -} - -.server-header-invite:hover img { - filter: brightness(0) invert(0.9); -} - -/* ============================================ - CHANNEL CATEGORIES - ============================================ */ -.channel-category-header { - display: flex; - align-items: center; - padding: 16px 8px 4px; - cursor: pointer; - font-size: 14px; - font-weight: 500; - color: var(--text-muted); - line-height: 1.2857142857142858; - letter-spacing: 0.02em; - user-select: none; -} - -.channel-category-header:hover { - color: var(--interactive-hover); -} - -.category-chevron { - margin-left: 4px; - font-size: 10px; - transition: transform 0.2s; - flex-shrink: 0; -} - -.category-chevron.collapsed { - transform: rotate(-90deg); -} - -.category-label { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.category-add-btn { - margin-left: auto; - background: none; - border: none; - color: var(--interactive-normal); - cursor: pointer; - font-size: 16px; - padding: 0 4px; - opacity: 0; - transition: opacity 0.1s; - display: flex; - align-items: center; -} - -.channel-category-header:hover .category-add-btn { - opacity: 1; -} - -.category-add-btn:hover { - color: var(--interactive-hover); -} - -/* ============================================ - CHAT HEADER ADDITIONS - ============================================ */ -.chat-header-divider { - width: 1px; - height: 24px; - background-color: var(--border-subtle); - margin: 0 8px; - flex-shrink: 0; -} - -.chat-header-topic { - color: var(--text-muted); - font-size: 12px; - font-weight: 400; - max-width: 300px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - cursor: default; -} - -.chat-header-topic:hover { - color: var(--interactive-hover); -} - -/* ============================================ - TITLEBAR NAV BUTTONS - ============================================ */ -.titlebar-nav { - position: absolute; - left: 8px; - top: 0; - height: 100%; - display: flex; - align-items: center; - gap: 4px; - -webkit-app-region: no-drag; -} - -.titlebar-nav-btn { - width: 28px; - height: 28px; - display: flex; - align-items: center; - justify-content: center; - background: none; - border: none; - color: var(--interactive-normal); - cursor: pointer; - border-radius: 4px; - padding: 0; -} - -.titlebar-nav-btn:hover { - color: var(--interactive-hover); -} - -/* ============================================ - USER PROFILE ROLE BADGES - ============================================ */ -.user-profile-roles { - display: flex; - flex-wrap: wrap; - gap: 4px; - margin-bottom: 12px; -} - -.user-profile-role-badge { - display: flex; - align-items: center; - gap: 4px; - background-color: var(--panel-bg); - border-radius: 4px; - padding: 4px 8px; - font-size: 12px; - color: var(--text-normal); - font-weight: 500; -} - -.user-profile-role-dot { - width: 12px; - height: 12px; - border-radius: 50%; - flex-shrink: 0; -} - -.user-profile-note-input { - width: 100%; - background-color: transparent; - border: none; - color: var(--text-muted); - font-size: 12px; - padding: 4px 0; - outline: none; - font-family: inherit; - resize: none; -} - -.user-profile-note-input:focus { - color: var(--text-normal); -} - -/* ============================================ - USER BADGE (OWNER etc.) - ============================================ */ -.user-badge { - display: inline-flex; - align-items: center; - background-color: var(--brand-experiment); - color: var(--header-primary); - font-size: 10px; - font-weight: 600; - padding: 1px 4px; - border-radius: 3px; - margin-left: 4px; - text-transform: uppercase; - letter-spacing: 0.02em; - vertical-align: middle; -} - -/* ============================================ - VOICE TIMER - ============================================ */ -.voice-timer { - color: var(--text-muted); - font-size: 12px; - font-variant-numeric: tabular-nums; -} - -/* ============================================ - NEW MESSAGES DIVIDER - ============================================ */ -.new-messages-divider { - display: flex; - align-items: center; - margin: 8px 16px; - position: relative; -} - -.new-messages-divider::before { - content: ""; - position: absolute; - top: 50%; - left: 0; - right: 0; - height: 1px; - background-color: var(--danger); - z-index: 1; -} - -.new-messages-divider span { - background-color: var(--bg-primary); - padding: 0 8px; - color: var(--danger); - font-size: 12px; - font-weight: 600; - position: relative; - z-index: 2; - margin-left: auto; -} - -/* ============================================ - THEME SELECTOR - ============================================ */ -.theme-selector-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.7); - display: flex; - align-items: center; - justify-content: center; - z-index: 10001; -} - -.theme-selector-modal { - background-color: var(--modal-background); - border-radius: 8px; - width: 440px; - max-width: 90vw; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4); - overflow: hidden; -} - -.theme-selector-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 16px 20px; -} - -.theme-selector-header h2 { - margin: 0; - color: var(--text-strong); - font-size: 20px; - font-weight: 700; -} - -.theme-selector-close { - background: none; - border: none; - color: var(--text-muted); - font-size: 20px; - cursor: pointer; - padding: 4px; - line-height: 1; -} - -.theme-selector-close:hover { - color: var(--text-default); -} - -.theme-selector-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 12px; - padding: 0 20px 20px; -} - -.theme-card { - cursor: pointer; - border-radius: 8px; - border: 2px solid transparent; - transition: border-color 0.15s; - overflow: hidden; -} - -.theme-card:hover { - border-color: var(--border-normal); -} - -.theme-card.active { - border-color: var(--control-primary-background-default); -} - -.theme-preview { - height: 80px; - display: flex; - border-radius: 6px 6px 0 0; - overflow: hidden; -} - -.theme-preview-sidebar { - width: 35%; - padding: 8px 6px; - display: flex; - flex-direction: column; - gap: 4px; - justify-content: center; -} - -.theme-preview-channel { - height: 6px; - border-radius: 3px; - width: 80%; -} - -.theme-preview-chat { - flex: 1; - padding: 10px 8px; - display: flex; - flex-direction: column; - gap: 6px; - justify-content: center; -} - -.theme-preview-message { - height: 8px; - border-radius: 4px; - width: 85%; -} - -.theme-card-label { - display: flex; - align-items: center; - gap: 8px; - padding: 10px 12px; - color: var(--text-default); - font-size: 14px; - font-weight: 500; -} - -.theme-radio { - width: 20px; - height: 20px; - border-radius: 50%; - border: 2px solid var(--text-muted); - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - transition: border-color 0.15s; -} - -.theme-radio.active { - border-color: var(--control-primary-background-default); -} - -.theme-radio-dot { - width: 10px; - height: 10px; - border-radius: 50%; - background-color: var(--control-primary-background-default); -} - -/* ============================================ - USER SETTINGS - AVATAR OVERLAY - ============================================ */ -.user-settings-avatar-wrapper { - position: relative; - border-radius: 50%; - overflow: hidden; -} - -.user-settings-avatar-overlay { - position: absolute; - top: 0; - left: 0; - width: 80px; - height: 80px; - border-radius: 50%; - background-color: rgba(0, 0, 0, 0.6); - display: flex; - align-items: center; - justify-content: center; - color: white; - font-size: 10px; - font-weight: 700; - text-transform: uppercase; - text-align: center; - line-height: 1.3; - opacity: 0; - transition: opacity 0.15s; - pointer-events: none; -} - -.user-settings-avatar-wrapper:hover .user-settings-avatar-overlay { - opacity: 1; -} - -/* Server icon upload (settings) */ -.server-icon-upload-wrapper { - position: relative; - width: 80px; - height: 80px; - border-radius: 16px; - overflow: hidden; - flex-shrink: 0; -} - -.server-icon-upload-overlay { - position: absolute; - top: 0; - left: 0; - width: 80px; - height: 80px; - border-radius: 16px; - background-color: rgba(0, 0, 0, 0.6); - display: flex; - align-items: center; - justify-content: center; - color: white; - font-size: 10px; - font-weight: 700; - text-transform: uppercase; - text-align: center; - line-height: 1.3; - opacity: 0; - transition: opacity 0.15s; - pointer-events: none; -} - -.server-icon-upload-wrapper:hover .server-icon-upload-overlay { - opacity: 1; -} - -/* ============================================ - AVATAR CROP MODAL - ============================================ */ -.avatar-crop-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.7); - display: flex; - align-items: center; - justify-content: center; - z-index: 10001; -} - -.avatar-crop-dialog { - width: 440px; - background-color: var(--bg-tertiary); - border-radius: 8px; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4); - overflow: hidden; -} - -.avatar-crop-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 16px 20px; -} - -.avatar-crop-area { - position: relative; - height: 300px; - background-color: #000; -} - -.avatar-crop-slider-row { - display: flex; - align-items: center; - gap: 12px; - padding: 16px 20px; -} - -.avatar-crop-slider { - flex: 1; - -webkit-appearance: none; - appearance: none; - height: 6px; - background: var(--bg-secondary); - border-radius: 3px; - outline: none; - cursor: pointer; -} - -.avatar-crop-slider::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 14px; - height: 14px; - border-radius: 50%; - background: var(--header-primary); - cursor: pointer; - border: none; -} - -.avatar-crop-slider::-moz-range-thumb { - width: 14px; - height: 14px; - border-radius: 50%; - background: var(--header-primary); - cursor: pointer; - border: none; -} - -.avatar-crop-actions { - display: flex; - justify-content: flex-end; - gap: 12px; - padding: 0 20px 20px; -} - -/* ============================================ - USER SETTINGS - MIC LEVEL METER - ============================================ */ -.mic-level-bar { - flex: 1; - height: 8px; - background-color: var(--bg-tertiary); - border-radius: 4px; - overflow: hidden; -} - -.mic-level-fill { - height: 100%; - background-color: #3ba55c; - border-radius: 4px; - transition: width 0.05s ease; -} - -/* ============================================ - USER SETTINGS - VOICE SLIDER - ============================================ */ -.voice-slider { - flex: 1; - -webkit-appearance: none; - appearance: none; - height: 6px; - background: var(--bg-tertiary); - border-radius: 3px; - outline: none; - cursor: pointer; -} - -.voice-slider::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 14px; - height: 14px; - border-radius: 50%; - background: var(--header-primary); - cursor: pointer; - border: none; -} - -.voice-slider::-moz-range-thumb { - width: 14px; - height: 14px; - border-radius: 50%; - background: var(--header-primary); - cursor: pointer; - border: none; -} - -/* ============================================ - UNREAD MESSAGES DIVIDER - ============================================ */ -.unread-divider { - border-top: 1px solid var(--danger); - margin: 0 16px 8px; - position: relative; - height: 0; -} - -.unread-pill { - position: absolute; - top: -7px; - right: 4px; - background: var(--danger); - color: #fff; - font-size: 10px; - font-weight: 700; - text-transform: uppercase; - padding: 1px 6px 1px 0; - border-radius: 0 4px 4px 0; - line-height: 13px; - display: flex; - align-items: center; -} - -.unread-pill-cap { - color: var(--danger); - margin-left: -7px; -} - -/* ============================================ - SIDEBAR UNREAD INDICATOR - ============================================ */ -.channel-unread-indicator { - position: absolute; - left: -8px; - top: 50%; - transform: translateY(-50%); - width: 4px; - height: 8px; - border-radius: 0 4px 4px 0; - background: var(--header-primary); -} - -/* ============================================ - DM SERVER STRIP NOTIFICATIONS - ============================================ */ -.dm-server-icon { - position: relative; - padding: 0; - overflow: visible; -} - -.dm-server-icon img, -.dm-server-icon > div:first-child { - width: 48px; - height: 48px; - border-radius: inherit; -} - -.dm-notification-dot { - position: absolute; - bottom: -2px; - right: -2px; - width: 14px; - height: 14px; - background-color: #f23f42; - border: 3px solid var(--bg-tertiary); - border-radius: 50%; - z-index: 1; -} - -/* ============================================ - CREATE CHANNEL MODAL - ============================================ */ -.create-channel-modal-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.85); - display: flex; - align-items: center; - justify-content: center; - z-index: 10001; -} - -.create-channel-modal { - width: 440px; - max-width: 90vw; - background-color: var(--bg-primary); - border-radius: 8px; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4); - overflow: hidden; -} - -.create-channel-modal-header { - display: flex; - align-items: flex-start; - justify-content: space-between; - padding: 16px 16px 12px; -} - -.create-channel-modal-close { - background: none; - border: none; - color: var(--text-muted); - cursor: pointer; - padding: 4px; - line-height: 1; - display: flex; - align-items: center; -} - -.create-channel-modal-close:hover { - color: var(--text-normal); -} - -.channel-type-option { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 12px; - border-radius: 4px; - cursor: pointer; - background-color: var(--bg-secondary); - margin-bottom: 8px; - transition: background-color 0.1s; -} - -.channel-type-option:hover { - background-color: var(--background-modifier-hover); -} - -.channel-type-option.selected { - background-color: var(--background-modifier-selected); -} - -.channel-type-radio { - width: 20px; - height: 20px; - border-radius: 50%; - border: 2px solid var(--interactive-normal); - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - transition: border-color 0.15s; -} - -.channel-type-radio.selected { - border-color: var(--brand-experiment); -} - -.channel-type-radio-dot { - width: 10px; - height: 10px; - border-radius: 50%; - background-color: var(--brand-experiment); -} - -.create-channel-name-input-wrapper { - display: flex; - align-items: center; - background-color: var(--bg-tertiary); - border-radius: 4px; - padding: 8px 12px; -} - -.create-channel-name-input { - flex: 1; - background: transparent; - border: none; - color: var(--text-normal); - font-size: 16px; - font-family: inherit; - outline: none; - padding: 0; -} - -.create-channel-name-input::placeholder { - color: var(--text-muted); -} - -.create-channel-modal-footer { - display: flex; - align-items: center; - justify-content: flex-end; - padding: 16px; - background-color: var(--bg-secondary); - gap: 12px; -} - -.create-channel-cancel-btn { - background: none; - border: none; - color: var(--text-normal); - cursor: pointer; - font-size: 14px; - font-weight: 500; - padding: 8px 16px; - font-family: inherit; -} - -.create-channel-cancel-btn:hover { - text-decoration: underline; -} - -.create-channel-submit-btn { - background-color: var(--brand-experiment); - color: white; - border: none; - border-radius: 3px; - padding: 8px 16px; - font-size: 14px; - font-weight: 500; - cursor: pointer; - font-family: inherit; - transition: background-color 0.15s; -} - -.create-channel-submit-btn:hover { - background-color: var(--brand-experiment-hover); -} - -.create-channel-submit-btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -/* ============================================ - CATEGORY TOGGLE SWITCH (visual only) - ============================================ */ -.category-toggle-switch { - width: 40px; - height: 24px; - background-color: var(--interactive-normal); - border-radius: 12px; - position: relative; - cursor: pointer; - flex-shrink: 0; - opacity: 0.5; -} - -.category-toggle-knob { - width: 18px; - height: 18px; - background-color: var(--header-primary); - border-radius: 50%; - position: absolute; - top: 3px; - left: 3px; - transition: left 0.2s; -} - -/* ============================================ - DRAG OVERLAY - ============================================ */ -.drag-overlay-channel { - display: flex; - align-items: center; - padding: 8px; - background-color: var(--background-modifier-selected); - border-radius: 4px; - color: var(--interactive-active); - font-weight: 500; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); - cursor: grabbing; - opacity: 0.9; - width: 200px; -} - -/* ============================================ - FORCED UPDATE MODAL (Flatpak) - ============================================ */ -.forced-update-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.85); - display: flex; - align-items: center; - justify-content: center; - z-index: 10002; -} - -.forced-update-modal { - background-color: var(--bg-secondary); - border-radius: 8px; - padding: 32px; - width: 400px; - max-width: 90vw; - text-align: center; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4); -} - -.forced-update-modal h2 { - color: var(--header-primary); - font-size: 20px; - font-weight: 700; - margin: 0 0 8px; -} - -.forced-update-modal p { - color: var(--header-secondary); - font-size: 14px; - margin: 0 0 24px; - line-height: 1.4; -} - -.forced-update-btn { - background-color: var(--brand-experiment); - color: white; - border: none; - border-radius: 3px; - padding: 10px 24px; - font-size: 14px; - font-weight: 600; - cursor: pointer; - font-family: inherit; - transition: background-color 0.15s; -} - -.forced-update-btn:hover { - background-color: var(--brand-experiment-hover); -} - -.update-progress { - display: flex; - flex-direction: column; - align-items: center; - gap: 8px; - width: 100%; -} - -.update-progress-bar-bg { - width: 100%; - height: 8px; - background-color: var(--bg-tertiary); - border-radius: 4px; - overflow: hidden; -} - -.update-progress-bar-fill { - height: 100%; - background-color: var(--brand-experiment); - border-radius: 4px; - transition: width 0.2s ease; -} - -.update-progress-text { - color: var(--header-secondary); - font-size: 13px; -} - -.update-error-text { - color: var(--text-danger, #ed4245); - font-size: 13px; - margin: 8px 0 0; -} - -/* ============================================ - VOICE USER ITEM (sidebar) - ============================================ */ -.voice-user-item { - padding: 6px 8px; - border-radius: 8px; - cursor: pointer; - transition: background-color 0.1s ease; - margin-right: 4px; -} - -.voice-user-item:hover { - background-color: var(--background-modifier-hover); -} - -.drag-overlay-category { - padding: 8px 12px; - background-color: var(--bg-secondary); - border-radius: 4px; - color: var(--text-muted); - font-weight: 600; - font-size: 12px; - text-transform: uppercase; - letter-spacing: 0.02em; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); - cursor: grabbing; - opacity: 0.9; - width: 200px; -} - -/* ============================================ - VOICE USER DRAG & DROP - ============================================ */ -.drag-overlay-voice-user { - display: flex; - align-items: center; - padding: 6px 10px; - background: var(--background-modifier-selected); - border-radius: 8px; - color: var(--interactive-active); - font-weight: 500; - font-size: 14px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); - cursor: grabbing; -} - -.voice-drop-target { - background-color: rgba(88, 101, 242, 0.15) !important; - outline: 2px dashed var(--brand-experiment); - border-radius: 4px; -} - -/* ============================================ - MOBILE BACK BUTTON - ============================================ */ -.mobile-back-btn { - background: none; - border: none; - color: var(--header-secondary); - cursor: pointer; - padding: 4px; - margin-right: 4px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 4px; -} - -.mobile-back-btn:hover { - color: var(--header-primary); -} - -/* ============================================ - MOBILE RESPONSIVE (max-width: 768px) - ============================================ */ -@media (max-width: 768px) { - /* Hide hover tooltips on mobile (triggered by simulated mouseenter on tap) */ - .tooltip { - display: none !important; - } - - /* App container: full dynamic viewport, no titlebar gap */ - .app-container.is-mobile { - height: 100dvh; - padding-bottom: env(safe-area-inset-bottom, 0px); - box-sizing: border-box; - overflow: hidden; - display: block; - } - - /* Sliding tray for swipe navigation */ - .mobile-swipe-tray { - display: flex; - flex-direction: row; - width: 200vw; - height: 100%; - touch-action: pan-y; - will-change: transform; - } - - .mobile-swipe-panel { - width: 100vw; - min-width: 100vw; - height: 100%; - flex-shrink: 0; - overflow: hidden; - position: relative; - overscroll-behavior-x: none; - } - - /* Disable pointer events on children during active swipe */ - .mobile-swipe-tray.is-swiping .mobile-swipe-panel > * { - pointer-events: none; - } - - /* Sidebar fills its panel on mobile */ - .is-mobile .sidebar { - width: 100%; - min-width: 100%; - height: 100%; - } - - /* Hide members list on mobile (also enforced in JS) */ - .is-mobile .members-list { - display: none !important; - } - - /* Auth box responsive */ - .auth-box { - width: calc(100vw - 32px); - max-width: 480px; - } - - /* Pinned panel full-width on mobile */ - .is-mobile .pinned-panel { - width: 100vw; - right: 0; - border-radius: 0; - } - - /* Toast container centered on mobile */ - .is-mobile .toast-container { - right: auto; - left: 50%; - transform: translateX(-50%); - bottom: 16px; - } - - .is-mobile .toast { - min-width: 260px; - } - - /* Responsive modals */ - .create-channel-modal { - width: calc(100vw - 32px); - } - - .theme-selector-modal { - width: calc(100vw - 32px); - } - - .avatar-crop-dialog { - width: calc(100vw - 32px); - } - - .forced-update-modal { - width: calc(100vw - 32px); - } - - /* Constrain images/videos/embeds in messages to fit mobile width */ - .message-content img, - .message-content video { - max-width: 100%; - height: auto; - } - - .link-preview { - max-width: 100%; - } - - .youtube-video-wrapper { - max-width: 100%; - } - - /* Chat container takes full width */ - .is-mobile .chat-container { - width: 100%; - height: 100%; - } - - /* Channel topic - hide on very small screens (also hidden via JS) */ - .is-mobile .chat-header-topic { - display: none; - } - - /* FriendsView takes full width */ - .is-mobile .friends-view { - width: 100%; - height: 100%; - } - - /* Search panel full-width on mobile */ - .is-mobile .search-panel { - width: 100vw; - right: 0; - border-radius: 0; - } - -} - -/* ============================================ - MOBILE MESSAGE ACTION DRAWER - ============================================ */ - -@keyframes mobileDrawerOverlayIn { - from { opacity: 0; } - to { opacity: 1; } -} - -@keyframes mobileDrawerSlideUp { - from { transform: translateY(100%); } - to { transform: translateY(0); } -} - -@keyframes mobileDrawerSlideDown { - from { transform: translateY(0); } - to { transform: translateY(100%); } -} - -.mobile-drawer-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.6); - z-index: 10002; - animation: mobileDrawerOverlayIn 0.2s ease; -} - -.mobile-drawer { - position: fixed; - bottom: 0; - left: 0; - right: 0; - max-height: 70vh; - background: #1C1D22; - border-radius: 16px 16px 0 0; - z-index: 10003; - animation: mobileDrawerSlideUp 0.25s ease-out; - padding-bottom: env(safe-area-inset-bottom, 0px); - touch-action: none; - overflow-y: auto; -} - -.mobile-drawer.mobile-drawer-closing { - animation: mobileDrawerSlideDown 0.2s ease-in forwards; -} - -.mobile-drawer-handle { - display: flex; - justify-content: center; - padding: 10px 0 6px; -} - -.mobile-drawer-handle-bar { - width: 40px; - height: 4px; - border-radius: 2px; - background: hsla(240, 4%, 60%, 0.4); -} - -.mobile-drawer-reactions { - display: flex; - justify-content: center; - align-items: center; - gap: 8px; - padding: 8px 16px 12px; -} - -.mobile-drawer-reaction-btn { - width: 44px; - height: 44px; - border-radius: 50%; - background: #2E3138; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - border: none; - -webkit-tap-highlight-color: transparent; - transition: background-color 0.15s; -} - -.mobile-drawer-reaction-btn:active { - background: hsla(240, 4%, 40%, 0.4); -} - -.mobile-drawer-separator { - height: 1px; - background: hsla(240, 4%, 60%, 0.12); - margin: 0 16px; -} - -.mobile-drawer-card { - margin: 8px 16px; - border-radius: 12px; - background: #27272F; - overflow: hidden; -} - -.mobile-drawer-action { - display: flex; - align-items: center; - gap: 12px; - padding: 14px 16px; - color: var(--text-normal, #dbdee1); - font-size: 15px; - cursor: pointer; - border: none; - background: none; - width: 100%; - text-align: left; - -webkit-tap-highlight-color: transparent; -} - -.mobile-drawer-action:active { - background: hsla(240, 4%, 60%, 0.08); -} - -.mobile-drawer-action + .mobile-drawer-action { - border-top: 1px solid hsla(240, 4%, 60%, 0.12); -} - -.mobile-drawer-action-danger { - color: #ed4245; -} - -.mobile-drawer-action-disabled { - opacity: 0.4; - pointer-events: none; -} - -/* Mobile Channel Settings */ -.mobile-channel-settings-category-list { - margin-top: 8px; - border-radius: 8px; - background: var(--bg-primary, #313338); - overflow: hidden; -} - -.mobile-channel-settings-category-option { - padding: 12px 16px; - color: var(--text-normal, #dbdee1); - font-size: 15px; - cursor: pointer; - -webkit-tap-highlight-color: transparent; -} - -.mobile-channel-settings-category-option + .mobile-channel-settings-category-option { - border-top: 1px solid hsla(240, 4%, 60%, 0.12); -} - -.mobile-channel-settings-category-option:active { - background: hsla(240, 4%, 60%, 0.08); -} - -.mobile-channel-settings-category-option.selected { - color: var(--brand-experiment, #5865f2); - font-weight: 600; -} - -.mobile-channel-settings-delete-btn { - width: 100%; - padding: 14px; - border-radius: 8px; - border: none; - background: #ed4245; - color: #fff; - font-size: 15px; - font-weight: 600; - cursor: pointer; - -webkit-tap-highlight-color: transparent; -} - -.mobile-channel-settings-delete-btn:active { - opacity: 0.85; -} - -.mobile-channel-settings-cancel-btn { - flex: 1; - padding: 14px; - border-radius: 8px; - border: 1px solid hsla(240, 4%, 60%, 0.3); - background: transparent; - color: var(--text-normal, #dbdee1); - font-size: 15px; - font-weight: 600; - cursor: pointer; - -webkit-tap-highlight-color: transparent; -} - -.mobile-channel-settings-cancel-btn:active { - background: hsla(240, 4%, 60%, 0.08); -} - -.mobile-channel-settings-delete-confirm { - padding: 16px; - border-radius: 8px; - border: 1px solid rgba(237, 66, 69, 0.4); - background: rgba(237, 66, 69, 0.08); -} - -textarea.mobile-create-input { - resize: none; - font-family: inherit; - line-height: 1.4; -} - -/* Mobile Server Drawer header */ -.mobile-server-drawer-header { - display: flex; - align-items: center; - gap: 12px; - padding: 8px 16px 12px; -} - -.mobile-server-drawer-icon { - width: 48px; - height: 48px; - border-radius: 12px; - background: var(--bg-tertiary, #1e1f22); - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - overflow: hidden; - font-size: 18px; - font-weight: 700; - color: var(--text-normal, #dbdee1); -} - -.mobile-server-drawer-icon img { - width: 100%; - height: 100%; - object-fit: cover; -} - -.mobile-server-drawer-name { - font-size: 16px; - font-weight: 700; - color: var(--text-normal, #dbdee1); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 250px; -} - -.mobile-server-drawer-members { - font-size: 13px; - color: var(--text-muted, #949ba4); - margin-top: 2px; -} - -/* Remove border between server-list and channel-list on mobile */ -.is-mobile .server-list { - border-right: none; -} - -/* ============================================ - SEARCH DROPDOWN (appears below header input) - ============================================ */ -.search-dropdown { - position: fixed; - z-index: 10001; - background-color: #111214; - border-radius: 8px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); - overflow: hidden; - animation: searchDropdownIn 0.15s ease; -} - -@keyframes searchDropdownIn { - from { opacity: 0; transform: translateY(-4px); } - to { opacity: 1; transform: translateY(0); } -} - -.search-dropdown-scrollable { - max-height: 500px; - overflow-y: auto; - padding: 8px 0; -} - -.search-dropdown-scrollable::-webkit-scrollbar { - width: 6px; -} - -.search-dropdown-scrollable::-webkit-scrollbar-thumb { - background-color: var(--scrollbar-auto-thumb, var(--bg-tertiary)); - border-radius: 3px; -} - -.search-dropdown-section-header { - font-size: 12px; - font-weight: 700; - color: var(--header-secondary); - text-transform: uppercase; - padding: 8px 16px 4px; - letter-spacing: 0.02em; - display: flex; - align-items: center; - justify-content: space-between; -} - -.search-dropdown-item { - display: flex; - align-items: center; - gap: 12px; - padding: 8px 16px; - cursor: pointer; - color: var(--text-normal); - font-size: 14px; - transition: background-color 0.1s; -} - -.search-dropdown-item:hover { - background-color: rgba(255, 255, 255, 0.06); -} - -.search-dropdown-item-icon { - display: flex; - align-items: center; - justify-content: center; - width: 20px; - color: var(--text-muted); - flex-shrink: 0; -} - -.search-dropdown-item-label { - font-weight: 600; - color: var(--header-primary); -} - -.search-dropdown-item-desc { - color: var(--text-muted); - font-size: 13px; -} - -.search-dropdown-member { - display: flex; - align-items: center; - gap: 10px; - padding: 8px 16px; - cursor: pointer; - transition: background-color 0.1s; -} - -.search-dropdown-member:hover { - background-color: rgba(255, 255, 255, 0.06); -} - -.search-dropdown-avatar { - width: 24px; - height: 24px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-weight: 600; - font-size: 11px; - flex-shrink: 0; - object-fit: cover; -} - -img.search-dropdown-avatar { - object-fit: cover; -} - -.search-dropdown-member-name { - color: var(--text-normal); - font-size: 14px; - font-weight: 500; -} - -.search-dropdown-channel { - display: flex; - align-items: center; - gap: 10px; - padding: 8px 16px; - cursor: pointer; - color: var(--text-normal); - font-size: 14px; - transition: background-color 0.1s; -} - -.search-dropdown-channel:hover { - background-color: rgba(255, 255, 255, 0.06); -} - -.search-dropdown-channel-hash { - font-size: 18px; - font-weight: 700; - color: var(--text-muted); - width: 24px; - text-align: center; - flex-shrink: 0; -} - -.search-dropdown-history-header { - display: flex; - align-items: center; - justify-content: space-between; -} - -.search-dropdown-clear-all { - background: none; - border: none; - color: var(--text-link); - font-size: 12px; - cursor: pointer; - padding: 0; - font-weight: 500; -} - -.search-dropdown-clear-all:hover { - text-decoration: underline; -} - -.search-dropdown-history-item { - display: flex; - align-items: center; - gap: 10px; - padding: 8px 16px; - cursor: pointer; - transition: background-color 0.1s; -} - -.search-dropdown-history-item:hover { - background-color: rgba(255, 255, 255, 0.06); -} - -.search-dropdown-history-icon { - color: var(--text-muted); - flex-shrink: 0; -} - -.search-dropdown-history-text { - flex: 1; - color: var(--text-normal); - font-size: 14px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.search-dropdown-history-delete { - background: none; - border: none; - color: var(--text-muted); - font-size: 16px; - cursor: pointer; - padding: 0 4px; - opacity: 0; - transition: opacity 0.1s; - line-height: 1; -} - -.search-dropdown-history-item:hover .search-dropdown-history-delete { - opacity: 1; -} - -.search-dropdown-history-delete:hover { - color: var(--header-primary); -} - -.search-dropdown-empty { - padding: 12px 16px; - color: var(--text-muted); - font-size: 13px; - text-align: center; -} - -/* ============================================ - SEARCH PANEL (results) - ============================================ */ -.search-panel { - position: absolute; - top: 0; - right: 0; - width: 420px; - height: 100%; - background-color: var(--bg-secondary); - border-left: 1px solid var(--border-subtle); - display: flex; - flex-direction: column; - z-index: 100; - box-shadow: -2px 0 8px rgba(0, 0, 0, 0.3); -} - -.search-panel-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 12px 16px; - border-bottom: 1px solid var(--border-subtle); -} - -.search-panel-header-left { - display: flex; - align-items: center; - gap: 8px; -} - -.search-panel-header-right { - display: flex; - align-items: center; - gap: 8px; -} - -.search-result-count { - color: var(--header-secondary); - font-size: 13px; - font-weight: 600; -} - -.search-panel-sort-wrapper { - position: relative; -} - -.search-panel-sort-btn { - display: flex; - align-items: center; - background: none; - border: none; - color: var(--text-link); - font-size: 13px; - font-weight: 500; - cursor: pointer; - padding: 4px 8px; - border-radius: 4px; - font-family: inherit; -} - -.search-panel-sort-btn:hover { - background-color: rgba(255, 255, 255, 0.06); -} - -.search-panel-sort-menu { - position: absolute; - top: 100%; - right: 0; - margin-top: 4px; - background-color: #111214; - border-radius: 4px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); - z-index: 10; - overflow: hidden; - min-width: 120px; -} - -.search-panel-sort-option { - padding: 8px 12px; - color: var(--text-normal); - font-size: 14px; - cursor: pointer; - transition: background-color 0.1s; -} - -.search-panel-sort-option:hover { - background-color: rgba(255, 255, 255, 0.06); -} - -.search-panel-sort-option.active { - color: var(--text-link); -} - -.search-panel-close { - background: none; - border: none; - color: var(--header-secondary); - font-size: 20px; - cursor: pointer; - line-height: 1; - padding: 4px; -} - -.search-panel-close:hover { - color: var(--header-primary); -} - -.search-filter-chips { - display: flex; - flex-wrap: wrap; - gap: 4px; - padding: 8px 16px; -} - -.search-filter-chip { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 2px 8px; - border-radius: 12px; - background-color: var(--brand-experiment); - color: white; - font-size: 12px; - font-weight: 500; -} - -.search-panel-results { - flex: 1; - overflow-y: auto; - padding: 4px 8px; -} - -.search-panel-results::-webkit-scrollbar { - width: 6px; -} - -.search-panel-results::-webkit-scrollbar-thumb { - background-color: var(--scrollbar-auto-thumb, var(--bg-tertiary)); - border-radius: 3px; -} - -.search-panel-empty { - text-align: center; - color: var(--text-muted); - padding: 32px 16px; - display: flex; - flex-direction: column; - align-items: center; -} - -.search-channel-header { - font-size: 12px; - font-weight: 600; - color: var(--header-secondary); - text-transform: uppercase; - padding: 8px 8px 4px; - letter-spacing: 0.02em; -} - -.search-result { - background-color: var(--bg-primary); - border-radius: 4px; - padding: 8px 12px; - margin-bottom: 4px; - cursor: pointer; - transition: background-color 0.1s; - display: flex; - gap: 12px; -} - -.search-result:hover { - background-color: var(--bg-mod-faint); -} - -.search-result-avatar { - width: 40px; - height: 40px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-weight: 600; - font-size: 15px; - flex-shrink: 0; -} - -.search-result-body { - flex: 1; - min-width: 0; -} - -.search-result-header { - display: flex; - align-items: baseline; - gap: 8px; - margin-bottom: 2px; -} - -.search-result-username { - color: var(--header-primary); - font-size: 1rem; - font-weight: 600; -} - -.search-result-time { - color: var(--text-muted); - font-size: 0.75rem; - margin-left: 0; -} - -.search-result-content { - color: var(--text-normal); - font-size: 0.9375rem; - line-height: 1.375; - word-break: break-word; - overflow: hidden; - display: -webkit-box; - -webkit-line-clamp: 3; - -webkit-box-orient: vertical; -} - -.search-result-content mark { - background-color: rgba(250, 166, 26, 0.3); - color: var(--text-normal); - border-radius: 2px; - padding: 0 1px; -} - -.search-result-link { - color: #00b0f4; - text-decoration: none; - cursor: pointer; -} -.search-result-link:hover { - text-decoration: underline; -} - -.search-result-badge { - display: inline-block; - font-size: 10px; - font-weight: 600; - color: var(--text-muted); - background-color: var(--bg-tertiary); - padding: 1px 6px; - border-radius: 3px; - margin-top: 4px; - margin-right: 4px; - text-transform: uppercase; -} - -/* DM Call Styles */ -.dm-call-stage { - height: 45%; - min-height: 200px; - max-height: 80%; - display: flex; - flex-direction: column; - border-bottom: 2px solid var(--bg-tertiary); - overflow: hidden; - position: relative; -} - -.dm-call-expand-btn { - position: absolute; - left: 8px; - top: 8px; - z-index: 5; - background: var(--bg-tertiary); - border: none; - border-radius: 4px; - width: 28px; - height: 28px; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - color: var(--interactive-normal); - opacity: 0.7; - transition: opacity 0.15s; -} - -.dm-call-expand-btn:hover { - opacity: 1; - color: var(--interactive-hover); -} - -.dm-call-resize-handle { - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 6px; - cursor: ns-resize; - z-index: 5; -} - -.dm-call-resize-handle:hover { - background: var(--interactive-normal); - opacity: 0.3; -} - -.dm-call-idle-stage { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - background-color: var(--bg-tertiary); - padding: 32px 16px; - gap: 8px; - min-height: 200px; - height: 45%; - max-height: 80%; - border-bottom: 2px solid var(--bg-tertiary); -} - -.dm-call-idle-username { - color: var(--text-normal); - font-size: 18px; - font-weight: 600; - margin-top: 8px; -} - -.dm-call-idle-status { - color: var(--text-muted); - font-size: 14px; -} - -.dm-call-join-btn { - background-color: #3ba55c; - color: white; - border: none; - border-radius: 4px; - padding: 6px 16px; - font-size: 13px; - font-weight: 600; - cursor: pointer; -} - -.dm-call-join-btn:hover { - background-color: #2d7d46; -} - -/* Incoming call UI */ -.incoming-call-ui { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - background-color: var(--bg-tertiary); - padding: 32px 16px; - gap: 12px; - flex: 1; - min-height: 260px; -} - -.incoming-call-avatar-ring { - position: relative; - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 8px; -} - -.incoming-call-avatar-ring::before { - content: ''; - position: absolute; - width: 96px; - height: 96px; - border-radius: 50%; - border: 3px solid #3ba55c; - animation: call-ring-pulse 1.5s ease-out infinite; -} - -@keyframes call-ring-pulse { - 0% { - transform: scale(1); - opacity: 0.8; - } - 100% { - transform: scale(1.4); - opacity: 0; - } -} - -.incoming-call-username { - color: var(--text-normal); - font-size: 20px; - font-weight: 600; -} - -.incoming-call-subtitle { - color: var(--text-muted); - font-size: 14px; -} - -.incoming-call-buttons { - display: flex; - gap: 24px; - margin-top: 16px; -} - -.incoming-call-btn { - width: 56px; - height: 56px; - border-radius: 50%; - border: none; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - color: white; - transition: filter 0.15s; -} - -.incoming-call-btn:hover { - filter: brightness(1.15); -} - -.incoming-call-btn.join { - background-color: #3ba55c; -} - -.incoming-call-btn.reject { - background-color: #ed4245; -} - -.voice-connecting-icon { - animation: voiceConnectingPulse 1.5s ease-in-out infinite; -} - -@keyframes voiceConnectingPulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.4; } -} - -/* Slash Command Menu */ -.slash-command-menu { - position: absolute; - bottom: calc(100% + 4px); - left: 0; - right: 0; - background-color: var(--background-surface-high, var(--embed-background)); - border-radius: 8px; - box-shadow: 0 0 0 1px rgba(0,0,0,.1), 0 8px 16px rgba(0,0,0,.24); - overflow: hidden; - z-index: 100; -} - -.slash-command-scroller { - max-height: 490px; - overflow-y: auto; - padding-bottom: 8px; -} - -.slash-command-scroller::-webkit-scrollbar { - width: 6px; -} - -.slash-command-scroller::-webkit-scrollbar-thumb { - background-color: var(--scrollbar-auto-thumb, var(--bg-tertiary)); - border-radius: 3px; -} - -.slash-command-section-header { - padding: 8px 12px 4px; - font-size: 12px; - font-weight: 600; - text-transform: uppercase; - color: var(--header-secondary); -} - -.slash-command-row { - display: flex; - align-items: center; - padding: 8px 12px; - gap: 10px; - cursor: pointer; -} - -.slash-command-row:hover, -.slash-command-row.selected { - background-color: var(--interactive-background-hover, rgba(255,255,255,0.06)); -} - -.slash-command-name { - font-size: 14px; - font-weight: 600; - color: var(--text-normal); - flex-shrink: 0; -} - -.slash-command-description { - font-size: 13px; - color: var(--text-muted); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -/* Ephemeral Messages */ -.ephemeral-message { - background: hsla(235, 86%, 65%, 0.05); -} - -.ephemeral-reply-context { - cursor: default; -} - -.ephemeral-reply-avatar { - width: 16px; - height: 16px; - border-radius: 50%; - background-color: #5865f2; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - margin-left: 20px; - color: white; -} - -.ephemeral-avatar { - width: 40px; - height: 40px; - border-radius: 50%; - background-color: #5865f2; - display: flex; - align-items: center; - justify-content: center; - color: white; -} - -.ephemeral-bot-badge { - font-size: 10px; - font-weight: 600; - background-color: #5865f2; - color: white; - padding: 1px 4px; - border-radius: 3px; - text-transform: uppercase; - line-height: 14px; - margin-left: 4px; - margin-right: 4px; -} - -.ephemeral-message-footer { - display: flex; - align-items: center; - gap: 4px; - font-size: 12px; - color: var(--text-muted); - margin-top: 4px; -} - -.ephemeral-message-footer-text { - opacity: 0.6; -} - -.ephemeral-message-footer-sep { - opacity: 0.4; - margin: 0 2px; -} - -.ephemeral-message-dismiss { - color: var(--text-link); - cursor: pointer; -} - -.ephemeral-message-dismiss:hover { - text-decoration: underline; -} - -/* ── Mobile Channel Header ── */ - -.mobile-channel-header-tap { - display: flex; - flex-direction: column; - align-items: flex-start; - background: none; - border: none; - padding: 2px 4px; - cursor: pointer; - -webkit-tap-highlight-color: transparent; - min-height: 40px; - justify-content: center; -} - -.mobile-channel-header-top { - display: flex; - align-items: center; - gap: 2px; -} - -.mobile-channel-header-top .chat-header-name { - font-size: 16px; - font-weight: 700; -} - -.mobile-channel-chevron { - color: var(--text-muted); - margin-left: 2px; - flex-shrink: 0; -} - -.mobile-channel-header-bottom { - display: flex; - align-items: center; - gap: 4px; - margin-top: 1px; -} - -.mobile-online-dot { - width: 8px; - height: 8px; - border-radius: 50%; - background-color: #3ba55c; - flex-shrink: 0; -} - -.mobile-online-count { - font-size: 11px; - color: var(--text-muted); - font-weight: 500; -} - -/* ── Mobile Members Screen ── */ - -.mobile-members-screen { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: var(--bg-primary, #313338); - z-index: 9999; - display: flex; - flex-direction: column; - transform: translateX(100%); - transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); -} - -.mobile-members-screen.visible { - transform: translateX(0); -} - -.mobile-members-header { - display: flex; - align-items: center; - gap: 12px; - padding: 12px 16px; - border-bottom: 1px solid var(--bg-tertiary, #1e1f22); - min-height: 56px; - flex-shrink: 0; -} - -.mobile-members-back { - background: none; - border: none; - color: var(--text-normal, #dbdee1); - padding: 4px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - -webkit-tap-highlight-color: transparent; -} - -.mobile-members-header-info { - display: flex; - flex-direction: column; - min-width: 0; -} - -.mobile-members-header-name { - font-size: 16px; - font-weight: 700; - color: var(--text-normal, #dbdee1); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.mobile-members-header-subtitle { - font-size: 12px; - color: var(--text-muted, #949ba4); - margin-top: 1px; -} - -.mobile-members-tabs { - display: flex; - gap: 0; - padding: 0 8px; - border-bottom: 1px solid var(--bg-tertiary, #1e1f22); - overflow-x: auto; - flex-shrink: 0; - -webkit-overflow-scrolling: touch; - scrollbar-width: none; -} - -.mobile-members-tabs::-webkit-scrollbar { - display: none; -} - -.mobile-members-tab { - background: none; - border: none; - color: var(--text-muted, #949ba4); - font-size: 13px; - font-weight: 600; - padding: 12px 12px; - cursor: pointer; - white-space: nowrap; - border-bottom: 2px solid transparent; - transition: color 0.15s, border-color 0.15s; - -webkit-tap-highlight-color: transparent; -} - -.mobile-members-tab.active { - color: var(--text-normal, #dbdee1); - border-bottom-color: var(--text-normal, #dbdee1); -} - -.mobile-members-content { - flex: 1; - overflow-y: auto; - padding: 8px 0; - -webkit-overflow-scrolling: touch; -} - -.mobile-member-item { - display: flex; - align-items: center; - padding: 8px 16px; - gap: 12px; - min-height: 48px; - cursor: pointer; -} - -.mobile-member-item:active { - background: var(--bg-secondary, #2b2d31); -} - -.mobile-members-placeholder { - display: flex; - align-items: center; - justify-content: center; - padding: 48px 16px; -} - -/* ── Mobile Search Bar + Invite Row (Sidebar) ── */ - -.mobile-search-invite-row { - display: flex; - align-items: center; - gap: 8px; - padding: 0 8px 8px; - border-bottom: 1px solid var(--app-frame-border); -} - -.mobile-search-bar-btn { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - background: var(--bg-secondary, #2b2d31); - border: none; - border-radius: 20px; - padding: 8px 12px; - color: var(--text-muted, #949ba4); - font-size: 14px; - cursor: pointer; - -webkit-tap-highlight-color: transparent; -} - -.mobile-search-bar-btn:active { - background: var(--bg-primary, #313338); -} - -.mobile-search-bar-btn svg { - flex-shrink: 0; - opacity: 0.7; -} - -.mobile-search-invite-btn { - flex-shrink: 0; - background: none; - border: none; - padding: 8px; - border-radius: 50%; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - -webkit-tap-highlight-color: transparent; -} - -.mobile-search-invite-btn:active { - background: var(--bg-secondary, #2b2d31); -} - -.mobile-search-invite-btn img { - width: 22px; - height: 22px; - filter: brightness(0) invert(0.7); -} - -/* ── Mobile Search Screen ── */ - -.mobile-search-screen { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: var(--bg-primary, #313338); - z-index: 9999; - display: flex; - flex-direction: column; - transform: translateX(100%); - transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); -} - -.mobile-search-screen.visible { - transform: translateX(0); -} - -.mobile-search-header { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - border-bottom: 1px solid var(--bg-tertiary, #1e1f22); - min-height: 56px; - flex-shrink: 0; -} - -.mobile-search-back { - background: none; - border: none; - color: var(--text-normal, #dbdee1); - padding: 4px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - -webkit-tap-highlight-color: transparent; -} - -.mobile-search-input-wrapper { - flex: 1; - display: flex; - align-items: center; - gap: 8px; - background: var(--bg-secondary, #2b2d31); - border-radius: 20px; - padding: 6px 12px; - min-width: 0; -} - -.mobile-search-input-icon { - flex-shrink: 0; - color: var(--text-muted, #949ba4); -} - -.mobile-search-input { - flex: 1; - background: none; - border: none; - outline: none; - color: var(--text-normal, #dbdee1); - font-size: 14px; - min-width: 0; -} - -.mobile-search-input::placeholder { - color: var(--text-muted, #949ba4); -} - -.mobile-search-clear { - background: none; - border: none; - color: var(--text-muted, #949ba4); - padding: 2px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - -webkit-tap-highlight-color: transparent; -} - -.mobile-search-tabs { - display: flex; - gap: 0; - padding: 0 8px; - border-bottom: 1px solid var(--bg-tertiary, #1e1f22); - overflow-x: auto; - flex-shrink: 0; - -webkit-overflow-scrolling: touch; - scrollbar-width: none; -} - -.mobile-search-tabs::-webkit-scrollbar { - display: none; -} - -.mobile-search-tab { - background: none; - border: none; - color: var(--text-muted, #949ba4); - font-size: 13px; - font-weight: 600; - padding: 12px 12px; - cursor: pointer; - white-space: nowrap; - border-bottom: 2px solid transparent; - transition: color 0.15s, border-color 0.15s; - -webkit-tap-highlight-color: transparent; -} - -.mobile-search-tab.active { - color: #5865f2; - border-bottom-color: #5865f2; -} - -.mobile-search-content { - flex: 1; - overflow-y: auto; - padding: 8px 0; - -webkit-overflow-scrolling: touch; -} - -.mobile-search-section { - display: flex; - flex-direction: column; -} - -.mobile-search-section-title { - padding: 8px 16px 4px; - font-size: 12px; - font-weight: 700; - text-transform: uppercase; - color: var(--text-muted, #949ba4); - letter-spacing: 0.02em; -} - -.mobile-search-channel-item { - display: flex; - align-items: center; - gap: 12px; - padding: 10px 16px; - background: none; - border: none; - width: 100%; - text-align: left; - cursor: pointer; - -webkit-tap-highlight-color: transparent; -} - -.mobile-search-channel-item:active { - background: var(--bg-secondary, #2b2d31); -} - -.mobile-search-channel-hash { - color: var(--text-muted, #949ba4); - font-size: 18px; - font-weight: 600; - width: 24px; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; -} - -.mobile-search-channel-info { - display: flex; - flex-direction: column; - min-width: 0; -} - -.mobile-search-channel-name { - color: var(--text-normal, #dbdee1); - font-size: 15px; - font-weight: 500; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.mobile-search-channel-activity { - color: var(--text-muted, #949ba4); - font-size: 12px; - margin-top: 1px; -} - -.mobile-search-member-item { - display: flex; - align-items: center; - gap: 12px; - padding: 8px 16px; - min-height: 48px; -} - -.mobile-search-member-name { - color: var(--text-normal, #dbdee1); - font-size: 15px; - font-weight: 500; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.mobile-search-empty { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 48px 16px; - color: var(--text-muted, #949ba4); - font-size: 14px; -} - -/* Mobile search results */ -.mobile-search-results { - padding: 0; -} - -.mobile-search-channel-group { - margin-bottom: 8px; -} - -.mobile-search-channel-group-header { - padding: 8px 16px 4px; - font-size: 12px; - font-weight: 700; - text-transform: uppercase; - color: var(--channels-default, #949ba4); - letter-spacing: 0.02em; - position: sticky; - top: 0; - background: var(--bg-primary, #313338); - z-index: 1; -} - -.mobile-search-result-item { - display: flex; - padding: 10px 16px; - gap: 12px; - cursor: pointer; - transition: background-color 0.1s; - align-items: flex-start; -} - -.mobile-search-result-item:active { - background-color: var(--background-modifier-hover, rgba(79, 84, 92, 0.16)); -} - -.mobile-search-result-avatar { - width: 36px; - height: 36px; - min-width: 36px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - color: #fff; - font-weight: 600; - font-size: 14px; - user-select: none; -} - -.mobile-search-result-body { - flex: 1; - min-width: 0; - overflow: hidden; -} - -.mobile-search-result-header { - display: flex; - align-items: baseline; - gap: 6px; - margin-bottom: 2px; -} - -.mobile-search-result-username { - font-size: 14px; - font-weight: 600; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.mobile-search-result-time { - font-size: 11px; - color: var(--text-muted, #949ba4); - white-space: nowrap; - flex-shrink: 0; -} - -.mobile-search-result-content { - font-size: 13px; - color: var(--text-normal, #dbdee1); - line-height: 1.375; - display: -webkit-box; - -webkit-line-clamp: 3; - -webkit-box-orient: vertical; - overflow: hidden; - word-break: break-word; -} - -.mobile-search-result-content mark { - background-color: rgba(250, 166, 26, 0.3); - color: var(--text-normal, #dbdee1); - border-radius: 2px; - padding: 0 1px; -} - -.mobile-search-result-content a.search-result-link { - color: var(--text-link, #00a8fc); - text-decoration: none; -} - -.mobile-search-result-content img { - max-width: 48px; - max-height: 48px; -} - -/* ─── Mobile Server Settings Modal (msm-*) ─── */ - -.msm-root { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: #0C0C0E; - z-index: 1000; - color: var(--text-normal); - display: flex; - flex-direction: column; -} - -.msm-screen { - display: flex; - flex-direction: column; - height: 100%; - min-height: 0; -} - -.msm-header { - display: flex; - align-items: center; - min-height: 56px; - padding: 8px 12px; - flex-shrink: 0; -} - -.msm-back-btn, -.msm-header-action { - width: 36px; - height: 36px; - border: none; - background: none; - color: var(--header-secondary); - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - border-radius: 50%; - flex-shrink: 0; - -webkit-tap-highlight-color: transparent; -} - -.msm-back-btn:active, -.msm-header-action:active { - background: rgba(255, 255, 255, 0.06); -} - -.msm-header-title { - flex: 1; - text-align: center; - font-size: 16px; - font-weight: 700; - color: var(--header-primary); -} - -.msm-content { - flex: 1; - overflow-y: auto; - padding: 0 16px 32px; - -webkit-overflow-scrolling: touch; -} - -/* Icon section (menu screen) */ -.msm-icon-section { - display: flex; - flex-direction: column; - align-items: center; - padding: 8px 0 20px; -} - -.msm-icon-wrapper { - width: 80px; - height: 80px; - position: relative; - cursor: pointer; - -webkit-tap-highlight-color: transparent; -} - -.msm-icon-img { - width: 80px; - height: 80px; - object-fit: cover; - border-radius: 24px; -} - -.msm-icon-placeholder { - width: 80px; - height: 80px; - border-radius: 24px; - background-color: #5865F2; - display: flex; - align-items: center; - justify-content: center; - color: #fff; - font-weight: 700; - font-size: 24px; - text-transform: uppercase; -} - -.msm-icon-badge { - position: absolute; - bottom: -2px; - right: -2px; - width: 28px; - height: 28px; - border-radius: 50%; - background-color: #5865F2; - display: flex; - align-items: center; - justify-content: center; - border: 3px solid #0C0C0E; -} - -.msm-icon-name { - margin-top: 10px; - font-size: 18px; - font-weight: 700; - color: var(--header-primary); - text-align: center; -} - -/* Section label */ -.msm-section-label { - font-size: 12px; - font-weight: 700; - color: var(--header-secondary); - text-transform: uppercase; - padding: 16px 0 8px; - letter-spacing: 0.02em; -} - -/* Cards */ -.msm-card { - background-color: #1A1A20; - border-radius: 12px; - overflow: hidden; -} - -.msm-card-item { - display: flex; - align-items: center; - padding: 14px; - gap: 12px; - cursor: pointer; - -webkit-tap-highlight-color: transparent; -} - -.msm-card-item:active { - background: rgba(255, 255, 255, 0.04); -} - -.msm-card-item + .msm-card-item { - border-top: 1px solid rgba(255, 255, 255, 0.06); -} - -.msm-card-item-danger { - color: #ed4245 !important; - justify-content: center; - font-weight: 600; - font-size: 15px; -} - -.msm-card-item-chevron { - color: var(--header-secondary); - flex-shrink: 0; - opacity: 0.5; -} - -.msm-card-item-icon { - color: var(--header-secondary); - flex-shrink: 0; - display: flex; - align-items: center; -} - -/* Role dot */ -.msm-role-dot { - width: 14px; - height: 14px; - border-radius: 50%; - flex-shrink: 0; -} - -.msm-role-count { - color: var(--header-secondary); - font-size: 13px; - margin-right: 4px; -} - -/* Toggle switch */ -.msm-toggle { - width: 44px; - height: 24px; - border-radius: 12px; - background-color: #72767d; - position: relative; - flex-shrink: 0; - transition: background-color 0.2s; - cursor: pointer; -} - -.msm-toggle-on { - background-color: #3ba55c; -} - -.msm-toggle-knob { - width: 18px; - height: 18px; - border-radius: 50%; - background-color: #fff; - position: absolute; - top: 3px; - left: 3px; - transition: left 0.2s; -} - -.msm-toggle-on .msm-toggle-knob { - left: 23px; -} - -/* Input / select */ -.msm-input { - width: 100%; - padding: 12px; - background: #1A1A20; - border: none; - border-radius: 8px; - color: var(--header-primary); - font-size: 15px; - box-sizing: border-box; - outline: none; - font-family: inherit; -} - -.msm-input:focus { - box-shadow: 0 0 0 2px #5865F2; -} - -.msm-select { - width: 100%; - padding: 12px; - background: #1A1A20; - border: none; - border-radius: 8px; - color: var(--header-primary); - font-size: 15px; - box-sizing: border-box; - cursor: pointer; - appearance: auto; - font-family: inherit; -} - -/* Color picker */ -.msm-color-input { - width: 36px; - height: 36px; - border: none; - border-radius: 50%; - padding: 0; - cursor: pointer; - background: none; - overflow: hidden; -} - -.msm-color-input::-webkit-color-swatch-wrapper { - padding: 0; -} - -.msm-color-input::-webkit-color-swatch { - border: 2px solid rgba(255, 255, 255, 0.1); - border-radius: 50%; -} - -/* Buttons */ -.msm-btn-primary { - background-color: #5865F2; - color: #fff; - border: none; - border-radius: 8px; - padding: 10px 20px; - font-weight: 600; - font-size: 14px; - cursor: pointer; - -webkit-tap-highlight-color: transparent; - font-family: inherit; -} - -.msm-btn-primary:active { - opacity: 0.85; -} - -.msm-btn-primary:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.msm-btn-full { - width: 100%; - display: block; -} - -.msm-btn-danger-outline { - background: transparent; - color: #ed4245; - border: 1px solid #ed4245; - border-radius: 8px; - padding: 8px 16px; - font-weight: 600; - font-size: 14px; - cursor: pointer; - -webkit-tap-highlight-color: transparent; - font-family: inherit; -} - -.msm-btn-danger-outline:active { - background: rgba(237, 66, 69, 0.1); -} - -/* Description text */ -.msm-description { - color: var(--header-secondary); - font-size: 13px; - line-height: 1.4; - margin: 0 0 16px; -} - -/* Empty state */ -.msm-empty { - color: var(--header-secondary); - text-align: center; - padding: 40px 0; - font-size: 14px; -} - -/* Emoji row */ -.msm-emoji-row { - display: flex; - align-items: center; - padding: 12px 14px; - gap: 12px; -} - -.msm-emoji-row + .msm-emoji-row { - border-top: 1px solid rgba(255, 255, 255, 0.06); -} - -.msm-emoji-img { - width: 32px; - height: 32px; - object-fit: contain; - flex-shrink: 0; -} - -.msm-emoji-info { - flex: 1; - min-width: 0; - display: flex; - flex-direction: column; - gap: 2px; -} - -.msm-emoji-name { - color: var(--header-primary); - font-size: 14px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.msm-emoji-uploader { - color: var(--header-secondary); - font-size: 12px; -} - -.msm-emoji-delete { - width: 32px; - height: 32px; - border: none; - background: none; - color: var(--header-secondary); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - border-radius: 6px; - flex-shrink: 0; - -webkit-tap-highlight-color: transparent; -} - -.msm-emoji-delete:active { - color: #ed4245; - background: rgba(237, 66, 69, 0.1); -} - -/* Member row */ -.msm-member-row { - display: flex; - align-items: center; - padding: 10px 14px; - gap: 10px; -} - -.msm-member-row + .msm-member-row { - border-top: 1px solid rgba(255, 255, 255, 0.06); -} - -.msm-member-avatar { - width: 36px; - height: 36px; - border-radius: 50%; - background-color: #5865F2; - display: flex; - align-items: center; - justify-content: center; - color: #fff; - font-weight: 700; - font-size: 14px; - flex-shrink: 0; - overflow: hidden; -} - -.msm-member-info { - flex: 1; - min-width: 0; -} - -.msm-member-name { - color: var(--header-primary); - font-weight: 600; - font-size: 14px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.msm-member-roles { - display: flex; - flex-wrap: wrap; - gap: 4px; - margin-top: 3px; -} - -.msm-member-role-pill { - font-size: 10px; - padding: 1px 6px; - border-radius: 4px; - border: 1px solid; - white-space: nowrap; -} - -.msm-member-role-toggle { - width: 18px; - height: 18px; - border-radius: 50%; - border: 2px solid; - cursor: pointer; - flex-shrink: 0; - -webkit-tap-highlight-color: transparent; - padding: 0; -} - -/* User settings tabs inside mobile msm-content */ -.msm-content .voice-slider { - min-width: 0; -} - -/* ============================================ - MOBILE CREATE CHANNEL / CATEGORY SCREENS - ============================================ */ - -.mobile-create-screen { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: var(--bg-tertiary, #1e1f22); - z-index: 9999; - display: flex; - flex-direction: column; - transform: translateX(100%); - transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); -} - -.mobile-create-screen.visible { - transform: translateX(0); -} - -.mobile-create-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 12px 16px; - min-height: 56px; - background-color: var(--bg-tertiary, #1e1f22); -} - -.mobile-create-close-btn { - background: transparent; - border: none; - color: var(--interactive-normal, #b5bac1); - cursor: pointer; - padding: 4px; - display: flex; - align-items: center; - justify-content: center; - -webkit-tap-highlight-color: transparent; -} - -.mobile-create-title { - font-size: 17px; - font-weight: 700; - color: var(--header-primary, #f2f3f5); -} - -.mobile-create-submit-btn { - background: transparent; - border: none; - color: var(--brand-experiment, #5865f2); - font-size: 16px; - font-weight: 600; - cursor: pointer; - padding: 4px 8px; - -webkit-tap-highlight-color: transparent; - transition: opacity 0.15s; -} - -.mobile-create-submit-btn.disabled { - opacity: 0.4; - pointer-events: none; -} - -.mobile-create-body { - flex: 1; - overflow-y: auto; - padding: 16px; - -webkit-overflow-scrolling: touch; -} - -.mobile-create-section { - margin-bottom: 24px; -} - -.mobile-create-section-label { - display: block; - color: var(--header-secondary, #b5bac1); - font-size: 12px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.02em; - margin-bottom: 8px; -} - -.mobile-create-input-wrapper { - display: flex; - align-items: center; - background-color: var(--bg-secondary, #2b2d31); - border-radius: 12px; - padding: 0 12px; - min-height: 48px; -} - -.mobile-create-input-prefix { - color: var(--interactive-normal, #b5bac1); - font-size: 18px; - margin-right: 6px; - display: flex; - align-items: center; -} - -.mobile-create-input { - flex: 1; - background: transparent; - border: none; - outline: none; - color: var(--text-normal, #dbdee1); - font-size: 16px; - font-family: inherit; - padding: 12px 0; -} - -.mobile-create-input::placeholder { - color: var(--text-muted, #6d6f78); -} - -.mobile-create-type-list { - border-radius: 12px; - overflow: hidden; - background-color: var(--bg-secondary, #2b2d31); -} - -.mobile-create-type-option { - display: flex; - align-items: center; - gap: 12px; - padding: 14px 14px; - cursor: pointer; - -webkit-tap-highlight-color: transparent; -} - -.mobile-create-type-option + .mobile-create-type-option { - border-top: 1px solid var(--bg-tertiary, #1e1f22); -} - -.mobile-create-type-icon { - font-size: 24px; - color: var(--interactive-normal, #b5bac1); - width: 28px; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; -} - -.mobile-create-type-info { - flex: 1; - min-width: 0; -} - -.mobile-create-type-name { - color: var(--header-primary, #f2f3f5); - font-weight: 500; - font-size: 16px; -} - -.mobile-create-type-desc { - color: var(--header-secondary, #b5bac1); - font-size: 12px; - margin-top: 2px; - line-height: 1.3; -} - -.mobile-create-radio { - width: 22px; - height: 22px; - border-radius: 50%; - border: 2px solid var(--interactive-normal, #b5bac1); - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - transition: border-color 0.15s; -} - -.mobile-create-radio.selected { - border-color: var(--brand-experiment, #5865f2); -} - -.mobile-create-radio-dot { - width: 12px; - height: 12px; - border-radius: 50%; - background-color: var(--brand-experiment, #5865f2); -} - -.mobile-create-toggle-row { - display: flex; - align-items: center; - justify-content: space-between; - padding: 4px 0; -} - -.mobile-create-toggle-left { - display: flex; - align-items: center; - gap: 8px; -} - -.mobile-create-toggle-label { - color: var(--header-primary, #f2f3f5); - font-size: 14px; - font-weight: 500; -} - -.mobile-create-private-desc { - color: var(--header-secondary, #b5bac1); - font-size: 13px; - line-height: 1.4; - margin: 0 0 12px; -} \ No newline at end of file diff --git a/packages/shared/src/pages/Chat.jsx b/packages/shared/src/pages/Chat.jsx deleted file mode 100644 index 0aeb236..0000000 --- a/packages/shared/src/pages/Chat.jsx +++ /dev/null @@ -1,814 +0,0 @@ -import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react'; -import { useQuery, useConvex } from 'convex/react'; -import { api } from '../../../../convex/_generated/api'; -import Sidebar from '../components/Sidebar'; -import ChatArea from '../components/ChatArea'; -import VoiceStage from '../components/VoiceStage'; -import FloatingStreamPiP from '../components/FloatingStreamPiP'; -import { useVoice } from '../contexts/VoiceContext'; -import FriendsView from '../components/FriendsView'; -import MembersList from '../components/MembersList'; -import MobileMembersScreen from '../components/MobileMembersScreen'; -import MobileSearchScreen from '../components/MobileSearchScreen'; -import ChatHeader from '../components/ChatHeader'; -import SearchPanel from '../components/SearchPanel'; -import SearchDropdown from '../components/SearchDropdown'; -import { useToasts } from '../components/Toast'; -import { PresenceProvider } from '../contexts/PresenceContext'; -import { getUserPref, setUserPref } from '../utils/userPreferences'; -import { usePlatform } from '../platform'; -import { useIsMobile } from '../hooks/useIsMobile'; -import { useSwipeNavigation } from '../hooks/useSwipeNavigation'; -import IncomingCallUI from '../components/IncomingCallUI'; -import Avatar from '../components/Avatar'; -import callRingSound from '../assets/sounds/default_call_sound.mp3'; -import { useSystemBars } from '../hooks/useSystemBars'; - -const MAX_SEARCH_HISTORY = 10; - -const Chat = () => { - const { crypto, settings } = usePlatform(); - const isMobile = useIsMobile(); - const [userId, setUserId] = useState(() => localStorage.getItem('userId')); - const [username, setUsername] = useState(() => localStorage.getItem('username') || ''); - const [view, setView] = useState(() => { - const id = localStorage.getItem('userId'); - return id ? getUserPref(id, 'lastView', 'server') : 'server'; - }); - const [activeChannel, setActiveChannel] = useState(null); - const [channelKeys, setChannelKeys] = useState({}); - const [activeDMChannel, setActiveDMChannel] = useState(null); - const [showMembers, setShowMembers] = useState(true); - const [showPinned, setShowPinned] = useState(false); - const [showMobileMembersScreen, setShowMobileMembersScreen] = useState(false); - const [showMobileSearchScreen, setShowMobileSearchScreen] = useState(false); - - const { activeView: mobileView, goToChat, goToSidebar, trayRef, trayStyle, isSwiping, swipeBindProps } = - useSwipeNavigation({ - enabled: isMobile, - canSwipeToChat: activeChannel !== null || activeDMChannel !== null || view === 'me' - }); - - useSystemBars(mobileView); - - // Jump-to-message state (for search result clicks) - const [jumpToMessageId, setJumpToMessageId] = useState(null); - const clearJumpToMessage = useCallback(() => setJumpToMessageId(null), []); - - // Search state - const [searchQuery, setSearchQuery] = useState(''); - const [showSearchDropdown, setShowSearchDropdown] = useState(false); - const [showSearchResults, setShowSearchResults] = useState(false); - const [searchSortOrder, setSearchSortOrder] = useState('newest'); - const [searchHistory, setSearchHistory] = useState(() => { - const id = localStorage.getItem('userId'); - return id ? getUserPref(id, 'searchHistory', []) : []; - }); - const searchInputRef = useRef(null); - - const convex = useConvex(); - const { toasts, addToast, removeToast, ToastContainer } = useToasts(); - const prevDmChannelsRef = useRef(null); - const { toggleMute, connectToVoice, disconnectVoice, activeChannelId: voiceActiveChannelId, voiceStates, room } = useVoice(); - - // Keyboard shortcuts - useEffect(() => { - const handler = (e) => { - if (e.ctrlKey && e.key === 'k') { - e.preventDefault(); - // Focus the search input - const input = searchInputRef.current?.querySelector('input'); - if (input) { - input.focus(); - } - } - if (e.ctrlKey && e.shiftKey && e.key === 'M') { - e.preventDefault(); - toggleMute(); - } - }; - window.addEventListener('keydown', handler); - return () => window.removeEventListener('keydown', handler); - }, [toggleMute]); - - const channels = useQuery(api.channels.list) || []; - const categories = useQuery(api.categories.list) || []; - const serverSettings = useQuery(api.serverSettings.get); - const serverName = serverSettings?.serverName || 'Secure Chat'; - const serverIconUrl = serverSettings?.iconUrl || null; - const allMembers = useQuery(api.members.listAll) || []; - const myPermissions = useQuery( - api.roles.getMyPermissions, - userId ? { userId } : "skip" - ) || {}; - - const rawChannelKeys = useQuery( - api.channelKeys.getKeysForUser, - userId ? { userId } : "skip" - ); - - const dmChannels = useQuery( - api.dms.listDMs, - userId ? { userId } : "skip" - ) || []; - - useEffect(() => { - if (!rawChannelKeys || rawChannelKeys.length === 0) return; - const privateKey = sessionStorage.getItem('privateKey'); - if (!privateKey) return; - - async function decryptKeys() { - const keys = {}; - for (const item of rawChannelKeys) { - try { - const bundleJson = await crypto.privateDecrypt(privateKey, item.encrypted_key_bundle); - Object.assign(keys, JSON.parse(bundleJson)); - } catch (e) { - console.error(`Failed to decrypt keys for channel ${item.channel_id}`, e); - } - } - setChannelKeys(keys); - } - - decryptKeys(); - }, [rawChannelKeys]); - - useEffect(() => { - if (activeChannel || channels.length === 0) return; - // Try to restore last active channel - if (userId) { - const savedChannel = getUserPref(userId, 'lastActiveChannel', null); - if (savedChannel && channels.some(c => c._id === savedChannel)) { - setActiveChannel(savedChannel); - return; - } - } - const firstTextChannel = channels.find(c => c.type === 'text'); - if (firstTextChannel) { - setActiveChannel(firstTextChannel._id); - } - }, [channels, activeChannel, userId]); - - // Persist active channel - useEffect(() => { - if (activeChannel && userId) { - setUserPref(userId, 'lastActiveChannel', activeChannel, settings); - } - }, [activeChannel, userId]); - - // Persist view mode - useEffect(() => { - if (userId) { - setUserPref(userId, 'lastView', view, settings); - } - }, [view, userId]); - - const openDM = useCallback(async (targetUserId, targetUsername) => { - const uid = localStorage.getItem('userId'); - if (!uid) return; - - try { - const { channelId, created } = await convex.mutation(api.dms.openDM, { - userId: uid, - targetUserId - }); - - if (created) { - const keyHex = await crypto.randomBytes(32); - - const allUsers = await convex.query(api.auth.getPublicKeys, {}); - const participants = allUsers.filter(u => u.id === uid || u.id === targetUserId); - - const batchKeys = []; - for (const u of participants) { - if (!u.public_identity_key) continue; - try { - const payload = JSON.stringify({ [channelId]: keyHex }); - const encryptedKeyHex = await crypto.publicEncrypt(u.public_identity_key, payload); - batchKeys.push({ - channelId, - userId: u.id, - encryptedKeyBundle: encryptedKeyHex, - keyVersion: 1 - }); - } catch (e) { - console.error('Failed to encrypt DM key for user', u.id, e); - } - } - - if (batchKeys.length > 0) { - await convex.mutation(api.channelKeys.uploadKeys, { keys: batchKeys }); - } - } - - setActiveDMChannel({ channel_id: channelId, other_username: targetUsername }); - setView('me'); - if (isMobile) goToChat(); - - } catch (err) { - console.error('Error opening DM:', err); - } - }, [convex, isMobile, goToChat]); - - const handleSelectChannel = useCallback((channelId) => { - setActiveChannel(channelId); - setShowPinned(false); - if (isMobile) goToChat(); - }, [isMobile, goToChat]); - - const handleMobileBack = useCallback(() => { - goToSidebar(); - }, [goToSidebar]); - - const activeChannelObj = channels.find(c => c._id === activeChannel); - const { watchingStreamOf } = useVoice(); - - const isDMView = view === 'me' && activeDMChannel; - const isServerTextChannel = view === 'server' && activeChannel && activeChannelObj?.type !== 'voice'; - const currentChannelId = isDMView ? activeDMChannel.channel_id : activeChannel; - const effectiveShowMembers = isMobile ? false : showMembers; - - // DM call state - const dmCallActive = isDMView && activeDMChannel - ? (voiceStates[activeDMChannel.channel_id]?.length > 0) : false; - const isInDMCall = isDMView && activeDMChannel - ? voiceActiveChannelId === activeDMChannel.channel_id : false; - const [dmCallExpanded, setDmCallExpanded] = useState(false); - const [dmCallStageHeight, setDmCallStageHeight] = useState(45); - const [rejectedCallChannelId, setRejectedCallChannelId] = useState(null); - const ringAudioRef = useRef(null); - const ringTimeoutRef = useRef(null); - - useEffect(() => { - if (!isInDMCall) setDmCallExpanded(false); - }, [isInDMCall]); - - // Global incoming DM call detection — not gated by current view - const incomingDMCall = useMemo(() => { - if (!userId) return null; - for (const dm of dmChannels) { - const users = voiceStates[dm.channel_id] || []; - if (users.length > 0 && voiceActiveChannelId !== dm.channel_id) { - const caller = users.find(u => u.userId !== userId); - if (caller) { - return { - channelId: dm.channel_id, - otherUsername: dm.other_username, - callerUsername: caller.username, - callerAvatarUrl: caller.avatarUrl || null, - }; - } - } - } - return null; - }, [dmChannels, voiceStates, voiceActiveChannelId, userId]); - - // Play ringing sound for incoming DM calls (global — works from any view) - useEffect(() => { - if (incomingDMCall && incomingDMCall.channelId !== rejectedCallChannelId) { - const audio = new Audio(callRingSound); - audio.loop = true; - audio.volume = 0.5; - audio.play().catch(() => {}); - ringAudioRef.current = audio; - - ringTimeoutRef.current = setTimeout(() => { - audio.pause(); - audio.currentTime = 0; - setRejectedCallChannelId(incomingDMCall.channelId); - }, 30000); - - return () => { - audio.pause(); - audio.currentTime = 0; - ringAudioRef.current = null; - clearTimeout(ringTimeoutRef.current); - ringTimeoutRef.current = null; - }; - } - }, [incomingDMCall?.channelId, rejectedCallChannelId]); - - // Clear rejected state when the rejected call's channel becomes empty - useEffect(() => { - if (rejectedCallChannelId && !(voiceStates[rejectedCallChannelId]?.length > 0)) { - setRejectedCallChannelId(null); - } - }, [rejectedCallChannelId, voiceStates]); - - const handleDmCallResizeStart = useCallback((e) => { - e.preventDefault(); - const chatContent = e.target.closest('.chat-content'); - if (!chatContent) return; - const startY = e.clientY; - const contentRect = chatContent.getBoundingClientRect(); - const startHeight = dmCallStageHeight; - - const onMouseMove = (moveE) => { - const deltaY = moveE.clientY - startY; - const deltaPercent = (deltaY / contentRect.height) * 100; - const newHeight = Math.min(80, Math.max(20, startHeight + deltaPercent)); - setDmCallStageHeight(newHeight); - }; - const onMouseUp = () => { - document.removeEventListener('mousemove', onMouseMove); - document.removeEventListener('mouseup', onMouseUp); - }; - document.addEventListener('mousemove', onMouseMove); - document.addEventListener('mouseup', onMouseUp); - }, [dmCallStageHeight]); - - const handleStartDMCall = useCallback(async () => { - if (!activeDMChannel) return; - const dmChannelId = activeDMChannel.channel_id; - const otherUsername = activeDMChannel.other_username; - - // Already in this DM call - if (voiceActiveChannelId === dmChannelId) return; - - // Suppress ringing when we leave later — just show the banner - setRejectedCallChannelId(dmChannelId); - - // If in another voice channel, disconnect first - if (voiceActiveChannelId) { - disconnectVoice(); - // Brief delay for cleanup - await new Promise(r => setTimeout(r, 300)); - } - - // Check if this is a new call (no one currently in it) - const isNewCall = !voiceStates[dmChannelId]?.length; - - connectToVoice(dmChannelId, otherUsername, userId, true); - - // Send system message for new calls - if (isNewCall && channelKeys[dmChannelId]) { - try { - const systemContent = JSON.stringify({ type: 'system', text: `${username} started a call` }); - const { content: encryptedContent, iv, tag } = await crypto.encryptData(systemContent, channelKeys[dmChannelId]); - const ciphertext = encryptedContent + tag; - const signingKey = sessionStorage.getItem('signingKey'); - if (signingKey) { - await convex.mutation(api.messages.send, { - channelId: dmChannelId, - senderId: userId, - ciphertext, - nonce: iv, - signature: await crypto.signMessage(signingKey, ciphertext), - keyVersion: 1 - }); - } - } catch (e) { - console.error('Failed to send call system message:', e); - } - } - }, [activeDMChannel, voiceActiveChannelId, disconnectVoice, connectToVoice, userId, username, channelKeys, crypto, convex, voiceStates]); - - // Pending call pattern: open DM then start call once it's active - const [pendingCallUserId, setPendingCallUserId] = useState(null); - - const handleStartCallWithUser = useCallback(async (targetUserId, targetUsername) => { - await openDM(targetUserId, targetUsername); - setPendingCallUserId(targetUserId); - }, [openDM]); - - useEffect(() => { - if (!pendingCallUserId || !activeDMChannel) return; - // Verify the DM channel is for the pending user - const isCorrectDM = activeDMChannel.other_user_id === pendingCallUserId || - activeDMChannel.channel_name?.includes(pendingCallUserId); - if (!isCorrectDM && activeDMChannel.other_username) { - // Just proceed - openDM should have set the right channel - } - setPendingCallUserId(null); - // Small delay for key distribution to settle - const timer = setTimeout(() => { - handleStartDMCall(); - }, 500); - return () => clearTimeout(timer); - }, [pendingCallUserId, activeDMChannel, handleStartDMCall]); - - // PiP: show when watching a stream and NOT currently viewing the voice channel in VoiceStage - const isViewingDMCallStage = isDMView && isInDMCall; - const isViewingVoiceStage = (view === 'server' && activeChannelObj?.type === 'voice' && activeChannel === voiceActiveChannelId) || isViewingDMCallStage; - const showPiP = watchingStreamOf !== null && !isViewingVoiceStage; - - const handleGoBackToStream = useCallback(() => { - if (voiceActiveChannelId) { - setActiveChannel(voiceActiveChannelId); - setView('server'); - } - }, [voiceActiveChannelId]); - - // Search handlers - const handleSearchQueryChange = useCallback((val) => { - setSearchQuery(val); - if (val === '') { - setShowSearchResults(false); - } - if (!showSearchDropdown && val !== undefined) { - setShowSearchDropdown(true); - } - }, [showSearchDropdown]); - - const handleSearchFocus = useCallback(() => { - setShowSearchDropdown(true); - }, []); - - const handleSearchBlur = useCallback(() => { - // Dropdown close is handled by click-outside in SearchDropdown - }, []); - - const handleSearchSubmit = useCallback(() => { - if (!searchQuery.trim()) return; - setShowSearchDropdown(false); - setShowSearchResults(true); - // Save to history - setSearchHistory(prev => { - const filtered = prev.filter(h => h !== searchQuery.trim()); - const updated = [searchQuery.trim(), ...filtered].slice(0, MAX_SEARCH_HISTORY); - if (userId) { - setUserPref(userId, 'searchHistory', updated, settings); - } - return updated; - }); - }, [searchQuery, userId, settings]); - - const handleSelectFilter = useCallback((prefix, value) => { - if (value !== null) { - // Replace the current active prefix with the completed token - const beforePrefix = searchQuery.replace(/\b(from|in|has|mentions):\S*$/i, '').trimEnd(); - const newQuery = beforePrefix + (beforePrefix ? ' ' : '') + prefix + ':' + value + ' '; - setSearchQuery(newQuery); - } else { - // Just insert the prefix (e.g., clicking "from:" suggestion) - const newQuery = searchQuery + (searchQuery && !searchQuery.endsWith(' ') ? ' ' : '') + prefix + ':'; - setSearchQuery(newQuery); - } - // Re-focus input - setTimeout(() => { - const input = searchInputRef.current?.querySelector('input'); - if (input) input.focus(); - }, 0); - }, [searchQuery]); - - const handleSelectHistoryItem = useCallback((item) => { - setSearchQuery(item); - setShowSearchDropdown(false); - setShowSearchResults(true); - }, []); - - const handleClearHistory = useCallback(() => { - setSearchHistory([]); - if (userId) { - setUserPref(userId, 'searchHistory', [], settings); - } - }, [userId, settings]); - - const handleClearHistoryItem = useCallback((index) => { - setSearchHistory(prev => { - const updated = prev.filter((_, i) => i !== index); - if (userId) { - setUserPref(userId, 'searchHistory', updated, settings); - } - return updated; - }); - }, [userId, settings]); - - const handleCloseSearchDropdown = useCallback(() => { - setShowSearchDropdown(false); - }, []); - - const handleCloseSearchResults = useCallback(() => { - setShowSearchResults(false); - setSearchQuery(''); - }, []); - - const handleJumpToMessage = useCallback((channelId, messageId) => { - // Switch to the correct channel if needed - const isDM = dmChannels.some(dm => dm.channel_id === channelId); - if (isDM) { - const dm = dmChannels.find(d => d.channel_id === channelId); - if (dm) { - setActiveDMChannel(dm); - setView('me'); - } - } else { - setActiveChannel(channelId); - setView('server'); - } - setShowSearchResults(false); - setSearchQuery(''); - setJumpToMessageId(messageId); - if (isMobile) goToChat(); - }, [dmChannels, isMobile, goToChat]); - - // Shared search props for ChatHeader - const searchProps = { - searchQuery, - onSearchQueryChange: handleSearchQueryChange, - onSearchSubmit: handleSearchSubmit, - onSearchFocus: handleSearchFocus, - onSearchBlur: handleSearchBlur, - searchInputRef, - searchActive: showSearchDropdown || showSearchResults, - }; - - function renderMainContent() { - if (view === 'me') { - if (activeDMChannel) { - const showIncomingUI = dmCallActive && !isInDMCall && incomingDMCall?.channelId === activeDMChannel?.channel_id && rejectedCallChannelId !== activeDMChannel?.channel_id; - return ( - <div className="chat-container"> - <ChatHeader - channelName={activeDMChannel.other_username} - channelType="dm" - onToggleMembers={() => {}} - showMembers={false} - onTogglePinned={() => setShowPinned(p => !p)} - isMobile={isMobile} - onMobileBack={handleMobileBack} - onStartCall={handleStartDMCall} - isDMCallActive={dmCallActive} - {...searchProps} - /> - <div className="chat-content" style={dmCallActive || isInDMCall ? { flexDirection: 'column' } : undefined}> - {showIncomingUI && ( - <IncomingCallUI - callerUsername={incomingDMCall.callerUsername} - callerAvatarUrl={incomingDMCall.callerAvatarUrl} - onJoin={handleStartDMCall} - onReject={() => setRejectedCallChannelId(incomingDMCall.channelId)} - /> - )} - {dmCallActive && !isInDMCall && !showIncomingUI && ( - <div className="dm-call-idle-stage"> - <Avatar username={incomingDMCall?.callerUsername || activeDMChannel.other_username} avatarUrl={incomingDMCall?.callerAvatarUrl || null} size={80} /> - <div className="dm-call-idle-username">{activeDMChannel.other_displayName || activeDMChannel.other_username}</div> - <div className="dm-call-idle-status">In a call</div> - <button className="dm-call-join-btn" onClick={handleStartDMCall}>Join Call</button> - </div> - )} - {isInDMCall && ( - <div className="dm-call-stage" style={dmCallExpanded ? { height: '100%', maxHeight: '100%' } : { height: `${dmCallStageHeight}%` }}> - <button - className="dm-call-expand-btn" - onClick={() => setDmCallExpanded(prev => !prev)} - title={dmCallExpanded ? "Show Chat" : "Hide Chat"} - > - <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" - style={{ transform: dmCallExpanded ? 'rotate(180deg)' : undefined, transition: 'transform 0.2s' }}> - <path d="M5.3 9.3a1 1 0 0 1 1.4 0l5.3 5.29 5.3-5.3a1 1 0 1 1 1.4 1.42l-6 6a1 1 0 0 1-1.4 0l-6-6a1 1 0 0 1 0-1.42Z"/> - </svg> - </button> - <VoiceStage room={room} channelId={activeDMChannel.channel_id} voiceStates={voiceStates} channelName={activeDMChannel.other_username} /> - {!dmCallExpanded && <div className="dm-call-resize-handle" onMouseDown={handleDmCallResizeStart} />} - </div> - )} - {(!isInDMCall || !dmCallExpanded) && ( - <div style={{ flex: 1, display: 'flex', overflow: 'hidden' }}> - <ChatArea - channelId={activeDMChannel.channel_id} - channelName={activeDMChannel.other_username} - channelType="dm" - channelKey={channelKeys[activeDMChannel.channel_id]} - username={username} - userId={userId} - showMembers={false} - onToggleMembers={() => {}} - onOpenDM={openDM} - showPinned={showPinned} - onTogglePinned={() => setShowPinned(false)} - jumpToMessageId={jumpToMessageId} - onClearJumpToMessage={clearJumpToMessage} - /> - <SearchPanel - visible={showSearchResults} - onClose={handleCloseSearchResults} - channels={channels} - isDM={true} - dmChannelId={activeDMChannel.channel_id} - onJumpToMessage={handleJumpToMessage} - query={searchQuery} - sortOrder={searchSortOrder} - onSortChange={setSearchSortOrder} - /> - </div> - )} - </div> - </div> - ); - } - return ( - <> - {isMobile && ( - <div className="chat-header" style={{ position: 'sticky', top: 0, zIndex: 10 }}> - <div className="chat-header-left"> - <button className="mobile-back-btn" onClick={handleMobileBack}> - <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg> - </button> - <span className="chat-header-name">Friends</span> - </div> - </div> - )} - <FriendsView onOpenDM={openDM} /> - </> - ); - } - - if (activeChannel) { - if (activeChannelObj?.type === 'voice') { - return ( - <div className="chat-container"> - {isMobile && ( - <div className="chat-header voice-header"> - <div className="chat-header-left"> - <button className="mobile-back-btn" onClick={handleMobileBack}> - <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg> - </button> - <svg width="20" height="20" viewBox="0 0 24 24" style={{ color: 'var(--text-muted)', marginRight: 4 }}> - <path fill="currentColor" d="M11.383 3.07904C11.009 2.92504 10.579 3.01004 10.293 3.29604L6.586 7.00304H3C2.45 7.00304 2 7.45304 2 8.00304V16.003C2 16.553 2.45 17.003 3 17.003H6.586L10.293 20.71C10.579 20.996 11.009 21.082 11.383 20.927C11.757 20.772 12 20.407 12 20.003V4.00304C12 3.59904 11.757 3.23404 11.383 3.07904Z" /> - </svg> - <span className="chat-header-name">{activeChannelObj?.name}</span> - </div> - </div> - )} - <VoiceStage room={room} channelId={activeChannel} voiceStates={voiceStates} channelName={activeChannelObj?.name} /> - </div> - ); - } - return ( - <div className="chat-container"> - <ChatHeader - channelName={activeChannelObj?.name || activeChannel} - channelType="text" - channelId={activeChannel} - channelTopic={activeChannelObj?.topic} - onToggleMembers={() => setShowMembers(!showMembers)} - showMembers={effectiveShowMembers} - onTogglePinned={() => setShowPinned(p => !p)} - serverName={serverName} - isMobile={isMobile} - onMobileBack={handleMobileBack} - onOpenMembersScreen={() => setShowMobileMembersScreen(true)} - {...searchProps} - /> - <div className="chat-content"> - <ChatArea - channelId={activeChannel} - channelName={activeChannelObj?.name || activeChannel} - channelType="text" - channelKey={channelKeys[activeChannel]} - username={username} - userId={userId} - showMembers={effectiveShowMembers} - onToggleMembers={() => setShowMembers(!showMembers)} - onOpenDM={openDM} - showPinned={showPinned} - onTogglePinned={() => setShowPinned(false)} - jumpToMessageId={jumpToMessageId} - onClearJumpToMessage={clearJumpToMessage} - /> - <MembersList - channelId={activeChannel} - visible={effectiveShowMembers} - onMemberClick={(member) => {}} - userId={userId} - myPermissions={myPermissions} - onOpenDM={openDM} - onStartCallWithUser={handleStartCallWithUser} - /> - <SearchPanel - visible={showSearchResults} - onClose={handleCloseSearchResults} - channels={channels} - onJumpToMessage={handleJumpToMessage} - query={searchQuery} - sortOrder={searchSortOrder} - onSortChange={setSearchSortOrder} - /> - </div> - </div> - ); - } - - return ( - <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#b9bbbe', flexDirection: 'column' }}> - <h2>Welcome to {serverName}</h2> - <p>No channels found.</p> - <p>Click the <b>+</b> in the sidebar to create your first encrypted channel.</p> - </div> - ); - } - - const handleSetActiveDMChannel = useCallback((dm) => { - setActiveDMChannel(dm); - if (isMobile && dm) goToChat(); - }, [isMobile, goToChat]); - - const handleViewChange = useCallback((newView) => { - setView(newView); - if (isMobile) goToSidebar(); - }, [isMobile, goToSidebar]); - - if (!userId) { - return ( - <div className="app-container"> - <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#b9bbbe' }}> - Loading... - </div> - </div> - ); - } - - const sidebarElement = ( - <Sidebar - channels={channels} - categories={categories} - activeChannel={activeChannel} - onSelectChannel={handleSelectChannel} - username={username} - channelKeys={channelKeys} - view={view} - onViewChange={handleViewChange} - onOpenDM={openDM} - activeDMChannel={activeDMChannel} - setActiveDMChannel={handleSetActiveDMChannel} - dmChannels={dmChannels} - userId={userId} - serverName={serverName} - serverIconUrl={serverIconUrl} - isMobile={isMobile} - onStartCallWithUser={handleStartCallWithUser} - onOpenMobileSearch={() => setShowMobileSearchScreen(true)} - /> - ); - - return ( - <PresenceProvider userId={userId}> - <div className={`app-container${isMobile ? ' is-mobile' : ''}`}> - {isMobile ? ( - <div - ref={trayRef} - {...swipeBindProps} - className={`mobile-swipe-tray${isSwiping ? ' is-swiping' : ''}`} - style={trayStyle} - > - <div className="mobile-swipe-panel"> - {sidebarElement} - </div> - <div className="mobile-swipe-panel"> - {renderMainContent()} - </div> - </div> - ) : ( - <> - {sidebarElement} - {renderMainContent()} - </> - )} - {showPiP && <FloatingStreamPiP onGoBackToStream={handleGoBackToStream} />} - {showSearchDropdown && !isMobile && ( - <SearchDropdown - visible={showSearchDropdown} - searchText={searchQuery} - channels={channels} - members={allMembers} - searchHistory={searchHistory} - onSelectFilter={handleSelectFilter} - onSelectHistoryItem={handleSelectHistoryItem} - onClearHistory={handleClearHistory} - onClearHistoryItem={handleClearHistoryItem} - anchorRef={searchInputRef} - onClose={handleCloseSearchDropdown} - /> - )} - <ToastContainer /> - {showMobileMembersScreen && isMobile && activeChannel && activeChannelObj?.type !== 'voice' && ( - <MobileMembersScreen - channelId={activeChannel} - channelName={activeChannelObj?.name || ''} - onClose={() => setShowMobileMembersScreen(false)} - /> - )} - {showMobileSearchScreen && isMobile && ( - <MobileSearchScreen - channels={channels} - allMembers={allMembers} - serverName={serverName} - onClose={() => setShowMobileSearchScreen(false)} - onSelectChannel={(channelId) => { - handleSelectChannel(channelId); - setShowMobileSearchScreen(false); - }} - onJumpToMessage={(channelId, messageId) => { - handleJumpToMessage(channelId, messageId); - setShowMobileSearchScreen(false); - }} - /> - )} - </div> - </PresenceProvider> - ); -}; - -export default Chat; diff --git a/packages/shared/src/pages/Login.jsx b/packages/shared/src/pages/Login.jsx deleted file mode 100644 index d7ebe6a..0000000 --- a/packages/shared/src/pages/Login.jsx +++ /dev/null @@ -1,151 +0,0 @@ -import React, { useState } from 'react'; -import { Link, useNavigate } from 'react-router-dom'; -import { useConvex } from 'convex/react'; -import { usePlatform } from '../platform'; -import { useSearch } from '../contexts/SearchContext'; -import { api } from '../../../../convex/_generated/api'; - -const Login = () => { - const [username, setUsername] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(''); - const [loading, setLoading] = useState(false); - const navigate = useNavigate(); - const convex = useConvex(); - const { crypto, session } = usePlatform(); - const searchCtx = useSearch(); - - async function decryptEncryptedField(encryptedJson, keyHex) { - const obj = JSON.parse(encryptedJson); - return crypto.decryptData(obj.content, keyHex, obj.iv, obj.tag); - } - - const handleLogin = async (e) => { - e.preventDefault(); - setError(''); - setLoading(true); - - try { - console.log('Starting login for:', username); - - const { salt } = await convex.query(api.auth.getSalt, { username }); - console.log('Got salt'); - - const { dek, dak } = await crypto.deriveAuthKeys(password, salt); - console.log('Derived keys'); - - // Derive a separate key for the local search database - const searchKeys = await crypto.deriveAuthKeys(password, 'searchdb-' + username); - sessionStorage.setItem('searchDbKey', searchKeys.dak); - - const verifyData = await convex.mutation(api.auth.verifyUser, { username, dak }); - - if (verifyData.error) { - throw new Error(verifyData.error); - } - - console.log('Login verified. Response data:', verifyData); - - if (verifyData.userId) { - console.log('Saving userId to localStorage:', verifyData.userId); - localStorage.setItem('userId', verifyData.userId); - } else { - console.error('MISSING USERID IN VERIFY RESPONSE!', verifyData); - } - - console.log('Decrypting Master Key...'); - const mkHex = await decryptEncryptedField(verifyData.encryptedMK, dek); - sessionStorage.setItem('masterKey', mkHex); - - console.log('Decrypting Private Keys...'); - const encryptedPrivateKeysObj = JSON.parse(verifyData.encryptedPrivateKeys); - - const signingKey = await decryptEncryptedField(JSON.stringify(encryptedPrivateKeysObj.ed), mkHex); - const rsaPriv = await decryptEncryptedField(JSON.stringify(encryptedPrivateKeysObj.rsa), mkHex); - - sessionStorage.setItem('signingKey', signingKey); - sessionStorage.setItem('privateKey', rsaPriv); - console.log('Keys decrypted and stored in session.'); - - localStorage.setItem('username', username); - if (verifyData.publicKey) { - localStorage.setItem('publicKey', verifyData.publicKey); - } - - // Persist session via safeStorage for auto-login on restart - if (session) { - try { - await session.save({ - userId: verifyData.userId, - username, - publicKey: verifyData.publicKey || '', - signingKey, - privateKey: rsaPriv, - masterKey: mkHex, - searchDbKey: searchKeys.dak, - savedAt: Date.now(), - }); - } catch (e) { - console.warn('Session persistence unavailable:', e); - } - } - - console.log('Immediate localStorage read check:', localStorage.getItem('userId')); - - searchCtx?.initialize(); - navigate('/chat'); - } catch (err) { - console.error('Login error:', err); - setError(err.message); - } finally { - setLoading(false); - } - }; - - return ( - <div className="auth-container"> - <div className="auth-box"> - <div className="auth-header"> - <h2>Welcome Back!</h2> - <p>We're so excited to see you again!</p> - </div> - {error && <div style={{ color: 'red', marginBottom: 10, textAlign: 'center' }}>{error}</div>} - <form onSubmit={handleLogin}> - <div className="form-group"> - <label>Username</label> - <input - type="text" - value={username} - onChange={(e) => setUsername(e.target.value)} - required - disabled={loading} - /> - </div> - <div className="form-group"> - <label>Password</label> - <input - type="password" - value={password} - onChange={(e) => setPassword(e.target.value)} - required - disabled={loading} - /> - </div> - <button type="submit" className="auth-button" disabled={loading}> - {loading ? 'Logging in...' : 'Log In'} - </button> - </form> - <div className="auth-footer"> - Need an account? <Link to="/register">Register</Link> - </div> - <div style={{ textAlign: 'center', marginTop: '8px' }}> - <Link to="/recovery" style={{ color: 'var(--brand-experiment)', fontSize: '14px' }}> - Forgot your password? - </Link> - </div> - </div> - </div> - ); -}; - -export default Login; diff --git a/packages/shared/src/pages/Register.jsx b/packages/shared/src/pages/Register.jsx deleted file mode 100644 index 9bf42f9..0000000 --- a/packages/shared/src/pages/Register.jsx +++ /dev/null @@ -1,217 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { Link, useNavigate, useLocation } from 'react-router-dom'; -import { useConvex } from 'convex/react'; -import { usePlatform } from '../platform'; -import { api } from '../../../../convex/_generated/api'; - -function parseInviteParams(input) { - const codeMatch = input.match(/[?&]code=([^&]+)/); - const keyMatch = input.match(/[?&]key=([^&]+)/); - if (codeMatch && keyMatch) return { code: codeMatch[1], secret: keyMatch[1] }; - return null; -} - -const Register = () => { - const [username, setUsername] = useState(''); - const [password, setPassword] = useState(''); - const [confirmPassword, setConfirmPassword] = useState(''); - const [error, setError] = useState(''); - const [loading, setLoading] = useState(false); - const [inviteKeys, setInviteKeys] = useState(null); - const [inviteLinkInput, setInviteLinkInput] = useState(''); - const [activeInviteCode, setActiveInviteCode] = useState(null); - - const navigate = useNavigate(); - const location = useLocation(); - const convex = useConvex(); - const { crypto } = usePlatform(); - - const processInvite = async (code, secret) => { - try { - const result = await convex.query(api.invites.use, { code }); - if (result.error) throw new Error(result.error); - - const blob = JSON.parse(result.encryptedPayload); - const decrypted = await crypto.decryptData(blob.c, secret, blob.iv, blob.t); - const keys = JSON.parse(decrypted); - - console.log('Invite keys decrypted successfully:', Object.keys(keys).length); - setInviteKeys(keys); - setActiveInviteCode(code); - setError(''); - } catch (err) { - console.error('Invite error:', err); - setError('Invite verification failed: ' + err.message); - } - }; - - useEffect(() => { - const params = new URLSearchParams(location.search); - const code = params.get('code'); - const secret = params.get('key'); - - if (code && secret) { - console.log('Invite detected in URL'); - processInvite(code, secret); - } - }, [location]); - - const handleManualInvite = () => { - const parsed = parseInviteParams(inviteLinkInput); - if (parsed) { - processInvite(parsed.code, parsed.secret); - } else { - setError("Invalid invite link format."); - } - }; - - const handleRegister = async (e) => { - e.preventDefault(); - setError(''); - - if (password !== confirmPassword) { - setError('Passwords do not match'); - return; - } - - setLoading(true); - - try { - console.log('Starting registration for:', username); - - const salt = await crypto.randomBytes(16); - const mk = await crypto.randomBytes(32); - const { dek, dak } = await crypto.deriveAuthKeys(password, salt); - const encryptedMK = JSON.stringify(await crypto.encryptData(mk, dek)); - const hak = await crypto.sha256(dak); - const keys = await crypto.generateKeys(); - - const encryptedPrivateKeys = JSON.stringify({ - rsa: await crypto.encryptData(keys.rsaPriv, mk), - ed: await crypto.encryptData(keys.edPriv, mk) - }); - - const data = await convex.mutation(api.auth.createUserWithProfile, { - username, - salt, - encryptedMK, - hak, - publicKey: keys.rsaPub, - signingKey: keys.edPub, - encryptedPrivateKeys, - inviteCode: activeInviteCode || undefined - }); - - if (data.error) throw new Error(data.error); - console.log('Registration successful:', data); - - if (inviteKeys && data.userId) { - console.log('Uploading invite keys...'); - const batchKeys = await Promise.all( - Object.entries(inviteKeys).map(async ([channelId, channelKeyHex]) => { - const payload = JSON.stringify({ [channelId]: channelKeyHex }); - const encryptedKeyBundle = await crypto.publicEncrypt(keys.rsaPub, payload); - return { channelId, userId: data.userId, encryptedKeyBundle, keyVersion: 1 }; - }).map(p => p.catch(err => { - console.error('Failed to encrypt key for channel:', err); - return null; - })) - ); - - const validKeys = batchKeys.filter(Boolean); - if (validKeys.length > 0) { - await convex.mutation(api.channelKeys.uploadKeys, { keys: validKeys }); - console.log('Uploaded invite keys'); - } - } - - navigate('/'); - } catch (err) { - console.error('Registration error:', err); - setError(err.message); - } finally { - setLoading(false); - } - }; - - return ( - <div className="auth-container"> - <div className="auth-box"> - <div className="auth-header"> - <h2>Create an Account</h2> - <p>Join the secure chat! {inviteKeys ? '(Invite Active)' : ''}</p> - </div> - {error && <div style={{ color: 'red', marginBottom: 10, textAlign: 'center' }}>{error}</div>} - - {!inviteKeys && ( - <div style={{ marginBottom: '15px', display: 'flex' }}> - <input - type="text" - placeholder="Paste Invite Link Here..." - value={inviteLinkInput} - onChange={(e) => setInviteLinkInput(e.target.value)} - style={{ flex: 1, marginRight: '8px' }} - /> - <button type="button" onClick={handleManualInvite} className="auth-button" style={{ width: 'auto' }}> - Apply - </button> - </div> - )} - - {inviteKeys ? ( - <form onSubmit={handleRegister}> - <div className="form-group"> - <label>Username</label> - <input - type="text" - value={username} - onChange={(e) => setUsername(e.target.value)} - required - disabled={loading} - /> - </div> - <div className="form-group"> - <label>Password</label> - <input - type="password" - value={password} - onChange={(e) => setPassword(e.target.value)} - required - disabled={loading} - /> - </div> - <div className="form-group"> - <label>Confirm Password</label> - <input - type="password" - value={confirmPassword} - onChange={(e) => setConfirmPassword(e.target.value)} - required - disabled={loading} - /> - </div> - <button type="submit" className="auth-button" disabled={loading}> - {loading ? 'Generating Keys...' : 'Continue'} - </button> - </form> - ) : ( - <div style={{ textAlign: 'center', marginTop: '20px', color: '#b9bbbe' }}> - <p>Registration is Invite-Only.</p> - <p style={{ fontSize: '0.9em' }}>Please paste a valid invite link above to proceed.</p> - - {/* Backdoor for First User */} - <p style={{ marginTop: '20px', fontSize: '0.8em', cursor: 'pointer', color: '#7289da' }} onClick={() => setInviteKeys({})}> - (First User / Verify Setup) - </p> - </div> - )} - - <div className="auth-footer"> - Already have an account? <Link to="/">Log In</Link> - </div> - </div> - </div> - ); -}; - -export default Register; diff --git a/packages/shared/src/styles/themes.css b/packages/shared/src/styles/themes.css deleted file mode 100644 index 3c3bb1c..0000000 --- a/packages/shared/src/styles/themes.css +++ /dev/null @@ -1,418 +0,0 @@ -/* ============================================ - Discord Theme System - 4 themes: Light, Dark (default), Ash, Onyx - CSS class mapping: - Light → .theme-light - Dark → .theme-dark - Ash → .theme-darker - Onyx → .theme-midnight - ============================================ */ - -/* ============================================ - DARK THEME (default) - ============================================ */ -.theme-dark { - /* Backgrounds */ - --background-base-low: #313338; - --background-base-lower: #2b2d31; - --background-base-lowest: #1e1f22; - --background-surface-high: #3c3e44; - --background-surface-higher: #3f4147; - --background-surface-highest: #43454b; - --chat-background: #313338; - --channeltextarea-background: #383a40; - --modal-background: #313338; - --panel-bg: #2b2d31; - --embed-background: #2f3136; - - /* Text */ - --text-default: #f2f3f5; - --text-strong: #f2f3f5; - --text-muted: #949ba4; - --text-subtle: #b5bac1; - --text-link: #00a8fc; - --channels-default: #949ba4; - --text-feedback-critical: #ed4245; - - /* Interactive */ - --interactive-background-hover: rgba(78, 80, 88, 0.3); - --interactive-background-active: rgba(78, 80, 88, 0.48); - --interactive-background-selected: rgba(78, 80, 88, 0.6); - --interactive-icon-default: #b5bac1; - --interactive-icon-hover: #dbdee1; - --interactive-icon-active: #ffffff; - --interactive-text-default: #b5bac1; - --interactive-text-hover: #dbdee1; - --interactive-text-active: #ffffff; - - /* Borders */ - --border-subtle: #1e1f22; - --border-muted: rgba(255, 255, 255, 0.04); - --border-normal: rgba(255, 255, 255, 0.2); - --border-strong: rgba(255, 255, 255, 0.44); - --app-frame-border: hsla(240, 4%, 60.784%, 0.122); - - /* Icons */ - --icon-default: #dbdee1; - --icon-strong: #ffffff; - --icon-muted: #949ba4; - --icon-subtle: #b5bac1; - - /* Controls */ - --control-primary-background-default: #5865f2; - --control-primary-background-hover: #4752c4; - --control-primary-background-active: #3b43a8; - --control-critical-primary-background-default: #ed4245; - - /* Input */ - --input-background-default: #383a40; - --input-border-default: rgba(255, 255, 255, 0.2); - --input-text-default: #dbdee1; - - /* Scrollbar */ - --scrollbar-auto-thumb: #1a1b1e; - --scrollbar-thin-thumb: #1a1b1e; - - /* Message */ - --message-background-hover: rgba(0, 0, 0, 0.06); - - /* Compatibility aliases (map old names → new semantic names) */ - --bg-primary: #313338; - --bg-secondary: #2b2d31; - --bg-tertiary: #1e1f22; - --text-normal: #dbdee1; - --header-primary: #f2f3f5; - --header-secondary: #b5bac1; - --interactive-normal: #b5bac1; - --interactive-hover: #dbdee1; - --interactive-active: #ffffff; - --brand-experiment: #5865f2; - --brand-experiment-hover: #4752c4; - --input-background: #383a40; - --danger: #ed4245; - --background-modifier-hover: rgba(78, 80, 88, 0.3); - --background-modifier-active: rgba(78, 80, 88, 0.48); - --background-modifier-selected: rgba(78, 80, 88, 0.6); - --div-border: #1e1f22; - - --text-feedback-warning: hsl(38.455, 100%, 43.137%); -} - - -/* ============================================ - LIGHT THEME - ============================================ */ -.theme-light { - /* Backgrounds */ - --background-base-low: #ffffff; - --background-base-lower: #f2f3f5; - --background-base-lowest: #e3e5e8; - --background-surface-high: #ffffff; - --background-surface-higher: #ffffff; - --background-surface-highest: #ffffff; - --chat-background: #ffffff; - --channeltextarea-background: #ebedef; - --modal-background: #ffffff; - --panel-bg: #f2f3f5; - --embed-background: #f2f3f5; - - /* Text */ - --text-default: #313338; - --text-strong: #060607; - --text-muted: #5c6470; - --text-subtle: #4e5058; - --text-link: #006ce7; - --channels-default: #5c6470; - --text-feedback-critical: #da373c; - - /* Interactive */ - --interactive-background-hover: rgba(116, 124, 138, 0.14); - --interactive-background-active: rgba(116, 124, 138, 0.22); - --interactive-background-selected: rgba(116, 124, 138, 0.30); - --interactive-icon-default: #4e5058; - --interactive-icon-hover: #313338; - --interactive-icon-active: #060607; - --interactive-text-default: #4e5058; - --interactive-text-hover: #313338; - --interactive-text-active: #060607; - - /* Borders */ - --border-subtle: rgba(0, 0, 0, 0.28); - --border-muted: rgba(0, 0, 0, 0.2); - --border-normal: rgba(0, 0, 0, 0.36); - --border-strong: rgba(0, 0, 0, 0.48); - --app-frame-border: hsla(240, 4%, 60.784%, 0.122); - - /* Icons */ - --icon-default: #313338; - --icon-strong: #060607; - --icon-muted: #5c6470; - --icon-subtle: #4e5058; - - /* Controls */ - --control-primary-background-default: #5865f2; - --control-primary-background-hover: #4752c4; - --control-primary-background-active: #3b43a8; - --control-critical-primary-background-default: #da373c; - - /* Input */ - --input-background-default: #e3e5e8; - --input-border-default: rgba(0, 0, 0, 0.36); - --input-text-default: #313338; - - /* Scrollbar */ - --scrollbar-auto-thumb: #c1c3c7; - --scrollbar-thin-thumb: #c1c3c7; - - /* Message */ - --message-background-hover: rgba(0, 0, 0, 0.06); - - /* Compatibility aliases */ - --bg-primary: #ffffff; - --bg-secondary: #f2f3f5; - --bg-tertiary: #e3e5e8; - --text-normal: #313338; - --header-primary: #060607; - --header-secondary: #4e5058; - --interactive-normal: #4e5058; - --interactive-hover: #313338; - --interactive-active: #060607; - --brand-experiment: #5865f2; - --brand-experiment-hover: #4752c4; - --input-background: #e3e5e8; - --danger: #da373c; - --background-modifier-hover: rgba(116, 124, 138, 0.14); - --background-modifier-active: rgba(116, 124, 138, 0.22); - --background-modifier-selected: rgba(116, 124, 138, 0.30); - --div-border: #e1e2e4; - - --text-feedback-warning: hsl(38.455, 100%, 43.137%); -} - - -/* ============================================ - ASH THEME (theme-darker) - ============================================ */ -.theme-darker { - /* Backgrounds */ - --background-base-low: #202225; - --background-base-lower: #1a1b1e; - --background-base-lowest: #111214; - --background-surface-high: #292b2f; - --background-surface-higher: #2e3035; - --background-surface-highest: #33363c; - --chat-background: #202225; - --channeltextarea-background: #252529; - --modal-background: #292b2f; - --panel-bg: hsl(240, 5.882%, 13.333%); - --embed-background: #242529; - - /* Text */ - --text-default: #f0f1f3; - --text-strong: #f5f5f7; - --text-muted: #858993; - --text-subtle: #a0a4ad; - --text-link: #00a8fc; - --channels-default: #858993; - --text-feedback-critical: #ed4245; - - /* Interactive */ - --interactive-background-hover: rgba(78, 80, 88, 0.15); - --interactive-background-active: rgba(78, 80, 88, 0.3); - --interactive-background-selected: rgba(78, 80, 88, 0.4); - --interactive-icon-default: #a0a4ad; - --interactive-icon-hover: #dddfe4; - --interactive-icon-active: #f5f5f7; - --interactive-text-default: #a0a4ad; - --interactive-text-hover: #dddfe4; - --interactive-text-active: #f5f5f7; - - /* Borders */ - --border-subtle: rgba(255, 255, 255, 0.12); - --border-muted: rgba(255, 255, 255, 0.04); - --border-normal: rgba(255, 255, 255, 0.2); - --border-strong: rgba(255, 255, 255, 0.44); - --app-frame-border: hsla(240, 4%, 60.784%, 0.122); - - /* Icons */ - --icon-default: #dddfe4; - --icon-strong: #f5f5f7; - --icon-muted: #858993; - --icon-subtle: #a0a4ad; - - /* Controls */ - --control-primary-background-default: #5865f2; - --control-primary-background-hover: #4752c4; - --control-primary-background-active: #3b43a8; - --control-critical-primary-background-default: #ed4245; - - /* Input */ - --input-background-default: #252529; - --input-border-default: rgba(255, 255, 255, 0.2); - --input-text-default: #dddfe4; - - /* Scrollbar */ - --scrollbar-auto-thumb: #15161a; - --scrollbar-thin-thumb: #15161a; - - /* Message */ - --message-background-hover: rgba(0, 0, 0, 0.08); - - /* Compatibility aliases */ - --bg-primary: #202225; - --bg-secondary: #1a1b1e; - --bg-tertiary: #121214; - --text-normal: #dddfe4; - --header-primary: #f5f5f7; - --header-secondary: #a0a4ad; - --interactive-normal: #a0a4ad; - --interactive-hover: #dddfe4; - --interactive-active: #f5f5f7; - --brand-experiment: #5865f2; - --brand-experiment-hover: #4752c4; - --input-background: #252529; - --danger: #ed4245; - --background-modifier-hover: rgba(78, 80, 88, 0.15); - --background-modifier-active: rgba(78, 80, 88, 0.3); - --background-modifier-selected: rgba(78, 80, 88, 0.4); - --div-border: #111214; - - --text-feedback-warning: hsl(38.455, 100%, 43.137%); -} - - -/* ============================================ - ONYX THEME (theme-midnight) - ============================================ */ -.theme-midnight { - /* Backgrounds */ - --background-base-low: #0c0c14; - --background-base-lower: #080810; - --background-base-lowest: #000000; - --background-surface-high: #141422; - --background-surface-higher: #1a1a2e; - --background-surface-highest: #202038; - --chat-background: #000000; - --channeltextarea-background: #1a1a2e; - --modal-background: #141422; - --panel-bg: #0c0c14; - --embed-background: #161626; - - /* Text */ - --text-default: #e0def0; - --text-strong: #f8f8fc; - --text-muted: #7a7687; - --text-subtle: #a8a5b5; - --text-link: #00a8fc; - --channels-default: #7a7687; - --text-feedback-critical: #ed4245; - - /* Interactive */ - --interactive-background-hover: rgba(78, 73, 106, 0.2); - --interactive-background-active: rgba(78, 73, 106, 0.36); - --interactive-background-selected: rgba(78, 73, 106, 0.48); - --interactive-icon-default: #a8a5b5; - --interactive-icon-hover: #e0def0; - --interactive-icon-active: #f8f8fc; - --interactive-text-default: #a8a5b5; - --interactive-text-hover: #e0def0; - --interactive-text-active: #f8f8fc; - - /* Borders */ - --border-subtle: rgba(255, 255, 255, 0.2); - --border-muted: rgba(255, 255, 255, 0.16); - --border-normal: rgba(255, 255, 255, 0.24); - --border-strong: rgba(255, 255, 255, 0.44); - --app-frame-border: hsla(240, 4%, 60.784%, 0.122); - - /* Icons */ - --icon-default: #e0def0; - --icon-strong: #f8f8fc; - --icon-muted: #7a7687; - --icon-subtle: #a8a5b5; - - /* Controls */ - --control-primary-background-default: #5865f2; - --control-primary-background-hover: #4752c4; - --control-primary-background-active: #3b43a8; - --control-critical-primary-background-default: #ed4245; - - /* Input */ - --input-background-default: #1a1a2e; - --input-border-default: rgba(255, 255, 255, 0.24); - --input-text-default: #e0def0; - - /* Scrollbar */ - --scrollbar-auto-thumb: #1a1a2e; - --scrollbar-thin-thumb: #1a1a2e; - - /* Message */ - --message-background-hover: rgba(0, 0, 0, 0.12); - - /* Compatibility aliases */ - --bg-primary: #0c0c14; - --bg-secondary: #080810; - --bg-tertiary: #000000; - --text-normal: #e0def0; - --header-primary: #f8f8fc; - --header-secondary: #a8a5b5; - --interactive-normal: #a8a5b5; - --interactive-hover: #e0def0; - --interactive-active: #f8f8fc; - --brand-experiment: #5865f2; - --brand-experiment-hover: #4752c4; - --input-background: #1a1a2e; - --danger: #ed4245; - --background-modifier-hover: rgba(78, 73, 106, 0.2); - --background-modifier-active: rgba(78, 73, 106, 0.36); - --background-modifier-selected: rgba(78, 73, 106, 0.48); - --div-border: #080810; - - --text-feedback-warning: hsl(38.455, 100%, 43.137%); -} - - -/* ============================================ - MOBILE OVERRIDES – Dark theme only - Darker, more unified backgrounds when .is-mobile is active. - Override --bg-tertiary so inline styles using var(--bg-tertiary) - (e.g. .channel-list in Sidebar.jsx) also pick up the new color. - ============================================ */ - -/* Override the variable so all var(--bg-tertiary) usages resolve to #1C1D22 */ -.theme-dark .is-mobile { - --bg-tertiary: #1C1D22; -} - -/* Server list needs a different (darker) color than --bg-tertiary */ -.theme-dark .is-mobile .server-list { - background-color: #141318; -} - -/* Remove borders on channel panel and DM view (inline styles, needs !important) */ -.theme-dark .is-mobile .channel-panel, -.theme-dark .is-mobile .channel-list { - border-left: none !important; -} - -/* Chat area/header use var(--bg-primary), not --bg-tertiary, so override explicitly */ -.theme-dark .is-mobile .chat-container, -.theme-dark .is-mobile .chat-area, -.theme-dark .is-mobile .chat-input-form, -.theme-dark .mobile-members-screen, -.theme-dark .mobile-search-screen { - background-color: #1C1D22; -} - -.theme-dark .is-mobile .chat-header { - background-color: #1C1D22; - border-bottom: 1px solid var(--app-frame-border); -} - -.theme-dark .is-mobile .chat-header.voice-header { - background-color: #000; -} - -.theme-dark .is-mobile .chat-input-wrapper { - background-color: #26262E; -} diff --git a/packages/shared/src/utils/emojiLookup.ts b/packages/shared/src/utils/emojiLookup.ts new file mode 100644 index 0000000..3eb8e28 --- /dev/null +++ b/packages/shared/src/utils/emojiLookup.ts @@ -0,0 +1,70 @@ +import emojiData from '@app/data/emojis.json'; + +interface EmojiEntry { + names: string[]; + surrogates: string; +} + +/** + * Build a shortcode → unicode map once at module load. Old messages + * store reactions by shortcode (e.g. `"fire"`); the new send path + * uses unicode surrogates (e.g. `"🔥"`). Both must render as + * Twemoji images, so we normalise every emojiKey through this lookup + * at display time. + */ +const NAME_TO_SURROGATES: Record<string, string> = (() => { + const map: Record<string, string> = {}; + const data = emojiData as unknown as Record<string, EmojiEntry[]>; + for (const key of Object.keys(data)) { + for (const entry of data[key] ?? []) { + for (const name of entry.names) { + map[name] = entry.surrogates; + } + } + } + return map; +})(); + +/** + * Given a reaction key (shortcode OR unicode), return the unicode + * surrogates. Returns the input unchanged if it already looks like + * unicode or is an unknown shortcode. + */ +export function resolveReactionKeyToUnicode(key: string): string { + if (!key) return key; + // Heuristic: shortcode is all ASCII letters/digits/underscores. + // Anything else (emoji characters, mxc URLs, etc.) passes through. + if (/^[a-zA-Z0-9_]+$/.test(key)) { + return NAME_TO_SURROGATES[key] ?? key; + } + return key; +} + +/** + * Lookup the canonical shortcode for a unicode emoji (used when + * sending reactions — the server stores by shortcode historically). + */ +export function unicodeToShortcode(surrogates: string): string | null { + const data = emojiData as unknown as Record<string, EmojiEntry[]>; + for (const key of Object.keys(data)) { + for (const entry of data[key] ?? []) { + if (entry.surrogates === surrogates) { + return entry.names[0] ?? null; + } + } + } + return null; +} + +/** + * Friendly label for a reaction key — used in the hover tooltip and + * the reactions modal. Custom shortcodes pass through unchanged; + * unicode surrogates are reverse-looked-up to their canonical + * shortcode (e.g. `👍` → `thumbsup`). Unknown unicode falls back to + * the raw emoji character. + */ +export function reactionKeyToName(key: string): string { + if (!key) return key; + if (/^[a-zA-Z0-9_]+$/.test(key)) return key; + return unicodeToShortcode(key) ?? key; +} diff --git a/packages/shared/src/utils/twemoji.ts b/packages/shared/src/utils/twemoji.ts new file mode 100644 index 0000000..9a57bbf --- /dev/null +++ b/packages/shared/src/utils/twemoji.ts @@ -0,0 +1,21 @@ +/** + * Twemoji CDN helpers. The new UI renders unicode emoji via + * Twemoji 14 SVGs from jsdelivr — no npm package, just an <img> + * pointed at the CDN. + */ + +export function emojiToCodepoint(emoji: string): string { + const codepoints: string[] = []; + for (let i = 0; i < emoji.length; i++) { + const code = emoji.codePointAt(i); + if (code === undefined) continue; + if (code === 0xfe0f) continue; // skip variation selector + codepoints.push(code.toString(16)); + if (code > 0xffff) i++; // skip surrogate pair + } + return codepoints.join('-'); +} + +export function getTwemojiUrl(emoji: string): string { + return `https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/svg/${emojiToCodepoint(emoji)}.svg`; +} diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json new file mode 100644 index 0000000..089549b --- /dev/null +++ b/packages/shared/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "paths": { + "@discord-clone/ui": ["../ui/src"], + "@discord-clone/constants": ["../constants/src"] + } + }, + "include": ["src"] +} diff --git a/packages/ui/package.json b/packages/ui/package.json new file mode 100644 index 0000000..88bf1f2 --- /dev/null +++ b/packages/ui/package.json @@ -0,0 +1,18 @@ +{ + "name": "@discord-clone/ui", + "private": true, + "version": "1.0.0", + "type": "module", + "main": "src/index.ts", + "dependencies": { + "@phosphor-icons/react": "^2.1.7", + "clsx": "^2.1.1", + "framer-motion": "^11.0.0", + "@floating-ui/react": "^0.26.0", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "typescript": "^5.7.0" + } +} diff --git a/packages/ui/src/Avatar.module.css b/packages/ui/src/Avatar.module.css new file mode 100644 index 0000000..b8b6543 --- /dev/null +++ b/packages/ui/src/Avatar.module.css @@ -0,0 +1,51 @@ +.wrapper { + position: relative; + display: inline-flex; + flex-shrink: 0; +} + +.image { + border-radius: 50%; + object-fit: cover; + width: 100%; + height: 100%; +} + +.fallback { + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-weight: 600; + color: #fff; +} + +.statusBadge { + position: absolute; + border-radius: 50%; + border-style: solid; + /* The badge border matches whatever surface the avatar sits on + so it reads as a cut-out from the background. Default targets + the sidebar surface (`--background-secondary`), but any parent + can override via the `--avatar-status-border` custom property + — see MobileYouPage / MobileMemberProfileSheet, where the + profile sits on the darker `--background-primary` surface. */ + border-color: var(--avatar-status-border, var(--background-secondary, #2b2d31)); + box-sizing: content-box; +} + +.online { + background: var(--status-online, #23a55a); +} + +.idle { + background: var(--status-idle, #f0b232); +} + +.dnd { + background: var(--status-dnd, #f23f43); +} + +.offline { + background: var(--status-offline, #80848e); +} diff --git a/packages/ui/src/Avatar.tsx b/packages/ui/src/Avatar.tsx new file mode 100644 index 0000000..03efff2 --- /dev/null +++ b/packages/ui/src/Avatar.tsx @@ -0,0 +1,93 @@ +import clsx from 'clsx'; +import type { CSSProperties } from 'react'; +import styles from './Avatar.module.css'; + +export interface AvatarProps { + src?: string | null; + alt?: string; + size?: number; + fallback?: string; + status?: 'online' | 'idle' | 'dnd' | 'offline'; + className?: string; + style?: CSSProperties; + onClick?: () => void; +} + +function getInitials(name: string): string { + return name + .split(/\s+/) + .map((w) => w[0]) + .join('') + .toUpperCase() + .slice(0, 2); +} + +function stringToColor(str: string): string { + let hash = 0; + for (let i = 0; i < str.length; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash); + } + const hue = Math.abs(hash % 360); + return `hsl(${hue}, 60%, 50%)`; +} + +function getStatusGeometry(avatarSize: number): { dotSize: number; borderWidth: number; offset: number } { + if (avatarSize <= 16) return { dotSize: 8, borderWidth: 0, offset: -1 }; + if (avatarSize <= 20) return { dotSize: 8, borderWidth: 0, offset: -1 }; + if (avatarSize <= 24) return { dotSize: 10, borderWidth: 2, offset: -2 }; + if (avatarSize <= 32) return { dotSize: 10, borderWidth: 3, offset: -3 }; + if (avatarSize <= 36) return { dotSize: 10, borderWidth: 3, offset: -3 }; + if (avatarSize <= 40) return { dotSize: 12, borderWidth: 3, offset: -3 }; + if (avatarSize <= 48) return { dotSize: 14, borderWidth: 3, offset: -3 }; + if (avatarSize <= 56) return { dotSize: 16, borderWidth: 3, offset: -3 }; + if (avatarSize <= 80) return { dotSize: 16, borderWidth: 6, offset: -4 }; + return { dotSize: 24, borderWidth: 8, offset: -6 }; +} + +export function Avatar({ src, alt = '', size = 40, fallback, status, className, style, onClick }: AvatarProps) { + const initials = fallback ? getInitials(fallback) : alt ? getInitials(alt) : '?'; + const bgColor = stringToColor(fallback || alt || '?'); + const geo = getStatusGeometry(size); + + return ( + <div + className={clsx(styles.wrapper, className)} + style={{ + width: size, + height: size, + cursor: onClick ? 'pointer' : undefined, + ...style, + }} + onClick={onClick} + > + {src ? ( + <img src={src} alt={alt} className={styles.image} /> + ) : ( + <div + className={styles.fallback} + style={{ + width: size, + height: size, + backgroundColor: bgColor, + fontSize: size * 0.4, + userSelect: 'none', + }} + > + {initials} + </div> + )} + {status && ( + <div + className={clsx(styles.statusBadge, styles[status])} + style={{ + width: geo.dotSize, + height: geo.dotSize, + borderWidth: geo.borderWidth, + bottom: geo.offset, + right: geo.offset, + }} + /> + )} + </div> + ); +} diff --git a/packages/ui/src/BottomSheet.module.css b/packages/ui/src/BottomSheet.module.css new file mode 100644 index 0000000..3f0dc8a --- /dev/null +++ b/packages/ui/src/BottomSheet.module.css @@ -0,0 +1,109 @@ +.overlay { + position: fixed; + inset: 0; + z-index: var(--z-index-modal, 10000); + display: flex; + flex-direction: column; + justify-content: flex-end; + pointer-events: none; +} + +.backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.55); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + pointer-events: auto; +} + +.sheet { + position: relative; + width: 100%; + /* Fill 95% of the viewport by default so drawers feel like full-screen + sheets on mobile (matches Fluxer). Uses svh so the mobile browser + chrome (address bar) doesn't cause the sheet to overflow. */ + height: 95svh; + max-height: 95svh; + /* Use the sidebar surface colour so the drawer sits one step darker + than the chat area, making the rounded action items pop against + the sheet background. */ + background-color: var(--background-secondary); + border-top-left-radius: 20px; + border-top-right-radius: 20px; + box-shadow: 0 -12px 32px rgba(0, 0, 0, 0.4); + display: flex; + flex-direction: column; + overflow: hidden; + pointer-events: auto; + padding-bottom: env(safe-area-inset-bottom, 0); + touch-action: pan-y; +} + +.handleRow { + display: flex; + align-items: center; + justify-content: center; + padding: 8px 0 4px; + flex-shrink: 0; + cursor: grab; +} + +.handle { + width: 40px; + height: 4px; + border-radius: 2px; + background-color: var(--text-tertiary, rgba(255, 255, 255, 0.25)); + opacity: 0.6; +} + +.header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 8px 20px 16px; + border-bottom: 1px solid var(--user-area-divider-color, rgba(255, 255, 255, 0.06)); + flex-shrink: 0; +} + +.title { + font-size: 1rem; + font-weight: 600; + color: var(--text-primary); + margin: 0; + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: center; +} + +.closeButton { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + flex-shrink: 0; + background: transparent; + border: none; + color: var(--text-secondary); + border-radius: 50%; + cursor: pointer; + transition: background-color 0.15s, color 0.15s; +} + +.closeButton:hover { + background-color: var(--background-modifier-hover, rgba(255, 255, 255, 0.04)); + color: var(--text-primary); +} + +.body { + flex: 1; + min-height: 0; + overflow-y: auto; + overflow-x: hidden; + -webkit-overflow-scrolling: touch; +} diff --git a/packages/ui/src/BottomSheet.tsx b/packages/ui/src/BottomSheet.tsx new file mode 100644 index 0000000..e2216bc --- /dev/null +++ b/packages/ui/src/BottomSheet.tsx @@ -0,0 +1,258 @@ +import { useEffect, useCallback, useState, type ReactNode } from 'react'; +import { createPortal } from 'react-dom'; +import { AnimatePresence, motion } from 'framer-motion'; +import { X } from '@phosphor-icons/react'; +import clsx from 'clsx'; +import styles from './BottomSheet.module.css'; + +/** + * BottomSheet — a mobile-style slide-up drawer. Portal-based, backdrop + * + Escape + click-outside close, drag handle, safe-area-inset aware. + * + * Usage: + * <BottomSheet isOpen={open} onClose={close} title="Search"> + * ...body... + * </BottomSheet> + * + * By default the sheet snaps to 95svh and scrolls its body internally. + * Callers that need a partial-height drawer can pass `initialHeightSvh` + * (e.g. 50 for half-screen) and `expandable` to allow the user to + * drag the handle UP to expand the sheet to full size. + * + * Callers that need a custom header layout can pass `disableDefaultHeader` + * and render their own header as the first child. + */ +export interface BottomSheetProps { + isOpen: boolean; + onClose: () => void; + /** Title shown in the default header. Ignored when disableDefaultHeader is true. */ + title?: string; + /** Render the title-X header strip. Default true. */ + disableDefaultHeader?: boolean; + /** Show the top drag handle strip. Default true. */ + showHandle?: boolean; + /** Close when the backdrop or drag handle is clicked. Default true. */ + dismissible?: boolean; + /** + * Whether the sheet can be dragged at all (to close or to expand). + * When false, the sheet is a pure open/closed component: no drag + * gestures are attached, no intermediate snap points exist, and + * the only ways to dismiss it are the backdrop click, the Escape + * key, or the owner calling `onClose` explicitly. Default true. + */ + draggable?: boolean; + /** Optional extra class for the sheet container. */ + className?: string; + /** + * Initial visible portion of the viewport in svh units (0–95). + * Default 95 (the full sheet shown). When set lower (e.g. 50), the + * sheet animates in to that partial height. The user can still + * drag down to dismiss; pair with `expandable` to also let them + * drag UP to grow it. + */ + initialHeightSvh?: number; + /** + * When true and `initialHeightSvh` is less than 95, the user can + * drag the sheet UP from its initial position to the fully open + * (95svh) snap point. Default false. + */ + expandable?: boolean; + /** + * Optional z-index override for the backdrop + sheet overlay. + * Needed when the sheet is opened on top of another portaled + * surface that already claims a high z-index (e.g. the + * ImageLightbox backdrop at 16000). Leave unset to inherit the + * global `--z-index-modal` token. + */ + zIndex?: number; + children: ReactNode; +} + +export function BottomSheet({ + isOpen, + onClose, + title, + disableDefaultHeader = false, + showHandle = true, + dismissible = true, + draggable = true, + className, + initialHeightSvh, + expandable = false, + zIndex, + children, +}: BottomSheetProps) { + const handleEscape = useCallback( + (e: KeyboardEvent) => { + if (e.key === 'Escape' && dismissible) onClose(); + }, + [onClose, dismissible], + ); + + useEffect(() => { + if (!isOpen) return; + document.addEventListener('keydown', handleEscape); + return () => document.removeEventListener('keydown', handleEscape); + }, [isOpen, handleEscape]); + + // Lock body scroll while the sheet is open — keeps the underlying page + // from scrolling behind the drawer on mobile. + useEffect(() => { + if (!isOpen) return; + const prev = document.body.style.overflow; + document.body.style.overflow = 'hidden'; + return () => { + document.body.style.overflow = prev; + }; + }, [isOpen]); + + // ── Snap-point logic ──────────────────────────────────────────────── + // Track the live viewport height in pixels so we can convert the + // svh-based snap props into px values for Framer Motion's drag. + // We resize-listen so rotating the device or showing/hiding the + // mobile browser chrome doesn't leave the sheet stuck at a stale + // pixel snap. + const [vh, setVh] = useState(() => + typeof window !== 'undefined' ? window.innerHeight : 0, + ); + useEffect(() => { + const onResize = () => setVh(window.innerHeight); + window.addEventListener('resize', onResize); + return () => window.removeEventListener('resize', onResize); + }, []); + + // The sheet's CSS height is 95svh. translate-Y of 0 means it's + // fully open (95svh visible). Translating DOWN by N pixels hides + // N pixels worth of the sheet's top portion off the bottom of the + // viewport. + // + // snapInitialPx — translation needed to show only `initialHeightSvh` + // of viewport height + // snapFullPx — 0 (sheet fully open) + // snapClosedPx — vh (sheet fully off-screen) + const partial = initialHeightSvh !== undefined && initialHeightSvh < 95; + const snapInitialPx = partial ? Math.max(0, vh * (1 - (initialHeightSvh as number) / 95)) : 0; + const snapFullPx = 0; + const snapClosedPx = vh; + + // Current snap state — only matters when the sheet is partial AND + // expandable, since otherwise there's only one snap target. + type SnapName = 'initial' | 'full'; + const [snap, setSnap] = useState<SnapName>('initial'); + + // Reset to the initial snap whenever the sheet (re-)opens so a + // previously-expanded sheet doesn't come back already expanded. + useEffect(() => { + if (isOpen) setSnap('initial'); + }, [isOpen]); + + const targetY = snap === 'full' ? snapFullPx : snapInitialPx; + + return createPortal( + <AnimatePresence> + {isOpen && ( + <div + className={styles.overlay} + style={zIndex !== undefined ? { zIndex } : undefined} + > + {/* Backdrop */} + <motion.div + className={styles.backdrop} + initial={{ opacity: 0 }} + animate={{ opacity: 1 }} + exit={{ opacity: 0 }} + transition={{ duration: 0.18 }} + onClick={dismissible ? onClose : undefined} + /> + + {/* Sheet surface */} + <motion.div + className={clsx(styles.sheet, className)} + initial={{ y: '100%' }} + animate={{ y: targetY }} + exit={{ y: '100%' }} + transition={{ type: 'spring', stiffness: 400, damping: 36 }} + onClick={(e) => e.stopPropagation()} + drag={dismissible && draggable ? 'y' : false} + dragConstraints={{ + // Allow dragging UP to the fully-open snap when + // the sheet is partial and expandable; otherwise + // pin top at the initial position. + top: partial && expandable ? snapFullPx - targetY : 0, + bottom: snapClosedPx - targetY, + }} + dragElastic={{ top: partial && expandable ? 0.05 : 0, bottom: 0.2 }} + onDragEnd={(_, info) => { + const finalY = targetY + info.offset.y; + + // Velocity-driven flicks first — they win over + // position-based heuristics so a fast swipe + // always does the obvious thing. + if (info.velocity.y > 600) { + // Strong downward flick → close. + onClose(); + return; + } + if (partial && expandable && info.velocity.y < -600) { + // Strong upward flick → expand to full. + setSnap('full'); + return; + } + + // Position-based snap fallback. + if (!partial) { + // Original behavior — close if dragged + // down past the threshold, otherwise + // spring back to fully open. + if (info.offset.y > 120) onClose(); + return; + } + + // Partial sheet position-based snap: + // - past the initial position by half its + // distance to closed → close + // - between snaps → snap to nearest + // - above the initial position by 1/3 the + // distance to full → expand + const closeThreshold = snapInitialPx + (snapClosedPx - snapInitialPx) * 0.4; + const expandThreshold = snapInitialPx - snapInitialPx * 0.33; + + if (finalY > closeThreshold) { + onClose(); + return; + } + if (expandable && finalY < expandThreshold) { + setSnap('full'); + return; + } + setSnap('initial'); + }} + > + {showHandle && ( + <div className={styles.handleRow}> + <div className={styles.handle} /> + </div> + )} + + {!disableDefaultHeader && title && ( + <div className={styles.header}> + <h2 className={styles.title}>{title}</h2> + <button + type="button" + className={styles.closeButton} + onClick={onClose} + aria-label="Close" + > + <X size={20} weight="bold" /> + </button> + </div> + )} + + <div className={styles.body}>{children}</div> + </motion.div> + </div> + )} + </AnimatePresence>, + document.body, + ); +} diff --git a/packages/ui/src/Button.module.css b/packages/ui/src/Button.module.css new file mode 100644 index 0000000..6a9d39f --- /dev/null +++ b/packages/ui/src/Button.module.css @@ -0,0 +1,270 @@ +.button { + display: flex; + align-items: center; + justify-content: center; + padding: 10px 16px; + font-size: 14px; + font-weight: 600; + line-height: 20px; + height: 44px; + min-height: 44px; + min-width: 96px; + border-radius: 8px; + border: none; + cursor: pointer; + appearance: none; + text-decoration: none; + position: relative; + overflow: hidden; + transition: + background-color 0.15s ease, + color 0.15s ease, + border-color 0.15s ease; +} + +.button:disabled { + cursor: not-allowed; + opacity: 0.5; +} + +.button.small { + height: 40px; + min-height: 40px; + min-width: 60px; + padding: 8px 12px; +} + +.button.compact { + height: 32px; + min-height: 32px; + min-width: 60px; + padding: 6px 12px; +} + +.button.superCompact { + height: 24px; + min-height: 24px; + min-width: 0; + padding: 4px; + font-size: 12px; + line-height: 16px; + border-radius: 6px; +} + +.button.fitContent { + min-width: 0; + padding: 10px 16px; +} + +.button.superCompact.fitContent { + padding: 4px 8px; +} + +.button.fitContainer { + width: 100%; +} + +.button.square { + width: 44px; + min-width: 44px; + padding: 10px; +} + +.button.square.small { + width: 40px; + min-width: 40px; + padding: 8px; +} + +.button.square.compact { + width: 32px; + min-width: 32px; + padding: 6px; +} + +/* ── Variant: Primary ── */ + +.button.primary { + background-color: var(--brand-primary); + color: var(--brand-primary-fill); +} + +.button.primary:hover:not(:disabled) { + background-color: var(--brand-secondary); +} + +.button.primary:active:not(:disabled) { + background-color: color-mix(in srgb, var(--brand-primary) 90%, #000); +} + +/* ── Variant: Secondary ── */ + +.button.secondary { + background-color: var(--background-tertiary); + color: var(--button-secondary-text); +} + +:global(.theme-light) .button.secondary { + background-color: var(--background-modifier-hover); + color: var(--button-ghost-text); +} + +.button.secondary:hover:not(:disabled) { + background-color: color-mix(in srgb, var(--background-tertiary), #fff 4%); + color: var(--button-secondary-active-text); +} + +:global(.theme-light) .button.secondary:hover:not(:disabled) { + background-color: color-mix(in srgb, var(--background-modifier-hover), #fff 4%); + color: var(--button-ghost-text); +} + +/* ── Variant: Danger Primary ── */ + +.button.dangerPrimary { + background-color: var(--button-danger-fill); + color: var(--button-danger-text); +} + +.button.dangerPrimary:hover:not(:disabled) { + background-color: var(--button-danger-active-fill); +} + +.button.dangerPrimary:active:not(:disabled) { + background-color: color-mix(in srgb, var(--button-danger-fill) 85%, #000); +} + +/* ── Variant: Danger Secondary ── */ + +.button.dangerSecondary { + background-color: color-mix(in srgb, var(--button-danger-fill) 12%, transparent); + color: var(--button-danger-outline-text); +} + +.button.dangerSecondary:hover:not(:disabled) { + background-color: color-mix(in srgb, var(--button-danger-fill) 20%, transparent); +} + +.button.dangerSecondary:active:not(:disabled) { + background-color: color-mix(in srgb, var(--button-danger-fill) 26%, transparent); +} + +/* ── Variant: Ghost ── */ + +.button.ghost { + background-color: transparent; + color: var(--button-ghost-text, var(--text-primary)); +} + +.button.ghost:hover:not(:disabled) { + background-color: var(--background-modifier-hover); +} + +.button.ghost:active:not(:disabled) { + background-color: var(--background-modifier-active); +} + +/* ── Variant: Link ── */ + +.button.link { + background-color: transparent; + color: var(--brand-primary); + min-width: 0; + height: auto; + min-height: auto; + padding: 0; + font-weight: 500; +} + +.button.link:hover:not(:disabled) { + text-decoration: underline; +} + +/* ── Spinner (pulsing ellipsis) ── */ + +.spinner { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; +} + +.spinnerInner { + display: flex; + align-items: center; + justify-content: center; + position: relative; + width: 28px; + height: auto; +} + +.spinnerItem { + display: inline-block; + width: 6px; + height: 6px; + margin-right: 2px; + background-color: hsl(0, 0%, 100%); + border-radius: 4px; + opacity: 0.3; + animation: spinnerPulsingEllipsis 1.4s ease-in-out infinite; +} + +:global(.theme-light) .button.secondary .spinnerItem { + background-color: #000; +} + +.spinnerItem:nth-of-type(2) { + animation-delay: 0.2s; +} + +.spinnerItem:nth-of-type(3) { + animation-delay: 0.4s; +} + +/* ── Layout helpers ── */ + +.iconWrapper { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + white-space: nowrap; +} + +.spinnerWrapper { + display: flex; + align-items: center; + justify-content: center; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.hidden { + opacity: 0; + pointer-events: none; +} + +.grid { + display: grid; + width: 100%; + height: 100%; +} + +@keyframes spinnerPulsingEllipsis { + 0% { + opacity: 1; + transform: scale(1); + } + 50% { + opacity: 0.3; + transform: scale(0.8); + } + 100% { + opacity: 1; + transform: scale(1); + } +} diff --git a/packages/ui/src/Button.tsx b/packages/ui/src/Button.tsx new file mode 100644 index 0000000..3b1ceb4 --- /dev/null +++ b/packages/ui/src/Button.tsx @@ -0,0 +1,108 @@ +import clsx from 'clsx'; +import React from 'react'; +import type { ButtonHTMLAttributes, ReactNode } from 'react'; +import styles from './Button.module.css'; + +export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> { + variant?: 'primary' | 'secondary' | 'danger' | 'dangerSecondary' | 'ghost' | 'link'; + size?: 'sm' | 'md' | 'lg'; + loading?: boolean; + icon?: ReactNode; + fitContainer?: boolean; + fitContent?: boolean; + square?: boolean; + children?: ReactNode; +} + +const variantClassMap: Record<string, string> = { + primary: 'primary', + secondary: 'secondary', + danger: 'dangerPrimary', + dangerSecondary: 'dangerSecondary', + ghost: 'ghost', + link: 'link', +}; + +const sizeClassMap: Record<string, string | undefined> = { + sm: 'compact', + md: undefined, + lg: undefined, +}; + +export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( + ( + { + variant = 'primary', + size = 'md', + loading = false, + icon, + fitContainer = false, + fitContent = false, + square = false, + children, + className, + disabled, + type = 'button', + onClick, + ...props + }, + ref, + ) => { + const variantClass = variantClassMap[variant] ?? 'primary'; + const sizeClass = sizeClassMap[size]; + + const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { + if (loading) { + event.preventDefault(); + return; + } + onClick?.(event); + }; + + return ( + <button + ref={ref} + className={clsx( + styles.button, + styles[variantClass], + { + [styles.compact]: sizeClass === 'compact', + [styles.square]: square, + [styles.fitContainer]: fitContainer, + [styles.fitContent]: fitContent, + }, + className, + )} + disabled={disabled || loading} + type={type} + onClick={handleClick} + tabIndex={disabled ? -1 : 0} + {...props} + > + <div className={styles.grid}> + <div className={clsx(styles.iconWrapper, { [styles.hidden]: loading })}> + {square ? ( + icon + ) : ( + <> + {icon} + {children} + </> + )} + </div> + <div className={clsx(styles.spinnerWrapper, { [styles.hidden]: !loading })}> + <span className={styles.spinner}> + <span className={styles.spinnerInner}> + <span className={styles.spinnerItem} /> + <span className={styles.spinnerItem} /> + <span className={styles.spinnerItem} /> + </span> + </span> + </div> + </div> + </button> + ); + }, +); + +Button.displayName = 'Button'; diff --git a/packages/ui/src/Modal.module.css b/packages/ui/src/Modal.module.css new file mode 100644 index 0000000..43898b0 --- /dev/null +++ b/packages/ui/src/Modal.module.css @@ -0,0 +1,119 @@ +.backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.35); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + z-index: var(--z-index-modal, 10000); +} + +.layer { + position: fixed; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + padding: 24px; + z-index: var(--z-index-modal, 10000); +} + +.root { + position: relative; + margin: 0 auto; + background-color: var(--background-secondary, #2b2d31); + border: 1px solid var(--background-header-secondary, #1e1f22); + border-radius: 8px; + box-shadow: + 0 0 0 1px hsla(223, 7%, 20%, 0.08), + 0 8px 24px -4px rgba(0, 0, 0, 0.25), + 0 20px 48px -8px rgba(0, 0, 0, 0.2); + max-height: calc(100vh - 48px); + display: flex; + flex-direction: column; + overflow: hidden; + pointer-events: all; +} + +/* Size variants */ +.small { + width: 440px; +} + +.medium { + width: 600px; +} + +.large { + width: 800px; +} + +/* Header */ +.header { + flex: 0 0 auto; + display: flex; + align-items: center; + padding: 16px; + gap: 14px; + border-bottom: 1px solid var(--background-modifier-accent, #3f4147); +} + +.headerTitle { + font-size: 1.25rem; + font-weight: 700; + color: var(--text-primary, #dbdee1); + flex: 1; + margin: 0; + line-height: 1.25; +} + +.closeButton { + width: 28px; + height: 28px; + border-radius: 9999px; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; + color: var(--text-primary-muted, #949ba4); + cursor: pointer; + opacity: 0.5; + transition: opacity 0.15s ease, color 0.15s ease; + padding: 0; + flex-shrink: 0; + -webkit-tap-highlight-color: transparent; +} + +.closeButton:hover { + opacity: 1; + color: var(--text-primary, #dbdee1); +} + +.closeButton:active { + opacity: 0.8; +} + +/* Content */ +.content { + flex: 1 1 auto; + overflow-y: auto; + padding: 0 16px 16px; + -webkit-overflow-scrolling: touch; + overscroll-behavior: contain; +} + +/* Footer */ +.footer { + flex: 0 0 auto; + display: flex; + gap: 8px; + padding: 16px; + justify-content: flex-end; + border-top: 1px solid var(--background-modifier-accent, #3f4147); +} + +/* Separator */ +.separator { + height: 1px; + background: var(--background-modifier-accent, #3f4147); +} diff --git a/packages/ui/src/Modal.tsx b/packages/ui/src/Modal.tsx new file mode 100644 index 0000000..78e29b1 --- /dev/null +++ b/packages/ui/src/Modal.tsx @@ -0,0 +1,190 @@ +import { useEffect, useCallback, type ReactNode } from 'react'; +import { createPortal } from 'react-dom'; +import { AnimatePresence, motion } from 'framer-motion'; +import { X } from '@phosphor-icons/react'; +import clsx from 'clsx'; +import styles from './Modal.module.css'; + +// --------------------------------------------------------------------------- +// Subcomponent types +// --------------------------------------------------------------------------- + +type ModalSize = 'small' | 'medium' | 'large'; + +interface ModalRootProps { + isOpen: boolean; + onClose: () => void; + size?: ModalSize; + children: ReactNode; + className?: string; + /** + * Optional z-index override for the backdrop + layer. Needed + * when the modal is opened on top of another portaled surface + * that already claims a high z-index (e.g. the ImageLightbox + * backdrop at 16000). Leave unset to inherit the global + * `--z-index-modal` token. + */ + zIndex?: number; +} + +interface ModalHeaderProps { + title: string; + onClose?: () => void; + children?: ReactNode; + className?: string; +} + +interface ModalContentProps { + children: ReactNode; + className?: string; +} + +interface ModalFooterProps { + children: ReactNode; + className?: string; +} + +// --------------------------------------------------------------------------- +// Modal.Root -- Portal + backdrop + centred layer + surface +// --------------------------------------------------------------------------- + +function ModalRoot({ isOpen, onClose, size = 'small', children, className, zIndex }: ModalRootProps) { + const handleEscape = useCallback( + (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }, + [onClose], + ); + + useEffect(() => { + if (!isOpen) return; + document.addEventListener('keydown', handleEscape); + return () => document.removeEventListener('keydown', handleEscape); + }, [isOpen, handleEscape]); + + return createPortal( + <AnimatePresence> + {isOpen && ( + <> + {/* Backdrop — purely decorative. The click-to-close + handler lives on `.layer` below because `.layer` + is a sibling that paints on top of the backdrop + (both `position: fixed; inset: 0`), so clicks + never actually reach the backdrop in practice. */} + <motion.div + className={styles.backdrop} + initial={{ opacity: 0 }} + animate={{ opacity: 1 }} + exit={{ opacity: 0 }} + style={zIndex !== undefined ? { zIndex } : undefined} + /> + + {/* Centring layer — click-to-close target for empty + space around the modal body. The inner + `motion.div` calls `stopPropagation` so clicks + inside the modal content don't bubble up here. */} + <div + className={styles.layer} + style={zIndex !== undefined ? { zIndex } : undefined} + onClick={onClose} + > + <motion.div + className={clsx(styles.root, styles[size], className)} + initial={{ opacity: 0, scale: 0.95, y: 10 }} + animate={{ opacity: 1, scale: 1, y: 0 }} + exit={{ opacity: 0, scale: 0.95, y: 10 }} + transition={{ duration: 0.15 }} + onClick={(e) => e.stopPropagation()} + > + {children} + </motion.div> + </div> + </> + )} + </AnimatePresence>, + document.body, + ); +} + +// --------------------------------------------------------------------------- +// Modal.Header +// --------------------------------------------------------------------------- + +function ModalHeader({ title, onClose, children, className }: ModalHeaderProps) { + return ( + <div className={clsx(styles.header, className)}> + <h2 className={styles.headerTitle}>{title}</h2> + {children} + {onClose && ( + <button + type="button" + className={styles.closeButton} + onClick={onClose} + aria-label="Close" + > + <X size={18} weight="bold" /> + </button> + )} + </div> + ); +} + +// --------------------------------------------------------------------------- +// Modal.Content +// --------------------------------------------------------------------------- + +function ModalContent({ children, className }: ModalContentProps) { + return <div className={clsx(styles.content, className)}>{children}</div>; +} + +// --------------------------------------------------------------------------- +// Modal.Footer +// --------------------------------------------------------------------------- + +function ModalFooter({ children, className }: ModalFooterProps) { + return <div className={clsx(styles.footer, className)}>{children}</div>; +} + +// --------------------------------------------------------------------------- +// Backward-compatible default export +// --------------------------------------------------------------------------- + +export interface ModalProps { + isOpen: boolean; + onClose: () => void; + children: ReactNode; + title?: string; + /** @deprecated Use `size` instead. Kept for backward compatibility. */ + width?: number; + size?: ModalSize; + className?: string; +} + +function sizeFromWidth(width: number | undefined): ModalSize { + if (width === undefined) return 'small'; + if (width >= 800) return 'large'; + if (width >= 600) return 'medium'; + return 'small'; +} + +function ModalCompat({ isOpen, onClose, children, title, width, size, className }: ModalProps) { + const resolvedSize = size ?? sizeFromWidth(width); + + return ( + <ModalRoot isOpen={isOpen} onClose={onClose} size={resolvedSize} className={className}> + {title && <ModalHeader title={title} onClose={onClose} />} + <ModalContent>{children}</ModalContent> + </ModalRoot> + ); +} + +// --------------------------------------------------------------------------- +// Compose the public API: Modal + Modal.Root / Header / Content / Footer +// --------------------------------------------------------------------------- + +export const Modal = Object.assign(ModalCompat, { + Root: ModalRoot, + Header: ModalHeader, + Content: ModalContent, + Footer: ModalFooter, +}); diff --git a/packages/ui/src/Spinner.tsx b/packages/ui/src/Spinner.tsx new file mode 100644 index 0000000..5106e3b --- /dev/null +++ b/packages/ui/src/Spinner.tsx @@ -0,0 +1,26 @@ +export interface SpinnerProps { + size?: number; + color?: string; +} + +export function Spinner({ size = 24, color = 'var(--text-secondary, #b5bac1)' }: SpinnerProps) { + return ( + <svg + width={size} + height={size} + viewBox="0 0 24 24" + style={{ animation: 'spin 0.8s linear infinite' }} + > + <circle + cx="12" + cy="12" + r="10" + stroke={color} + strokeWidth="3" + fill="none" + strokeLinecap="round" + strokeDasharray="50 30" + /> + </svg> + ); +} diff --git a/packages/ui/src/Toast.tsx b/packages/ui/src/Toast.tsx new file mode 100644 index 0000000..7116ebb --- /dev/null +++ b/packages/ui/src/Toast.tsx @@ -0,0 +1,71 @@ +import { AnimatePresence, motion } from 'framer-motion'; +import type { ReactNode } from 'react'; + +export interface ToastData { + id: string; + message: ReactNode; + type?: 'info' | 'success' | 'error' | 'warning'; + duration?: number; +} + +export interface ToastProps { + toast: ToastData; + onDismiss: (id: string) => void; +} + +const TYPE_COLORS: Record<string, string> = { + info: 'var(--brand-primary, #5865f2)', + success: '#23a55a', + error: '#f23f43', + warning: '#f0b232', +}; + +export function Toast({ toast, onDismiss }: ToastProps) { + return ( + <motion.div + layout + initial={{ opacity: 0, y: 20, scale: 0.95 }} + animate={{ opacity: 1, y: 0, scale: 1 }} + exit={{ opacity: 0, y: -20, scale: 0.95 }} + style={{ + backgroundColor: 'var(--background-floating, #111214)', + color: 'var(--text-primary, #dbdee1)', + padding: '12px 16px', + borderRadius: 'var(--radius-md, 4px)', + boxShadow: '0 4px 12px rgba(0,0,0,0.3)', + display: 'flex', + alignItems: 'center', + gap: 12, + minWidth: 300, + maxWidth: 500, + borderLeft: `4px solid ${TYPE_COLORS[toast.type || 'info']}`, + cursor: 'pointer', + }} + onClick={() => onDismiss(toast.id)} + > + <div style={{ flex: 1, fontSize: '0.875rem' }}>{toast.message}</div> + </motion.div> + ); +} + +export function ToastContainer({ toasts, onDismiss }: { toasts: ToastData[]; onDismiss: (id: string) => void }) { + return ( + <div + style={{ + position: 'fixed', + bottom: 24, + right: 24, + zIndex: 'var(--z-index-toast, 50000)' as any, + display: 'flex', + flexDirection: 'column', + gap: 8, + }} + > + <AnimatePresence> + {toasts.map((toast) => ( + <Toast key={toast.id} toast={toast} onDismiss={onDismiss} /> + ))} + </AnimatePresence> + </div> + ); +} diff --git a/packages/ui/src/Tooltip.module.css b/packages/ui/src/Tooltip.module.css new file mode 100644 index 0000000..9bec418 --- /dev/null +++ b/packages/ui/src/Tooltip.module.css @@ -0,0 +1,54 @@ +.tooltip { + background: var(--background-floating, #111214); + color: var(--text-primary, #dbdee1); + padding: 6px 10px; + border-radius: var(--radius-md, 6px); + font-size: 14px; + font-weight: 500; + max-width: 220px; + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.24); + z-index: var(--z-index-tooltip, 45000); + pointer-events: none; + word-wrap: break-word; + line-height: 1.3; + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; +} + +.content { + text-align: center; +} + +.shortcutRow { + display: flex; + align-items: center; + gap: 3px; +} + +.keycap { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 20px; + height: 18px; + padding: 0 5px; + border-radius: 3px; + background: var(--background-secondary, #202225); + color: var(--text-primary-muted, #b5bac1); + font-size: 10px; + font-weight: 700; + line-height: 1; + letter-spacing: 0.03em; + text-transform: uppercase; + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.35); +} + +.arrow { + width: 8px; + height: 8px; + background: var(--background-floating, #111214); + transform: rotate(45deg); + position: absolute; +} diff --git a/packages/ui/src/Tooltip.tsx b/packages/ui/src/Tooltip.tsx new file mode 100644 index 0000000..0ed9c3a --- /dev/null +++ b/packages/ui/src/Tooltip.tsx @@ -0,0 +1,254 @@ +import { AnimatePresence, motion } from 'framer-motion'; +import { type ReactNode, cloneElement, isValidElement, useRef, useState, useCallback, useLayoutEffect } from 'react'; +import { createPortal } from 'react-dom'; +import styles from './Tooltip.module.css'; + +export interface TooltipProps { + content: ReactNode; + placement?: 'top' | 'bottom' | 'left' | 'right'; + children: ReactNode; + delay?: number; + /** + * Optional keyboard shortcut rendered as a row of keycaps under + * the main label. Accepts a lowercase `+`-joined combo string + * like `"ctrl+shift+m"` (the same format the KeybindStore uses). + * Empty / undefined hides the row entirely. + */ + shortcut?: string; + /** + * When true the tooltip stays open while the cursor is over the + * floating panel (not just the trigger), and clicks on the panel + * bubble through to `onContentClick`. Used for reaction chips + * where the tooltip doubles as a "click to view details" target. + */ + interactive?: boolean; + /** + * Fired when the user clicks the floating content. Only hooked + * up when `interactive` is true. + */ + onContentClick?: () => void; +} + +/** Split a KeybindStore-format combo into display tokens and label + * each one so the Tooltip can render them as individual keycaps. */ +function parseShortcut(combo: string): string[] { + if (!combo) return []; + return combo + .split('+') + .map((t) => t.trim()) + .filter(Boolean) + .map(formatShortcutToken); +} + +function formatShortcutToken(token: string): string { + // KeybindContext emits combos like `Ctrl+Shift+M` or `Ctrl+,` — so + // normalize the incoming token to lowercase for switch matching, + // but keep the original around for the default branch so + // punctuation and single letters round-trip as uppercase keycaps. + const lower = token.toLowerCase(); + switch (lower) { + case 'ctrl': + case 'control': + return 'CTRL'; + case 'shift': + return '⇧'; + case 'alt': + case 'option': + return 'ALT'; + case 'meta': + case 'cmd': + case 'command': + return '⌘'; + case 'arrowup': + return '↑'; + case 'arrowdown': + return '↓'; + case 'arrowleft': + return '←'; + case 'arrowright': + return '→'; + case 'escape': + return 'ESC'; + case 'enter': + return '↵'; + case 'tab': + return 'TAB'; + case ' ': + case 'space': + return 'SPACE'; + default: + return token.length === 1 ? token.toUpperCase() : token.toUpperCase(); + } +} + +export function Tooltip({ + content, + placement = 'top', + children, + delay = 300, + shortcut, + interactive = false, + onContentClick, +}: TooltipProps) { + const [isOpen, setIsOpen] = useState(false); + const referenceRef = useRef<HTMLElement | null>(null); + const floatingRef = useRef<HTMLDivElement | null>(null); + const openTimeoutRef = useRef<ReturnType<typeof setTimeout>>(undefined); + // Close timer — separate from the open timer so we can cancel a + // pending close when the cursor enters the floating panel. + const closeTimeoutRef = useRef<ReturnType<typeof setTimeout>>(undefined); + const [pos, setPos] = useState<React.CSSProperties>({ position: 'fixed', visibility: 'hidden' }); + + /** + * Callback ref used when cloning the trigger. Stores the DOM node + * AND forwards it to whatever ref the caller already had on the + * child — without this forwarding the Tooltip would overwrite + * refs like `pinsButtonRef` and break any `getBoundingClientRect` + * logic on the consumer side. + */ + const makeSetRef = useCallback( + (childRef: any) => (node: HTMLElement | null) => { + referenceRef.current = node; + if (!childRef) return; + if (typeof childRef === 'function') { + childRef(node); + } else if (typeof childRef === 'object' && 'current' in childRef) { + (childRef as { current: HTMLElement | null }).current = node; + } + }, + [], + ); + + // Compute position after the tooltip mounts and the reference is visible + useLayoutEffect(() => { + if (!isOpen || !referenceRef.current) return; + + const computePos = () => { + const el = referenceRef.current; + const floating = floatingRef.current; + if (!el) return; + + const rect = el.getBoundingClientRect(); + const floatingRect = floating?.getBoundingClientRect(); + const fw = floatingRect?.width || 0; + const fh = floatingRect?.height || 0; + + const style: React.CSSProperties = { position: 'fixed', zIndex: 20000 }; + + if (placement === 'top') { + style.left = rect.left + rect.width / 2 - fw / 2; + style.top = rect.top - fh - 8; + } else if (placement === 'bottom') { + style.left = rect.left + rect.width / 2 - fw / 2; + style.top = rect.bottom + 8; + } else if (placement === 'left') { + style.left = rect.left - fw - 8; + style.top = rect.top + rect.height / 2 - fh / 2; + } else { + style.left = rect.right + 8; + style.top = rect.top + rect.height / 2 - fh / 2; + } + + setPos(style); + }; + + // First render: measure floating element then position + requestAnimationFrame(computePos); + }, [isOpen, placement]); + + const handleMouseEnter = useCallback(() => { + if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current); + if (openTimeoutRef.current) clearTimeout(openTimeoutRef.current); + openTimeoutRef.current = setTimeout(() => setIsOpen(true), delay); + }, [delay]); + + const handleMouseLeave = useCallback(() => { + if (openTimeoutRef.current) clearTimeout(openTimeoutRef.current); + // Interactive mode: give the user a short grace window to + // move from the trigger onto the floating panel. Non- + // interactive tooltips still close instantly to match the + // classic label behaviour. + if (interactive) { + if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current); + closeTimeoutRef.current = setTimeout(() => setIsOpen(false), 150); + } else { + setIsOpen(false); + } + }, [interactive]); + + const handleFloatingEnter = useCallback(() => { + if (!interactive) return; + if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current); + }, [interactive]); + + const handleFloatingLeave = useCallback(() => { + if (!interactive) return; + if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current); + closeTimeoutRef.current = setTimeout(() => setIsOpen(false), 150); + }, [interactive]); + + return ( + <> + {isValidElement(children) && + cloneElement(children as React.ReactElement<any>, { + ref: makeSetRef((children as any).ref), + onMouseEnter: handleMouseEnter, + onMouseLeave: handleMouseLeave, + onFocus: handleMouseEnter, + onBlur: handleMouseLeave, + })} + {createPortal( + <AnimatePresence> + {isOpen && ( + <motion.div + ref={floatingRef} + style={{ + ...pos, + // Interactive tooltips must accept pointer events so + // the user can hover onto them and click them. The + // default tooltip is non-interactive and leaves the + // CSS default alone. + pointerEvents: interactive ? 'auto' : undefined, + cursor: interactive && onContentClick ? 'pointer' : undefined, + // Interactive tooltips often carry richer content + // (emoji glyph, multi-line body) — give them a wider + // clamp than the default 220px label limit. + maxWidth: interactive ? 280 : undefined, + }} + className={styles.tooltip} + initial={{ opacity: 0, scale: 0.95 }} + animate={{ opacity: 1, scale: 1 }} + exit={{ opacity: 0, scale: 0.95 }} + transition={{ duration: 0.1 }} + role={interactive && onContentClick ? 'button' : 'tooltip'} + onMouseEnter={handleFloatingEnter} + onMouseLeave={handleFloatingLeave} + onClick={ + interactive && onContentClick + ? () => { + if (closeTimeoutRef.current) + clearTimeout(closeTimeoutRef.current); + setIsOpen(false); + onContentClick(); + } + : undefined + } + > + <div className={styles.content}>{content}</div> + {shortcut && ( + <div className={styles.shortcutRow}> + {parseShortcut(shortcut).map((token, i) => ( + <span key={`${i}-${token}`} className={styles.keycap}> + {token} + </span> + ))} + </div> + )} + </motion.div> + )} + </AnimatePresence>, + document.body, + )} + </> + ); +} diff --git a/packages/ui/src/css-modules.d.ts b/packages/ui/src/css-modules.d.ts new file mode 100644 index 0000000..5b5c1c7 --- /dev/null +++ b/packages/ui/src/css-modules.d.ts @@ -0,0 +1,4 @@ +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts new file mode 100644 index 0000000..a3885b6 --- /dev/null +++ b/packages/ui/src/index.ts @@ -0,0 +1,8 @@ +export { Avatar } from './Avatar'; +export { Button } from './Button'; +export { Tooltip } from './Tooltip'; +export { Modal } from './Modal'; +export { BottomSheet } from './BottomSheet'; +export type { BottomSheetProps } from './BottomSheet'; +export { Spinner } from './Spinner'; +export { Toast } from './Toast'; diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json new file mode 100644 index 0000000..564a599 --- /dev/null +++ b/packages/ui/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src"] +} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..15d4898 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true, + "allowJs": true, + "checkJs": false, + "noEmit": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["vite/client"] + } +}