feat: init first beta

This commit is contained in:
2025-07-02 13:37:16 +03:00
parent 8d95d047ea
commit d684cf3cb1
126 changed files with 7907 additions and 351 deletions

View File

@@ -0,0 +1,90 @@
import { NextIntlClientProvider, hasLocale } from 'next-intl';
import { getTranslations } from 'next-intl/server';
import { notFound } from 'next/navigation';
import { routing } from '@/i18n/routing';
import { Inter } from "next/font/google";
import MainEffects from '@/app/ui/homepage/js/MainEffects';
import Template from './template';
import { GoogleTagManagerHead, GoogleTagManagerBody } from '@/app/ui/components/GoogleTagManager';
import '@/app/ui/homepage/css/index.css';
const interSans = Inter({
variable: "--font-inter-sans",
subsets: ["latin", "cyrillic"],
});
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'website_home' });
// Get the base URL from environment variables
const protocol = process.env.PROTOCOL || 'https';
const hostname = process.env.HOSTNAME || 'www.mssg.me';
const baseUrl = `${protocol}://${hostname}`;
// Current page path (this is for the homepage)
const currentPath = '';
// Generate canonical URL
const canonicalUrl = locale === 'en'
? `${baseUrl}${currentPath}`
: `${baseUrl}/${locale}${currentPath}`;
// Generate alternate URLs for all locales
const alternateUrls: Record<string, string> = {};
routing.locales.forEach((loc) => {
alternateUrls[loc] = loc === 'en'
? `${baseUrl}${currentPath}`
: `${baseUrl}/${loc}${currentPath}`;
});
// Add x-default for users who don't match any specific language
alternateUrls['x-default'] = `${baseUrl}${currentPath}`;
return {
title: t('head.title'),
description: t('head.description'),
alternates: {
canonical: canonicalUrl,
languages: alternateUrls,
},
icons: {
other: [
{
rel: 'mask-icon',
url: '/favicons/safari-pinned-tab.svg',
color: '#000000',
}
]
}
};
}
export default async function LocaleLayout({
children,
params
}: {
children: React.ReactNode;
params: Promise<{locale: string}>;
}) {
const {locale} = await params;
if (!hasLocale(routing.locales, locale)) {
notFound();
}
return (
<html lang={locale} id="homepage">
<head>
<GoogleTagManagerHead gtmId="GTM-5MPBB4D" />
</head>
<body className={interSans.variable}>
<GoogleTagManagerBody gtmId="GTM-5MPBB4D" />
<Template>
<NextIntlClientProvider>{children}</NextIntlClientProvider>
<MainEffects />
</Template>
</body>
</html>
);
}

View File

@@ -0,0 +1,355 @@
import { useLocale, useTranslations } from 'next-intl';
import Image from "next/image";
import { Link } from '@/i18n/navigation';
import { LocaleType } from '@/i18n/i18n';
const { BLACK_FRIDAY_PROMO } = process.env;
import LanguageSwitcher from '@/app/ui/components/LanguageSwitcher';
import { generateAuthUrl } from '@/app/helpers/generateAuthUrl';
export default function Home() {
const t = useTranslations();
const currentYear = new Date().getFullYear();
const locale = useLocale();
const loginUrl = generateAuthUrl(locale as LocaleType, 'login');
const signUpUrl = generateAuthUrl(locale as LocaleType, 'signup');
return (
<>
{BLACK_FRIDAY_PROMO && (
<div className="promo">
{t('promo.black_friday')}
</div>
)}
<div className="page-container">
<header className="header">
<Link href="/" className="logo">
<Image src="/img/ui/logo_home.svg" alt="mssg.me logo" width={34} height={35} />
</Link>
<ul className="auth-buttons">
<li>
<a href={loginUrl} className="right">{t('website_home.common.auth.login')}</a>
</li>
<li>
<a href={signUpUrl}>{t('website_home.common.auth.signup')}</a>
</li>
</ul>
</header>
<section className="section intro-section">
<div className="content">
<h1 className="main-title">
<span className="gradiented">{t('website_home.home.intro.title.1')}</span>
<span>{t('website_home.home.intro.title.2')}</span>
</h1>
<p>{t('website_home.home.intro.description')}</p>
<div className="button-container center">
<a href={signUpUrl} className="button">{t('website_home.common.sign_up_cta')}</a>
</div>
<div className="bbg-label"><span>backed by</span> <Image src="/img/ui/google_for_startups_logo.svg" alt="Google for startups logotype" width={260} height={32} /></div>
{/* <Link
href="https://apps.apple.com/us/app/mssg-me-website-builder/id6454930943?itsct=apps_box_badge&amp;itscg=30200"
style={{
display: "inline-block",
overflow: "hidden",
borderRadius: "13px",
width: "250px",
height: "83px",
marginTop: "20px"
}}
>
<Image
src="https://tools.applemediaservices.com/api/badges/download-on-the-app-store/black/en-us?size=250x83&amp;releaseDate=1705968000"
alt="Download on the App Store"
style={{
borderRadius: "13px",
width: "250px",
height: "83px"
}}
/>
</Link> */}
</div>
</section>
<section className="section presentation-section">
<div className="presentation-hero">
<Image
className="presentation-hero-left"
src="/img/home/presentation/presentation_left_378w.webp"
sizes="(max-width: 420px) 126px,
(max-width: 1920px) 252px,
378px"
alt="Contact me landing example"
width={253}
height={471}
/>
<Image
className="presentation-hero-right"
src="/img/home/presentation/presentation_right_378w.webp"
sizes="(max-width: 420px) 126px,
(max-width: 1920px) 252px,
378px"
alt="Shop landing example"
width={253}
height={471}
/>
<Image
className="presentation-hero-center"
src="/img/home/presentation/presentation_center_441w.webp"
sizes="(max-width: 420px) 147px,
(max-width: 1920px) 294px,
441px"
alt="Multilink landing example"
width={253}
height={471}
priority
/>
</div>
<div className="content section-content">
<h2 className="gradiented">{t('website_home.home.presentation.title')}</h2>
<p>{t('website_home.home.presentation.description')}</p>
<div className="button-container center">
<a href={signUpUrl} className="button">{t('website_home.common.sign_up_cta')}</a>
</div>
</div>
</section>
<section className="section multi-link-section">
<div className="multi-link-hero-container">
<div className="multi-link-hero">
<Image
src="/img/home/multilink/multilink_441w.webp"
sizes="(max-width: 420px) 147px,
(max-width: 1920px) 294px,
441px"
alt="Multi-link page example"
width={295}
height={550}
/>
<div className="multi-link-hero__icon instagram">
<div>
<div>
<Image src="/img/home/multilink/instagram.svg" alt="Instagram icon" width={57} height={57} />
</div>
</div>
</div>
<div className="multi-link-hero__icon youtube">
<div>
<div>
<Image src="/img/home/multilink/youtube.svg" alt="YouTube icon" width={43} height={30} />
</div>
</div>
</div>
<div className="multi-link-hero__icon tiktok">
<div>
<div>
<Image src="/img/home/multilink/tiktok.svg" alt="TikTok icon" width={42} height={42} />
</div>
</div>
</div>
<div className="multi-link-hero__icon twitter">
<div>
<div>
<Image src="/img/home/multilink/twitter.svg" alt="Twitter icon" width={27} height={27} />
</div>
</div>
</div>
</div>
</div>
<div className="content section-content">
<h2 className="gradiented section-title">{t('website_home.home.multi_link.title')}</h2>
<h3 className="section-subtitle">{t('website_home.home.multi_link.subtitle')}</h3>
<p>{t('website_home.home.multi_link.description')}</p>
<div className="button-container">
<a href={signUpUrl} className="button">{t('website_home.common.sign_up_cta')}</a>
</div>
</div>
</section>
<section className="section messengers-section">
<div className="messengers-hero-container">
<div className="messengers-hero">
<Image
src="/img/home/messengers/messengers_441w.webp"
sizes="(max-width: 420px) 147px,
(max-width: 1920px) 294px,
441px"
alt="Page with messengers example"
width={294}
height={548}
/>
<div className="icon-hey">
<Image
src="/img/home/messengers/hey_192w.webp"
sizes="(max-width: 420px) 64px,
(max-width: 1920px) 128px,
192px"
alt="Hey icon"
width={128}
height={128}
/>
</div>
<div className="message">
<Image
src="/img/home/messengers/message_501w.webp"
sizes="(max-width: 420px) 167px,
(max-width: 1920px) 334px,
501px"
alt="User message image"
width={338}
height={77}
/>
</div>
</div>
</div>
<div className="content section-content">
<h2 className="gradiented section-title">{t('website_home.home.messengers.title')}</h2>
<h3 className="section-subtitle">{t('website_home.home.messengers.subtitle')}</h3>
<p>{t('website_home.home.messengers.description')}</p>
<div className="button-container">
<a href={signUpUrl} className="button">{t('website_home.common.sign_up_cta')}</a>
</div>
</div>
</section>
<section className="section store-section">
<div className="store-hero-container">
<div className="store-hero">
<Image
src="/img/home/store/store_441w.webp"
sizes="(max-width: 420px) 147px,
(max-width: 1920px) 294px,
441px"
alt="Online store page example"
width={294}
height={549}
/>
<div className="icon-cart">
<Image
src="/img/home/store/cart_192w.webp"
sizes="(max-width: 420px) 64px,
(max-width: 1920px) 128px,
192px"
alt="Shopping cart icon"
width={129}
height={129}
/>
</div>
<div className="order">
<Image
src="/img/home/store/order_342w.webp"
sizes="(max-width: 420px) 114px,
(max-width: 1920px) 228px,
342px"
alt="Shopping cart icon"
width={228}
height={200}
/>
</div>
</div>
</div>
<div className="content section-content">
<h2 className="gradiented section-title">{t('website_home.home.store.title')}</h2>
<h3 className="section-subtitle">{t('website_home.home.store.subtitle')}</h3>
<p>{t('website_home.home.store.description')}</p>
<div className="button-container">
<a href={signUpUrl} className="button">{t('website_home.common.sign_up_cta')}</a>
</div>
</div>
</section>
<section className="section builder-section">
<div className="builder-hero-container">
<div className="builder-hero">
<Image
src="/img/home/builder/builder_441w.webp"
sizes="(max-width: 420px) 147px,
(max-width: 1920px) 294px,
441px"
alt="Website builder example"
width={264}
height={568}
/>
<div className="blocks">
<Image
src="/img/home/builder/blocks_333w.webp"
sizes="(max-width: 420px) 111px,
(max-width: 1920px) 222px,
333px"
alt="Website builder blocks"
width={198}
height={287}
/>
</div>
<div className="customize">
<Image
src="/img/home/builder/customize_486w.webp"
sizes="(max-width: 420px) 162px,
(max-width: 1920px) 324px,
486px"
alt="Website builder blocks customization"
width={291}
height={196}
/>
</div>
</div>
</div>
<div className="content section-content">
<h2 className="gradiented section-title">{t('website_home.home.builder.title')}</h2>
<h3 className="section-subtitle">{t('website_home.home.builder.subtitle')}</h3>
<p>{t('website_home.home.builder.description')}</p>
<div className="button-container">
<a href={signUpUrl} className="button">{t('website_home.common.sign_up_cta')}</a>
</div>
</div>
</section>
<section className="features-section">
<div className="content">
<h2 className="section-subtitle">{t('website_home.home.features.title')}</h2>
<ul className="features-list">
<li>
<h3 className="section-subtitle">{t('website_home.home.features.list.domain.title')}</h3>
<p>{t('website_home.home.features.list.domain.description')}</p>
</li>
<li>
<h3 className="section-subtitle">{t('website_home.home.features.list.qr.title')}</h3>
<p>{t('website_home.home.features.list.qr.description')}</p>
</li>
<li>
<h3 className="section-subtitle">{t('website_home.home.features.list.analytics.title')}</h3>
<p>{t('website_home.home.features.list.analytics.description')}</p>
</li>
</ul>
</div>
</section>
<section className="footer-cta-section">
<div className="content">
<h2 className="gradiented">{t('website_home.home.footer.title')}</h2>
<p>{t('website_home.home.footer.description')}</p>
<div className="button-container center">
<a href={signUpUrl} className="button">{t('website_home.common.sign_up_cta')}</a>
</div>
</div>
</section>
<footer className="footer">
<div className="content">
<ul className="footer-list">
<li>
<Link href="/privacy-policy">{t('website_home.common.terms.politics')}</Link>
</li>
<li>
<Link href="/terms-of-use">{t('website_home.common.terms.terms')}</Link>
</li>
</ul>
<ul className="footer-lang-chooser">
<LanguageSwitcher />
</ul>
<span className="copyright">mssg.me © {currentYear}</span>
<span className="made-in">Made in Ukraine <Image width="17" height="12" src="/img/ui/ua_flag.gif" alt="Ukrainian flag" unoptimized /></span>
<div className="bbg-label-footer"><span>backed by</span> <Image src="/img/ui/google_for_startups_logo.svg" alt="Google for startups logotype" width={160} height={19} /></div>
</div>
</footer>
</div>
<div className="cookie-message" id="cookie-message">
<span>{t('website_home.common.cookies.message')}</span>
<Link href="/privacy-policy">{t('website_home.common.cookies.link')}</Link>
<div className="close-cookie" id="close-cookie"></div>
</div>
</>
);
}

View File

@@ -0,0 +1,3 @@
export default function Template({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}

View File

@@ -0,0 +1,49 @@
import { useTranslations } from 'next-intl';
import { Link } from '@/i18n/navigation';
import LanguageSwitcher from '@/app/ui/components/LanguageSwitcher';
export default function Footer() {
const t = useTranslations();
return (
<>
<footer className="main-footer">
<div className="main-footer-content">
<nav className="main-footer-content-left">
<ul className="footer-nav-list">
<li>
<h6>{t('menu.subheading4')}</h6>
<ul className="footer-menu-list">
<li>
<Link href="/terms-of-use">{t('menu.terms')}</Link>
</li>
<li>
<Link href="/privacy-policy">{t('menu.privacy')}</Link>
</li>
</ul>
</li>
</ul>
<ul className="lang-chooser-list lang-chooser-list-mobile">
<LanguageSwitcher />
</ul>
</nav>
<div className="main-footer-content-right">
<ul className="footer-socials-list">
<li>
<Link href="https://t.me/mssgme" target="_blank" data-footer-social="Telegram">
<i className="fab fa-telegram"></i>
</Link>
</li>
</ul>
</div>
</div>
</footer>
<div className="cookie-message" id="cookie-message">
<span>{t('cookies.message')}</span>
<Link href="/privacy-policy">{t('cookies.link')}</Link>
<div className="close-cookie" id="close-cookie"></div>
</div>
</>
);
}

View File

@@ -0,0 +1,43 @@
import { useLocale, useTranslations } from 'next-intl';
import { Link } from '@/i18n/navigation';
import Image from 'next/image';
import LanguageSwitcher from '@/app/ui/components/LanguageSwitcher';
import { generateAuthUrl } from '@/app/helpers/generateAuthUrl';
import { LocaleType } from '@/i18n/i18n';
export default function Header() {
const t = useTranslations();
const locale = useLocale();
const loginUrl = generateAuthUrl(locale as LocaleType, 'login');
return (
<header className="main-header">
<div className="main-header-content">
<div className="main-header-left">
<Link href="/" className="header-logo">
<Image src="/img/ui/logo_full.svg" alt="mssg.me logo" width={151} height={44} />
</Link>
</div>
<div className="main-header-right">
<div className="header-buttons">
<ul
className="lang-chooser-list lang-chooser-list-desktop"
style={{ margin: '0 10px 0 0', lineHeight: '26px' }}
>
<LanguageSwitcher />
</ul>
<Link href={loginUrl} className="button button-header button-outline">{t('button.login')}</Link>
</div>
<div className="mobile-menu-btn">
<div className="mobile-menu-btn-inner">
<div></div>
<div></div>
<div></div>
</div>
</div>
</div>
</div>
</header>
);
}

View File

@@ -0,0 +1,5 @@
import { notFound } from 'next/navigation';
export default function CatchAllNotFound() {
notFound();
}

View File

@@ -0,0 +1,188 @@
import { getTranslations, getLocale } from 'next-intl/server';
import Link from 'next/link';
import React from 'react';
import { Metadata } from 'next';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faFacebook, faTwitter } from '@fortawesome/free-brands-svg-icons';
interface PostCategory {
name: string;
}
interface Post {
id: number;
title: {
rendered: string;
};
content: {
rendered: string;
};
thumbnail_full_url: string;
thumbnail_article_1x: string;
thumbnail_article_2x: string;
post_categories: PostCategory[];
acf: {
time_to_read: string;
title?: string;
description?: string;
og_image?: string;
og_type?: string;
header_image?: {
sizes: {
article_header_image_1x: string;
article_header_image_2x: string;
};
};
};
slug: string;
link: string;
}
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>
}): Promise<Metadata> {
const { slug } = await params;
const locale = await getLocale();
try {
const data = await fetch(`https://w.mssg.me/${locale}/wp-json/wp/v2/posts?slug=${slug}`);
const post: Post[] = await data.json();
const pData = post[0];
if (!pData) {
return {
title: 'Post not found',
description: 'The requested blog post could not be found.'
};
}
const openGraphUrl = `https://mssg.me/${locale}/blog/${slug}`;
return {
title: pData.acf.title || pData.title.rendered,
description: pData.acf.description,
openGraph: {
siteName: 'mssg.me',
title: pData.acf.title || pData.title.rendered,
description: pData.acf.description,
url: openGraphUrl,
type: (pData.acf.og_type as 'website' | 'article') || 'article',
images: pData.acf.og_image ? [
{
url: pData.acf.og_image,
alt: pData.title.rendered,
}
] : undefined,
},
twitter: {
card: 'summary',
title: pData.acf.title || pData.title.rendered,
description: pData.acf.description,
images: pData.acf.og_image ? [pData.acf.og_image] : undefined,
},
};
} catch (error) {
console.error('Error generating metadata:', error);
return {
title: 'Blog Post',
description: 'Read our latest blog post on mssg.me'
};
}
}
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params;
const locale = await getLocale();
const t = await getTranslations({ locale });
const postsData = await fetch(`https://w.mssg.me/${locale}/wp-json/wp/v2/posts`);
const posts: Post[] = await postsData.json();
const data = await fetch(`https://w.mssg.me/${locale}/wp-json/wp/v2/posts?slug=${slug}`);
const post: Post[] = await data.json();
const pData = post[0];
return (
<div className="page-wrapper">
<main>
<section className="section-article">
<div className="common-content">
<div className="section-article-content">
<div className="section-article-header">
<div className="section-article-header-img">
<img
src={pData.acf.header_image ? pData.acf.header_image.sizes.article_header_image_1x : pData.thumbnail_full_url}
srcSet={[pData.acf.header_image ? pData.acf.header_image.sizes.article_header_image_1x : `${pData.thumbnail_full_url} 1x`, pData.acf.header_image ? pData.acf.header_image.sizes.article_header_image_2x : `${pData.thumbnail_full_url} 2x`].join(', ')}
alt={pData.title.rendered}
/>
</div>
<div className="section-article-header-heading">
<div className="section-article-header-heading-content">
<h1>{pData.title.rendered}</h1>
<span>
{pData.post_categories.map((cat: PostCategory, index: number) => (
<React.Fragment key={index}>
<span>{cat.name}</span> | {pData.acf.time_to_read}
</React.Fragment>
))}
</span>
</div>
</div>
</div>
<div className="section-article-text clearfix" dangerouslySetInnerHTML={{ __html: pData.content.rendered }} />
<ul className="article-share-list">
<li className="article-share-list-fb">
<a href={`https://www.facebook.com/sharer/sharer.php?u=https://www.mssg.me/${locale}/blog/${slug}`} target="_blank" data-share-social="Facebook" className="article-share-link">
<FontAwesomeIcon icon={faFacebook} />
Facebook
</a>
</li>
<li className="article-share-list-twitter">
<a href={`https://twitter.com/home?status=https://www.mssg.me/${locale}/blog/${slug}`} target="_blank" data-share-social="Twitter" className="article-share-link">
<FontAwesomeIcon icon={faTwitter} />
Twitter
</a>
</li>
</ul>
</div>
</div>
</section>
<section className="section-fp-news">
<div className="common-content">
<div className="section-fp-news-content section-fp-news-content-article">
<h3>{t('single_article.posts_section.heading')}</h3>
<ul className="section-fp-news-list">
{posts.filter((post: Post) => post.id !== pData.id).slice(0,3).map((element: Post, index: number) => (
<li key={index}>
<div className="section-fp-news-list-item-preview">
<img
src={element.thumbnail_article_1x}
srcSet={`${element.thumbnail_article_1x} 1x ` + `${element.thumbnail_article_2x} 2x`}
alt={element.title.rendered}
/>
</div>
<div className="section-fp-news-list-item-content">
<h4>{element.title.rendered}</h4>
<span>
{element.post_categories.map((cat: PostCategory, index: number) => (
<React.Fragment key={index}>
<span>{cat.name}</span> | {element.acf.time_to_read}
</React.Fragment>
))}
</span>
</div>
<Link href={`/blog/${element.slug}`} className="section-fp-news-list-item-link" />
</li>
))}
</ul>
</div>
</div>
</section>
</main>
</div>
);
}

View File

@@ -0,0 +1,68 @@
import { getTranslations, getLocale } from 'next-intl/server';
import Link from 'next/link';
import React from 'react';
import { Metadata } from 'next';
interface PostCategory {
name: string;
}
interface Post {
title: {
rendered: string;
};
thumbnail_medium_url: string;
post_categories: PostCategory[];
acf: {
time_to_read: string;
};
slug: string;
}
export async function generateMetadata(): Promise<Metadata> {
const locale = await getLocale();
const t = await getTranslations({ locale });
return {
title: t('blog.title'),
description: t('blog.description'),
};
}
export default async function Page() {
const locale = await getLocale();
const data = await fetch(`https://w.mssg.me/${locale}/wp-json/wp/v2/posts`);
const posts: Post[] = await data.json();
return (
<div className="page-wrapper">
<section className="section-category-blog">
<div className="common-content">
<div className="section-category-blog-content">
<ul className="section-fp-news-list section-category-blog-list">
{posts.map((element: Post, index: number) =>
<li key={index}>
<div className="section-fp-news-list-item-preview">
<img src={element.thumbnail_medium_url} alt={element.title.rendered} />
</div>
<div className="section-fp-news-list-item-content">
<h4>{element.title.rendered}</h4>
<span>
{element.post_categories.map((cat: PostCategory, index: number) =>
<React.Fragment key={index}>
<span>{cat.name}</span> | {element.acf.time_to_read}
</React.Fragment>
)}
</span>
</div>
<Link href={`/blog/${element.slug}`} className="section-fp-news-list-item-link" />
</li>
)}
</ul>
</div>
</div>
</section>
</div>
);
}

View File

@@ -0,0 +1,104 @@
import { getLocale } from 'next-intl/server';
interface AnswerBlock {
acf_fc_layout: string;
text?: string;
heading?: string;
content?: string;
}
interface Question {
question: string;
answer: AnswerBlock[];
}
interface FaqTopic {
topic_name: string;
topic_icon_new: string;
topic_icon_new_active: string;
topic_subheading?: string;
'qa-list': Question[];
}
interface PageData {
acf: {
faq_topics: FaqTopic[];
};
}
export default async function Page() {
const locale = await getLocale();
const data = await fetch(`https://w.mssg.me/${locale}/wp-json/wp/v2/pages/993`);
const pData: PageData = await data.json();
return (
<div className="page-wrapper">
<main>
<div className="page-faq">
<div className="common-content">
<div className="page-faq-content">
<article className="page-faq-container">
<div className="faq-container">
<div className="faq-container-left">
<ul className="faq-top-controls-list clearfix">
{
pData.acf.faq_topics.map((element: FaqTopic) => (
<li key={element.topic_name}>
<img src={element.topic_icon_new} className="default" alt={element.topic_name} />
<img src={element.topic_icon_new_active} className="active" alt={element.topic_name} />
<h2>{element.topic_name}</h2>
{/* <p>{element.topic_subheading}</p> */}
</li>
))
}
</ul>
</div>
<div className="faq-container-right">
<ul className="faq-sublists clearfix">
{pData.acf.faq_topics.map((element: FaqTopic, indexOuter: number) =>
<li key={indexOuter}>
<ul className="faq-panes-list">
{element['qa-list'].map((question: Question, index: number) =>
<li id={`${indexOuter+1}-${index+1}`} key={index}>
<div className="faq-panes-list-item-header">
<span className="faq-panes-list-item-counter"></span>
<h3>{question.question}</h3>
<span className="faq-panes-list-item-status"></span>
</div>
<div className="faq-panes-list-item-content">
{question.answer.map((block: AnswerBlock, index: number) => {
if (block.acf_fc_layout === 'text') {
return <div key={index} dangerouslySetInnerHTML={{ __html: block.text || '' }} />;
}
if (block.acf_fc_layout === 'accordeon') {
return (
<div key={index} style={{ marginBottom: 6 }}>
<div className="accordeon-header">
<h4 className="accordeon-heading">
{block.heading}
</h4>
<span className="faq-panes-list-item-status"></span>
</div>
<div className="accordeon-content" dangerouslySetInnerHTML={{ __html: block.content || '' }} />
</div>
)
}
})}
</div>
</li>
)}
</ul>
</li>
)}
</ul>
</div>
</div>
</article>
</div>
</div>
</div>
</main>
</div>
);
}

View File

@@ -0,0 +1,97 @@
import { NextIntlClientProvider, hasLocale } from 'next-intl';
import { getTranslations } from 'next-intl/server';
import { notFound } from 'next/navigation';
import { routing } from '@/i18n/routing';
import { Inter } from "next/font/google";
import Template from './template';
import Header from './Header';
import Footer from './Footer';
import MainScripts from '@/app/ui/components/MainScripts';
import { GoogleTagManagerHead, GoogleTagManagerBody } from '@/app/ui/components/GoogleTagManager';
import '@/app/ui/wppages/css/index.css';
import '@/lib/fontawesome';
const interSans = Inter({
variable: "--font-inter-sans",
subsets: ["latin", "cyrillic"],
});
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'website_home' });
// Get the base URL from environment variables
const protocol = process.env.PROTOCOL || 'https';
const hostname = process.env.HOSTNAME || 'www.mssg.me';
const baseUrl = `${protocol}://${hostname}`;
// Current page path (this is for the root page, adjust for specific pages)
const currentPath = '';
// Generate canonical URL
const canonicalUrl = locale === 'en'
? `${baseUrl}${currentPath}`
: `${baseUrl}/${locale}${currentPath}`;
// Generate alternate URLs for all locales
const alternateUrls: Record<string, string> = {};
routing.locales.forEach((loc) => {
alternateUrls[loc] = loc === 'en'
? `${baseUrl}${currentPath}`
: `${baseUrl}/${loc}${currentPath}`;
});
// Add x-default for users who don't match any specific language
alternateUrls['x-default'] = `${baseUrl}${currentPath}`;
return {
title: t('head.title'),
description: t('head.description'),
alternates: {
canonical: canonicalUrl,
languages: alternateUrls,
},
icons: {
other: [
{
rel: 'mask-icon',
url: '/favicons/safari-pinned-tab.svg',
color: '#000000',
}
]
}
};
}
export default async function LocaleLayout({
children,
params
}: {
children: React.ReactNode;
params: Promise<{locale: string}>;
}) {
const {locale} = await params;
if (!hasLocale(routing.locales, locale)) {
notFound();
}
return (
<html lang={locale} id="homepage">
<head>
<GoogleTagManagerHead gtmId="GTM-5MPBB4D" />
</head>
<body className={interSans.variable}>
<GoogleTagManagerBody gtmId="GTM-5MPBB4D" />
<Template>
<NextIntlClientProvider>
<Header />
{children}
<Footer />
<MainScripts />
</NextIntlClientProvider>
</Template>
</body>
</html>
);
}

View File

@@ -0,0 +1,13 @@
import { getLocale } from 'next-intl/server';
export async function GET() {
const locale = await getLocale();
const data = await fetch(`https://w.mssg.me/${locale}/wp-json/wp/v2/pages/138`);
const pData = await data.json();
return new Response(pData.content.rendered, {
headers: {
'Content-Type': 'text/html; charset=utf-8',
},
});
}

View File

@@ -0,0 +1,22 @@
import { getLocale } from 'next-intl/server';
export default async function Page() {
const locale = await getLocale();
const data = await fetch(`https://w.mssg.me/${locale}/wp-json/wp/v2/pages/138`);
const pData = await data.json();
return (
<div className="page-wrapper page-wrapper-doc">
<div className="page-default-wrapper">
<article className="page-default-content">
<h1>{pData.title.rendered}</h1>
<div className="clearfix">
<div dangerouslySetInnerHTML={{ __html: pData.content.rendered }} />
</div>
</article>
</div>
</div>
);
}

View File

@@ -0,0 +1,3 @@
export default function Template({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}

View File

@@ -0,0 +1,13 @@
import { getLocale } from 'next-intl/server';
export async function GET() {
const locale = await getLocale();
const data = await fetch(`https://w.mssg.me/${locale}/wp-json/wp/v2/pages/140`);
const pData = await data.json();
return new Response(pData.content.rendered, {
headers: {
'Content-Type': 'text/html; charset=utf-8',
},
});
}

View File

@@ -0,0 +1,22 @@
import { getLocale } from 'next-intl/server';
export default async function Page() {
const locale = await getLocale();
const data = await fetch(`https://w.mssg.me/${locale}/wp-json/wp/v2/pages/140`);
const pData = await data.json();
return (
<div className="page-wrapper page-wrapper-doc">
<div className="page-default-wrapper">
<article className="page-default-content">
<h1>{pData.title.rendered}</h1>
<div className="clearfix">
<div dangerouslySetInnerHTML={{ __html: pData.content.rendered }} />
</div>
</article>
</div>
</div>
);
}

View File

@@ -0,0 +1,41 @@
import { getTranslations, getLocale } from 'next-intl/server';
import Link from 'next/link';
import React from 'react';
import { Metadata } from 'next';
import '@/app/ui/wppages/css/index.css';
export async function generateMetadata(): Promise<Metadata> {
const locale = await getLocale();
const t = await getTranslations({ locale });
return {
title: t('404.heading'),
description: t('404.error'),
};
}
export default async function NotFound() {
const locale = await getLocale();
const t = await getTranslations({ locale });
return (
<html lang={locale}>
<body>
<div className="page-wrapper">
<div className="header-404">
<Link href="/" className="header-404-logo">
<img src="/img/ui/logo_full.svg" alt="mssg.me logo" />
</Link>
</div>
<div className="content-404">
<div className="content-404-block">
<h1>{t('404.heading')}</h1>
<p>{t('404.error')}</p>
<Link href="/" className="button button-outline">{t('404.button')}</Link>
</div>
</div>
</div>
</body>
</html>
);
}

BIN
src/app/apple-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,42 +0,0 @@
:root {
--background: #ffffff;
--foreground: #171717;
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
html,
body {
max-width: 100vw;
overflow-x: hidden;
}
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
a {
color: inherit;
text-decoration: none;
}
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
}

View File

@@ -0,0 +1,17 @@
import { LocaleType } from '@/i18n/i18n';
export const generateAuthUrl = (lang: LocaleType, type: 'login' | 'signup') => {
const utmMideumMap = {
login: 'login_click',
signup: 'sign_up_click',
}
const baseUrl = `/auth/${type}`;
const params = new URLSearchParams({
lang,
utm_source: 'website',
utm_medium: utmMideumMap[type],
utm_campaign: 'mssgme_website',
});
return `${baseUrl}?${params.toString()}`;
}

View File

@@ -1,32 +0,0 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={`${geistSans.variable} ${geistMono.variable}`}>
{children}
</body>
</html>
);
}

23
src/app/manifest.ts Normal file
View File

@@ -0,0 +1,23 @@
import type { MetadataRoute } from 'next'
export default function manifest(): MetadataRoute.Manifest {
return {
name: 'mssg.me',
short_name: 'mssg.me',
display: 'standalone',
background_color: '#ffffff',
theme_color: '#ffffff',
icons: [
{
src: '/favicons/android-chrome-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: '/favicons/android-chrome-512x512.png',
sizes: '512x512',
type: 'image/png',
},
],
}
}

21
src/app/not-found.tsx Normal file
View File

@@ -0,0 +1,21 @@
import Link from 'next/link';
import React from 'react';
export default function GlobalNotFound() {
return (
<div className="page-wrapper">
<div className="header-404">
<Link href="/" className="header-404-logo">
<img src="/img/ui/logo_full.svg" alt="mssg.me logo" />
</Link>
</div>
<div className="content-404">
<div className="content-404-block">
<h1>404 Error</h1>
<p>The page doesn't exist</p>
<Link href="/" className="button button-outline">Visit Homepage</Link>
</div>
</div>
</div>
);
}

BIN
src/app/opengraph-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -1,168 +0,0 @@
.page {
--gray-rgb: 0, 0, 0;
--gray-alpha-200: rgba(var(--gray-rgb), 0.08);
--gray-alpha-100: rgba(var(--gray-rgb), 0.05);
--button-primary-hover: #383838;
--button-secondary-hover: #f2f2f2;
display: grid;
grid-template-rows: 20px 1fr 20px;
align-items: center;
justify-items: center;
min-height: 100svh;
padding: 80px;
gap: 64px;
font-family: var(--font-geist-sans);
}
@media (prefers-color-scheme: dark) {
.page {
--gray-rgb: 255, 255, 255;
--gray-alpha-200: rgba(var(--gray-rgb), 0.145);
--gray-alpha-100: rgba(var(--gray-rgb), 0.06);
--button-primary-hover: #ccc;
--button-secondary-hover: #1a1a1a;
}
}
.main {
display: flex;
flex-direction: column;
gap: 32px;
grid-row-start: 2;
}
.main ol {
font-family: var(--font-geist-mono);
padding-left: 0;
margin: 0;
font-size: 14px;
line-height: 24px;
letter-spacing: -0.01em;
list-style-position: inside;
}
.main li:not(:last-of-type) {
margin-bottom: 8px;
}
.main code {
font-family: inherit;
background: var(--gray-alpha-100);
padding: 2px 4px;
border-radius: 4px;
font-weight: 600;
}
.ctas {
display: flex;
gap: 16px;
}
.ctas a {
appearance: none;
border-radius: 128px;
height: 48px;
padding: 0 20px;
border: none;
border: 1px solid transparent;
transition:
background 0.2s,
color 0.2s,
border-color 0.2s;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
line-height: 20px;
font-weight: 500;
}
a.primary {
background: var(--foreground);
color: var(--background);
gap: 8px;
}
a.secondary {
border-color: var(--gray-alpha-200);
min-width: 158px;
}
.footer {
grid-row-start: 3;
display: flex;
gap: 24px;
}
.footer a {
display: flex;
align-items: center;
gap: 8px;
}
.footer img {
flex-shrink: 0;
}
/* Enable hover only on non-touch devices */
@media (hover: hover) and (pointer: fine) {
a.primary:hover {
background: var(--button-primary-hover);
border-color: transparent;
}
a.secondary:hover {
background: var(--button-secondary-hover);
border-color: transparent;
}
.footer a:hover {
text-decoration: underline;
text-underline-offset: 4px;
}
}
@media (max-width: 600px) {
.page {
padding: 32px;
padding-bottom: 80px;
}
.main {
align-items: center;
}
.main ol {
text-align: center;
}
.ctas {
flex-direction: column;
}
.ctas a {
font-size: 14px;
height: 40px;
padding: 0 16px;
}
a.secondary {
min-width: auto;
}
.footer {
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
}
@media (prefers-color-scheme: dark) {
.logo {
filter: invert();
}
}

View File

@@ -1,95 +0,0 @@
import Image from "next/image";
import styles from "./page.module.css";
export default function Home() {
return (
<div className={styles.page}>
<main className={styles.main}>
<Image
className={styles.logo}
src="/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol>
<li>
Get started by editing <code>src/app/page.tsx</code>.
</li>
<li>Save and see your changes instantly.</li>
</ol>
<div className={styles.ctas}>
<a
className={styles.primary}
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className={styles.logo}
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
className={styles.secondary}
>
Read our docs
</a>
</div>
</main>
<footer className={styles.footer}>
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org
</a>
</footer>
</div>
);
}

13
src/app/robots.ts Normal file
View File

@@ -0,0 +1,13 @@
import type { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
const { HOSTNAME, PROTOCOL } = process.env;
return {
rules: {
userAgent: '*',
disallow: '',
},
sitemap: `${PROTOCOL}://${HOSTNAME}/sitemap.xml`,
}
}

88
src/app/sitemap.ts Normal file
View File

@@ -0,0 +1,88 @@
import type { MetadataRoute } from 'next'
import { LOCALES, DEFAULT_LOCALE } from '../i18n/i18n'
interface BlogPost {
slug: string;
modified: string;
}
interface WordPressPost {
slug: string;
modified: string;
}
async function getBlogPosts(): Promise<BlogPost[]> {
try {
const response = await fetch(`https://w.mssg.me/${DEFAULT_LOCALE}/wp-json/wp/v2/posts?per_page=100`);
if (!response.ok) return [];
const posts = await response.json() as WordPressPost[];
return posts.map((post: WordPressPost) => ({
slug: post.slug,
modified: post.modified
}));
} catch (error) {
console.error('Failed to fetch blog posts:', error);
return [];
}
}
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = 'https://mssg.me';
const staticPages = ['', '/blog', '/faq-next', '/privacy-policy', '/terms-of-use'];
const sitemap: MetadataRoute.Sitemap = [];
// Add static pages for all locales
for (const page of staticPages) {
for (const locale of LOCALES) {
const url = locale === DEFAULT_LOCALE
? `${baseUrl}${page}`
: `${baseUrl}/${locale}${page}`;
sitemap.push({
url,
lastModified: new Date(),
changeFrequency: page === '' ? 'monthly' : 'weekly',
priority: page === '' ? 1.0 : (page === '/blog' ? 0.8 : 0.5),
alternates: {
languages: Object.fromEntries(
LOCALES.map(loc => [
loc,
loc === DEFAULT_LOCALE
? `${baseUrl}${page}`
: `${baseUrl}/${loc}${page}`
])
)
}
});
}
}
// Add blog posts for all locales
const blogPosts = await getBlogPosts();
for (const post of blogPosts) {
for (const locale of LOCALES) {
const url = locale === DEFAULT_LOCALE
? `${baseUrl}/blog/${post.slug}`
: `${baseUrl}/${locale}/blog/${post.slug}`;
sitemap.push({
url,
lastModified: new Date(post.modified),
changeFrequency: 'monthly',
priority: 0.6,
alternates: {
languages: Object.fromEntries(
LOCALES.map(loc => [
loc,
loc === DEFAULT_LOCALE
? `${baseUrl}/blog/${post.slug}`
: `${baseUrl}/${loc}/blog/${post.slug}`
])
)
}
});
}
}
return sitemap;
}

View File

@@ -0,0 +1,36 @@
import Script from 'next/script';
interface GoogleTagManagerProps {
gtmId: string;
}
export function GoogleTagManagerHead({ gtmId }: GoogleTagManagerProps) {
return (
<Script
id="gtm-script"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','${gtmId}');
`,
}}
/>
);
}
export function GoogleTagManagerBody({ gtmId }: GoogleTagManagerProps) {
return (
<noscript>
<iframe
src={`https://www.googletagmanager.com/ns.html?id=${gtmId}`}
height="0"
width="0"
style={{ display: 'none', visibility: 'hidden' }}
/>
</noscript>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { LOCALES } from '@/i18n/i18n';
import { useLocale } from 'next-intl';
import { Link, usePathname } from '@/i18n/navigation';
import clsx from 'clsx';
export default function LanguageSwitcher() {
const locale = useLocale();
const pathname = usePathname();
const visibleLocales = LOCALES.filter(lang => lang !== 'ru');
return visibleLocales.map((lang) => (
<li key={lang}>
<Link href={pathname} locale={lang} className={clsx('lang-chooser', lang === locale && 'active')}>{lang.toUpperCase()}</Link>
</li>
))
}

View File

@@ -0,0 +1,402 @@
'use client';
import { useEffect } from 'react';
export default function MainScripts() {
useEffect(() => {
// Fixed header
const handleScroll = () => {
const header = document.querySelector('.main-header');
if (header) {
if (window.scrollY > 0) {
header.classList.add('fixed');
} else {
header.classList.remove('fixed');
}
}
};
window.addEventListener('scroll', handleScroll);
handleScroll(); // Initial check
// Share popup function
const windowPopup = (url: string, width: number, height: number) => {
const left = screen.width / 2 - width / 2;
const top = screen.height / 2 - height / 2;
window.open(
url,
'',
`menubar=no,toolbar=no,resizable=yes,scrollbars=yes,width=${width},height=${height},top=${top},left=${left}`
);
};
// Share popup on click
const shareLinks = document.querySelectorAll('.article-share-list > li > a');
const handleShareClick = (e: Event) => {
e.preventDefault();
const target = e.currentTarget as HTMLAnchorElement;
if (target?.href) {
windowPopup(target.href, 500, 300);
}
};
shareLinks.forEach(link => {
link.addEventListener('click', handleShareClick);
});
// FAQ slider functionality
const initFaqSlider = () => {
const topControls = document.querySelectorAll('.faq-top-controls-list > li');
const sliderList = document.querySelector('.faq-sublists') as HTMLElement;
const sliderListItems = document.querySelectorAll('.faq-sublists > li') as NodeListOf<HTMLElement>;
const questionPanes = document.querySelectorAll('.faq-panes-list > li');
const hash = location.hash.substr(2);
if (!topControls.length || !sliderList || !sliderListItems.length) return;
// Build initial state
topControls[0]?.classList.add('active');
sliderListItems[0]?.classList.add('active');
const parentWidth = sliderList.parentElement?.offsetWidth || 0;
sliderListItems.forEach(item => {
item.style.width = `${parentWidth}px`;
});
// Scroll to hash in URL
if (hash !== '') {
const listIndex = parseInt(hash.substring(0, hash.indexOf('-'))) - 1;
const questionItem = document.getElementById(hash);
if (listIndex >= 0 && questionItem) {
topControls.forEach((control, index) => {
control.classList.toggle('active', index === listIndex);
});
sliderListItems.forEach((item, index) => {
item.classList.toggle('active', index === listIndex);
});
const itemWidth = sliderListItems[0]?.offsetWidth || 0;
sliderList.style.transform = `translateX(-${listIndex * itemWidth}px)`;
questionItem.classList.add('opened');
const answer = questionItem.querySelector('.faq-panes-list-item-content') as HTMLElement;
if (answer) {
answer.style.display = 'block';
}
setTimeout(() => {
const header = document.querySelector('.main-header') as HTMLElement;
const headerHeight = header?.offsetHeight || 0;
window.scrollTo({
top: questionItem.offsetTop - headerHeight - 30,
behavior: 'smooth'
});
}, 1300);
}
}
// Fixed topics on desktop
const handleFaqScroll = () => {
if (window.innerWidth > 1239) {
const faqControls = document.querySelector('.faq-top-controls-list');
if (faqControls) {
if (window.scrollY > 70) {
faqControls.classList.add('fixed');
} else {
faqControls.classList.remove('fixed');
}
}
}
};
window.addEventListener('scroll', handleFaqScroll);
// Rebuild on resize
const handleResize = () => {
const parentWidth = sliderList.parentElement?.offsetWidth || 0;
sliderListItems.forEach(item => {
item.style.width = `${parentWidth}px`;
});
};
window.addEventListener('resize', handleResize);
// Top slider controls event
const handleTopControlClick = (e: Event) => {
const target = e.currentTarget as HTMLElement;
const thisIndex = Array.from(topControls).indexOf(target);
topControls.forEach((control, index) => {
control.classList.toggle('active', index === thisIndex);
});
sliderListItems.forEach((item, index) => {
item.classList.toggle('active', index === thisIndex);
});
const itemWidth = sliderListItems[0]?.offsetWidth || 0;
sliderList.style.transform = `translateX(-${thisIndex * itemWidth}px)`;
questionPanes.forEach(pane => {
pane.classList.remove('opened');
const answer = pane.querySelector('.faq-panes-list-item-content') as HTMLElement;
if (answer) {
answer.style.display = 'none';
}
});
if (window.scrollY > 0) {
window.scrollTo({ top: 0, behavior: 'smooth' });
}
};
topControls.forEach(control => {
control.addEventListener('click', handleTopControlClick);
});
// Question pane heading click
const questionHeadings = document.querySelectorAll('.faq-panes-list-item-header');
const handleQuestionClick = (e: Event) => {
const target = e.currentTarget as HTMLElement;
const parentLi = target.closest('li');
const answer = target.nextElementSibling as HTMLElement;
if (parentLi && answer) {
window.location.hash = `#q${parentLi.id}`;
if (parentLi.classList.contains('opened')) {
parentLi.classList.remove('opened');
answer.style.display = 'none';
} else {
parentLi.classList.add('opened');
answer.style.display = 'block';
}
}
};
questionHeadings.forEach(heading => {
heading.addEventListener('click', handleQuestionClick);
});
// Accordion functionality
const accordionHeaders = document.querySelectorAll('.accordeon-header');
const handleAccordionClick = (e: Event) => {
const target = e.currentTarget as HTMLElement;
const content = target.nextElementSibling as HTMLElement;
if (content) {
if (target.classList.contains('visible')) {
content.style.display = 'none';
} else {
content.style.display = 'block';
}
target.classList.toggle('visible');
}
};
accordionHeaders.forEach(header => {
header.addEventListener('click', handleAccordionClick);
});
return () => {
window.removeEventListener('scroll', handleFaqScroll);
window.removeEventListener('resize', handleResize);
topControls.forEach(control => {
control.removeEventListener('click', handleTopControlClick);
});
questionHeadings.forEach(heading => {
heading.removeEventListener('click', handleQuestionClick);
});
accordionHeaders.forEach(header => {
header.removeEventListener('click', handleAccordionClick);
});
};
};
// Initialize FAQ slider if container exists
const faqContainer = document.querySelector('.faq-container-right');
let faqCleanup: (() => void) | undefined;
if (faqContainer) {
faqCleanup = initFaqSlider();
}
// Pricing FAQ list
const pricingFaqItems = document.querySelectorAll('.section-page-pricing-faq-list > li');
if (pricingFaqItems.length > 0) {
const firstItem = pricingFaqItems[0];
const firstHeader = firstItem.querySelector('h4');
const firstContent = firstItem.querySelector('.section-page-pricing-faq-list-item-content') as HTMLElement;
if (firstHeader && firstContent) {
firstHeader.classList.add('active');
firstContent.style.display = 'block';
}
}
const handlePricingFaqClick = (e: Event) => {
const target = e.currentTarget as HTMLElement;
const content = target.nextElementSibling as HTMLElement;
if (content) {
if (target.classList.contains('active')) {
target.classList.remove('active');
content.style.display = 'none';
} else {
target.classList.add('active');
content.style.display = 'block';
}
}
};
const pricingFaqHeaders = document.querySelectorAll('.section-page-pricing-faq-list > li > h4');
pricingFaqHeaders.forEach(header => {
header.addEventListener('click', handlePricingFaqClick);
});
// Mobile menu
const handleMobileMenuClick = (e: Event) => {
const target = e.currentTarget as HTMLElement;
const mobileMenu = document.querySelector('.mobile-menu');
const body = document.body;
if (target.classList.contains('active')) {
target.classList.remove('active');
mobileMenu?.classList.remove('active');
body.classList.remove('overflow-hidden');
} else {
target.classList.add('active');
mobileMenu?.classList.add('active');
body.classList.add('overflow-hidden');
}
};
const mobileMenuBtn = document.querySelector('.mobile-menu-btn');
if (mobileMenuBtn) {
mobileMenuBtn.addEventListener('click', handleMobileMenuClick);
}
// Pricing switcher
const handleSwitcherClick = (e: Event) => {
const target = e.currentTarget as HTMLElement;
const priceEl = document.getElementById('price');
if (priceEl) {
const priceMonthly = priceEl.getAttribute('data-price-monthly');
const priceAnnually = priceEl.getAttribute('data-price-annually');
const price = target.classList.contains('active') ? priceAnnually : priceMonthly;
target.classList.toggle('active');
priceEl.innerHTML = price || '';
}
};
const switcher = document.getElementById('switcher');
if (switcher) {
switcher.addEventListener('click', handleSwitcherClick);
}
// Cookie functions
const setCookie = (name: string, value: string, days: number) => {
let expires = '';
if (days) {
const date = new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
expires = '; expires=' + date.toUTCString();
}
document.cookie = name + '=' + (value || '') + expires + '; path=/';
};
const getCookie = (name: string): string | null => {
const nameEQ = name + '=';
const ca = document.cookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
}
return null;
};
// Article links with images no underline
const articleText = document.querySelector('.section-article-text');
if (articleText) {
const articleLinks = articleText.querySelectorAll('a');
articleLinks.forEach(link => {
if (link.querySelector('img')) {
link.classList.add('no-underline');
}
});
}
// Language chooser
const handleLanguageChange = (e: Event) => {
const target = e.currentTarget as HTMLElement;
const destinationLang = target.getAttribute('data-lang');
if (destinationLang) {
setCookie('lang', destinationLang, 7);
}
};
const langChoosers = document.querySelectorAll('.lang-chooser');
langChoosers.forEach(chooser => {
chooser.addEventListener('click', handleLanguageChange);
});
// Cookie acceptance
const termsAccepted = getCookie('terms_accept');
const cookieMessage = document.getElementById('cookie-message');
if (!termsAccepted && cookieMessage) {
cookieMessage.classList.add('visible');
const handleCloseCookie = () => {
setCookie('terms_accept', 'true', 365);
if (window.innerWidth > 767) {
cookieMessage.style.transform = 'translateY(100px)';
cookieMessage.style.opacity = '0';
} else {
cookieMessage.style.transform = 'translateY(-100px)';
cookieMessage.style.opacity = '0';
}
setTimeout(() => {
cookieMessage.remove();
}, 400);
};
const closeCookieBtn = document.getElementById('close-cookie');
if (closeCookieBtn) {
closeCookieBtn.addEventListener('click', handleCloseCookie);
}
} else if (cookieMessage) {
cookieMessage.remove();
}
// Cleanup function
return () => {
window.removeEventListener('scroll', handleScroll);
shareLinks.forEach(link => {
link.removeEventListener('click', handleShareClick);
});
pricingFaqHeaders.forEach(header => {
header.removeEventListener('click', handlePricingFaqClick);
});
if (mobileMenuBtn) {
mobileMenuBtn.removeEventListener('click', handleMobileMenuClick);
}
if (switcher) {
switcher.removeEventListener('click', handleSwitcherClick);
}
langChoosers.forEach(chooser => {
chooser.removeEventListener('click', handleLanguageChange);
});
if (faqCleanup) {
faqCleanup();
}
};
}, []);
return null; // This component doesn't render anything
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}
/*# sourceMappingURL=normalize.min.css.map */

View File

@@ -0,0 +1,120 @@
"use client";
import { useEffect } from 'react';
import lax from 'lax.js';
function setCookie(name: string, value: string, days: number) {
let expires = '';
if (days) {
const date = new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
expires = '; expires=' + date.toUTCString();
}
document.cookie = name + '=' + (value || '') + expires + '; path=/';
}
function getCookie(name: string) {
const nameEQ = name + '=';
const ca = document.cookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
export default function MainEffects() {
useEffect(() => {
// Cookie message logic
const cookieMessageEl = document.getElementById('cookie-message');
const closeCookieIconEl = document.getElementById('close-cookie');
const termsAccepted = getCookie('terms_accept');
if (cookieMessageEl && closeCookieIconEl) {
if (!termsAccepted) {
cookieMessageEl.classList.add('visible');
closeCookieIconEl.addEventListener('click', function handler() {
setCookie('terms_accept', 'true', 365);
cookieMessageEl.classList.add('hide');
setTimeout(function() {
cookieMessageEl.remove();
}, 400);
closeCookieIconEl.removeEventListener('click', handler);
});
} else {
cookieMessageEl.remove();
}
}
const langClick = (e: Event) => {
const target = e.target as HTMLElement;
const lang = target.getAttribute('data-tolang');
if (lang === 'ru' || lang === 'en') {
setCookie('lang', lang, 7);
}
};
document.addEventListener('click', langClick);
// Lax.js logic
lax.init();
lax.addDriver('scrollY', () => window.scrollY);
lax.addElements(
'.presentation-hero-left, .presentation-hero-right',
{
scrollY: {
translateZ: [['elInY', 'elCenterY', 'elOutY'], [-50, -1, -1]],
translateY: [['elInY', 'elCenterY', 'elOutY'], [40, 0, -30]],
scale: [['elInY', 'elCenterY', 'elOutY'], [0.9, 1, 1]],
rotateX: [['elInY', 'elCenterY', 'elOutY'], [20, 0, 0]],
},
}
);
lax.addElements(
'.presentation-hero-center, .multi-link-hero > img, .messengers-hero > img, .store-hero > img, .builder-hero > img',
{
scrollY: {
translateY: [['elInY', 'elCenterY', 'elOutY'], [10, 0, -20]],
rotateX: [['elInY', 'elCenterY', 'elOutY'], [20, 0, 0]],
},
}
);
lax.addElements(
'.multi-link-hero__icon',
{
scrollY: {
translateZ: [['elInY', 'elCenterY', 'elOutY'], [50, 1, 1]],
rotateX: [['elInY', 'elCenterY', 'elOutY'], [20, 0, 0]],
},
}
);
lax.addElements(
'.icon-hey, .message, .icon-cart, .order, .customize',
{
scrollY: {
translateZ: [['elInY', 'elCenterY', 'elOutY'], [50, 1, 1]],
rotateX: [['elInY', 'elCenterY', 'elOutY'], [20, 0, 0]],
translateY: [['elInY', 'elCenterY', 'elOutY'], [80, 0, -60]],
},
}
);
lax.addElements(
'.blocks',
{
scrollY: {
translateZ: [['elInY', 'elCenterY', 'elOutY'], [-50, -1, -1]],
rotateX: [['elInY', 'elCenterY', 'elOutY'], [20, 0, 0]],
translateY: [['elInY', 'elCenterY', 'elOutY'], [80, 0, -60]],
},
}
);
setTimeout(function() {
document.querySelectorAll('.presentation-hero > img').forEach(function(el) {
el.classList.add('no-transition');
});
}, 600);
// Cleanup
return () => {
document.removeEventListener('click', langClick);
};
}, []);
return null;
}

View File

@@ -0,0 +1,71 @@
/* PAGE - 404 */
.body-404 .main-header,
.body-404 .main-footer {
display: none;
}
.body-404 .page-wrapper {
display: flex;
flex-direction: column;
padding: 0;
min-height: 100vh;
}
.header-404 {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
width: 100%;
height: 114px;
}
a.header-404-logo,
.header-404-logo {
display: block;
height: 44px;
}
.header-404-logo > img {
display: block;
height: 100%;
width: auto;
}
.content-404 {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
flex-grow: 1;
padding: 0 20px;
}
.content-404-block {
width: 600px;
max-width: 100%;
text-align: center;
padding: 0 0 176px 0;
}
.content-404-block > h1 {
font-weight: bold;
font-size: 36px;
line-height: 42px;
margin: 0 0 10px 0;
}
.content-404-block > p {
font-size: 24px;
line-height: 30px;
margin: 0 0 40px 0;
}
.content-404-block > a.button {
font-weight: bold;
}
.body-404 #mssgme_widget {
display: none;
}

View File

@@ -0,0 +1,237 @@
/* SINGLE - ARTICLE */
/* SINGLE - ARTICLE - Section*/
.section-article {
padding: 40px 0 25px 0;
overflow: hidden;
}
.section-article-content {
width: 1200px;
max-width: 100%;
margin: 0 auto;
}
.section-article-header {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 0 40px 0 0;
}
.section-article-header-img {
width: calc(100% - 580px);
padding-top: 43%;
position: relative;
}
.section-article-header-img > img {
display: block;
width: 100%;
height: 100%;
border-radius: 6px;
position: absolute;
top: 0;
left: 0;
object-fit: cover;
}
.section-article-header-heading {
width: 500px;
max-width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.section-article-header-heading-content > h1 {
font-weight: bold;
font-size: 38px;
line-height: 48px;
margin: 0 0 10px 0;
}
.section-article-header-heading-content > span {
display: block;
font-size: 16px;
line-height: 24px;
color: #8e8e93;
}
.section-article-text {
width: 800px;
max-width: 100%;
padding: 100px 0 50px 0;
margin: 0 auto;
font-size: 18px;
line-height: 30px;
}
.section-article-text p {
margin: 0 0 30px 0;
}
.section-article-text h1,
.section-article-text h2 {
font-weight: bold;
font-size: 26px;
line-height: 36px;
margin: 0 0 20px 0;
}
.section-article-text h3 {
font-weight: bold;
font-size: 22px;
line-height: 32px;
margin: 0 0 20px 0;
}
.section-article-text h4 {
font-weight: bold;
font-size: 18px;
line-height: 28px;
margin: 0 0 20px 0;
}
.section-article-text h5 {
font-weight: bold;
font-size: 14px;
line-height: 24px;
margin: 0 0 20px 0;
}
.section-article-text h6 {
font-weight: bold;
font-size: 14px;
line-height: 24px;
margin: 0 0 20px 0;
}
.section-article-text ol {
list-style-type: decimal;
margin: 30px 0 30px 20px;
}
.section-article-text ul {
list-style-type: disc;
margin: 30px 0 30px 20px;
}
.section-article-text a {
display: inline-block;
vertical-align: top;
position: relative;
color: #007aff;
}
.section-article-text a.no-underline::after {
display: none;
}
.section-article-text a::after {
content: '';
display: block;
width: 100%;
height: 2px;
background-color: #007aff;
position: absolute;
bottom: 0;
left: 0;
}
.section-article-text em {
display: inline-block;
vertical-align: top;
position: relative;
font-style: normal;
text-decoration: none;
background-color: #e4f0ff;
}
.section-article-text img,
.section-article-text video,
.section-article-text iframe {
width: auto;
max-width: 100%;
height: auto;
margin: 30px auto;
}
.section-article-text img.full-width-img {
max-width: 100vw;
width: 100vw;
margin-left: calc((100% - 100vw) / 2);
}
.section-article-text p img,
.section-article-text a img {
margin-bottom: 0;
margin-top: 0;
}
.aligncenter {
display: block;
margin: 30px auto;
}
.article-share-list {
width: 800px;
max-width: 100%;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.article-share-list > li {
width: 154px;
height: 46px;
border-radius: 6px;
background-color: #111;
text-align: center;
font-weight: 500;
font-size: 15px;
line-height: 46px;
color: #fff;
margin: 0 5px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.article-share-list > li:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.2);
}
.article-share-list > li > a {
display: block;
width: 100%;
height: 100%;
color: #fff;
}
.article-share-list > li > a > svg {
margin: 0 4px 0 0;
font-size: 20px;
position: relative;
top: 2px;
}
.article-share-list > li.article-share-list-fb {
background-color: #4f78a3;
}
.article-share-list > li.article-share-list-twitter {
background-color: #1da1f2;
}
.article-share-list > li.article-share-list-vk {
background-color: #4f78a3;
}
.section-fp-news-content-article > h3 {
text-align: center;
margin: 0 0 60px 0;
}

View File

@@ -0,0 +1,116 @@
/* BASIC STYLES */
/* ------------ */
* {
-webkit-box-sizing: border-box;
box-sizing: border-box;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
::-moz-selection {
background: #111;
color: #fff;
}
::selection {
background: #111;
color: #fff;
}
html,
body {
width: 100%;
max-width: 100vw;
background-color: #fafafa;
}
body {
font-family: 'Helvetica Neue', sans-serif;
font-size: 16px;
line-height: 26px;
font-weight: normal;
font-style: normal;
color: #111;
}
a,
a:hover,
a:focus,
a:active,
a:visited {
text-decoration: none;
outline: none;
color: #111;
}
ul,
ol {
margin: 0;
padding: 0;
list-style: none;
}
input,
textarea,
select {
border: none;
border-radius: 0;
outline: none;
padding: 0;
margin: 0;
display: block;
width: 100%;
background-color: rgba(255, 255, 255, 0);
}
textarea {
resize: none;
}
button {
background: transparent;
border-radius: 0;
border: none;
outline: none;
padding: 0;
}
.clearfix::before,
.clearfix::after {
content: '';
display: table;
}
.clearfix::after {
clear: both;
}
.overflow-hidden {
overflow: hidden;
height: 100%;
}
.text-center {
text-align: center;
}
.page-wrapper {
position: relative;
min-height: calc(100vh - 200px);
padding: 114px 0 0 0;
}
.page-wrapper > main {
width: 100%;
}
.common-content {
padding: 0 20px;
}
.mobile-only {
display: none;
}
.desktop-only {
display: block;
}

View File

@@ -0,0 +1,165 @@
/* CATEGORY - BLOG */
.section-fp-news {
padding: 55px 0 60px 0;
}
.section-fp-news-content {
width: 1120px;
max-width: 100%;
margin: 0 auto;
}
.section-fp-news-content > h3 {
font-weight: bold;
font-size: 36px;
line-height: 46px;
margin: 0 0 60px 0;
padding: 0 0 0 30px;
}
.section-fp-news-content > h3 > span {
color: #666;
}
.section-fp-news-list {
width: 100%;
display: flex;
flex-wrap: wrap;
}
.section-fp-news-list > li {
position: relative;
width: 350px;
max-width: 100%;
margin: 0 35px 0 0;
box-shadow: 0 0 20px 0 rgba(31, 45, 61, 0.05);
background-color: #ffffff;
border-radius: 6px;
overflow: hidden;
transition: transform 0.6s ease, box-shadow 0.6s ease;
}
.section-fp-news-list > li:hover {
transform: translateY(-5px);
box-shadow: 0 5px 40px 0 rgba(31, 45, 61, 0.15);
}
.section-fp-news-list > li:last-of-type {
margin: 0;
}
.section-fp-news-list > li > a {
display: block;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
.section-fp-news-list-item-preview {
width: 100%;
padding-top: 50%;
position: relative;
overflow: hidden;
}
.section-fp-news-list-item-preview > img {
display: block;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
object-fit: cover;
}
.section-fp-news-list-item-content {
padding: 30px;
}
.section-fp-news-list-item-content > h4 {
font-weight: bold;
font-size: 18px;
line-height: 26px;
margin: 0 0 6px 0;
}
.section-fp-news-list-item-content > span {
display: block;
font-size: 13px;
line-height: 18px;
color: #8e8e93;
}
.section-fp-intro-content-subheading {
font-size: 24px;
line-height: 34px;
margin: 0;
text-align: center;
}
.section-fp-intro-content .cta-tutorial {
display: block;
}
.section-category-blog {
padding: 80px 0 60px 0;
}
.section-category-blog-content {
width: 1120px;
max-width: 100%;
margin: 0 auto;
}
.section-category-blog-list {
justify-content: flex-start;
}
.section-category-blog-list > li {
margin: 0 35px 35px 0;
}
.section-category-blog-list > li:nth-of-type(3n),
.section-category-blog-list > li:last-of-type {
margin: 0 0 35px 0;
}
.blog-pagination {
margin: 45px 0 0 0;
text-align: center;
}
.blog-pagination-list {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: center;
font-weight: 500;
font-size: 16px;
line-height: 30px;
}
.blog-pagination-list > li {
margin: 0 3px;
}
.blog-pagination-list > li > a {
display: block;
width: 34px;
height: 34px;
border: 2px solid transparent;
border-radius: 4px;
transition: color 0.3s ease;
}
.blog-pagination-list > li.active > a {
border-color: #111;
pointer-events: none;
}
.blog-pagination-list > li:not(.active) > a:hover {
color: #007aff;
}

View File

@@ -0,0 +1,57 @@
/* COOKIE MESSAGE */
.cookie-message {
display: none;
padding: 8px 52px 8px 20px;
background-color: #fff;
border-radius: 10px;
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.1);
position: fixed;
bottom: 24px;
left: 20px;
z-index: 1000;
font-size: 13px;
line-height: 20px;
transition: transform 0.3s ease, opacity 0.3s ease;
}
.cookie-message.visible {
display: block;
}
.cookie-message > span {
display: block;
}
.cookie-message > a {
display: inline-block;
vertical-align: top;
font-weight: 500;
color: #007aff;
transition: color 0.3s ease;
}
.cookie-message > a:hover {
color: #0061e6;
}
.close-cookie {
display: block;
width: 32px;
height: 32px;
border-radius: 4px;
background-color: #f5f5f5;
position: absolute;
top: calc(50% - 16px);
right: 10px;
cursor: pointer;
background-image: url('/img/ui/icon-close-coockies.svg');
background-size: 8px 8px;
background-position: center;
background-repeat: no-repeat;
transition: background-color 0.3s ease;
}
.close-cookie:hover {
background-color: #f1f1f1;
}

View File

@@ -0,0 +1,113 @@
/* PAGE - DEFAULT */
.page-default-wrapper {
padding: 50px 25px;
}
.page-default-content {
width: 800px;
max-width: 100%;
margin: 0 auto;
}
.page-default-content > h1 {
font-weight: bold;
font-size: 38px;
line-height: 48px;
margin: 0 0 20px 0;
}
.page-default-content-text {
width: 800px;
max-width: 100%;
padding: 0 0 50px 0;
margin: 0 auto;
font-size: 18px;
line-height: 30px;
}
.page-default-content-text p {
margin: 0 0 30px 0;
}
.page-default-content-text h1,
.page-default-content-text h2 {
font-weight: bold;
font-size: 26px;
line-height: 36px;
margin: 0 0 20px 0;
}
.page-default-content-text h3 {
font-weight: bold;
font-size: 22px;
line-height: 32px;
margin: 0 0 20px 0;
}
.page-default-content-text h4 {
font-weight: bold;
font-size: 18px;
line-height: 28px;
margin: 0 0 20px 0;
}
.page-default-content-text h5 {
font-weight: bold;
font-size: 14px;
line-height: 24px;
margin: 0 0 20px 0;
}
.page-default-content-text h6 {
font-weight: bold;
font-size: 14px;
line-height: 24px;
margin: 0 0 20px 0;
}
.page-default-content-text ol {
list-style-type: decimal;
margin: 30px 0 30px 20px;
}
.page-default-content-text ul {
list-style-type: disc;
margin: 30px 0 30px 20px;
}
.page-default-content-text a {
display: inline-block;
vertical-align: top;
position: relative;
color: #007aff;
}
.page-default-content-text a::after {
content: '';
display: block;
width: 100%;
height: 2px;
background-color: #007aff;
position: absolute;
bottom: 0;
left: 0;
}
.page-default-content-text em {
display: inline-block;
vertical-align: top;
position: relative;
font-style: normal;
text-decoration: none;
background-color: #e4f0ff;
}
.page-default-content-text img,
.page-default-content-text video,
.page-default-content-text iframe {
width: 100%;
max-width: 100%;
height: auto;
margin: 0 30px;
}

View File

@@ -0,0 +1,336 @@
/* PAGE - FAQ */
.page-faq {
padding: 50px 0;
overflow: hidden;
}
.page-faq-content {
width: 1120px;
max-width: 100%;
margin: 0 auto;
}
.page-faq-container {
overflow: hidden;
width: 1120px;
max-width: 100%;
margin: 0 auto;
}
.faq-container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.faq-container-left {
position: relative;
z-index: 1;
width: 430px;
max-width: 100%;
}
.faq-container-right {
width: 670px;
max-width: 100%;
}
.faq-top-controls-list.fixed {
position: fixed;
width: 100%;
max-width: 430px;
top: 94px;
}
.faq-top-controls-list > li {
position: relative;
min-height: 70px;
padding: 24px 40px 16px 90px;
border-radius: 6px;
margin: 0 0 10px 0;
cursor: pointer;
transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease;
}
.faq-top-controls-list > li:hover {
background-color: rgba(255, 255, 255, 0.8);
}
.faq-top-controls-list > li:last-of-type {
margin: 0;
}
.faq-top-controls-list > li.active {
color: #007aff;
background-color: #fff;
box-shadow: 0 0 30px 0 rgba(31, 45, 61, 0.03);
}
.faq-top-controls-list > li > img {
display: block;
width: 70px;
height: auto;
position: absolute;
top: 0;
left: 0;
transition: opacity 0.3s ease;
}
.faq-top-controls-list > li > img.active {
opacity: 0;
pointer-events: none;
}
.faq-top-controls-list > li.active > img.default {
opacity: 0;
pointer-events: none;
}
.faq-top-controls-list > li.active > img.active {
opacity: 1;
pointer-events: auto;
}
.faq-top-controls-list > li > h2 {
font-weight: 500;
font-size: 18px;
line-height: 24px;
margin: 0 0 2px 0;
}
.faq-top-controls-list > li > p {
font-size: 13px;
line-height: 20px;
opacity: 0.5;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
margin: 0;
}
.faq-sublists {
width: 10000px;
transition: -webkit-transform 1s ease;
transition: transform 1s ease;
transition: transform 1s ease, -webkit-transform 1s ease;
}
.faq-sublists > li {
float: left;
width: 670px;
max-width: 100%;
opacity: 0;
pointer-events: none;
transform: scale(0.9);
transition: opacity 0.3s ease, transform 1s ease;
}
.faq-sublists > li.active {
opacity: 1;
-webkit-transform: scale(1);
transform: scale(1);
pointer-events: auto;
transition: opacity 0.5s ease, transform 1s ease;
}
.faq-panes-list {
counter-reset: faqlist;
}
.faq-panes-list > li {
margin: 0 0 10px 0;
}
.faq-panes-list > li:last-of-type {
margin: 0;
}
.faq-panes-list-item-header {
position: relative;
padding: 20px 115px 20px 55px;
background-color: #fff;
border-radius: 6px;
box-shadow: 0 0 30px 0 rgba(31, 45, 61, 0.03);
cursor: pointer;
}
.faq-panes-list-item-header > h3 {
font-weight: 500;
font-size: 16px;
line-height: 22px;
margin: 0;
}
.accordeon-header {
position: relative;
padding: 10px 40px 10px 15px;
background-color: #fff;
border-radius: 6px;
box-shadow: 0 0 30px 0 rgba(31, 45, 61, 0.03);
cursor: pointer;
}
.faq-panes-list-item-content h4.accordeon-heading {
font-weight: 500;
font-size: 16px;
margin: 0;
}
.accordeon-header > .faq-panes-list-item-status {
right: 15px;
top: 13px;
}
.accordeon-header:hover .faq-panes-list-item-status {
background-color: #007aff;
}
.faq-panes-list > li.opened .accordeon-header.visible > .faq-panes-list-item-status::after {
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
}
.accordeon-content {
padding: 15px;
display: none;
}
.faq-panes-list-item-counter {
display: block;
position: absolute;
top: 0;
left: 30px;
font-size: 13px;
line-height: 62px;
opacity: 0.5;
}
.faq-panes-list-item-counter::before {
counter-increment: faqlist;
content: counter(faqlist, decimal-leading-zero);
}
.faq-panes-list-item-status {
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #dedede;
position: absolute;
top: 21px;
right: 30px;
transition: background-color 0.3s ease;
}
.faq-panes-list-item-header:hover .faq-panes-list-item-status {
background-color: #007aff;
}
.faq-panes-list-item-status::before {
content: '';
display: block;
width: 10px;
height: 2px;
border-radius: 1px;
background-color: #fff;
position: absolute;
top: calc(50% - 1px);
left: calc(50% - 5px);
}
.faq-panes-list-item-status::after,
.faq-panes-list > li.opened .accordeon-header > .faq-panes-list-item-status::after {
content: '';
display: block;
width: 2px;
height: 10px;
border-radius: 1px;
background-color: #fff;
position: absolute;
top: calc(50% - 5px);
left: calc(50% - 1px);
transform: none;
transition: transform 0.3s ease;
}
.faq-panes-list > li.opened .faq-panes-list-item-status::after {
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
}
.faq-panes-list-item-content {
display: none;
padding: 20px 55px 10px 55px;
font-size: 15px;
line-height: 24px;
}
.faq-panes-list-item-content p {
margin: 0 0 20px 0;
}
.faq-panes-list-item-content > *:last-child {
margin-bottom: 0;
}
.faq-panes-list-item-content img,
.faq-panes-list-item-content iframe,
.faq-panes-list-item-content video {
display: block;
max-width: 100%;
height: auto;
margin: 20px auto;
}
.faq-panes-list-item-content h1,
.faq-panes-list-item-content h2,
.faq-panes-list-item-content h3,
.faq-panes-list-item-content h4,
.faq-panes-list-item-content h5,
.faq-panes-list-item-content h6 {
font-size: 17px;
line-height: 26px;
margin: 0 0 20px 0;
}
.faq-panes-list-item-content ol,
.faq-panes-list-item-content ul {
margin: 0 0 20px 16px;
}
.faq-panes-list-item-content ol {
list-style-type: decimal;
}
.faq-panes-list-item-content ul {
list-style-type: square;
}
.faq-panes-list-item-content ol li,
.faq-panes-list-item-content ul li {
margin: 0 0 10px 0;
}
.faq-panes-list-item-content ol li:last-of-type,
.faq-panes-list-item-content ul li:last-of-type {
margin: 0;
}
.faq-panes-list-item-content strong,
.faq-panes-list-item-content b {
font-weight: 500;
}
.faq-panes-list-item-content em {
font-style: normal;
font-weight: 500;
background-color: #d5e6fb;
text-decoration: none;
}
.faq-panes-list-item-content a {
font-weight: 500;
color: #007aff;
text-decoration: underline;
}

View File

@@ -0,0 +1,130 @@
/* FOOTER */
.main-footer {
padding: 40px 20px 50px 20px;
overflow: hidden;
}
.main-footer-content {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
width: 1040px;
max-width: 100%;
margin: 0 auto;
}
.main-footer-content-left {
padding: 8px 0 0 0;
}
.footer-nav-list {
display: flex;
flex-wrap: wrap;
}
.footer-nav-list > li {
max-width: 100%;
margin: 0 88px 0 0;
}
.footer-nav-list > li:last-of-type {
margin: 0;
}
.footer-nav-list > li > h6 {
font-weight: normal;
font-size: 17px;
line-height: 24px;
color: #8e8e93;
margin: 0 0 17px 0;
}
.footer-menu-list {
font-weight: 500;
font-size: 16px;
line-height: 30px;
}
.footer-menu-list > li > a {
max-width: 100%;
display: inline-block;
vertical-align: top;
transition: color 0.3s ease;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.footer-menu-list > li > a:hover {
color: #007aff;
}
.footer-socials-list {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
}
.footer-socials-list > li {
width: 40px;
height: 40px;
margin: 0 0 0 15px;
}
.footer-socials-list > li:first-of-type {
margin: 0;
}
.footer-socials-list > li > a {
display: block;
width: 100%;
height: 100%;
font-size: 24px;
line-height: 40px;
text-align: center;
}
.footer-socials-list > li > a > i {
transition: transform 0.3s ease;
}
.footer-socials-list > li > a:hover > i {
transform: translateY(-3px);
}
.lang-chooser-list {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
margin: 35px 0 0 0;
font-weight: 500;
font-size: 16px;
line-height: 22px;
}
.lang-chooser-list > li > a {
display: block;
padding: 10px 3px;
color: #8e8e93;
transition: color 0.3s ease;
}
.lang-chooser-list > li > a:hover,
.lang-chooser-list > li > a.active {
color: #111;
}
.lang-chooser-list > li > a.active {
pointer-events: none;
}
.lang-chooser-list-mobile {
display: none;
}
.main-footer-content-right {
display: flex;
align-items: flex-end;
justify-content: center;
}

View File

@@ -0,0 +1,308 @@
/* HEADER */
.main-header {
position: fixed;
z-index: 100;
width: 100%;
height: 114px;
background-color: #fafafa;
will-change: transform;
padding: 34px 20px;
transition: box-shadow 0.3s ease, padding 0.3s ease, height 0.3s ease;
}
.main-header.fixed {
box-shadow: 0 0 20px 0 rgba(31, 45, 61, 0.1);
padding: 13px 20px;
height: 78px;
}
.main-header-content {
width: 1200px;
max-width: 100%;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
}
.main-header-left {
display: flex;
flex-wrap: wrap;
align-items: center;
}
.header-logo {
display: block;
height: 44px;
margin: 0 60px 0 0;
}
.header-logo > img {
display: block;
height: 100%;
width: auto;
}
.header-menu-list {
display: flex;
flex-wrap: wrap;
height: 100%;
}
.header-menu-list > li {
position: relative;
height: 100%;
}
.header-menu-list > li:last-of-type,
.header-menu-list > li:only-of-type {
margin: 0;
}
.header-menu-list > li > a,
.header-menu-list > li > span {
position: relative;
display: block;
height: 100%;
padding: 0 20px;
font-weight: 500;
font-size: 18px;
line-height: 50px;
cursor: pointer;
transition: color 0.3s ease;
}
.header-menu-list > li:not(.with-submenu) > a:hover {
color: #007aff;
}
.header-menu-list > li.with-submenu > a,
.header-menu-list > li.with-submenu > span {
padding-right: 41px;
}
.header-menu-list > li.with-submenu > a::after,
.header-menu-list > li.with-submenu > span::after {
box-sizing: border-box;
content: '';
display: block;
width: 10px;
height: 10px;
background-image: url('/img/ui/icon-arrow.svg');
-webkit-background-size: contain;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
position: absolute;
right: 20px;
top: 21px;
transition: transform 0.3s ease;
}
.header-menu-list > li.with-submenu:hover > a::after,
.header-menu-list > li.with-submenu:hover > span::after {
transform: rotate(-180deg);
}
.header-buttons {
display: flex;
flex-wrap: wrap;
}
.header-submenu {
display: flex;
width: 100%;
width: 300px;
background-color: #fff;
padding: 19px 9px 16px 9px;
position: absolute;
top: 50px;
left: -10px;
border-radius: 10px;
border: 1px solid #ededde;
opacity: 0;
pointer-events: none;
transform: translateY(-10px) scale(0.9);
transition: transform 0.3s ease, opacity 0.3s ease;
}
.header-menu-list > li.with-submenu:hover .header-submenu {
opacity: 1;
pointer-events: auto;
transform: translateY(0) scale(1);
}
.header-submenu-column {
width: 50%;
padding: 0 20px;
}
.header-submenu-column > h6 {
font-weight: 500;
font-size: 14px;
line-height: 20px;
color: #007aff;
margin: 0 0 3px 0;
}
.header-submenu-column:last-of-type > h6 {
color: #5856d6;
}
.header-submenu-column > ul {
font-weight: bold;
font-size: 18px;
line-height: 30px;
}
.header-submenu-column > ul > li > a {
display: inline-block;
vertical-align: top;
transition: color 0.3s ease;
}
.header-submenu-column > ul > li > a:hover {
color: #007aff;
}
/* PRODUCTHUNT HEADER */
.producthunt-header {
width: 100%;
height: 60px;
padding: 0 30px;
background-color: #fff;
position: absolute;
top: 0;
left: 0;
display: flex;
justify-content: space-between;
align-items: center;
transition: transform 0.3s ease;
border-bottom: 1px solid #eee;
}
.main-header-with-product-hunt ~ .page-wrapper {
padding: 174px 0 0 0;
}
.producthunt-header-left {
display: flex;
align-items: center;
font-size: 16px;
line-height: 20px;
color: #000;
flex: 1;
}
.producthunt-logo {
display: block;
width: 40px;
height: 40px;
margin: 0 20px 0 0;
}
.producthunt-header-left > span {
display: block;
max-width: calc(100% - 60px);
}
.producthunt-header-left > span > i {
border: #cb5c3b dashed 1px;
padding: 2px 7px 3px;
margin: 0px 4px;
background: rgba(255, 255, 255, 0.15);
font-weight: 500;
font-style: normal;
}
.producthunt-header-right > .button-blue {
background-color: #000;
padding: 6px 20px;
}
.producthunt-header-right > .button-blue > i {
display: none;
}
.main-header-with-product-hunt {
padding-top: 80px;
height: 174px;
}
.main-header-with-product-hunt.fixed .producthunt-header {
transform: translateY(-100%);
}
@media (max-width: 1279px) {
.main-header.main-header-with-product-hunt {
padding-top: 80px;
height: 174px;
}
.main-header.main-header-with-product-hunt.fixed {
height: 78px;
}
}
@media (max-width: 767px) {
.producthunt-header {
padding: 0 10px;
}
.producthunt-logo {
margin: 0 10px 0 0;
}
.producthunt-header-right > .button-blue {
padding: 6px 7px;
}
.producthunt-header-right > .button-blue > span,
.producthunt-header-left > span > span {
display: block;
}
.producthunt-header-right > .button-blue > i {
display: none;
}
.main-header.main-header-with-product-hunt {
height: 130px;
}
.main-header-with-product-hunt ~ .page-wrapper {
padding: 130px 0 0 0;
}
.main-header.main-header-with-product-hunt.fixed {
height: 54px;
}
.main-header-with-product-hunt .mobile-menu-btn {
height: 74px;
top: 60px;
transition: top 0.3s ease;
}
.main-header-with-product-hunt.fixed .mobile-menu-btn {
height: 54px;
top: 0;
}
.main-header-with-product-hunt + .mobile-menu {
padding: 130px 0 0 0;
}
.main-header-with-product-hunt.fixed + .mobile-menu {
padding: 70px 0 0 0;
}
}
@media (max-width: 374px) {
.producthunt-header-left {
font-size: 13px;
}
}

View File

@@ -0,0 +1,14 @@
@import 'normalize.css';
@import 'basic.css';
@import 'ui.css';
@import 'cookie.css';
@import 'header.css';
@import 'footer.css';
@import 'category.css';
@import 'pricing.css';
@import 'article.css';
@import 'default.css';
@import 'faq.css';
@import '404.css';
@import 'mobile.css';
@import 'mediaq.css';

View File

@@ -0,0 +1,676 @@
@media all and (max-width: 1279px) {
/* Main */
.common-content {
width: 1024px;
max-width: 100%;
margin: 0 auto;
padding: 0 30px;
}
/* Header */
.main-header {
padding: 34px 30px;
}
.main-header.fixed {
padding: 13px 30px;
}
/* Footer */
.main-footer {
padding: 40px 30px 50px 30px;
}
.footer-nav-list > li {
margin: 0 60px 0 0;
}
/* CATEGORY - BLOG */
.section-category-blog {
padding: 20px 0 60px 0;
}
/* SINGLE - BLOG */
.section-article-header-img {
padding-top: 33%;
}
.section-fp-news-list > li {
width: 298px;
}
/* PAGE - FAQ */
.faq-container-left {
width: 350px;
}
.faq-container-right {
width: 604px;
}
}
@media all and (max-width: 1023px) {
/* Main */
.common-content {
width: 768px;
max-width: 100%;
margin: 0 auto;
padding: 0 20px;
}
/* Header */
.header-logo {
width: 44px;
overflow: hidden;
margin: 0 10px 0 0;
}
.header-buttons a.button-outline {
display: block;
}
/* Footer */
.main-footer {
padding: 40px 20px 50px 20px;
}
.main-footer-content-right {
width: 100%;
margin: 40px 0 0 0;
}
.main-footer-content-left {
margin: 0 auto;
}
.footer-socials-list {
justify-content: center;
}
.lang-chooser-list {
justify-content: center;
margin: 10px 0 0 0;
}
/* CATEGORY - BLOG */
.section-category-blog-list > li:last-of-type {
display: block;
}
.section-category-blog-list > li:nth-of-type(2n) {
margin: 0 0 35px 0;
}
.section-category-blog-list > li:nth-of-type(3n):not(:last-of-type) {
margin: 0 35px 35px 0;
}
/* SINGLE - BLOG */
.section-article-header-heading {
width: 400px;
}
.section-article-header-img {
width: calc(100% - 450px);
}
.section-fp-news-list > li {
width: 346px;
}
.section-fp-news-list > li:last-of-type {
display: none;
}
.section-fp-news-list > li:nth-of-type(2) {
margin: 0;
}
.section-fp-news-content > h3 {
padding: 0;
}
/* PAGE - FAQ */
.faq-container-left {
width: 300px;
}
.faq-container-right {
width: 417px;
}
/* PAGE - PRICING */
.pricing-plans-list > li {
width: 230px;
}
}
@media all and (max-width: 767px) {
/* MAIN */
.common-content {
width: 375px;
max-width: 100%;
padding: 0 20px;
}
.page-wrapper {
padding: 70px 0 0 0;
min-height: auto;
}
.button-large {
font-size: 16px;
line-height: 22px;
}
.cta-tutorial,
a.cta-tutorial {
font-size: 18px;
line-height: 24px;
text-decoration: underline;
}
.cta-tutorial > img {
margin: 2px 0 0 6px;
}
/* UI */
.soon-label-desktop {
display: none;
}
.soon-label-mobile {
display: block;
margin: 0;
position: absolute;
top: -13px;
right: 31px;
}
.mobile-only {
display: block;
}
.desktop-only {
display: none;
}
/* HEADER */
.main-header {
height: 70px;
/* padding: 18px 65px 18px 15px; */
padding: 18px 15px;
}
.main-header.fixed {
height: 54px;
/* padding: 10px 65px 10px 15px; */
padding: 10px 15px;
}
.header-logo {
width: auto;
height: 34px;
}
.header-logo + nav {
display: none;
}
.button-header {
margin: 0;
padding: 4px 10px 4px 10px;
font-size: 14px;
line-height: 20px;
}
.mobile-menu-btn {
/* display: flex; */
display: none;
flex-wrap: wrap;
justify-content: center;
align-items: center;
width: 70px;
height: 100%;
position: absolute;
top: 0;
right: 0;
}
.mobile-menu-btn-inner {
width: 22px;
height: 16px;
position: relative;
}
.mobile-menu-btn-inner > div {
width: 100%;
height: 2px;
background-color: #111;
position: absolute;
left: 0;
border-radius: 1px;
transition: transform 0.6s ease, opacity 0.6s ease;
}
.mobile-menu-btn-inner > div:nth-of-type(1) {
transform-origin: 100% 0;
top: 0;
}
.mobile-menu-btn-inner > div:nth-of-type(2) {
top: calc(50% - 1px);
}
.mobile-menu-btn-inner > div:nth-of-type(3) {
transform-origin: 100% 100%;
bottom: 0;
}
.mobile-menu-btn.active .mobile-menu-btn-inner > div:nth-of-type(1) {
transform: rotate(-45deg) translateY(-1px);
}
.mobile-menu-btn.active .mobile-menu-btn-inner > div:nth-of-type(2) {
transform: scale(0);
opacity: 0;
}
.mobile-menu-btn.active .mobile-menu-btn-inner > div:nth-of-type(3) {
transform: rotate(45deg) translateY(1px);
}
.mobile-menu {
display: block;
width: 100%;
height: 100%;
background-color: #fafafa;
padding: 70px 0 0 0;
position: fixed;
top: 0;
left: 0;
z-index: 99;
transform: translateY(-100%);
transition: transform 0.6s ease;
pointer-events: none;
will-change: transform;
}
.mobile-menu.active {
transform: translateY(0);
pointer-events: auto;
}
.mobile-menu nav {
font-weight: bold;
font-size: 24px;
line-height: 32px;
padding: 20px 35px 0 35px;
height: 100%;
padding-bottom: 100px;
overflow: auto;
}
.mobile-menu nav > ul {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.mobile-menu nav > ul > li {
margin: 0 0 40px 0;
width: 50%;
}
.mobile-menu nav > ul > li:nth-of-type(2n) {
padding: 0 0 0 35px;
}
.mobile-menu nav > ul > li > span {
display: block;
font-size: 18px;
line-height: 24px;
color: #007aff;
margin: 0 0 6px 0;
}
.mobile-menu nav > ul > li > ul > li {
margin: 0 0 6px 0;
}
.mobile-menu nav > ul > li > ul > li:last-of-type {
margin: 0;
}
.mobile-menu > a {
position: absolute;
left: 38px;
bottom: 38px;
width: calc(100% - 76px);
padding: 17px 10px 19px 10px;
text-align: center;
background-color: #fafafa;
}
/* FOOTER */
.main-footer {
padding: 30px 20px 20px 20px;
}
.main-footer-content {
width: 100%;
display: flex;
flex-direction: column-reverse;
}
.main-footer-content-right {
/* margin: 0 0 60px 0; */
margin: 0 0 20px 0;
display: none;
}
.main-footer-content-left {
width: 100%;
padding: 0;
}
.footer-nav-list {
width: 335px;
max-width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 0;
margin: 0 auto;
}
.footer-nav-list > li {
margin: 0 0 30px 0;
/* width: 50%; */
width: 100%;
text-align: center;
}
.footer-nav-list > li > h6 {
margin: 0 0 10px 0;
display: none;
}
.footer-menu-list {
font-size: 12px;
line-height: 25px;
opacity: 0.5;
}
.page-wrapper.page-wrapper-home {
min-height: calc(100vh - 100px);
}
.page-wrapper.page-wrapper-home .common-content {
padding: 0 25px;
}
.lang-chooser-list-desktop {
/* display: none; */
display: flex;
font-size: 14px;
line-height: 12px !important;
}
.lang-chooser-list-mobile {
width: 335px;
max-width: 100%;
margin: 0 auto;
/* display: flex; */
display: none;
justify-content: flex-start;
transform: translateX(-10px);
}
/* PAGE - PRICING */
.section-page-pricing {
padding: 20px 0;
}
.section-pricing-cta-block {
margin: 30px 0 40px 0;
}
.pricing-plans-list {
flex-direction: column;
justify-content: center;
align-items: center;
}
.pricing-plans-list > li {
width: 320px;
max-width: 100%;
margin: 0 0 30px 0;
}
.pricing-plans-list > li:last-of-type {
margin: 0;
}
.section-dropdown > ul {
width: 100%;
}
.plan-content-card {
min-height: auto;
}
.section-page-pricing-faq {
padding: 20px 0 50px 0;
}
.section-page-pricing-faq-content > h3 {
font-size: 28px;
line-height: 32px;
text-align: left;
margin: 0 0 40px 0;
padding: 0 10px;
}
.section-page-pricing-faq-list > li > h4 {
font-size: 18px;
line-height: 28px;
padding: 10px 70px 10px 20px;
}
.section-page-pricing-faq-list-item-content {
padding: 20px 20px 30px 20px;
}
/* SINGLE - BLOG */
.section-article {
padding: 0 0 30px 0;
}
.section-article-header {
display: block;
padding: 0;
}
.section-article-header-img {
width: calc(100% + 40px);
padding-top: 86%;
margin: 0 0 26px -20px;
}
.section-article-header-img > img {
border-radius: 0;
}
.section-article-header-heading {
width: 100%;
padding: 0 5px;
}
.section-article-header-heading-content > h1 {
font-size: 22px;
line-height: 30px;
margin: 0 0 8px 0;
}
.section-article-header-heading-content > span {
font-size: 15px;
line-height: 22px;
}
.section-article-text {
padding: 30px 5px 0 5px;
}
.article-share-list {
margin: 30px 0 0 0;
}
.article-share-list > li > a {
font-size: 0;
}
.article-share-list > li {
width: calc(33% - 14px);
}
.article-share-list > li > a > i {
line-height: 42px;
font-size: 26px;
margin: 0;
}
.section-fp-news-content-article .section-fp-news-content > h3 {
padding: 0;
font-size: 18px;
line-height: 28px;
text-align: center;
margin: 0 0 30px 0;
}
.section-fp-news {
padding: 15px 0 30px 0;
}
.section-fp-news-content > h3 {
padding: 0 0 0 10px;
font-size: 28px;
line-height: 36px;
margin: 0 0 60px 0;
}
.section-fp-news-list {
display: block;
width: calc(100% + 14px);
margin: 0 0 0 -7px;
}
.section-fp-news-list > li,
.section-fp-news-list > li:nth-of-type(2) {
width: 100%;
margin: 0 0 10px 0;
}
.section-fp-news-list > li:last-of-type {
display: block;
}
.section-fp-news-list-item-preview {
padding-top: 91%;
}
.section-fp-news-list-item-content {
padding: 30px 25px;
}
.section-fp-news-list-item-content > h4 {
font-size: 22px;
line-height: 28px;
}
.section-fp-news-list-item-content > span {
font-size: 15px;
line-height: 20px;
}
/* PAGE - FAQ */
.page-faq {
padding: 20px 0;
}
.faq-container-left {
width: 100%;
margin: 0 0 20px 0;
}
.faq-top-controls-list > li {
width: 100%;
float: none;
margin: 0 0 10px 0;
min-height: auto;
padding: 10px 20px 10px 50px;
}
.faq-top-controls-list > li > img {
height: 46px;
width: auto;
}
.faq-panes-list-item-status {
right: 15px;
}
.faq-panes-list-item-counter {
left: 15px;
}
.faq-panes-list-item-header {
padding: 20px 50px 20px 40px;
}
.faq-panes-list-item-content {
padding: 20px 10px 10px 10px;
}
}
@media all and (max-width: 374px) {
.common-content {
padding: 0 15px;
}
.mobile-menu nav {
padding: 20px 25px 0 25px;
}
.mobile-menu nav > ul > li:nth-of-type(2n) {
padding: 0 0 0 25px;
}
.mobile-menu > a {
width: calc(100% - 50px);
left: 25px;
bottom: 25px;
}
.button-large {
padding: 17px 30px 19px 30px;
}
.section-fp-news-list-item-preview {
padding-top: 70%;
}
}

View File

@@ -0,0 +1,6 @@
/* HIDDEN MOBILE ELEMENTS */
.mobile-menu-btn,
.mobile-menu {
display: none;
}

351
src/app/ui/wppages/css/normalize.css vendored Executable file
View File

@@ -0,0 +1,351 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input {
/* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select {
/* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type='button'],
[type='reset'],
[type='submit'] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type='button']::-moz-focus-inner,
[type='reset']::-moz-focus-inner,
[type='submit']::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type='button']:-moz-focusring,
[type='reset']:-moz-focusring,
[type='submit']:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type='checkbox'],
[type='radio'] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type='number']::-webkit-inner-spin-button,
[type='number']::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type='search'] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type='search']::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}

View File

@@ -0,0 +1,433 @@
/* PAGE - PRICING */
/* PAGE - PRICING - Plans */
.section-page-pricing {
padding: 40px 0 50px 0;
}
.section-page-pricing-content {
width: 990px;
max-width: 100%;
margin: 0 auto;
}
.pricing-plans-list {
display: flex;
flex-wrap: wrap;
justify-content: center;
flex-direction: row;
}
.pricing-plans-list > li {
width: 214px;
max-width: 100%;
position: relative;
margin: 0 50px 0 50px;
}
.plan-header {
padding: 0 25px 5px 0;
}
.plan-header > h2 {
font-weight: bold;
font-size: 22px;
line-height: 28px;
margin: 0 0 15px 0;
}
.plan-header .starts-from {
font-size: 14px;
color: rgba(0, 0, 0, 0.6);
display: inline-block;
width: 100%;
margin-bottom: 8px;
}
.plan-price-block {
width: 100%;
display: flex;
flex-wrap: wrap;
}
.plan-price-block p {
font-size: 14px;
color: rgba(0, 0, 0, 0.6);
}
.plan-price {
display: block;
font-size: 24px;
line-height: 36px;
}
.plan-price i {
font-style: normal;
display: inline-block;
font-size: 46px;
font-weight: bold;
margin-left: 5px;
margin-right: 10px;
position: relative;
top: 2px;
}
.plan-price-description {
display: block;
font-size: 14px;
line-height: 14px;
margin: 3px 0 0 8px;
}
/*.plan-content-card {*/
/*padding: 25px;*/
/*border-radius: 6px;*/
/*background-color: #ededed;*/
/*position: relative;*/
/*min-height: 280px;*/
/*}*/
.plan-content-card-products-list > li {
margin: 0 0 26px 0;
}
.plan-content-card-products-list > li:last-of-type {
margin: 0;
}
.plan-content-card-products-list > li > h3 {
font-weight: bold;
font-size: 18px;
line-height: 24px;
margin: 0 0 12px 0;
}
.features-list {
font-size: 14px;
line-height: 20px;
}
.features-list > li {
margin: 0 0 12px 0;
font-size: 16px;
font-weight: normal;
line-height: 22px;
}
.features-list > li > span {
display: block;
opacity: 0.6;
font-size: 14px;
line-height: 16px;
font-weight: normal;
margin-top: 8px;
margin-bottom: 22px;
}
.features-list > li:last-of-type {
margin: 0;
}
.features-list > li > a {
text-decoration: underline;
}
.features-list > li i {
display: inline-block;
vertical-align: top;
padding: 1px 6px 2px 6px;
border-radius: 100px;
background-color: #c7c7cc;
font-weight: 500;
font-style: normal;
font-size: 12px;
line-height: 16px;
color: #fff;
margin: 0 0 0 3px;
}
a.plan-card-button {
display: flex;
flex-wrap: wrap;
width: calc(100% - 50px);
height: 50px;
justify-content: center;
align-items: center;
background-color: #c7c7cc;
border-radius: 4px;
font-weight: bold;
font-size: 16px;
line-height: 22px;
color: #fff;
text-align: center;
position: absolute;
bottom: 25px;
left: 25px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
a.plan-card-button:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.1);
}
a.plan-card-button > span {
display: block;
width: 100%;
font-weight: normal;
font-size: 13px;
line-height: 18px;
margin: -12px 0 0 0;
}
a.plan-card-button-blue {
background-color: #007aff;
}
a.plan-card-button-blue:hover {
box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.2);
}
.section-pricing-cta-block {
margin: 10px 0 40px 0;
text-align: center;
}
.section-pricing-cta-block .button {
width: 100%;
}
/* PAGE - PRICING - FAQ */
.section-page-pricing-faq {
padding: 50px 0 100px 0;
}
.section-page-pricing-faq-content {
width: 690px;
max-width: 100%;
margin: 0 auto;
}
.section-page-pricing-faq-content > h3 {
font-weight: bold;
font-size: 44px;
line-height: 50px;
text-align: center;
margin: 0 0 30px 0;
}
.section-page-pricing-faq-list {
width: 600px;
margin: 0 auto;
max-width: 100%;
}
.section-page-pricing-faq-list > li {
margin: 0 0 10px 0;
}
.section-page-pricing-faq-list > li:last-of-type {
margin: 0;
}
.section-page-pricing-faq-list > li > h4 {
display: flex;
flex-wrap: wrap;
align-items: center;
position: relative;
font-weight: bold;
font-size: 24px;
line-height: 34px;
min-height: 80px;
margin: 0;
padding: 10px 80px 10px 20px;
background-color: #ededed;
border-radius: 6px;
cursor: pointer;
}
.section-page-pricing-faq-list > li > h4::before,
.section-page-pricing-faq-list > li > h4::after {
content: '';
width: 14px;
height: 1px;
background-color: #111;
position: absolute;
top: 50%;
right: 33px;
transition: transform 0.3s ease;
}
.section-page-pricing-faq-list > li > h4.active::after {
transform: rotate(-90deg);
}
.section-page-pricing-faq-list-item-content {
font-size: 16px;
line-height: 24px;
display: none;
padding: 20px 80px 30px 20px;
overflow: hidden;
}
.section-page-pricing-faq-list-item-content p {
margin: 0 0 16px 0;
}
.section-page-pricing-faq-list-item-content p:last-of-type {
margin: 0;
}
.section-page-pricing-faq-list-item-content a {
text-decoration: underline;
}
.section-page-pricing-faq-list-item-content ol {
list-style-type: decimal;
padding: 0 0 0 18px;
}
.section-page-pricing-faq-ps {
width: 600px;
max-width: 100%;
font-size: 16px;
line-height: 24px;
padding: 0 80px 0 20px;
margin: 40px auto 0 auto;
}
.section-dropdown {
width: 100%;
background: url('/static/website/img/icons/chevron-down.svg');
background-repeat: no-repeat;
background-position: right 3px top 14px;
background-size: 18px;
position: relative;
}
.section-dropdown.open {
background: url('/static/website/img/icons/chevron-up.svg');
background-repeat: no-repeat;
background-position: right 3px top 14px;
background-size: 18px;
}
.section-dropdown > span {
font-size: 14px;
color: rgba(0, 0, 0, 0.6);
cursor: pointer;
}
.section-dropdown > span > i {
display: inline-block;
color: #007aff;
font-style: normal;
width: 100%;
position: relative;
top: -5px;
}
.section-dropdown > ul {
background: black;
color: white;
border-radius: 4px;
padding: 10px;
font-size: 14px;
width: 215px;
position: absolute;
top: 56px;
left: 0;
height: 0px;
opacity: 0;
z-index: -1;
transition: all 0.3s ease;
}
.section-dropdown > ul > li {
font-weight: bold;
}
.section-dropdown > ul > li:first-of-type {
font-weight: normal;
}
.section-dropdown > ul > li:last-of-type {
font-weight: normal;
text-align: center;
padding-top: 10px;
}
.section-dropdown > ul > li:last-of-type > a {
color: #007aff;
display: inline-block;
width: 100%;
}
.section-dropdown > ul > li > span {
float: right;
}
.section-dropdown.open > ul {
height: 286px;
opacity: 1;
z-index: 1;
}
.free .plan-header > h2 {
margin-bottom: 47px;
}
.free .plan-header {
padding: 0 25px 3px 0;
}
.free .plan-header p {
font-size: 14px;
color: rgba(0, 0, 0, 0.6);
margin: 8px 0 24px 0;
}
.switch-section {
text-align: center;
margin-bottom: 30px;
}
.switcher {
display: inline-block;
position: relative;
border-radius: 36px;
background: #fff;
box-shadow: 0 2px 30px 0 rgba(0, 0, 0, 0.08);
padding: 6px;
margin-top: 32px;
}
.switcher div {
float: left;
padding: 7px 24px 5px 24px;
text-align: center;
cursor: pointer;
position: relative;
z-index: 2;
min-width: 141px;
line-height: 16px;
font-size: 16px;
transition: all 0.3s cubic-bezier(0.39, 0.58, 0.57, 1);
}
.switcher div:nth-child(1) {
color: #fff;
}
.switcher:after {
content: ' ';
display: block;
width: 141px;
height: 28px;
position: absolute;
top: 6px;
left: 7px;
border-radius: 36px;
background: #3eb991;
z-index: 1;
transform: translate3d(0, 0, 0);
transition: all 0.3s ease;
}
.switcher.active:after {
transform: translate3d(140px, 0, 0);
}
.switcher.active div:nth-child(1) {
color: #000;
}
.switcher.active div:nth-child(2) {
color: #fff;
}
.switcher:before {
content: attr(data-discount);
position: absolute;
color: #e13434;
font-size: 12px;
font-weight: bold;
width: 141px;
left: 6px;
top: -26px;
text-align: center;
}

View File

@@ -0,0 +1,165 @@
/* UI */
.button {
display: inline-block;
vertical-align: top;
border: 2px solid #262a34;
padding: 10px 26px;
border-radius: 4px;
font-weight: 500;
font-size: 16px;
line-height: 22px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.button:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.2);
}
.button-header {
display: block;
margin: 0 0 0 10px;
}
.button-outline {
background-color: transparent;
}
.button-fill {
border-color: transparent;
}
.button-blue,
a.button-blue {
background-color: #007aff;
color: #fff;
}
.button-blue:hover {
color: #fff;
}
.button-white,
a.button-white {
background-color: #fff;
color: #007aff;
}
.button-white:hover {
color: #007aff;
}
.button-black,
a.button-black {
background-color: #111;
color: #fff;
}
.button-black:hover {
color: #fff;
}
.button-large {
padding: 17px 39px 19px 39px;
font-weight: bold;
font-size: 18px;
line-height: 24px;
}
.button-standart {
padding: 7px 19px 9px 19px;
font-weight: bold;
font-size: 16px;
line-height: 30px;
}
.decor-label {
display: inline-block;
vertical-align: top;
position: relative;
font-weight: bold;
transition: transform 0.3s ease;
}
.decor-label::before {
box-sizing: border-box;
content: '';
display: block;
width: calc(100% + 4px);
height: 100%;
border-radius: 4px;
transform: rotate(1deg);
background-color: rgba(0, 122, 255, 0.15);
position: absolute;
top: 0;
left: -2px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.decor-label:hover {
transform: translateY(-2px);
}
.decor-label:hover::before {
transform: rotate(0deg);
}
.decor-label > span {
position: relative;
color: #007aff;
}
.decor-label-skew-right::before {
transform: rotate(-1deg);
}
.decor-label-violet > span {
color: #5856d6;
}
.decor-label-violet::before {
background-color: #5856d6;
opacity: 0.15;
}
.cta-tutorial,
a.cta-tutorial {
/* display: inline-block; */
vertical-align: top;
font-size: 21px;
line-height: 28px;
color: #0070c9;
margin: 20px 0 0 0;
display: none;
text-decoration: underline;
}
.cta-tutorial > img {
display: inline-block;
vertical-align: top;
width: auto;
height: 22px;
margin: 3px 0 0 6px;
}
.soon-label {
display: inline-block;
padding: 4px 34px 2px 12px;
border-radius: 100px;
background-color: #111;
font-weight: 500;
font-size: 14px;
line-height: 20px;
color: #fff;
background-image: url('/img/ui/image-man-manager-1x.png');
-webkit-background-size: 18px 20px;
background-size: 18px 20px;
background-position: calc(100% - 12px) 100%;
background-repeat: no-repeat;
margin: 16px 0 0 0;
}
.soon-label-mobile {
display: none;
}

View File

@@ -0,0 +1,269 @@
import $ from 'jquery';
$(document).ready(function() {
// Fixed header
$(window).on('scroll load', function() {
if ($(document).scrollTop() > 0) {
$('.main-header').addClass('fixed');
} else {
$('.main-header').removeClass('fixed');
}
});
// Share popup
function windowPopup(url, width, height) {
const left = screen.width / 2 - width / 2,
top = screen.height / 2 - height / 2;
window.open(
url,
'',
'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,width=' +
width +
',height=' +
height +
',top=' +
top +
',left=' +
left
);
}
// Share popup on click
$('.article-share-list > li > a').on('click', function(e) {
e.preventDefault();
windowPopup($(this).attr('href'), 500, 300);
});
// FAQ slider
function faqSlider() {
const topControls = $('.faq-top-controls-list > li'),
sliderList = $('.faq-sublists'),
sliderListItem = sliderList.children('li'),
questionPane = $('.faq-panes-list > li'),
questionPaneHeading = questionPane.find('.faq-panes-list-item-header'),
questionPaneAnswer = questionPane.find('.faq-panes-list-item-content'),
hash = location.hash.substr(2);
// Build
topControls.eq(0).addClass('active');
sliderListItem.eq(0).addClass('active');
sliderListItem.outerWidth(sliderList.parent().outerWidth());
// Scroll to hash in url
if (hash != '') {
const listIndex = hash.substring(0, hash.indexOf('-')) - 1,
questionItem = $('#' + hash);
topControls
.removeClass('active')
.eq(listIndex)
.addClass('active');
sliderListItem
.removeClass('active')
.eq(listIndex)
.addClass('active');
sliderList.css('transform', 'translateX(-' + listIndex * sliderListItem.outerWidth() + 'px)');
questionItem
.addClass('opened')
.find(questionPaneAnswer)
.slideDown(300);
$('html, body')
.delay(1300)
.animate({ scrollTop: questionItem.offset().top - $('.main-header').outerHeight() - 30 }, 600);
}
// Fixed topics on desktop
$(window).on('scroll', function() {
if ($(window).width() > 1239) {
if ($(window).scrollTop() > 70) {
$('.faq-top-controls-list').addClass('fixed');
} else {
$('.faq-top-controls-list').removeClass('fixed');
}
}
});
// Rebuild on resize
$(window).on('resize', function() {
sliderListItem.outerWidth(sliderList.parent().outerWidth());
});
// Top slider controls event
topControls.on('click', function() {
const thisIndex = $(this).index();
topControls
.removeClass('active')
.eq(thisIndex)
.addClass('active');
sliderListItem
.removeClass('active')
.eq(thisIndex)
.addClass('active');
sliderList.css('transform', 'translateX(-' + thisIndex * sliderListItem.outerWidth() + 'px)');
questionPane
.removeClass('opened')
.find(questionPaneAnswer)
.slideUp(1000);
if ($(window).scrollTop() > 0) {
$('html, body').animate({ scrollTop: 0 }, 1000);
}
});
// Uncollapse item
questionPaneHeading.on('click', function() {
window.location.hash =
'#q' +
$(this)
.parent('li')
.attr('id');
if (
$(this)
.parent('li')
.hasClass('opened')
) {
$(this)
.parent('li')
.removeClass('opened');
$(this)
.next(questionPaneAnswer)
.slideUp(300);
} else {
$(this)
.parent('li')
.addClass('opened');
$(this)
.next(questionPaneAnswer)
.slideDown(300);
}
});
// Toggle accordion
$('.accordeon-header').on('click', function() {
const contentEl = $(this).next('.accordeon-content');
if ($(this).hasClass('visible')) {
contentEl.slideUp(300);
} else {
contentEl.slideDown(300);
}
$(this).toggleClass('visible');
});
}
// Init slider
if ($('.faq-container-right')) faqSlider();
// Pricing FAQ list
$('.section-page-pricing-faq-list > li')
.eq(0)
.children('h4')
.addClass('active')
.next('.section-page-pricing-faq-list-item-content')
.slideDown(600);
$('.section-page-pricing-faq-list > li > h4').on('click', function() {
if ($(this).hasClass('active')) {
$(this).removeClass('active');
$(this)
.next('.section-page-pricing-faq-list-item-content')
.slideUp(600);
} else {
$(this).addClass('active');
$(this)
.next('.section-page-pricing-faq-list-item-content')
.slideDown(600);
}
});
// Mobile menu
$('.mobile-menu-btn').on('click', function() {
if ($(this).hasClass('active')) {
$(this).removeClass('active');
$('.mobile-menu').removeClass('active');
$('body').removeClass('overflow-hidden');
} else {
$(this).addClass('active');
$('.mobile-menu').addClass('active');
$('body').addClass('overflow-hidden');
}
});
// Pricing switcher
$('#switcher').on('click', function() {
const priceEl = $('#price');
const switcherEl = $(this);
const price = switcherEl.hasClass('active') ? priceEl.data('priceAnnually') : priceEl.data('priceMonthly');
switcherEl.toggleClass('active');
priceEl.html(price);
});
// Cookies functions
function setCookie(name, value, days) {
let expires = '';
if (days) {
var date = new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
expires = '; expires=' + date.toUTCString();
}
document.cookie = name + '=' + (value || '') + expires + '; path=/';
}
function getCookie(name) {
const nameEQ = name + '=';
let ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
// Article links with images no underline
if ($('.section-article-text')) {
$('.section-article-text a').each(function() {
$(this)
.has('img')
.addClass('no-underline');
});
}
// Change language
$('.lang-chooser').on('click', function() {
const destinationLang = $(this).data('lang');
setCookie('lang', destinationLang, 7);
});
// Accept cookie
const termsAccepted = getCookie('terms_accept');
if (!termsAccepted) {
$('#cookie-message').addClass('visible');
$('#close-cookie').on('click', function() {
setCookie('terms_accept', 'true', 365);
if ($(window).width() > 767) {
$('#cookie-message').css({
transform: 'translateY(100px)',
opacity: 0,
});
} else {
$('#cookie-message').css({
transform: 'translateY(-100px)',
opacity: 0,
});
}
setTimeout(function() {
$('#cookie-message').remove();
}, 400);
});
} else {
$('#cookie-message').remove();
}
});

3
src/i18n/i18n.ts Normal file
View File

@@ -0,0 +1,3 @@
export const LOCALES = ['en', 'uk', 'ru', 'es', 'pt', 'th'] as const;
export type LocaleType = typeof LOCALES[number];
export const DEFAULT_LOCALE = 'en';

4
src/i18n/navigation.ts Normal file
View File

@@ -0,0 +1,4 @@
import { createNavigation } from 'next-intl/navigation';
import { routing } from './routing';
export const { Link, redirect, usePathname, useRouter, getPathname } = createNavigation(routing);

15
src/i18n/request.ts Normal file
View File

@@ -0,0 +1,15 @@
import { getRequestConfig } from 'next-intl/server';
import { hasLocale } from 'next-intl';
import { routing } from './routing';
export default getRequestConfig(async ({ requestLocale }) => {
const requested = await requestLocale;
const locale = hasLocale(routing.locales, requested)
? requested
: routing.defaultLocale;
return {
locale,
messages: (await import(`../../messages/${locale}.json`)).default
};
});

8
src/i18n/routing.ts Normal file
View File

@@ -0,0 +1,8 @@
import { defineRouting } from 'next-intl/routing';
import { DEFAULT_LOCALE, LOCALES } from './i18n';
export const routing = defineRouting({
locales: LOCALES,
defaultLocale: DEFAULT_LOCALE,
localePrefix: 'as-needed',
});

24
src/lax.d.ts vendored Normal file
View File

@@ -0,0 +1,24 @@
declare module 'lax.js' {
interface LaxAnimationValue {
[key: string]: [string[], number[]];
}
interface LaxElementConfig {
scrollY?: LaxAnimationValue;
[key: string]: LaxAnimationValue | undefined;
}
interface Lax {
init: () => void;
addDriver: (name: string, callback: () => number) => void;
addElement: (selector: string, config: LaxElementConfig) => void;
addElements: (selector: string, config: LaxElementConfig) => void;
removeElement: (selector: string) => void;
removeElements: (selector: string) => void;
updateElements: () => void;
destroy: () => void;
}
const lax: Lax;
export default lax;
}

5
src/lib/fontawesome.ts Normal file
View File

@@ -0,0 +1,5 @@
import { config } from '@fortawesome/fontawesome-svg-core'
import '@fortawesome/fontawesome-svg-core/styles.css'
// Tell Font Awesome to skip adding the CSS automatically since it's being imported above
config.autoAddCss = false

11
src/middleware.ts Normal file
View File

@@ -0,0 +1,11 @@
import createMiddleware from 'next-intl/middleware';
import { routing } from './i18n/routing';
export default createMiddleware(routing);
export const config = {
// Match all pathnames except for
// - … if they start with `/api`, `/trpc`, `/_next` or `/_vercel`
// - … the ones containing a dot (e.g. `favicon.ico`)
matcher: '/((?!api|trpc|_next|_vercel|.*\\..*).*)'
};