From ef29bb6eb3dfc7d5274c2e3fa4fc5ba30c732c78 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Wed, 4 Mar 2026 11:22:49 -0500 Subject: [PATCH] update --- dist/asset-manifest.json | 7 + dist/components/dm-settings-modal.html | 108 + .../subscription-history-modal.html | 111 + dist/css/docs.fe4863d7877544b1.css | 77 + .../tailwind.generated.31c8693d9b82d026.css | 11836 ++++++++++++++++ dist/docs/README.md | 35 + dist/docs/VideoModal-overview.md | 82 + dist/docs/app-coordinators.md | 104 + dist/docs/capacitor-native-app-plan.md | 849 ++ dist/docs/events-cache-store-overview.md | 96 + dist/docs/moderation-service-overview.md | 73 + dist/docs/moderationService-overview.md | 99 + dist/docs/nip71-overview.md | 53 + dist/docs/nostrEventSchemas-overview.md | 86 + .../weekly-perf-report-2026-02-13.md | 50 + dist/docs/playbackService-overview.md | 75 + dist/docs/profileModalController-overview.md | 76 + ...weekly-race-condition-report-2026-02-14.md | 44 + dist/docs/scheduled-agent-prompts.md | 115 + dist/docs/smoke-test.md | 77 + .../testing/high-quality-playwright-tests.md | 148 + .../testing/playwright-function-coverage.md | 90 + ...-integration-recommendations-2026-02-22.md | 63 + dist/docs/webtorrent-architecture.md | 50 + dist/js/app/authSessionCoordinator.js | 1769 +++ dist/js/app/bindCoordinator.js | 22 + dist/js/app/feedCoordinator.js | 1766 +++ dist/js/app/modalCoordinator.js | 647 + dist/js/app/moderationCoordinator.js | 324 + dist/js/app/playbackCoordinator.js | 1216 ++ dist/js/app/routerCoordinator.js | 88 + dist/js/app/uiCoordinator.js | 321 + dist/js/embed.98945fd2b67db09c.js | 440 + dist/js/gridHealthLogic.js | 134 + dist/js/index.d1b55d32505a5ef6.js | 1408 ++ dist/js/nostr/commentTargetNormalizer.js | 982 ++ dist/js/nostr/decryptionSchemeCache.js | 70 + dist/js/nostr/eventsMap.js | 90 + dist/js/nostr/listEventOrdering.js | 70 + dist/js/nostr/managers/ConnectionManager.js | 965 ++ dist/js/nostr/managers/EventsCacheStore.js | 408 + dist/js/nostr/managers/PersistenceManager.js | 354 + dist/js/nostr/managers/SignerManager.js | 1134 ++ dist/js/nostr/nip46Connector.js | 1093 ++ dist/js/nostr/nip46LoggingUtils.js | 158 + dist/js/nostr/relayBatchFetcher.js | 248 + dist/js/nostr/relayConstants.js | 25 + dist/js/nostr/utils.js | 12 + dist/js/nostr/videoEventBuffer.js | 218 + dist/js/nostr/videoPayloadBuilder.js | 491 + dist/js/nostr/videoPublisher.js | 236 + .../nostrToolsBootstrap.8f9629740a8f3b78.js | 699 + dist/js/services/authUtils.js | 53 + dist/js/services/moderationUtils.js | 512 + dist/js/services/playbackStrategyService.js | 345 + dist/js/services/profileMetadataService.js | 294 + dist/js/services/relaySubscriptionService.js | 250 + dist/js/testHarness.js | 773 + .../ui/components/VideoModalSimilarHelpers.js | 344 + dist/js/ui/components/revertModalRenderers.js | 439 + .../video-modal/linkPreviewController.js | 309 + .../components/video-modal/zapController.js | 928 ++ dist/js/ui/creatorProfileController.js | 489 + dist/js/ui/dm/DMRelaySettings.js | 80 + dist/js/ui/dm/DMSettingsModalController.js | 261 + dist/js/ui/notificationController.js | 266 + .../ui/profileModal/ProfileAdminController.js | 885 ++ .../ui/profileModal/ProfileAdminRenderer.js | 283 + .../ProfileBlockListController.js | 617 + .../ProfileDirectMessageActions.js | 949 ++ .../ProfileDirectMessageController.js | 679 + .../ProfileDirectMessageHelper.js | 786 + .../ProfileDirectMessageRenderer.js | 911 ++ .../ui/profileModal/ProfileEditController.js | 311 + .../profileModal/ProfileHashtagController.js | 598 + .../ProfileModerationController.js | 932 ++ .../ui/profileModal/ProfileRelayController.js | 466 + .../profileModal/ProfileStorageController.js | 702 + .../profileModal/ProfileWalletController.js | 758 + dist/js/ui/profileModalContract.js | 916 ++ dist/js/ui/subscriptionHistoryController.js | 370 + dist/js/ui/thumbnailBinder.js | 92 + dist/js/ui/videoModalController.js | 393 + dist/js/ui/videoSettingsMenuController.js | 171 + dist/js/utils/asyncUtils.js | 130 + dist/js/utils/cryptoUtils.js | 69 + dist/js/utils/mime.js | 49 + dist/js/utils/nostrHelpers.js | 102 + dist/js/utils/pointerNormalization.js | 164 + dist/js/utils/simpleEventEmitter.js | 40 + dist/js/workers/exploreData.worker.js | 145 + dist/releases/draft-20260124.md | 17 + dist/releases/draft-20260131.md | 15 + dist/releases/draft-20260207.md | 26 + dist/releases/draft-20260212.md | 15 + dist/releases/draft-20260216.md | 15 + dist/views/dev/agent-dashboard.html | 114 + 97 files changed, 46855 insertions(+) create mode 100644 dist/asset-manifest.json create mode 100644 dist/components/dm-settings-modal.html create mode 100644 dist/components/subscription-history-modal.html create mode 100644 dist/css/docs.fe4863d7877544b1.css create mode 100644 dist/css/tailwind.generated.31c8693d9b82d026.css create mode 100644 dist/docs/README.md create mode 100644 dist/docs/VideoModal-overview.md create mode 100644 dist/docs/app-coordinators.md create mode 100644 dist/docs/capacitor-native-app-plan.md create mode 100644 dist/docs/events-cache-store-overview.md create mode 100644 dist/docs/moderation-service-overview.md create mode 100644 dist/docs/moderationService-overview.md create mode 100644 dist/docs/nip71-overview.md create mode 100644 dist/docs/nostrEventSchemas-overview.md create mode 100644 dist/docs/perf-reports/weekly-perf-report-2026-02-13.md create mode 100644 dist/docs/playbackService-overview.md create mode 100644 dist/docs/profileModalController-overview.md create mode 100644 dist/docs/race-condition-reports/weekly-race-condition-report-2026-02-14.md create mode 100644 dist/docs/scheduled-agent-prompts.md create mode 100644 dist/docs/smoke-test.md create mode 100644 dist/docs/testing/high-quality-playwright-tests.md create mode 100644 dist/docs/testing/playwright-function-coverage.md create mode 100644 dist/docs/testing/playwright-integration-recommendations-2026-02-22.md create mode 100644 dist/docs/webtorrent-architecture.md create mode 100644 dist/js/app/authSessionCoordinator.js create mode 100644 dist/js/app/bindCoordinator.js create mode 100644 dist/js/app/feedCoordinator.js create mode 100644 dist/js/app/modalCoordinator.js create mode 100644 dist/js/app/moderationCoordinator.js create mode 100644 dist/js/app/playbackCoordinator.js create mode 100644 dist/js/app/routerCoordinator.js create mode 100644 dist/js/app/uiCoordinator.js create mode 100644 dist/js/embed.98945fd2b67db09c.js create mode 100644 dist/js/gridHealthLogic.js create mode 100644 dist/js/index.d1b55d32505a5ef6.js create mode 100644 dist/js/nostr/commentTargetNormalizer.js create mode 100644 dist/js/nostr/decryptionSchemeCache.js create mode 100644 dist/js/nostr/eventsMap.js create mode 100644 dist/js/nostr/listEventOrdering.js create mode 100644 dist/js/nostr/managers/ConnectionManager.js create mode 100644 dist/js/nostr/managers/EventsCacheStore.js create mode 100644 dist/js/nostr/managers/PersistenceManager.js create mode 100644 dist/js/nostr/managers/SignerManager.js create mode 100644 dist/js/nostr/nip46Connector.js create mode 100644 dist/js/nostr/nip46LoggingUtils.js create mode 100644 dist/js/nostr/relayBatchFetcher.js create mode 100644 dist/js/nostr/relayConstants.js create mode 100644 dist/js/nostr/utils.js create mode 100644 dist/js/nostr/videoEventBuffer.js create mode 100644 dist/js/nostr/videoPayloadBuilder.js create mode 100644 dist/js/nostr/videoPublisher.js create mode 100644 dist/js/nostrToolsBootstrap.8f9629740a8f3b78.js create mode 100644 dist/js/services/authUtils.js create mode 100644 dist/js/services/moderationUtils.js create mode 100644 dist/js/services/playbackStrategyService.js create mode 100644 dist/js/services/profileMetadataService.js create mode 100644 dist/js/services/relaySubscriptionService.js create mode 100644 dist/js/testHarness.js create mode 100644 dist/js/ui/components/VideoModalSimilarHelpers.js create mode 100644 dist/js/ui/components/revertModalRenderers.js create mode 100644 dist/js/ui/components/video-modal/linkPreviewController.js create mode 100644 dist/js/ui/components/video-modal/zapController.js create mode 100644 dist/js/ui/creatorProfileController.js create mode 100644 dist/js/ui/dm/DMRelaySettings.js create mode 100644 dist/js/ui/dm/DMSettingsModalController.js create mode 100644 dist/js/ui/notificationController.js create mode 100644 dist/js/ui/profileModal/ProfileAdminController.js create mode 100644 dist/js/ui/profileModal/ProfileAdminRenderer.js create mode 100644 dist/js/ui/profileModal/ProfileBlockListController.js create mode 100644 dist/js/ui/profileModal/ProfileDirectMessageActions.js create mode 100644 dist/js/ui/profileModal/ProfileDirectMessageController.js create mode 100644 dist/js/ui/profileModal/ProfileDirectMessageHelper.js create mode 100644 dist/js/ui/profileModal/ProfileDirectMessageRenderer.js create mode 100644 dist/js/ui/profileModal/ProfileEditController.js create mode 100644 dist/js/ui/profileModal/ProfileHashtagController.js create mode 100644 dist/js/ui/profileModal/ProfileModerationController.js create mode 100644 dist/js/ui/profileModal/ProfileRelayController.js create mode 100644 dist/js/ui/profileModal/ProfileStorageController.js create mode 100644 dist/js/ui/profileModal/ProfileWalletController.js create mode 100644 dist/js/ui/profileModalContract.js create mode 100644 dist/js/ui/subscriptionHistoryController.js create mode 100644 dist/js/ui/thumbnailBinder.js create mode 100644 dist/js/ui/videoModalController.js create mode 100644 dist/js/ui/videoSettingsMenuController.js create mode 100644 dist/js/utils/asyncUtils.js create mode 100644 dist/js/utils/cryptoUtils.js create mode 100644 dist/js/utils/mime.js create mode 100644 dist/js/utils/nostrHelpers.js create mode 100644 dist/js/utils/pointerNormalization.js create mode 100644 dist/js/utils/simpleEventEmitter.js create mode 100644 dist/js/workers/exploreData.worker.js create mode 100644 dist/releases/draft-20260124.md create mode 100644 dist/releases/draft-20260131.md create mode 100644 dist/releases/draft-20260207.md create mode 100644 dist/releases/draft-20260212.md create mode 100644 dist/releases/draft-20260216.md create mode 100644 dist/views/dev/agent-dashboard.html diff --git a/dist/asset-manifest.json b/dist/asset-manifest.json new file mode 100644 index 00000000..d924060d --- /dev/null +++ b/dist/asset-manifest.json @@ -0,0 +1,7 @@ +{ + "css/docs.css": "css/docs.fe4863d7877544b1.css", + "css/tailwind.generated.css": "css/tailwind.generated.31c8693d9b82d026.css", + "js/embed.js": "js/embed.98945fd2b67db09c.js", + "js/index.js": "js/index.d1b55d32505a5ef6.js", + "js/nostrToolsBootstrap.js": "js/nostrToolsBootstrap.8f9629740a8f3b78.js" +} diff --git a/dist/components/dm-settings-modal.html b/dist/components/dm-settings-modal.html new file mode 100644 index 00000000..4de6b779 --- /dev/null +++ b/dist/components/dm-settings-modal.html @@ -0,0 +1,108 @@ + diff --git a/dist/components/subscription-history-modal.html b/dist/components/subscription-history-modal.html new file mode 100644 index 00000000..a47d7cea --- /dev/null +++ b/dist/components/subscription-history-modal.html @@ -0,0 +1,111 @@ + diff --git a/dist/css/docs.fe4863d7877544b1.css b/dist/css/docs.fe4863d7877544b1.css new file mode 100644 index 00000000..8b833c45 --- /dev/null +++ b/dist/css/docs.fe4863d7877544b1.css @@ -0,0 +1,77 @@ +/* Docs layout + table of contents styling */ +#docsToc { + position: sticky; + top: var(--space-xl); + align-self: flex-start; + max-height: calc(100vh - var(--space-2xl)); + overflow-y: auto; + padding: var(--space-md); + border: var(--border-width-hairline) solid var(--border-subtle); + border-radius: var(--radius-lg); + background-color: var(--surface-sunken); + box-shadow: inset 0 0 0 var(--border-width-hairline) + var(--surface-overlay-darker); +} + +#docsContent { + min-width: 0; +} + +#docsContent .markdown-prose { + max-width: 72ch; +} + +#docsTocList, +#docsTocDrawerNav { + gap: var(--space-xs); +} + +#docsTocList ul, +#docsTocDrawerNav ul { + list-style: none; + margin: 0; + padding: 0; +} + +#docsTocList li, +#docsTocDrawerNav li { + margin: 0; +} + +#docsTocList [data-docs-toc-item], +#docsTocDrawerNav [data-docs-toc-item] { + display: block; + padding: var(--space-2xs) var(--space-xs); + border-radius: var(--radius-md); + color: var(--text-muted); + text-decoration: none; + transition: + background-color var(--motion-duration-fast) ease, + color var(--motion-duration-fast) ease; +} + +#docsTocList [data-docs-toc-item]:hover, +#docsTocDrawerNav [data-docs-toc-item]:hover, +#docsTocList [data-docs-toc-item]:focus-visible, +#docsTocDrawerNav [data-docs-toc-item]:focus-visible { + background-color: var(--surface-muted); + color: var(--text-primary); +} + +#docsTocList [data-docs-toc-item][aria-current="page"], +#docsTocDrawerNav [data-docs-toc-item][aria-current="page"] { + background-color: var(--surface-overlay-panel-soft); + color: var(--text-primary); +} + +#docsTocList [data-docs-toc-item]:focus-visible, +#docsTocDrawerNav [data-docs-toc-item]:focus-visible { + outline: var(--space-3xs) solid var(--color-accent); + outline-offset: var(--space-3xs); +} + +#docsTocDrawerNav { + max-height: calc(100vh - var(--space-2xl)); + overflow-y: auto; + padding-right: var(--space-2xs); +} diff --git a/dist/css/tailwind.generated.31c8693d9b82d026.css b/dist/css/tailwind.generated.31c8693d9b82d026.css new file mode 100644 index 00000000..4b0c2b58 --- /dev/null +++ b/dist/css/tailwind.generated.31c8693d9b82d026.css @@ -0,0 +1,11836 @@ +:root { + /* Semantic palette */ + --surface-base: var(--color-surface); + --surface-alt: var(--color-surface-alt); + --surface-raised: var(--color-card); + --surface-raised-private: var(--color-card-private); + --surface-raised-critical: var(--color-card-critical); + --surface-panel: var(--color-panel); + --surface-panel-hover: var(--color-panel-hover); + --surface-sunken: var(--color-surface-sunken); + --surface-muted: var(--color-surface-muted); + --surface-veil: var(--color-overlay-veil); + --surface-inverse: var(--color-page); + + --surface-overlay: var(--color-overlay); + --surface-overlay-muted: var(--color-overlay-muted); + --surface-overlay-strong: var(--color-overlay-strong); + --surface-overlay-darker: var(--color-overlay-darker); + --surface-overlay-black: var(--color-overlay-black); + --surface-overlay-banner-base: var(--color-page); + --surface-overlay-banner: var(--surface-overlay-banner-base); + --surface-overlay-banner-opaque: var(--surface-overlay-banner-base); + --surface-overlay-banner-strong: color-mix( + in srgb, + var(--surface-overlay-banner-base) 82%, + transparent + ); + --surface-overlay-banner-bottom: color-mix( + in srgb, + var(--surface-overlay-banner-base) 50%, + transparent + ); + --surface-overlay-banner-channel-mid: color-mix( + in srgb, + var(--surface-overlay-banner-base) 24%, + transparent + ); + --surface-overlay-banner-channel-faint: color-mix( + in srgb, + var(--surface-overlay-banner-base) 10%, + transparent + ); + --surface-overlay-banner-soft: color-mix( + in srgb, + var(--surface-overlay-banner-base) 56%, + transparent + ); + --surface-overlay-banner-faint: color-mix( + in srgb, + var(--surface-overlay-banner-base) 18%, + transparent + ); + --surface-overlay-banner-transparent: transparent; + --surface-overlay-banner-gradient-header: linear-gradient( + to bottom, + var(--surface-overlay-banner-opaque) 0%, + var(--surface-overlay-banner-strong) 38%, + var(--surface-overlay-banner-soft) 70%, + var(--surface-overlay-banner-faint) 88%, + var(--surface-overlay-banner-transparent) 100% + ); + --surface-overlay-banner-gradient-channel: linear-gradient( + to top, + var(--surface-overlay-banner-bottom) 0%, + var(--surface-overlay-banner-channel-mid) 28%, + var(--surface-overlay-banner-channel-faint) 42%, + var(--surface-overlay-banner-transparent) 50% + ); + --surface-overlay-banner-gradient: var( + --surface-overlay-banner-gradient-header + ); + --surface-overlay-panel: var(--color-overlay-panel); + --surface-overlay-panel-soft: var(--color-overlay-panel-soft); + --surface-overlay-panel-tint: var(--color-overlay-panel-tint); + --surface-overlay-panel-glass: var(--color-overlay-panel-glass); + --surface-overlay-panel-glass-soft: var(--color-overlay-panel-glass-soft); + --surface-modal-backdrop: var(--color-modal-backdrop); + --surface-modal: var(--color-modal-surface); + --blur-modal-backdrop: 18px; + --blur-modal-alert: 14px; + --blur-thumbnail-blur: 18px; + --modal-nav-backdrop-blur: 6px; + + --app-header-centerline-offset: calc( + var(--space-sm) + (var(--space-2xl-plus) / 2) + ); + --border-default: var(--color-border); + --border-subtle: var(--color-border-subtle); + --border-strong: var(--color-border-translucent-stronger); + --border-overlay: var(--color-border-translucent); + --border-overlay-strong: var(--color-border-translucent-strongest); + --border-overlay-glow: var(--color-border-translucent-glow); + --border-neutral-soft: var(--color-border-neutral-soft); + --border-info-soft: var(--color-border-info-soft); + --border-sidebar-strong: var(--color-border-sidebar-strong); + --border-width-hairline: 1px; + --border-width-fine: 0.5px; + + --text-primary: var(--color-text); + --text-inverse: var(--color-text-inverse); + --text-muted: var(--color-text-muted); + --text-muted-strong: var(--color-muted-strong); + --text-subtle: var(--color-subtle-text); + --text-frosted-primary: var(--color-text-frosted); + --text-frosted-strong: var(--color-text-frosted-strong); + --text-frosted-soft: var(--color-text-frosted-soft); + + --status-info: var(--color-info); + --status-info-strong: var(--color-info-strong); + --status-info-surface: var(--color-info-glass); + --status-info-border: var(--color-info-border); + --status-info-on: var(--color-info-on); + + --status-success: var(--color-success); + --status-success-strong: var(--color-success-strong); + --status-success-surface: var(--color-success-surface); + --status-success-border: var(--color-success-border); + --status-success-on: var(--color-success-on); + + --status-warning: var(--color-warning); + --status-warning-strong: var(--color-warning-strong); + --status-warning-surface: var(--color-warning-surface); + --status-warning-border: var(--color-warning-border); + --status-warning-on: var(--color-warning-on); + + --status-danger: var(--color-critical); + --status-danger-strong: var(--color-critical-strong); + --status-danger-surface: var(--color-danger-surface); + --status-danger-border: var(--color-danger-border); + --status-danger-on: var(--color-danger-on); + + --status-private: var(--color-private); + --status-private-strong: var(--color-private-strong); + --status-private-surface: var(--color-private-surface); + --status-private-border: var(--color-private-border); + --status-private-on: var(--color-private-on); + + --status-neutral: var(--color-muted); + --status-neutral-strong: var(--color-muted-strong); + --status-neutral-surface: var(--color-neutral-surface); + --status-neutral-border: var(--color-neutral-border); + --status-neutral-on: var(--color-neutral-on); + + /* Messaging & notifications */ + --message-bubble-background: var(--surface-sunken); + --message-bubble-border: var(--border-subtle); + --message-bubble-text: var(--text-primary); + --message-bubble-meta: var(--text-muted); + --message-bubble-outgoing-background: var(--surface-overlay-panel); + --message-bubble-outgoing-border: var(--border-overlay); + + --unread-badge-background: var(--status-info-strong); + --unread-badge-text: var(--text-inverse); + + --reply-highlight-background: var(--status-info-surface); + --reply-highlight-border: var(--status-info-border); + --reply-highlight-text: var(--status-info-on); + --reply-highlight-icon: var(--status-info-on); + + --notification-banner-accent: var(--color-accent-strong); + --notification-banner-accent-strong: var(--color-accent); + --notification-banner-border: var(--surface-overlay-panel-tint); + --notification-banner-background: var(--surface-overlay-panel); + --notification-banner-text: var(--text-primary); + --notification-banner-icon: var(--text-primary); + + --notification-banner-critical-accent: var(--status-danger-strong); + --notification-banner-critical-accent-strong: var(--status-danger); + --notification-banner-critical-border: var(--color-ring-critical-soft); + --notification-banner-critical-background: var(--surface-raised-critical); + --notification-banner-critical-text: var(--status-danger-on); + + --notification-banner-success-accent: var(--status-success-strong); + --notification-banner-success-accent-strong: var(--status-success); + --notification-banner-success-border: var(--status-success-border); + --notification-banner-success-background: var(--status-success-surface); + --notification-banner-success-text: var(--status-success-on); + + --notification-banner-info-accent: var(--status-info-strong); + --notification-banner-info-accent-strong: var(--status-info); + --notification-banner-info-border: var(--status-info-border); + --notification-banner-info-background: var(--status-info-surface); + --notification-banner-info-text: var(--status-info-on); + + --notification-banner-warning-accent: var(--status-warning-strong); + --notification-banner-warning-accent-strong: var(--status-warning); + --notification-banner-warning-border: var(--status-warning-border); + --notification-banner-warning-background: var(--status-warning-surface); + --notification-banner-warning-text: var(--status-warning-on); + + /* Layout & stacking */ + --scrollbar-width: 0.5rem; + --scrollbar-track: var(--color-white-soft); + --scrollbar-thumb: var(--color-white-softer); + --scrollbar-thumb-hover: var(--color-white-softest); + --z-app-header: 40; + --z-overlay-nav: 60; + --z-overlay-floating: 70; + --z-overlay-popover: 140; + --z-overlay-toast: 180; + --z-sidebar-overlay: 10; + --z-sidebar: 20; + --z-modal-root: 100; + --z-modal-overlay: 110; + --z-modal-content: 120; + --z-modal-player: 130; + --z-modal-top-root: 140; + --z-modal-top-overlay: 150; + --z-modal-top-content: 160; + --z-modal-peak-root: 165; + --z-modal-peak-overlay: 175; + --z-modal-peak-content: 185; + --z-modal-always-top: 200; + --overlay-backdrop-color: var(--color-overlay-backdrop); + --overlay-backdrop-blur: 12px; + --sidebar-width-expanded-base: 16rem; + --sidebar-dropup-content-width: 0px; + --sidebar-width-expanded: max( + var(--sidebar-width-expanded-base), + var(--sidebar-dropup-content-width) + ); + --sidebar-width-collapsed: 4rem; + --sidebar-width: var(--sidebar-width-expanded); + --sidebar-control-size: 2.75rem; + --sidebar-content-gap: 1rem; + --space-sidebar-shell-inline: var(--space-lg); + --space-sidebar-shell-block-start: var(--space-sm); + --space-sidebar-shell-block-end: 1.75rem; + --space-sidebar-scroll-gap: 1.75rem; + --space-sidebar-panel-gap: 1.35rem; + --space-sidebar-panel-padding-block: 1.35rem; + --space-sidebar-panel-padding-inline: 1.1rem; + --space-sidebar-footer-gap: 0.85rem; + --space-sidebar-trigger-padding-block: 0.78rem; + --space-sidebar-popover-offset: 0.85rem; + --space-sidebar-popover-padding-block: 0.85rem; + --space-sidebar-popover-padding-inline: 0.65rem; + --space-sidebar-popover-gap: 0.45rem; + --space-sidebar-dropup-scroll-reserve: calc( + var(--scrollbar-thin-width) + var(--space-sm) + var(--space-2xs) + ); + --sidebar-dropup-max-height: calc(3rem * 6); + --space-sidebar-nav-padding-block: 0.7rem; + --space-sidebar-nav-padding-inline-start: var(--space-xs); + --space-sidebar-nav-padding-inline-end: calc( + var(--space-lg) + var(--space-2xs) + ); + --space-sidebar-subnav-padding-block: 0.55rem; + --space-sidebar-nav-translate: 0.35rem; + --grid-card-min-width: 20rem; + /* Breakpoints (sync with Tailwind screen settings) */ + --breakpoint-lg: 64rem; + --popover-backdrop-blur: 12px; + --overlay-panel-radius: var(--radius-lg); + --overlay-panel-shadow: var(--shadow-lg); + --overlay-panel-padding-inline: var(--popup-padding-inline); + --overlay-panel-padding-block: var(--popup-padding-block); + --toast-translate: -1.5rem; + --sidebar-mobile-gutter: 3rem; + --popup-padding-inline: 1rem; + --popup-padding-block: 0.75rem; + --video-modal-primary-fr: 1.85fr; + --video-modal-secondary-min: 320px; + --video-modal-secondary-max: 35rem; + --video-modal-secondary-fr: 1fr; + --video-modal-layout-max: calc( + var(--layout-player-max-width) + var(--video-modal-secondary-max) + + var(--space-2xl-plus) + ); + --revert-modal-sidebar-min: 240px; + --revert-modal-sidebar-width: 280px; + --popover-inline-safe-max: calc(100vw - var(--space-xl)); + + /* Progress primitives */ + --progress-height-sm: 0.25rem; + --progress-height-md: 0.5rem; + --progress-radius: var(--radius-full); + --progress-track-color: var(--color-border-subtle); + --progress-fill-color: var(--color-info); + --progress-transition-duration: var(--motion-duration-fast); + --progress-transition-ease: var(--motion-ease-out); + + /* Spacing scale */ + --space-4xs: 0.0625rem; + --space-3xs: 0.125rem; + --space-2xs: 0.25rem; + --space-xs: 0.5rem; + --space-xs-snug: 0.4rem; + --space-sm: 0.75rem; + --space-md: 1rem; + --space-md-plus: 1.25rem; + --space-lg: 1.5rem; + --space-xl: 2rem; + --space-xl-compact: 2.25rem; + --space-xl-plus: 2.5rem; + --space-2xl: 3rem; + --space-2xl-plus: 3.5rem; + + /* Measurement primitives */ + --size-2px: 2px; + --size-3px: 3px; + --size-12px: 12px; + --size-24px: 24px; + --size-30px: 30px; + --size-32px: 32px; + --size-64px: 64px; + --size-80px: 80px; + --size-96px: 96px; + --size-128px: 128px; + --size-140px: 140px; + --size-168px: 168px; + --size-256px: 256px; + --size-5-75rem: 5.75rem; + --size-6rem: 6rem; + --size-7rem: 7rem; + --size-8rem: 8rem; + --size-12rem: 12rem; + --size-12-5rem: 12.5rem; + --size-14rem: 14rem; + --size-16rem: 16rem; + --size-18rem: 18rem; + --size-20rem: 20rem; + --size-28rem: 28rem; + --size-34rem: 34rem; + --size-40rem: 40rem; + --size-45rem: 45rem; + --size-70rem: 70rem; + + /* Border radii */ + --radius-sm: 0.25rem; + --radius-md: 0.5rem; + --radius-lg: 0.75rem; + --radius-xl: 1rem; + --radius-full: 9999px; + --radius-modal-xl: 1.75rem; + --radius-sidebar-shell: 1.5rem; + --radius-sidebar-panel: 1.1rem; + --radius-sidebar-nav: 0.95rem; + --radius-toggle-thumb: 0.5625rem; + + /* Component sizing */ + --menu-min-width: 12rem; + --header-search-max-inline: var(--size-34rem); + --modal-max-width: 62.5rem; + --modal-sheet-inline-max: var(--modal-max-width); + --modal-shell-max-height: 90vh; + --modal-sheet-max-height: 92vh; + --modal-body-max-height: calc(92vh - 4rem); + --profile-modal-inline-max: var(--size-70rem); + --player-modal-module-scroll-min-height: var(--size-14rem); + --player-modal-module-scroll-max-height: var(--size-28rem); + --modal-pane-max-height: 48vh; + --modal-pane-min-height: 20rem; + --moderation-badge-max-inline: var(--size-28rem); + --notification-banner-max-inline: var(--size-28rem); + --profile-modal-nav-min-width: max-content; + --profile-modal-nav-max-width: max-content; + --profile-modal-scrollbar-offset: var(--space-2xs); + --modal-embed-height: 80vh; + --profile-history-max-height: 26.25rem; + --comment-avatar-size: var(--space-xl-plus); + --comment-composer-min-height: var(--size-7rem); + --video-modal-reaction-label-min-inline: var(--size-6rem); + --video-modal-item-media-min: var(--size-5-75rem); + --video-modal-item-media-max: var(--size-8rem); + --video-card-compact-media-min: var(--size-8rem); + --video-card-compact-media-max: var(--size-12-5rem); + --similar-card-media-width: var(--size-168px); + --similar-card-media-width-sm: var(--size-140px); + --docs-popover-scroll-height: var(--size-45rem); + --profile-modal-active-min-width: var(--size-12rem); + --status-log-min-height: 5rem; + --layout-player-max-width: 68.75rem; + --layout-docs-max-width: 92.5rem; + --icon-size-sm: var(--font-size-lg); + --icon-size-md: var(--space-md-plus); + --icon-size-lg: var(--space-xl-plus); + --avatar-size-lg: var(--space-2xl-plus); + --form-textarea-min-height: 8rem; + --scrollbar-thin-width: 0.4rem; + --modal-progress-height: calc(var(--space-3xs) * 3); + --video-modal-action-min-width: 2.75rem; + --outline-thick-width: 0.1875rem; + --icon-button-ring-width: var(--outline-thick-width); + + /* Form controls */ + --form-select-icon-offset: var(--space-sm); + --form-select-icon-gap: var(--space-xs-snug); + --form-select-icon-hit-area: var(--space-xl-plus); + --form-select-icon-size: 0.6rem; + --form-select-divider-width: var(--border-width-hairline); + --form-checkbox-checkmark-offset: calc(var(--space-3xs) * -1); + + /* Utility measurements */ + --breakpoint-edge-offset: 0.02px; + --offscreen-offset: -9999px; + + /* Motion */ + --motion-card-enter-translate: var(--space-xs); + --motion-fade-in-translate: var(--space-md-plus); + --motion-login-option-hover-translate: calc(var(--size-3px) * -1); + --motion-video-card-hover-translate: -0.35rem; + --motion-video-card-backdrop-translate: 0.15rem; + + /* Shadows */ + --shadow-sm: 0 0.0625rem 0.125rem 0 var(--color-shadow-floating); + --shadow-md: 0 0.25rem 0.375rem -0.0625rem var(--color-shadow-floating); + --shadow-lg: 0 0.625rem 0.9375rem -0.1875rem var(--color-shadow-elevated); + --shadow-badge-offset-y: var(--size-12px); + --shadow-badge-blur: var(--size-30px); + --shadow-notification-offset-y: var(--size-32px); + --shadow-notification-blur: var(--size-64px); + --shadow-modal: 0 2.75rem 6rem -1.25rem var(--color-shadow-modal); + --shadow-sidebar-shell: 0 1.875rem 3.75rem -1.5rem var(--color-shadow-sidebar); + --shadow-sidebar-accent: 0 1.65rem 3.25rem -1.35rem var(--color-sidebar-glow); + --shadow-sidebar-trigger: 0 1.25rem 2.5rem -1.35rem var(--color-shadow-panel); + --shadow-sidebar-dropup: 0 1.75rem 3.25rem -1.45rem var(--color-sidebar-glow); + --shadow-sidebar-panel: 0 2.25rem 4.5rem -1.85rem var(--color-shadow-sidebar); + --shadow-sidebar-focus-ring: + 0 0 0 0.125rem var(--color-sidebar-glow), + 0 0 0 0.25rem var(--color-sidebar-hover-strong); + --shadow-popover-zap: 0 22px 48px -18px var(--color-shadow-intense); + --shadow-video-modal: 0 1rem 2.25rem var(--color-shadow-elevated); + --blur-video-card-backdrop-strong: var(--size-24px); + --video-modal-ambient-blur-min: var(--size-128px); + --video-modal-ambient-blur-max: var(--size-256px); + + /* Typography */ + --font-family-sans: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif; + --font-family-mono: "Fira Code", "SFMono-Regular", ui-monospace, monospace; + --font-size-4xs: 0.65rem; + --font-size-3xs: 0.6875rem; + --font-size-2xs: 0.7rem; + --font-size-xs: 0.75rem; + --font-size-sm: 0.875rem; + --font-size-base: 1rem; + --font-size-lg: 1.125rem; + --font-size-xl: 1.5rem; + --font-size-2xl: 2rem; + --font-size-xl-plus: 1.875rem; + --font-size-modal-title-base: 1.25rem; + --font-size-sidebar-nav: 0.95rem; + --font-size-sidebar-subnav: 0.85rem; + --font-size-sidebar-trigger: 0.9rem; + --link-underline-thickness: var(--size-2px); + --line-height-tight: 1.1; + --line-height-snug: 1.25; + --line-height-base: 1.5; + --line-height-relaxed: 1.75; + --tracking-extra-wide: 0.2em; + + /* Beacon UI tokens */ + --grid-border-width: calc(var(--space-2xs) / 4); + --grid-canvas-padding-top: var(--grid-border-width); + --grid-viewport-min-height: calc(var(--space-xl) - var(--space-sm)); + --grid-cell-padding-block: var(--space-sm); + --grid-cell-padding-inline: var(--space-md); + --grid-header-font-size: var(--font-size-xs); + --grid-header-letter-spacing: 0.08em; + --grid-overlay-margin: 10%; + --grid-overlay-padding-block: var(--space-md); + --grid-overlay-padding-inline: var(--space-xl); + --grid-overlay-radius: var(--radius-lg); + --grid-overlay-font-size: var(--font-size-lg); + --grid-overlay-text-opacity: 0.66; + --grid-resizer-width: calc(var(--space-2xs) * 1.25); + --grid-resize-overlay-width: var(--grid-border-width); + --grid-sort-indicator-size: calc(var(--space-lg) - var(--space-2xs)); + --grid-sort-indicator-offset: var(--space-xs); + --grid-sort-indicator-stroke: calc(var(--space-2xs) / 2); + --grid-sort-indicator-thickness: var(--grid-border-width); + --notify-padding-block: var(--space-md); + --notify-padding-inline: var(--space-2xl); + --notify-font-size: var(--font-size-base); + --notify-dismiss-size: calc(var(--space-xl) + var(--space-xs)); + --notify-dismiss-size-compact: calc(var(--space-lg) + var(--space-2xs)); + --notify-dismiss-font-size: var(--font-size-sm); + --notify-dismiss-radius: var(--radius-full); + --notify-dismiss-color: var(--color-subtle); + --notify-dismiss-background: var(--color-overlay-panel); + --notify-dismiss-background-hover: var(--color-overlay-panel-soft); + --z-notify: 1200; + + /* Motion */ + --motion-duration-fast: 120ms; + --motion-duration-base: 160ms; + --motion-duration-slow: 280ms; + --motion-ease-out: cubic-bezier(0.16, 1, 0.3, 1); + --motion-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + --modal-motion-duration: var(--motion-duration-base); +} +:root[data-theme="light"] { + /* Light theme palette */ + --color-page: rgb(247 248 252); + --color-page-alt: rgb(242 244 248); + --color-surface: rgb(255 255 255); + --color-surface-alt: rgb(248 250 252); + --color-bg: var(--color-surface); + --color-card: rgb(255 255 255); + --color-card-private: rgb(243 236 255); + --color-card-critical: rgb(255 236 238); + --color-panel: rgb(252 253 255); + --color-panel-hover: rgb(239 242 249); + --color-border: rgb(210 219 236); + --color-border-subtle: rgb(229 234 242); + + --color-accent: rgb(99 102 241); + --color-accent-strong: rgb(79 70 229); + --color-accent-pressed: rgb(67 56 202); + --color-accent-override: rgb(84 0 17); + --color-accent-strong-override: rgb(254 0 50); + --color-accent-pressed-override: rgb(169 0 33); + --color-primary: var(--color-accent-strong); + --color-secondary: var(--color-accent); + + --color-logo-accent: var(--color-accent-strong); + --color-logo-wordmark: rgb(40 54 77); + --color-logo-wordmark-inverse: var(--color-white); + --color-logo-background: rgb(15 23 42); + --color-logo-background-inverse: var(--color-white); + + --color-info: rgb(59 130 246); + --color-info-strong: rgb(37 99 235); + --color-info-pressed: rgb(29 78 216); + --color-info-glass: rgb(59 130 246 / 0.14); + --color-info-border: rgb(59 130 246 / 0.35); + --color-info-on: rgb(227 242 255); + + --color-muted-strong: rgb(71 85 105); + --color-text: rgb(24 31 44); + --color-text-strong: rgb(12 16 26); + --color-text-inverse: rgb(255 255 255); + --color-text-muted: rgb(102 112 133); + --color-muted: rgb(136 146 166); + --color-muted-alt: rgb(120 130 150); + --color-subtle-text: rgb(136 146 166); + --color-subtle: rgb(243 245 250); + --color-surface-sunken: rgb(232 235 244); + --color-surface-muted: rgb(236 240 249); + + --color-critical: rgb(220 54 70); + --color-critical-strong: rgb(185 32 55); + --color-danger-surface: rgb(252 226 230); + --color-danger-border: rgb(220 54 70 / 0.35); + --color-danger-on: rgb(96 18 31); + + --color-warning: rgb(234 179 8); + --color-warning-strong: rgb(202 138 4); + --color-warning-surface: rgb(253 224 71 / 0.35); + --color-warning-border: rgb(234 179 8 / 0.35); + --color-warning-glass: rgb(253 224 71 / 0.18); + --color-warning-on: rgb(92 55 2); + + --color-success: rgb(22 163 74); + --color-success-strong: rgb(21 128 61); + --color-success-surface: rgb(222 250 236); + --color-success-border: rgb(34 197 94 / 0.3); + --color-success-on: rgb(18 84 48); + + --color-private: rgb(139 92 246); + --color-private-strong: rgb(124 58 237); + --color-private-surface: rgb(237 233 254); + --color-private-border: rgb(139 92 246 / 0.32); + --color-private-on: rgb(74 29 150); + + --color-neutral-700: rgb(71 85 105); + --color-neutral-600: rgb(102 112 133); + --color-neutral-surface: rgb(232 236 244); + --color-neutral-border: rgb(205 213 226); + --color-neutral-on: rgb(62 72 92); + + --color-white: rgb(255 255 255); + --color-black: rgb(0 0 0); + + --color-overlay: rgb(15 23 42 / 0.3); + --color-overlay-black: rgb(15 23 42 / 0.7); + --color-overlay-muted: rgb(60 75 105 / 0.18); + --color-overlay-strong: rgb(15 23 42 / 0.56); + --color-overlay-darker: rgb(12 18 32 / 0.68); + --color-overlay-panel: rgb(248 250 255 / 0.92); + --color-overlay-panel-soft: rgb(244 247 255 / 0.78); + --color-overlay-panel-tint: rgb(146 170 211 / 0.24); + --color-overlay-panel-glass: rgb(255 255 255 / 0.85); + --color-overlay-panel-glass-soft: rgb(246 249 255 / 0.7); + --color-overlay-panel-gradient-start: rgb(255 255 255 / 0.95); + --color-overlay-panel-gradient-end: rgb(232 236 244 / 0.92); + --color-modal-backdrop: rgb(15 23 42 / 0.28); + --color-modal-surface: rgb(255 255 255 / 0.94); + --color-overlay-veil: rgb(103 112 133 / 0.45); + --color-overlay-banner-bottom: rgb(255 255 255 / 0.6); + --color-overlay-banner-transparent: rgb(255 255 255 / 0); + --color-overlay-backdrop: rgb(15 23 42 / 0.2); + + --scrollbar-track: color-mix( + in srgb, + var(--color-sidebar-border-strong) 40%, + transparent + ); + --scrollbar-thumb: color-mix(in srgb, var(--color-accent) 64%, transparent); + --scrollbar-thumb-hover: color-mix( + in srgb, + var(--color-accent-strong) 76%, + transparent + ); + + --color-sidebar-surface: rgb(255 255 255 / 0.9); + --color-sidebar-surface-alt: rgb(236 240 247 / 0.72); + --color-sidebar-border: rgb(184 194 212 / 0.28); + --color-sidebar-border-strong: rgb(148 163 184 / 0.36); + --color-sidebar-hover: color-mix( + in srgb, + var(--color-accent) 16%, + transparent + ); + --color-sidebar-hover-strong: color-mix( + in srgb, + var(--color-accent-strong) 24%, + transparent + ); + --color-sidebar-glow: color-mix( + in srgb, + var(--color-accent) 36%, + transparent + ); + --color-sidebar-accent-start: color-mix( + in srgb, + var(--color-accent-strong) 88%, + transparent + ); + --color-sidebar-accent-end: color-mix( + in srgb, + var(--color-accent) 82%, + transparent + ); + --color-sidebar-text-muted: var(--color-text-muted); + --sidebar-icon-filter: brightness(0) saturate(100%); + + --color-surface-tint-subtle: rgb(15 23 42 / 0.08); + --color-border-translucent: rgb(148 163 184 / 0.18); + --color-border-translucent-strong: rgb(148 163 184 / 0.22); + --color-border-translucent-bright: rgb(148 163 184 / 0.28); + --color-border-translucent-stronger: rgb(148 163 184 / 0.32); + --color-border-translucent-strongest: rgb(148 163 184 / 0.36); + --color-border-translucent-glow: rgb(148 163 184 / 0.45); + --color-border-translucent-medium: rgb(148 163 184 / 0.2); + --color-border-neutral-soft: rgb(148 163 184 / 0.25); + --color-border-info-soft: rgb(59 130 246 / 0.32); + --color-border-sidebar-strong: rgb(102 112 133 / 0.75); + + --color-ring-private: rgb(139 92 246 / 0.5); + --color-ring-private-hover: rgb(168 85 247 / 0.6); + --color-ring-critical: rgb(220 54 70 / 0.35); + --color-ring-critical-soft: rgb(220 54 70 / 0.3); + --color-ring-critical-hover: rgb(220 54 70 / 0.4); + --color-ring-critical-strong: rgb(220 54 70 / 0.5); + --color-ring-alert: rgb(239 68 68 / 0.48); + + --color-gradient-visibility-start: rgb(99 102 241 / 0.28); + --color-gradient-visibility-end: rgb(129 140 248 / 0.24); + + --color-white-soft: rgb(255 255 255 / 0.08); + --color-white-softer: rgb(255 255 255 / 0.24); + --color-white-softest: rgb(255 255 255 / 0.32); + + --color-shadow-floating: rgb(15 23 42 / 0.18); + --color-shadow-elevated: rgb(15 23 42 / 0.16); + --color-shadow-panel: rgb(15 23 42 / 0.12); + --color-shadow-notification: rgb(15 23 42 / 0.14); + --color-shadow-intense: rgb(15 23 42 / 0.22); + --color-shadow-sidebar: rgb(15 23 42 / 0.28); + --color-shadow-modal: rgb(15 23 42 / 0.28); + + --color-divider-strong: rgb(203 213 225 / 0.6); + --color-text-frosted-strong: rgb(40 54 77 / 0.85); + --color-text-frosted: rgb(61 74 98 / 0.8); + --color-text-frosted-soft: rgb(82 96 122 / 0.72); +} +:root[data-theme="dark"], +:root:not([data-theme]) { + /* Dark theme palette */ + --color-page: rgb(6 10 18); + --color-page-alt: rgb(8 12 22); + --color-surface: rgb(10 14 26); + --color-surface-alt: rgb(14 20 34); + --color-bg: var(--color-surface); + --color-card: rgb(18 26 44); + --color-card-private: rgb(38 28 62); + --color-card-critical: rgb(58 20 28); + --color-panel: rgb(16 24 40); + --color-panel-hover: rgb(24 34 52); + --color-border: rgb(61 80 112); + --color-border-subtle: rgb(38 52 76); + + --color-accent: rgb(129 140 248); + --color-accent-strong: rgb(99 102 241); + --color-accent-pressed: rgb(79 70 229); + --color-accent-override: rgb(84 0 17); + --color-accent-strong-override: rgb(254 0 50); + --color-accent-pressed-override: rgb(169 0 33); + --color-primary: var(--color-accent-strong); + --color-secondary: rgb(56 189 248); + + --color-logo-accent: var(--color-accent-strong); + --color-logo-wordmark: rgb(198 207 222); + --color-logo-wordmark-inverse: var(--color-white); + --color-logo-background: rgb(12 18 30); + --color-logo-background-inverse: var(--color-white); + + --color-info: rgb(96 165 250); + --color-info-strong: rgb(56 132 255); + --color-info-pressed: rgb(37 99 235); + --color-info-glass: rgb(56 132 255 / 0.22); + --color-info-border: rgb(96 165 250 / 0.42); + --color-info-on: rgb(235 244 255); + + --color-muted-strong: rgb(206 215 232); + --color-text: rgb(226 232 240); + --color-text-strong: rgb(244 247 255); + --color-text-inverse: rgb(12 18 30); + --color-text-muted: rgb(165 179 201); + --color-muted: rgb(148 163 184); + --color-muted-alt: rgb(128 139 162); + --color-subtle-text: rgb(168 180 205); + --color-subtle: rgb(28 38 58); + --color-surface-sunken: rgb(13 19 32 / 0.92); + --color-surface-muted: rgb(18 26 44 / 0.82); + + --color-critical: rgb(239 83 98); + --color-critical-strong: rgb(220 38 38); + --color-danger-surface: rgb(220 38 38 / 0.28); + --color-danger-border: rgb(220 38 38 / 0.45); + --color-danger-on: rgb(255 228 233); + + --color-warning: rgb(250 204 21); + --color-warning-strong: rgb(234 179 8); + --color-warning-surface: rgb(253 224 71 / 0.22); + --color-warning-border: rgb(250 204 21 / 0.42); + --color-warning-glass: rgb(253 224 71 / 0.2); + --color-warning-on: rgb(26 18 0); + + --color-success: rgb(34 197 94); + --color-success-strong: rgb(22 163 74); + --color-success-surface: rgb(22 163 74 / 0.24); + --color-success-border: rgb(34 197 94 / 0.45); + --color-success-on: rgb(210 252 228); + + --color-private: rgb(168 85 247); + --color-private-strong: rgb(139 92 246); + --color-private-surface: rgb(124 58 237 / 0.26); + --color-private-border: rgb(168 85 247 / 0.45); + --color-private-on: rgb(239 230 255); + + --color-neutral-700: rgb(58 75 110); + --color-neutral-600: rgb(90 105 133); + --color-neutral-surface: rgb(99 114 144 / 0.24); + --color-neutral-border: rgb(99 114 144 / 0.35); + --color-neutral-on: rgb(210 220 238); + + --color-white: rgb(255 255 255); + --color-black: rgb(0 0 0); + + --color-overlay: rgb(8 11 20 / 0.72); + --color-overlay-black: rgb(0 0 0 / 0.88); + --color-overlay-muted: rgb(12 18 30 / 0.6); + --color-overlay-strong: rgb(8 12 24 / 0.82); + --color-overlay-darker: rgb(5 8 16 / 0.9); + --color-overlay-panel: rgb(14 20 34 / 0.92); + --color-overlay-panel-soft: rgb(18 26 42 / 0.78); + --color-overlay-panel-tint: rgb(56 93 160 / 0.36); + --color-overlay-panel-glass: rgb(14 24 42 / 0.86); + --color-overlay-panel-glass-soft: rgb(16 28 48 / 0.72); + --color-overlay-panel-gradient-start: rgb(18 26 44 / 0.92); + --color-overlay-panel-gradient-end: rgb(10 16 30 / 0.92); + --color-modal-backdrop: rgb(5 8 14 / 0.68); + --color-modal-surface: rgb(14 22 38 / 0.88); + --color-overlay-veil: rgb(4 6 12 / 0.94); + --color-overlay-banner-bottom: rgb(0 0 0 / 0.6); + --color-overlay-banner-transparent: rgb(0 0 0 / 0); + --color-overlay-backdrop: rgb(5 8 14 / 0.75); + + --scrollbar-track: color-mix( + in srgb, + var(--color-sidebar-surface-alt) 54%, + transparent + ); + --scrollbar-thumb: color-mix(in srgb, var(--color-accent) 72%, transparent); + --scrollbar-thumb-hover: color-mix( + in srgb, + var(--color-accent-strong) 84%, + transparent + ); + + --color-sidebar-surface: rgb(12 18 30 / 0.94); + --color-sidebar-surface-alt: rgb(10 18 32 / 0.8); + --color-sidebar-border: rgb(86 106 142 / 0.26); + --color-sidebar-border-strong: rgb(112 133 170 / 0.42); + --color-sidebar-hover: color-mix( + in srgb, + var(--color-accent) 22%, + transparent + ); + --color-sidebar-hover-strong: color-mix( + in srgb, + var(--color-accent-strong) 32%, + transparent + ); + --color-sidebar-glow: color-mix( + in srgb, + var(--color-accent) 42%, + transparent + ); + --color-sidebar-accent-start: color-mix( + in srgb, + var(--color-accent) 92%, + transparent + ); + --color-sidebar-accent-end: color-mix( + in srgb, + var(--color-accent-strong) 88%, + transparent + ); + --color-sidebar-text-muted: var(--color-text-muted); + --sidebar-icon-filter: brightness(0) invert(1); + + --color-surface-tint-subtle: rgb(94 113 161 / 0.12); + --color-border-translucent: rgb(99 114 144 / 0.22); + --color-border-translucent-strong: rgb(110 130 170 / 0.28); + --color-border-translucent-bright: rgb(148 163 184 / 0.35); + --color-border-translucent-stronger: rgb(148 163 184 / 0.38); + --color-border-translucent-strongest: rgb(198 207 222 / 0.45); + --color-border-translucent-glow: rgb(129 140 248 / 0.55); + --color-border-translucent-medium: rgb(99 114 144 / 0.28); + --color-border-neutral-soft: rgb(99 114 144 / 0.3); + --color-border-info-soft: rgb(96 165 250 / 0.35); + --color-border-sidebar-strong: rgb(32 46 68 / 0.88); + + --color-ring-private: rgb(168 85 247 / 0.55); + --color-ring-private-hover: rgb(192 132 252 / 0.65); + --color-ring-critical: rgb(239 83 98 / 0.42); + --color-ring-critical-soft: rgb(239 83 98 / 0.35); + --color-ring-critical-hover: rgb(239 83 98 / 0.5); + --color-ring-critical-strong: rgb(220 38 38 / 0.6); + --color-ring-alert: rgb(220 38 38 / 0.6); + + --color-gradient-visibility-start: rgb(129 140 248 / 0.4); + --color-gradient-visibility-end: rgb(192 132 252 / 0.32); + + --color-white-soft: rgb(255 255 255 / 0.1); + --color-white-softer: rgb(255 255 255 / 0.3); + --color-white-softest: rgb(255 255 255 / 0.4); + + --color-shadow-floating: rgb(8 11 30 / 0.55); + --color-shadow-elevated: rgb(8 11 30 / 0.45); + --color-shadow-panel: rgb(15 23 42 / 0.35); + --color-shadow-notification: rgb(15 23 42 / 0.3); + --color-shadow-intense: rgb(250 204 21 / 0.48); + --color-shadow-sidebar: rgb(0 0 0 / 0.45); + --color-shadow-modal: rgb(8 11 30 / 0.55); + + --color-divider-strong: rgb(15 23 42 / 0.35); + --color-text-frosted-strong: rgb(226 232 240 / 0.94); + --color-text-frosted: rgb(226 232 240 / 0.9); + --color-text-frosted-soft: rgb(226 232 240 / 0.82); +} +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} +/* ! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com */ +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ +*, +::before, +::after { + box-sizing: border-box; /* 1 */ + border-width: 0; /* 2 */ + border-style: solid; /* 2 */ + border-color: #e5e7eb; /* 2 */ +} +::before, +::after { + --tw-content: ''; +} +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +7. Disable tap highlights on iOS +*/ +html, +:host { + line-height: 1.5; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ + -moz-tab-size: 4; /* 3 */ + -o-tab-size: 4; + tab-size: 4; /* 3 */ + font-family: var(--font-family-sans); /* 4 */ + font-feature-settings: normal; /* 5 */ + font-variation-settings: normal; /* 6 */ + -webkit-tap-highlight-color: transparent; /* 7 */ +} +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ +body { + margin: 0; /* 1 */ + line-height: inherit; /* 2 */ +} +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ +hr { + height: 0; /* 1 */ + color: inherit; /* 2 */ + border-top-width: 1px; /* 3 */ +} +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} +/* +Remove the default font size and weight for headings. +*/ +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ +a { + color: inherit; + text-decoration: inherit; +} +/* +Add the correct font weight in Edge and Safari. +*/ +b, +strong { + font-weight: bolder; +} +/* +1. Use the user's configured `mono` font-family by default. +2. Use the user's configured `mono` font-feature-settings by default. +3. Use the user's configured `mono` font-variation-settings by default. +4. Correct the odd `em` font sizing in all browsers. +*/ +code, +kbd, +samp, +pre { + font-family: var(--font-family-mono); /* 1 */ + font-feature-settings: normal; /* 2 */ + font-variation-settings: normal; /* 3 */ + font-size: 1em; /* 4 */ +} +/* +Add the correct font size in all browsers. +*/ +small { + font-size: 80%; +} +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sub { + bottom: -0.25em; +} +sup { + top: -0.5em; +} +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ +table { + text-indent: 0; /* 1 */ + border-color: inherit; /* 2 */ + border-collapse: collapse; /* 3 */ +} +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-feature-settings: inherit; /* 1 */ + font-variation-settings: inherit; /* 1 */ + font-size: 100%; /* 1 */ + font-weight: inherit; /* 1 */ + line-height: inherit; /* 1 */ + letter-spacing: inherit; /* 1 */ + color: inherit; /* 1 */ + margin: 0; /* 2 */ + padding: 0; /* 3 */ +} +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ +button, +select { + text-transform: none; +} +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ +button, +input:where([type='button']), +input:where([type='reset']), +input:where([type='submit']) { + -webkit-appearance: button; /* 1 */ + background-color: transparent; /* 2 */ + background-image: none; /* 2 */ +} +/* +Use the modern Firefox focus style for all focusable elements. +*/ +:-moz-focusring { + outline: auto; +} +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ +:-moz-ui-invalid { + box-shadow: none; +} +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ +progress { + vertical-align: baseline; +} +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ +[type='search'] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ +::-webkit-search-decoration { + -webkit-appearance: none; +} +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} +/* +Add the correct display in Chrome and Safari. +*/ +summary { + display: list-item; +} +/* +Removes the default spacing and border for appropriate elements. +*/ +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} +fieldset { + margin: 0; + padding: 0; +} +legend { + padding: 0; +} +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} +/* +Reset default styling for dialogs. +*/ +dialog { + padding: 0; +} +/* +Prevent resizing textareas horizontally by default. +*/ +textarea { + resize: vertical; +} +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; /* 1 */ + color: #9ca3af; /* 2 */ +} +input::placeholder, +textarea::placeholder { + opacity: 1; /* 1 */ + color: #9ca3af; /* 2 */ +} +/* +Set the default cursor for buttons. +*/ +button, +[role="button"] { + cursor: pointer; +} +/* +Make sure disabled buttons don't get the pointer cursor. +*/ +:disabled { + cursor: default; +} +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; /* 1 */ + vertical-align: middle; /* 2 */ +} +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ +img, +video { + max-width: 100%; + height: auto; +} +/* Make elements with the HTML hidden attribute stay hidden by default */ +[hidden]:where(:not([hidden="until-found"])) { + display: none; +} +html { + font-size: 80%; + } +@media (min-width: 768px) { + html { + font-size: 100%; + } + } +@media (min-width: 1024px) { + html { + font-size: 75%; + } + } +*, + *::before, + *::after { + scrollbar-width: thin; + scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track); + } +*::-webkit-scrollbar { + width: var(--scrollbar-width); + height: var(--scrollbar-width); + } +*::-webkit-scrollbar-track { + background: var(--scrollbar-track); + } +*::-webkit-scrollbar-thumb { + background: var(--scrollbar-thumb); + border-radius: var(--radius-sm); + } +*::-webkit-scrollbar-thumb:hover { + background: var(--scrollbar-thumb-hover); + } +body { + --app-header-centerline: calc( + env(safe-area-inset-top, 0) + var(--app-header-centerline-offset) + ); + font-family: var(--font-family-sans); + background-color: var(--color-page); + color: var(--color-text-strong); + line-height: 1.5; + margin: 0; + padding: 0; + overflow-x: hidden; + } +:root[data-theme-accent="override"] { + --color-accent: var(--color-accent-override); + --color-accent-strong: var(--color-accent-strong-override); + --color-accent-pressed: var(--color-accent-pressed-override); + } +#uiOverlay { + position: fixed; + inset: 0; + pointer-events: none; + z-index: var(--z-overlay-popover); + } +#uiOverlay > * { + pointer-events: auto; + } +#nip71TagsFieldset > legend, + #editNip71TagsFieldset > legend { + margin-left: 0; + padding: 0; + } +#app { + background-color: var(--color-page); + color: inherit; + } +html.modal-open, + body.modal-open { + overflow: hidden; + height: 100%; + } +body.modal-open #app { + overflow: hidden; + } +body.modal-open #sidebar { + pointer-events: none; + opacity: 0; + visibility: hidden; + z-index: var(--z-sidebar-overlay); + } +[ng\:cloak], + [ng-cloak], + [data-ng-cloak], + [x-ng-cloak], + .ng-cloak, + .x-ng-cloak, + .ng-hide:not(.ng-hide-animate) { + display: none !important; + } +ng\:form { + display: block; + } +.\!container { + width: 100% !important; +} +.container { + width: 100%; +} +@media (min-width: 400px) { + .\!container { + max-width: 400px !important; + } + .container { + max-width: 400px; + } +} +@media (min-width: 480px) { + .\!container { + max-width: 480px !important; + } + .container { + max-width: 480px; + } +} +@media (min-width: 640px) { + .\!container { + max-width: 640px !important; + } + .container { + max-width: 640px; + } +} +@media (min-width: 768px) { + .\!container { + max-width: 768px !important; + } + .container { + max-width: 768px; + } +} +@media (min-width: 1024px) { + .\!container { + max-width: 1024px !important; + } + .container { + max-width: 1024px; + } +} +@media (min-width: 1280px) { + .\!container { + max-width: 1280px !important; + } + .container { + max-width: 1280px; + } +} +@media (min-width: 1536px) { + .\!container { + max-width: 1536px !important; + } + .container { + max-width: 1536px; + } +} +.ds-header { + display: flex; + align-items: center; + justify-content: flex-start; + padding-top: var(--space-sm); + padding-bottom: var(--space-sm); +} +.app-shell__header { + position: sticky; + top: env(safe-area-inset-top, 0); + z-index: var(--z-app-header); + padding-top: calc(var(--space-sm) + env(safe-area-inset-top, 0)); + padding-bottom: var(--space-sm); + background-color: transparent; + background-image: var(--surface-overlay-banner-gradient-header); + background-repeat: no-repeat; + background-size: 100% 100%; + } +.ds-metric-probe { + position: absolute; + top: 0; + left: 0; + visibility: hidden; + pointer-events: none; + contain: layout size style; + inline-size: 0; + block-size: var(--ds-metric-probe-length, 0); + overflow: hidden; + } +.ds-header__logo { + height: var(--space-2xl-plus); + width: auto; + max-width: none; +} +.focus-ring { + outline: 2px solid transparent; + outline-offset: 2px; +} +.focus-ring:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.author-pic { + display: block; + height: 100%; + width: 100%; + border-radius: var(--radius-full); + -o-object-fit: cover; + object-fit: cover; +} +.header-icon-toggle { + outline: 2px solid transparent; + outline-offset: 2px; +} +.header-icon-toggle:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.header-icon-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: var(--radius-full); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + width: var(--icon-size-lg); + height: var(--icon-size-lg); + min-width: var(--icon-size-lg); + min-height: var(--icon-size-lg); + padding: 0; + border: none; + background: transparent; + color: var(--color-text-muted); + line-height: 1; +} +.header-icon-toggle[data-theme-state="light"] { + color: var(--color-text-strong); + } +.header-icon-toggle[data-theme-state="dark"] { + color: var(--color-text); + } +.header-icon-toggle__icon-wrapper { + display: inline-flex; + align-items: center; + justify-content: center; + inline-size: var(--space-lg); + block-size: var(--space-lg); +} +.header-icon-toggle__icon { + inline-size: 100%; + block-size: 100%; + display: none; + fill: currentColor; + } +.header-icon-toggle__icon-wrapper:not([data-theme-icon-state]), + .header-icon-toggle__icon-wrapper[data-theme-icon-state="light"] { + color: inherit; + } +.header-icon-toggle__icon-wrapper[data-theme-icon-state="light"] + .header-icon-toggle__icon--dark { + display: block; + } +.header-icon-toggle__icon-wrapper[data-theme-icon-state="dark"] + .header-icon-toggle__icon--light { + display: block; + } +.header-icon-toggle__icon-wrapper:not([data-theme-icon-state]) + .header-icon-toggle__icon--dark { + display: block; + } +#profileUnreadDot { + position: absolute; + top: var(--space-2xs); + right: var(--space-2xs); + width: var(--space-sm); + height: var(--space-sm); + border-radius: var(--radius-full); + border: var(--border-width-hairline) solid var(--color-page); + background-color: var(--status-danger); + display: none; + pointer-events: none; + } +#profileUnreadDot.is-visible { + display: block; + } +.header-icon-toggle[data-theme-state="light"]:hover, + .header-icon-toggle[data-theme-state="light"]:focus-visible { + color: var(--color-text); + } +.header-icon-toggle[data-theme-state="dark"]:hover, + .header-icon-toggle[data-theme-state="dark"]:focus-visible { + color: var(--color-text-strong); + } +.header-icon-toggle[aria-pressed="true"] { + color: var(--color-text-strong); + } +.header-icon-toggle[aria-pressed="true"]:hover, + .header-icon-toggle[aria-pressed="true"]:focus-visible { + color: var(--color-text-strong); + } + + .bv-modal.header-search__region { + display: none; + } +.profile-pane.header-search__region { + display: none; + } +.header-search__region { + display: none; + flex: 1 1 0%; + justify-content: center; +} +@media (min-width: 768px) { + .header-search__region { + display: flex; + } +} +.header-search__region { + min-width: 0; + padding-inline: clamp(var(--space-md), 4vw, var(--space-3xl)); + } +.header-search { + display: flex; + align-items: center; + gap: 0.75rem; + border-radius: var(--radius-full); + border-width: 1px; + border-color: var(--border-default); + --tw-border-opacity: 0.4; + background-color: var(--surface-overlay-panel); + padding-left: 1rem; + padding-right: 1rem; + --tw-shadow: var(--shadow-sm); + --tw-shadow-colored: var(--shadow-sm); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + backdrop-filter: blur(var(--popover-backdrop-blur)); + -webkit-backdrop-filter: blur(var(--popover-backdrop-blur)); + padding-block: calc(var(--space-sm) * 0.9); + inline-size: min(100%, var(--header-search-max-inline)); +} +.header-search:focus-within { + border-color: var(--color-accent-strong); +} +.header-search__icon { + display: block; + height: 1.25rem; + width: 1.25rem; + background-color: currentColor; + mask-repeat: no-repeat; + mask-position: center; + mask-size: contain; + -webkit-mask-repeat: no-repeat; + -webkit-mask-position: center; + -webkit-mask-size: contain; +} +.header-search__icon--filter { + mask-image: url("../assets/svg/filter.svg"); + -webkit-mask-image: url("../assets/svg/filter.svg"); + } +.header-search__icon--submit { + mask-image: url("../assets/svg/search.svg"); + -webkit-mask-image: url("../assets/svg/search.svg"); + } +.header-search__filter { + outline: 2px solid transparent; + outline-offset: 2px; +} +.header-search__filter:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.header-search__filter { + display: inline-flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + border-radius: var(--radius-full); + border-width: 1px; + border-color: transparent; + background-color: transparent; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + color: var(--color-accent); + inline-size: calc(var(--icon-size-lg) * 0.9); + block-size: calc(var(--icon-size-lg) * 0.9); + min-inline-size: calc(var(--icon-size-lg) * 0.9); + min-block-size: calc(var(--icon-size-lg) * 0.9); +} +.header-search__filter:hover, + .header-search__filter:focus-visible { + color: var(--color-accent-strong); + } +.header-search__input { + flex: 1 1 0%; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: transparent; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + color: var(--text-primary); + outline: 2px solid transparent; + outline-offset: 2px; +} +.header-search__input::-moz-placeholder { + color: var(--text-muted); +} +.header-search__input::placeholder { + color: var(--text-muted); +} +.header-search__input { + min-width: 0; + } +.header-search__input:focus-visible { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} +.header-search__submit { + outline: 2px solid transparent; + outline-offset: 2px; +} +.header-search__submit:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.header-search__submit { + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: var(--radius-full); + border-width: 1px; + border-color: transparent; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + background-color: var(--color-accent-strong); + color: var(--color-white); + inline-size: calc(var(--icon-size-lg) * 0.9); + block-size: calc(var(--icon-size-lg) * 0.9); + min-inline-size: calc(var(--icon-size-lg) * 0.9); + min-block-size: calc(var(--icon-size-lg) * 0.9); + padding: 0; +} +.header-search__submit:hover { + background-color: var(--color-accent); + } +.header-search__submit:focus-visible { + background-color: var(--color-accent-pressed); + } +.bv-logo { + --logo-wordmark-color: var(--color-logo-wordmark); + --logo-accent-color: var(--color-logo-accent); + --logo-background-color: var(--color-logo-background); + } +.bv-logo[data-variant="inverse"] { + --logo-wordmark-color: var(--color-logo-wordmark-inverse); + --logo-background-color: var(--color-logo-background-inverse); + } +.bv-logo[data-wordmark="current"] { + --logo-wordmark-color: currentColor; + } +.bv-logo[data-accent="current"] { + --logo-accent-color: currentColor; + } +.bv-logo[data-background="transparent"] { + --logo-background-color: transparent; + } +.bv-logo__wordmark { + fill: var(--logo-wordmark-color); + color: var(--logo-wordmark-color); + } +.bv-logo__accent { + fill: var(--logo-accent-color); + color: var(--logo-accent-color); + } +.bv-logo__background { + fill: var(--logo-background-color); + color: var(--logo-background-color); + } +.markdown-prose { + color: var(--color-text); + max-width: none; +} +.markdown-prose :where(p):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + margin-bottom: 1rem; +} +.markdown-prose :where([class~="lead"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-lead); + font-size: 1.25em; + line-height: 1.6; + margin-top: 1.2em; + margin-bottom: 1.2em; +} +.markdown-prose :where(a):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--color-primary); + text-decoration: none; + font-weight: 500; +} +.markdown-prose :where(strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-bold); + font-weight: 600; +} +.markdown-prose :where(a strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} +.markdown-prose :where(blockquote strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} +.markdown-prose :where(thead th strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} +.markdown-prose :where(ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: decimal; + margin-top: 1.25em; + margin-bottom: 1.25em; + padding-inline-start: 1.625em; +} +.markdown-prose :where(ol[type="A"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: upper-alpha; +} +.markdown-prose :where(ol[type="a"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: lower-alpha; +} +.markdown-prose :where(ol[type="A" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: upper-alpha; +} +.markdown-prose :where(ol[type="a" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: lower-alpha; +} +.markdown-prose :where(ol[type="I"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: upper-roman; +} +.markdown-prose :where(ol[type="i"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: lower-roman; +} +.markdown-prose :where(ol[type="I" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: upper-roman; +} +.markdown-prose :where(ol[type="i" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: lower-roman; +} +.markdown-prose :where(ol[type="1"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: decimal; +} +.markdown-prose :where(ul):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: disc; + margin-top: 1.25em; + margin-bottom: 1.25em; + padding-inline-start: 1.625em; +} +.markdown-prose :where(ol > li):not(:where([class~="not-prose"],[class~="not-prose"] *))::marker { + font-weight: 400; + color: var(--tw-prose-counters); +} +.markdown-prose :where(ul > li):not(:where([class~="not-prose"],[class~="not-prose"] *))::marker { + color: var(--tw-prose-bullets); +} +.markdown-prose :where(dt):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + margin-top: 1.25em; +} +.markdown-prose :where(hr):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + border-color: var(--color-border-subtle); + border-top-width: 1px; + margin-top: 2rem; + margin-bottom: 2rem; +} +.markdown-prose :where(blockquote):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 500; + font-style: italic; + color: var(--color-muted); + border-inline-start-width: 0.25rem; + border-inline-start-color: var(--tw-prose-quote-borders); + quotes: "\201C""\201D""\2018""\2019"; + margin-top: 1rem; + margin-bottom: 1rem; + padding-inline-start: 1em; + border-left-width: 4px; + border-left-color: var(--color-border-subtle); + padding-left: 1rem; +} +.markdown-prose :where(blockquote p:first-of-type):not(:where([class~="not-prose"],[class~="not-prose"] *))::before { + content: open-quote; +} +.markdown-prose :where(blockquote p:last-of-type):not(:where([class~="not-prose"],[class~="not-prose"] *))::after { + content: close-quote; +} +.markdown-prose :where(h1):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 700; + font-size: var(--font-size-2xl); + margin-top: 0.5rem; + margin-bottom: 1rem; + line-height: 1.1111111; +} +.markdown-prose :where(h1 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 900; + color: inherit; +} +.markdown-prose :where(h2):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 700; + font-size: var(--font-size-xl); + margin-top: 1.25rem; + margin-bottom: 0.75rem; + line-height: 1.3333333; +} +.markdown-prose :where(h2 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 800; + color: inherit; +} +.markdown-prose :where(h3):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + font-size: var(--font-size-lg); + margin-top: 1rem; + margin-bottom: 0.75rem; + line-height: 1.6; +} +.markdown-prose :where(h3 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 700; + color: inherit; +} +.markdown-prose :where(h4):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + margin-top: 1.5em; + margin-bottom: 0.5em; + line-height: 1.5; +} +.markdown-prose :where(h4 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 700; + color: inherit; +} +.markdown-prose :where(img):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + margin-bottom: 1rem; + border-radius: var(--radius-md); + max-width: 100%; + height: auto; +} +.markdown-prose :where(picture):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + display: block; + margin-top: 2em; + margin-bottom: 2em; +} +.markdown-prose :where(video):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 2em; + margin-bottom: 2em; +} +.markdown-prose :where(kbd):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 500; + font-family: inherit; + color: var(--tw-prose-kbd); + box-shadow: 0 0 0 1px var(--tw-prose-kbd-shadows), 0 3px 0 var(--tw-prose-kbd-shadows); + font-size: 0.875em; + border-radius: 0.3125rem; + padding-top: 0.1875em; + padding-inline-end: 0.375em; + padding-bottom: 0.1875em; + padding-inline-start: 0.375em; +} +.markdown-prose :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--color-secondary); + font-weight: 500; + font-size: var(--font-size-sm); + background-color: var(--color-page-alt); + padding: 0.2rem 0.4rem; + border-radius: var(--radius-md); + font-family: var(--font-family-mono); +} +.markdown-prose :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *))::before { + content: none; +} +.markdown-prose :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *))::after { + content: none; +} +.markdown-prose :where(a code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} +.markdown-prose :where(h1 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} +.markdown-prose :where(h2 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; + font-size: 0.875em; +} +.markdown-prose :where(h3 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; + font-size: 0.9em; +} +.markdown-prose :where(h4 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} +.markdown-prose :where(blockquote code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} +.markdown-prose :where(thead th code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} +.markdown-prose :where(pre):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--color-text-strong); + background-color: var(--color-page-alt); + overflow-x: auto; + font-weight: 400; + font-size: var(--font-size-sm); + line-height: 1.7142857; + margin-top: 0; + margin-bottom: 1rem; + border-radius: var(--radius-md); + padding-top: 0.8571429em; + padding-inline-end: 1.1428571em; + padding-bottom: 0.8571429em; + padding-inline-start: 1.1428571em; + padding: 1rem; + font-family: var(--font-family-mono); +} +.markdown-prose :where(pre code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + background-color: transparent; + border-width: 0; + border-radius: 0; + padding: 0; + font-weight: inherit; + color: inherit; + font-size: inherit; + font-family: inherit; + line-height: inherit; +} +.markdown-prose :where(pre code):not(:where([class~="not-prose"],[class~="not-prose"] *))::before { + content: none; +} +.markdown-prose :where(pre code):not(:where([class~="not-prose"],[class~="not-prose"] *))::after { + content: none; +} +.markdown-prose :where(table):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + width: 100%; + table-layout: auto; + margin-top: 2em; + margin-bottom: 2em; + font-size: 0.875em; + line-height: 1.7142857; +} +.markdown-prose :where(thead):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + border-bottom-width: 1px; + border-bottom-color: var(--tw-prose-th-borders); +} +.markdown-prose :where(thead th):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + vertical-align: bottom; + padding-inline-end: 0.5714286em; + padding-bottom: 0.5714286em; + padding-inline-start: 0.5714286em; +} +.markdown-prose :where(tbody tr):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + border-bottom-width: 1px; + border-bottom-color: var(--tw-prose-td-borders); +} +.markdown-prose :where(tbody tr:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + border-bottom-width: 0; +} +.markdown-prose :where(tbody td):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + vertical-align: baseline; +} +.markdown-prose :where(tfoot):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + border-top-width: 1px; + border-top-color: var(--tw-prose-th-borders); +} +.markdown-prose :where(tfoot td):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + vertical-align: top; +} +.markdown-prose :where(th, td):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + text-align: start; +} +.markdown-prose :where(figure > *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + margin-bottom: 0; +} +.markdown-prose :where(figcaption):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-captions); + font-size: 0.875em; + line-height: 1.4285714; + margin-top: 0.8571429em; +} +.markdown-prose { + --tw-prose-body: var(--color-text); + --tw-prose-headings: var(--color-text-strong); + --tw-prose-lead: #4b5563; + --tw-prose-links: var(--color-primary); + --tw-prose-bold: var(--color-text-strong); + --tw-prose-counters: var(--color-text-muted); + --tw-prose-bullets: var(--color-text-muted); + --tw-prose-hr: var(--color-border-subtle); + --tw-prose-quotes: var(--color-muted); + --tw-prose-quote-borders: #e5e7eb; + --tw-prose-captions: #6b7280; + --tw-prose-kbd: #111827; + --tw-prose-kbd-shadows: rgb(17 24 39 / 10%); + --tw-prose-code: var(--color-secondary); + --tw-prose-pre-code: #e5e7eb; + --tw-prose-pre-bg: #1f2937; + --tw-prose-th-borders: var(--color-border-subtle); + --tw-prose-td-borders: var(--color-border-subtle); + --tw-prose-invert-body: #d1d5db; + --tw-prose-invert-headings: #fff; + --tw-prose-invert-lead: #9ca3af; + --tw-prose-invert-links: #fff; + --tw-prose-invert-bold: #fff; + --tw-prose-invert-counters: #9ca3af; + --tw-prose-invert-bullets: #4b5563; + --tw-prose-invert-hr: #374151; + --tw-prose-invert-quotes: #f3f4f6; + --tw-prose-invert-quote-borders: #374151; + --tw-prose-invert-captions: #9ca3af; + --tw-prose-invert-kbd: #fff; + --tw-prose-invert-kbd-shadows: rgb(255 255 255 / 10%); + --tw-prose-invert-code: #fff; + --tw-prose-invert-pre-code: #d1d5db; + --tw-prose-invert-pre-bg: rgb(0 0 0 / 50%); + --tw-prose-invert-th-borders: #4b5563; + --tw-prose-invert-td-borders: #374151; + font-size: 1rem; + line-height: 1.6; +} +.markdown-prose :where(picture > img):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + margin-bottom: 0; +} +.markdown-prose :where(li):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} +.markdown-prose :where(ol > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-inline-start: 0.375em; +} +.markdown-prose :where(ul > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-inline-start: 0.375em; +} +.markdown-prose :where(.prose > ul > li p):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.75em; + margin-bottom: 0.75em; +} +.markdown-prose :where(.prose > ul > li > p:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.25em; +} +.markdown-prose :where(.prose > ul > li > p:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-bottom: 1.25em; +} +.markdown-prose :where(.prose > ol > li > p:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.25em; +} +.markdown-prose :where(.prose > ol > li > p:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-bottom: 1.25em; +} +.markdown-prose :where(ul ul, ul ol, ol ul, ol ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.75em; + margin-bottom: 0.75em; +} +.markdown-prose :where(dl):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.25em; + margin-bottom: 1.25em; +} +.markdown-prose :where(dd):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.5em; + padding-inline-start: 1.625em; +} +.markdown-prose :where(hr + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; +} +.markdown-prose :where(h2 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; +} +.markdown-prose :where(h3 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; +} +.markdown-prose :where(h4 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; +} +.markdown-prose :where(thead th:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-inline-start: 0; +} +.markdown-prose :where(thead th:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-inline-end: 0; +} +.markdown-prose :where(tbody td, tfoot td):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-top: 0.5714286em; + padding-inline-end: 0.5714286em; + padding-bottom: 0.5714286em; + padding-inline-start: 0.5714286em; +} +.markdown-prose :where(tbody td:first-child, tfoot td:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-inline-start: 0; +} +.markdown-prose :where(tbody td:last-child, tfoot td:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-inline-end: 0; +} +.markdown-prose :where(figure):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 2em; + margin-bottom: 2em; +} +.markdown-prose :where(.prose > :first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; +} +.markdown-prose :where(.prose > :last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-bottom: 0; +} +.markdown-prose { + font-family: var(--font-family-sans); +} +.markdown-prose :where(h4, h5, h6):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 600; + margin-top: 0.75rem; + margin-bottom: 0.5rem; +} +.markdown-prose :where(ul, ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + margin-bottom: 1rem; + padding-left: 1.25rem; +} +.markdown-prose :where(a:hover):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + text-decoration: underline; +} +.markdown-prose { + max-width: none; + line-height: 1.625; + color: var(--text-primary); + --tw-prose-body: var(--color-text); + --tw-prose-headings: var(--color-text-strong); + --tw-prose-invert-headings: var(--color-text-strong); + --tw-prose-lead: var(--color-text); + --tw-prose-links: var(--color-accent-strong); + --tw-prose-bold: var(--color-text-strong); + --tw-prose-counters: var(--color-text-muted); + --tw-prose-bullets: var(--color-text-muted); + --tw-prose-hr: var(--color-border-translucent-strong); + --tw-prose-quotes: var(--color-text-strong); + --tw-prose-quote-borders: var(--color-border-subtle); + --tw-prose-captions: var(--color-text-muted); + --tw-prose-code: var(--color-text-strong); + --tw-prose-pre-code: var(--color-text); + --tw-prose-pre-bg: var(--surface-muted); + --tw-prose-th-borders: var(--color-border); + --tw-prose-td-borders: var(--color-border-subtle); +} +.markdown-prose :where(h1, h2, h3, h4, h5, h6) { + color: var(--color-text-strong); + } +.markdown-prose :where(h1, h2, h3, h4, h5, h6) strong { + color: inherit; + } +.markdown-prose a { + color: var(--color-accent-strong); + text-decoration-color: color-mix( + in srgb, + var(--color-accent-strong) 65%, + transparent + ); + text-decoration-thickness: var(--link-underline-thickness); + text-underline-offset: 0.2em; + } +.markdown-prose a:hover, + .markdown-prose a:focus-visible { + color: var(--color-accent); + text-decoration-line: underline; + } +.markdown-prose blockquote { + border-radius: var(--radius-md); + border-left-width: 4px; + border-color: var(--border-subtle); + background-color: var(--surface-muted); + --tw-bg-opacity: 0.5; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + color: var(--text-muted); +} +.markdown-prose :where(pre, code) { + font-family: var(--font-family-mono); +} +.markdown-prose code { + border-radius: var(--radius-md); + background-color: var(--surface-muted); + padding-left: 0.25rem; + padding-right: 0.25rem; + padding-top: 0.125rem; + padding-bottom: 0.125rem; + color: var(--color-accent-strong); +} +.markdown-prose pre { + overflow-x: auto; + border-radius: var(--radius-lg); + border-width: 1px; + border-color: var(--border-subtle); + background-color: var(--surface-muted); + padding: 1rem; + color: var(--text-primary); +} +.markdown-prose pre code { + background-color: transparent; + color: inherit; + padding: 0; + } +.\!popover { + position: relative; + display: inline-flex; +} +.popover { + position: relative; + display: inline-flex; +} +.popover-panel, + .popover__panel { + outline: 2px solid transparent; + outline-offset: 2px; +} +.popover-panel:focus-visible, + .popover__panel:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.popover-panel, + .popover__panel { + pointer-events: none; + z-index: var(--z-overlay-popover); + min-width: var(--menu-min-width); + max-width: var(--popover-inline-safe-max); + --tw-scale-x: .95; + --tw-scale-y: .95; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + border-radius: var(--overlay-panel-radius); + border-width: 1px; + border-color: var(--border-default); + --tw-border-opacity: 0.4; + background-color: var(--surface-overlay-panel); + padding-left: var(--overlay-panel-padding-inline); + padding-right: var(--overlay-panel-padding-inline); + padding-top: var(--overlay-panel-padding-block); + padding-bottom: var(--overlay-panel-padding-block); + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + color: var(--text-primary); + opacity: 0; + --tw-shadow: var(--overlay-panel-shadow); + --tw-shadow-colored: var(--overlay-panel-shadow); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + --tw-shadow-color: var(--surface-overlay-panel); + --tw-shadow: var(--tw-shadow-colored); + transition-property: opacity; + transition-property: transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + position: var(--popover-strategy, fixed); + left: var(--popover-left, 0); + top: var(--popover-top, 0); + max-width: var(--popover-max-width, none); + max-height: var(--popover-max-height, none); +} +.popover-panel[data-state="open"], + .popover__panel[data-state="open"] { + pointer-events: auto; + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + opacity: 1; +} +.popover-panel[data-state="closed"], + .popover__panel[data-state="closed"] { + pointer-events: none; + --tw-scale-x: .95; + --tw-scale-y: .95; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + opacity: 0; +} +.search-filter-chip { + display: inline-flex; + align-items: center; + gap: 0.5rem; + border-radius: var(--radius-full); + border-width: 1px; + border-color: var(--border-subtle); + background-color: var(--surface-alt); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + font-weight: 500; + color: var(--text-primary); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +} +.search-filter-chip[data-state="on"] { + border-color: var(--color-accent-strong); + background-color: var(--color-accent-strong); + color: var(--color-white); +} +[data-popover-arrow="true"] { + left: var(--popover-arrow-left, auto); + right: var(--popover-arrow-right, auto); + top: var(--popover-arrow-top, auto); + bottom: var(--popover-arrow-bottom, auto); + } +.ds-overlay-backdrop { + position: fixed; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background: var(--overlay-backdrop-color); + backdrop-filter: blur(var(--overlay-backdrop-blur)); + -webkit-backdrop-filter: blur(var(--overlay-backdrop-blur)); + } +.popover-panel[data-variant="zap"], + .popover__panel[data-variant="zap"] { + background: var(--color-overlay-veil); + color: var(--color-subtle); + border: var(--border-width-hairline) solid + var(--color-border-translucent-stronger); + box-shadow: var(--shadow-popover-zap); + backdrop-filter: blur(var(--popover-backdrop-blur)); + -webkit-backdrop-filter: blur(var(--popover-backdrop-blur)); + } +.\!btn { + outline: 2px solid transparent; + outline-offset: 2px; +} +.\!btn:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.\!btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + border-radius: var(--radius-full); + border-width: 1px; + border-color: transparent; + background-color: var(--color-accent-strong); + padding-left: 1.25rem; + padding-right: 1.25rem; + padding-top: 0.625rem; + padding-bottom: 0.625rem; + font-size: var(--font-size-base); + line-height: var(--line-height-base); + font-weight: 600; + color: var(--color-white); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.btn { + outline: 2px solid transparent; + outline-offset: 2px; +} +.btn:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + border-radius: var(--radius-full); + border-width: 1px; + border-color: transparent; + background-color: var(--color-accent-strong); + padding-left: 1.25rem; + padding-right: 1.25rem; + padding-top: 0.625rem; + padding-bottom: 0.625rem; + font-size: var(--font-size-base); + line-height: var(--line-height-base); + font-weight: 600; + color: var(--color-white); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.\!btn[data-size="sm"] { + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.375rem; + padding-bottom: 0.375rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); +} +.btn[data-size="sm"], + .btn-ghost[data-size="sm"] { + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.375rem; + padding-bottom: 0.375rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); +} +.\!btn[data-size="sm"] { + gap: 0.375rem; +} +.btn[data-size="sm"], + .btn-ghost[data-size="sm"] { + gap: 0.375rem; +} +.\!btn:hover { + background-color: var(--color-accent); +} +.btn:hover { + background-color: var(--color-accent); +} +.\!btn:focus-visible { + background-color: var(--color-accent-pressed); +} +.btn:focus-visible { + background-color: var(--color-accent-pressed); +} +.\!btn:disabled { + pointer-events: none; + cursor: not-allowed; + opacity: 0.6; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.btn:disabled { + pointer-events: none; + cursor: not-allowed; + opacity: 0.6; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.btn-link { + outline: 2px solid transparent; + outline-offset: 2px; +} +.btn-link:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.btn-link { + display: inline-flex; + align-items: center; + gap: 0.25rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + font-weight: 600; + color: var(--color-accent); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +} +.btn-link:hover, + .btn-link:focus-visible { + color: var(--color-accent-strong); + text-decoration-line: underline; +} +.progress, + [data-progress-meter] { + --progress-height: var(--progress-height-md); + --progress-track: var(--progress-track-color); + --progress-fill: var(--progress-fill-color); + --progress-radius-current: var(--progress-radius); + display: block; + width: 100%; + height: var(--progress-height); + border: none; + border-radius: var(--progress-radius-current); + overflow: hidden; + background-color: var(--progress-track); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + } +.progress::-webkit-progress-bar, + [data-progress-meter]::-webkit-progress-bar { + background-color: var(--progress-track); + border-radius: var(--progress-radius-current); + } +.progress::-webkit-progress-value, + [data-progress-meter]::-webkit-progress-value { + background-color: var(--progress-fill); + border-radius: var(--progress-radius-current); + -webkit-transition: width var(--progress-transition-duration) + var(--progress-transition-ease); + transition: width var(--progress-transition-duration) + var(--progress-transition-ease); + } +.progress::-moz-progress-bar, + [data-progress-meter]::-moz-progress-bar { + background-color: var(--progress-fill); + border-radius: var(--progress-radius-current); + -moz-transition: width var(--progress-transition-duration) + var(--progress-transition-ease); + transition: width var(--progress-transition-duration) + var(--progress-transition-ease); + } +.progress[data-variant="surface"], + [data-progress-meter][data-variant="surface"], + .progress.progress--surface, + [data-progress-meter].progress--surface { + --progress-track: var(--color-panel); + } +.progress[data-variant="accent"], + [data-progress-meter][data-variant="accent"], + .progress.progress--accent, + [data-progress-meter].progress--accent { + --progress-fill: var(--color-info-strong); + } +.btn-ghost { + outline: 2px solid transparent; + outline-offset: 2px; +} +.btn-ghost:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.btn-ghost { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + border-radius: var(--radius-full); + border-width: 1px; + border-color: transparent; + background-color: var(--color-accent-strong); + padding-left: 1.25rem; + padding-right: 1.25rem; + padding-top: 0.625rem; + padding-bottom: 0.625rem; + font-size: var(--font-size-base); + line-height: var(--line-height-base); + font-weight: 600; + color: var(--color-white); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.btn-ghost[data-size="sm"] { + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.375rem; + padding-bottom: 0.375rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + gap: 0.375rem; +} +.btn-ghost:hover { + background-color: var(--color-accent); +} +.btn-ghost:focus-visible { + background-color: var(--color-accent-pressed); +} +.btn-ghost:disabled { + pointer-events: none; + cursor: not-allowed; + opacity: 0.6; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +body.iframe-form button.btn-ghost { + align-self: flex-start; + margin-top: var(--space-md); + } +.btn-ghost { + border-width: 1px; + border-color: var(--border-default); + --tw-border-opacity: 0.6; + background-color: transparent; + color: var(--text-primary); + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +@media (prefers-reduced-motion: reduce) { + + .btn-ghost { + transition-property: none; + } +} +.btn-ghost:hover { + background-color: var(--surface-panel-hover); + color: var(--text-primary); +} +.btn-ghost:focus-visible { + background-color: var(--surface-panel-hover); +} +.btn-ghost:disabled { + pointer-events: none; + opacity: 0.5; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.btn-ghost[data-variant="critical"] { + border-width: 1px; + border-color: var(--color-critical); + --tw-border-opacity: 0.6; + color: var(--color-critical); +} +.btn-ghost[data-variant="critical"]:hover, + .btn-ghost[data-variant="critical"]:focus-visible { + background-color: var(--color-critical); + --tw-bg-opacity: 0.2; + color: var(--color-critical-strong); +} +.login-option { + outline: 2px solid transparent; + outline-offset: 2px; +} +.login-option:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.login-option { + position: relative; + display: flex; + height: 100%; + width: 100%; + flex-direction: column; + gap: 1rem; + overflow: hidden; + border-width: 1px; + border-color: var(--border-default); + background-color: var(--surface-overlay-panel-soft); + padding-left: 1.5rem; + padding-right: 1.5rem; + padding-top: 1.25rem; + padding-bottom: 1.25rem; + text-align: left; + color: var(--text-primary); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 300ms; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + border-radius: calc(var(--radius-xl) * 1.12); + isolation: isolate; + backdrop-filter: blur(var(--blur-modal-backdrop)); + background-image: linear-gradient( + 140deg, + color-mix(in srgb, var(--surface-overlay-panel-soft) 88%, transparent) 0%, + color-mix(in srgb, var(--surface-overlay-panel) 72%, transparent) 100% + ); + transform: translate3d(0, 0, 0); + transition-property: border-color, box-shadow, transform; +} +.login-option::after { + content: ""; + position: absolute; + inset: 0; + z-index: 0; + border-radius: inherit; + opacity: 0; + transform: translate3d(0, 14%, 0); + transition: + opacity 220ms ease, + transform 220ms ease; + background: radial-gradient( + 115% 115% at 0% 0%, + color-mix(in srgb, var(--status-neutral) 32%, transparent) 0%, + transparent 65% + ); + } +.login-option:hover, + .login-option:focus-visible { + border-color: var(--border-strong); + box-shadow: var(--shadow-modal); + transform: translate3d(0, var(--motion-login-option-hover-translate), 0); +} +.login-option:hover::after, + .login-option:focus-visible::after { + opacity: 0.65; + transform: translate3d(0, 0, 0); + } +.login-option:disabled, + .login-option[aria-disabled="true"] { + cursor: not-allowed; + opacity: 0.6; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transform: none; +} +.login-option[data-tone="accent"]::after { + background: radial-gradient( + 120% 120% at 0% 0%, + color-mix(in srgb, var(--status-info) 38%, transparent) 0%, + transparent 65% + ); + } +.login-option[data-tone="warning"]::after { + background: radial-gradient( + 120% 120% at 0% 0%, + color-mix(in srgb, var(--status-warning) 42%, transparent) 0%, + transparent 65% + ); + } +.login-option--primary { + border-color: var(--border-default); +} +.login-option--ghost { + background-image: linear-gradient( + 145deg, + color-mix(in srgb, var(--surface-overlay-panel-soft) 82%, transparent) 0%, + color-mix(in srgb, var(--surface-overlay-panel-soft) 68%, transparent) + 100% + ); + } +.login-option--outline { + border-color: var(--border-strong); +} +.login-option--link { + border-width: 1px; + border-color: transparent; + background-color: transparent; + color: var(--color-accent); + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.login-option__header, + .login-option__copy, + .login-option__status, + .login-option__capabilities { + position: relative; + z-index: 1; + } +.login-option__header { + display: flex; + width: 100%; + align-items: flex-start; + justify-content: space-between; + gap: 1rem; +} +.login-option__copy { + display: flex; + min-width: 0px; + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; +} +.login-option__eyebrow { + font-size: var(--font-size-2xs); + font-weight: 600; + text-transform: uppercase; + color: var(--text-muted); + letter-spacing: 0.18em; +} +.login-option:hover .login-option__eyebrow, + .login-option:focus-visible .login-option__eyebrow { + color: var(--text-primary); +} +.login-option__label { + font-size: var(--font-size-lg); + line-height: var(--line-height-base); + font-weight: 600; + color: var(--text-primary); +} +.login-option__description { + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + line-height: 1.625; + color: var(--text-muted); +} +.login-option__chevron { + display: inline-flex; + height: 2.25rem; + width: 2.25rem; + align-items: center; + justify-content: center; + border-radius: var(--radius-full); + background-color: var(--surface-overlay-panel); + color: var(--text-muted); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 200ms; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + box-shadow: inset 0 0 0 var(--border-width-hairline) + color-mix(in srgb, var(--border-overlay) 60%, transparent); +} +.login-option:hover .login-option__chevron, + .login-option:focus-visible .login-option__chevron { + background-color: var(--color-accent); + color: var(--color-white); + box-shadow: none; +} +.login-option__status { + display: flex; + width: 100%; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + color: var(--text-muted); +} +.login-option__capabilities { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + position: relative; +} +.btn-ghost[data-state="active"], + .btn-ghost[aria-pressed="true"] { + border-color: transparent; + background-color: var(--color-primary); + color: var(--color-white); + --tw-shadow: var(--shadow-md); + --tw-shadow-colored: var(--shadow-md); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.btn-ghost[data-state="active"]:hover, + .btn-ghost[data-state="active"]:focus-visible, + .btn-ghost[aria-pressed="true"]:hover, + .btn-ghost[aria-pressed="true"]:focus-visible { + background-color: var(--color-accent-strong); +} +/* + * Blog embed heights are now driven by the iframe content via postMessage. + * Styling lives inline so the shell can expand naturally with its card. + */ +/* + * Blog carousel component styles + * Tokens consumed here are defined in css/tokens.css under the + * "Blog carousel tokens" section: + */ +.\!badge { + display: inline-flex; + align-items: center; + gap: 0.25rem; + border-radius: var(--radius-full); + border-width: 1px; + border-color: var(--status-neutral-border); + background-color: var(--status-neutral-surface); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + font-size: var(--font-size-2xs); + line-height: var(--line-height-tight); + font-weight: 600; + text-transform: uppercase; + letter-spacing: var(--tracking-extra-wide); + color: var(--status-neutral-on); +} +.badge { + display: inline-flex; + align-items: center; + gap: 0.25rem; + border-radius: var(--radius-full); + border-width: 1px; + border-color: var(--status-neutral-border); + background-color: var(--status-neutral-surface); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + font-size: var(--font-size-2xs); + line-height: var(--line-height-tight); + font-weight: 600; + text-transform: uppercase; + letter-spacing: var(--tracking-extra-wide); + color: var(--status-neutral-on); +} +.\!badge[data-variant="info"] { + border-color: var(--status-info-border); + background-color: var(--status-info-surface); + color: var(--status-info-on); +} +.badge[data-variant="info"] { + border-color: var(--status-info-border); + background-color: var(--status-info-surface); + color: var(--status-info-on); +} +.\!badge[data-variant="success"] { + border-color: var(--status-success-border); + background-color: var(--status-success-surface); + color: var(--status-success-on); +} +.badge[data-variant="success"] { + border-color: var(--status-success-border); + background-color: var(--status-success-surface); + color: var(--status-success-on); +} +.\!badge[data-variant="warning"] { + border-color: var(--status-warning-border); + background-color: var(--status-warning-surface); + color: var(--status-warning-on); +} +.badge[data-variant="warning"] { + border-color: var(--status-warning-border); + background-color: var(--status-warning-surface); + color: var(--status-warning-on); +} +.\!badge[data-variant="critical"] { + border-color: var(--status-danger-border); + background-color: var(--status-danger-surface); + color: var(--status-danger-on); +} +.badge[data-variant="critical"] { + border-color: var(--status-danger-border); + background-color: var(--status-danger-surface); + color: var(--status-danger-on); +} +.\!badge[data-variant="private"] { + border-color: var(--status-private-border); + background-color: var(--status-private-surface); + color: var(--status-private-on); +} +.badge[data-variant="private"] { + border-color: var(--status-private-border); + background-color: var(--status-private-surface); + color: var(--status-private-on); +} +.\!badge[data-variant="neutral"] { + border-color: var(--status-neutral-border); + background-color: var(--status-neutral-surface); + color: var(--status-neutral-on); +} +.badge[data-variant="neutral"] { + border-color: var(--status-neutral-border); + background-color: var(--status-neutral-surface); + color: var(--status-neutral-on); +} +.moderation-badge { + display: inline-flex; + flex-direction: column; + align-items: flex-start; + gap: var(--space-sm); + border-radius: var(--radius-xl); + border-width: 1px; + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.375rem; + padding-bottom: 0.375rem; + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + font-weight: 600; + text-transform: none; + letter-spacing: -0.025em; + --tw-shadow: var(--shadow-sm); + --tw-shadow-colored: var(--shadow-sm); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.moderation-badge--interactive { + pointer-events: auto; + } +.moderation-badge__actions { + display: inline-flex; + flex-wrap: nowrap; + align-items: center; + gap: var(--space-sm); +} +.moderation-badge__label { + display: inline-flex; + align-items: center; + gap: var(--space-xs); + white-space: nowrap; +} +.moderation-badge[data-variant="warning"] { + border-color: var(--status-warning-border); + background-color: var(--status-warning-surface); + color: var(--status-warning-on); + box-shadow: 0 var(--shadow-badge-offset-y) var(--shadow-badge-blur) + color-mix(in srgb, var(--status-warning) 18%, transparent); +} +.moderation-badge[data-variant="neutral"] { + border-color: var(--status-neutral-border); + background-color: var(--status-neutral-surface); + color: var(--status-neutral-on); + box-shadow: 0 var(--shadow-badge-offset-y) var(--shadow-badge-blur) + color-mix(in srgb, var(--status-neutral) 18%, transparent); +} +.moderation-badge__icon { + display: inline-flex; + height: 1.25rem; + width: 1.25rem; + align-items: center; + justify-content: center; + border-radius: var(--radius-full); + background-color: var(--status-warning-surface); + background-color: color-mix( + in srgb, + var(--status-warning) 18%, + transparent + ); + color: var(--status-warning-on); +} +.moderation-badge[data-variant="neutral"] .moderation-badge__icon { + background-color: var(--status-neutral-surface); + background-color: color-mix( + in srgb, + var(--status-neutral) 18%, + transparent + ); + color: var(--status-neutral-on); +} +.moderation-badge__icon-mark { + height: 0.875rem; + width: 0.875rem; +} +.moderation-badge__text { + white-space: nowrap; + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + font-weight: 600; + letter-spacing: -0.025em; +} +.moderation-badge__action { + display: inline-flex; + align-items: center; + gap: 0.375rem; + border-radius: var(--radius-full); + border-width: 1px; + border-color: var(--status-warning-border); + background-color: var(--status-warning-surface); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + font-weight: 600; + text-transform: none; + letter-spacing: -0.025em; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + background-color: color-mix( + in srgb, + var(--status-warning) 16%, + transparent + ); + border-color: color-mix( + in srgb, + var(--status-warning-border) 80%, + transparent + ); + color: var(--status-warning-on); +} +.moderation-badge[data-variant="warning"] .moderation-badge__action:hover { + background-color: color-mix( + in srgb, + var(--status-warning) 24%, + transparent + ); + } +.moderation-badge[data-variant="neutral"] .moderation-badge__action { + border-color: var(--status-neutral-border); + background-color: var(--status-neutral-surface); + background-color: color-mix( + in srgb, + var(--status-neutral) 16%, + transparent + ); + border-color: color-mix( + in srgb, + var(--status-neutral-border) 80%, + transparent + ); + color: var(--status-neutral-on); +} +.moderation-badge[data-variant="neutral"] .moderation-badge__action:hover { + background-color: color-mix( + in srgb, + var(--status-neutral) 24%, + transparent + ); + } +.moderation-badge__action:focus-visible { + outline: var(--outline-thick-width) solid + color-mix(in srgb, var(--status-warning) 38%, transparent); + outline-offset: var(--space-3xs); + } +.moderation-badge[data-variant="neutral"] + .moderation-badge__action:focus-visible { + outline: var(--outline-thick-width) solid + color-mix(in srgb, var(--status-neutral) 38%, transparent); + } +.moderation-badge__action[disabled] { + cursor: wait; + opacity: 0.7; + } +.pill { + display: inline-flex; + align-items: center; + gap: 0.5rem; + border-radius: var(--radius-full); + border-width: 1px; + border-color: var(--status-neutral-border); + background-color: var(--status-neutral-surface); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + font-weight: 500; + color: var(--status-neutral-on); +} +.pill[data-size="compact"] { + padding-left: 0.5rem; + padding-right: 0.5rem; + padding-top: 0.125rem; + padding-bottom: 0.125rem; + font-size: var(--font-size-2xs); + line-height: var(--line-height-tight); +} +.pill[data-variant="info"] { + border-color: var(--status-info-border); + background-color: var(--status-info-surface); + color: var(--status-info-on); +} +.pill[data-variant="success"] { + border-color: var(--status-success-border); + background-color: var(--status-success-surface); + color: var(--status-success-on); +} +.pill[data-variant="warning"] { + border-color: var(--status-warning-border); + background-color: var(--status-warning-surface); + color: var(--status-warning-on); +} +.pill[data-variant="critical"] { + border-color: var(--status-danger-border); + background-color: var(--status-danger-surface); + color: var(--status-danger-on); +} +.pill[data-variant="private"] { + border-color: var(--status-private-border); + background-color: var(--status-private-surface); + color: var(--status-private-on); +} +.pill[data-variant="neutral"] { + border-color: var(--status-neutral-border); + background-color: var(--status-neutral-surface); + color: var(--status-neutral-on); +} +.\!menu { + display: flex; + flex-direction: column; + gap: 0.25rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} +.menu { + display: flex; + flex-direction: column; + gap: 0.25rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} +.menu__heading { + padding-left: 1rem; + padding-right: 1rem; + padding-bottom: 0.5rem; + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.025em; + color: var(--text-muted); +} +.menu__separator { + margin-top: 0.5rem; + margin-bottom: 0.5rem; + height: 1px; + background-color: var(--border-default); + --tw-bg-opacity: 0.2; +} +.menu__item { + outline: 2px solid transparent; + outline-offset: 2px; +} +.menu__item:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.menu__item { + position: relative; + display: flex; + width: 100%; + align-items: center; + gap: 0.75rem; + border-radius: var(--radius-md); + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + font-weight: 500; + color: var(--text-primary); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: var(--motion-ease-in-out); +} +.menu__item:hover { + background-color: var(--surface-panel-hover); + color: var(--text-primary); +} +.menu__item[data-state="active"] { + background-color: var(--color-accent); + --tw-bg-opacity: 0.2; + color: var(--color-accent-strong); +} +.menu__item[data-variant="critical"] { + color: var(--color-critical-strong); +} +.menu__item[data-variant="critical"]:hover { + background-color: var(--color-critical); + --tw-bg-opacity: 0.2; +} +.menu__item:disabled, + .menu__item[aria-disabled="true"] { + cursor: not-allowed; + background-color: transparent; + color: var(--text-muted); + opacity: 0.6; +} +.menu__item:disabled:hover, + .menu__item[aria-disabled="true"]:hover { + background-color: transparent; + color: var(--text-muted); +} +.\!card { + min-width: 0 !important; + --video-card-lift-translate-y: 0 !important; + --video-card-backdrop-opacity: 0 !important; + --video-card-backdrop-translate-y: var(--space-md-plus) !important; + --video-card-backdrop-scale: 1.08 !important; + --video-card-backdrop-blur: var(--blur-thumbnail-blur) !important; + } +.card { + min-width: 0; + --video-card-lift-translate-y: 0; + --video-card-backdrop-opacity: 0; + --video-card-backdrop-translate-y: var(--space-md-plus); + --video-card-backdrop-scale: 1.08; + --video-card-backdrop-blur: var(--blur-thumbnail-blur); + } +.\!card { + outline: 2px solid transparent; + outline-offset: 2px; +} +.\!card:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.\!card { + position: relative; + border-radius: var(--radius-lg); + border-width: 1px; + border-color: transparent; + background-color: var(--surface-raised); + color: var(--text-primary); + --tw-shadow: var(--shadow-md); + --tw-shadow-colored: var(--shadow-md); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.card { + outline: 2px solid transparent; + outline-offset: 2px; +} +.card:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.card { + position: relative; + border-radius: var(--radius-lg); + border-width: 1px; + border-color: transparent; + background-color: var(--surface-raised); + color: var(--text-primary); + --tw-shadow: var(--shadow-md); + --tw-shadow-colored: var(--shadow-md); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.\!card { + transform: translate3d(0, var(--video-card-lift-translate-y), 0) !important; + transition-property: + transform, box-shadow, border-color, background-color, color !important; + will-change: transform !important; + isolation: isolate !important; + } +.card { + transform: translate3d(0, var(--video-card-lift-translate-y), 0); + transition-property: + transform, box-shadow, border-color, background-color, color; + will-change: transform; + isolation: isolate; + } +.\!card::before { + content: "" !important; + position: absolute !important; + inset: 0 !important; + z-index: 0 !important; + opacity: var(--video-card-backdrop-opacity) !important; + background-image: var(--video-card-thumb-url, none) !important; + background-position: center !important; + background-repeat: no-repeat !important; + background-size: cover !important; + transform: translate3d(0, var(--video-card-backdrop-translate-y), 0) + scale(var(--video-card-backdrop-scale)) !important; + filter: blur(var(--video-card-backdrop-blur)) !important; + transition: + opacity var(--motion-duration-base) var(--motion-ease-in-out), + transform var(--motion-duration-base) var(--motion-ease-in-out), + filter var(--motion-duration-base) var(--motion-ease-in-out) !important; + pointer-events: none !important; + will-change: opacity, transform, filter !important; + } +.card::before { + content: ""; + position: absolute; + inset: 0; + z-index: 0; + opacity: var(--video-card-backdrop-opacity); + background-image: var(--video-card-thumb-url, none); + background-position: center; + background-repeat: no-repeat; + background-size: cover; + transform: translate3d(0, var(--video-card-backdrop-translate-y), 0) + scale(var(--video-card-backdrop-scale)); + filter: blur(var(--video-card-backdrop-blur)); + transition: + opacity var(--motion-duration-base) var(--motion-ease-in-out), + transform var(--motion-duration-base) var(--motion-ease-in-out), + filter var(--motion-duration-base) var(--motion-ease-in-out); + pointer-events: none; + will-change: opacity, transform, filter; + } +.\!card > * { + position: relative !important; + z-index: 1 !important; + } +.card > * { + position: relative; + z-index: 1; + } +.card--compact { + display: flex; + flex-direction: row; + align-items: flex-start; + gap: var(--space-md); +} +.card--compact .video-card__media { + inline-size: clamp( + var(--video-card-compact-media-min), + calc(var(--video-modal-secondary-min) * 0.4), + var(--video-card-compact-media-max) + ); + flex-basis: clamp( + var(--video-card-compact-media-min), + calc(var(--video-modal-secondary-min) * 0.4), + var(--video-card-compact-media-max) + ); + display: block; + flex-shrink: 0; + overflow: hidden; + border-radius: var(--radius-lg); + } +.card--compact .video-card__content { + display: flex; + min-width: 0px; + flex: 1 1 0%; + flex-direction: column; + gap: var(--space-sm); + padding: var(--space-md); +} +.card--compact .video-card__title { + font-size: var(--font-size-base); + line-height: var(--line-height-base); + font-weight: 600; + line-height: 1.375; +} +.card--compact .video-card__author-name { + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); +} +.card--compact .video-card__meta { + margin-top: 0px; + display: flex; + align-items: center; + gap: 0.5rem; + font-size: var(--font-size-2xs); + line-height: var(--line-height-tight); + color: var(--text-muted-strong); +} +.card--compact .video-card__meta .view-count-text { + font-size: var(--font-size-2xs); + line-height: var(--line-height-tight); +} +.\!card:hover, + .\!card:focus-within { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate) !important; + --video-card-backdrop-opacity: 0.425 !important; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ) !important; + --video-card-backdrop-scale: 1.12 !important; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong) !important; + } +.card:hover, + .card:focus-within { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate); + --video-card-backdrop-opacity: 0.425; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ); + --video-card-backdrop-scale: 1.12; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong); + } +.\!card:hover, + .\!card:focus-within { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate) !important; + --video-card-backdrop-opacity: 0.425 !important; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ) !important; + --video-card-backdrop-scale: 1.12 !important; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong) !important; + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } +.card:hover, + .card:focus-within { + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.\!card:hover, + .\!card:focus-within { + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.\!card:focus-visible { + border-color: var(--color-accent); + --tw-border-opacity: 0.4; + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.card:focus-visible { + border-color: var(--color-accent); + --tw-border-opacity: 0.4; + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.\!card[data-state="critical"] { + border-width: 1px; + border-color: var(--color-critical); + --tw-border-opacity: 0.4; + background-color: var(--surface-raised-critical); +} +.card[data-state="critical"] { + border-width: 1px; + border-color: var(--color-critical); + --tw-border-opacity: 0.4; + background-color: var(--surface-raised-critical); +} +.\!card[data-state="private"] { + background-color: var(--surface-raised-private); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.card[data-state="private"] { + background-color: var(--surface-raised-private); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.\!card[data-state="disabled"] { + opacity: 0.6; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.card[data-state="disabled"] { + opacity: 0.6; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.video-card__title { + transition: color var(--motion-duration-base) var(--motion-ease-in-out); + } +.video-card__title:hover, + .video-card__title:focus-visible { + color: var(--color-accent); + } +.form-control { + outline: 2px solid transparent; + outline-offset: 2px; +} +.form-control:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.form-control { + display: block; + width: 100%; + border-radius: var(--radius-md); + border-width: 1px; + border-color: var(--border-subtle); + background-color: var(--surface-alt); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + color: var(--text-primary); + --tw-shadow: var(--shadow-sm); + --tw-shadow-colored: var(--shadow-sm); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.form-control::-moz-placeholder { + color: var(--text-muted); +} +.form-control::placeholder { + color: var(--text-muted); +} +.form-control:hover { + border-color: var(--border-default); +} +.form-control:focus-visible { + border-color: var(--color-accent-strong); +} +.form-control:disabled { + cursor: not-allowed; + background-color: var(--surface-base); + color: var(--text-muted); + opacity: 0.6; +} +.form-control:invalid { + border-color: var(--color-critical); + --tw-border-opacity: 0.6; +} +.form-control[data-state="locked"], + .input[data-state="locked"], + .textarea[data-state="locked"], + .select[data-state="locked"], + textarea.form-control[data-state="locked"], + select.form-control[data-state="locked"] { + cursor: default; + border-color: var(--border-sidebar-strong); + background-color: var(--surface-overlay-panel-soft); + color: var(--text-muted); +} + + .\!input[data-state="locked"] { + cursor: default; + border-color: var(--border-sidebar-strong); + background-color: var(--surface-overlay-panel-soft); + color: var(--text-muted); +} +.form-control[data-state="locked"]:focus-visible, + .input[data-state="locked"]:focus-visible, + .textarea[data-state="locked"]:focus-visible, + .select[data-state="locked"]:focus-visible, + textarea.form-control[data-state="locked"]:focus-visible, + select.form-control[data-state="locked"]:focus-visible { + border-color: var(--border-sidebar-strong); + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-offset-width: 0px; +} + + .\!input[data-state="locked"]:focus-visible { + border-color: var(--border-sidebar-strong); + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-offset-width: 0px; +} +.form-control[data-state="locked"]:disabled, + .input[data-state="locked"]:disabled, + .textarea[data-state="locked"]:disabled, + .select[data-state="locked"]:disabled, + textarea.form-control[data-state="locked"]:disabled, + select.form-control[data-state="locked"]:disabled { + cursor: default; + opacity: 1; +} + + .\!input[data-state="locked"]:disabled { + cursor: default; + opacity: 1; +} +.\!input { + outline: 2px solid transparent; + outline-offset: 2px; +} +.\!input:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.\!input { + display: block; + width: 100%; + border-radius: var(--radius-md); + border-width: 1px; + border-color: var(--border-subtle); + background-color: var(--surface-alt); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + color: var(--text-primary); + --tw-shadow: var(--shadow-sm); + --tw-shadow-colored: var(--shadow-sm); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.\!input::-moz-placeholder { + color: var(--text-muted); +} +.\!input::placeholder { + color: var(--text-muted); +} +.\!input:hover { + border-color: var(--border-default); +} +.\!input:focus-visible { + border-color: var(--color-accent-strong); +} +.\!input:disabled { + cursor: not-allowed; + background-color: var(--surface-base); + color: var(--text-muted); + opacity: 0.6; +} +.\!input:invalid { + border-color: var(--color-critical); + --tw-border-opacity: 0.6; +} +.\!input[data-state="locked"], + textarea.\!input[data-state="locked"], + select.\!input[data-state="locked"] { + cursor: default; + border-color: var(--border-sidebar-strong); + background-color: var(--surface-overlay-panel-soft); + color: var(--text-muted); +} +.\!input[data-state="locked"]:focus-visible, + textarea.\!input[data-state="locked"]:focus-visible, + select.\!input[data-state="locked"]:focus-visible { + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + border-color: var(--border-sidebar-strong); + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-offset-width: 0px; +} +.\!input[data-state="locked"]:disabled, + textarea.\!input[data-state="locked"]:disabled, + select.\!input[data-state="locked"]:disabled { + cursor: default; + opacity: 1; +} +@media (prefers-reduced-motion: reduce) { + .\!input { + transition: none; + } + } +body.iframe-form label + .\!input { + margin-top: var(--space-xs); + } +.input { + outline: 2px solid transparent; + outline-offset: 2px; +} +.input:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.input { + display: block; + width: 100%; + border-radius: var(--radius-md); + border-width: 1px; + border-color: var(--border-subtle); + background-color: var(--surface-alt); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + color: var(--text-primary); + --tw-shadow: var(--shadow-sm); + --tw-shadow-colored: var(--shadow-sm); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.input::-moz-placeholder { + color: var(--text-muted); +} +.input::placeholder { + color: var(--text-muted); +} +.input:hover { + border-color: var(--border-default); +} +.input:focus-visible { + border-color: var(--color-accent-strong); +} +.input:disabled { + cursor: not-allowed; + background-color: var(--surface-base); + color: var(--text-muted); + opacity: 0.6; +} +.input:invalid { + border-color: var(--color-critical); + --tw-border-opacity: 0.6; +} +.input[data-state="locked"], + textarea.input[data-state="locked"], + select.input[data-state="locked"] { + cursor: default; + border-color: var(--border-sidebar-strong); + background-color: var(--surface-overlay-panel-soft); + color: var(--text-muted); +} +.input[data-state="locked"]:focus-visible, + textarea.input[data-state="locked"]:focus-visible, + select.input[data-state="locked"]:focus-visible { + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + border-color: var(--border-sidebar-strong); + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-offset-width: 0px; +} +.input[data-state="locked"]:disabled, + textarea.input[data-state="locked"]:disabled, + select.input[data-state="locked"]:disabled { + cursor: default; + opacity: 1; +} +@media (prefers-reduced-motion: reduce) { + .input { + transition: none; + } + } +body.iframe-form label + .input { + margin-top: var(--space-xs); + } +.textarea { + outline: 2px solid transparent; + outline-offset: 2px; +} +.textarea:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.textarea { + display: block; + width: 100%; + border-radius: var(--radius-md); + border-width: 1px; + border-color: var(--border-subtle); + background-color: var(--surface-alt); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + color: var(--text-primary); + --tw-shadow: var(--shadow-sm); + --tw-shadow-colored: var(--shadow-sm); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.textarea::-moz-placeholder { + color: var(--text-muted); +} +.textarea::placeholder { + color: var(--text-muted); +} +.textarea:hover { + border-color: var(--border-default); +} +.textarea:focus-visible { + border-color: var(--color-accent-strong); +} +.textarea:disabled { + cursor: not-allowed; + background-color: var(--surface-base); + color: var(--text-muted); + opacity: 0.6; +} +.textarea:invalid { + border-color: var(--color-critical); + --tw-border-opacity: 0.6; +} +.textarea[data-state="locked"], + textarea.textarea[data-state="locked"], + select.textarea[data-state="locked"] { + cursor: default; + border-color: var(--border-sidebar-strong); + background-color: var(--surface-overlay-panel-soft); + color: var(--text-muted); +} +.textarea[data-state="locked"]:focus-visible, + textarea.textarea[data-state="locked"]:focus-visible, + select.textarea[data-state="locked"]:focus-visible { + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + border-color: var(--border-sidebar-strong); + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-offset-width: 0px; +} +.textarea[data-state="locked"]:disabled, + textarea.textarea[data-state="locked"]:disabled, + select.textarea[data-state="locked"]:disabled { + cursor: default; + opacity: 1; +} +@media (prefers-reduced-motion: reduce) { + .textarea { + transition: none; + } + } +body.iframe-form label + .textarea { + margin-top: var(--space-xs); + } +.textarea { + min-height: var(--form-textarea-min-height); + resize: vertical; +} +.select { + outline: 2px solid transparent; + outline-offset: 2px; +} +.select:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.select { + display: block; + width: 100%; + border-radius: var(--radius-md); + border-width: 1px; + border-color: var(--border-subtle); + background-color: var(--surface-alt); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + color: var(--text-primary); + --tw-shadow: var(--shadow-sm); + --tw-shadow-colored: var(--shadow-sm); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.select::-moz-placeholder { + color: var(--text-muted); +} +.select::placeholder { + color: var(--text-muted); +} +.select:hover { + border-color: var(--border-default); +} +.select:focus-visible { + border-color: var(--color-accent-strong); +} +.select:disabled { + cursor: not-allowed; + background-color: var(--surface-base); + color: var(--text-muted); + opacity: 0.6; +} +.select:invalid { + border-color: var(--color-critical); + --tw-border-opacity: 0.6; +} +.select[data-state="locked"], + textarea.select[data-state="locked"], + select.select[data-state="locked"] { + cursor: default; + border-color: var(--border-sidebar-strong); + background-color: var(--surface-overlay-panel-soft); + color: var(--text-muted); +} +.select[data-state="locked"]:focus-visible, + textarea.select[data-state="locked"]:focus-visible, + select.select[data-state="locked"]:focus-visible { + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + border-color: var(--border-sidebar-strong); + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-offset-width: 0px; +} +.select[data-state="locked"]:disabled, + textarea.select[data-state="locked"]:disabled, + select.select[data-state="locked"]:disabled { + cursor: default; + opacity: 1; +} +@media (prefers-reduced-motion: reduce) { + .select { + transition: none; + } + } +body.iframe-form label + .select { + margin-top: var(--space-xs); + } +.select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding-right: 2.5rem; + --select-arrow-color: var(--color-border); + background-image: + linear-gradient(135deg, transparent 50%, var(--select-arrow-color) 50%), + linear-gradient(45deg, var(--select-arrow-color) 50%, transparent 50%), + linear-gradient( + to right, + var(--color-border-subtle), + var(--color-border-subtle) + ); + background-position: + right var(--form-select-icon-offset) center, + right var(--form-select-icon-gap) center, + right var(--form-select-icon-hit-area) center; + background-size: + var(--form-select-icon-size) var(--form-select-icon-size), + var(--form-select-icon-size) var(--form-select-icon-size), + var(--form-select-divider-width) 60%; + background-repeat: no-repeat; +} +.select:hover { + --select-arrow-color: var(--color-border); + } +.select:focus-visible { + --select-arrow-color: var(--color-accent-strong); + } +.select:disabled { + --select-arrow-color: var(--color-border-subtle); + } +.select:invalid { + --select-arrow-color: var(--color-critical); + } +.\!checkbox { + outline: 2px solid transparent; + outline-offset: 2px; +} +.\!checkbox:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.\!checkbox { + display: inline-flex; + height: 1.25rem; + width: 1.25rem; + cursor: pointer; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + align-items: center; + justify-content: center; + border-radius: 0.25rem; + border-width: 1px; + border-color: var(--border-subtle); + background-color: var(--surface-alt); + color: var(--color-white); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.checkbox { + outline: 2px solid transparent; + outline-offset: 2px; +} +.checkbox:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.checkbox { + display: inline-flex; + height: 1.25rem; + width: 1.25rem; + cursor: pointer; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + align-items: center; + justify-content: center; + border-radius: 0.25rem; + border-width: 1px; + border-color: var(--border-subtle); + background-color: var(--surface-alt); + color: var(--color-white); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.\!checkbox:hover { + border-color: var(--border-default); +} +.checkbox:hover { + border-color: var(--border-default); +} +.\!checkbox:disabled { + cursor: not-allowed; + border-color: var(--border-subtle); + background-color: var(--surface-base); + color: var(--text-muted); + opacity: 0.6; +} +.checkbox:disabled { + cursor: not-allowed; + border-color: var(--border-subtle); + background-color: var(--surface-base); + color: var(--text-muted); + opacity: 0.6; +} +.\!checkbox::after { + content: "" !important; + } +.checkbox::after { + content: ""; + } +.\!checkbox::after { + pointer-events: none; + height: 0.5rem; + width: 0.75rem; + border-bottom-width: 2px; + border-right-width: 2px; + border-color: var(--color-white); + opacity: 0; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.checkbox::after { + pointer-events: none; + height: 0.5rem; + width: 0.75rem; + border-bottom-width: 2px; + border-right-width: 2px; + border-color: var(--color-white); + opacity: 0; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.\!checkbox::after { + transform: rotate(45deg) translateY(var(--form-checkbox-checkmark-offset)) + scale(0.85) !important; + } +.checkbox::after { + transform: rotate(45deg) translateY(var(--form-checkbox-checkmark-offset)) + scale(0.85); + } +.\!checkbox:checked, + .\!checkbox[aria-checked="true"] { + border-color: var(--color-accent-strong); + background-color: var(--color-accent-strong); +} +.checkbox:checked, + .checkbox[aria-checked="true"] { + border-color: var(--color-accent-strong); + background-color: var(--color-accent-strong); +} +.\!checkbox:checked, + .\!checkbox[aria-checked="true"] { + border-color: var(--color-accent-strong); + background-color: var(--color-accent-strong); +} +.\!checkbox:checked::after, + .\!checkbox[aria-checked="true"]::after { + opacity: 1; +} +.checkbox:checked::after, + .checkbox[aria-checked="true"]::after { + opacity: 1; +} +.\!checkbox:checked::after, + .\!checkbox[aria-checked="true"]::after { + opacity: 1; + transform: rotate(45deg) translateY(var(--form-checkbox-checkmark-offset)) + scale(1) !important; +} +.checkbox:checked::after, + .checkbox[aria-checked="true"]::after { + transform: rotate(45deg) translateY(var(--form-checkbox-checkmark-offset)) + scale(1); + } +.\!checkbox:checked::after, + .\!checkbox[aria-checked="true"]::after { + transform: rotate(45deg) translateY(var(--form-checkbox-checkmark-offset)) + scale(1) !important; + } +.switch { + outline: 2px solid transparent; + outline-offset: 2px; +} +.switch:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.switch { + position: relative; + display: inline-flex; + height: 1.5rem; + width: 2.75rem; + cursor: pointer; + align-items: center; + border-radius: var(--radius-full); + border-width: 1px; + border-color: var(--border-subtle); + background-color: var(--border-subtle); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.switch::before { + content: ""; + pointer-events: none; + position: absolute; + left: 0.125rem; + top: 50%; + height: 1.25rem; + width: 1.25rem; + --tw-translate-y: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + border-radius: var(--radius-full); + background-color: var(--surface-base); + --tw-shadow: var(--shadow-md); + --tw-shadow-colored: var(--shadow-md); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + } +.switch:disabled { + cursor: not-allowed; + border-color: var(--border-subtle); + background-color: var(--surface-base); + color: var(--text-muted); + opacity: 0.6; +} +.switch[aria-checked="true"], + .switch.is-on { + border-color: var(--color-accent-strong); + background-color: var(--color-accent-strong); +} +.switch[aria-checked="true"]::before, + .switch.is-on::before { + --tw-translate-x: 1.25rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +@media (prefers-reduced-motion: reduce) { + .form-control, + .checkbox, + .checkbox::after, + .switch, + .switch::before, + .popover-panel, + .popover__panel, + .menu__item { + transition: none !important; + } + + .\!checkbox, + .\!checkbox::after { + transition: none !important; + } + } +.bv-modal { + position: fixed; + inset: 0px; + z-index: var(--z-modal-root); + display: flex; + align-items: center; + justify-content: center; + padding: 1.5rem; +} +@media (min-width: 640px) { + .bv-modal { + padding: 2rem; + } +} +.bv-modal[hidden], + .bv-modal.hidden { + display: none !important; + } +.bv-modal__panel { + outline: 2px solid transparent; + outline-offset: 2px; +} +.bv-modal__panel:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.bv-modal__panel { + position: relative; + z-index: var(--z-modal-content); + width: 100%; + max-width: var(--modal-max-width); + border-radius: var(--radius-modal-xl); + border-width: 1px; + border-color: var(--border-overlay); + padding-left: 2rem; + padding-right: 2rem; + padding-bottom: 2rem; + padding-top: 1.75rem; + text-align: left; + color: var(--text-primary); + --tw-shadow: var(--shadow-modal); + --tw-shadow-colored: var(--shadow-modal); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + background-color: var(--color-modal-surface, var(--color-overlay-panel)); +} +.bv-modal__panel:focus-visible { + border-color: var(--color-border-translucent-bright); +} +.bv-modal-backdrop { + position: fixed; + inset: 0px; + z-index: var(--z-modal-overlay); + --tw-backdrop-saturate: saturate(1.5); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + background-color: var(--color-modal-backdrop, var(--color-overlay-muted)); + backdrop-filter: blur(var(--blur-modal-backdrop)); + -webkit-backdrop-filter: blur(var(--blur-modal-backdrop)); +} +.bv-modal-backdrop[data-variant="veil"] { + --tw-backdrop-blur: blur(64px); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + background-color: var(--color-overlay-veil); +} +.bv-modal-backdrop[data-variant="muted"] { + background-color: var(--color-overlay-muted); + } +.modal-sheet { + outline: 2px solid transparent; + outline-offset: 2px; +} +.modal-sheet:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.modal-sheet { + position: relative; + z-index: var(--z-modal-content); + width: 100%; + max-width: var(--modal-max-width); + border-radius: var(--radius-modal-xl); + border-width: 1px; + border-color: var(--border-overlay); + padding-left: 2rem; + padding-right: 2rem; + padding-bottom: 2rem; + padding-top: 1.75rem; + text-align: left; + color: var(--text-primary); + --tw-shadow: var(--shadow-modal); + --tw-shadow-colored: var(--shadow-modal); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + background-color: var(--color-modal-surface, var(--color-overlay-panel)); +} +.modal-sheet:focus-visible { + border-color: var(--color-border-translucent-bright); +} +@media (max-width: calc(1024px - var(--breakpoint-edge-offset))) { + + .modal-sheet { + inline-size: 100vw; + block-size: 100vh; + max-width: none; + max-height: none; + margin: 0; + border-radius: 0; + border: 0; + padding-top: calc(var(--space-lg) + env(safe-area-inset-top)); + padding-right: calc(var(--space-lg) + env(safe-area-inset-right)); + padding-bottom: calc(var(--space-lg) + env(safe-area-inset-bottom)); + padding-left: calc(var(--space-lg) + env(safe-area-inset-left)); + overflow: auto; + } + } +#playerModal > .modal-sheet { + max-width: none; + width: 100%; + min-height: 100%; + padding: 0; + border-radius: 0; + box-shadow: none; + border: none; + background-color: var(--color-surface); + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: flex-start; + flex: 1 1 auto; + } +#modalContainer > [id$="Modal"]:not(#playerModal) .modal-sheet { + z-index: var(--z-modal-content); + } +#modalContainer > #profileModal .modal-sheet, + #profileModal[data-modal-stack="top"] .modal-sheet { + z-index: var(--z-modal-top-content); + } +#modalContainer > #eventDetailsModal .modal-sheet { + z-index: var(--z-modal-top-content); + } +#shareNostrModal .modal-sheet { + z-index: var(--z-modal-peak-content); + } +#modalContainer > #loginModal .modal-sheet { + z-index: var(--z-modal-peak-content); + } +.modal-sheet { + position: relative; + margin-top: 0px; + margin-bottom: 0px; + width: 100%; + overflow: hidden; +} +#profileModal .modal-sheet { + width: 100%; + height: 100%; + max-width: none; + max-height: none; + border-radius: 0; + border: none; +} +.modal-sheet { + max-width: var(--modal-sheet-inline-max); + overscroll-behavior: contain; + } +@media (max-width: calc(1024px - var(--breakpoint-edge-offset))) { + .bv-modal { + align-items: stretch; + justify-content: stretch; + padding: 0; + } + + .bv-modal__panel, + .modal-sheet { + inline-size: 100vw; + block-size: 100vh; + max-width: none; + max-height: none; + margin: 0; + border-radius: 0; + border: 0; + padding-top: calc(var(--space-lg) + env(safe-area-inset-top)); + padding-right: calc(var(--space-lg) + env(safe-area-inset-right)); + padding-bottom: calc(var(--space-lg) + env(safe-area-inset-bottom)); + padding-left: calc(var(--space-lg) + env(safe-area-inset-left)); + overflow: auto; + } + } +.modal-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: var(--space-md); + padding-left: 2rem; + padding-right: 2rem; + padding-bottom: 1rem; + padding-top: 1.75rem; + background: transparent; + border: 0; +} +.modal-header[data-variant="flush"] { + padding-left: 0px; + padding-right: 0px; + padding-bottom: 0px; + padding-top: 0px; + background: transparent; +} +.modal-close { + outline: 2px solid transparent; + outline-offset: 2px; +} +.modal-close:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.modal-close { + display: flex; + height: 2.5rem; + width: 2.5rem; + align-items: center; + justify-content: center; + border-radius: var(--radius-full); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + border: var(--border-width-hairline) solid + var(--color-border-translucent-medium); + background: var(--color-surface-tint-subtle); + color: var(--color-text-frosted-strong); +} +.modal-close:hover { + background: var(--color-border-translucent); + border-color: var(--color-border-translucent-stronger); + color: var(--color-text); + } +.modal-back { + display: inline-flex; + align-items: center; + gap: 0.5rem; +} +@media (min-width: 1024px) { + .modal-back { + display: none !important; + } + } +.modal-body > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.25rem * var(--tw-space-y-reverse)); +} +.modal-body { + padding-left: 2rem; + padding-right: 2rem; + padding-bottom: 2rem; + padding-top: 0.5rem; + overscroll-behavior: contain; + scrollbar-width: thin; +} +.modal-body::-webkit-scrollbar { + width: var(--scrollbar-thin-width); + } +.modal-body::-webkit-scrollbar-track { + background: transparent; + } +.modal-body::-webkit-scrollbar-thumb { + background: var(--color-border-translucent); + border-radius: var(--radius-full); + } +/* Modal embed containers ensure iframe wrappers respect modal sizing tokens. */ +.modal-embed-frame { + display: flex; + height: var(--modal-embed-height); + max-height: var(--modal-embed-height); + width: 100%; + flex-direction: column; +} +.modal-footer { + display: flex; + flex-direction: column; + gap: 0.75rem; + padding-left: 2rem; + padding-right: 2rem; + padding-bottom: 2rem; + padding-top: 1rem; +} +.modal-alert { + display: flex; + align-items: flex-start; + gap: 0.75rem; + border-radius: var(--radius-md); + border-width: 1px; + border-color: var(--status-warning-strong); + --tw-border-opacity: 0.6; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + color: var(--status-warning-strong); + backdrop-filter: blur(var(--blur-modal-alert)); + background-color: var(--color-warning-glass); +} +.modal-alert__icon { + margin-top: 0.125rem; + height: 1.25rem; + width: 1.25rem; + flex-shrink: 0; + color: var(--status-warning-strong); +} +.modal-tile { + border-radius: var(--radius-xl); + border-width: 1px; + border-color: var(--border-overlay); + background-color: var(--surface-overlay-panel-soft); + padding-left: 1.25rem; + padding-right: 1.25rem; + padding-top: 1rem; + padding-bottom: 1rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + color: var(--text-frosted-primary); + background-color: var(--color-overlay-panel-glass-soft); +} +.modal-tile > h3 { + font-size: var(--font-size-base); + line-height: var(--line-height-base); + font-weight: 600; + color: var(--text-primary); +} +.modal-primary { + outline: 2px solid transparent; + outline-offset: 2px; +} +.modal-primary:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.modal-primary { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + border-radius: var(--radius-full); + border-width: 1px; + border-color: transparent; + background-color: var(--color-accent-strong); + padding-left: 1.25rem; + padding-right: 1.25rem; + padding-top: 0.625rem; + padding-bottom: 0.625rem; + font-size: var(--font-size-base); + line-height: var(--line-height-base); + font-weight: 600; + color: var(--color-white); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.modal-primary[data-size="sm"] { + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.375rem; + padding-bottom: 0.375rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + gap: 0.375rem; +} +.modal-primary:hover { + background-color: var(--color-accent); +} +.modal-primary:focus-visible { + background-color: var(--color-accent-pressed); +} +.modal-primary:disabled { + pointer-events: none; + cursor: not-allowed; + opacity: 0.6; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +body.iframe-form button.modal-primary { + align-self: flex-start; + margin-top: var(--space-md); + } +.modal-primary { + width: 100%; + justify-content: center; +} +@media (prefers-reduced-motion: reduce) { + + .modal-primary { + transition-property: none; + } +} +.grid-stack { + display: grid; + gap: var(--space-lg); +} +.grid-stack[data-variant="cards"] { + grid-template-columns: repeat(1, minmax(0, 1fr)); +} +@media (min-width: 640px) { + .grid-stack[data-variant="cards"] { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} +@media (min-width: 1280px) { + .grid-stack[data-variant="cards"] { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} +.grid-stack[data-orientation="vertical"] { + display: flex; + flex-direction: column; + gap: var(--space-md); +} +.bv-stack { + display: flex; + flex-direction: column; + gap: var(--space-lg); +} +.bv-stack--tight { + gap: var(--space-md); +} +.bv-grid-video { + display: grid; + gap: var(--space-xl); + padding-top: var(--space-lg); + padding-bottom: var(--space-lg); + grid-template-columns: repeat( + auto-fill, + minmax(var(--grid-card-min-width), 1fr) + ); +} +.\!card[data-alert="nsfw-owner"] { + border-width: 1px; + border-color: var(--color-critical); + --tw-border-opacity: 0.4; + background-color: var(--color-black); +} +.card[data-alert="nsfw-owner"] { + border-width: 1px; + border-color: var(--color-critical); + --tw-border-opacity: 0.4; + background-color: var(--color-black); +} +.\!card[data-alert="nsfw-owner"]:hover { + --tw-border-opacity: 0.6; + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.card[data-alert="nsfw-owner"]:hover { + --tw-border-opacity: 0.6; + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.\!card img[data-thumbnail-state="blurred"], + .\!card img[data-visual-state="blurred"] { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.card img[data-thumbnail-state="blurred"], + .card img[data-visual-state="blurred"], + .channel-profile-container img[data-visual-state="blurred"] { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.\!card img[data-thumbnail-state="blurred"], + .\!card img[data-visual-state="blurred"] { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + filter: blur(var(--blur-thumbnail-blur)) !important; + transform: scale(1.05) !important; +} +.card img[data-thumbnail-state="blurred"], + .card img[data-visual-state="blurred"], + .channel-profile-container img[data-visual-state="blurred"] { + filter: blur(var(--blur-thumbnail-blur)); + transform: scale(1.05); + } +.\!card img[data-thumbnail-state="blurred"], + .\!card img[data-visual-state="blurred"] { + filter: blur(var(--blur-thumbnail-blur)) !important; + transform: scale(1.05) !important; + } +.channel-moderation-badge-anchor { + display: flex; + flex-direction: column; + gap: var(--space-sm); + pointer-events: none; + width: 100%; +} +.channel-moderation-badge-anchor:empty { + display: none; + } +.channel-moderation-badge-anchor > [data-moderation-badge="true"] { + pointer-events: auto; + max-width: min(100%, var(--moderation-badge-max-inline)); + } +#playerModal .video-modal__video { + position: relative; + } +#playerModal + .video-modal__video[data-visual-state="blurred"] + .video-modal__media { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + filter: blur(var(--blur-thumbnail-blur)); + transform: scale(1.05); +} +#playerModal .video-modal__moderation-bar { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + padding-bottom: var(--space-md); + } +#playerModal .video-modal__moderation-bar[hidden] { + display: none; + } +#playerModal .video-modal__moderation-bar .moderation-badge { + width: 100%; + max-width: none; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + gap: var(--space-sm); + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + } +#playerModal .video-modal__moderation-bar .moderation-badge__label { + flex: 1 1 auto; + } +#playerModal .video-modal__moderation-bar .moderation-badge__actions { + flex-shrink: 0; + } +.\!card[data-owner-is-viewer="true"][data-url-health-state="offline"][data-stream-health-state="unhealthy"] { + border-width: 1px; + border-color: var(--color-critical); + --tw-border-opacity: 0.4; + background-color: var(--surface-raised-critical); +} +.card[data-owner-is-viewer="true"][data-url-health-state="offline"][data-stream-health-state="unhealthy"] { + border-width: 1px; + border-color: var(--color-critical); + --tw-border-opacity: 0.4; + background-color: var(--surface-raised-critical); +} +.video-visibility-toggle { + display: inline-flex; + align-items: center; + gap: 0.25rem; + border-radius: var(--radius-full); + border-width: 1px; + border-color: var(--border-default); + background-color: var(--surface-panel); + padding-left: 0.25rem; + padding-right: 0.25rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} +.video-visibility-toggle__option { + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: var(--radius-full); + border-width: 1px; + border-color: transparent; + background-color: var(--surface-panel); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + font-weight: 600; + text-transform: uppercase; + color: var(--text-muted); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.video-visibility-toggle__option:not(.is-disabled):hover { + background-color: var(--surface-panel-hover); + color: var(--text-primary); +} +.video-visibility-toggle__option[data-active="true"] { + border-width: 1px; + border-color: var(--color-accent); + --tw-border-opacity: 0.6; + color: var(--text-primary); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.video-visibility-toggle__option.is-disabled { + cursor: not-allowed; + opacity: 0.45; +} +.video-modal__container { + display: flex; + height: 100%; + min-height: 100vh; + width: 100%; + flex: 1 1 0%; + flex-direction: column; +} +.player-modal__container { + display: flex; + height: 100%; + min-height: 100vh; + width: 100%; + flex: 1 1 0%; + flex-direction: column; + gap: 1.5rem; +} +#playerModal { + --player-modal-vertical-padding: var(--space-md-plus); + --player-modal-primary-inline-padding-lg: clamp( + var(--space-sm), + 2.75vw, + var(--space-lg) + ); + } +#playerModal > .bv-modal__panel { + max-width: none; + width: 100%; + min-height: 100%; + padding: 0; + border-radius: 0; + box-shadow: none; + border: none; + background-color: var(--color-surface); + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: flex-start; + flex: 1 1 auto; + } +.video-modal__panel { + position: relative; + z-index: var(--z-modal-content); + display: flex; + flex: 1 1 auto; + width: 100%; + min-height: 100%; + flex-direction: column; + overflow: visible; + padding: 0; + margin: 0; + border: none; + border-radius: 0; + background-color: var(--color-surface); + box-shadow: none; + } +#playerModal .watch-container { + width: 100%; + max-width: var(--player-modal-inline-max, var(--layout-player-max-width)); + margin-inline: auto; + padding-inline: var( + --player-modal-inline-padding, + clamp(var(--space-md), 4vw, var(--space-xl-plus)) + ); + overflow: visible; + } +#playerModal .watch-container.video-modal__similar { + padding-inline: clamp(var(--space-3xs), 0.6vw, var(--space-2xs)); + } +#playerModal .video-modal__primary > .watch-container:first-child { + padding-block-start: var(--player-modal-vertical-padding); + } +#playerModal .video-modal__primary > .watch-container:last-child { + padding-block-end: var(--player-modal-vertical-padding); + } +#playerModal .video-modal__secondary > .watch-container:last-child { + padding-block-end: var(--player-modal-vertical-padding); + } +#playerModal .modal-nav .watch-container { + max-width: none; + margin-inline: 0; + } +#playerModal .modal-info-panel { + display: flex; + flex-direction: column; + gap: var(--space-md-plus); + } +#playerModal h2#videoTitle { + margin: 0; + font-size: clamp( + var(--font-size-xl), + calc(var(--font-size-modal-title-base) + 0.5vw), + var(--font-size-xl-plus) + ); + font-weight: 600; + line-height: 1.3; + color: var(--color-text); + min-width: 0; + overflow-wrap: break-word; + word-break: break-word; + } +@media (min-width: 1024px) { + .video-modal__headline #videoTitle.video-modal__title { + flex: 1 1 0%; + min-width: 0; + max-width: calc(100% - var(--space-3xl) - var(--space-md)); + } + + .video-modal__headline .video-modal__status { + flex: 0 0 auto; + max-width: clamp(var(--space-xl), 25%, var(--space-3xl)); + margin-left: auto; + } + } +#playerModal .modal-meta { + font-size: var(--font-size-sm); + line-height: 1.4; + color: var(--color-text-muted); + } +#playerModal .modal-view-count { + font-weight: 600; + color: var(--color-text); + } +#playerModal .channel-card { + display: flex; + align-items: center; + gap: var(--space-md); + padding: var(--space-md) var(--space-lg); + border-radius: var(--radius-xl); + border: var(--border-width-hairline) solid var(--color-border-translucent); + background-color: var(--color-surface); + box-shadow: var(--shadow-sm); + } +#playerModal .player-modal__module { + display: flex; + flex-direction: column; + gap: clamp(var(--space-2xs), 1vw, var(--space-sm)); + width: 100%; + padding: clamp(var(--space-xs), 1vw, var(--space-sm)); + border-radius: var(--radius-xl); + border: var(--border-width-hairline) solid var(--color-border-translucent); + background-color: var(--color-surface); + box-shadow: var(--shadow-sm); + } +#playerModal .player-modal__module-title { + margin: 0; + font-size: var(--font-size-lg); + font-weight: 600; + line-height: 1.3; + color: var(--color-text); + } +#playerModal .player-modal__module-list { + display: flex; + flex-direction: column; + gap: var(--space-sm); + width: 100%; + margin: 0; + padding: 0; + list-style: none; + } +#playerModal .video-modal__similar .player-modal__module { + gap: clamp(var(--space-3xs), 0.6vw, var(--space-xs)); + padding: clamp(var(--space-2xs), 0.6vw, var(--space-xs)); + border-width: 0; + box-shadow: none; + background-color: transparent; + } +#playerModal .video-modal__similar .player-modal__module-title { + padding-inline: clamp(var(--space-3xs), 0.6vw, var(--space-2xs)); + } +#playerModal .video-modal__similar .player-modal__module-list { + gap: clamp(var(--space-3xs), 0.6vw, var(--space-xs)); + padding-inline: clamp(var(--space-3xs), 0.6vw, var(--space-2xs)); + padding-bottom: clamp(var(--space-3xs), 0.6vw, var(--space-2xs)); + } +#playerModal .player-modal__module-scroll { + max-height: clamp( + var(--player-modal-module-scroll-min-height), + calc( + 100vh - (var(--player-modal-vertical-padding) * 2) - var(--space-2xl) + ), + var(--player-modal-module-scroll-max-height) + ); + overflow-y: auto; + overflow-x: hidden; + overscroll-behavior: contain; + scrollbar-gutter: stable; + padding-inline-end: var(--space-xs); + } +#playerModal .video-modal__similar .player-modal__module-scroll { + max-height: none; + overflow: visible; + padding-inline-end: 0; + scrollbar-gutter: auto; + } +#playerModal .player-modal__module-scroll > li { + scroll-margin-block: var(--space-xs); + } +@media (max-width: 480px) { + #playerModal .modal-nav .icon-button { + width: var(--space-xl); + height: var(--space-xl); + min-width: var(--space-xl); + min-height: var(--space-xl); + } + + #playerModal .watch-container { + padding-inline: var(--space-sm); + } + + #playerModal .channel-card { + gap: var(--space-sm); + padding: var(--space-sm) var(--space-md); + } + } +#playerModal .channel-card__avatar { + flex-shrink: 0; + width: var(--avatar-size-lg); + height: var(--avatar-size-lg); + border-radius: var(--radius-full); + overflow: hidden; + background-color: var(--color-border-subtle); + } +#playerModal .channel-card__avatar img { + display: block; + width: 100%; + height: 100%; + -o-object-fit: cover; + object-fit: cover; + } +#playerModal .channel-card__name { + margin: 0; + font-size: var(--font-size-lg); + font-weight: 600; + color: var(--color-text); + } +#playerModal .channel-card__npub { + margin: 0; + font-size: var(--font-size-sm); + color: var(--color-text-muted); + } +#playerModal .description-panel { + padding: var(--space-md-plus) var(--space-lg); + border-radius: var(--radius-xl); + border: var(--border-width-hairline) solid var(--color-border-translucent); + background-color: var(--color-surface); + box-shadow: var(--shadow-sm); + } +#playerModal .description-panel p { + margin: 0; + font-size: var(--font-size-sm); + line-height: 1.6; + color: var(--color-text); + } +.player-modal__content { + position: relative; + z-index: var(--z-modal-content); + display: flex; + flex: 1 1 auto; + width: 100%; + min-height: 100%; + flex-direction: column; + overflow: visible; + padding: 0; + margin: 0; + border: none; + border-radius: 0; + background-color: var(--color-surface); + box-shadow: none; + } +@media (prefers-reduced-motion: reduce) { + + .player-modal__content { + transition-property: none; + } +} +#playerModal .modal-nav { + position: sticky; + top: 0px; + z-index: var(--z-overlay-nav); + --tw-translate-y: 0px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + transition-property: transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + background-color: var(--surface-overlay-panel); + background-image: linear-gradient( + 180deg, + color-mix(in srgb, var(--surface-overlay-panel) 94%, transparent) 0%, + color-mix(in srgb, var(--surface-overlay-panel) 88%, transparent) 100% + ); + border-bottom: var(--border-width-hairline) solid var(--border-overlay); + backdrop-filter: blur(var(--modal-nav-backdrop-blur)); + -webkit-backdrop-filter: blur(var(--modal-nav-backdrop-blur)); +} +#playerModal .modal-nav__bar { + display: flex; + width: 100%; + align-items: center; + justify-content: flex-start; + gap: 0.5rem; + padding-block: var(--space-xs); + padding-inline: clamp(var(--space-sm), 2.5vw, var(--space-lg)); +} +#playerModal .modal-nav__rail { + display: flex; + width: 100%; + align-items: center; + justify-content: space-between; + gap: 0.5rem; +} +#playerModal .modal-nav__slot { + display: flex; + align-items: center; + gap: 0.5rem; +} +#playerModal .modal-nav__slot--context { + justify-content: flex-end; + margin-inline-start: auto; + min-inline-size: 0; +} +#playerModal .modal-nav__context { + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.025em; + color: var(--text-muted); +} +#playerModal .modal-nav .modal-actions { + display: flex; + align-items: center; + gap: 0.5rem; + margin-left: 0; + align-self: stretch; +} +#playerModal .modal-nav .icon-button { + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: var(--radius-full); + color: var(--text-muted); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + inline-size: var(--icon-size-md); + block-size: var(--icon-size-md); + background: var(--surface-overlay-panel-tint); + border: var(--border-width-hairline) solid var(--border-overlay); + color: var(--color-text-muted); +} +#playerModal .modal-nav .icon-button:hover, + #playerModal .modal-nav .icon-button:focus-visible { + background: var(--surface-overlay-panel-soft); + border-color: var(--border-overlay-strong); + color: var(--color-text); + } +.modal-nav--hidden { + --tw-translate-y: -100%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.modal-nav--visible { + --tw-translate-y: 0px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.video-modal__layout { + --video-modal-secondary-size: clamp( + var(--video-modal-secondary-min), + 46vw, + var(--video-modal-secondary-max) + ); + --video-modal-gap: clamp(var(--space-2xs), 0.85vw, var(--space-sm)); + display: grid; + gap: var(--video-modal-gap); + width: min(100%, var(--video-modal-layout-max)); + margin-inline: auto; + grid-template-columns: minmax(0, 1fr); + grid-template-areas: + "primary" + "secondary"; + align-items: start; + } +@media (min-width: 1024px) { + .video-modal__layout { + --video-modal-secondary-size: clamp( + var(--video-modal-secondary-min), + 30vw, + var(--video-modal-secondary-max) + ); + grid-template-columns: + minmax(0, 1fr) + minmax( + var(--video-modal-secondary-min), + var(--video-modal-secondary-size) + ); + grid-template-areas: "primary secondary"; + align-items: stretch; + grid-auto-flow: column; + } + + .video-modal__primary { + min-width: 0; + min-height: 0; + grid-area: primary; + } + + .video-modal__secondary { + min-width: var(--video-modal-secondary-min); + min-height: 0; + max-width: var(--video-modal-secondary-size); + grid-area: secondary; + height: 100%; + } + + #playerModal .video-modal__layout .watch-container { + max-width: none; + margin-inline: 0; + width: 100%; + } + } +@media (min-width: 1280px) { + .video-modal__layout { + --video-modal-secondary-size: clamp( + var(--video-modal-secondary-min), + 28vw, + var(--video-modal-secondary-max) + ); + } + } +#playerModal .video-modal__layout { + display: grid; + } +@media (min-width: 1024px) { + #playerModal .video-modal__layout { + grid-template-columns: + minmax(0, 1fr) + minmax( + var(--video-modal-secondary-min), + var(--video-modal-secondary-size) + ); + grid-template-areas: "primary secondary"; + align-items: stretch; + grid-auto-flow: column; + } + + #playerModal .video-modal__primary { + grid-area: primary; + } + + #playerModal .video-modal__secondary { + grid-area: secondary; + } + #playerModal .video-modal__primary { + --player-modal-inline-padding: var( + --player-modal-primary-inline-padding-lg, + clamp(var(--space-sm), 2.75vw, var(--space-lg)) + ); + } + + #playerModal .modal-nav__bar { + --player-modal-inline-padding: var( + --player-modal-primary-inline-padding-lg, + clamp(var(--space-sm), 2.75vw, var(--space-lg)) + ); + } + } +.revert-modal__layout { + display: grid; + gap: 1.5rem; +} +@media (min-width: 1024px) { + .revert-modal__layout { + grid-template-columns: + minmax( + var(--revert-modal-sidebar-min), + var(--revert-modal-sidebar-width) + ) + minmax(0, 1fr); + } + } +.video-modal__primary { + display: flex; + flex-direction: column; + gap: 1.5rem; + min-width: 0; + min-height: 0; +} +.video-modal__secondary { + display: flex; + flex-direction: column; + gap: 1rem; + min-width: 0; + min-height: 0; + width: 100%; + align-self: stretch; +} +.video-modal__similar { + display: none; + } +@media (min-width: 1024px) { + .video-modal__similar { + display: flex; + flex-direction: column; + gap: 1.5rem; + align-self: stretch; + flex: 1 1 auto; + min-height: 0; + } + #playerModal .video-modal__similar .player-modal__module { + height: 100%; + display: flex; + flex: 1 1 auto; + min-height: 0; + flex-direction: column; + } + + #playerModal .video-modal__similar .player-modal__module-list { + flex: 1 1 auto; + min-height: 0; + } + + #playerModal .video-modal__similar .player-modal__module-scroll { + height: 100%; + max-height: none; + min-height: 0; + } + } +#playerModal .player-modal__module-item { + width: 100%; + } +#playerModal + .player-modal__module-item + > .card:not(.player-modal__similar-card) { + display: flex; + width: 100%; + flex-direction: row; + flex-wrap: nowrap; + align-items: stretch; + gap: var(--space-md); +} +#playerModal + .player-modal__module-item + > .\!card:not(.player-modal__similar-card) { + display: flex; + width: 100%; + flex-direction: row; + flex-wrap: nowrap; + align-items: stretch; + gap: var(--space-md); +} +#playerModal + .player-modal__module-item + > .card:not(.player-modal__similar-card) + .video-card__media { + width: clamp( + var(--video-modal-item-media-min), + 18vw, + var(--video-modal-item-media-max) + ); + } +#playerModal + .player-modal__module-item + > .\!card:not(.player-modal__similar-card) + .video-card__media { + width: clamp( + var(--video-modal-item-media-min), + 18vw, + var(--video-modal-item-media-max) + ) !important; + } +#playerModal + .player-modal__module-item + > .card:not(.player-modal__similar-card) + .video-card__media { + flex-shrink: 0; + overflow: hidden; + border-radius: var(--radius-lg); +} +#playerModal + .player-modal__module-item + > .\!card:not(.player-modal__similar-card) + .video-card__media { + flex-shrink: 0; + overflow: hidden; + border-radius: var(--radius-lg); +} +#playerModal + .player-modal__module-item + > .card:not(.player-modal__similar-card) + .video-card__content { + padding: clamp(var(--space-sm), 1.25vw, var(--space-md)); + } +#playerModal + .player-modal__module-item + > .\!card:not(.player-modal__similar-card) + .video-card__content { + padding: clamp(var(--space-sm), 1.25vw, var(--space-md)) !important; + } +#playerModal + .player-modal__module-item + > .card:not(.player-modal__similar-card) + .video-card__content { + display: flex; + min-width: 0px; + flex: 1 1 0%; + flex-direction: column; + gap: var(--space-sm); +} +#playerModal + .player-modal__module-item + > .\!card:not(.player-modal__similar-card) + .video-card__content { + display: flex; + min-width: 0px; + flex: 1 1 0%; + flex-direction: column; + gap: var(--space-sm); +} +#playerModal + .player-modal__module-item + > .card:not(.player-modal__similar-card) + .video-card__meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem; + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + color: var(--text-muted-strong); +} +#playerModal + .player-modal__module-item + > .\!card:not(.player-modal__similar-card) + .video-card__meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem; + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + color: var(--text-muted-strong); +} +#playerModal .video-modal__similar .player-modal__module-list { + gap: clamp(var(--space-2xs), 0.85vw, var(--space-sm)); + } +.player-modal__similar-card { + outline: 2px solid transparent; + outline-offset: 2px; +} +.player-modal__similar-card:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.player-modal__similar-card { + position: relative; + display: flex; + width: 100%; + min-width: 0px; + align-items: flex-start; + gap: 0.75rem; + border-radius: var(--radius-xl); + padding: 0.5rem; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +} +.player-modal__similar-card:hover, + .player-modal__similar-card:focus-within { + background-color: var(--surface-panel-hover); + } +.player-modal__similar-card-media { + position: relative; + display: block; + flex-shrink: 0; + overflow: hidden; + border-radius: var(--radius-lg); + background-color: var(--surface-alt); + width: var(--similar-card-media-width); + aspect-ratio: 16 / 9; + background-image: var(--similar-card-thumb-url, none); + background-position: center; + background-size: cover; +} +@media (max-width: 640px) { + .player-modal__similar-card-media { + width: var(--similar-card-media-width-sm); + } + } +.player-modal__similar-card-media img { + display: block; + width: 100%; + height: 100%; + -o-object-fit: cover; + object-fit: cover; + border-radius: inherit; + } +.player-modal__similar-card-content { + display: flex; + min-width: 0px; + flex: 1 1 0%; + flex-direction: column; + gap: 0.25rem; +} +.player-modal__similar-card-title { + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + font-weight: 600; + line-height: 1.375; + color: var(--text-primary); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + min-width: 0; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} +.player-modal__similar-card:hover .player-modal__similar-card-title, + .player-modal__similar-card-title:focus-visible { + color: var(--color-accent-strong); + } +.player-modal__similar-card-author { + display: flex; + min-width: 0px; + align-items: center; +} +.player-modal__similar-card-author-meta { + display: flex; + min-width: 0px; + flex-direction: column; +} +.player-modal__similar-card-author-name { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + font-weight: 500; + color: var(--text-muted); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +} +.player-modal__similar-card:hover .player-modal__similar-card-author-name { + color: var(--color-text-strong); +} +/* Hidden for now to match cleaner modern look, but kept in DOM if needed */ +.player-modal__similar-card-author-npub { + display: none; + } +.player-modal__similar-card-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.25rem; + font-size: var(--font-size-2xs); + line-height: var(--line-height-tight); + color: var(--text-muted-strong); + min-width: 0; +} +.player-modal__similar-card-timestamp { + white-space: nowrap; + } +.video-modal__stage { + position: relative; + isolation: isolate; + overflow: visible; + --video-modal-ambient-bleed: clamp( + var(--space-2xl), + 12vw, + var(--space-5xl) + ); + --video-modal-ambient-scale: 1.38; + --video-modal-ambient-blur: clamp( + var(--video-modal-ambient-blur-min), + 20vw, + var(--video-modal-ambient-blur-max) + ); + --video-modal-ambient-opacity: 1; + } +.video-modal__video { + position: relative; + z-index: 1; + border-radius: var(--radius-xl); + padding-block: clamp(var(--space-xs), 2.5vw, var(--space-md-plus)); + background-color: transparent; + box-shadow: 0 0 0 var(--border-width-hairline) + color-mix(in srgb, var(--color-border-strong) 8%, transparent) inset; + overflow: visible; + } +.video-modal__ambient { + position: absolute; + inset: calc(var(--video-modal-ambient-bleed) * -1); + border-radius: calc(var(--radius-3xl) + var(--video-modal-ambient-bleed)); + pointer-events: none; + z-index: 0; + display: block; + overflow: visible; + } +.video-modal__ambient::before { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; + background: radial-gradient( + circle at 50% 50%, + color-mix(in srgb, var(--color-overlay-strong) 18%, transparent) 0%, + color-mix(in srgb, var(--color-overlay) 34%, transparent) 62%, + transparent 100% + ); + opacity: 0.9; + mix-blend-mode: screen; + pointer-events: none; + } +.video-modal__ambient-canvas { + position: relative; + width: 100%; + height: 100%; + display: block; + transform: scale(var(--video-modal-ambient-scale)); + filter: blur(var(--video-modal-ambient-blur)) saturate(2) brightness(1.24); + opacity: var(--video-modal-ambient-opacity); + } +@media (min-width: 1024px) { + .video-modal__video { + position: sticky; + top: calc(var(--space-2xl) + var(--space-3xl)); + z-index: 1; + } + + .video-modal__status { + flex-basis: clamp(var(--space-xl), 25%, var(--space-3xl)); + max-width: clamp(var(--space-xl), 25%, var(--space-3xl)); + } + + .video-modal__details { + position: relative; + z-index: 2; + margin-top: var(--space-lg); + } + } +#playerModal .video-modal__video video { + position: relative; + z-index: 1; + background-color: var(--color-black); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-video-modal); + max-height: min(80vh, calc(100vw * 9 / 16)); + -o-object-fit: contain; + object-fit: contain; + } +.video-modal__media { + display: block; + aspect-ratio: 16 / 9; + width: 100%; + -o-object-fit: contain; + object-fit: contain; + } +.video-modal__details { + display: flex; + flex-direction: column; + gap: 1.5rem; +} +.video-modal__meta { + border-bottom-width: 1px; + border-color: var(--border-default); + --tw-border-opacity: 0.2; + padding-bottom: 1rem; +} +/* Video modal tag strip */ +.video-modal__tags { + margin-left: -0.25rem; + margin-right: -0.25rem; + display: flex; + gap: 0.5rem; + padding-left: 0.25rem; + padding-right: 0.25rem; +} +.video-modal__similar .video-modal__tags { + margin-bottom: clamp(var(--space-xs), 0.9vw, var(--space-md)); + } +.video-modal__tags[hidden] { + display: none; + } +.video-modal__tags[data-fade] { + position: relative; + } +.video-modal__tags[data-fade]::before, + .video-modal__tags[data-fade]::after { + content: ""; + position: sticky; + top: 0; + bottom: 0; + width: var(--space-lg); + pointer-events: none; + z-index: 1; + } +.video-modal__tags[data-fade]::before { + left: 0; + background: linear-gradient( + to right, + var(--surface-overlay-muted), + transparent + ); + } +.video-modal__tags[data-fade]::after { + right: 0; + background: linear-gradient( + to left, + var(--surface-overlay-muted), + transparent + ); + } +.video-tag-strip { + display: flex; + flex-wrap: nowrap; + align-items: center; + gap: var(--space-xs); + overflow: hidden; + } +.video-tag-strip--scroll { + overflow-x: auto; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + padding-bottom: var(--space-xs); + scrollbar-width: none; + -ms-overflow-style: none; + } +.video-tag-strip--scroll::-webkit-scrollbar { + display: none; + width: 0; + height: 0; + } +.video-tag-pill { + display: inline-flex; + align-items: center; + gap: 0.5rem; + border-radius: var(--radius-full); + border-width: 1px; + border-color: var(--status-neutral-border); + background-color: var(--status-neutral-surface); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + font-size: var(--font-size-2xs); + line-height: var(--line-height-tight); + font-weight: 500; + color: var(--status-neutral-on); + position: relative; + justify-content: center; + white-space: nowrap; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + min-height: calc(var(--space-sm) + var(--space-xs)); + margin-inline-end: 0.25rem; +} +.video-tag-pill__label { + transition-property: opacity; + transition-duration: 150ms; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + opacity: 1; +} +.video-tag-pill__icon { + position: absolute; + inset: 0px; + display: flex; + align-items: center; + justify-content: center; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + opacity: 0; + transform: scale(0.8); +} +.video-tag-pill__icon svg { + height: 0.75rem; + width: 0.75rem; +} +.video-tag-pill:hover .video-tag-pill__label, + .video-tag-pill:focus-visible .video-tag-pill__label { + opacity: 0; + } +.video-tag-pill:hover .video-tag-pill__icon, + .video-tag-pill:focus-visible .video-tag-pill__icon { + opacity: 1; + transform: scale(1); + } +.video-modal__actions-row { + display: flex; + width: 100%; + flex-wrap: wrap; + align-items: flex-start; + -moz-column-gap: 0.75rem; + column-gap: 0.75rem; + row-gap: 0.75rem; +} +@media (min-width: 640px) { + .video-modal__actions-row { + -moz-column-gap: 1rem; + column-gap: 1rem; + } +} +.video-modal__reactions, + .video-modal__actions-group { + flex: 1 1 var(--menu-min-width); + min-inline-size: 0; + } +.video-modal__reactions { + display: flex; + flex-basis: 100%; + flex-wrap: wrap; + align-items: flex-start; + gap: 0.75rem; +} +@media (min-width: 640px) { + .video-modal__reactions { + gap: 1rem; + } +} +@media (min-width: 768px) { + .video-modal__reactions { + flex-basis: auto; + } +} +.video-modal__reaction-buttons { + display: inline-flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem; +} +.video-modal__reaction-button { + --video-modal-reaction-active-bg: color-mix( + in srgb, + var(--color-accent) 22%, + var(--surface-overlay-panel) + ); + --video-modal-reaction-active-border: color-mix( + in srgb, + var(--color-accent) 38%, + var(--border-overlay) + ); + } +.video-modal__reaction-button[aria-pressed="true"] { + color: var(--color-accent-strong); + background-color: var(--video-modal-reaction-active-bg); + border-color: var(--video-modal-reaction-active-border); + } +.video-modal__reaction-button[aria-pressed="true"] + .video-modal__reaction-count { + color: var(--color-accent-strong); + } +.video-modal__reaction-count { + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + font-weight: 600; + color: var(--text-muted); +} +.video-modal__reaction-meter { + display: flex; + align-items: center; + gap: var(--space-sm); + flex-wrap: wrap; + flex: 1 1 var(--menu-min-width); + max-inline-size: 100%; + } +.video-modal__reaction-meter-label { + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + font-weight: 500; + color: var(--text-muted); + min-inline-size: var(--video-modal-reaction-label-min-inline); +} +.video-modal__reaction-meter-track { + position: relative; + flex: 1 1 auto; + inline-size: 100%; + block-size: var(--space-xs); + border-radius: var(--radius-full); + background-color: var(--color-border-muted); + overflow: hidden; + } +.video-modal__reaction-meter-fill { + position: absolute; + inset-inline-start: 0; + inset-block-start: 0; + block-size: 100%; + max-inline-size: 100%; + inline-size: var(--video-modal-reaction-fill, 0%); + width: var(--video-modal-reaction-fill, 0%); + background-image: linear-gradient( + 90deg, + var(--color-accent) 0%, + var(--color-accent-strong) 100% + ); + transition: inline-size var(--motion-duration-base) var(--motion-ease-out); + } +.video-modal__reaction-meter-fill--empty { + inline-size: 0%; + width: 0%; + } +.video-modal__actions-group { + display: flex; + flex-basis: 100%; + flex-wrap: wrap; + align-items: flex-start; + justify-content: flex-start; + gap: 0.75rem; +} +@media (min-width: 768px) { + .video-modal__actions-group { + flex-basis: auto; + justify-content: flex-end; + margin-inline-start: auto; + } +} +.video-modal__action-button { + display: inline-flex; + height: 2.75rem; + align-items: center; + justify-content: center; + gap: 0.5rem; + border-radius: var(--radius-full); + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + font-weight: 600; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + min-width: var(--video-modal-action-min-width); + padding-inline: var(--space-md); + border-width: var(--border-width-hairline); + border-style: solid; +} + + .bv-modal.video-modal__action-label { + display: none; + } +.profile-pane.video-modal__action-label { + display: none; + } +.video-modal__action-label { + display: none; +} +@media (min-width: 640px) { + .video-modal__action-label { + display: inline; + } +} +.video-modal__action-icon { + height: 1.25rem; + width: 1.25rem; +} +.video-modal__channel, + .video-modal__description, + .video-modal__stats { + border-radius: 1rem; + border-width: 1px; + border-color: var(--border-default); + --tw-border-opacity: 0.2; + background-color: var(--surface-base); + padding-left: 1.25rem; + padding-right: 1.25rem; + padding-top: 1rem; + padding-bottom: 1rem; + --tw-shadow: var(--shadow-sm); + --tw-shadow-colored: var(--shadow-sm); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.video-modal__description-body { + overflow-wrap: anywhere; + word-break: break-word; + } +.video-modal__description-body a { + color: var(--color-accent-strong); + text-decoration: underline; + text-decoration-color: currentColor; + word-break: break-word; + } +.video-modal__description-body a:hover, + .video-modal__description-body a:focus-visible { + color: var(--color-accent-pressed); + } +.video-modal__comments { + border-radius: 1rem; + border-width: 1px; + border-color: var(--border-default); + --tw-border-opacity: 0.2; + background-color: var(--surface-base); + padding-left: 1.25rem; + padding-right: 1.25rem; + padding-top: 1.25rem; + padding-bottom: 1.25rem; + --tw-shadow: var(--shadow-sm); + --tw-shadow-colored: var(--shadow-sm); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + display: flex; + flex-direction: column; + gap: var(--space-lg); +} +.comment-thread { + display: flex; + flex-direction: column; + gap: var(--space-lg); + } +.comment-thread__header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: var(--space-sm); + } +.comment-thread__title { + font-size: var(--font-size-lg); + line-height: var(--line-height-base); + font-weight: 600; + color: var(--text-primary); +} +.comment-thread__count { + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + color: var(--text-muted); + margin: 0; +} +.comment-thread__empty { + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + color: var(--text-muted); + margin: 0; + padding: var(--space-md); + border-radius: var(--radius-lg); + border: var(--border-width-hairline) dashed + color-mix(in srgb, var(--border-default) 40%, transparent); + background-color: var(--surface-alt); +} +.comment-thread__list { + display: flex; + flex-direction: column; + gap: var(--space-md); + margin: 0; + padding: 0; + list-style: none; + } +.comment-thread__item { + display: grid; + grid-template-columns: auto 1fr; + gap: var(--space-md); + padding-block: var(--space-sm); + border-bottom: var(--border-width-hairline) solid var(--border-subtle); + } +.comment-thread__item:last-child { + border-bottom: none; + padding-bottom: 0; + } +.comment-thread__avatar { + inline-size: var(--comment-avatar-size); + block-size: var(--comment-avatar-size); + } +.comment-thread__avatar img { + border-radius: var(--radius-full); + -o-object-fit: cover; + object-fit: cover; + inline-size: 100%; + block-size: 100%; +} +.comment-thread__content { + display: flex; + flex-direction: column; + gap: var(--space-xs); + min-inline-size: 0; + } +.comment-thread__meta { + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + color: var(--text-muted); + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--space-xs); +} +.comment-thread__author { + font-weight: 600; + color: var(--text-primary); + } +.comment-thread__timestamp { + color: var(--text-muted); + } +.comment-thread__body { + color: var(--text-primary); + line-height: 1.6; + word-break: break-word; + } +.comment-thread__actions { + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--space-sm); +} +.comment-thread__actions button { + outline: 2px solid transparent; + outline-offset: 2px; +} +.comment-thread__actions button:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.comment-thread__actions button { + display: inline-flex; + align-items: center; + gap: 0.25rem; + border-radius: var(--radius-full); + border-width: 1px; + border-color: var(--border-subtle); + background-color: var(--surface-base); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + font-weight: 600; + color: var(--text-muted); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +} +.comment-thread__actions button:hover, + .comment-thread__actions button:focus-visible { + color: var(--text-primary); + border-color: var(--border-default); + } +.comment-thread__replies { + display: flex; + flex-direction: column; + gap: var(--space-md); + margin: 0; + margin-inline-start: calc(var(--space-lg) + var(--space-sm)); + padding-inline-start: var(--space-md); + padding-block-start: var(--space-md); + border-inline-start: var(--border-width-hairline) solid var(--border-subtle); + list-style: none; + } +.comment-thread__load-more { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + border-radius: var(--radius-full); + border-width: 1px; + border-color: var(--border-subtle); + background-color: var(--surface-base); + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + font-weight: 600; + color: var(--text-primary); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +} +.comment-thread__load-more:hover, + .comment-thread__load-more:focus-visible { + border-color: var(--border-default); + color: var(--text-primary); + background-color: color-mix(in srgb, var(--surface-alt) 70%, transparent); + } +.comment-thread__disabled { + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + color: var(--text-muted); + border-radius: var(--radius-lg); + border: var(--border-width-hairline) dashed + color-mix(in srgb, var(--border-default) 32%, transparent); + background-color: var(--surface-alt); + padding: var(--space-md); +} +.comment-composer { + display: flex; + flex-direction: column; + gap: var(--space-md); + border-top: var(--border-width-hairline) solid var(--border-subtle); + padding-top: var(--space-lg); + } +.comment-composer__field { + display: flex; + flex-direction: column; + gap: var(--space-2xs); + } +.comment-composer__label { + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + font-weight: 600; + color: var(--text-primary); +} +.comment-composer__textarea { + min-block-size: var(--comment-composer-min-height); + } +.comment-composer__footer { + display: flex; + flex-direction: column; + gap: var(--space-sm); + } +.comment-composer__hint { + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + color: var(--text-muted); + margin: 0; +} +.comment-composer__actions { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + gap: var(--space-sm); + } +.comment-composer__char-count { + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + font-weight: 500; + color: var(--text-muted); +} +.comment-composer__submit { + outline: 2px solid transparent; + outline-offset: 2px; +} +.comment-composer__submit:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.comment-composer__status { + margin: 0; + } +@media (min-width: 640px) { + .comment-composer__footer { + flex-direction: row; + align-items: center; + justify-content: space-between; + } + } +.video-modal__progress { + height: 0.5rem; + width: 100%; + overflow: hidden; + border-radius: var(--radius-full); + background-color: var(--border-subtle); +} +.video-modal__stat-metrics { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + color: var(--text-muted); +} +.profile-modal { + display: flex; + flex-direction: column; + inline-size: 100%; + block-size: 100%; + min-block-size: 100%; + max-block-size: none; + overflow: hidden; + --modal-sheet-inline-max: var(--profile-modal-inline-max); + --profile-modal-scale: 0.9; + --profile-modal-gap: calc( + (var(--space-md) - var(--space-3xs)) * var(--profile-modal-scale) + ); + --profile-modal-padding-inline: calc( + (var(--space-lg) - var(--space-md)) * var(--profile-modal-scale) + ); + --profile-modal-padding-block-start: calc( + (var(--space-md) - var(--space-2xs)) * var(--profile-modal-scale) + ); + --profile-modal-padding-block-end: calc( + (var(--space-lg) - var(--space-sm)) * var(--profile-modal-scale) + ); + --profile-modal-nav-gap: calc(var(--space-xs) * var(--profile-modal-scale)); + } +.profile-modal__content { + display: flex; + flex: 1 1 auto; + flex-direction: column; + block-size: 100%; + min-block-size: 0; + transform: scale(var(--profile-modal-scale)); + transform-origin: top center; + } +.profile-modal__active-profile { + outline: 2px solid transparent; + outline-offset: 2px; +} +.profile-modal__active-profile:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.profile-modal__active-profile { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 1rem; + padding: 1rem; +} +.profile-modal__active-details { + display: flex; + flex: 1 1 var(--profile-modal-active-min-width); + flex-direction: column; + gap: var(--space-2xs); + min-inline-size: 0; + } +.profile-modal__active-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--space-xs); + } +.profile-hashtag-list { + display: flex; + flex-wrap: wrap; + gap: var(--space-xs); + margin: 0; + padding: 0; + list-style: none; + } +.profile-hashtag-item { + display: inline-flex; + align-items: center; + gap: var(--space-3xs); + padding-block: var(--space-3xs); + padding-inline: var(--space-sm); + border-radius: var(--radius-full); + border: var(--border-width-hairline) solid var(--color-border-translucent); + background-color: var(--color-panel); + color: var(--color-text); + font-size: var(--font-size-sm); + font-weight: var(--font-weight-medium); + line-height: var(--font-lineheight-tight); + } +.profile-hashtag-remove { + display: inline-flex; + align-items: center; + justify-content: center; + inline-size: var(--space-md); + block-size: var(--space-md); + border-radius: var(--radius-full); + color: var(--color-text-muted); + background: transparent; + border: none; + padding: 0; + cursor: pointer; + } +.profile-hashtag-remove:hover { + color: var(--color-text); + } +.profile-modal__remove-button { + border-width: 2px; +} +.profile-modal__remove-button:focus-visible { + --tw-ring-color: var(--color-critical-strong); + } +.profile-modal__add-profile { + --tw-ring-color: var(--color-ring-alert); + outline: 2px solid transparent; + outline-offset: 2px; + } +.profile-modal__add-profile:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.profile-modal__add-profile { + position: relative; + display: flex; + width: 100%; + align-items: center; + justify-content: flex-start; + gap: 1rem; + border-radius: var(--radius-xl); + padding-left: 1.25rem; + padding-right: 1.25rem; + padding-top: 1rem; + padding-bottom: 1rem; + text-align: left; + color: var(--text-primary); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + border: var(--outline-thick-width) solid var(--color-ring-alert); + background-color: var(--surface-panel-hover); +} +.profile-modal__add-profile:hover { + background-color: color-mix( + in srgb, + var(--surface-panel-hover) 88%, + var(--color-ring-alert) 12% + ); + } +.profile-modal__add-profile:focus-visible { + background-color: color-mix( + in srgb, + var(--surface-panel-hover) 80%, + var(--color-ring-alert) 20% + ); + } +.profile-modal__add-profile-icon { + display: flex; + height: 2.5rem; + width: 2.5rem; + align-items: center; + justify-content: center; + border-radius: var(--radius-full); + background-color: color-mix( + in srgb, + var(--color-ring-alert) 18%, + var(--surface-panel) + ); + color: var(--color-ring-alert); +} +.profile-modal__body { + display: flex; + flex: 1 1 auto; + flex-direction: column; + gap: var(--profile-modal-gap); + inline-size: 100%; + min-block-size: 0; + overflow: hidden; + overflow-x: hidden; + padding-block-start: var(--profile-modal-padding-block-start); + padding-block-end: var(--profile-modal-padding-block-end); + padding-inline: var(--profile-modal-padding-inline); + } +.profile-modal__layout { + display: flex; + flex: 1 1 auto; + flex-direction: column; + gap: var(--profile-modal-gap); + inline-size: 100%; + min-block-size: 0; + } +.profile-modal__header { + align-items: center; + } +.profile-modal__header-title { + display: flex; + align-items: center; + gap: var(--space-sm); + } +.profile-modal__menu { + display: flex; + flex-direction: column; + gap: var(--space-sm); + } +.profile-modal__pane-wrapper { + display: flex; + flex: 1 1 auto; + flex-direction: column; + min-block-size: 0; + } +.profile-modal__nav { + display: flex; + flex-wrap: wrap; + gap: var(--profile-modal-nav-gap); + align-self: flex-start; + overflow-x: visible; + } +.profile-nav__unread-dot { + position: absolute; + top: 50%; + left: -6px; + right: auto; + width: var(--space-sm); + height: var(--space-sm); + border-radius: var(--radius-full); + border: var(--border-width-hairline) solid var(--surface-panel); + background-color: var(--status-danger); + display: none; + pointer-events: none; + transform: translateY(-50%); + } +.profile-nav__unread-dot.is-visible { + display: block; + } +.profile-modal__panes { + display: flex; + flex: 1 1 auto; + flex-direction: column; + block-size: 100%; + min-block-size: 0; + overflow-y: auto; + overflow-x: hidden; + padding-inline-end: var(--profile-modal-scrollbar-offset); + scrollbar-gutter: stable; + } +.profile-pane { + display: flex; + flex-direction: column; + min-block-size: auto; + overflow-x: hidden; + } +/* Specific override for messages pane to prevent parent scrolling on desktop */ +@media (min-width: 768px) { + #profilePaneMessages { + height: 100%; + min-height: 0; + overflow: hidden; + } + + #dmAppShellMount { + height: 100%; + min-height: 0; + } + } +.profile-pane.hidden { + display: none !important; + } +@supports not (scrollbar-gutter: stable) { + .profile-modal__panes { + padding-inline-end: calc( + var(--profile-modal-scrollbar-offset) + var(--space-2xs) + ); + } + } +@media (max-width: calc(1024px - var(--breakpoint-edge-offset))) { + .profile-modal__layout[data-mobile-view="menu"] + .profile-modal__pane-wrapper { + display: none; + } + + .profile-modal__layout[data-mobile-view="pane"] .profile-modal__menu { + display: none; + } + } +@media (min-width: 1024px) { + .profile-modal { + max-block-size: var(--modal-shell-max-height); + } + + .profile-modal__body { + gap: calc(var(--profile-modal-gap) + var(--space-2xs)); + padding-inline: calc( + var(--profile-modal-padding-inline) + var(--space-xs) + ); + } + + .profile-modal__layout { + display: grid; + align-items: stretch; + block-size: 100%; + gap: calc(var(--profile-modal-gap) + var(--space-2xs)); + grid-template-columns: + minmax( + var(--profile-modal-nav-min-width), + var(--profile-modal-nav-max-width) + ) + minmax(0, 1fr); + grid-auto-rows: minmax(0, 1fr); + } + + .profile-modal__nav { + flex-direction: column; + flex-wrap: nowrap; + gap: var(--profile-modal-nav-gap); + justify-self: start; + inline-size: var(--profile-modal-nav-max-width); + min-inline-size: var(--profile-modal-nav-min-width); + } + + .profile-modal__pane-wrapper { + block-size: 100%; + } + } +/* Torrent helper classes */ +.beacon-hidden-download, + [data-beacon-hidden-download], + .torrent-download-anchor { + display: none !important; + } +.beacon-clipboard-offscreen, + [data-beacon-clipboard-state="offscreen"], + [data-beacon-clipboard="offscreen"], + .torrent-clipboard-textarea { + position: fixed; + top: 0; + left: var(--offscreen-offset); + opacity: 0; + width: 0; + height: 0; + pointer-events: none; + } +.beacon-toast-motion, + .torrent-toast-motion { + transition-property: opacity, transform; + will-change: opacity, transform; + opacity: 0; + transform: translateY(var(--toast-translate)); + } +.beacon-toast-motion[data-beacon-motion="enter"], + .beacon-toast-motion[data-beacon-motion="show"], + .torrent-toast-motion[data-beacon-motion="enter"], + .torrent-toast-motion[data-beacon-motion="show"] { + opacity: 1; + transform: translateY(0); + } +.beacon-toast-motion[data-beacon-motion="exit"], + .torrent-toast-motion[data-beacon-motion="exit"] { + opacity: 0; + transform: translateY(var(--toast-translate)); + } +.torrent-toast { + pointer-events: auto; + width: 100%; + max-width: 28rem; + border-radius: var(--radius-lg); + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + font-weight: 500; + letter-spacing: -0.025em; + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-inset: inset; +} +.torrent-toast--info { + border-width: 1px; + border-color: var(--border-overlay); + background-color: var(--surface-base); + color: var(--text-primary); +} +.torrent-toast--success { + border-width: 1px; + border-color: var(--color-info); + --tw-border-opacity: 0.6; + background-color: var(--color-info); + color: var(--color-white); +} +.torrent-toast--error { + border-width: 1px; + border-color: var(--color-critical); + --tw-border-opacity: 0.6; + background-color: var(--color-critical); + color: var(--color-white); +} +.torrent-toast--warn { + border-width: 1px; + border-color: var(--status-warning-strong); + --tw-border-opacity: 0.7; + background-color: var(--status-warning-strong); + color: var(--status-warning-strong); +} +.watch-history-card { + min-width: 0; + --video-card-lift-translate-y: 0; + --video-card-backdrop-opacity: 0; + --video-card-backdrop-translate-y: var(--space-md-plus); + --video-card-backdrop-scale: 1.08; + --video-card-backdrop-blur: var(--blur-thumbnail-blur); + outline: 2px solid transparent; + outline-offset: 2px; + } +.watch-history-card:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 0.7; + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: var(--surface-base); + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-fast); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + --tw-ring-color: var(--color-accent); +} +.watch-history-card { + position: relative; + border-radius: var(--radius-lg); + border-width: 1px; + border-color: transparent; + background-color: var(--surface-raised); + color: var(--text-primary); + --tw-shadow: var(--shadow-md); + --tw-shadow-colored: var(--shadow-md); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transform: translate3d(0, var(--video-card-lift-translate-y), 0); + transition-property: + transform, box-shadow, border-color, background-color, color; + will-change: transform; + isolation: isolate; +} +.watch-history-card::before { + content: ""; + position: absolute; + inset: 0; + z-index: 0; + opacity: var(--video-card-backdrop-opacity); + background-image: var(--video-card-thumb-url, none); + background-position: center; + background-repeat: no-repeat; + background-size: cover; + transform: translate3d(0, var(--video-card-backdrop-translate-y), 0) + scale(var(--video-card-backdrop-scale)); + filter: blur(var(--video-card-backdrop-blur)); + transition: + opacity var(--motion-duration-base) var(--motion-ease-in-out), + transform var(--motion-duration-base) var(--motion-ease-in-out), + filter var(--motion-duration-base) var(--motion-ease-in-out); + pointer-events: none; + will-change: opacity, transform, filter; + } +.watch-history-card > * { + position: relative; + z-index: 1; + } +.watch-history-card:hover,.watch-history-card:focus-within { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate); + --video-card-backdrop-opacity: 0.425; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ); + --video-card-backdrop-scale: 1.12; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } +.watch-history-card:focus-visible { + border-color: var(--color-accent); + --tw-border-opacity: 0.4; + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.watch-history-card[data-state="critical"] { + border-width: 1px; + border-color: var(--color-critical); + --tw-border-opacity: 0.4; + background-color: var(--surface-raised-critical); +} +.watch-history-card[data-state="private"] { + background-color: var(--surface-raised-private); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.watch-history-card[data-state="disabled"] { + opacity: 0.6; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.watch-history-card[data-alert="nsfw-owner"] { + border-width: 1px; + border-color: var(--color-critical); + --tw-border-opacity: 0.4; + background-color: var(--color-black); +} +.watch-history-card[data-alert="nsfw-owner"]:hover { + --tw-border-opacity: 0.6; + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.watch-history-card img[data-thumbnail-state="blurred"],.watch-history-card img[data-visual-state="blurred"] { + transition-duration: var(--motion-duration-base); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + filter: blur(var(--blur-thumbnail-blur)); + transform: scale(1.05); +} +.watch-history-card[data-owner-is-viewer="true"][data-url-health-state="offline"][data-stream-health-state="unhealthy"] { + border-width: 1px; + border-color: var(--color-critical); + --tw-border-opacity: 0.4; + background-color: var(--surface-raised-critical); +} +#playerModal + .player-modal__module-item + > .watch-history-card:not(.player-modal__similar-card) { + display: flex; + width: 100%; + flex-direction: row; + flex-wrap: nowrap; + align-items: stretch; + gap: var(--space-md); +} +#playerModal + .player-modal__module-item + > .watch-history-card:not(.player-modal__similar-card) + .video-card__media { + width: clamp( + var(--video-modal-item-media-min), + 18vw, + var(--video-modal-item-media-max) + ); + flex-shrink: 0; + overflow: hidden; + border-radius: var(--radius-lg); + } +#playerModal + .player-modal__module-item + > .watch-history-card:not(.player-modal__similar-card) + .video-card__content { + padding: clamp(var(--space-sm), 1.25vw, var(--space-md)); + display: flex; + min-width: 0px; + flex: 1 1 0%; + flex-direction: column; + gap: var(--space-sm); + } +#playerModal + .player-modal__module-item + > .watch-history-card:not(.player-modal__similar-card) + .video-card__meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem; + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); + color: var(--text-muted-strong); +} +.watch-history-card[data-motion="enter"] { + opacity: 0; + animation: card-motion-enter 220ms ease-out forwards; + } +@media (min-width: 1024px) { + .watch-history-card:hover .marquee-host.can-marquee .marquee-static { + opacity: 0; + } + .watch-history-card:hover .marquee-host.can-marquee .marquee-anim { + opacity: 1; + } + .watch-history-card:hover .marquee-host.can-marquee .marquee-track { + animation-play-state: running; + } + .watch-history-card:hover .marquee-host.can-marquee .marquee-static { + opacity: 0; + } + .watch-history-card:hover .marquee-host.can-marquee .marquee-anim { + opacity: 1; + } + .watch-history-card:hover .marquee-host.can-marquee .marquee-track { + animation-play-state: running; + } + .watch-history-card:hover .marquee-host.can-marquee .marquee-static { + opacity: 0; + } + .watch-history-card:hover .marquee-host.can-marquee .marquee-anim { + opacity: 1; + } + .watch-history-card:hover .marquee-host.can-marquee .marquee-track { + animation-play-state: running; + } + } +@media (max-width: calc(1024px - var(--breakpoint-edge-offset))) { + .watch-history-card[data-mobile-active="true"] { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate); + --video-card-backdrop-opacity: 0.425; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ); + --video-card-backdrop-scale: 1.12; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } + + .watch-history-card[data-mobile-active="true"] .video-card__title { + color: var(--color-accent); + } + + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-static { + opacity: 0; + } + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-anim { + opacity: 1; + } + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-track { + animation-play-state: running; + } + .watch-history-card[data-mobile-active="true"] { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate); + --video-card-backdrop-opacity: 0.425; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ); + --video-card-backdrop-scale: 1.12; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } + + .watch-history-card[data-mobile-active="true"] .video-card__title { + color: var(--color-accent); + } + + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-static { + opacity: 0; + } + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-anim { + opacity: 1; + } + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-track { + animation-play-state: running; + } + .watch-history-card[data-mobile-active="true"] { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate); + --video-card-backdrop-opacity: 0.425; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ); + --video-card-backdrop-scale: 1.12; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } + + .watch-history-card[data-mobile-active="true"] .video-card__title { + color: var(--color-accent); + } + + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-static { + opacity: 0; + } + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-anim { + opacity: 1; + } + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-track { + animation-play-state: running; + } + .watch-history-card[data-mobile-active="true"] { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate); + --video-card-backdrop-opacity: 0.425; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ); + --video-card-backdrop-scale: 1.12; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } + + .watch-history-card[data-mobile-active="true"] .video-card__title { + color: var(--color-accent); + } + + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-static { + opacity: 0; + } + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-anim { + opacity: 1; + } + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-track { + animation-play-state: running; + } + .watch-history-card[data-mobile-active="true"] { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate); + --video-card-backdrop-opacity: 0.425; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ); + --video-card-backdrop-scale: 1.12; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } + + .watch-history-card[data-mobile-active="true"] .video-card__title { + color: var(--color-accent); + } + + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-static { + opacity: 0; + } + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-anim { + opacity: 1; + } + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-track { + animation-play-state: running; + } + .watch-history-card[data-mobile-active="true"] { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate); + --video-card-backdrop-opacity: 0.425; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ); + --video-card-backdrop-scale: 1.12; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } + + .watch-history-card[data-mobile-active="true"] .video-card__title { + color: var(--color-accent); + } + + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-static { + opacity: 0; + } + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-anim { + opacity: 1; + } + .watch-history-card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-track { + animation-play-state: running; + } + } +.watch-history-card { + display: flex; + flex-wrap: wrap; + align-items: stretch; + gap: 1.25rem; + padding: 1rem; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +} +@media (prefers-reduced-motion: reduce) { + + .watch-history-card,.watch-history-card[data-motion="enter"] { + transition-property: none; + } + + .watch-history-card[data-motion="enter"] { + opacity: 1; + animation: none; + } +} +.watch-history-card:hover { + --tw-translate-y: -0.125rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +@media (min-width: 640px) { + .watch-history-card { + gap: 1.5rem; + padding: 1.25rem; + } +} +.watch-history-card [data-thumbnail-state="blurred"] img, + .watch-history-card [data-visual-state="blurred"] img { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + filter: blur(var(--blur-thumbnail-blur)); + transform: scale(1.05); +} +.watch-history-card__hidden { + margin-top: var(--space-md); + border-radius: var(--radius-lg); + border-width: 1px; + border-style: dashed; + border-color: var(--border-default); + background-color: var(--surface-overlay-panel-soft); + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + color: var(--text-primary); +} +.watch-history-card__hidden .moderation-badge { + align-self: flex-start; +} +/* Ensure the video modal avatar respects the blur state */ +#playerModal .channel-card__avatar img[data-visual-state="blurred"] { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + filter: blur(var(--blur-thumbnail-blur)); + transform: scale(1.05); +} +/* Override moderation badge text color inside the modal bar for better contrast in all themes */ +#playerModal .video-modal__moderation-bar .moderation-badge { + --text-status-neutral-on: var(--color-text); + --text-status-warning-on: var(--color-warning-on); + } +#playerModal + .video-modal__moderation-bar + .moderation-badge[data-variant="neutral"] { + color: var(--color-text); + } +.\!container { + max-width: var(--layout-docs-max-width) !important; + margin: 0 auto !important; + padding: var(--space-md) !important; + } +.container { + max-width: var(--layout-docs-max-width); + margin: 0 auto; + padding: var(--space-md); + } +.qr-code__table { + border: 0; + border-collapse: collapse; + margin: var(--qr-code-table-margin, 0); + } +.qr-code__cell { + border: 0; + border-collapse: collapse; + padding: 0; + margin: 0; + width: var(--qr-code-cell-width); + height: var(--qr-code-cell-height); + background-color: var(--qr-code-color-light); + } +.qr-code__cell--dark { + background-color: var(--qr-code-color-dark); + } +.qr-code__container[data-qr-code-mode="image"] .qr-code__canvas { + display: none; + } +.qr-code__container[data-qr-code-mode="image"] .qr-code__image { + display: block; + } +.qr-code__container[data-qr-code-mode="canvas"] .qr-code__canvas { + display: block; + } +.qr-code__container[data-qr-code-mode="canvas"] .qr-code__image { + display: none; + } +.fade-in { + opacity: 0; + animation: interface-fade-in 280ms ease-out forwards; + animation-delay: var(--fade-in-delay, 0ms); + } +.fade-in-delay-100 { + --fade-in-delay: 100ms; + } +.fade-in-delay-200 { + --fade-in-delay: 200ms; + } +.fade-in-delay-300 { + --fade-in-delay: 300ms; + } +.\!card[data-motion="enter"] { + opacity: 0 !important; + animation: card-motion-enter 220ms ease-out forwards !important; + } +.card[data-motion="enter"] { + opacity: 0; + animation: card-motion-enter 220ms ease-out forwards; + } +.icon-button { + display: inline-flex; + align-items: center; + justify-content: center; + width: var(--icon-size-lg); + height: var(--icon-size-lg); + min-width: var(--icon-size-lg); + min-height: var(--icon-size-lg); + flex: 0 0 var(--icon-size-lg); + line-height: 0; + background-color: var(--color-neutral-700); + color: var(--color-white); + border: none; + border-radius: var(--radius-full); + cursor: pointer; + transition: + background-color 200ms ease, + box-shadow 200ms ease; + } +.icon-button:hover { + background-color: var(--color-neutral-600); + } +.icon-button:focus, + .icon-button:active { + outline: none; + box-shadow: 0 0 0 var(--icon-button-ring-width) var(--color-ring-alert); + } +.icon-image { + width: var(--icon-size-md); + height: var(--icon-size-md); + pointer-events: none; + } +.accent-action-button, + .channel-action-button { + --accent-action-bg: color-mix( + in srgb, + var(--surface-overlay-panel) 78%, + transparent + ); + --accent-action-border: color-mix( + in srgb, + var(--border-overlay) 92%, + transparent + ); + --accent-action-bg-hover: color-mix( + in srgb, + var(--color-accent) 22%, + var(--surface-overlay-panel) + ); + --accent-action-ring: color-mix( + in srgb, + var(--color-accent) 48%, + transparent + ); + --accent-action-bg-active: color-mix( + in srgb, + var(--color-accent-pressed) 28%, + var(--surface-overlay-panel) + ); + + color: var(--color-text); + background-color: var(--accent-action-bg); + border-color: var(--accent-action-border); + border-style: solid; + border-width: var(--border-width-hairline); + } +.accent-action-button:hover, + .accent-action-button:focus-visible, + .channel-action-button:hover, + .channel-action-button:focus-visible { + color: var(--color-accent-strong); + background-color: var(--accent-action-bg-hover); + } +.accent-action-button:focus, + .accent-action-button:focus-visible, + .channel-action-button:focus, + .channel-action-button:focus-visible { + box-shadow: 0 0 0 var(--icon-button-ring-width) var(--accent-action-ring); + } +.accent-action-button:active, + .channel-action-button:active { + color: var(--color-accent-pressed); + background-color: var(--accent-action-bg-active); + } +.modal-nav .icon-button { + box-shadow: none; + padding: 0; + gap: 0; + width: var(--icon-size-lg); + height: var(--icon-size-lg); + min-width: var(--icon-size-lg); + min-height: var(--icon-size-lg); + } +.modal-nav + .icon-button:not(.accent-action-button):not(.channel-action-button) { + border: none; + } +.modal-meta { + display: inline-flex; + align-items: center; + gap: var(--space-xs); + } +.modal-view-count { + display: inline-flex; + align-items: center; + gap: var(--space-2xs); + font-size: var(--font-size-sm); + line-height: var(--space-md-plus); + color: var(--color-muted-alt); + white-space: nowrap; + } +.view-count-text { + white-space: nowrap; + color: var(--color-muted-strong); + } +.ratio-16-9 { + position: relative; + width: 100%; + padding-top: 56.25%; + background-color: var(--color-card); + } +.ratio-16-9 > img { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + -o-object-fit: cover; + object-fit: cover; + } +#modalContainer { + position: fixed; + inset: 0; + z-index: var(--z-modal-root); + pointer-events: none; + isolation: isolate; + } +#modalContainer > * { + pointer-events: auto; + } +#modalContainer > [id$="Modal"]:not(#playerModal) { + z-index: var(--z-modal-root); + } +#modalContainer > [id$="Modal"]:not(#playerModal) > .bv-modal-backdrop { + z-index: var(--z-modal-overlay); + } +#modalContainer > [id$="Modal"]:not(#playerModal) .bv-modal__panel { + z-index: var(--z-modal-content); + } +#modalContainer > #profileModal, + #profileModal[data-modal-stack="top"] { + z-index: var(--z-modal-top-root); + } +#modalContainer > #profileModal > .bv-modal-backdrop, + #profileModal[data-modal-stack="top"] > .bv-modal-backdrop { + z-index: var(--z-modal-top-overlay); + } +#modalContainer > #profileModal .bv-modal__panel, + #profileModal[data-modal-stack="top"] .bv-modal__panel { + z-index: var(--z-modal-top-content); + } +#modalContainer > #eventDetailsModal { + z-index: var(--z-modal-top-root); + } +#modalContainer > #eventDetailsModal > .bv-modal-backdrop { + z-index: var(--z-modal-top-overlay); + } +#modalContainer > #eventDetailsModal .bv-modal__panel { + z-index: var(--z-modal-top-content); + } +#shareNostrModal { + z-index: var(--z-modal-peak-root) !important; + } +#shareNostrModal > .bv-modal-backdrop { + z-index: var(--z-modal-peak-overlay) !important; + } +#shareNostrModal .bv-modal__panel { + z-index: var(--z-modal-peak-content) !important; + } +.bv-modal[data-nested-modal-active="true"] { + pointer-events: none; + visibility: hidden; + } +#modalContainer > #loginModal { + z-index: var(--z-modal-peak-root); + } +#modalContainer > #loginModal > .bv-modal-backdrop { + z-index: var(--z-modal-peak-overlay); + } +#modalContainer > #loginModal .bv-modal__panel { + z-index: var(--z-modal-peak-content); + } +#modalContainer > #playerModal { + z-index: var(--z-modal-player); + } +#modalContainer > #playerModal .player-modal__container, + #modalContainer > #playerModal .player-modal__content { + z-index: var(--z-modal-content); + } +.modal-always-on-top { + z-index: var(--z-modal-always-top) !important; + } +#playerModal { + position: fixed; + inset: 0; + z-index: var(--z-modal-player); + flex-direction: column; + align-items: stretch; + justify-content: flex-start; + width: 100vw; + height: 100vh; + padding: 0; + background-color: var(--color-surface); + color: var(--color-subtle-text); + --player-modal-inline-max: var(--layout-player-max-width); + --player-modal-inline-padding: clamp( + var(--space-md-plus), + 4vw, + var(--space-2xl) + ); + overflow-x: hidden; + overflow-y: auto; + overscroll-behavior: contain; + -webkit-overflow-scrolling: touch; + } +#modalVideo { + width: 100%; + aspect-ratio: 16 / 9; + background-color: var(--color-black); + } +.notification-portal { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: var(--z-overlay-toast); + display: flex; + flex-direction: column; + align-items: center; + gap: var(--space-md); + padding: calc(var(--space-xl) + env(safe-area-inset-top)) var(--space-lg) + var(--space-lg); + pointer-events: none; + opacity: 0; + transform: translateY(var(--toast-translate)); + transition: + transform var(--motion-duration-base) var(--motion-ease-out), + opacity var(--motion-duration-base) var(--motion-ease-out); + } +@media (min-width: 640px) { + .notification-portal { + padding-inline: var(--space-xl); + } + } +.notification-portal:has(.notification-banner:not(.hidden)) { + opacity: 1; + transform: translateY(0); + } +.notification-portal--active { + opacity: 1; + transform: translateY(0); + } +.notification-banner, + .torrent-toast { + --notify-accent: var(--notification-banner-accent); + --notify-accent-strong: var(--notification-banner-accent-strong); + --notify-border: var(--notification-banner-border); + --notify-background: var(--notification-banner-background); + --notify-foreground: var(--notification-banner-text); + + position: relative; + display: flex; + align-items: center; + gap: var(--space-md); + width: min(var(--notification-banner-max-inline), 100%); + padding: var(--space-md) var(--space-xl); + border-radius: var(--radius-xl); + border: var(--border-width-hairline) solid var(--notify-border); + background: var(--notify-background); + color: var(--notify-foreground); + box-shadow: 0 var(--shadow-notification-offset-y) + var(--shadow-notification-blur) var(--color-shadow-notification); + backdrop-filter: blur(var(--overlay-backdrop-blur)); + pointer-events: auto; + isolation: isolate; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + font-weight: 600; + line-height: 1.375; + transition: + transform var(--motion-duration-base) var(--motion-ease-out), + opacity var(--motion-duration-base) var(--motion-ease-out); + } +.notification-banner::before, + .torrent-toast::before { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; + background: linear-gradient( + 135deg, + var(--notify-accent) 0%, + var(--notify-accent-strong) 100% + ); + opacity: 0.18; + pointer-events: none; + } +.notification-banner > *, + .torrent-toast > * { + position: relative; + z-index: 1; + } +.notification-dismiss { + display: inline-flex; + height: 2rem; + width: 2rem; + align-items: center; + justify-content: center; + border-radius: var(--radius-full); + border-width: 1px; + border-color: transparent; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: var(--motion-duration-base); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + color: inherit; + opacity: 0.75; + background-color: var(--notify-dismiss-background); +} +.notification-dismiss:hover, + .notification-dismiss:focus-visible { + opacity: 1; + background-color: var(--notify-dismiss-background-hover); + } +.notification-dismiss svg { + height: 1rem; + width: 1rem; +} +.notification-banner:not(.hidden) { + animation: notification-slide-in var(--motion-duration-base) + var(--motion-ease-out); + } +.notification-banner--critical, + .torrent-toast--error { + --notify-accent: var(--notification-banner-critical-accent); + --notify-accent-strong: var(--notification-banner-critical-accent-strong); + --notify-border: var(--notification-banner-critical-border); + --notify-background: var(--notification-banner-critical-background); + --notify-foreground: var(--notification-banner-critical-text); + } +.notification-banner--success, + .torrent-toast--success { + --notify-accent: var(--notification-banner-success-accent); + --notify-accent-strong: var(--notification-banner-success-accent-strong); + --notify-border: var(--notification-banner-success-border); + --notify-background: var(--notification-banner-success-background); + --notify-foreground: var(--notification-banner-success-text); + } +.notification-banner--info, + .torrent-toast--info { + --notify-accent: var(--notification-banner-info-accent); + --notify-accent-strong: var(--notification-banner-info-accent-strong); + --notify-border: var(--notification-banner-info-border); + --notify-background: var(--notification-banner-info-background); + --notify-foreground: var(--notification-banner-info-text); + } +.notification-banner--warning, + .torrent-toast--warn { + --notify-accent: var(--notification-banner-warning-accent); + --notify-accent-strong: var(--notification-banner-warning-accent-strong); + --notify-border: var(--notification-banner-warning-border); + --notify-background: var(--notification-banner-warning-background); + --notify-foreground: var(--notification-banner-warning-text); + } +.status-banner .status-spinner { + animation: spin 1s linear infinite; + border: var(--space-3xs) solid var(--color-info-border); + border-radius: var(--radius-full); + border-top-color: var(--color-info); + flex-shrink: 0; + height: var(--icon-size-md); + width: var(--icon-size-md); + } +.status-banner .status-message { + flex: 1 1 auto; + } +@keyframes notification-slide-in { + from { + opacity: 0; + transform: translateY(var(--toast-translate)); + } + + to { + opacity: 1; + transform: translateY(0); + } + } +.status-spinner--inline { + animation: spin 1s linear infinite; + border: var(--space-3xs) solid var(--color-border-info-soft); + border-radius: var(--radius-full); + border-top-color: var(--color-info); + display: inline-block; + height: var(--space-md); + width: var(--space-md); + } +body.iframe-form { + background-color: transparent; + color: var(--color-text); + font-family: var(--font-family-sans); + margin: var(--space-lg); + } +body.iframe-form .container { + width: 100%; + max-width: 100%; + margin: 0; + padding: 0; + } +body.iframe-form .\!container { + width: 100% !important; + max-width: 100% !important; + margin: 0 !important; + padding: 0 !important; + } +body.iframe-form .form-container { + background-color: var(--color-surface-alt); + border-radius: var(--radius-md); + box-shadow: var(--shadow-md); + color: var(--color-text); + display: flex; + flex-direction: column; + gap: var(--space-md); + padding: var(--space-lg); + } +body.iframe-form .form-container > p { + margin: 0; + } +body.iframe-form form { + display: flex; + flex-direction: column; + } +body.iframe-form form > * + * { + margin-top: var(--space-sm); + } +body.iframe-form form h1, + body.iframe-form form h2 { + color: var(--color-text); + margin-top: var(--space-md); + } +body.iframe-form label { + color: var(--color-subtle-text); + display: block; + font-weight: 600; + margin-top: var(--space-sm); + } +body.iframe-form label + .form-control, + body.iframe-form label + .textarea, + body.iframe-form label + .select { + margin-top: var(--space-xs); + } +body.iframe-form .checkbox-group, + body.iframe-form .radio-group { + display: flex; + flex-wrap: wrap; + gap: var(--space-sm); + margin-top: var(--space-xs); + } +body.iframe-form .checkbox-group label, + body.iframe-form .radio-group label { + align-items: center; + color: var(--color-subtle-text); + display: inline-flex; + font-weight: 500; + gap: var(--space-xs); + margin: 0; + } +body.iframe-form .checkbox-group input[type="checkbox"], + body.iframe-form .radio-group input[type="radio"] { + margin: 0; + } +body.iframe-form .radio-group input[type="radio"] { + accent-color: var(--color-accent-strong); + } +body.iframe-form button.btn { + align-self: flex-start; + margin-top: var(--space-md); + } +body.iframe-form button.\!btn { + align-self: flex-start !important; + margin-top: var(--space-md) !important; + } +body.iframe-form #status { + background: var(--color-surface); + border-radius: var(--radius-sm); + box-shadow: inset 0 0 0 var(--border-width-hairline) + var(--color-border-translucent); + color: var(--color-subtle-text); + min-height: calc(var(--space-2xl) + var(--space-xl)); + padding: var(--space-sm); + white-space: pre-wrap; + } +body.iframe-form .status-line { + color: var(--color-subtle-text); + margin: var(--space-2xs) 0; + } +body.iframe-form .status-line.error { + color: var(--color-critical-strong); + } +body.iframe-form .status-line.\!error { + color: var(--color-critical-strong) !important; + } +body.iframe-form .status-line.success { + color: var(--color-info-strong); + } +body.iframe-form .status-line.\!success { + color: var(--color-info-strong) !important; + } +body.iframe-form .status-line.warn { + color: var(--color-warning-strong); + } +@media (max-width: 640px) { + body.iframe-form { + margin: var(--space-md); + } + + body.iframe-form .form-container { + padding: var(--space-md); + } + } +#sidebarContainer { + position: relative; + overflow: visible; + } +#sidebar { + position: fixed; + top: 0; + left: 0; + width: var(--sidebar-width); + min-width: var(--sidebar-width); + height: 100vh; + height: 100dvh; + color: var(--color-text-frosted-strong); + overflow-x: hidden; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + overscroll-behavior: contain; + overscroll-behavior-inline: contain; + touch-action: pan-y; + scrollbar-gutter: stable both-edges; + background: transparent; + border-right: none; + backdrop-filter: none; + -webkit-backdrop-filter: none; + transition: + width 200ms ease, + transform 300ms ease; + z-index: var(--z-sidebar); + box-shadow: none; + isolation: isolate; + } +body.sidebar-collapsed, + #sidebar.sidebar-collapsed, + #sidebar[data-state="collapsed"], + #app.sidebar-collapsed { + --sidebar-width: var(--sidebar-width-collapsed); + } +body.sidebar-expanded, + #sidebar.sidebar-expanded, + #sidebar[data-state="expanded"], + #app.sidebar-expanded { + --sidebar-width: var(--sidebar-width-expanded); + } +#sidebar .sidebar__inner { + display: flex; + flex-direction: column; + height: 100%; + padding-block-start: max( + var(--space-sidebar-shell-block-start), + calc(var(--app-header-centerline) - (var(--sidebar-control-size) / 2)) + ); + padding-block-end: var(--space-sidebar-shell-block-end); + padding-inline: var(--space-sidebar-shell-inline); + width: 100%; + border-radius: var(--radius-sidebar-shell); + border: none; + background: none; + box-shadow: none; + backdrop-filter: none; + -webkit-backdrop-filter: none; + overflow: visible; + isolation: isolate; + transition: color 200ms ease; + } +.sidebar-scroll { + display: flex; + flex-direction: column; + gap: var(--space-sidebar-scroll-gap); + width: 100%; + height: 100%; + min-height: 0; + flex: 1 1 auto; + overflow: visible; + } +.sidebar-panel { + position: relative; + width: 100%; + display: flex; + flex-direction: column; + gap: var(--space-sidebar-panel-gap); + padding: var(--space-sidebar-panel-padding-block) + var(--space-sidebar-panel-padding-inline); + border-radius: var(--radius-sidebar-panel); + border: none; + background: transparent; + box-shadow: none; + color: inherit; + backdrop-filter: none; + -webkit-backdrop-filter: none; + overflow: hidden; + isolation: isolate; + transition: + border-color 200ms ease, + box-shadow 220ms ease, + background 220ms ease; + } +.sidebar-panel--primary { + flex: 1 1 auto; + min-height: 0; + padding-block-start: 0; + } +.sidebar-panel__header { + display: flex; + justify-content: flex-end; + margin-bottom: var(--space-md); + } +.sidebar-collapse-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + width: var(--sidebar-control-size); + height: var(--sidebar-control-size); + border-radius: var(--radius-full); + border: var(--border-width-hairline) solid + var(--color-border-translucent-medium); + background: var(--color-surface-tint-subtle); + color: inherit; + cursor: pointer; + transition: + background 180ms ease, + color 180ms ease, + border-color 180ms ease; + flex-shrink: 0; + box-sizing: border-box; + } +.sidebar-collapse-toggle:hover { + background: var(--color-border-translucent); + border-color: var(--color-border-translucent-stronger); + color: var(--color-text); + } +.sidebar-collapse-toggle:focus-visible { + outline: var(--space-3xs) solid var(--color-secondary); + outline-offset: var(--space-2xs); + } +.sidebar-collapse-toggle__icon { + width: var(--icon-size-sm); + height: var(--icon-size-sm); + transition: transform 200ms ease; + } +.sidebar-nav { + display: flex; + flex-direction: column; + gap: var(--space-xs); + opacity: 1; + transition: opacity 160ms ease; + width: 100%; + } +.sidebar-nav-link { + --sidebar-nav-translate-x: 0; + --sidebar-nav-link-highlight-opacity: 0; + --sidebar-nav-link-highlight-scale: 0.96; + display: flex; + align-items: center; + gap: var(--space-sidebar-footer-gap); + padding-block: var(--space-sidebar-nav-padding-block); + padding-inline-start: var(--space-sidebar-nav-padding-inline-start); + padding-inline-end: var(--space-sidebar-nav-padding-inline-end); + min-height: var(--sidebar-control-size); + border-radius: var(--radius-sidebar-nav); + font-size: var(--font-size-sidebar-nav); + font-weight: 600; + letter-spacing: 0.015em; + color: var(--color-text); + text-decoration: none; + width: 100%; + position: relative; + border: var(--border-width-hairline) solid transparent; + background: none; + box-shadow: none; + transform: translateX(var(--sidebar-nav-translate-x)); + transition: + color 180ms ease, + border-color 200ms ease, + box-shadow 220ms ease, + transform 220ms ease; + overflow: hidden; + isolation: isolate; + backdrop-filter: none; + -webkit-backdrop-filter: none; + } +.sidebar-nav-link::before, + .sidebar-nav-link::after { + content: ""; + position: absolute; + inset: calc(var(--border-width-hairline) * -1); + border-radius: inherit; + pointer-events: none; + transition: + opacity 220ms ease, + transform 240ms ease; + z-index: -1; + } +.sidebar-nav-link::before { + background: linear-gradient( + 120deg, + var(--color-sidebar-accent-start) 0%, + var(--color-sidebar-accent-end) 100% + ); + opacity: var(--sidebar-nav-link-highlight-opacity); + transform: scale(var(--sidebar-nav-link-highlight-scale)); + filter: saturate(140%); + } +.sidebar-nav-link::after { + background: linear-gradient( + 105deg, + var(--color-sidebar-hover) 0%, + transparent 75% + ); + opacity: 0.22; + mix-blend-mode: screen; + z-index: -2; + } +.sidebar-nav-link:hover, + .sidebar-nav-link:focus-visible { + --sidebar-nav-link-highlight-opacity: 1; + --sidebar-nav-link-highlight-scale: 1.02; + --sidebar-nav-translate-x: var(--space-sidebar-nav-translate); + color: var(--color-white); + border-color: var(--color-sidebar-hover-strong); + box-shadow: + 0 0 0 var(--border-width-hairline) var(--color-sidebar-hover-strong), + var(--shadow-sidebar-accent); + } +.sidebar-nav-link:focus-visible { + outline: none; + box-shadow: var(--shadow-sidebar-focus-ring), var(--shadow-sidebar-accent); + } +.sidebar-nav-link:hover::after, + .sidebar-nav-link:focus-visible::after { + opacity: 0.35; + } +.sidebar-nav-link--sub { + font-size: var(--font-size-sidebar-subnav); + font-weight: 500; + color: var(--color-sidebar-text-muted); + padding-block: var(--space-sidebar-subnav-padding-block); + letter-spacing: 0.02em; + --sidebar-nav-link-highlight-opacity: 0.25; + --sidebar-nav-link-highlight-scale: 1; + } +.sidebar-icon { + width: var(--space-lg); + height: var(--space-lg); + min-width: var(--space-lg); + min-height: var(--space-lg); + flex-shrink: 0; + display: block; + filter: var(--sidebar-icon-filter); + transition: filter 180ms ease; + } +.sidebar-footer { + display: flex; + flex-direction: column; + gap: var(--space-sidebar-footer-gap); + margin-top: auto; + position: relative; + z-index: 2; + } +.sidebar-dropup-trigger { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-sidebar-footer-gap); + width: 100%; + padding-block: var(--space-sidebar-trigger-padding-block); + padding-inline-start: var(--space-sidebar-nav-padding-inline-start); + padding-inline-end: var(--space-sidebar-nav-padding-inline-end); + min-height: var(--sidebar-control-size); + border-radius: var(--radius-sidebar-nav); + border: var(--border-width-hairline) solid transparent; + background: none; + color: var(--color-text); + font-size: var(--font-size-sidebar-trigger); + font-weight: 600; + cursor: pointer; + box-sizing: border-box; + flex-shrink: 0; + overflow: hidden; + isolation: isolate; + backdrop-filter: none; + -webkit-backdrop-filter: none; + box-shadow: none; + transition: + border-color 200ms ease, + color 200ms ease, + box-shadow 220ms ease; + } +.sidebar-dropup-trigger::before { + content: ""; + position: absolute; + inset: calc(var(--border-width-hairline) * -1); + border-radius: inherit; + pointer-events: none; + background: linear-gradient( + 120deg, + var(--color-sidebar-accent-start) 0%, + var(--color-sidebar-accent-end) 100% + ); + opacity: 0; + transform: scale(0.96); + transition: + opacity 200ms ease, + transform 220ms ease; + z-index: -1; + filter: saturate(140%); + } +.sidebar-dropup-trigger::after { + content: ""; + position: absolute; + inset: calc(var(--border-width-hairline) * -1); + border-radius: inherit; + pointer-events: none; + background: linear-gradient( + 110deg, + var(--color-sidebar-hover) 0%, + transparent 80% + ); + opacity: 0.18; + mix-blend-mode: screen; + transition: opacity 200ms ease; + z-index: -2; + } +.sidebar-dropup-trigger:hover, + .sidebar-dropup-trigger:focus-visible { + color: var(--color-white); + border-color: var(--color-sidebar-hover-strong); + box-shadow: + 0 0 0 var(--border-width-hairline) var(--color-sidebar-hover-strong), + var(--shadow-sidebar-dropup); + } +.sidebar-dropup-trigger:hover::before, + .sidebar-dropup-trigger:focus-visible::before { + opacity: 1; + transform: scale(1.02); + } +.sidebar-dropup-trigger:hover::after, + .sidebar-dropup-trigger:focus-visible::after { + opacity: 0.35; + } +.sidebar-dropup-trigger:focus-visible { + outline: none; + box-shadow: var(--shadow-sidebar-focus-ring), var(--shadow-sidebar-dropup); + } +.sidebar-dropup-trigger__glyph { + width: var(--icon-size-md); + height: var(--icon-size-md); + opacity: 0.9; + } +.sidebar-dropup-trigger__chevron { + width: var(--icon-size-sm); + height: var(--icon-size-sm); + transition: transform 180ms ease; + } +.sidebar-dropup-trigger__chevron.is-rotated, + .sidebar-dropup-trigger[aria-expanded="true"] + .sidebar-dropup-trigger__chevron { + transform: rotate(180deg); + } +.sidebar-dropup-panel { + position: absolute; + left: 0; + right: 0; + bottom: calc(100% + var(--space-sidebar-popover-offset)); + border-radius: var(--radius-xl); + border: var(--border-width-hairline) solid + var(--color-sidebar-border-strong); + background: linear-gradient( + 160deg, + color-mix(in srgb, var(--color-sidebar-surface) 88%, transparent) 0%, + color-mix(in srgb, var(--color-sidebar-surface-alt) 68%, transparent) 100% + ); + box-shadow: + inset 0 0 0 var(--border-width-fine) var(--color-sidebar-border), + var(--shadow-sidebar-panel); + padding: var(--space-sidebar-popover-padding-block) + var(--space-sidebar-popover-padding-inline); + color: var(--color-text-frosted); + opacity: 0; + visibility: hidden; + pointer-events: none; + transform: translateY(var(--space-sm)); + transition: + opacity 180ms ease, + transform 180ms ease, + visibility 180ms ease, + box-shadow 220ms ease; + backdrop-filter: blur(var(--sidebar-panel-backdrop-blur)) + saturate(var(--sidebar-panel-backdrop-saturation)); + -webkit-backdrop-filter: blur(var(--sidebar-panel-backdrop-blur)) + saturate(var(--sidebar-panel-backdrop-saturation)); + z-index: 5; + isolation: isolate; + } +.sidebar-dropup-panel[data-state="expanded"] { + opacity: 1; + visibility: visible; + pointer-events: auto; + transform: translateY(0); + } +.sidebar-dropup-panel__inner { + display: flex; + flex-direction: column; + gap: var(--space-sidebar-popover-gap); + width: 100%; + padding-inline-end: var(--space-sidebar-dropup-scroll-reserve); + max-height: var(--sidebar-dropup-max-height); + overflow-y: auto; + overflow-x: hidden; + scrollbar-gutter: stable both-edges; + } +.sidebar-dropup-panel__inner.is-measuring { + position: absolute; + width: -moz-max-content; + width: max-content; + max-width: none; + visibility: hidden; + pointer-events: none; + left: var(--offscreen-offset); + right: auto; + top: auto; + bottom: auto; + } +.sidebar-dropup-measure { + position: absolute; + visibility: hidden; + pointer-events: none; + height: 0; + overflow: hidden; + } +.sidebar-dropup-panel__inner > * { + width: 100%; + min-width: 0; + } +#sidebar[data-footer-state="expanded"] .sidebar-nav { + opacity: 0; + pointer-events: none; + } +#sidebar[data-footer-state="collapsed"] .sidebar-nav { + opacity: 1; + } +.sidebar-loading-wrapper { + display: flex; + justify-content: center; + align-items: center; + padding: var(--space-2xl) 0; + width: 100%; + grid-column: 1 / -1; + } +.sidebar-loading-indicator { + display: inline-flex; + align-items: center; + gap: var(--space-sm); + color: var(--color-muted-strong); + font-size: var(--font-size-sm); + font-weight: 500; + letter-spacing: 0.01em; + } +.sidebar-loading-text { + white-space: nowrap; + } +#app { + box-sizing: border-box; + transition: + margin-left 200ms ease, + width 200ms ease, + max-width 200ms ease; + } +#app[data-sidebar-state="collapsed"] { + margin-left: calc( + var(--sidebar-width-collapsed) + var(--sidebar-content-gap) + ); + width: calc( + 100vw - var(--sidebar-width-collapsed) - var(--sidebar-content-gap) + ); + max-width: calc( + 100vw - var(--sidebar-width-collapsed) - var(--sidebar-content-gap) + ); + } +#app[data-sidebar-state="expanded"] { + margin-left: calc( + var(--sidebar-width-expanded) + var(--sidebar-content-gap) + ); + width: calc( + 100vw - var(--sidebar-width-expanded) - var(--sidebar-content-gap) + ); + max-width: calc( + 100vw - var(--sidebar-width-expanded) - var(--sidebar-content-gap) + ); + } +body.sidebar-collapsed #sidebar .sidebar-nav-link, + #sidebar.sidebar-collapsed .sidebar-nav-link { + justify-content: center; + width: var(--sidebar-control-size); + padding: 0; + margin-inline: auto; + border-radius: var(--radius-full); + --sidebar-nav-translate-x: 0; + --sidebar-nav-link-highlight-scale: 1; + } +body.sidebar-expanded #sidebar .sidebar-nav-link, + #sidebar.sidebar-expanded .sidebar-nav-link { + justify-content: flex-start; + } +body.sidebar-collapsed #sidebar .sidebar-text, + #sidebar.sidebar-collapsed .sidebar-text { + display: none; + } +body.sidebar-collapsed #sidebar .sidebar__inner, + #sidebar.sidebar-collapsed .sidebar__inner { + padding-inline: var(--space-2xs); + } +body.sidebar-collapsed #sidebar .sidebar-panel, + #sidebar.sidebar-collapsed .sidebar-panel { + padding-inline: calc(var(--space-3xs) * 3); + } +body.sidebar-collapsed #sidebar .sidebar-panel__header, + #sidebar.sidebar-collapsed .sidebar-panel__header { + justify-content: center; + } +body.sidebar-collapsed #sidebar .sidebar-collapse-toggle, + #sidebar.sidebar-collapsed .sidebar-collapse-toggle { + margin-inline: auto; + } +#sidebar .sidebar-toggle-icon--rotated { + transform: rotate(180deg); + } +body.sidebar-collapsed #sidebar .sidebar-toggle-icon--rotated, + #sidebar.sidebar-collapsed .sidebar-toggle-icon--rotated { + transform: rotate(0deg); + } +body.sidebar-collapsed #sidebar .sidebar-dropup-trigger, + #sidebar.sidebar-collapsed .sidebar-dropup-trigger { + justify-content: center; + width: var(--sidebar-control-size); + height: var(--sidebar-control-size); + padding: 0; + margin-inline: auto; + border-radius: var(--radius-full); + } +body.sidebar-collapsed #sidebar .sidebar-dropup-trigger .sidebar-text, + #sidebar.sidebar-collapsed .sidebar-dropup-trigger .sidebar-text { + display: none; + } +body.sidebar-collapsed #sidebar .sidebar-dropup-trigger__chevron { + display: none; + } +body.sidebar-collapsed #sidebar .sidebar-dropup-trigger__glyph, + #sidebar.sidebar-collapsed .sidebar-dropup-trigger__glyph { + margin: 0; + } +body.sidebar-collapsed #sidebar .sidebar-nav-link:hover, + #sidebar.sidebar-collapsed .sidebar-nav-link:hover, + body.sidebar-collapsed #sidebar .sidebar-nav-link:focus-visible, + #sidebar.sidebar-collapsed .sidebar-nav-link:focus-visible { + --sidebar-nav-translate-x: 0; + } +@media (min-width: 768px) { + #sidebar { + transform: translateX(0) !important; + width: var(--sidebar-width); + overflow: visible; + } + + #app[data-sidebar-state="collapsed"] { + margin-left: calc( + var(--sidebar-width-collapsed) + var(--sidebar-content-gap) + ); + width: calc( + 100vw - var(--sidebar-width-collapsed) - var(--sidebar-content-gap) + ); + max-width: calc( + 100vw - var(--sidebar-width-collapsed) - var(--sidebar-content-gap) + ); + } + + #app[data-sidebar-state="expanded"] { + margin-left: max( + calc(var(--sidebar-width-expanded-base) + var(--sidebar-content-gap)), + calc(var(--sidebar-dropup-content-width) + var(--sidebar-content-gap)) + ); + width: min( + calc( + 100vw - var(--sidebar-width-expanded-base) - + var(--sidebar-content-gap) + ), + calc( + 100vw - var(--sidebar-dropup-content-width) - + var(--sidebar-content-gap) + ) + ); + max-width: min( + calc( + 100vw - var(--sidebar-width-expanded-base) - + var(--sidebar-content-gap) + ), + calc( + 100vw - var(--sidebar-dropup-content-width) - + var(--sidebar-content-gap) + ) + ); + } + } +@keyframes card-motion-enter { + from { + opacity: 0; + transform: translateY(var(--motion-card-enter-translate)); + } + + to { + opacity: 1; + transform: translateY(0); + } + } +@keyframes video-thumbnail-fade-in { + from { + opacity: 0; + } + + to { + opacity: 1; + } + } +@keyframes interface-fade-in { + from { + opacity: 0; + } + + to { + opacity: 1; + } + } +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(var(--motion-fade-in-translate)); + } + + to { + opacity: 1; + transform: translateY(0); + } + } +@keyframes spin { + to { + transform: rotate(360deg); + } + } +.status-line { + margin-top: 0.25rem; + margin-bottom: 0.25rem; + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); + line-height: 1.375; + color: var(--text-primary); +} +/* Status log containers reserve space for dynamic messages. */ +.status-line.error { + color: var(--color-critical-strong); + } +.status-line.\!error { + color: var(--color-critical-strong) !important; + } +.status-line.success { + } +.status-line.\!success { + } +.status-line.warn { + color: var(--color-warning-strong); + } +/* Blog layout root scaffolding */ +@keyframes ztr-rotate { + 100% { + transform: rotate(360deg); + } + } +@keyframes ztr-dash { + 0% { + stroke-dasharray: 1, 150; + stroke-dashoffset: 0; + } + + 50% { + stroke-dasharray: 90, 150; + stroke-dashoffset: -35; + } + + 100% { + stroke-dasharray: 90, 150; + stroke-dashoffset: -124; + } + } +@keyframes svelte-181fgen-loader-animation { + 0% { + transform: rotate(0); + } + + to { + transform: rotate(360deg); + } + } +@keyframes svelte-181fgen-fade-in { + to { + opacity: 1; + } + } +.dm-app-shell { + display: flex; + flex-direction: column; + gap: var(--space-lg); + padding: var(--space-lg); + background: var(--surface-base); + color: var(--text-primary); + min-height: 100vh; + } +.profile-modal .dm-app-shell { + min-height: 0; + height: auto; + padding: 0; + gap: var(--space-md); + } +.dm-app-shell__sidebar, + .dm-app-shell__main { + border-radius: var(--radius-lg); + border: var(--border-width-hairline) solid var(--border-default); + background: var(--surface-panel); + box-shadow: var(--shadow-soft); + } +.profile-modal .dm-app-shell__sidebar, + .profile-modal .dm-app-shell__main { + height: auto; + min-height: 0; + border: none; + box-shadow: none; + background: transparent; + padding: 0; + } +.dm-app-shell__sidebar { + display: flex; + flex-direction: column; + gap: var(--space-md); + padding: var(--space-md); + } +.dm-app-shell__title { + font-size: var(--font-size-lg); + margin: 0; + } +.dm-app-shell__main { + flex: 1; + display: flex; + flex-direction: column; + gap: var(--space-md); + padding: var(--space-md); + background: var(--surface-raised); + } +.profile-modal .dm-app-shell__main, + .profile-modal .dm-app-shell__sidebar { + min-width: 0; + } +.dm-conversation-list { + display: flex; + flex-direction: column; + gap: var(--space-sm); + } +.dm-conversation-list__header { + display: flex; + justify-content: space-between; + align-items: center; + } +.dm-conversation-list__items { + display: flex; + flex-direction: column; + gap: var(--space-xs); + overflow-y: auto; + max-height: 60vh; + padding-right: var(--space-xs); + } +.dm-conversation-list__state { + font-size: var(--font-size-sm); + color: var(--text-muted); + padding: var(--space-md); + text-align: center; + background: var(--surface-sunken); + border-radius: var(--radius-md); + } +.dm-contact-row { + display: flex; + align-items: center; + gap: var(--space-sm); + padding: var(--space-sm) var(--space-md); + border-radius: var(--radius-md); + border: var(--border-width-hairline) solid var(--border-subtle); + background: transparent; + color: inherit; + text-align: left; + cursor: pointer; + } +.dm-contact-row:hover, + .dm-contact-row:focus-visible { + background: var(--surface-muted); + border-color: var(--border-strong); + outline: none; + } +.dm-contact-row--active { + background: var(--surface-sunken); + border-color: var(--border-strong); + } +.dm-contact-row__content { + display: flex; + flex-direction: column; + gap: var(--space-2xs); + flex: 1; + } +.dm-contact-row__header { + display: flex; + justify-content: space-between; + align-items: baseline; + gap: var(--space-xs); + } +.dm-contact-row__meta { + display: inline-flex; + align-items: center; + gap: var(--space-xs); + } +.dm-contact-row__name { + font-weight: 600; + } +.dm-contact-row__badge { + align-self: flex-start; + padding: var(--space-3xs) var(--space-xs); + border-radius: var(--radius-full); + background: var(--color-accent); + color: var(--color-white); + font-size: var(--font-size-3xs); + } +.dm-avatar { + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + border-radius: var(--radius-full); + background: var(--surface-sunken); + color: var(--text-muted); + font-weight: 600; + overflow: hidden; + border: var(--border-width-hairline) solid var(--border-subtle); + } +.dm-avatar--md { + width: var(--space-xl-plus); + height: var(--space-xl-plus); + min-width: var(--space-xl-plus); + min-height: var(--space-xl-plus); + } +.dm-avatar--sm { + width: var(--space-xl); + height: var(--space-xl); + min-width: var(--space-xl); + min-height: var(--space-xl); + } +.dm-avatar__image { + width: 100%; + height: 100%; + -o-object-fit: cover; + object-fit: cover; + } +.dm-message-thread { + display: flex; + flex-direction: column; + gap: var(--space-sm); + flex: 1; + min-height: 0; + } +.dm-message-thread__header { + display: flex; + align-items: baseline; + gap: var(--space-sm); + } +.dm-message-thread__title { + font-size: var(--font-size-base); + margin: 0; + } +.dm-message-thread__status { + font-size: var(--font-size-xs); + color: var(--text-muted); + } +.dm-message-thread__zap-summary { + display: inline-flex; + align-items: center; + gap: var(--space-xs); + padding: var(--space-2xs) var(--space-xs); + border-radius: var(--radius-full); + border: var(--border-width-hairline) solid var(--border-subtle); + background: var(--surface-sunken); + color: var(--text-muted); + font-size: var(--font-size-2xs); + } +.dm-message-thread__zap-label { + text-transform: uppercase; + letter-spacing: var(--tracking-wide); + } +.dm-message-thread__zap-total { + font-weight: 600; + color: var(--text-primary); + } +.dm-message-thread__zap-profile { + color: var(--text-muted); + } +.dm-message-thread__timeline { + display: flex; + flex-direction: column; + gap: var(--space-sm); + padding: var(--space-sm); + border-radius: var(--radius-md); + background: var(--surface-panel); + border: var(--border-width-hairline) solid var(--border-subtle); + overflow-y: auto; + min-height: var(--size-16rem); + } +.dm-message-thread__state { + padding: var(--space-md); + text-align: center; + color: var(--text-muted); + background: var(--surface-sunken); + border-radius: var(--radius-md); + } +.dm-message-row { + display: flex; + align-items: flex-end; + gap: var(--space-xs); + width: 100%; + } +.dm-message-avatar { + width: var(--space-xl); + height: var(--space-xl); + flex-shrink: 0; + border-radius: var(--radius-full); + -o-object-fit: cover; + object-fit: cover; + background-color: var(--surface-sunken); + border: var(--border-width-hairline) solid var(--border-subtle); + } +.dm-message-bubble { + max-width: 75%; + display: flex; + flex-direction: column; + gap: var(--space-2xs); + padding: var(--space-sm) var(--space-md); + border-radius: var(--radius-lg); + border: var(--border-width-hairline) solid var(--message-bubble-border); + background: var(--message-bubble-background); + color: var(--message-bubble-text); + } +.dm-message-bubble--outgoing .dm-message-bubble__meta, + .dm-message-bubble--outgoing .dm-message-bubble__status { + color: var(--color-white); + opacity: 0.9; + } +.dm-message-bubble__content { + font-size: var(--font-size-sm); + overflow-wrap: anywhere; + white-space: pre-wrap; + } +.dm-message-bubble__meta { + display: flex; + gap: var(--space-xs); + font-size: var(--font-size-2xs); + color: var(--message-bubble-meta); + } +.dm-message-bubble__status { + color: var(--text-muted); + } +.dm-message-bubble__status[data-status="pending"] { + color: var(--status-warning); + } +.dm-message-bubble__status[data-status="sending"] { + color: var(--status-info); + } +.dm-message-bubble__status[data-status="failed"] { + color: var(--status-danger); + } +.dm-message-thread__new { + align-self: center; + border-radius: var(--radius-full); + border: var(--border-width-hairline) solid var(--border-subtle); + padding: var(--space-2xs) var(--space-sm); + font-size: var(--font-size-2xs); + background: var(--surface-sunken); + color: var(--text-muted); + } +.dm-message-thread__new:hover, + .dm-message-thread__new:focus-visible { + background: var(--surface-overlay-panel-soft); + color: var(--text-primary); + outline: none; + } +.dm-day-divider { + display: grid; + grid-template-columns: 1fr auto 1fr; + align-items: center; + gap: var(--space-xs); + color: var(--text-muted); + font-size: var(--font-size-2xs); + } +.dm-day-divider__line { + height: var(--border-width-hairline); + background: var(--border-subtle); + } +.dm-notification-center { + display: flex; + flex-direction: column; + gap: var(--space-sm); + } +.dm-notification-center__filters { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--space-xs); + } +.dm-notification-center__filter-button { + border-radius: var(--radius-full); + border: var(--border-width-hairline) solid var(--border-subtle); + background: var(--surface-sunken); + padding: var(--space-2xs) var(--space-sm); + font-size: var(--font-size-2xs); + font-weight: 600; + color: var(--text-muted); + transition: + color var(--motion-duration-fast) var(--motion-ease-out), + background var(--motion-duration-fast) var(--motion-ease-out), + border-color var(--motion-duration-fast) var(--motion-ease-out); + } +.dm-notification-center__filter-button:hover, + .dm-notification-center__filter-button:focus-visible { + outline: none; + border-color: var(--border-strong); + background: var(--surface-raised); + color: var(--text-primary); + } +.dm-notification-center__filter-button--active { + border-color: var(--border-strong); + background: var(--surface-overlay-panel); + color: var(--text-primary); + } +.dm-notification-center__groups { + display: flex; + flex-direction: column; + gap: var(--space-md); + } +.dm-notification-center__group { + display: flex; + flex-direction: column; + gap: var(--space-xs); + } +.dm-notification-center__group-title { + font-size: var(--font-size-2xs); + font-weight: 600; + text-transform: uppercase; + letter-spacing: var(--tracking-wide); + color: var(--text-muted); + } +.dm-notification-center__list { + display: flex; + flex-direction: column; + gap: var(--space-xs); + } +.dm-notification-center__empty { + font-size: var(--font-size-xs); + color: var(--text-muted); + } +.dm-notification-item { + display: grid; + grid-template-columns: auto 1fr auto; + align-items: center; + gap: var(--space-sm); + padding: var(--space-sm); + border-radius: var(--radius-md); + border: var(--border-width-hairline) solid var(--border-subtle); + background: var(--surface-sunken); + font-size: var(--font-size-xs); + text-align: left; + transition: + border-color var(--motion-duration-fast) var(--motion-ease-out), + background var(--motion-duration-fast) var(--motion-ease-out); + } +.dm-notification-item:hover, + .dm-notification-item:focus-visible { + outline: none; + border-color: var(--border-strong); + background: var(--surface-raised); + } +.dm-notification-item__icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: var(--icon-size-md); + height: var(--icon-size-md); + border-radius: var(--radius-full); + background: var(--surface-overlay-panel-soft); + color: var(--text-muted); + font-size: var(--font-size-xs); + } +.dm-notification-item__body { + display: flex; + flex-direction: column; + gap: var(--space-2xs); + min-width: 0; + } +.dm-notification-item__title { + font-weight: 600; + color: var(--text-primary); + } +.dm-notification-item__message { + color: var(--text-muted); + } +.dm-notification-item__meta { + display: flex; + align-items: center; + gap: var(--space-2xs); + color: var(--text-muted); + } +.dm-notification-item__timestamp { + font-variant-numeric: tabular-nums; + white-space: nowrap; + } +.dm-composer { + display: flex; + flex-direction: column; + gap: var(--space-xs); + } +.dm-composer__label { + font-size: var(--font-size-xs); + color: var(--text-muted); + } +.dm-composer__input { + resize: vertical; + border-radius: var(--radius-md); + border: var(--border-width-hairline) solid var(--border-default); + background: var(--surface-base); + color: var(--text-primary); + padding: var(--space-sm); + font-family: inherit; + } +.dm-composer__input:focus-visible { + outline: var(--space-3xs) solid var(--color-accent); + outline-offset: var(--space-3xs); + } +.dm-composer__actions { + display: flex; + justify-content: space-between; + align-items: center; + gap: var(--space-sm); + } +.dm-composer__tools { + display: flex; + align-items: center; + gap: var(--space-sm); + flex-wrap: wrap; + position: relative; + } +.dm-composer__hint { + font-size: var(--font-size-2xs); + color: var(--text-muted); + } +.dm-composer__attach, + .dm-composer__more-btn { + border-radius: var(--radius-md); + border: var(--border-width-hairline) solid var(--border-default); + background: var(--surface-base); + color: var(--text-muted); + padding: var(--space-2xs) var(--space-sm); + font-size: var(--font-size-2xs); + cursor: pointer; + } +.dm-composer__more-btn { + font-weight: 700; + line-height: 1; + min-width: var(--space-xl); + } +.dm-composer__more-menu { + position: absolute; + bottom: 100%; + left: 0; + margin-bottom: var(--space-xs); + background: var(--surface-overlay-panel); + border: var(--border-width-hairline) solid var(--border-default); + border-radius: var(--radius-md); + box-shadow: var(--shadow-popover); + z-index: 10; + display: flex; + flex-direction: column; + min-width: var(--size-8rem); + padding: var(--space-xs); + } +.dm-composer__menu-item { + text-align: left; + padding: var(--space-xs) var(--space-sm); + border-radius: var(--radius-sm); + font-size: var(--font-size-xs); + color: var(--text-muted); + background: transparent; + border: none; + cursor: pointer; + transition: background var(--motion-duration-fast) ease-out; + } +.dm-composer__menu-item:hover, + .dm-composer__menu-item:focus-visible { + background: var(--surface-overlay-panel-soft); + color: var(--text-primary); + outline: none; + } +.dm-composer__menu-item[aria-pressed="true"] { + background: var(--color-accent); + color: var(--color-white); + } +.dm-composer__attach:hover, + .dm-composer__attach:focus-visible, + .dm-composer__more-btn:hover, + .dm-composer__more-btn:focus-visible { + background: var(--surface-overlay-panel-soft); + color: var(--text-primary); + outline: none; + } +.dm-composer__send { + border-radius: var(--radius-md); + border: var(--border-width-hairline) solid var(--border-strong); + background: var(--surface-overlay-panel); + color: var(--text-primary); + padding: var(--space-xs) var(--space-md); + font-weight: 600; + cursor: pointer; + } +.dm-composer__send:hover, + .dm-composer__send:focus-visible { + background: var(--surface-overlay-panel-soft); + outline: none; + } +.dm-composer__status { + font-size: var(--font-size-2xs); + color: var(--status-danger); + min-height: var(--space-md); + } +@media (max-width: calc(768px - 0.02px)) { + .dm-app-shell[data-dm-view="list"] .dm-app-shell__main { + display: none !important; + } + + .dm-app-shell[data-dm-view="thread"] .dm-app-shell__sidebar { + display: none !important; + } + + .dm-message-thread__back { + display: inline-flex; + align-items: center; + gap: 0.5rem; + } + } +@media (min-width: 768px) { + .dm-app-shell { + flex-direction: row; + align-items: stretch; + } + + .dm-app-shell__sidebar { + flex: 0 0 var(--size-18rem); + max-width: var(--size-20rem); + height: calc(100vh - var(--space-lg) * 2); + display: flex !important; + } + + .dm-app-shell__main { + height: calc(100vh - var(--space-lg) * 2); + display: flex !important; + } + + .dm-conversation-list__items { + max-height: none; + flex: 1; + } + + .dm-message-thread__back { + display: none !important; + } + } +/* Ensure the profile pane for messages takes full height */ +#profilePaneMessages { + height: 100%; + min-height: 0; + } +@media (min-width: 768px) { + .profile-modal .dm-app-shell { + flex-direction: row; + align-items: stretch; + height: 100%; + min-height: 0; + } + + .profile-modal .dm-app-shell__sidebar { + flex: 0 0 var(--size-18rem); + max-width: var(--size-20rem); + height: 100%; + overflow: hidden; + display: flex; + flex-direction: column; + } + + .profile-modal .dm-app-shell__main { + flex: 1 1 auto; + height: 100%; + overflow: hidden; + display: flex; + flex-direction: column; + } + + .profile-modal .dm-conversation-list { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + } + + .profile-modal .dm-conversation-list__items { + flex: 1; + overflow-y: auto; + min-height: 0; + overscroll-behavior-y: contain; + scrollbar-width: none; + -ms-overflow-style: none; + } +.profile-modal .dm-conversation-list__items::-webkit-scrollbar { + display: none; +} + + .profile-modal .dm-message-thread { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + } + + .profile-modal .dm-message-thread__timeline { + flex: 1; + overflow-y: auto; + min-height: 0; + overscroll-behavior-y: contain; + scrollbar-width: none; + -ms-overflow-style: none; + } +.profile-modal .dm-message-thread__timeline::-webkit-scrollbar { + display: none; +} + } +@media (min-width: 1024px) { + .dm-app-shell__sidebar { + flex-basis: var(--size-20rem); + } + + .dm-message-thread__timeline { + padding: var(--space-md); + } + } +.marquee-host { + position: relative; + overflow: hidden; + white-space: nowrap; + width: 100%; + min-width: 0; + max-width: 100%; + } +.marquee-static { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + opacity: 1; + transition: opacity 0.3s ease; +} +.marquee-anim { + position: absolute; + inset: 0; + display: flex; + align-items: center; + opacity: 0; + transition: opacity 0.3s ease; + pointer-events: none; + mask-image: linear-gradient( + to right, + transparent, + black 4%, + black 96%, + transparent + ); + -webkit-mask-image: linear-gradient( + to right, + transparent, + black 4%, + black 96%, + transparent + ); + } +.marquee-track { + display: flex; + width: -moz-max-content; + width: max-content; + animation: marquee-scroll linear infinite; + animation-duration: var(--marquee-duration, 10s); + animation-play-state: paused; + } +.marquee-item { + flex-shrink: 0; + padding-right: var(--space-xl); /* Gap between loop items */ + } +@keyframes marquee-scroll { + 0% { + transform: translateX(0); + } + 100% { + transform: translateX(-50%); + } + } +/* Desktop Hover State */ +@media (min-width: 1024px) { + .\!card:hover .marquee-host.can-marquee .marquee-static { + opacity: 0 !important; + } + .\!card:hover .marquee-host.can-marquee .marquee-anim { + opacity: 1 !important; + } + .\!card:hover .marquee-host.can-marquee .marquee-track { + animation-play-state: running !important; + } + .card:hover .marquee-host.can-marquee .marquee-static { + opacity: 0; + } + .card:hover .marquee-host.can-marquee .marquee-anim { + opacity: 1; + } + .card:hover .marquee-host.can-marquee .marquee-track { + animation-play-state: running; + } + .\!card:hover .marquee-host.can-marquee .marquee-static { + opacity: 0 !important; + } + .\!card:hover .marquee-host.can-marquee .marquee-anim { + opacity: 1 !important; + } + .\!card:hover .marquee-host.can-marquee .marquee-track { + animation-play-state: running !important; + } + .\!card:hover .marquee-host.can-marquee .marquee-static { + opacity: 0 !important; + } + .\!card:hover .marquee-host.can-marquee .marquee-anim { + opacity: 1 !important; + } + .\!card:hover .marquee-host.can-marquee .marquee-track { + animation-play-state: running !important; + } + } +/* Mobile Active State */ +@media (max-width: calc(1024px - var(--breakpoint-edge-offset))) { + .\!card[data-mobile-active="true"] { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate) !important; + --video-card-backdrop-opacity: 0.425 !important; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ) !important; + --video-card-backdrop-scale: 1.12 !important; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong) !important; + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } + + .\!card[data-mobile-active="true"] .video-card__title { + color: var(--color-accent) !important; + } + + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-static { + opacity: 0 !important; + } + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-anim { + opacity: 1 !important; + } + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-track { + animation-play-state: running !important; + } + .card[data-mobile-active="true"] { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate); + --video-card-backdrop-opacity: 0.425; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ); + --video-card-backdrop-scale: 1.12; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong); + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } + + .card[data-mobile-active="true"] .video-card__title { + color: var(--color-accent); + } + + .card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-static { + opacity: 0; + } + .card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-anim { + opacity: 1; + } + .card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-track { + animation-play-state: running; + } + .\!card[data-mobile-active="true"] { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate) !important; + --video-card-backdrop-opacity: 0.425 !important; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ) !important; + --video-card-backdrop-scale: 1.12 !important; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong) !important; + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } + + .\!card[data-mobile-active="true"] .video-card__title { + color: var(--color-accent) !important; + } + + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-static { + opacity: 0 !important; + } + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-anim { + opacity: 1 !important; + } + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-track { + animation-play-state: running !important; + } + .\!card[data-mobile-active="true"] { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate) !important; + --video-card-backdrop-opacity: 0.425 !important; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ) !important; + --video-card-backdrop-scale: 1.12 !important; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong) !important; + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } + + .\!card[data-mobile-active="true"] .video-card__title { + color: var(--color-accent) !important; + } + + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-static { + opacity: 0 !important; + } + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-anim { + opacity: 1 !important; + } + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-track { + animation-play-state: running !important; + } + .\!card[data-mobile-active="true"] { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate) !important; + --video-card-backdrop-opacity: 0.425 !important; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ) !important; + --video-card-backdrop-scale: 1.12 !important; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong) !important; + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } + + .\!card[data-mobile-active="true"] .video-card__title { + color: var(--color-accent) !important; + } + + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-static { + opacity: 0 !important; + } + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-anim { + opacity: 1 !important; + } + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-track { + animation-play-state: running !important; + } + .\!card[data-mobile-active="true"] { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate) !important; + --video-card-backdrop-opacity: 0.425 !important; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ) !important; + --video-card-backdrop-scale: 1.12 !important; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong) !important; + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } + + .\!card[data-mobile-active="true"] .video-card__title { + color: var(--color-accent) !important; + } + + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-static { + opacity: 0 !important; + } + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-anim { + opacity: 1 !important; + } + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-track { + animation-play-state: running !important; + } + .\!card[data-mobile-active="true"] { + --video-card-lift-translate-y: var(--motion-video-card-hover-translate) !important; + --video-card-backdrop-opacity: 0.425 !important; + --video-card-backdrop-translate-y: var( + --motion-video-card-backdrop-translate + ) !important; + --video-card-backdrop-scale: 1.12 !important; + --video-card-backdrop-blur: var(--blur-video-card-backdrop-strong) !important; + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } + + .\!card[data-mobile-active="true"] .video-card__title { + color: var(--color-accent) !important; + } + + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-static { + opacity: 0 !important; + } + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-anim { + opacity: 1 !important; + } + .\!card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-track { + animation-play-state: running !important; + } + } +/* Logo accent wiring */ +.bv-logo__accent { + fill: var(--bitvid-accent, var(--logo-accent-color)); + } +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} +.pointer-events-none { + pointer-events: none; +} +.pointer-events-auto { + pointer-events: auto; +} +.\!visible { + visibility: visible !important; +} +.visible { + visibility: visible; +} +.collapse { + visibility: collapse; +} +.static { + position: static; +} +.fixed { + position: fixed; +} +.absolute { + position: absolute; +} +.relative { + position: relative; +} +.sticky { + position: sticky; +} +.inset-0 { + inset: 0px; +} +.inset-x-0 { + left: 0px; + right: 0px; +} +.bottom-16 { + bottom: 4rem; +} +.bottom-4 { + bottom: 1rem; +} +.bottom-6 { + bottom: 1.5rem; +} +.left-1\/2 { + left: 50%; +} +.left-4 { + left: 1rem; +} +.right-2 { + right: 0.5rem; +} +.right-3 { + right: 0.75rem; +} +.right-4 { + right: 1rem; +} +.right-6 { + right: 1.5rem; +} +.top-0 { + top: 0px; +} +.top-1\/2 { + top: 50%; +} +.top-12 { + top: 3rem; +} +.top-2 { + top: 0.5rem; +} +.top-3 { + top: 0.75rem; +} +.top-4 { + top: 1rem; +} +.z-10 { + z-index: 10; +} +.z-20 { + z-index: 20; +} +.z-50 { + z-index: 50; +} +.z-overlay-floating { + z-index: var(--z-overlay-floating); +} +.z-overlay-toast { + z-index: var(--z-overlay-toast); +} +.order-1 { + order: 1; +} +.order-2 { + order: 2; +} +.col-span-full { + grid-column: 1 / -1; +} +.mx-1 { + margin-left: 0.25rem; + margin-right: 0.25rem; +} +.mx-auto { + margin-left: auto; + margin-right: auto; +} +.my-4 { + margin-top: 1rem; + margin-bottom: 1rem; +} +.my-6 { + margin-top: 1.5rem; + margin-bottom: 1.5rem; +} +.-ml-1 { + margin-left: -0.25rem; +} +.-ml-2 { + margin-left: -0.5rem; +} +.mb-1 { + margin-bottom: 0.25rem; +} +.mb-2 { + margin-bottom: 0.5rem; +} +.mb-3 { + margin-bottom: 0.75rem; +} +.mb-4 { + margin-bottom: 1rem; +} +.mb-6 { + margin-bottom: 1.5rem; +} +.mb-8 { + margin-bottom: 2rem; +} +.ml-1 { + margin-left: 0.25rem; +} +.ml-2 { + margin-left: 0.5rem; +} +.ml-4 { + margin-left: 1rem; +} +.ml-auto { + margin-left: auto; +} +.ml-md { + margin-left: var(--space-md); +} +.mt-0\.5 { + margin-top: 0.125rem; +} +.mt-1 { + margin-top: 0.25rem; +} +.mt-1\.5 { + margin-top: 0.375rem; +} +.mt-2 { + margin-top: 0.5rem; +} +.mt-3 { + margin-top: 0.75rem; +} +.mt-4 { + margin-top: 1rem; +} +.mt-6 { + margin-top: 1.5rem; +} +.mt-8 { + margin-top: 2rem; +} +.mt-auto { + margin-top: auto; +} +.mt-sm { + margin-top: var(--space-sm); +} +.line-clamp-2 { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} +.block { + display: block; +} +.inline-block { + display: inline-block; +} +.inline { + display: inline; +} +.flex { + display: flex; +} +.inline-flex { + display: inline-flex; +} +.table { + display: table; +} +.grid { + display: grid; +} +.contents { + display: contents; +} +.hidden { + display: none; +} +.aspect-square { + aspect-ratio: 1 / 1; +} +.aspect-video { + aspect-ratio: 16 / 9; +} +.h-1 { + height: 0.25rem; +} +.h-1\.5 { + height: 0.375rem; +} +.h-10 { + height: 2.5rem; +} +.h-12 { + height: 3rem; +} +.h-14 { + height: 3.5rem; +} +.h-16 { + height: 4rem; +} +.h-24 { + height: 6rem; +} +.h-2xl { + height: var(--space-2xl); +} +.h-3 { + height: 0.75rem; +} +.h-3\.5 { + height: 0.875rem; +} +.h-32 { + height: 8rem; +} +.h-4 { + height: 1rem; +} +.h-4\/5 { + height: 80%; +} +.h-40 { + height: 10rem; +} +.h-48 { + height: 12rem; +} +.h-5 { + height: 1.25rem; +} +.h-6 { + height: 1.5rem; +} +.h-8 { + height: 2rem; +} +.h-9 { + height: 2.25rem; +} +.h-\[80vh\] { + height: 80vh; +} +.h-auto { + height: auto; +} +.h-full { + height: 100%; +} +.h-px { + height: 1px; +} +.h-screen { + height: 100vh; +} +.max-h-\[90vh\] { + max-height: 90vh; +} +.max-h-\[980px\] { + max-height: 980px; +} +.max-h-modal-body { + max-height: var(--modal-body-max-height); +} +.max-h-modal-pane { + max-height: var(--modal-pane-max-height); +} +.max-h-modal-sheet { + max-height: var(--modal-sheet-max-height); +} +.max-h-modal-shell { + max-height: var(--modal-shell-max-height); +} +.min-h-0 { + min-height: 0px; +} +.min-h-\[200px\] { + min-height: 200px; +} +.min-h-\[var\(--size-80px\)\] { + min-height: var(--size-80px); +} +.min-h-screen { + min-height: 100vh; +} +.w-10 { + width: 2.5rem; +} +.w-12 { + width: 3rem; +} +.w-14 { + width: 3.5rem; +} +.w-16 { + width: 4rem; +} +.w-20 { + width: 5rem; +} +.w-24 { + width: 6rem; +} +.w-28 { + width: 7rem; +} +.w-3 { + width: 0.75rem; +} +.w-3\.5 { + width: 0.875rem; +} +.w-32 { + width: 8rem; +} +.w-4 { + width: 1rem; +} +.w-44 { + width: 11rem; +} +.w-48 { + width: 12rem; +} +.w-5 { + width: 1.25rem; +} +.w-56 { + width: 14rem; +} +.w-6 { + width: 1.5rem; +} +.w-72 { + width: 18rem; +} +.w-8 { + width: 2rem; +} +.w-80 { + width: 20rem; +} +.w-9 { + width: 2.25rem; +} +.w-auto { + width: auto; +} +.w-full { + width: 100%; +} +.w-screen { + width: 100vw; +} +.min-w-0 { + min-width: 0px; +} +.min-w-full { + min-width: 100%; +} +.max-w-2xl { + max-width: 42rem; +} +.max-w-3xl { + max-width: 48rem; +} +.max-w-4xl { + max-width: 56rem; +} +.max-w-5xl { + max-width: 64rem; +} +.max-w-7xl { + max-width: 80rem; +} +.max-w-\[200px\] { + max-width: 200px; +} +.max-w-docs { + max-width: var(--layout-docs-max-width); +} +.max-w-lg { + max-width: 32rem; +} +.max-w-md { + max-width: 28rem; +} +.max-w-none { + max-width: none; +} +.max-w-popover-safe { + max-width: var(--popover-inline-safe-max); +} +.max-w-sm { + max-width: 24rem; +} +.max-w-xs { + max-width: 20rem; +} +.flex-1 { + flex: 1 1 0%; +} +.flex-shrink-0 { + flex-shrink: 0; +} +.shrink-0 { + flex-shrink: 0; +} +.flex-grow { + flex-grow: 1; +} +.grow { + flex-grow: 1; +} +.origin-top { + transform-origin: top; +} +.-translate-x-1\/2 { + --tw-translate-x: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.-translate-y-1\/2 { + --tw-translate-y: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.rotate-0 { + --tw-rotate: 0deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.rotate-180 { + --tw-rotate: 180deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.rotate-90 { + --tw-rotate: 90deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.scale-\[1\.2\] { + --tw-scale-x: 1.2; + --tw-scale-y: 1.2; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +@keyframes pulse { + 50% { + opacity: .5; + } +} +.animate-pulse { + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} +@keyframes spin { + to { + transform: rotate(360deg); + } +} +.animate-spin { + animation: spin 1s linear infinite; +} +.cursor-not-allowed { + cursor: not-allowed; +} +.cursor-pointer { + cursor: pointer; +} +.cursor-wait { + cursor: wait; +} +.select-none { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} +.select-all { + -webkit-user-select: all; + -moz-user-select: all; + user-select: all; +} +.resize-y { + resize: vertical; +} +.resize { + resize: both; +} +.list-disc { + list-style-type: disc; +} +.auto-rows-fr { + grid-auto-rows: minmax(0, 1fr); +} +.grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); +} +.grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} +.grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} +.flex-row { + flex-direction: row; +} +.flex-col { + flex-direction: column; +} +.flex-wrap { + flex-wrap: wrap; +} +.items-start { + align-items: flex-start; +} +.items-end { + align-items: flex-end; +} +.items-center { + align-items: center; +} +.items-stretch { + align-items: stretch; +} +.justify-start { + justify-content: flex-start; +} +.justify-end { + justify-content: flex-end; +} +.justify-center { + justify-content: center; +} +.justify-between { + justify-content: space-between; +} +.gap-1 { + gap: 0.25rem; +} +.gap-1\.5 { + gap: 0.375rem; +} +.gap-12 { + gap: 3rem; +} +.gap-2 { + gap: 0.5rem; +} +.gap-3 { + gap: 0.75rem; +} +.gap-4 { + gap: 1rem; +} +.gap-6 { + gap: 1.5rem; +} +.gap-8 { + gap: 2rem; +} +.gap-lg { + gap: var(--space-lg); +} +.gap-sm { + gap: var(--space-sm); +} +.gap-xl { + gap: var(--space-xl); +} +.gap-xs { + gap: var(--space-xs); +} +.gap-x-2 { + -moz-column-gap: 0.5rem; + column-gap: 0.5rem; +} +.gap-x-3 { + -moz-column-gap: 0.75rem; + column-gap: 0.75rem; +} +.gap-y-1 { + row-gap: 0.25rem; +} +.gap-y-6 { + row-gap: 1.5rem; +} +.gap-y-8 { + row-gap: 2rem; +} +.space-x-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); +} +.space-x-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.75rem * var(--tw-space-x-reverse)); + margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); +} +.space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); +} +.space-y-1 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); +} +.space-y-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} +.space-y-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.75rem * var(--tw-space-y-reverse)); +} +.space-y-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); +} +.space-y-5 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.25rem * var(--tw-space-y-reverse)); +} +.space-y-6 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); +} +.space-y-8 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(2rem * var(--tw-space-y-reverse)); +} +.divide-y > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); +} +.divide-border-translucent > :not([hidden]) ~ :not([hidden]) { + border-color: var(--border-overlay); +} +.self-start { + align-self: flex-start; +} +.overflow-auto { + overflow: auto; +} +.overflow-hidden { + overflow: hidden; +} +.overflow-x-auto { + overflow-x: auto; +} +.overflow-y-auto { + overflow-y: auto; +} +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.whitespace-nowrap { + white-space: nowrap; +} +.whitespace-pre-line { + white-space: pre-line; +} +.whitespace-pre-wrap { + white-space: pre-wrap; +} +.break-words { + overflow-wrap: break-word; +} +.break-all { + word-break: break-all; +} +.rounded { + border-radius: 0.25rem; +} +.rounded-2xl { + border-radius: 1rem; +} +.rounded-3xl { + border-radius: 1.5rem; +} +.rounded-full { + border-radius: var(--radius-full); +} +.rounded-lg { + border-radius: var(--radius-lg); +} +.rounded-md { + border-radius: var(--radius-md); +} +.rounded-none { + border-radius: 0px; +} +.rounded-xl { + border-radius: var(--radius-xl); +} +.rounded-r-md { + border-top-right-radius: var(--radius-md); + border-bottom-right-radius: var(--radius-md); +} +.rounded-t-lg { + border-top-left-radius: var(--radius-lg); + border-top-right-radius: var(--radius-lg); +} +.border { + border-width: 1px; +} +.border-0 { + border-width: 0px; +} +.border-2 { + border-width: 2px; +} +.border-b { + border-bottom-width: 1px; +} +.border-l { + border-left-width: 1px; +} +.border-l-2 { + border-left-width: 2px; +} +.border-l-4 { + border-left-width: 4px; +} +.border-r { + border-right-width: 1px; +} +.border-t { + border-top-width: 1px; +} +.border-dashed { + border-style: dashed; +} +.border-none { + border-style: none; +} +.border-accent { + border-color: var(--color-accent); +} +.border-border { + border-color: var(--border-default); +} +.border-border-strong { + border-color: var(--border-strong); +} +.border-border-subtle { + border-color: var(--border-subtle); +} +.border-border-translucent { + border-color: var(--border-overlay); +} +.border-border-translucent-bright { + border-color: var(--color-border-translucent-bright); +} +.border-border-translucent-medium { + border-color: var(--color-border-translucent-medium); +} +.border-overlay { + border-color: var(--border-overlay); +} +.border-overlay-strong { + border-color: var(--border-overlay-strong); +} +.border-status-danger-border { + border-color: var(--status-danger-border); +} +.border-status-info { + border-color: var(--status-info); +} +.border-status-info-border { + border-color: var(--status-info-border); +} +.border-status-success-border { + border-color: var(--status-success-border); +} +.border-status-warning-border { + border-color: var(--status-warning-border); +} +.border-surface-strong { + border-color: var(--border-strong); +} +.border-transparent { + border-color: transparent; +} +.border-opacity-20 { + --tw-border-opacity: 0.2; +} +.border-opacity-40 { + --tw-border-opacity: 0.4; +} +.bg-accent-strong { + background-color: var(--color-accent-strong); +} +.bg-overlay-muted { + background-color: var(--surface-overlay-muted); +} +.bg-overlay-panel { + background-color: var(--surface-overlay-panel); +} +.bg-overlay-panel-soft { + background-color: var(--surface-overlay-panel-soft); +} +.bg-overlay-strong { + background-color: var(--surface-overlay-strong); +} +.bg-panel { + background-color: var(--surface-panel); +} +.bg-status-danger-surface { + background-color: var(--status-danger-surface); +} +.bg-status-info-surface { + background-color: var(--status-info-surface); +} +.bg-status-success-surface { + background-color: var(--status-success-surface); +} +.bg-status-warning-surface { + background-color: var(--status-warning-surface); +} +.bg-surface { + background-color: var(--surface-base); +} +.bg-surface-alt { + background-color: var(--surface-alt); +} +.bg-surface-muted { + background-color: var(--surface-muted); +} +.bg-transparent { + background-color: transparent; +} +.bg-white { + background-color: var(--color-white); +} +.bg-opacity-95 { + --tw-bg-opacity: 0.95; +} +.bg-gradient-to-b { + background-image: linear-gradient(to bottom, var(--tw-gradient-stops)); +} +.bg-overlay-channel { + background-image: var(--surface-overlay-banner-gradient-channel); +} +.via-transparent { + --tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), transparent var(--tw-gradient-via-position), var(--tw-gradient-to); +} +.to-transparent { + --tw-gradient-to: transparent var(--tw-gradient-to-position); +} +.stroke-border-translucent { + stroke: var(--border-overlay); +} +.stroke-current { + stroke: currentColor; +} +.object-cover { + -o-object-fit: cover; + object-fit: cover; +} +.p-0 { + padding: 0px; +} +.p-1 { + padding: 0.25rem; +} +.p-2 { + padding: 0.5rem; +} +.p-3 { + padding: 0.75rem; +} +.p-4 { + padding: 1rem; +} +.p-5 { + padding: 1.25rem; +} +.p-6 { + padding: 1.5rem; +} +.p-8 { + padding: 2rem; +} +.p-lg { + padding: var(--space-lg); +} +.p-md { + padding: var(--space-md); +} +.px-0 { + padding-left: 0px; + padding-right: 0px; +} +.px-1 { + padding-left: 0.25rem; + padding-right: 0.25rem; +} +.px-1\.5 { + padding-left: 0.375rem; + padding-right: 0.375rem; +} +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} +.px-5 { + padding-left: 1.25rem; + padding-right: 1.25rem; +} +.px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} +.px-lg { + padding-left: var(--space-lg); + padding-right: var(--space-lg); +} +.px-md { + padding-left: var(--space-md); + padding-right: var(--space-md); +} +.py-0 { + padding-top: 0px; + padding-bottom: 0px; +} +.py-0\.5 { + padding-top: 0.125rem; + padding-bottom: 0.125rem; +} +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} +.py-1\.5 { + padding-top: 0.375rem; + padding-bottom: 0.375rem; +} +.py-12 { + padding-top: 3rem; + padding-bottom: 3rem; +} +.py-16 { + padding-top: 4rem; + padding-bottom: 4rem; +} +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} +.py-5 { + padding-top: 1.25rem; + padding-bottom: 1.25rem; +} +.py-6 { + padding-top: 1.5rem; + padding-bottom: 1.5rem; +} +.py-8 { + padding-top: 2rem; + padding-bottom: 2rem; +} +.py-md { + padding-top: var(--space-md); + padding-bottom: var(--space-md); +} +.py-xl { + padding-top: var(--space-xl); + padding-bottom: var(--space-xl); +} +.pb-16 { + padding-bottom: 4rem; +} +.pb-2 { + padding-bottom: 0.5rem; +} +.pb-4 { + padding-bottom: 1rem; +} +.pb-6 { + padding-bottom: 1.5rem; +} +.pb-8 { + padding-bottom: 2rem; +} +.pl-2 { + padding-left: 0.5rem; +} +.pl-5 { + padding-left: 1.25rem; +} +.pl-6 { + padding-left: 1.5rem; +} +.pl-md { + padding-left: var(--space-md); +} +.pr-1 { + padding-right: 0.25rem; +} +.pt-1 { + padding-top: 0.25rem; +} +.pt-2 { + padding-top: 0.5rem; +} +.pt-4 { + padding-top: 1rem; +} +.pt-5 { + padding-top: 1.25rem; +} +.pt-6 { + padding-top: 1.5rem; +} +.text-left { + text-align: left; +} +.text-center { + text-align: center; +} +.text-right { + text-align: right; +} +.align-top { + vertical-align: top; +} +.align-middle { + vertical-align: middle; +} +.font-mono { + font-family: var(--font-family-mono); +} +.font-sans { + font-family: var(--font-family-sans); +} +.text-2xl { + font-size: var(--font-size-2xl); + line-height: var(--line-height-relaxed); +} +.text-2xs { + font-size: var(--font-size-2xs); + line-height: var(--line-height-tight); +} +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} +.text-3xs { + font-size: var(--font-size-3xs); + line-height: var(--line-height-tight); +} +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} +.text-\[0\.75rem\] { + font-size: 0.75rem; +} +.text-base { + font-size: var(--font-size-base); + line-height: var(--line-height-base); +} +.text-lg { + font-size: var(--font-size-lg); + line-height: var(--line-height-base); +} +.text-sm { + font-size: var(--font-size-sm); + line-height: var(--line-height-snug); +} +.text-xl { + font-size: var(--font-size-xl); + line-height: var(--line-height-relaxed); +} +.text-xs { + font-size: var(--font-size-xs); + line-height: var(--line-height-tight); +} +.font-bold { + font-weight: 700; +} +.font-extrabold { + font-weight: 800; +} +.font-medium { + font-weight: 500; +} +.font-semibold { + font-weight: 600; +} +.uppercase { + text-transform: uppercase; +} +.lowercase { + text-transform: lowercase; +} +.normal-case { + text-transform: none; +} +.italic { + font-style: italic; +} +.ordinal { + --tw-ordinal: ordinal; + font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction); +} +.tabular-nums { + --tw-numeric-spacing: tabular-nums; + font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction); +} +.leading-none { + line-height: 1; +} +.leading-relaxed { + line-height: 1.625; +} +.leading-snug { + line-height: 1.375; +} +.tracking-extra-wide { + letter-spacing: var(--tracking-extra-wide); +} +.tracking-tight { + letter-spacing: -0.025em; +} +.tracking-wide { + letter-spacing: 0.025em; +} +.tracking-wider { + letter-spacing: 0.05em; +} +.text-accent { + color: var(--color-accent); +} +.text-black { + color: var(--color-black); +} +.text-critical { + color: var(--color-critical); +} +.text-critical-strong { + color: var(--color-critical-strong); +} +.text-info { + color: var(--color-info); +} +.text-info-strong { + color: var(--color-info-strong); +} +.text-muted { + color: var(--text-muted); +} +.text-muted-strong { + color: var(--text-muted-strong); +} +.text-primary { + color: var(--text-primary); +} +.text-status-danger { + color: var(--status-danger); +} +.text-status-danger-on { + color: var(--status-danger-on); +} +.text-status-info { + color: var(--status-info); +} +.text-status-info-on { + color: var(--status-info-on); +} +.text-status-neutral { + color: var(--status-neutral); +} +.text-status-private-on { + color: var(--status-private-on); +} +.text-status-success { + color: var(--status-success); +} +.text-status-success-on { + color: var(--status-success-on); +} +.text-status-warning { + color: var(--status-warning); +} +.text-status-warning-on { + color: var(--status-warning-on); +} +.text-subtle { + color: var(--text-subtle); +} +.text-text { + color: var(--text-primary); +} +.text-text-frosted { + color: var(--text-frosted-primary); +} +.text-text-muted { + color: var(--text-muted); +} +.text-text-strong { + color: var(--color-text-strong); +} +.text-warning { + color: var(--status-warning); +} +.text-warning-strong { + color: var(--status-warning-strong); +} +.text-white { + color: var(--color-white); +} +.underline { + text-decoration-line: underline; +} +.decoration-dotted { + text-decoration-style: dotted; +} +.underline-offset-2 { + text-underline-offset: 2px; +} +.opacity-0 { + opacity: 0; +} +.opacity-20 { + opacity: 0.2; +} +.opacity-40 { + opacity: 0.4; +} +.opacity-50 { + opacity: 0.5; +} +.opacity-60 { + opacity: 0.6; +} +.opacity-70 { + opacity: 0.7; +} +.opacity-80 { + opacity: 0.8; +} +.opacity-95 { + opacity: 0.95; +} +.shadow-2xl { + --tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25); + --tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.shadow-inner { + --tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.shadow-lg { + --tw-shadow: var(--shadow-lg); + --tw-shadow-colored: var(--shadow-lg); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.shadow-md { + --tw-shadow: var(--shadow-md); + --tw-shadow-colored: var(--shadow-md); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.shadow-sm { + --tw-shadow: var(--shadow-sm); + --tw-shadow-colored: var(--shadow-sm); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.shadow-xl { + --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.outline-none { + outline: 2px solid transparent; + outline-offset: 2px; +} +.outline { + outline-style: solid; +} +.blur { + --tw-blur: blur(8px); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} +.blur-xl { + --tw-blur: blur(24px); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} +.drop-shadow-md { + --tw-drop-shadow: drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06)); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} +.\!filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow) !important; +} +.filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} +.backdrop-blur-md { + --tw-backdrop-blur: blur(12px); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); +} +.backdrop-blur-sm { + --tw-backdrop-blur: blur(4px); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); +} +.transition { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.transition-all { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.transition-colors { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.transition-opacity { + transition-property: opacity; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.transition-transform { + transition-property: transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.duration-150 { + transition-duration: 150ms; +} +.duration-200 { + transition-duration: 200ms; +} +.duration-300 { + transition-duration: 300ms; +} +.duration-fast { + transition-duration: var(--motion-duration-fast); +} +.ease-out { + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +} +@media (max-width: 640px) { + .bv-grid-video { + gap: var(--space-md); + grid-template-columns: 1fr; + } +} +@media (prefers-reduced-motion: reduce) { + :root { + --motion-duration-fast: 0s; + --motion-duration-base: 0s; + --motion-duration-slow: 0s; + --modal-motion-duration: 0s; + } + + *, + *::before, + *::after { + animation-delay: 0s !important; + animation-duration: 0s !important; + animation-iteration-count: 1 !important; + animation-timing-function: linear !important; + transition-delay: 0s !important; + transition-duration: 0s !important; + transition-timing-function: linear !important; + } + + .player-modal__similar-card::before { + transition: none !important; + } + + .btn, + .btn-ghost, + .card, + .card[data-motion="enter"], + .fade-in, + .icon-button, + .menu__item, + .modal-nav, + .popover-panel, + .popover__panel, + .progress-bar-fill, + .notification-banner, + .notification-portal, + .torrent-toast, + .status-banner .status-spinner, + .status-spinner--inline, + .video-modal__panel, + .watch-history-card, + .watch-history-card__creatorAvatar, + .watch-history-card__creatorName, + .watch-history-card__action, + .watch-history-card__title, + #app, + #playerModal .player-modal__content, + #sidebar, + .sidebar-collapse-toggle, + .sidebar-collapse-toggle__icon, + .sidebar-dropup-panel, + .sidebar-dropup-trigger, + .sidebar-dropup-trigger__chevron, + .sidebar-nav, + .sidebar-nav-link, + .sidebar-loading-indicator, + .sidebar-loading-wrapper { + transition-property: none !important; + } + + .popover-panel, + .popover__panel { + transform: none !important; + } + + .popover-panel[data-state="open"], + .popover__panel[data-state="open"] { + opacity: 1; + } + + .popover-panel[data-state="closed"], + .popover__panel[data-state="closed"] { + opacity: 0; + } + + .card[data-motion="enter"], + .fade-in { + opacity: 1; + animation: none !important; + } + + [data-video-thumbnail][data-thumbnail-loaded="true"] { + animation: none !important; + opacity: 1; + } + + .notification-banner, + .torrent-toast, + .status-banner .status-spinner, + .status-spinner--inline { + animation: none !important; + } + + .notification-portal { + transform: none !important; + opacity: 1 !important; + } +} +/* Profile Modal Full Screen Overrides */ +#profileModal { + padding: 0; + width: 100vw; + height: 100vh; + max-width: none; + max-height: none; +} +#profileModal .bv-modal__panel { + width: 100%; + height: 100%; + max-width: none; + max-height: none; + border-radius: 0; + border: none; +} +/* Override responsive max-height constraint */ +@media (min-width: 1024px) { + #profileModal .profile-modal { + max-block-size: none; + } +} +.no-scrollbar { + scrollbar-width: none !important; + -ms-overflow-style: none !important; +} +.no-scrollbar::-webkit-scrollbar { + display: none !important; +} +/* Embed & Video Modal Fixes */ +.empty\:hidden:empty + .bv-modal { + display: none !important; + } +.empty\:hidden:empty.profile-pane { + display: none !important; + } +@media (min-width: 768px) { + + .md\:hidden + .bv-modal { + display: none !important; + } + + .md\:hidden.profile-pane { + display: none !important; + } +} +@media (min-width: 1024px) { + + .lg\:hidden + .bv-modal { + display: none !important; + } + + .lg\:hidden.profile-pane { + display: none !important; + } +} +.placeholder\:text-muted::-moz-placeholder { + color: var(--text-muted); +} +.placeholder\:text-muted::placeholder { + color: var(--text-muted); +} +.empty\:hidden:empty { + display: none; +} +.hover\:scale-105:hover { + --tw-scale-x: 1.05; + --tw-scale-y: 1.05; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.hover\:border-status-danger-border:hover { + border-color: var(--status-danger-border); +} +.hover\:bg-overlay-panel:hover { + background-color: var(--surface-overlay-panel); +} +.hover\:bg-panel-hover:hover { + background-color: var(--surface-panel-hover); +} +.hover\:text-accent:hover { + color: var(--color-accent); +} +.hover\:text-accent-strong:hover { + color: var(--color-accent-strong); +} +.hover\:text-critical:hover { + color: var(--color-critical); +} +.hover\:text-info-strong:hover { + color: var(--color-info-strong); +} +.hover\:text-status-danger-strong:hover { + color: var(--status-danger-strong); +} +.hover\:text-text:hover { + color: var(--text-primary); +} +.hover\:text-text-strong:hover { + color: var(--color-text-strong); +} +.hover\:underline:hover { + text-decoration-line: underline; +} +.hover\:opacity-80:hover { + opacity: 0.8; +} +.hover\:opacity-90:hover { + opacity: 0.9; +} +.hover\:shadow-sm:hover { + --tw-shadow: var(--shadow-sm); + --tw-shadow-colored: var(--shadow-sm); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} +.focus\:ring-0:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} +.focus\:ring-2:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} +.focus\:ring-accent:focus { + --tw-ring-color: var(--color-accent); +} +.focus\:ring-info:focus { + --tw-ring-color: var(--color-info); +} +.focus\:ring-status-info:focus { + --tw-ring-color: var(--status-info); +} +.focus\:ring-status-success:focus { + --tw-ring-color: var(--status-success); +} +.focus\:ring-offset-2:focus { + --tw-ring-offset-width: 2px; +} +.focus\:ring-offset-surface:focus { + --tw-ring-offset-color: var(--surface-base); +} +.focus-visible\:bg-primary:focus-visible { + background-color: var(--color-primary); +} +.focus-visible\:outline:focus-visible { + outline-style: solid; +} +.focus-visible\:outline-2:focus-visible { + outline-width: 2px; +} +.focus-visible\:outline-offset-2:focus-visible { + outline-offset: 2px; +} +.focus-visible\:outline-info:focus-visible { + outline-color: var(--color-info); +} +.active\:scale-95:active { + --tw-scale-x: .95; + --tw-scale-y: .95; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.disabled\:cursor-not-allowed:disabled { + cursor: not-allowed; +} +.disabled\:opacity-50:disabled { + opacity: 0.5; +} +.group:hover .group-hover\:scale-105 { + --tw-scale-x: 1.05; + --tw-scale-y: 1.05; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.group:hover .group-hover\:text-primary { + color: var(--text-primary); +} +.group:hover .group-hover\:opacity-100 { + opacity: 1; +} +@media not all and (min-width: 640px) { + .max-sm\:h-12 { + height: 3rem; + } + .max-sm\:w-12 { + width: 3rem; + } + .max-sm\:text-2xs { + font-size: var(--font-size-2xs); + line-height: var(--line-height-tight); + } + .max-sm\:text-base { + font-size: var(--font-size-base); + line-height: var(--line-height-base); + } +} +@media (min-width: 640px) { + .sm\:order-1 { + order: 1; + } + .sm\:order-2 { + order: 2; + } + .sm\:col-span-2 { + grid-column: span 2 / span 2; + } + .sm\:flex { + display: flex; + } + .sm\:w-1\/3 { + width: 33.333333%; + } + .sm\:w-32 { + width: 8rem; + } + .sm\:w-48 { + width: 12rem; + } + .sm\:w-auto { + width: auto; + } + .sm\:max-w-xs { + max-width: 20rem; + } + .sm\:flex-none { + flex: none; + } + .sm\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .sm\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + .sm\:flex-row { + flex-direction: row; + } + .sm\:flex-wrap { + flex-wrap: wrap; + } + .sm\:items-start { + align-items: flex-start; + } + .sm\:items-end { + align-items: flex-end; + } + .sm\:items-center { + align-items: center; + } + .sm\:justify-end { + justify-content: flex-end; + } + .sm\:justify-between { + justify-content: space-between; + } + .sm\:gap-2 { + gap: 0.5rem; + } + .sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0px * var(--tw-space-y-reverse)); + } + .sm\:self-end { + align-self: flex-end; + } + .sm\:p-0 { + padding: 0px; + } + .sm\:p-4 { + padding: 1rem; + } + .sm\:p-5 { + padding: 1.25rem; + } + .sm\:px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; + } + .sm\:py-0 { + padding-top: 0px; + padding-bottom: 0px; + } + .sm\:pt-8 { + padding-top: 2rem; + } +} +@media (min-width: 768px) { + .md\:col-span-2 { + grid-column: span 2 / span 2; + } + .md\:flex { + display: flex; + } + .md\:hidden { + display: none; + } + .md\:h-64 { + height: 16rem; + } + .md\:w-1\/3 { + width: 33.333333%; + } + .md\:w-2\/3 { + width: 66.666667%; + } + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .md\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + .md\:flex-row { + flex-direction: row; + } + .md\:items-end { + align-items: flex-end; + } + .md\:items-center { + align-items: center; + } + .md\:justify-end { + justify-content: flex-end; + } + .md\:gap-md { + gap: var(--space-md); + } + .md\:pb-12 { + padding-bottom: 3rem; + } +} +@media (min-width: 1024px) { + .lg\:ml-auto { + margin-left: auto; + } + .lg\:block { + display: block; + } + .lg\:flex { + display: flex; + } + .lg\:hidden { + display: none; + } + .lg\:h-auto { + height: auto; + } + .lg\:max-h-modal-shell { + max-height: var(--modal-shell-max-height); + } + .lg\:w-64 { + width: 16rem; + } + .lg\:max-w-4xl { + max-width: 56rem; + } + .lg\:max-w-5xl { + max-width: 64rem; + } + .lg\:shrink-0 { + flex-shrink: 0; + } + .lg\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .lg\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + .lg\:flex-row { + flex-direction: row; + } + .lg\:flex-wrap { + flex-wrap: wrap; + } + .lg\:items-start { + align-items: flex-start; + } + .lg\:justify-center { + justify-content: center; + } + .lg\:justify-between { + justify-content: space-between; + } + .lg\:rounded-modal-xl { + border-radius: var(--radius-modal-xl); + } + .lg\:border { + border-width: 1px; + } + .lg\:p-8 { + padding: 2rem; + } + .lg\:px-8 { + padding-left: 2rem; + padding-right: 2rem; + } + .lg\:text-right { + text-align: right; + } +} +@media (min-width: 1280px) { + .xl\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + .xl\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } +} diff --git a/dist/docs/README.md b/dist/docs/README.md new file mode 100644 index 00000000..30c50a0b --- /dev/null +++ b/dist/docs/README.md @@ -0,0 +1,35 @@ +# Documentation Index + +Welcome to the bitvid documentation. This directory contains detailed architectural guides, API references, and system overviews. + +## Core Systems + +- **[Application Coordinators](./app-coordinators.md)**: Overview of the coordinator pattern used to manage app-wide state and services. +- **[Authentication Architecture](./auth-architecture.md)**: How session management, login flows, and signer integration work. +- **[Feed Engine](./feed-engine.md)**: The pipeline for fetching, filtering, scoring, and sorting video feeds. +- **[Playback & Fallback](./playback-fallback.md)**: The URL-first playback strategy with WebTorrent fallback orchestration. +- **[Watch History System](./watch-history-system.md)**: Encrypted watch history synchronization and storage. + +## Nostr Protocol + +- **[Nostr Event Schemas](./nostr-event-schemas.md)**: The definitive catalogue of all Nostr event kinds and data structures used by bitvid. +- **[DM Privacy Model](./dm-privacy-model.md)**: How Direct Messages are handled, including NIP-17 relays and privacy controls. +- **[NIP-46 Client Overview](./nip46-client-overview.md)**: Details on the NIP-46 remote signer implementation. + +## Moderation & Safety + +- **[Moderation Service](./moderation-service-overview.md)**: How content moderation, reporting, and admin lists function. +- **[Logging Strategy](./logging.md)**: Guidelines for using `devLogger` vs `userLogger` and production logging policies. + +## Design & UI + +- **[Design System](./design-system.md)**: Guide to the token-based design system and CSS architecture. +- **[Menus & Popovers](./menus.md)**: Documentation for the menu and popover system. + +## Operations + +- **[Instance Configuration](./instance-config.md)**: Guide to configuring a bitvid instance. +- **[Deployment Notes](./deployment-notes.md)**: Notes and checklists for deploying bitvid. +- **[Manual QA Checklist](./qa.md)**: The standard manual QA script for releases. + +For more details on contributing, please see [CONTRIBUTING.md](../CONTRIBUTING.md). diff --git a/dist/docs/VideoModal-overview.md b/dist/docs/VideoModal-overview.md new file mode 100644 index 00000000..d4d2fd92 --- /dev/null +++ b/dist/docs/VideoModal-overview.md @@ -0,0 +1,82 @@ +# VideoModal Component Overview + +The `VideoModal` (`js/ui/components/VideoModal.js`) is the primary UI controller for the full-screen video playback experience in bitvid. It manages the modal lifecycle, orchestrates playback, and coordinates sub-controllers for social interactions (comments, reactions, moderation). + +## Core Responsibilities + +1. **Lifecycle Management**: Handles lazy-loading of the modal HTML, DOM injection, and visibility toggling. +2. **Playback Orchestration**: Connects the video metadata to the underlying media player (via `mediaLoader` or direct video element manipulation). +3. **Social Coordination**: Instantiates and manages `CommentsController`, `ReactionsController`, `SimilarContentController`, and `ModerationController`. +4. **Accessibility**: Enforces focus trapping and ARIA attributes via `ModalAccessibility`. +5. **Global State Integration**: Updates the application's modal state (`setGlobalModalState`) to pause background feeds and handle back-button navigation. + +## Public API + +The `VideoModal` exposes a minimal public interface for the application orchestrator (`ModalManager` or `App`): + +| Method | Description | +|--------|-------------| +| `constructor(deps)` | Initializes the controller with dependencies (DOM, logger, state helpers). Does *not* touch the DOM. | +| `async load()` | Fetches `components/video-modal.html`, injects it into the DOM, and hydrates references. Safe to call multiple times (idempotent). | +| `open(video, options)` | Resets state, populates the modal with the given `video` object, and makes it visible. | +| `close()` | Hides the modal, pauses playback, clears state, and returns focus to the trigger element. | + +### Dependencies +The constructor requires a robust dependency injection object: +- `document`: The DOM document. +- `setGlobalModalState`: Function to notify the app of modal visibility changes. +- `logger`: Logging utility. +- `mediaLoader`: Service for handling media source resolution. +- `assets`, `state`, `helpers`: Configuration objects. + +## internal Architecture + +### Lazy Loading & Hydration +To improve initial page load performance, `VideoModal` does not render its markup until `load()` is called (typically on the first user interaction). +1. **Fetch**: Retrieves `components/video-modal.html`. +2. **Inject**: Appends the HTML to the `#modalContainer`. +3. **Hydrate**: Queries for all necessary DOM elements (buttons, containers, inputs) and binds event listeners. +4. **Sub-Controller Init**: Initializes `CommentsController`, `ReactionsController`, etc., passing them the fresh DOM references. + +### Sub-Controllers +The `VideoModal` acts as a parent controller, delegating specific logic to specialized classes: +- **`CommentsController`**: Manages the comment thread, composer, and fetching. +- **`ReactionsController`**: Handles likes/dislikes and reaction counts. +- **`SimilarContentController`**: Fetches and renders related videos. +- **`ModerationController`**: Manages content warnings, blurring, and reporting flows. + +## Usage Example + +```javascript +// Instantiation (usually in ModalManager) +const videoModal = new VideoModal({ + document: document, + setGlobalModalState: (key, active) => app.setModalState(key, active), + logger: logger, + mediaLoader: mediaLoader +}); + +// Initialization (lazy) +await videoModal.load(); + +// Opening +const video = { + id: "...", + title: "My Video", + pubkey: "...", + // ... other metadata +}; +videoModal.open(video, { triggerElement: clickedCard }); +``` + +## Key Invariants + +1. **Single Active Instance**: Only one video modal exists in the DOM at a time. Opening a new video replaces the content of the existing modal. +2. **Focus Management**: When open, the modal traps focus. When closed, it restores focus to the element that triggered it (via `options.triggerElement`). +3. **State Synchronization**: The `setGlobalModalState` callback MUST be called on open/close to ensure the back button works correctly and background videos pause. + +## When to Change + +- **Refactoring**: This file is extremely large (>6000 LOC). Consider extracting more logic into sub-controllers (e.g., a `MetadataController` for title/description/tags). +- **Styling**: Changes to the modal layout should be mirrored in `components/video-modal.html` and tested for responsiveness. +- **Playback Logic**: Changes to how videos load or play should likely be done in `mediaLoader` or `PlaybackService` rather than here, unless it's strictly UI-related (e.g., custom controls). diff --git a/dist/docs/app-coordinators.md b/dist/docs/app-coordinators.md new file mode 100644 index 00000000..1b5d7af0 --- /dev/null +++ b/dist/docs/app-coordinators.md @@ -0,0 +1,104 @@ +# Application Coordinator Architecture + +The `Application` class (`js/app.js`) acts as the **composition root** for bitvid. +Business logic that previously lived directly on `Application` has been extracted +into **coordinator modules** under `js/app/`. `Application` retains thin +delegator methods so that every public call-site continues to work unchanged. + +## Coordinator Modules + +| Module | File | Responsibility | +|--------|------|----------------| +| Feed | `js/app/feedCoordinator.js` | Feed registration, runtime building, refresh, video loading, URL-health helpers, feed telemetry | +| Playback | `js/app/playbackCoordinator.js` | URL-first + magnet fallback pipeline, probing, torrent status, video timestamps | +| Auth/Session | `js/app/authSessionCoordinator.js` | Signer lifecycle, profile switching, relay/block/wallet management, cleanup | +| Modal | `js/app/modalCoordinator.js` | Modal open/close, view-count subscriptions, watch-history metadata, playback logging | +| Moderation | `js/app/moderationCoordinator.js` | Moderation settings, video decoration, override/block/hide actions | + +A shared helper (`js/app/bindCoordinator.js`) binds every method returned by a +coordinator factory to the `Application` instance so that `this` works as expected. + +## Dependency Injection + +Each coordinator is a factory function that receives a `deps` object containing +every module-level dependency it needs. Coordinators **never import globals** +themselves; all references come through the closure created by `deps`: + +```javascript +export function createFeedCoordinator(deps) { + const { devLogger, nostrClient, watchHistoryService, ... } = deps; + + return { + async loadVideos(forceFetch = false) { + devLogger.log("Starting loadVideos..."); // closure var, not import + // `this` is the Application instance (bound by caller) + this.checkRelayHealthWarning(); + // ... + }, + }; +} +``` + +`Application._initCoordinators()` creates all five coordinators and injects their +dependencies from the module scope of `js/app.js`. It is called eagerly in the +constructor and idempotently from every delegator, so test harnesses that use +`Object.create(Application.prototype)` also work. + +## Delegation Pattern + +Every extracted method has a matching thin delegator on `Application`: + +```javascript +class Application { + async loadVideos(...args) { + this._initCoordinators(); + return this._feed.loadVideos(...args); + } +} +``` + +This preserves the public API surface. Callers (views, services, test harnesses) +do not need to know about coordinators. + +## Old-to-New Mapping + +| Old location (`Application` method) | New location | +|--------------------------------------|--------------| +| `registerRecentFeed`, `registerForYouFeed`, `registerKidsFeed`, `registerExploreFeed`, `registerSubscriptionsFeed`, `registerWatchHistoryFeed` | `feedCoordinator` | +| `buildForYouFeedRuntime`, `buildExploreFeedRuntime`, `buildRecentFeedRuntime`, `buildKidsFeedRuntime` | `feedCoordinator` | +| `refreshForYouFeed`, `refreshKidsFeed`, `refreshExploreFeed`, `refreshRecentFeed` | `feedCoordinator` | +| `loadVideos`, `loadForYouVideos`, `loadKidsVideos`, `loadExploreVideos`, `loadOlderVideos` | `feedCoordinator` | +| `renderVideoList`, `refreshVideoDiscussionCounts` | `feedCoordinator` | +| `checkRelayHealthWarning`, `hasOlderVersion` | `feedCoordinator` | +| Feed telemetry (`getFeedTelemetryState`, `emitFeedImpressions`, `recordFeedClick`, ...) | `feedCoordinator` | +| `playHttp`, `playViaWebTorrent`, `playVideoWithFallback` | `playbackCoordinator` | +| `playVideoByEventId`, `playVideoWithoutEvent` | `playbackCoordinator` | +| `probeUrl`, `probeUrlWithVideoElement`, `checkUrlParams` | `playbackCoordinator` | +| `autoplayModalVideo`, `startTorrentStatusMirrors` | `playbackCoordinator` | +| `buildModalTimestampPayload`, `resolveVideoPostedAt`, `ensureModalPostedTimestamp` | `playbackCoordinator` | +| `shouldDeferModeratedPlayback`, `resumePendingModeratedPlayback` | `playbackCoordinator` | +| `handleAuthLogin`, `handleAuthLogout`, `requestLogout` | `authSessionCoordinator` | +| `handleBlocksLoaded`, `handleRelaysLoaded`, `scheduleRelayUiRefresh` | `authSessionCoordinator` | +| `cleanup`, `waitForCleanup`, `clearActiveIntervals` | `authSessionCoordinator` | +| `handleProfileSwitchRequest`, `waitForIdentityRefresh`, `handleProfileLogoutRequest` | `authSessionCoordinator` | +| `handleProfileRelayOperation`, `handleProfileBlocklistMutation`, `handleProfileAdminMutation` | `authSessionCoordinator` | +| `handleProfileWalletPersist`, `handleProfileWalletTest`, `handleProfileWalletDisconnect` | `authSessionCoordinator` | +| `showModalWithPoster`, `hideModal`, `ensureVideoModalReady` | `modalCoordinator` | +| `subscribeModalViewCount`, `teardownModalViewCountSubscription` | `modalCoordinator` | +| `preparePlaybackLogging`, `teardownVideoElement` | `modalCoordinator` | +| `persistWatchHistoryMetadataForVideo`, `handleRemoveHistoryAction` | `modalCoordinator` | +| `setShareButtonState`, `getShareUrlBase` | `modalCoordinator` | +| `handleModerationSettingsChange`, `refreshVisibleModerationUi` | `moderationCoordinator` | +| `decorateVideoModeration`, `initializeModerationActionController` | `moderationCoordinator` | +| `handleModerationOverride`, `handleModerationBlock`, `handleModerationHide` | `moderationCoordinator` | +| `normalizeModerationSettings`, `getActiveModerationThresholds` | `moderationCoordinator` | + +## Adding New Methods + +When adding a method that belongs to an existing coordinator: + +1. Add the method to the coordinator's return object. +2. Add a thin delegator on `Application` that calls `this._initCoordinators()` + then delegates to the coordinator. +3. If the method uses a new module-level import, add it to the coordinator's + `deps` destructuring and pass it from `_initCoordinators()`. diff --git a/dist/docs/capacitor-native-app-plan.md b/dist/docs/capacitor-native-app-plan.md new file mode 100644 index 00000000..2dd2f6a4 --- /dev/null +++ b/dist/docs/capacitor-native-app-plan.md @@ -0,0 +1,849 @@ +# Capacitor Native App Plan + +> **Status**: Planning +> **Platforms**: Android, iOS, Windows, macOS, Linux +> **Framework**: [Capacitor](https://capacitorjs.com/) by Ionic +> **Goal**: Ship bitvid as a native app on all major platforms while keeping a single codebase with the existing web/PWA deployment. + +--- + +## Table of Contents + +1. [Why Capacitor](#why-capacitor) +2. [Architecture Overview](#architecture-overview) +3. [Repository Structure](#repository-structure) +4. [Phase 1 — Foundation](#phase-1--foundation) +5. [Phase 2 — Android & iOS](#phase-2--android--ios) +6. [Phase 3 — Desktop (Electron via Capacitor)](#phase-3--desktop-electron-via-capacitor) +7. [Phase 4 — Platform-Specific Enhancements](#phase-4--platform-specific-enhancements) +8. [Phase 5 — CI/CD for Native Builds](#phase-5--cicd-for-native-builds) +9. [Phase 6 — Testing Strategy](#phase-6--testing-strategy) +10. [Platform Considerations & Risks](#platform-considerations--risks) +11. [App Store Strategy](#app-store-strategy) +12. [Task Checklist](#task-checklist) + +--- + +## Why Capacitor + +### Fit for bitvid + +bitvid is a fully static, client-side web application. There is no backend server — all authentication, data, and state management happens in the browser via Nostr relays and WebTorrent. This makes it an ideal candidate for Capacitor, which wraps an existing web app in a native WebView shell. + +### Key reasons + +| Reason | Detail | +|--------|--------| +| **Zero rewrite** | The existing `dist/` build output drops directly into Capacitor as the web layer. No framework migration needed. | +| **Single codebase** | Web, PWA, Android, iOS, and desktop all share the same HTML/CSS/JS. Platform-specific behavior is handled via feature detection and Capacitor's plugin bridge. | +| **Progressive native access** | Start with a pure web wrapper, then incrementally add native plugins (deep links, push notifications, share targets, file system) only when needed. | +| **Mature mobile support** | Capacitor has first-class Android and iOS support with a large plugin ecosystem. | +| **Desktop via Electron** | The `@nicepkg/capacitor-electron` community plugin (or the `@nicepkg/cap-electron` plugin) lets Capacitor drive an Electron shell for Windows/macOS/Linux using the same web build. | +| **Existing PWA foundation** | bitvid already has `site.webmanifest`, a service worker (`sw.min.js`), and app icons — all of which Capacitor respects and builds on. | +| **Active ecosystem** | Large community, official Ionic backing, regular releases, good documentation. | + +### Alternatives considered + +| Framework | Why not primary | +|-----------|----------------| +| **Tauri** | Excellent for desktop (small binaries, Rust backend), but mobile support is still in beta. Could be revisited later as a desktop-only option if Electron bundle size becomes a concern. | +| **Electron standalone** | Bundles ~150MB of Chromium per platform. Capacitor+Electron gives us the same result but with a unified build pipeline across all platforms. | +| **React Native / Flutter** | Would require a complete rewrite. bitvid is vanilla JS with no component framework — porting to RN or Flutter would be a ground-up rebuild. | +| **PWA only** | Already partially implemented. Good for web-savvy users but lacks app store discoverability, has weaker iOS support (no push notifications, limited background execution), and misses users who expect a "real" app install. | + +--- + +## Architecture Overview + +``` +┌─────────────────────────────────────────────────────┐ +│ bitvid codebase │ +│ │ +│ ┌───────────┐ npm run build ┌───────────────┐ │ +│ │ Source │ ────────────────→ │ dist/ │ │ +│ │ (js/, │ │ (static web │ │ +│ │ css/, │ │ assets) │ │ +│ │ views/) │ └──────┬────────┘ │ +│ └───────────┘ │ │ +│ │ │ +│ ┌────────────────┼─────┐ │ +│ │ npx cap sync │ │ │ +│ ▼ ▼ ▼ │ +│ ┌─────────┐ ┌─────┐ ┌────────┐ │ +│ │ android/│ │ ios/│ │electron/│ │ +│ │ (native │ │ │ │ │ │ +│ │ project)│ │ │ │ │ │ +│ └─────────┘ └─────┘ └────────┘ │ +│ │ +│ Deploy targets: │ +│ • Web/PWA → dist/ served via Netlify/Vercel/etc │ +│ • Android → android/ → Play Store APK/AAB │ +│ • iOS → ios/ → App Store IPA │ +│ • Desktop → electron/ → Windows/macOS/Linux │ +└─────────────────────────────────────────────────────┘ +``` + +### Data flow (unchanged) + +The native shell is purely a container. All app logic remains in the web layer: + +1. User action triggers a UI controller event handler +2. Controller invokes callback provided by `bitvidApp` +3. `bitvidApp` executes logic, communicates with Nostr relays via WebSocket +4. WebTorrent handles P2P video streaming via WebRTC data channels +5. UI updates via DOM manipulation + +The only new data path is the **Capacitor bridge** — a thin JS-to-native channel used for: +- Deep link handling (Nostr URIs, `nostr:` protocol) +- Push notification tokens (optional, future) +- Native share sheet integration +- App lifecycle events (background/foreground) +- Status bar / navigation bar styling + +--- + +## Repository Structure + +The native project directories live at the repo root alongside the existing web source. This is the standard Capacitor monorepo layout. + +``` +bitvid/ +├── android/ # NEW — Native Android project (Android Studio) +│ ├── app/ +│ │ ├── src/main/ +│ │ │ ├── AndroidManifest.xml +│ │ │ ├── java/com/bitvid/app/ +│ │ │ └── res/ +│ │ └── build.gradle +│ ├── capacitor.settings.gradle +│ └── build.gradle +├── ios/ # NEW — Native iOS project (Xcode) +│ └── App/ +│ ├── App/ +│ │ ├── AppDelegate.swift +│ │ ├── Info.plist +│ │ └── capacitor.config.json (generated) +│ ├── App.xcodeproj +│ └── Podfile +├── electron/ # NEW — Electron desktop shell +│ ├── src/ +│ │ ├── index.ts +│ │ └── preload.ts +│ ├── capacitor.config.ts +│ └── package.json +├── capacitor.config.ts # NEW — Capacitor root config +├── dist/ # Existing build output (web assets) +├── js/ # Existing source +├── css/ # Existing source +├── config/ +│ └── instance-config.js # Extended with platform detection +├── scripts/ +│ ├── build-dist.mjs # Existing web build +│ ├── build-android.mjs # NEW — Android build helper +│ ├── build-ios.mjs # NEW — iOS build helper +│ └── build-desktop.mjs # NEW — Electron build helper +├── tests/ +│ ├── unit/ # Existing +│ ├── e2e/ # Existing (web) +│ ├── visual/ # Existing +│ ├── native/ # NEW — Native-specific tests +│ │ ├── android/ +│ │ ├── ios/ +│ │ └── desktop/ +│ └── platform/ # NEW — Cross-platform behavior tests +├── .github/workflows/ +│ ├── ci.yml # Existing (web CI) +│ ├── ci-android.yml # NEW — Android build + test +│ ├── ci-ios.yml # NEW — iOS build + test +│ └── ci-desktop.yml # NEW — Electron build + test +├── package.json # Extended with native scripts +├── capacitor.config.ts # Capacitor root configuration +└── site.webmanifest # Existing PWA manifest +``` + +### What gets gitignored + +```gitignore +# Native build artifacts +android/app/build/ +android/.gradle/ +android/capacitor-cordova-android-plugins/build/ +ios/App/Pods/ +ios/App/DerivedData/ +electron/dist/ +electron/node_modules/ + +# Capacitor copies of web assets (regenerated by cap sync) +android/app/src/main/assets/public/ +ios/App/App/public/ +``` + +### What gets committed + +The native project scaffolding (`android/`, `ios/`, `electron/`) **is committed** to the repo. This is the Capacitor convention — the native projects are source code, not generated artifacts. They contain platform-specific configuration (permissions, entitlements, splash screens, icons) that must be version-controlled. + +--- + +## Phase 1 — Foundation + +**Goal**: Initialize Capacitor, get the web app running inside native shells on Android and iOS simulators, and Electron on desktop. + +### Tasks + +1. **Install Capacitor core** + ```bash + npm install @capacitor/core @capacitor/cli + ``` + +2. **Create `capacitor.config.ts`** + ```typescript + import type { CapacitorConfig } from '@capacitor/cli'; + + const config: CapacitorConfig = { + appId: 'com.bitvid.app', + appName: 'bitvid', + webDir: 'dist', + server: { + // Allow WebSocket connections to Nostr relays + allowNavigation: ['*'], + }, + plugins: { + SplashScreen: { + launchShowDuration: 2000, + backgroundColor: '#0f172a', // matches theme + }, + }, + }; + + export default config; + ``` + +3. **Add native platforms** + ```bash + npx cap add android + npx cap add ios + ``` + +4. **Update build pipeline** — extend `build-dist.mjs` or add a wrapper: + ```bash + npm run build && npx cap sync + ``` + The `cap sync` command copies `dist/` into each native project and updates native dependencies. + +5. **Add Electron support** via community plugin: + ```bash + npm install @nicepkg/capacitor-electron + npx cap add electron + ``` + +6. **Add npm scripts** + ```json + { + "scripts": { + "cap:sync": "npx cap sync", + "cap:android": "npm run build && npx cap sync android && npx cap open android", + "cap:ios": "npm run build && npx cap sync ios && npx cap open ios", + "cap:electron": "npm run build && npx cap sync electron && npx cap open electron", + "build:all": "npm run build && npx cap sync" + } + } + ``` + +7. **Verify basic functionality** + - App loads in Android emulator + - App loads in iOS simulator + - App loads in Electron window + - Nostr relay WebSocket connections work + - Theme renders correctly + - Navigation / SPA routing works + +### Platform detection helper + +Add a lightweight platform detection utility that the rest of the app can use: + +```javascript +// js/utils/platform.js +import { Capacitor } from '@capacitor/core'; + +export const platform = { + isNative: Capacitor.isNativePlatform(), + isAndroid: Capacitor.getPlatform() === 'android', + isIos: Capacitor.getPlatform() === 'ios', + isElectron: Capacitor.getPlatform() === 'electron', + isWeb: Capacitor.getPlatform() === 'web', +}; +``` + +This module is safe to import on web — `@capacitor/core` detects it's running in a browser and returns `'web'` for the platform. + +--- + +## Phase 2 — Android & iOS + +**Goal**: Production-quality mobile builds ready for app store submission. + +### Android-specific work + +1. **Permissions** in `AndroidManifest.xml`: + - `INTERNET` (already default) + - `ACCESS_NETWORK_STATE` (connectivity detection) + - `CAMERA` / `MICROPHONE` — only if video recording is added later (not needed now) + +2. **WebView configuration**: + - Enable mixed content mode (for WebSocket `wss://` alongside `https://`) + - Enable JavaScript (default in Capacitor) + - Set `webSettings.mediaPlaybackRequiresUserGesture = false` for autoplay + +3. **App icons and splash screens**: + - Use `@capacitor/splash-screen` plugin + - Generate from existing `assets/png/android-chrome-512x512.png` + - Use `cordova-res` or `@capacitor/assets` to generate all density variants + +4. **Deep links**: + - Register `nostr:` URI scheme via Android intent filters + - Register `https://bitvid.tv` domain for App Links (needs `.well-known/assetlinks.json`) + +5. **Status bar and navigation bar**: + - `@capacitor/status-bar` plugin to match the dark theme (`#0f172a`) + - Handle safe areas / notch insets via CSS `env(safe-area-inset-*)` + +### iOS-specific work + +1. **Info.plist configuration**: + - `NSAppTransportSecurity` — allow WebSocket connections + - App description strings for any permission prompts + +2. **WKWebView settings**: + - `allowsInlineMediaPlayback: true` + - `mediaTypesRequiringUserActionForPlayback: []` (enables autoplay) + - These are configurable in `capacitor.config.ts` under `ios.webContentsConfiguration` + +3. **Deep links**: + - Universal Links (needs `apple-app-site-association` on the domain) + - Custom URL scheme `bitvid://` + +4. **Safe areas**: + - iOS notch, Dynamic Island, home indicator + - CSS `env(safe-area-inset-top)` etc. — update layout in `index.html` and key UI components + +5. **App icons**: + - Generate full icon set from existing 512x512 source + - Must include 1024x1024 for App Store + +### Shared mobile work + +1. **Hardware back button** (Android): + - Handle back navigation within the SPA instead of exiting the app + - Use `@capacitor/app` plugin's `backButton` listener + +2. **App lifecycle**: + - Pause/resume WebSocket connections on app background/foreground + - Use `@capacitor/app` `appStateChange` listener + +3. **Keyboard handling**: + - Adjust layout when virtual keyboard appears (search bar, comment inputs) + - Capacitor provides `Keyboard` plugin for this + +4. **Haptic feedback** (optional): + - Light haptics on zap actions using `@capacitor/haptics` + +--- + +## Phase 3 — Desktop (Electron via Capacitor) + +**Goal**: Ship bitvid as a standalone desktop app for Windows, macOS, and Linux. + +### Approach + +Use Capacitor's Electron integration so the desktop build uses the same `dist/` web assets and the same Capacitor plugin bridge as mobile. + +### Desktop-specific work + +1. **Window configuration**: + - Default size: 1280x800 (fits well for video content) + - Minimum size: 800x600 + - Remember window position/size across launches + - Frameless window option with custom title bar (matches bitvid header) + +2. **Menu bar**: + - Minimal native menu (File, Edit, View, Help) + - Or hide native menu and use the web app's own navigation + +3. **Tray icon** (optional): + - System tray icon for background seeding (WebTorrent) + - "Minimize to tray" behavior + +4. **Auto-updater**: + - Electron's `autoUpdater` for seamless desktop updates + - Or use GitHub Releases as the update feed + +5. **Protocol handler**: + - Register `nostr:` and `bitvid:` URI schemes at OS level + +6. **Packaging**: + - Windows: NSIS installer or MSIX + - macOS: DMG + notarization + - Linux: AppImage, .deb, .rpm + +--- + +## Phase 4 — Platform-Specific Enhancements + +These are optional features that improve the native experience. Implement after core functionality is solid. + +### Native share sheet + +```javascript +import { Share } from '@capacitor/share'; + +async function shareVideo(video) { + if (platform.isNative) { + await Share.share({ + title: video.title, + text: video.description, + url: `https://bitvid.tv/#video/${video.id}`, + dialogTitle: 'Share video', + }); + } +} +``` + +### Push notifications (future) + +Nostr-based push could be implemented via: +- A lightweight push relay that watches for mentions/zaps and sends FCM/APNs +- `@capacitor/push-notifications` plugin to receive them + +This is a significant feature and should be its own planning document. + +### Offline support + +- Cache the app shell and critical assets in the service worker +- Cache recently viewed video metadata for offline browsing +- Queue zaps and reactions for when connectivity returns + +### Picture-in-picture + +- Android: native PiP mode via custom Capacitor plugin +- iOS: `AVPictureInPictureController` via the WebView's native video player +- Desktop: Electron `BrowserWindow` PiP API + +--- + +## Phase 5 — CI/CD for Native Builds + +### New CI workflows + +#### `ci-android.yml` + +```yaml +name: CI — Android + +on: + push: + branches: [main] + paths: + - 'js/**' + - 'css/**' + - 'views/**' + - 'components/**' + - 'android/**' + - 'capacitor.config.ts' + - 'package.json' + pull_request: + paths: + - 'js/**' + - 'css/**' + - 'views/**' + - 'components/**' + - 'android/**' + - 'capacitor.config.ts' + +jobs: + build-android: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + - name: Install dependencies + run: npm ci + - name: Build web assets + run: npm run build + - name: Sync Capacitor + run: npx cap sync android + - name: Build Android debug APK + working-directory: android + run: ./gradlew assembleDebug + - name: Upload APK artifact + uses: actions/upload-artifact@v4 + with: + name: bitvid-android-debug + path: android/app/build/outputs/apk/debug/app-debug.apk +``` + +#### `ci-ios.yml` + +```yaml +name: CI — iOS + +on: + push: + branches: [main] + paths: ['js/**', 'css/**', 'views/**', 'ios/**', 'capacitor.config.ts'] + pull_request: + paths: ['js/**', 'css/**', 'views/**', 'ios/**', 'capacitor.config.ts'] + +jobs: + build-ios: + runs-on: macos-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + - name: Install dependencies + run: npm ci + - name: Build web assets + run: npm run build + - name: Sync Capacitor + run: npx cap sync ios + - name: Install CocoaPods + working-directory: ios/App + run: pod install + - name: Build iOS (no sign) + run: | + xcodebuild build \ + -workspace ios/App/App.xcworkspace \ + -scheme App \ + -destination 'generic/platform=iOS Simulator' \ + CODE_SIGN_IDENTITY="" \ + CODE_SIGNING_REQUIRED=NO +``` + +#### `ci-desktop.yml` + +```yaml +name: CI — Desktop + +on: + push: + branches: [main] + paths: ['js/**', 'css/**', 'views/**', 'electron/**', 'capacitor.config.ts'] + pull_request: + paths: ['js/**', 'css/**', 'views/**', 'electron/**', 'capacitor.config.ts'] + +jobs: + build-desktop: + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + - name: Install dependencies + run: npm ci + - name: Build web assets + run: npm run build + - name: Sync Capacitor + run: npx cap sync electron + - name: Build Electron + working-directory: electron + run: npm run build +``` + +### Path-filtered triggers + +Each native CI workflow uses `paths` filters so that: +- A CSS-only change triggers Android + iOS + Desktop builds (the web layer changed) +- A change to only `android/` triggers just the Android build +- Existing web CI (`ci.yml`) continues to run independently + +### CI cost management + +Native CI is more expensive than web CI: +- **iOS builds require `macos-latest` runners** (2-3x cost on GitHub Actions) +- **Android builds need JDK + Gradle** (slower startup) +- **Desktop builds run on 3 OS matrix** entries + +To manage costs: +- Only run native CI on PRs that touch relevant paths +- Cache Gradle/CocoaPods/node_modules aggressively +- Consider running iOS CI only on `main` merges (not every PR) initially +- Use GitHub Actions concurrency groups with `cancel-in-progress: true` + +--- + +## Phase 6 — Testing Strategy + +### Testing layers + +``` +┌─────────────────────────────────────────────┐ +│ Unit Tests (existing) │ ← Platform-agnostic +│ vitest — js logic, nostr, schemas, utils │ No changes needed +├─────────────────────────────────────────────┤ +│ Visual Regression (existing) │ ← Web viewport +│ Playwright — screenshot comparison │ Add mobile viewports +├─────────────────────────────────────────────┤ +│ E2E Web (existing) │ ← Browser-based +│ Playwright — user journeys in Chrome │ No changes needed +├─────────────────────────────────────────────┤ +│ E2E Mobile (NEW) │ ← Device/emulator +│ Appium or Detox — native app user flows │ +├─────────────────────────────────────────────┤ +│ Platform Smoke Tests (NEW) │ ← Quick validation +│ Per-platform build + launch + basic checks │ +├─────────────────────────────────────────────┤ +│ Cross-Platform Behavior Tests (NEW) │ ← Feature parity +│ Verify features work consistently across │ +│ web, Android, iOS, and desktop │ +└─────────────────────────────────────────────┘ +``` + +### New test categories + +#### Platform smoke tests (`tests/native/`) + +Quick checks that run after each native build: +- App launches without crash +- Main view renders +- WebSocket connection to a relay succeeds +- Video playback starts (URL source) +- Theme renders correctly (screenshot comparison) + +#### Mobile viewport visual tests + +Extend existing Playwright visual tests with mobile viewports: +```javascript +// playwright.config.ts addition +projects: [ + // existing desktop viewports... + { + name: 'Mobile Android', + use: { ...devices['Pixel 7'] }, + }, + { + name: 'Mobile iOS', + use: { ...devices['iPhone 14'] }, + }, +] +``` + +This runs in Playwright's browser emulation (not on real devices) but catches responsive layout issues early. + +#### WebView-specific E2E tests + +For testing inside the actual native WebView (not just a browser), use: +- **Android**: Espresso + WebView assertions, or Appium with WebView context switching +- **iOS**: XCUITest + WebView inspection, or Appium + +These are heavier to set up but catch WebView-specific issues (service worker behavior, WebRTC in WKWebView, etc). + +#### What to test per platform + +| Test | Web | Android | iOS | Desktop | +|------|-----|---------|-----|---------| +| Unit tests | yes | n/a | n/a | n/a | +| Visual regression (browser) | yes | mobile viewport | mobile viewport | yes | +| E2E web journeys | yes | — | — | — | +| App launches | — | yes | yes | yes | +| Relay WebSocket connects | yes | yes | yes | yes | +| Video playback (URL) | yes | yes | yes | yes | +| Video playback (WebTorrent) | yes | yes | **test carefully** | yes | +| Deep links (`nostr:`) | — | yes | yes | yes | +| Back button navigation | — | yes | — | — | +| Safe area rendering | — | — | yes | — | +| Keyboard interaction | — | yes | yes | — | +| Offline behavior | yes | yes | yes | yes | + +--- + +## Platform Considerations & Risks + +### WebTorrent on iOS (HIGH RISK) + +**Issue**: WKWebView's WebRTC support is more limited than desktop Chrome. WebTorrent relies on WebRTC data channels for peer-to-peer streaming. + +**Mitigation**: +- bitvid already has a URL-first playback fallback (`DEFAULT_PLAYBACK_SOURCE` in instance config) +- For iOS builds, default to URL-first playback: `DEFAULT_PLAYBACK_SOURCE: 'url'` +- Use platform detection to set this automatically: + ```javascript + if (platform.isIos) { + setUrlFirstEnabled(true); + } + ``` +- Test WebTorrent streaming on real iOS devices — it may work in modern iOS versions (16+), but should not be the primary path + +### Service worker in Capacitor WebView + +**Issue**: The `sw.min.js` service worker intercepts fetch requests to proxy WebTorrent stream data. Service worker support in native WebViews varies. + +**Mitigation**: +- Android: WKWebView supports service workers (API level 33+). For older Android, the URL fallback handles it. +- iOS: WKWebView supports service workers in iOS 14+, but with some caveats around scope and lifecycle. +- Test the WebTorrent streaming service worker flow specifically in native WebViews. +- If service worker is unreliable, consider a Capacitor plugin that bridges native HTTP interception as an alternative stream proxy. + +### WebSocket connectivity + +**Issue**: Native platforms may restrict background WebSocket connections. + +**Mitigation**: +- Use `@capacitor/app` lifecycle events to gracefully disconnect/reconnect: + ```javascript + App.addListener('appStateChange', ({ isActive }) => { + if (isActive) { + nostrClient.reconnect(); + } else { + nostrClient.pause(); + } + }); + ``` +- This also reduces battery drain from idle relay connections. + +### Content Security Policy + +**Issue**: Capacitor serves web assets from a local origin (`capacitor://localhost` on iOS, `https://localhost` on Android). CSP rules may need adjustment. + +**Mitigation**: +- Update `_headers` / CSP meta tags to allow the Capacitor local origin +- Allow `wss://*` for Nostr relay connections +- Allow `blob:` and `data:` for WebTorrent stream playback + +### App Store rejection risk + +See the [App Store Strategy](#app-store-strategy) section below. + +--- + +## App Store Strategy + +### Google Play Store + +**Risk level**: Low-Medium + +Google is generally more permissive. Key considerations: +- **Content rating**: Declare user-generated content (UGC) in the rating questionnaire +- **Moderation**: bitvid's existing moderation system (NIP-56 reports, NIP-51 mute lists, blur thresholds, autoplay blocking) demonstrates active content moderation +- **Crypto/payments**: Lightning/Zap functionality may trigger financial services review. Frame it as "tips" or "donations", not as a payment system. Emphasize that bitvid does not custody funds. + +### Apple App Store + +**Risk level**: Medium-High + +Apple has been unpredictable with Nostr/decentralized apps. Key considerations: +- **Guideline 4.2 (Minimum Functionality)**: The app must provide functionality beyond a "repackaged website". Native features (deep links, share sheet, haptics, PiP) help differentiate. +- **Guideline 1.2 (User Generated Content)**: Must have content reporting, blocking, and moderation. bitvid already has this. +- **Guideline 3.1.1 (In-App Purchase)**: If Lightning zaps are interpreted as digital purchases, Apple may require IAP. Mitigation: frame zaps as peer-to-peer tips external to the app (similar to how other Nostr apps handle this). +- **Review strategy**: Submit early with a clear App Review note explaining the decentralized architecture and content moderation approach. Engage with the review team proactively. + +### Desktop distribution + +**Risk level**: Low + +- **Windows**: Distribute via GitHub Releases initially. Microsoft Store submission optional. +- **macOS**: Notarize the DMG with an Apple Developer account. Distribute via GitHub Releases or the Mac App Store. +- **Linux**: AppImage on GitHub Releases. Snap Store / Flathub optional. + +--- + +## Task Checklist + +### Phase 1 — Foundation +- [ ] Install `@capacitor/core` and `@capacitor/cli` +- [ ] Create `capacitor.config.ts` with bitvid app ID and config +- [ ] Run `npx cap add android` and verify project structure +- [ ] Run `npx cap add ios` and verify project structure +- [ ] Set up Electron via community Capacitor plugin +- [ ] Add `js/utils/platform.js` platform detection utility +- [ ] Update `package.json` with Capacitor scripts (`cap:sync`, `cap:android`, `cap:ios`, `cap:electron`) +- [ ] Update `.gitignore` for native build artifacts +- [ ] Verify web app loads in Android emulator +- [ ] Verify web app loads in iOS simulator +- [ ] Verify web app loads in Electron window +- [ ] Verify Nostr relay connections work on all platforms +- [ ] Document local development setup for native builds + +### Phase 2 — Android & iOS +- [ ] Configure Android permissions in `AndroidManifest.xml` +- [ ] Configure iOS `Info.plist` settings +- [ ] Generate app icons for all densities (Android + iOS) +- [ ] Generate splash screens +- [ ] Implement safe area CSS for iOS notch / Dynamic Island +- [ ] Add `@capacitor/status-bar` and theme it to match bitvid +- [ ] Add `@capacitor/app` for lifecycle events (background/foreground) +- [ ] Implement hardware back button handling (Android) +- [ ] Set up deep link handling for `nostr:` URIs +- [ ] Configure WebView settings for media autoplay +- [ ] Test video playback (URL source) on both platforms +- [ ] Test video playback (WebTorrent) on both platforms +- [ ] Test service worker behavior in native WebViews +- [ ] Set iOS default to URL-first playback if WebTorrent is unreliable + +### Phase 3 — Desktop +- [ ] Configure Electron window defaults (size, min size, position memory) +- [ ] Set up native menu bar or hide it +- [ ] Register `nostr:` protocol handler at OS level +- [ ] Configure packaging (NSIS/DMG/AppImage) +- [ ] Test on Windows, macOS, and Linux +- [ ] Set up auto-updater with GitHub Releases feed + +### Phase 4 — Platform Enhancements +- [ ] Implement native share sheet integration +- [ ] Add `@capacitor/keyboard` for mobile input handling +- [ ] Explore picture-in-picture on each platform +- [ ] Add haptic feedback for zap actions (optional) +- [ ] Implement app shell caching in service worker for offline support + +### Phase 5 — CI/CD +- [ ] Create `ci-android.yml` workflow +- [ ] Create `ci-ios.yml` workflow +- [ ] Create `ci-desktop.yml` workflow (3-OS matrix) +- [ ] Add path filters to limit when native CI runs +- [ ] Set up artifact uploads for debug builds +- [ ] Configure caching (Gradle, CocoaPods, node_modules) +- [ ] Add concurrency groups to cancel stale runs +- [ ] Document release signing process (keystores, certificates, notarization) + +### Phase 6 — Testing +- [ ] Add mobile viewport projects to Playwright config +- [ ] Create platform smoke test suite (`tests/native/`) +- [ ] Set up Android emulator tests in CI +- [ ] Set up iOS simulator tests in CI +- [ ] Test WebTorrent streaming in native WebViews on real devices +- [ ] Test service worker behavior across platforms +- [ ] Test deep link handling on all platforms +- [ ] Create cross-platform feature parity test matrix +- [ ] Document known platform-specific limitations in `KNOWN_ISSUES.md` + +### App Store Submission +- [ ] Create Google Play developer account +- [ ] Prepare Play Store listing (screenshots, description, content rating) +- [ ] Submit Android app for review +- [ ] Create Apple Developer account (if not existing) +- [ ] Prepare App Store listing (screenshots, description, review notes) +- [ ] Submit iOS app for review +- [ ] Set up GitHub Releases for desktop distribution +- [ ] Notarize macOS DMG + +--- + +## Related Documentation + +- [Playback Fallback Architecture](playback-fallback.md) — URL-first strategy that mitigates WebTorrent issues on mobile +- [Instance Configuration](instance-config.md) — Platform-specific config overrides +- [WebTorrent Architecture](webtorrent-architecture.md) — Service worker streaming details +- [Moderation Service Overview](moderation-service-overview.md) — Content moderation (relevant for app store review) diff --git a/dist/docs/events-cache-store-overview.md b/dist/docs/events-cache-store-overview.md new file mode 100644 index 00000000..dd9e5a75 --- /dev/null +++ b/dist/docs/events-cache-store-overview.md @@ -0,0 +1,96 @@ +# EventsCacheStore Overview + +The `EventsCacheStore` is a specialized persistence layer designed to handle the application's critical state: thousands of video events and their deletion status (tombstones). It uses **IndexedDB** as the primary storage backend, falling back to `localStorage` (via `PersistenceManager`) only if IDB is unavailable. + +## 1. Problem Statement + +Nostr clients must handle high-volume event streams. Storing every event naively to disk on every change causes: +- **Write Trashing**: Frequent, redundant writes degrade performance and battery life. +- **I/O Blocking**: Large JSON serializations on the main thread cause UI jank. +- **Startup Latency**: Reading thousands of small files or one giant blob is slow. + +## 2. Architecture: Incremental Persistence + +`EventsCacheStore` solves this with a **Differential State Snapshot** approach. + +### Key Concepts + +1. **Memory Fingerprints**: + - The store maintains a `Map` in memory, mirroring what is currently on disk. + - `Event Fingerprint`: `JSON.stringify(videoObject)` (fast enough for 1000s of items). + - `Tombstone Fingerprint`: `ts:`. + +2. **Debounced Snapshots**: + - `PersistenceManager` debounces writes (e.g., 75ms). When a write triggers, it passes the *entire* current state (`allEvents`) plus a set of `dirtyKeys` (IDs that changed since last save). + +3. **The Diff Algorithm (`_diffAndStore`)**: + - Instead of clearing and rewriting the DB, it iterates over the `dirtyKeys`. + - It computes the new fingerprint. + - **Optimization**: If `newFingerprint === persistedFingerprint`, the write is skipped. + - If different, it performs a `put()` operation and updates the memory fingerprint map. + +4. **Deletion Detection**: + - After processing updates, it compares the size of the memory state (`items.size`) vs the persisted state (`fingerprintsMap.size`). + - Since `fingerprintsMap` is updated during the loop to include new items, if `items.size < fingerprintsMap.size`, it implies some items in the persisted map are no longer in memory. + - In this case, it iterates the *entire* fingerprint map to find and `delete()` the missing keys. + +### Flow Diagram + +```mermaid +sequenceDiagram + participant App as NostrClient + participant PM as PersistenceManager + participant ECS as EventsCacheStore + participant IDB as IndexedDB + + App->>App: Event arrives (updates allEvents) + App->>PM: saveLocalData(reason, dirtyKeys) + PM->>PM: Debounce (75ms) + PM->>ECS: persistSnapshot(payload, dirtyKeys) + + activate ECS + ECS->>ECS: Ensure fingerprints loaded (once) + + loop For each Dirty Key + ECS->>ECS: Compute Fingerprint + alt Fingerprint Changed + ECS->>IDB: put(record) + ECS->>ECS: Update Fingerprint Map + else Same Fingerprint + ECS->>ECS: Skip + end + end + + alt items.size < fingerprintsMap.size + loop For each Persisted Key + alt Not in Memory + ECS->>IDB: delete(key) + ECS->>ECS: Remove from Fingerprint Map + end + end + end + + ECS-->>PM: { writes, deletes, skipped } + deactivate ECS +``` + +## 3. Data Schema + +The IndexedDB `bitvid-events-cache` (v1) contains three object stores: + +| Store | Key Path | Value Description | +| :--- | :--- | :--- | +| `events` | `id` | `{ id, video, fingerprint }` | +| `tombstones` | `key` | `{ key, timestamp, fingerprint }` | +| `meta` | `key` | `{ key: "meta", savedAt: , version: 1 }` | + +## 4. Invariants & Assumptions + +- **Single Threaded**: The store assumes it is the only writer. It does not handle cross-tab locking (though IDB transactions provide atomicity). +- **Memory-First**: The `allEvents` Map in memory is the source of truth. IDB is a cache. +- **TTL**: On `restoreSnapshot()`, if the data is older than the TTL (e.g. 10 minutes), the entire cache is wiped to ensure freshness. + +## 5. Maintenance + +- **Adding Fields**: If the `Video` object shape changes, the fingerprint (JSON string) automatically changes, forcing a rewrite. No migration needed for content changes. +- **Schema Changes**: To add a new store, increment `EVENTS_CACHE_DB_VERSION` and update `onupgradeneeded` in `getDb()`. diff --git a/dist/docs/moderation-service-overview.md b/dist/docs/moderation-service-overview.md new file mode 100644 index 00000000..1387305e --- /dev/null +++ b/dist/docs/moderation-service-overview.md @@ -0,0 +1,73 @@ +# ModerationService Overview + +The `ModerationService` is the central authority for Trust & Safety within the application. It implements a "Web of Trust" model to filter content, aggregating reports (NIP-56) and mute lists (NIP-51) to determine the visibility and safety level of content. + +## Core Concepts + +### 1. Web of Trust +Instead of a centralized moderation authority, the service builds a local "trust graph" based on the user's social connections: +- **Viewer**: The currently logged-in user. +- **Trusted Contacts**: Users followed by the Viewer (Kind 3 contact list). +- **Trusted Seeds**: An optional set of admin-defined pubkeys that provide a baseline of trust (e.g., project maintainers). +- **Admin Whitelist/Blacklist**: Hardcoded lists in `accessControl` that override dynamic trust. + +### 2. Trust Aggregation +When a piece of content (Event) is reported (Kind 1984), the service aggregates these reports but *only counts reports from trusted users*. +- **Untrusted Reports**: Ignored by default. +- **Trusted Reports**: Count towards the "trust score" for a specific category (e.g., "nudity", "spam"). + +### 3. Thresholds +The service defines thresholds for automatic actions based on the trusted report count: +- **`AUTOPLAY_TRUST_THRESHOLD` (1)**: If ≥1 trusted user reports content as NSFW/Spam, autoplay is disabled. +- **`BLUR_TRUST_THRESHOLD` (1)**: If ≥1 trusted user reports content, it is blurred by default. +- **`TRUSTED_MUTE_WINDOW_DAYS` (60)**: Mute list entries are considered valid for 60 days to prevent stale mutes from persisting forever. + +## Architecture + +```mermaid +graph TD + User[User / Viewer] -->|Follows| Contacts[Trusted Contacts] + Admins[Admin / Seed List] -->|Augments| Contacts + + Reports[Report Events (Kind 1984)] -->|Ingested| Service[Moderation Service] + Contacts -->|Filters| Service + + Service -->|Aggregates| Summary[Trusted Report Summary] + Summary -->|Checks Thresholds| UI[UI Components] + + UI -->|Blur / Hide| Content +``` + +## Data Flow + +1. **Initialization**: + - The service initializes with empty sets. + - When `setViewerPubkey(pubkey)` is called, it fetches the user's Kind 3 Contact List. + - It merges these contacts with any configured `trustedSeedContacts`. + +2. **Report Ingestion**: + - The service subscribes to Kind 1984 events referencing active content. + - When a report arrives, `ingestReportEvent` checks if the reporter is in the `trustedContacts` set. + - If trusted, the report is indexed by `targetEventId` -> `reportType`. + +3. **Summary Recomputation**: + - `recomputeSummaryForEvent` calculates the total number of *trusted* reports per category. + - It emits a `summary` event, which UI components listen to (e.g., `VideoCard`). + - It logs "threshold transitions" (e.g., "autoplay-block-enabled") to the user logger. + +4. **Muting**: + - **Viewer Mutes**: The user's own Kind 10000 mute list. Directly hides content. + - **Trusted Mutes**: Aggregates mute lists from trusted contacts. If enough trusted users mute an author, that author may be flagged (feature partially implemented via `trustedMutedAuthors`). + +## Key Methods + +- `setViewerPubkey(pubkey)`: Switches the active user context, reloading trust lists. +- `submitReport({ eventId, type, ... })`: Publishes a NIP-56 report to relays. +- `getTrustedReportSummary(eventId)`: Returns the current trust status for an event. +- `getAccessControlStatus(pubkey)`: Checks against the hardcoded Admin Whitelist/Blacklist. + +## Internal Utilities + +The file includes internal implementations of `bech32` encoding/decoding. This is done to: +1. Reduce external dependencies for critical path logic. +2. Ensure consistent handling of `npub` / `hex` conversions without relying on the global `NostrTools` object potentially being unavailable or different versions. diff --git a/dist/docs/moderationService-overview.md b/dist/docs/moderationService-overview.md new file mode 100644 index 00000000..a5c0beda --- /dev/null +++ b/dist/docs/moderationService-overview.md @@ -0,0 +1,99 @@ +# Moderation Service Overview + +**File**: `js/services/moderationService.js` + +## Purpose + +The `ModerationService` implements the decentralized Trust & Safety model for bitvid. It filters content based on a "Web of Trust" derived from the current user's social graph (NIP-02 Contact List, Kind 3) and administrative overrides. + +Instead of a central authority deciding what is "safe," the service aggregates reports (NIP-56) only from users the viewer follows (their "trusted contacts"). This creates a personalized moderation layer where spam and abuse are filtered by community consensus within the user's network. + +## Key Concepts + +### Web of Trust +- **Viewer Context**: The service tracks the currently logged-in user (`viewerPubkey`). +- **Trusted Contacts**: The set of pubkeys the viewer follows (from their Kind 3 event). +- **Trust Score**: The number of unique trusted users who have reported a specific piece of content. +- **Thresholds**: Actions (e.g., blurring, disabling autoplay) are triggered when the Trust Score exceeds defined limits (`AUTOPLAY_TRUST_THRESHOLD`, `BLUR_TRUST_THRESHOLD`). + +### NIP-56 Reports (Kind 1984) +- Users can report content for reasons like `nudity`, `spam`, `illegal`, etc. +- Reports are public events signed by the reporter. +- The service subscribes to reports for active content and aggregates them locally. +- **Crucially**: Reports from users *outside* the trusted contact list are ignored (unless whitelisted by admin). + +### NIP-51 Mutes (Kind 10000) +- Users can mute other users to hide their content entirely. +- The service syncs with `UserBlocks` to enforce mutes. +- It also supports "Trusted Mutes" where mutes from trusted contacts can influence visibility (though this feature is evolving). + +### Admin Overrides +- The service integrates with `AccessControl` to respect an instance-wide **Blacklist** (always hidden) and **Whitelist** (always trusted). + +## Architecture & Data Flow + +### 1. Initialization (Setting the Viewer) +When a user logs in, `setViewerPubkey(pubkey)` is called: +1. The service fetches the user's latest Kind 3 Contact List. +2. It extracts all followed pubkeys into `trustedContacts`. +3. It merges any admin-defined "Trust Seeds" (default trusted users). +4. It recomputes trust scores for all currently loaded content based on the new graph. + +### 2. Report Aggregation +When viewing a video, the app calls `subscribeToReports(eventId)`: +1. The service subscribes to Kind 1984 events referencing the video ID. +2. Incoming reports are ingested via `ingestReportEvent`. +3. `recomputeSummaryForEvent` checks if the reporter is in `trustedContacts`. +4. If trusted, the report increments the score for its category. +5. If the score crosses a threshold, the service emits a `summary` event, updating the UI (e.g., to blur the video). + +## Usage Example + +```javascript +import moderationService from './services/moderationService.js'; + +// 1. Initialize with the logged-in user +await moderationService.setViewerPubkey(myPubkey); + +// 2. Subscribe to reports for a video +await moderationService.subscribeToReports(videoEventId); + +// 3. Listen for trust score updates +const unsubscribe = moderationService.on('summary', ({ eventId, summary }) => { + if (summary.totalTrusted >= 1) { + console.log(`Video ${eventId} is flagged by trusted users!`); + // UI logic: blur thumbnail, show warning + } +}); + +// 4. Submit a report (as the viewer) +await moderationService.submitReport({ + eventId: videoEventId, + type: 'nudity', + targetPubkey: videoAuthorPubkey, + content: 'Contains explicit content' +}); +``` + +## Public API Summary + +| Method | Description | +| :--- | :--- | +| `setViewerPubkey(pubkey)` | Sets the active user and rebuilds the trust graph from their contacts. | +| `subscribeToReports(eventId)` | Starts listening for NIP-56 reports for the given event ID. | +| `submitReport(params)` | Publishes a signed Kind 1984 report event to relays. | +| `getTrustedReportSummary(eventId)` | Returns the current aggregated trust score and report types. | +| `isPubkeyBlockedByViewer(pubkey)` | Checks if the author is blocked/muted by the viewer. | +| `refreshViewerFromClient()` | Helper to sync the viewer with the active Nostr client state. | + +## Invariants + +- **Trust is Subjective**: A report only counts if the *viewer* follows the reporter. Changing the viewer changes all trust scores immediately. +- **Admin Trumps Trust**: Blacklisted users are ignored even if followed. Whitelisted users are trusted even if not followed. +- **Reactive**: The service emits events (`summary`, `contacts`, `user-blocks`) to drive UI updates; it does not directly manipulate the DOM. + +## When to Change + +- **Refactoring**: Logic for "Trusted Mutes" (NIP-51 lists from others) is complex and partially deprecated/evolved. This area may need cleanup. +- **Performance**: If the number of reports or contacts grows large, the in-memory aggregation (`reportEvents` map) might need optimization or worker offloading. +- **Protocol Updates**: Changes to NIP-56 or NIP-51 should be reflected here. diff --git a/dist/docs/nip71-overview.md b/dist/docs/nip71-overview.md new file mode 100644 index 00000000..c315d0d0 --- /dev/null +++ b/dist/docs/nip71-overview.md @@ -0,0 +1,53 @@ +# NIP-71 Video Events Module (`js/nostr/nip71.js`) + +This module implements the **NIP-71** specification for video events in BitVid. It handles the lifecycle of video metadata, including creation, parsing, caching, and association with playable video notes (Kind 30078). + +## Core Responsibilities + +1. **Event Building**: Constructs NIP-71 events (Kind 22/21) with standardized tags (`imeta`, `segment`, `p`, etc.). +2. **Parsing**: Extracts structured metadata from raw Nostr events. +3. **Caching**: Manages a memory cache of NIP-71 metadata, indexed by video root ID, event ID, and `d` tags. +4. **Hydration**: Merges cached NIP-71 metadata into legacy or standard video objects for the UI. + +## Typical Data Flow + +### 1. Publishing a Video +When a user uploads a video, `Nip71FormManager` (in `js/ui/`) collects metadata and calls: +```javascript +import { buildNip71VideoEvent } from './js/nostr/nip71.js'; + +const event = buildNip71VideoEvent({ + metadata: { ... }, // title, summary, imeta, etc. + pubkey: userPubkey, + title: "My Video", + pointerIdentifiers: { videoRootId: "..." } +}); +// Sign and publish `event` +``` + +### 2. Viewing the Feed +The app fetches video notes (Kind 30078) and potential NIP-71 metadata events (Kind 22/21). +1. **Ingestion**: `processNip71Events(events, { nip71Cache })` parses incoming NIP-71 events and updates the cache. +2. **Hydration**: `mergeNip71MetadataIntoVideo(video, { nip71Cache })` attaches the best available metadata to the video object. + - It checks `videoRootId` -> `eventId` -> `d` tag priority. + - Falls back to the most recent metadata if specific pointers miss. + +## Key Exports + +| Function | Purpose | +|----------|---------| +| `buildNip71VideoEvent` | Creates a signed-ready NIP-71 event object. | +| `extractNip71MetadataFromTags` | Parses a raw event into `{ metadata, pointers }`. | +| `processNip71Events` | Updates the global `nip71Cache` with new events. | +| `mergeNip71MetadataIntoVideo` | Enriches a video object with cached metadata. | +| `populateNip71MetadataForVideos` | Batch-fetches metadata for a list of videos (if missing). | + +## Invariants & Assumptions + +- **Video Root ID**: The stable identifier for a video series/content, usually the `d` tag of the first Kind 30078 note. +- **Cache Structure**: The `nip71Cache` is a `Map`. +- **Immutability**: Metadata objects in the cache are cloned before being attached to video objects to prevent mutation side-effects. + +## When to Refactor +- If the cache logic becomes too complex or memory-intensive, move `nip71Cache` to a dedicated `Nip71Service` or `CacheService`. +- If NIP-71 spec changes significantly, versioning logic in `extractNip71MetadataFromTags` may need expansion. diff --git a/dist/docs/nostrEventSchemas-overview.md b/dist/docs/nostrEventSchemas-overview.md new file mode 100644 index 00000000..dd4ef8a0 --- /dev/null +++ b/dist/docs/nostrEventSchemas-overview.md @@ -0,0 +1,86 @@ +# Nostr Event Schemas (`js/nostrEventSchemas.js`) + +This module is the central definition for all Nostr event kinds, tags, and content structures used in the application. It provides builder functions to construct valid events and validation logic to ensure compliance with the defined schemas. + +## Purpose + +* **Centralized Definitions**: Maps application-specific event types (e.g., `VIDEO_POST`) to Nostr Kinds (e.g., `30078`) and required tags. +* **Event Builders**: Pure functions that take parameters and return a signed-ready event object (excluding the signature). +* **Validation**: Development-mode validation to catch malformed events before they are signed or published. +* **Sanitization**: Helpers to clean and normalize input data (e.g., UTF-8 enforcement, tag normalization). + +## Public API + +### Constants + +* `NOTE_TYPES`: An enum of all supported event types (e.g., `NOTE_TYPES.VIDEO_POST`, `NOTE_TYPES.ZAP_REQUEST`). +* `KIND_MUTE_LIST`: The kind number for NIP-51 Mute Lists (10000). + +### Builder Functions + +Each builder function accepts a parameters object and returns a standard Nostr event object: +`{ kind, pubkey, created_at, tags, content }`. + +Common parameters: +* `pubkey`: The hex public key of the author. +* `created_at`: Unix timestamp (in seconds). +* `additionalTags`: Array of extra tags to append (e.g., `[['t', 'hashtag']]`). + +**Key Builders:** + +* `buildVideoPostEvent(params)`: Constructs a Kind 30078 video metadata event. +* `buildVideoCommentEvent(params)`: Constructs a Kind 1111 comment event. +* `buildVideoReactionEvent(params)`: Constructs a Kind 7 reaction event. +* `buildRepostEvent(params)`: Constructs a Kind 6 (Repost) or Kind 16 (Generic Repost). +* `buildZapRequestEvent(params)`: Constructs a Kind 9734 zap request. +* `buildViewEvent(params)`: Constructs a view analytics event (Kind 30078 variant). + +### Schema & Validation + +* `getNostrEventSchema(type)`: Returns the schema configuration for a given `NOTE_TYPE`. +* `validateEventStructure(type, event)`: checks if an event object matches the schema for `type`. +* `setNostrEventSchemaOverrides(overrides)`: Allows runtime injection of schema overrides (useful for feature flags or testing). + +## Usage Example + +```javascript +import { NOTE_TYPES, buildVideoPostEvent } from './nostrEventSchemas.js'; + +const params = { + pubkey: '...hex_pubkey...', + created_at: Math.floor(Date.now() / 1000), + dTagValue: 'my-video-slug', + content: { + title: 'My Awesome Video', + videoRootId: 'my-video-slug', + url: 'https://example.com/video.mp4', + thumbnail: 'https://example.com/thumb.jpg' + }, + additionalTags: [['t', 'art']] +}; + +const event = buildVideoPostEvent(params); + +// Result: +// { +// kind: 30078, +// pubkey: '...', +// created_at: ..., +// tags: [['d', 'my-video-slug'], ['t', 'video'], ['t', 'art'], ['s', 'nostr:my-video-slug']], +// content: '{"title":"My Awesome Video",...}' +// } +``` + +## Internal Details + +### Schema Configuration +Each entry in `BASE_SCHEMAS` defines: +* `kind`: The Nostr event kind. +* `requiredTagNames`: List of tag names that must be present. +* `identifierTag`: Configuration for NIP-33 `d` tags. +* `appendTags`: Fixed tags to always include. +* `content`: Format specification (`json`, `text`, `empty`, etc.). + +### Sanitization +* `ensureValidUtf8Content`: Ensures the `content` field contains valid UTF-8, stripping invalid surrogates to prevent relay rejection. +* `sanitizeAdditionalTags`: Normalizes user-provided tags (trimming strings, handling pointers). diff --git a/dist/docs/perf-reports/weekly-perf-report-2026-02-13.md b/dist/docs/perf-reports/weekly-perf-report-2026-02-13.md new file mode 100644 index 00000000..d6710f52 --- /dev/null +++ b/dist/docs/perf-reports/weekly-perf-report-2026-02-13.md @@ -0,0 +1,50 @@ +# Weekly Performance Report — 2026-02-13 + +**Agent:** `bitvid-perf-deepdive-agent` +**Focus:** Relay Request Concurrency + +## Executive Summary + +Identified a critical performance risk in `js/nostr/reactionEvents.js` where reaction list and publish operations were using unbounded `Promise.all` across all configured relays. This could lead to network saturation, browser connection limits (blocking other requests), and potential timeouts for users with many relays. + +**Optimization:** Replaced `Promise.all` with `pMap` to enforce a concurrency limit of 3 (`RELAY_BACKGROUND_CONCURRENCY`), aligning with best practices established in `commentEvents.js`. + +## Methodology + +### Scenario +Simulate `listVideoReactions` call with a client configured for 20 relays. +- **Tools:** `benchmarks/reaction_loading_bench.mjs` (new harness) +- **Environment:** Node.js 22 (Agent Environment) +- **Metric:** Peak concurrent `pool.list()` calls. + +### Baseline Measurements + +| Metric | Value | Notes | +| :--- | :--- | :--- | +| Peak Concurrency | **20** | Unbounded; fired all requests simultaneously. | +| Total Time (Mock) | ~111ms | Limited only by the longest single request (mock delay 100ms). | + +### After Optimization + +| Metric | Value | Notes | +| :--- | :--- | :--- | +| Peak Concurrency | **3** | Capped by `RELAY_BACKGROUND_CONCURRENCY`. | +| Total Time (Mock) | ~705ms | Increased latency is expected and acceptable for background operations to preserve network health. | + +## Changes + +### 1. Concurrency Limiting in `js/nostr/reactionEvents.js` +- **Before:** `await Promise.all(relayList.map(...))` +- **After:** `await pMap(relayList, ..., { concurrency: 3 })` +- **Impact:** Prevents "thundering herd" of WebSocket requests. + +### 2. New Benchmark Harness +- Added `benchmarks/reaction_loading_bench.mjs` to reproducible measure concurrency of reaction loading. + +## Verification +- Unit tests (`tests/nostr/reaction-events.test.mjs`) passed. +- Benchmark confirmed concurrency cap is active. + +## Next Steps +- **Audit `js/nostr/viewEvents.js`**: Similar unbounded `Promise.all` pattern exists there and should be patched in a future run. +- **Audit `js/nostr/dmSignalEvents.js`**: Another potential candidate for concurrency limiting. diff --git a/dist/docs/playbackService-overview.md b/dist/docs/playbackService-overview.md new file mode 100644 index 00000000..4f7363da --- /dev/null +++ b/dist/docs/playbackService-overview.md @@ -0,0 +1,75 @@ +# PlaybackService Overview + +**File:** `js/services/playbackService.js` + +The `PlaybackService` is the central orchestrator for video playback in bitvid. It implements the "Hybrid Playback Strategy", ensuring users get the fastest possible start (via direct URL) while maintaining the censorship-resistance of P2P (via WebTorrent) as a seamless fallback. + +## Key Concepts + +### 1. Hybrid Playback Strategy +The goal is to play video from a standard HTTPS URL (CDN/Server) first because it offers instant seek and low latency. If that fails (404, blocked, or stalls), the system automatically switches to the WebTorrent engine to stream the content from the P2P network using the magnet link. + +### 2. PlaybackSession +Each attempt to play a video creates a `PlaybackSession`. This object encapsulates the state machine for that specific video request. +- **Request Signature:** A unique ID derived from the URL and Magnet. Used to prevent race conditions (e.g., user clicks the same video twice, or switches videos rapidly). +- **Watchdogs:** Event listeners attached to the `