import { style } from '@vanilla-extract/css'; import { toRem } from 'folds'; import { VOJO_HORSESHOE_VOID_COLOR, VOJO_HORSESHOE_GAP_PX, VOJO_HORSESHOE_RADIUS_PX, } from '../styles/horseshoe'; // Color of the «void» between the app shell and the bottom call rail — // shared across every horseshoe surface, see `styles/horseshoe.ts`. // Painted only when the call rail is mounted, so the rest of the app // keeps its normal page background. const SURFACE_GAP_COLOR = VOJO_HORSESHOE_VOID_COLOR; const HORSESHOE_RADIUS = toRem(VOJO_HORSESHOE_RADIUS_PX); const HORSESHOE_GAP = toRem(VOJO_HORSESHOE_GAP_PX); // Outer flex column hosting app shell + bottom call rail. // `min-height: 0` is required for nested scroll containers inside // ClientLayout to shrink correctly. export const surface = style({ display: 'flex', flexDirection: 'column', flex: 1, minWidth: 0, minHeight: 0, }); export const surfaceActive = style({ backgroundColor: SURFACE_GAP_COLOR, }); // === App shell === // // Wraps the whole client UI. Gets rounded BOTTOM corners + 8px // margin pushing the rest of the app up while a call surface is // active. export const appShell = style({ display: 'flex', flex: 1, minWidth: 0, minHeight: 0, }); export const appShellBottomRound = style({ borderBottomLeftRadius: HORSESHOE_RADIUS, borderBottomRightRadius: HORSESHOE_RADIUS, overflow: 'hidden', marginBottom: HORSESHOE_GAP, }); // === Bottom horseshoe (call rail) === // // Rounded *top* corners only because it sits flush against the // safe-area inset; the bottom is the screen edge. `position: relative` // carries the absolute-positioned orbit border. export const bottomRail = style({ display: 'flex', flexDirection: 'column', flexShrink: 0, }); export const bottomRailActive = style({ position: 'relative', borderTopLeftRadius: HORSESHOE_RADIUS, borderTopRightRadius: HORSESHOE_RADIUS, overflow: 'hidden', }); // Orbit border on the bottom rail — small green segment of a // conic-gradient that runs around the rail's perimeter when a ring is // incoming. The mask trick (content-box layer XOR full-box layer) // cuts the inner area, so only the 2px rim shows the gradient. The // angle is driven by `--vojo-orbit-angle`, registered via `@property` // in `src/index.css`. export const ringOrbit = style({ position: 'absolute', inset: 0, borderRadius: 'inherit', padding: toRem(2), pointerEvents: 'none', background: 'conic-gradient(from var(--vojo-orbit-angle, 0deg), transparent 0deg 280deg, rgba(91, 227, 197, 0.15) 300deg, #5BE3C5 335deg, rgba(91, 227, 197, 0.15) 350deg, transparent 360deg)', filter: 'drop-shadow(0 0 6px rgba(91, 227, 197, 0.55))', WebkitMask: 'linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0)', WebkitMaskComposite: 'xor', mask: 'linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0)', maskComposite: 'exclude', animationName: 'vojo-orbit-sweep', animationDuration: '1.8s', animationTimingFunction: 'linear', animationIterationCount: 'infinite', '@media': { '(prefers-reduced-motion: reduce)': { animation: 'none', }, }, });