fixes
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
@@ -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>
|
||||
))}
|
||||
|
||||
@@ -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
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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">
|
||||
эффективного взаимодействия с клиентами
|
||||
</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() {
|
||||
с помощью интерактивных инструментов и анализа
|
||||
пользовательского опыта
|
||||
</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
|
||||
|
||||
@@ -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">
|
||||
{' '}
|
||||
условия использования и политику конфиденциальности
|
||||
условия использования и
|
||||
<Link to="https://graff.tech/privacypolicy">
|
||||
политику конфиденциальности
|
||||
</Link>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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' })
|
||||
}
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)]">
|
||||
Повышаем количество посетителей на стенде,
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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
@@ -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',
|
||||
},
|
||||
|
||||
@@ -29,7 +29,7 @@ export const promotionFeatures: IPromotionFeature[] = [
|
||||
{
|
||||
description:
|
||||
'Дают возможность удаленно демонстрировать экспонаты, проводить презентации, организовывать виртуальные туры. Это повышает доступность и географию охвата мероприятия.',
|
||||
title: 'Приложения с виртуальной и дополненной реальностью',
|
||||
title: 'Приложения с\xa0виртуальной и дополненной реальностью',
|
||||
images: [
|
||||
'/images/promotion/templates/1.png',
|
||||
'/images/promotion/templates/2.png',
|
||||
|
||||
@@ -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)];
|
||||
}
|
||||
|
||||
@@ -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 />
|
||||
|
||||
Reference in New Issue
Block a user