// Mobile members horseshoe — exact mirror of the 1:1 profile // horseshoe in `RoomViewProfilePanel.css.ts`. Same silhouette // wrapper geometry, same handle, same chatBody gap + radius // constants — both ends of the chat read with identical visual // language. Kept as a separate file (rather than reusing the profile // CSS) because the two horseshoes can diverge in the future without // rippling regressions through the other surface. import { style } from '@vanilla-extract/css'; import { color, toRem } from 'folds'; // Re-export the two horseshoe constants from the profile panel so any // future tweak (e.g. radius=28 trial) lives in one place. export { HORSESHOE_GAP_PX, HORSESHOE_RADIUS_PX } from './RoomViewProfilePanel.css'; // Re-export the avatar-full-view styles from the profile panel — both // the embedded user-profile branch (tap on member row → tap on avatar) // and the group-hero branch (tap on group avatar) use the same // silhouette-filling overlay treatment. Keeping the styles in one // place ensures the two surfaces stay visually identical. export { avatarFullView, avatarFullImage, avatarFullFallback } from './RoomViewProfilePanel.css'; export const container = style({ position: 'relative', display: 'flex', flex: 1, flexDirection: 'column', minWidth: 0, minHeight: 0, overflow: 'hidden', }); export const silhouette = style({ display: 'flex', flexDirection: 'column', flexShrink: 0, overflow: 'hidden', backgroundColor: color.Background.Container, willChange: 'border-bottom-left-radius, border-bottom-right-radius', }); export const panelViewport = style({ position: 'relative', width: '100%', overflow: 'hidden', willChange: 'height', touchAction: 'pan-y', userSelect: 'none', }); // Anchor at the top so the list slides downward as the viewport grows // — title strip + first group label become visible first. Mirrors the // profile panel's emerge direction. `padding-top: var(--vojo-safe-top)` // keeps the list clear of the Android status-bar icons in the open // state. export const panelContent = style({ position: 'absolute', top: 0, left: 0, right: 0, boxSizing: 'border-box', paddingTop: 'var(--vojo-safe-top, 0px)', }); export const panelInner = style({ display: 'flex', flexDirection: 'column', height: '100%', }); // Wrapper around the `MembersList` so the host can switch overflow on // when content exceeds the safety cap. Same idiom as `panelScroll` in // the profile panel — the list owns its own scroll, but at the rail // boundary the host still hides chrome and toggles overflow. export const panelScroll = style({ flex: 1, minHeight: 0, scrollbarWidth: 'none', selectors: { '&::-webkit-scrollbar': { display: 'none', }, }, }); export const panelHandle = style({ flexShrink: 0, height: toRem(20), display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'grab', touchAction: 'none', selectors: { '&:active': { cursor: 'grabbing' }, }, }); export const panelHandleBar = style({ width: toRem(36), height: toRem(4), borderRadius: toRem(4), backgroundColor: color.Surface.ContainerLine, }); export const headerViewport = style({ flexShrink: 0, overflow: 'hidden', willChange: 'height', userSelect: 'none', }); export const headerViewportInner = style({ boxSizing: 'border-box', minHeight: 0, overflow: 'hidden', paddingTop: 'var(--vojo-safe-top, 0px)', backgroundColor: color.SurfaceVariant.Container, }); export const chatBody = style({ display: 'flex', flex: 1, flexDirection: 'column', minWidth: 0, minHeight: 0, willChange: 'margin-top, border-top-left-radius, border-top-right-radius', });