import { style } from '@vanilla-extract/css'; import { DefaultReset, color, toRem } from 'folds'; // BotShell is the bot-page container: it OWNS the hero and the iframe // mount. Standard Cinny `RoomViewHeader` is intentionally absent here — // the BotsDesktop mockup (stream-v2-dawn.jsx:660-672) places the hero as // the first row of the bot panel, with no chat-style chrome above. // Shell bg = SurfaceVariant.Container (#181a20) — matches DAWN.bg. The widget // iframe body uses the same #181a20 (apps/widget-telegram/src/styles.css:8). // Mockup canon paints the bot panel on DAWN.bg sitting on a DAWN.bg2 // (#0d0e11) parent — Background.Container would invert that and produce a // visible seam at the iframe's top edge. SurfaceVariant.Container keeps the // hero and the iframe body on the same tone. export const Shell = style([ DefaultReset, { width: '100%', height: '100%', display: 'flex', flexDirection: 'column', backgroundColor: color.SurfaceVariant.Container, overflow: 'hidden', // Native safe-top: `#root` no longer reserves the status-bar inset // (src/index.css), so BotShell extends to the screen top. The // padding keeps the Hero (avatar + title + actions) clear of the // system icons. Shell bg already matches the widget body tone, so // the padding zone reads as a continuation of the bot surface. On // web `--vojo-safe-top` is 0. // // Bottom inset is intentionally NOT added here: the iframe inside // `Frame` paints its own body bg (#181a20, see widget-telegram // styles.css) and the widget is responsible for the gesture-pill // clearance of its own action rows. Padding Shell here exposed a // visible seam between the iframe area and the env-bottom-tall // strip below it on Android. paddingTop: 'var(--vojo-safe-top, 0px)', }, ]); export const Frame = style([ DefaultReset, { flex: 1, minHeight: 0, position: 'relative', }, ]); // Hero outer band — full-width strip carrying the border-bottom that // separates the hero from the iframe body. Vertical padding only; the // horizontal padding sits on `HeroInner` so the inner content can be // constrained to the same `max-width: 960px` the widget body uses // (apps/widget-telegram/src/styles.css:64-72), keeping the host hero's // left/right edges aligned with the body content visible inside the // iframe. export const Hero = style([ DefaultReset, { borderBottom: `1px solid ${color.Background.ContainerLine}`, flexShrink: 0, padding: `${toRem(36)} 0 ${toRem(28)}`, '@media': { // Compact mobile band — matches the visual height of Cinny's standard // chat header (~56-64px), per user's request «хедер по разумеру как // чат обычный». The big desktop hero (avatar 56 + 2-line title + // multi-line description) is too heavy on a narrow viewport. '(max-width: 600px)': { padding: `${toRem(8)} 0`, }, }, }, ]); // Inner row — constrained to 960px to match the widget body. Horizontal // padding lives here. flex row carries the back-chevron / avatar / body / // settings-button stack. export const HeroInner = style([ DefaultReset, { maxWidth: toRem(960), margin: '0 auto', padding: `0 ${toRem(40)}`, display: 'flex', alignItems: 'flex-start', gap: toRem(18), '@media': { '(max-width: 600px)': { padding: `0 ${toRem(12)}`, gap: toRem(10), // Single row on mobile — no wrap. Avatar + name + settings fit // as a chat-header-like strip; handle and description are hidden // by their own media blocks below. alignItems: 'center', }, }, }, ]); // Mobile-only back chevron that lives at the start of the hero row. The // hero re-orders to flex-start on mobile (hero already wraps via the // max-width:600px media block), so the chevron leads the row rather than // stacking awkwardly with the avatar. export const HeroBack = style([ DefaultReset, { flexShrink: 0, alignSelf: 'center', }, ]); // 56×56 circular avatar, fleet violet (DAWN.fleet) bg. Fleet color is // hardcoded here because it's the canonical bot accent in the mockup and we // don't want it varying with Folds palette swaps. The violet disk shows when // the bot's Matrix profile has no `avatar_url` (fallback to the initial // letter); when it does, the inner covers the violet — `overflow: // hidden` keeps it inside the round mask. export const HeroAvatar = style([ DefaultReset, { width: toRem(56), height: toRem(56), borderRadius: '50%', backgroundColor: '#9580ff', color: '#0c0c0e', fontSize: toRem(24), fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, overflow: 'hidden', '@media': { '(max-width: 600px)': { width: toRem(36), height: toRem(36), fontSize: toRem(16), }, }, }, ]); // Avatar image fills the violet square. `objectFit: cover` plus the // container's `overflow: hidden` means non-square Matrix avatars (which // arbitrarily sized) render correctly without showing the violet bg // around the corners. export const HeroAvatarImg = style([ DefaultReset, { width: '100%', height: '100%', objectFit: 'cover', display: 'block', }, ]); export const HeroBody = style([ DefaultReset, { flex: 1, minWidth: 0, }, ]); export const HeroTitleRow = style([ DefaultReset, { display: 'flex', alignItems: 'baseline', gap: toRem(10), marginBottom: toRem(4), flexWrap: 'wrap', '@media': { '(max-width: 600px)': { marginBottom: 0, }, }, }, ]); export const HeroName = style([ DefaultReset, { fontSize: toRem(22), fontWeight: 700, color: color.Surface.OnContainer, '@media': { '(max-width: 600px)': { // Single-line truncated name — chat-header style. fontSize: toRem(16), overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'block', maxWidth: '100%', }, }, }, ]); // Handle is desktop-only — mxid is rarely useful on phone where you can't // easily copy it anyway, and the chat-header-style mobile band only has // room for one row of metadata under the name (the short description, // not the mxid). export const HeroHandle = style([ DefaultReset, { fontSize: toRem(13), color: color.SurfaceVariant.OnContainer, fontFamily: 'ui-monospace, "JetBrains Mono", "SF Mono", monospace', wordBreak: 'break-all', opacity: 0.6, '@media': { '(max-width: 600px)': { display: 'none', }, }, }, ]); // Desktop description: full sentence(s), wraps freely up to 560px. Hidden // on mobile in favor of the single-line `HeroDescriptionShort` below — the // long copy doesn't fit in a chat-header band without pushing the avatar // row out of vertical alignment. export const HeroDescription = style([ DefaultReset, { fontSize: toRem(14), // toRem keeps line-height in lockstep with font-size when the user // scales root size or zooms; mixing raw px would break the 1.43 ratio. lineHeight: toRem(20), color: color.SurfaceVariant.OnContainer, maxWidth: toRem(560), '@media': { '(max-width: 600px)': { display: 'none', }, }, }, ]); // Mobile-only one-liner description — sits directly under the name in the // chat-header-style band. Truncated with ellipsis so a long short-desc // from /config.json doesn't break the header height. Desktop hides this // since the full-length `HeroDescription` carries the same content with // more room. export const HeroDescriptionShort = style([ DefaultReset, { display: 'none', '@media': { '(max-width: 600px)': { display: 'block', fontSize: toRem(12), lineHeight: toRem(16), color: color.SurfaceVariant.OnContainer, opacity: 0.75, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '100%', marginTop: toRem(2), }, }, }, ]); // «О боте» button moved INSIDE the widget body (next to login/refresh/ // logout cards). The hero only keeps the standard three-dots IconButton // as a trailing action — its styling comes from folds, no host-side CSS // needed.