This commit is contained in:
2024-09-24 14:09:02 +05:00
parent 824be0bbd1
commit 299297496a
25 changed files with 356 additions and 368 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.
+9 -14
View File
@@ -1,18 +1,13 @@
import { clients } from '../consts/clients';
import { IClient } from '../types/IClient';
export function Clients() {
return (
<div
itemScope
itemType="http://schema.org/Clients"
className="select-none grid grid-rows-[repeat(3,124px)] gap-y-[clamp(20px,2vw,32px)] pb-16 border-b border-[#3D425C] sm:-mx-6 -mx-4"
>
<div className="flex w-screen overflow-hidden">
<div className="space-y-8 select-none">
<div className="flex items-center overflow-hidden w-screen lg:-mx-10 -mx-6 mt-10 min-h-[117px]">
<MarqueeHalf items={clients.slice(0, clients.length / 3)} />
<MarqueeHalf items={clients.slice(0, clients.length / 3)} />
</div>
<div className="flex w-screen overflow-hidden">
<div className="flex items-center overflow-hidden w-screen lg:-mx-10 -mx-6 min-h-[117px]">
<MarqueeHalf
reversed
items={clients.slice(clients.length / 3, (2 * clients.length) / 3)}
@@ -22,7 +17,7 @@ export function Clients() {
items={clients.slice(clients.length / 3, (2 * clients.length) / 3)}
/>
</div>
<div className="flex w-screen overflow-hidden">
<div className="border-b border-[#3D425C] flex items-center overflow-hidden w-screen lg:-mx-10 -mx-6 min-h-[117px] pb-16">
<MarqueeHalf items={clients.slice(2 * (clients.length / 3))} />
<MarqueeHalf items={clients.slice(2 * (clients.length / 3))} />
</div>
@@ -34,7 +29,7 @@ function MarqueeHalf({
reversed = false,
items,
}: {
items: IClient[];
items: { src: string }[];
reversed?: boolean;
}) {
return (
@@ -48,14 +43,14 @@ function MarqueeHalf({
>
{items.map(client => (
<div
itemProp={client.title}
key={client.src}
className="border-l border-[#3D425C] w-[clamp(200px,19.5vw,312px)] flex justify-center items-center"
className="border-l border-[#3D425C] w-[312px] h-[124px] flex justify-center items-center relative"
>
<img
key={client.src}
src={client.src}
alt={client.title}
className="pointer-events-none select-none"
alt={client.src}
className="!relative"
/>
</div>
))}
+2 -2
View File
@@ -18,9 +18,9 @@ export function Contacts() {
className="lg:px-6 px-4 sm:grid lg:grid-cols-12 sm:grid-cols-2 lg:gap-x-4 sm:gap-x-14 lg:gap-y-[68px] pb-20 pt-[70px]"
>
<h2 className="font-medium lg:col-span-7 sm:col-span-full h2 max-lg:mb-6">
Хотите использовать интерактивные тренажеры в обучении?
Хотите интерактивное решение для выставки?
<br />
<span className="text-gradient">Давайте обсудим детали.</span>
<span className="text-gradient">Давайте обсудим детали</span>
</h2>
<Button
color="primary"
+37 -29
View File
@@ -9,7 +9,10 @@ import { ChevronDownIcon } from './icons/ChevronDownIcon';
export function Devices() {
return (
<div id="devices" className="space-y-8 sm:space-y-10 lg:space-y-20">
<div
id="devices"
className="pt-12 space-y-8 lg:pt-16 sm:space-y-10 lg:space-y-20"
>
<Title>
Работаем с
<span className="text-gradient"> любыми типами оборудования</span>
@@ -61,29 +64,32 @@ function DesktopDevice({
>
<div className="space-y-6">
<p className="font-medium h3">{title}</p>
{hovered && (
<motion.div
ref={descriptionRef}
initial={{ opacity: 0 }}
animate={{ opacity: 0.6 }}
transition={{ delay: 0.5 }}
className="l-text space-y-4 max-w-[calc(600/1552*100%)] absolute"
>
{description.map(paragraph => (
<p>
{paragraph}
<br />
</p>
))}
</motion.div>
)}
<AnimatePresence>
{hovered && (
<motion.div
ref={descriptionRef}
initial={{ opacity: 0 }}
animate={{ opacity: 0.6 }}
exit={{ opacity: 0 }}
transition={{ delay: 0.3 }}
className="l-text space-y-4 max-w-[calc(600/1552*100%)] absolute"
>
{description.map(paragraph => (
<p key={paragraph}>
{paragraph}
<br />
</p>
))}
</motion.div>
)}
</AnimatePresence>
</div>
<AnimatePresence>
{hovered && (
<motion.img
src={img}
alt={title}
className="absolute bottom-0 right-[calc(144/1552*100%)] w-[calc(560/1552*100%)]"
className="bottom-0 right-[calc(144/1552*100%)] w-[calc(560/1552*100%)] absolute"
initial={{ opacity: 0, y: 500 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 100 }}
@@ -115,7 +121,7 @@ function Device({ title, description, img }: IDevice) {
setDescriptionHeight(descriptionRef.current?.clientHeight ?? 0);
}, [descriptionRef, expanded]);
useOnClickOutside(root, () => setExpanded(false));
useOnClickOutside(root, () => setExpanded(false), 'mouseup');
return (
<motion.div
@@ -127,10 +133,10 @@ function Device({ title, description, img }: IDevice) {
56
: 72,
}}
transition={{ duration: 0.4 }}
transition={{ duration: 0.3 }}
ref={root}
onClick={() => setExpanded(prev => !prev)}
className="py-4 space-y-6 border-t last:border-b border-[#3D425C]"
className="py-4 space-y-6 border-t last:border-b border-[#3D425C] relative select-none"
>
<div className="flex items-center justify-between py-2">
<p className="font-medium h3">{title}</p>
@@ -141,12 +147,14 @@ function Device({ title, description, img }: IDevice) {
<motion.p
ref={descriptionRef}
initial={{ opacity: 0 }}
animate={{ opacity: +expanded, transition: { delay: 0.4 } }}
exit={{ opacity: 0, transition: { delay: 0 } }}
transition={{ duration: 0.4 }}
className="mb-4 text-sm"
animate={{ opacity: +expanded, transition: { delay: 0.3 } }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
className="mb-4 space-y-4 h4"
>
{description}
{description.map(paragraph => (
<p key={paragraph}>{paragraph}</p>
))}
</motion.p>
)}
</AnimatePresence>
@@ -155,9 +163,9 @@ function Device({ title, description, img }: IDevice) {
<motion.img
ref={imgRef}
initial={{ opacity: 0 }}
animate={{ opacity: +expanded, transition: { delay: 0.4 } }}
exit={{ opacity: 0, transition: { delay: 0 } }}
transition={{ duration: 0.4 }}
animate={{ opacity: +expanded, transition: { delay: 0.3 } }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
src={img}
alt={title}
/>
+1 -1
View File
@@ -3,7 +3,7 @@ import { FeedbackForm } from './Layout/FeedbackForm';
export function Form() {
return (
<div className="py-6 space-y-8 lg:space-y-20 sm:space-y-10">
<div className="pt-12 space-y-8 lg:space-y-20 sm:space-y-10 lg:pt-16">
<Title className="lg:max-w-[58vw]">
Хотите интерактивное решение для выставки?
<br className="max-lg:hidden" />
+22 -5
View File
@@ -1,16 +1,18 @@
import { useEffect, useRef } from 'react';
import { interactions } from '../consts/interactions';
import { Title } from './ui/Title';
import { useInView } from 'framer-motion';
export function Interactions() {
return (
<div className="items-start max-sm:space-y-8 lg:flex sm:max-lg:space-y-10 gap-x-4">
<Title className="lg:sticky top-10 max-lg:hidden max-w-[50vw]">
<div className="items-start pt-12 lg:pt-16 max-sm:space-y-8 lg:flex sm:max-lg:space-y-10 gap-x-4">
<Title className="lg:sticky top-16 max-lg:hidden max-w-[50vw]">
С помощью интерактивных инструментов создаем систему{' '}
<span className="text-gradient">
эффективного взаимодействия с&nbsp;клиентами
</span>
</Title>
<Title className="lg:sticky top-10 lg:hidden">
<Title className="lg:hidden">
Создаем систему для
<span className="text-gradient">
{' '}
@@ -19,18 +21,33 @@ export function Interactions() {
с&nbsp;помощью интерактивных инструментов и&nbsp;анализа
пользовательского опыта
</Title>
<div className="space-y-4">{interactions.map(Interaction)}</div>
<div className="space-y-4">
{interactions.map(interaction => (
<Interaction key={interaction.title} {...interaction} />
))}
</div>
</div>
);
}
function Interaction({ title, video }: { title: string; video: string }) {
const videoRef = useRef<HTMLVideoElement>(null);
const inView = useInView(videoRef, { amount: 'all' });
useEffect(() => {
if (!videoRef.current) return;
if (inView) videoRef.current.play();
else videoRef.current.pause();
}, [inView]);
return (
<div className="sm:p-8 p-3 border border-[#3D425C] bg-[#14161F]">
<div className="sm:p-8 p-3 border border-[#3D425C] bg-[#14161F] overflow-clip space-y-4">
<p className="font-medium leading-none text-[clamp(20px,20px+(100vw-360px)/1240*12,32px)]">
{title}
</p>
<video
ref={videoRef}
src={video}
muted
autoPlay
+5 -1
View File
@@ -9,6 +9,7 @@ import { Country } from 'react-phone-number-input';
import { api } from '../../api';
import { getExampleNumber } from 'libphonenumber-js';
import examples from 'libphonenumber-js/mobile/examples';
import { Link } from 'react-router-dom';
export function FeedbackForm({
inModal = false,
@@ -203,7 +204,10 @@ export function FeedbackForm({
*нажимая кнопку отправить, вы принимаете
<span className="text-gradient">
{' '}
условия использования и политику конфиденциальности
условия использования и&nbsp;
<Link to="https://graff.tech/privacypolicy">
политику конфиденциальности
</Link>
</span>
</p>
</div>
+6 -6
View File
@@ -1,4 +1,4 @@
import { Logo } from '../icons/Logo';
import { LogoIcon } from '../icons/LogoIcon';
import { Link } from 'react-router-dom';
export function Footer() {
@@ -6,10 +6,10 @@ export function Footer() {
<footer className="sm:grid xl:grid-cols-[2fr_1fr_1fr] sm:grid-cols-2 sm:max-xl:grid-rows-2 bg-[#14161F]">
<div className="flex sm:items-center max-sm:flex-col sm:px-6 px-4 sm:py-9 py-4 border-t border-[#3D425C] gap-6 sm:max-xl:row-start-1 sm:max-xl:col-span-2">
<Link to={'/'}>
<Logo />
<LogoIcon />
</Link>
<div className="flex flex-col gap-y-1">
<div className="flex gap-x-4">
<div className="flex gap-x-4 m-text">
<Link
to="https://graff.tech/privacypolicy"
className="flex gap-4 sm:font-medium m-text"
@@ -28,7 +28,7 @@ export function Footer() {
<Contact type="email" text="info@graff.tech" />
<Contact type="phone" text="8 800 770 00 67" />
</div>
<div className="font-medium p-[14px] border border-[#3D425C] rounded-full m-text">
<div className="font-medium p-[14px] border border-[#3D425C] rounded-full h4">
RU
</div>
</div>
@@ -37,7 +37,7 @@ export function Footer() {
<Contact type="email" text="sam@graff.tech" />
<Contact type="phone" text="+971 58 506 0097" />
</div>
<div className="font-medium py-[14px] px-[10px] border border-[#3D425C] rounded-full m-text">
<div className="font-medium py-[14px] px-[10px] border border-[#3D425C] rounded-full h4">
UAE
</div>
</div>
@@ -60,7 +60,7 @@ function Contact({
to={
type === 'email' ? `mailto:${text}` : `tel:${text.replace(' ', '')}`
}
className={'m-text ' + className}
className={'l-text ' + className}
>
{text}
</Link>
+7 -4
View File
@@ -1,5 +1,5 @@
import { Link } from 'react-router-dom';
import { Logo } from '../icons/Logo';
import { LogoIcon } from '../icons/LogoIcon';
import { Button } from '../ui/Button';
import { ClassNameWrapper } from '../../hocs/ClassNameWrapper';
import { useModalStore } from '../../stores/modalStore';
@@ -10,7 +10,7 @@ import { BurgerIcon } from '../icons/BurgerIcon';
import { CloseIcon } from '../icons/CloseIcon';
import { useOnClickOutside } from 'usehooks-ts';
import { useLanguageStore } from '../../stores/languageStore';
import { LogoWithoutText } from '../icons/LogoWithoutText';
import { LogoWithoutText } from '../icons/LogoWithoutTextIcon';
export function Header() {
const { setModal } = useModalStore();
@@ -26,7 +26,10 @@ export function Header() {
return (
<header className="sticky top-0 lg:px-6 px-4 flex max-lg:justify-between items-center lg:h-16 h-12 border-b border-[#3D425C] bg-[#14161F] z-50">
<Link to={'/'} className="max-sm:hidden">
<ClassNameWrapper element={<Logo />} className="h-8 lg:h-10" />
<ClassNameWrapper
element={<LogoIcon />}
className="h-8 lg:h-10 lg:w-[113px] w-[93px]"
/>
</Link>
<Link to={'/'} className="sm:hidden">
<ClassNameWrapper element={<LogoWithoutText />} className="h-8" />
@@ -101,7 +104,7 @@ export function Header() {
function HashLink({ path, text }: { path: string; text: string }) {
return (
<Link
className="border-l lg:last:border-r border-[#3D425C] px-10 self-stretch font-medium lg:text-[#9299BD] content-center hover:bg-[#3D425C] max-lg:flex max-lg:py-6 btn-text bg-[#14161F] w-full max-lg:[&:not(:last-child)]:border-b"
className="btn-text border-l lg:last:border-r border-[#3D425C] px-10 self-stretch font-medium lg:text-[#9299BD] content-center hover:bg-[#3D425C] max-lg:flex max-lg:py-6 btn-text bg-[#14161F] w-full max-lg:[&:not(:last-child)]:border-b"
to={path}
onClick={() => {
document
+1 -1
View File
@@ -5,7 +5,7 @@ export function ModalContainer() {
return (
modal && (
<div className="fixed top-0 left-0 z-50 w-full h-full flex justify-center items-center bg-black bg-opacity-40 transition-opacity">
<div className="fixed top-0 left-0 z-50 w-full h-full flex justify-center items-center bg-black [backdrop-filter:blur(10px);] bg-opacity-90 transition-opacity">
<div onClick={e => e.stopPropagation()} className="cursor-default">
{modal}
</div>
+28
View File
@@ -0,0 +1,28 @@
import { CloseIcon } from '../../components/icons/CloseIcon';
import { useModalStore } from '../../stores/modalStore';
interface VideoModalProps {
link: string;
}
export function VideoModal({ link }: VideoModalProps) {
const { setModal } = useModalStore();
const handleOnCloseClick = () => {
setModal(null);
};
return (
<div className="w-screen h-dvh absolute z-[110] top-0 left-0 overflow-hidden flex justify-center items-center">
<div className="flex items-center justify-center w-full aspect-video">
<button
className="p-4 rounded-full border absolute top-4 right-4 z-[100] cursor-pointer"
onClick={handleOnCloseClick}
>
<CloseIcon />
</button>
<video src={link} className="h-full" autoPlay muted loop playsInline />
</div>
</div>
);
}
+10 -12
View File
@@ -11,23 +11,21 @@ export function Motivation() {
</h1>
<div className="flex flex-wrap sm:gap-x-5 gap-x-2 gap-y-4">
{[
{ solution: 'VR и AR', count: 12 },
{ solution: 'Интерактивные приложения', count: 21 },
{ solution: '3D макеты', count: 14 },
].map(({ count, solution }, index) => (
{
solution: 'Интерактивные приложения',
count: 21,
to: 'interactive',
},
{ solution: '3D макеты', count: 14, to: '3d_makets' },
{ solution: 'VR и AR', count: 12, to: 'vr_ar' },
].map(({ count, solution, to }, index) => (
<Link
key={solution}
className="h4 font-medium flex gap-x-[7px]"
to={`#${index === 0 ? 'vr_ar' : index == 1 ? 'interactive' : '3d_makets'}`}
to={`#${to}`}
onClick={() =>
document
.getElementById(
index === 0
? 'vr_ar'
: index == 1
? 'interactive'
: '3d_makets',
)
.getElementById(to)
?.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
>
+3 -3
View File
@@ -4,7 +4,7 @@ import { Title } from './ui/Title';
export function Projects() {
return (
<div id="projects" className="space-y-10 sm:space-y-6">
<div id="projects" className="pt-12 space-y-10 lg:pt-16 sm:space-y-6">
<Title>Проекты</Title>
<div className="flex-col max-sm:space-y-8 lg:flex gap-y-16 sm:max-lg:space-y-10">
<div className="max-sm:space-y-8 lg:flex sm:max-lg:space-y-10 gap-x-4">
@@ -37,8 +37,8 @@ function Project({
<div className="flex gap-x-6">
{tags.map(tag => (
<div key={tag} className="flex items-center py-2 gap-x-2">
<div className="w-3 h-3 bg-white" />
<p className="font-medium h4">{tag}</p>
<div className="w-[10px] h-[10px] bg-[#52587A]" />
<p className="font-medium caption text-[#737AA1]">{tag}</p>
</div>
))}
</div>
+12 -9
View File
@@ -15,7 +15,7 @@ export function Promotion() {
return (
<div
id="products"
className="space-y-8 lg:space-y-20 sm:space-y-10 lg:-mt-4 sm:-mt-10"
className="pt-16 space-y-8 lg:space-y-20 sm:space-y-10 lg:-mt-4 sm:-mt-10"
>
<Title className="max-w-[calc(1310/1600*100vw)]">
Повышаем количество посетителей на&nbsp;стенде,
@@ -28,13 +28,17 @@ export function Promotion() {
<div>
{promotionFeatures.map((feature, index) =>
width >= 1200 ? (
<DesktopFeature
<DesktopPromotionFeature
number={index + 1}
{...feature}
key={feature.title}
/>
) : (
<Feature number={index + 1} {...feature} key={feature.title} />
<PromotionFeature
number={index + 1}
{...feature}
key={feature.title}
/>
),
)}
</div>
@@ -42,7 +46,7 @@ export function Promotion() {
);
}
function DesktopFeature({
function DesktopPromotionFeature({
description,
images,
number,
@@ -114,7 +118,7 @@ function DesktopFeature({
src={image}
alt={title}
transition={{ duration: 0.5 }}
className="h-full pointer-events-none"
className="object-cover h-full pointer-events-none aspect-video"
animate={{
maxHeight:
expanded && index === 0 ? 0.265 * width : 0.0875 * width,
@@ -131,7 +135,7 @@ function DesktopFeature({
);
}
function Feature({
function PromotionFeature({
title,
description,
images,
@@ -160,7 +164,6 @@ function Feature({
useEffect(() => {
if (!ref.current) return;
ref.current.style.height = `${ref.current.clientHeight} ${descriptionHeight}px`;
}, [descriptionHeight, expanded]);
useOnClickOutside(ref, () => setExpanded(false));
@@ -169,7 +172,7 @@ function Feature({
<div
ref={ref}
id={hashes.get(number)}
className="select-none sm:py-4 py-2 sm:space-y-8 space-y-4 border-t border-[#3D425C] last:border-b flex flex-col justify-between relative transition-all"
className="relative select-none sm:py-4 py-2 sm:space-y-8 space-y-4 border-t border-[#3D425C] last:border-b flex flex-col justify-between"
onClick={() => setExpanded(prev => !prev)}
>
<div className="flex items-center justify-between gap-x-5">
@@ -192,7 +195,7 @@ function Feature({
</motion.p>
)}
</AnimatePresence>
<div className="flex mb-0 gap-x-2 max-sm:hidden">
<div className="relative z-10 flex mb-0 gap-x-2 max-sm:hidden">
{images.map((image, index) => (
<AnimatePresence key={image}>
<motion.div
+30 -7
View File
@@ -1,14 +1,37 @@
import { ClassNameWrapper } from '../hocs/ClassNameWrapper';
import { useEffect } from 'react';
import { FullScreenIcon } from './icons/FullScreenIcon';
import { useModalStore } from '../stores/modalStore';
import { VideoModal } from './Layout/VideoModal';
export function Showreel() {
const setModal = useModalStore(state => state.setModal);
useEffect(() => {
const listener = (event: KeyboardEvent) => {
if (event.key === 'Escape') setModal(null);
};
document.addEventListener('keydown', listener);
return () => document.removeEventListener('keydown', listener);
}, [setModal]);
return (
<div className="lg:aspect-[1552/616] sm:aspect-[720/440] aspect-[328/204] [background:linear-gradient(rgba(0,0,0,0.2),rgba(0,0,0,0.2)),center/cover_url(/images/motivation/Showreel.png)_no-repeat] flex justify-center items-center">
<button className="sm:p-[22px] p-3 aspect-square rounded-full border bg-[#14161F33]">
<ClassNameWrapper
className="max-sm:w-5 max-sm:h-5"
element={<FullScreenIcon />}
/>
<div className="lg:mb-[100px] sm:mb-[70px] mb-14 w-full relative flex justify-center items-center group">
<video
src="/video/showreel.mp4"
autoPlay
loop
muted
playsInline
className="self-stretch object-cover w-full"
/>
<button
className="absolute z-10 lg:p-8 sm:p-6 p-4 rounded-full border group-hover:block hidden bg-[#14161F33]"
onClick={() => {
setModal(<VideoModal link={'/video/showreel.mp4'} />);
}}
>
<FullScreenIcon />
</button>
</div>
);
+2 -2
View File
@@ -9,7 +9,7 @@ export function Stands() {
const width = useWindowWidth();
return (
<div className="space-y-10 lg:space-y-20">
<div className="pt-12 space-y-10 lg:space-y-20 lg:pt-16">
<Title>
Мы разработчики с собственной
<span className="text-gradient">
@@ -21,7 +21,7 @@ export function Stands() {
<SliderWithScaling
slides={stands}
SlideElement={Stand}
className="pb-0 space-y-8 max-sm:pb-20"
className="pb-40 space-y-8 sm:max-lg:pb-10 max-sm:pb-20"
slideSizes={
width >= 1024
? ['31.6vw', '31.8vw', '48vw', '48vw']
+1 -1
View File
@@ -6,7 +6,7 @@ import { statistics } from '../consts/statistics';
export function Statistics() {
return (
<div className="lg:space-y-20 sm:space-y-10 space-y-8 sm:border-b border-[#3D425C]">
<div className="lg:space-y-20 sm:space-y-10 space-y-8 sm:border-b border-[#3D425C] lg:pt-16 pt-12">
<Title>
За 15 лет работы cоздали более <br />
<span className="text-gradient"> 250 интерактивных проектов </span>с 3D
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,149 @@
export function LogoWithoutText() {
return (
<svg
width={32}
height={32}
viewBox="0 0 32 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M16.0068 31.7756C24.7187 31.7756 31.7812 24.6624 31.7812 15.8878C31.7812 14.8132 31.6752 13.7635 31.4733 12.7487H21.9434V19.0483H25.0184C23.7216 22.7909 20.1861 25.476 16.028 25.476C10.7704 25.476 6.50824 21.1832 6.50824 15.8878C6.50824 10.5924 10.7704 6.2996 16.028 6.2996V0H16.0068C7.29484 0 0.232422 7.11322 0.232422 15.8878C0.232422 24.6624 7.29484 31.7756 16.0068 31.7756Z"
fill="#798FFF"
/>
<path
d="M15.2134 0.0192822C8.43584 1.07525 3.24805 6.9655 3.24805 14.0736C3.24805 21.9287 9.58346 28.2965 17.3986 28.2965C25.2137 28.2965 31.5491 21.9287 31.5491 14.0736C31.5491 13.6206 31.528 13.1725 31.4868 12.7302H21.9386V19.0207H25.0156C23.718 22.7579 20.1803 25.4391 16.0196 25.4391C10.7587 25.4391 6.49397 21.1526 6.49397 15.8648C6.49397 10.8645 10.3077 6.7595 15.171 6.32797C15.4506 6.30316 15.7336 6.29048 16.0196 6.29048V0H15.9984C15.7352 0 15.4735 0.0064807 15.2134 0.0192822Z"
fill="#D375FF"
/>
<path
opacity={0.3}
d="M14.1721 6.49428C14.766 6.38081 15.3793 6.32141 16.0065 6.32141V0H15.985C14.7151 0 13.4796 0.146971 12.2949 0.424748L14.1721 6.49428Z"
fill="black"
fillOpacity={0.6}
/>
<path
opacity={0.3}
d="M5.85411 3.5923L4.87109 17.8592L6.27619 16.5207C6.26785 16.3561 6.26363 16.1905 6.26363 16.0239C6.26363 10.7371 10.5116 6.44454 15.774 6.38605L9.46338 1.3916C8.15979 1.9647 6.94741 2.70757 5.85411 3.5923Z"
fill="black"
fillOpacity={0.6}
/>
<path
opacity={0.3}
d="M0.233036 16.068L7.42135 19.9466C6.85231 18.725 6.53463 17.3632 6.53463 15.9272C6.53463 14.4893 6.85318 13.1257 7.42368 11.9028L5.22181 4.40674C2.15036 7.29096 0.232422 11.3855 0.232422 15.9272C0.232422 15.9742 0.232627 16.0211 0.233036 16.068Z"
fill="black"
fillOpacity={0.6}
/>
<path
opacity={0.3}
d="M0.232422 15.4311L9.76944 29.7295L20.1824 29.92L16.1202 25.4434C16.0775 25.444 16.0347 25.4443 15.9918 25.4443C10.7435 25.4443 6.48895 21.1903 6.48895 15.9427C6.48895 15.7294 6.49598 15.5177 6.50982 15.3079L0.236835 15.3079C0.235206 15.3489 0.233735 15.39 0.232422 15.4311Z"
fill="black"
fillOpacity={0.6}
/>
<path
opacity={0.3}
d="M7.15389 28.9923L13.2217 25.0028C9.29605 23.7888 6.45123 20.2061 6.45123 15.9752C6.45123 15.4316 6.4982 14.8987 6.58839 14.3801L2.55078 24.3784C3.74294 26.2084 5.31302 27.7812 7.15389 28.9923Z"
fill="black"
fillOpacity={0.6}
/>
<path
opacity={0.3}
d="M21.1095 30.9426C19.4907 31.4827 17.7562 31.7755 15.9523 31.7755C12.6205 31.7755 9.5255 30.7765 6.95898 29.0657L13.0341 25.0493C13.9614 25.3396 14.9491 25.4962 15.9739 25.4962C16.0118 25.4962 16.0495 25.496 16.0872 25.4956L21.1095 30.9426Z"
fill="black"
fillOpacity={0.6}
/>
<path
opacity={0.3}
d="M19.4856 31.3881C18.3496 31.6417 17.1678 31.7755 15.9543 31.7755C15.4285 31.7755 14.9087 31.7504 14.396 31.7013L12.9902 25.0493C13.18 25.1104 13.3724 25.1658 13.5672 25.2154L19.4856 31.3881Z"
fill="black"
fillOpacity={0.4}
/>
<path
opacity={0.3}
d="M22.0371 15.3079L31.5481 13.0601C31.5306 12.9586 31.5123 12.8574 31.493 12.7566H22.0371V15.3079Z"
fill="black"
fillOpacity={0.6}
/>
<path
opacity={0.3}
d="M24.0147 19.0189L31.5486 12.767C31.5479 12.7636 31.5472 12.7601 31.5465 12.7566L22.2695 19.0189H24.0147Z"
fill="black"
fillOpacity={0.4}
/>
<path
opacity={0.3}
d="M28.4374 12.7566L21.5742 30.6159C24.2777 29.583 26.6241 27.8259 28.3767 25.5816L29.4614 12.7566H28.4374Z"
fill="black"
fillOpacity={0.6}
/>
<path
opacity={0.3}
d="M31.4792 12.7566L21.5742 30.6159C27.5403 28.3562 31.7812 22.5945 31.7812 15.8436C31.7812 14.7871 31.6773 13.7549 31.4792 12.7566Z"
fill="black"
fillOpacity={0.4}
/>
<path d="M25.2917 0H31.5481V6.26234H25.2917V0Z" fill="#798FFF" />
<path
d="M31.5481 6.26234H25.2917L22.0371 9.50949H28.0618L31.5481 6.26234Z"
fill="#798FFF"
/>
<path
d="M25.2917 6.26234V0L22.0371 3.47908V9.50949L25.2917 6.26234Z"
fill="#798FFF"
/>
<path
opacity={0.3}
d="M31.5486 5.39675V6.27041L28.1019 9.50937H27.373V5.33447L31.5486 5.39675Z"
fill="black"
fillOpacity={0.6}
/>
<path
opacity={0.3}
d="M22.0371 9.50942V3.38461L26.4051 3.01514L27.8365 3.39351L22.0371 9.50942Z"
fill="black"
fillOpacity={0.6}
/>
<path
opacity={0.3}
d="M24.8196 0.695801L22.0371 3.56156V9.50947H25.7487L24.8196 0.695801Z"
fill="black"
fillOpacity={0.6}
/>
<path
opacity={0.3}
d="M22.2022 3.23811L22.0371 3.41586V9.50945L25.5313 6.14701L27.1669 6.08275L30.8522 6.70395L30.5017 6.17914L26.0836 1.3916L22.2022 3.23811Z"
fill="#D375FF"
/>
<path
opacity={0.3}
d="M25.5173 0H25.2871L24.3574 0.99057L25.4543 1.62357L25.5173 0Z"
fill="black"
fillOpacity={0.6}
/>
<path
opacity={0.3}
d="M30.3896 7.46476L28.2194 9.50937H27.8379L28.1787 5.42173L29.8668 5.10254L30.3896 7.46476Z"
fill="black"
fillOpacity={0.6}
/>
<path
d="M25.2852 6.26234H31.5485V0H25.2852V6.26234Z"
fill="url(#paint0_linear_875_2111)"
/>
<defs>
<linearGradient
id="paint0_linear_875_2111"
x1={28.8491}
y1={0}
x2={28.8491}
y2={6.26234}
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#D375FF" />
<stop offset={1} stopColor="#798FFF" />
</linearGradient>
</defs>
</svg>
);
}
+10 -10
View File
@@ -4,8 +4,8 @@ export const devices: IDevice[] = [
{
title: 'Интерактивные экраны',
description: [
'За счет высокого разрешение, яркости и широкого угла обзора светодиодные стены четко и ярко отображают контент.',
'Их используют на выставках, мероприятиях и в коммерческих пространствах, где нужно выделиться и оставить яркое впечатление.',
'Интерактивные экраны позволяют зрителям взаимодействовать с контентом, используя жесты, голосовые команды или другие формы ввода. ',
'Благодаря широкому спектру возможностей, включая функции распознавания жестов и голосового управления, интерактивные экраны идеально подходят для музеев, выставок, конференций и других мероприятий, где нужно создать интерактивный опыт.',
],
img: '/images/devices/interactive.png',
},
@@ -20,32 +20,32 @@ export const devices: IDevice[] = [
{
title: 'Проекционные экраны',
description: [
'За счет высокого разрешение, яркости и широкого угла обзора светодиодные стены четко и ярко отображают контент.',
'Их используют на выставках, мероприятиях и в коммерческих пространствах, где нужно выделиться и оставить яркое впечатление.',
'Отображают изображения и видео с помощью проектора, обеспечивая четкое и яркое отображение контента благодаря высокой яркости, высокому разрешению и широкому углу обзора.',
'Поэтому их используют на презентациях, лекциях, конференциях и других мероприятиях, где нужно передать информацию или идеи аудитории.',
],
img: '/images/devices/projection.png',
},
{
title: 'Транспарентные экраны',
description: [
'За счет высокого разрешение, яркости и широкого угла обзора светодиодные стены четко и ярко отображают контент.',
'Их используют на выставках, мероприятиях и в коммерческих пространствах, где нужно выделиться и оставить яркое впечатление.',
'Транспарентные экраны создают эффект "невидимого" дисплея, отображая изображения и видео на прозрачном экране. ',
'Благодаря своей уникальности, они идеально подходят для магазинов, выставок и других коммерческих пространств, где нужно создать уникальный и запоминающийся брендинг.',
],
img: '/images/devices/transparent.png',
},
{
title: 'Голографические пирамиды',
description: [
'За счет высокого разрешение, яркости и широкого угла обзора светодиодные стены четко и ярко отображают контент.',
'Их используют на выставках, мероприятиях и в коммерческих пространствах, где нужно выделиться и оставить яркое впечатление.',
'Голографические пирамиды отображают трехмерные изображения с помощью лазерной технологии, создавая уникальный и запоминающийся опыт.',
'Поэтому их используют в музеях, на выставках, конференциях и других мероприятиях, где нужно удивить и заинтересовать аудиторию.',
],
img: '/images/devices/holographic.png',
},
{
title: 'Мобильные устройства',
description: [
'За счет высокого разрешение, яркости и широкого угла обзора светодиодные стены четко и ярко отображают контент.',
'Их используют на выставках, мероприятиях и в коммерческих пространствах, где нужно выделиться и оставить яркое впечатление.',
'Мобильные устройства являются портативными инструментами для презентаций, оснащенными функциями беспроводной связи и поддержкой различных форматов файлов.',
'Благодаря своей мобильности и гибкости, они идеально подходят для презентаций, лекций, конференций и других мероприятий, где нужно эффективно передать информацию или идеи аудитории.',
],
img: '/images/devices/mobile.png',
},
+1 -1
View File
@@ -29,7 +29,7 @@ export const promotionFeatures: IPromotionFeature[] = [
{
description:
'Дают возможность удаленно демонстрировать экспонаты, проводить презентации, организовывать виртуальные туры. Это повышает доступность и географию охвата мероприятия.',
title: 'Приложения с виртуальной и дополненной реальностью',
title: 'Приложения с\xa0виртуальной и дополненной реальностью',
images: [
'/images/promotion/templates/1.png',
'/images/promotion/templates/2.png',
+4
View File
@@ -51,6 +51,10 @@ body {
@apply text-[clamp(12px,12px+(100vw-360px)/1240*4,16px)] leading-[140%];
}
.l-caption {
@apply text-[clamp(14px,14px+(100vw-360px)/1240*2,16px)] leading-none;
}
.m-caption {
@apply text-[clamp(10px,10px+(100vw-360px)/1240*2,12px)] leading-[clamp(12px,12px+(100vw-360px)/1240*2.4,14.4px)];
}
+1 -1
View File
@@ -10,7 +10,7 @@ import { Interactions } from '../components/Interactions';
export function MainPage() {
return (
<div className="lg:space-y-[180px] sm:space-y-[140px] space-y-20">
<div className="lg:space-y-[116px] sm:space-y-[92px] space-y-4">
<Motivation />
<Promotion />
<Interactions />