import { style } from '@vanilla-extract/css'; import { color, config, toRem } from 'folds'; // ── Card root ──────────────────────────────────────────────────── // // Establishes the positioning context for the floating top-right // 3-dot actions trigger. The card content (hero / info / alerts) // sits in normal flow; the trigger is absolute-positioned over it. export const CardRoot = style({ position: 'relative', }); // 3-dot button anchor — top-right of the card. `right: 0` lines it // up flush with the card content's right edge (the panel padding // is supplied by the host, so we don't add inset here). export const CardActionsAnchor = style({ position: 'absolute', top: 0, right: 0, zIndex: 1, }); // ── Hero ───────────────────────────────────────────────────────── export const HeroRoot = style({ width: '100%', paddingTop: toRem(8), }); // 96px round avatar wrapper. Uses `position: relative` so the // online-dot can sit inset at bottom-right with a ring matching the // surrounding background. Folds' `Avatar` is overridden globally to // be circular, so we just size the wrapper and let the inner avatar // fill it. export const HeroAvatar = style({ position: 'relative', display: 'inline-flex', width: toRem(96), height: toRem(96), borderRadius: '50%', flexShrink: 0, // Hairline ring sits on top of the gradient so we get the same // «inset border» rhythm as the chat-list rows in the design bundle. boxShadow: `0 0 0 ${config.borderWidth.B600} ${color.Background.Container}`, }); // Linear gradient is set via inline style (so `colorMXID` can drive // the base hue). This rule just makes the fallback span fill the // avatar circle and centre its initial. export const HeroAvatarFallback = style({ width: '100%', height: '100%', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontWeight: 600, fontSize: toRem(36), letterSpacing: '0.5px', textTransform: 'uppercase', }); // Online indicator dot — green pill inset bottom-right of the // avatar with a ring matching the parent background so it punches // out against the avatar circle, mirroring the design bundle's // `.online` glyph. export const HeroOnlineDot = style({ position: 'absolute', right: toRem(2), bottom: toRem(2), width: toRem(16), height: toRem(16), borderRadius: '50%', backgroundColor: color.Success.Main, boxShadow: `0 0 0 ${toRem(3)} ${color.Background.Container}`, }); // Reset native button chrome for the tap-to-zoom avatar wrapper. export const HeroAvatarButton = style({ display: 'inline-flex', background: 'transparent', border: 'none', padding: 0, margin: 0, cursor: 'pointer', }); export const HeroIdentity = style({ width: '100%', }); export const HeroPresence = style({ width: '100%', justifyContent: 'center', }); // Coloured «онлайн» tag — mirrors the chat header's `OnlineTag` // style so the two surfaces use identical typography for the same // signal. Dropping the green dot prefix here is intentional: the // avatar already carries the green online indicator, so a second // dot would just be visual noise. export const HeroOnlineTag = style({ color: color.Success.Main, flexShrink: 0, whiteSpace: 'nowrap', }); export const HeroBullet = style({ color: color.Surface.ContainerLine, }); export const HeroE2ee = style({ display: 'inline-flex', alignItems: 'center', gap: toRem(4), color: color.Success.Main, }); // Quiet «last activity» dot prefix used only on offline / idle // presence labels. Online state has no dot here — the avatar's own // green indicator serves that role. export const PresenceDot = style({ width: toRem(8), height: toRem(8), borderRadius: '50%', backgroundColor: color.Surface.ContainerLine, flexShrink: 0, }); // ── Info section / rows (Fleet-style attribute table) ──────────── export const InfoSection = style({ display: 'flex', flexDirection: 'column', gap: toRem(2), paddingTop: toRem(8), paddingBottom: toRem(8), borderTop: `${config.borderWidth.B300} solid ${color.Surface.ContainerLine}`, borderBottom: `${config.borderWidth.B300} solid ${color.Surface.ContainerLine}`, }); export const InfoRow = style({ display: 'flex', alignItems: 'center', gap: toRem(12), minHeight: toRem(32), padding: `${toRem(4)} ${toRem(2)}`, }); // Fixed-width monospace label column. The `letter-spacing` and // uppercase mirror Fleet's panel-attribute look. export const InfoRowLabel = style({ flexShrink: 0, width: toRem(72), fontFamily: 'ui-monospace, "JetBrains Mono", monospace', fontSize: toRem(11), letterSpacing: '0.06em', textTransform: 'uppercase', color: color.Surface.OnContainer, opacity: 0.55, }); export const InfoRowValue = style({ flex: 1, minWidth: 0, fontSize: toRem(13), color: color.Surface.OnContainer, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', }); // Right-side affordance — copy / open / chevron-menu trigger. Looks // like a tiny ghost icon button: transparent, hover-tints in the // «hover surface» token, never grows past the row's intrinsic // height. export const InfoRowTrailing = style({ flexShrink: 0, display: 'inline-flex', alignItems: 'center', }); export const InfoRowAction = style({ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: toRem(24), height: toRem(24), borderRadius: toRem(6), background: 'transparent', border: 'none', color: color.Surface.OnContainer, cursor: 'pointer', opacity: 0.7, selectors: { '&:hover': { opacity: 1, background: color.SurfaceVariant.Container, }, '&:disabled': { opacity: 0.3, cursor: 'default', }, '&[aria-pressed="true"]': { opacity: 1, background: color.SurfaceVariant.Container, }, }, }); // Variants used inside `InfoRowValue` — accent for creators (star // badge), mixed (label + faint mono pl number side by side), mono // (just the pl-N tag). export const InfoRowAccent = style({ display: 'inline-flex', alignItems: 'center', gap: toRem(6), color: color.Warning.Main, fontWeight: 600, }); export const InfoRowMixed = style({ display: 'inline-flex', alignItems: 'center', gap: toRem(8), });