// Окружение редактора: топбар, сайдбар, таб-бар, статусбар, клавиши
// Global CTA target
const CTA_LINK = 'https://t.me/ai_mng';
const chromeStyles = {
topbar: {
position: 'sticky', top: 0, zIndex: 50,
display: 'flex', alignItems: 'center',
height: 42, padding: '0 14px',
background: 'rgba(10,10,11,0.85)',
backdropFilter: 'blur(14px)',
borderBottom: '1px solid var(--line)',
fontFamily: 'Manrope,sans-serif', fontSize: 12,
color: 'var(--fg-dim)',
gap: 16
},
dot: { width: 12, height: 12, borderRadius: '50%' },
menu: { display: 'flex', gap: 18, color: 'var(--fg-dim)' },
menuItem: { cursor: 'default', transition: 'color .15s' },
breadcrumb: {
display: 'flex', alignItems: 'center', gap: 8,
padding: '4px 12px', background: 'var(--bg-2)', borderRadius: 8,
border: '1px solid var(--line)',
color: 'var(--fg-dim)', fontSize: 11
}
};
// Встроенный аудио-плеер — маленькая кнопка с нотой
function MusicButton() {
const [playing, setPlaying] = React.useState(false);
const audioRef = React.useRef(null);
React.useEffect(() => {
const src = window.__AUDIO && window.__AUDIO.track || 'assets/track.mp3';
const a = new Audio(src);
a.loop = true;
a.volume = 0.7;
a.preload = 'auto';
audioRef.current = a;
const onEnd = () => setPlaying(false);
a.addEventListener('pause', () => setPlaying(false));
a.addEventListener('play', () => setPlaying(true));
return () => {
a.pause();
audioRef.current = null;
};
}, []);
const toggle = () => {
const a = audioRef.current;
if (!a) return;
if (a.paused) {
a.play().catch((err) => console.warn('audio play blocked:', err));
} else {
a.pause();
}
};
return (
);
}
// Навигация в стиле референса: логотип + меню + CTA (+ бургер на мобилке)
function NavBar() {
const [open, setOpen] = React.useState(false);
const menu = [
['Программа', 'modules'],
['Отзывы', 'testimonials'],
['Тарифы', 'pricing'],
['FAQ', 'faq']];
return (
<>
{/* Mobile menu overlay */}
{open &&
{menu.map(([m, id], i) =>
setOpen(false)}
style={{
padding: '18px 4px',
borderBottom: '1px solid var(--line)',
color: 'var(--fg)', textDecoration: 'none',
fontFamily: 'Manrope,sans-serif', fontSize: 22, fontWeight: 600,
display: 'flex', alignItems: 'center', justifyContent: 'space-between'
}}>
{m}
→
)}
}
>);
}
function TopBar() {
return (
ФайлПравкаВид
АгентТерминалПомощь
⌘K
Найти обучение автоматизациям…
vibecode://new-level
●agent: online
);
}
// 3D клавиша — pillow-keycap style (зеркалит клавиатуру в финальном CTA)
function Kbd({ children, small, size = 'md', wide, tone = 'default', onClick, href, style = {}, pressed = false, interactive = false }) {
const [hov, setHov] = React.useState(false);
const [prs, setPrs] = React.useState(false);
const dims = small ? { h: 22, px: 9, fs: 11, r: 7 } :
size === 'lg' ? { h: 44, px: 18, fs: 17, r: 12 } :
size === 'xl' ? { h: 58, px: 24, fs: 20, r: 16 } :
{ h: 32, px: 12, fs: 13, r: 9 };
const palettes = {
yellow: {
faceGrad: 'linear-gradient(180deg, #a08bff 0%, #7c5cff 55%, #6d4aff 100%)',
fg: '#ffffff',
shadow: 'rgba(40, 24, 110, 0.55)',
hi: 'rgba(255,255,210,0.7)',
innerBot: 'rgba(80,60,0,0.35)'
},
orange: {
faceGrad: 'linear-gradient(180deg, #a08bff 0%, #7c5cff 55%, #6d4aff 100%)',
fg: '#ffffff',
shadow: 'rgba(40, 24, 110, 0.55)',
hi: 'rgba(255,255,210,0.7)',
innerBot: 'rgba(80,60,0,0.35)'
},
blue: {
faceGrad: 'linear-gradient(180deg, #7be0a9 0%, #3ee08a 55%, #22c46d 100%)',
fg: '#0a1a10',
shadow: 'rgba(0,80,40,0.55)',
hi: 'rgba(190,210,255,0.55)',
innerBot: 'rgba(15,42,102,0.45)'
},
ghost: {
faceGrad: 'transparent',
fg: 'var(--fg)',
shadow: 'transparent',
hi: 'transparent',
innerBot: 'transparent',
border: '1px solid var(--line-2)'
},
default: {
faceGrad: 'linear-gradient(180deg, #2a2a32 0%, #1e1e23 55%, #141418 100%)',
fg: 'var(--fg)',
shadow: 'rgba(0,0,0,0.55)',
hi: 'rgba(255,255,255,0.08)',
innerBot: 'rgba(0,0,0,0.4)'
}
};
const pal = palettes[tone] || palettes.default;
const isInteractive = !!onClick || !!href || interactive;
const isPressed = pressed || prs;
const translate = isPressed ? 2 : 0;
const wrapStyle = {
position: 'relative',
display: 'inline-flex',
verticalAlign: 'middle',
height: dims.h,
minWidth: wide ? dims.h * 2.6 : dims.h,
cursor: isInteractive ? 'pointer' : 'default',
userSelect: 'none',
textDecoration: 'none',
...style
};
const shadowStyle = {
position: 'absolute',
left: '6%', right: '6%',
top: '55%', bottom: -dims.h * 0.22,
borderRadius: dims.r,
background: `radial-gradient(50% 60% at 50% 40%, ${pal.shadow} 0%, transparent 70%)`,
filter: 'blur(4px)',
opacity: isPressed ? 0.5 : hov ? 0.95 : 0.8,
transition: 'opacity .15s',
zIndex: 0,
pointerEvents: 'none'
};
const faceStyle = {
position: 'relative',
flex: 1,
height: dims.h,
padding: `0 ${dims.px}px`,
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
gap: 6,
background: pal.faceGrad,
border: pal.border,
borderRadius: dims.r,
boxShadow: pal.faceGrad === 'transparent' ? undefined : `
inset 0 1.5px 0 ${pal.hi},
inset 0 -2px 0 ${pal.innerBot},
inset 0 -6px 10px -4px ${pal.innerBot},
0 1px 0 rgba(255,255,255,0.12)
`,
color: pal.fg,
fontFamily: 'Manrope,sans-serif',
fontWeight: 600,
fontSize: dims.fs,
letterSpacing: '-0.005em',
whiteSpace: 'nowrap',
transform: `translateY(${translate}px)`,
transition: 'transform .12s',
zIndex: 2
};
const handlers = isInteractive ? {
onMouseEnter: () => setHov(true),
onMouseLeave: () => {setHov(false);setPrs(false);},
onMouseDown: () => setPrs(true),
onMouseUp: () => setPrs(false),
onClick
} : {};
const inner =
<>
{pal.faceGrad !== 'transparent' && }
{children}
>;
if (href) return {inner};
return {inner};
}
// CTA кнопка — pillow keycap (зеркалит клавиши клавиатуры)
function CTAButton({ children, tone = 'yellow', size = 'lg', icon = '', block = false, small = false, href = CTA_LINK, onClick, target = '_blank' }) {
const [h, sH] = React.useState(false);
const [p, sP] = React.useState(false);
const height = size === 'xl' ? 64 : size === 'lg' ? 54 : 42;
const radius = Math.round(height * 0.28);
const fontSize = size === 'xl' ? 20 : size === 'lg' ? 18 : 18;
const padding = size === 'xl' ? '0 44px' : size === 'lg' ? '0 36px' : '0 24px';
// pillow palettes
const palettes = {
yellow: {
faceGrad: 'linear-gradient(180deg, #a08bff 0%, #7c5cff 45%, #6d4aff 100%)',
fg: '#ffffff',
shadow: 'rgba(40, 24, 110, 0.6)',
glow: 'rgba(124, 92, 255, 0.55)',
hi: 'rgba(220,210,255,0.7)',
innerBot: 'rgba(40,24,110,0.35)'
},
blue: {
faceGrad: 'linear-gradient(180deg, #7be0a9 0%, #3ee08a 45%, #22c46d 100%)',
fg: '#0a1a10',
shadow: 'rgba(0,80,40,0.6)',
glow: 'rgba(59,130,246,0.4)',
hi: 'rgba(190,210,255,0.55)',
innerBot: 'rgba(15,42,102,0.45)'
},
light: {
faceGrad: 'linear-gradient(180deg, #ffffff 0%, #f6f4ee 55%, #ebe8df 100%)',
fg: '#1a1a1b',
shadow: 'rgba(80,75,65,0.35)',
glow: 'rgba(255,255,255,0.15)',
hi: 'rgba(255,255,255,0.95)',
innerBot: 'rgba(180,175,160,0.45)'
},
dark: {
faceGrad: 'linear-gradient(180deg, #2a2a32 0%, #1e1e23 55%, #141418 100%)',
fg: '#ededee',
shadow: 'rgba(0,0,0,0.55)',
glow: 'rgba(124, 92, 255,0.2)',
hi: 'rgba(255,255,255,0.08)',
innerBot: 'rgba(0,0,0,0.4)'
}
};
const pal = palettes[tone] || palettes.yellow;
const translate = p ? 3 : h ? -2 : 0;
const wrapperStyle = {
position: 'relative',
display: block ? 'block' : 'inline-block',
width: block ? '100%' : 'auto',
height,
cursor: 'pointer',
textDecoration: 'none',
userSelect: 'none',
verticalAlign: 'top'
};
// soft pillow shadow under the key
const shadowStyle = {
position: 'absolute',
left: height * 0.06,
right: height * 0.06,
top: height * 0.55,
bottom: -height * 0.22,
borderRadius: radius,
background: `radial-gradient(50% 60% at 50% 40%, ${pal.shadow} 0%, transparent 70%)`,
filter: h && !p ? 'blur(12px)' : 'blur(8px)',
opacity: p ? 0.4 : h ? 1 : 0.85,
transform: h && !p ? 'translateY(3px) scale(1.04)' : p ? 'translateY(-2px) scale(0.94)' : 'none',
transition: 'opacity .15s, transform .15s, filter .15s',
zIndex: 0,
pointerEvents: 'none'
};
// ambient glow behind (for accent tones)
const glowStyle = {
position: 'absolute',
inset: -8,
borderRadius: radius + 10,
boxShadow: `0 10px 40px ${pal.glow}`,
opacity: h || p ? 1 : 0.5,
pointerEvents: 'none',
zIndex: 0,
transition: 'opacity .25s'
};
// key face
const faceStyle = {
position: 'relative',
height,
width: block ? '100%' : 'auto',
borderRadius: radius,
background: pal.faceGrad,
boxShadow: `
inset 0 1.5px 0 ${pal.hi},
inset 0 -3px 0 ${pal.innerBot},
inset 0 -10px 14px -6px ${pal.innerBot},
0 1px 0 rgba(255,255,255,0.18)
`,
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
gap: 12,
padding,
fontFamily: 'Manrope,sans-serif',
fontWeight: 600,
fontSize,
color: pal.fg,
letterSpacing: '-0.005em',
transform: `translateY(${translate}px)`,
transition: 'transform .12s',
zIndex: 2,
whiteSpace: 'nowrap'
};
const handlers = {
onMouseEnter: () => sH(true),
onMouseLeave: () => {sH(false);sP(false);},
onMouseDown: () => sP(true),
onMouseUp: () => sP(false),
onTouchStart: () => sP(true),
onTouchEnd: () => sP(false)
};
const inner =
<>
{icon ? {icon} : null}
{children}
>;
if (href && !onClick) {
return (
{inner}
);
}
return (
);
}
// NavBar CTA — compact keycap
function NavCTA() {
return (
Записаться
);
}
// Окно редактора (карточка с tabs)
function EditorWindow({ tabs = ['index.vibe'], active = 0, children, style = {}, right = null }) {
return (
{tabs.map((t, i) =>
{t}
{i === active && }
)}
{right}
{children}
);
}
// Номера строк
function LineNumbers({ count = 20, start = 1, highlight = [] }) {
return (
{Array.from({ length: count }).map((_, i) =>
{String(start + i).padStart(2, '0')}
)}
);
}
// Секционный разделитель
function SectionLabel({ id, title, right }) {
return (
);
}
Object.assign(window, { TopBar, NavBar, Kbd, CTAButton, EditorWindow, LineNumbers, SectionLabel });