// All selectors MUST be scoped under .scrying-pool. // Use --sp-* tokens only — no Foundry --color-* / --font-* / --border-* tokens allowed. // Implemented in story 1.5. // ============================================================ // CSS Custom Properties (State Tokens) // ============================================================ :root { --sp-state-active: hsl(140, 60%, 55%); --sp-state-hidden: hsl(0, 0%, 50%); --sp-state-self-muted: hsl(200, 60%, 55%); --sp-state-cam-lost: hsl(30, 80%, 55%); --sp-state-pending: hsl(50, 90%, 55%); --sp-urgency-director: hsl(38, 90%, 55%); --sp-state-color: hsl(140, 60%, 55%); // default, overridden per state } // ============================================================ // ScryingPoolStrip Layout // ============================================================ // Outer Foundry Application window (has .scrying-pool-strip via defaultOptions.classes). // Only visual appearance — sizing is controlled by JS setPosition(). // WARNING: do NOT add max-width or overflow here; the outer window also carries this class // and would clip the expanded inner content. .scrying-pool-strip { background: var(--sp-bg, hsl(220, 15%, 12%)); border-radius: 8px; // Hide Foundry's default window header — replaced by a lightweight in-content button. header.window-header { display: none; } // Remove window-content padding so the strip fills the frame edge-to-edge. .window-content { padding: 0; overflow: hidden; } } // Inner template div (has BOTH .scrying-pool AND .scrying-pool-strip). // Controls the expand/collapse behaviour; safe to use max-width + overflow here. .scrying-pool.scrying-pool-strip { position: relative; display: flex; flex-direction: column; align-items: center; max-width: 44px; overflow: hidden; transition: max-width 200ms ease-in-out; &.is-expanded { max-width: 240px; } } // ── Drag grip (top bar, replaces window header drag affordance) ──────────────── .sp-strip__grip { width: 100%; height: 16px; display: flex; align-items: center; justify-content: center; cursor: grab; color: var(--sp-text, hsl(0, 0%, 80%)); opacity: 0.35; font-size: 10px; flex-shrink: 0; transition: opacity 0.15s; user-select: none; &:hover { opacity: 0.75; } &:active { cursor: grabbing; opacity: 1; } } // ── Lightweight close button (replaces window header) ───────────────────────── .sp-strip__close-btn { position: absolute; top: 4px; right: 4px; z-index: 10; width: 18px; height: 18px; padding: 0; line-height: 18px; font-size: 13px; font-weight: 400; background: transparent; color: var(--sp-text, hsl(0, 0%, 80%)); border: none; border-radius: 3px; cursor: pointer; opacity: 0.45; transition: opacity 0.15s, background 0.15s; &:hover { opacity: 1; background: rgba(255, 255, 255, 0.1); } &:active { opacity: 0.75; } } // ── Toolbar row: toggle + Director's Board on the same line ─────────────────── .sp-strip__toolbar { display: flex; flex-direction: row; align-items: center; width: 100%; flex-shrink: 0; border-bottom: 1px solid rgba(255, 255, 255, 0.06); } .sp-strip__toggle { width: 44px; min-width: 44px; height: 28px; display: flex; align-items: center; justify-content: center; background: none; border: none; cursor: pointer; color: var(--sp-text, hsl(0, 0%, 80%)); font-size: 11px; flex-shrink: 0; opacity: 0.6; &:hover { opacity: 1; } } // ── Director's Board CTA button (shown when sidebar injection not available) ── .sp-strip__directors-board-cta { flex: 1; min-width: 0; height: 28px; display: flex; align-items: center; gap: 6px; padding: 0 8px; background: none; border: none; border-left: 1px solid rgba(255, 255, 255, 0.06); cursor: pointer; color: var(--sp-text-secondary, #7a8390); font-size: 11px; text-align: left; transition: background 0.15s, color 0.15s; flex-shrink: 0; i { font-size: 12px; flex-shrink: 0; } span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } &:hover { background: rgba(255, 255, 255, 0.06); color: var(--sp-text-primary, #dde2e8); } } .sp-strip__participants { list-style: none; margin: 0; padding: 0; width: 100%; display: flex; flex-direction: column; gap: 4px; } .sp-strip__first-tip { font-size: 0.75rem; color: var(--sp-text-muted, hsl(0, 0%, 60%)); padding: 4px 8px; margin: 0; } // ============================================================ // ParticipantAvatar (44×44px container, 32px rounded image) // ============================================================ .sp-participant-avatar { position: relative; width: 44px; height: 44px; display: flex; align-items: center; justify-content: flex-start; background: none; border: none; cursor: pointer; padding: 6px; border-radius: 4px; gap: 8px; overflow: hidden; &:focus-visible { outline: 2px solid var(--sp-focus-ring, hsl(200, 80%, 60%)); outline-offset: 2px; } .is-expanded & { width: 100%; height: 135px; // 16:9 at 240 px strip width padding: 0; align-items: flex-end; background: hsl(220, 15%, 14%); // Gradient scrim so name text is legible over any video &::after { content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 48px; background: linear-gradient(transparent, hsla(0, 0%, 0%, 0.72)); z-index: 2; pointer-events: none; border-radius: 0 0 4px 4px; } .sp-avatar__img { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1; width: 48px; height: 48px; } .sp-avatar__name { position: absolute; bottom: 20px; left: 8px; right: 8px; z-index: 3; color: hsl(0, 0%, 95%); } .sp-avatar__state-label { position: absolute; bottom: 6px; left: 8px; z-index: 3; color: hsla(0, 0%, 85%, 0.85); } .sp-avatar__corner-badge { bottom: 6px; right: 6px; z-index: 4; width: 10px; height: 10px; } } } // Video container for WebRTC stream (full AV replacement mode) .sp-participant-video { position: absolute; inset: 0; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; overflow: hidden; z-index: 1; } .sp-avatar__img { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; flex-shrink: 0; } // Video element styling .sp-participant-video__element { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; background: hsl(220, 15%, 18%); .is-expanded & { border-radius: 4px; } } // Hide avatar image when video stream is active (has video element) .sp-participant-video:not(:empty) ~ .sp-avatar__img { display: none; } .sp-avatar__corner-badge { position: absolute; bottom: 2px; right: 2px; width: 12px; height: 12px; border-radius: 50%; background: var(--sp-state-color); font-size: 8px; display: flex; align-items: center; justify-content: center; } .sp-avatar__name { font-size: 0.85rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--sp-text, hsl(0, 0%, 85%)); } .sp-avatar__state-label { font-size: 0.7rem; color: var(--sp-text-muted, hsl(0, 0%, 60%)); } // ============================================================ // StateRing variants (applied as class on .sp-participant-avatar) // ============================================================ .sp-participant-avatar.sp-state-active, .sp-participant-avatar.sp-state-self-muted { --sp-state-color: var(--sp-state-active); .sp-avatar__img { box-shadow: 0 0 0 2px var(--sp-state-color); } } .sp-participant-avatar.sp-state-hidden, .sp-participant-avatar.sp-state-cam-lost { --sp-state-color: var(--sp-state-hidden); .sp-avatar__img { outline: 2px dashed var(--sp-state-color); outline-offset: 2px; } } .sp-participant-avatar.sp-state-pending { --sp-state-color: var(--sp-state-pending); .sp-avatar__img { box-shadow: 0 0 0 2px var(--sp-state-color); } } // ============================================================ // StateRing animations — gated under no-preference (AC-16) // ============================================================ @media (prefers-reduced-motion: no-preference) { .sp-participant-avatar.sp-state-pending .sp-avatar__img { animation: sp-pulse 2s ease-in-out infinite; } @keyframes sp-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } // Revert flash (200ms amber, then restore) .sp-participant-avatar.sp-state-revert .sp-avatar__img { animation: sp-revert-flash 200ms ease-out forwards; } @keyframes sp-revert-flash { 0% { box-shadow: 0 0 0 3px var(--sp-urgency-director); } 100% { box-shadow: 0 0 0 2px var(--sp-state-color); } } } // ============================================================ // EmptyStatePanel (AC-11) // ============================================================ .sp-strip__empty-state { display: flex; flex-direction: column; align-items: center; padding: 16px 8px; gap: 8px; color: var(--sp-text-muted, hsl(0, 0%, 60%)); } .sp-empty__icon { font-size: 1.5rem; display: block; } .sp-empty__text { font-size: 0.75rem; text-align: center; } @media (prefers-reduced-motion: no-preference) { .sp-empty__icon { animation: sp-breathe 3s ease-in-out infinite; } @keyframes sp-breathe { 0%, 100% { opacity: 0.6; transform: scale(1); } 50% { opacity: 1.0; transform: scale(1.05); } } } // ============================================================ // AV Tile overlays (applied to .camera-view[data-user-id="..."]) // ============================================================ .sp-lock-overlay { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: hsla(0, 0%, 0%, 0.45); pointer-events: none; z-index: 10; &::before { content: '\f023'; // fa-lock font-family: 'Font Awesome 6 Free'; font-weight: 900; font-size: 1.2rem; color: hsl(0, 0%, 85%); } } .camera-view.sp-state-hidden { opacity: 0.55; position: relative; } .sp-portrait-fallback { position: absolute; inset: 0; background: var(--sp-bg, hsl(220, 15%, 18%)) center/cover no-repeat; pointer-events: none; } // ============================================================ // Context menu // ============================================================ .sp-context-menu { background: var(--sp-bg, hsl(220, 15%, 15%)); border: 1px solid hsl(0, 0%, 30%); border-radius: 4px; padding: 4px 0; min-width: 160px; z-index: 1000; box-shadow: 0 4px 12px hsla(0, 0%, 0%, 0.4); .sp-context-menu__item { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 12px; background: none; border: none; cursor: pointer; color: var(--sp-text, hsl(0, 0%, 85%)); font-size: 0.875rem; text-align: left; &:hover, &:focus-visible { background: hsla(200, 60%, 55%, 0.15); } } } // ============================================================ // ActionPopover () // ============================================================ .sp-action-popover { background: var(--sp-bg, hsl(220, 15%, 15%)); border: 1px solid hsl(0, 0%, 30%); border-radius: 6px; padding: 12px; min-width: 160px; box-shadow: 0 4px 16px hsla(0, 0%, 0%, 0.5); color: var(--sp-text, hsl(0, 0%, 85%)); .sp-action-popover__cta { display: block; width: 100%; padding: 8px 16px; background: hsl(200, 60%, 40%); border: none; border-radius: 4px; cursor: pointer; color: hsl(0, 0%, 95%); font-size: 0.875rem; &:hover:not(:disabled) { background: hsl(200, 60%, 50%); } &:disabled { opacity: 0.45; cursor: not-allowed; } } }