Initial commit

This commit is contained in:
Никита Круглицкий 2024-11-26 16:15:12 +03:00
commit b3c99a667a
97 changed files with 14314 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist
# Node dependencies
node_modules
# Logs
logs
*.log
# Misc
.DS_Store
.fleet
.idea
# Local env files
.env
.env.*
!.env.example

9
README.md Normal file
View File

@ -0,0 +1,9 @@
# Сборка проекта
Запустить
```shell
yarn generate
```
Скопировать содержимое директории `dist` в директорию `/www` на хостинге

37
app.vue Normal file
View File

@ -0,0 +1,37 @@
<template>
<AppHeader />
<main>
<HomepageHero />
<HomepageResults />
<HomepageHowItWorks />
<HomepageTariff />
<HomepageReviews />
<HomepageFaq />
<HomepageAnyQuestions />
</main>
<AppFooter />
<ScrollToTop />
</template>
<style lang="scss">
main {
padding-top: 32px;
@include mobile {
padding-top: 16px;
}
> * {
&:not(:last-child) {
margin-bottom: 100px;
@include mobile {
margin-bottom: 24px;
}
}
}
}
</style>

View File

@ -0,0 +1,6 @@
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.1444 20.6935C9.39139 20.1995 9.19117 19.5989 8.69719 19.3519C8.20321 19.1049 7.60254 19.3051 7.35555 19.7991L6.35555 21.7991C6.10856 22.2931 6.30878 22.8938 6.80276 23.1407C7.29674 23.3877 7.89741 23.1875 8.1444 22.6935L9.1444 20.6935Z" fill="#056E62"/>
<path d="M15.3556 20.6935C15.1086 20.1995 15.3088 19.5989 15.8028 19.3519C16.2968 19.1049 16.8975 19.3051 17.1445 19.7991L18.1445 21.7991C18.3914 22.2931 18.1912 22.8938 17.6972 23.1407C17.2033 23.3877 16.6026 23.1875 16.3556 22.6935L15.3556 20.6935Z" fill="#056E62"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.25 2.24634C11.25 1.69405 11.6977 1.24634 12.25 1.24634C12.8023 1.24634 13.25 1.69405 13.25 2.24634V3.24634H18.25C19.9069 3.24634 21.25 4.58948 21.25 6.24634V14.2463C21.25 15.9032 19.9069 17.2463 18.25 17.2463H6.25C4.59315 17.2463 3.25 15.9032 3.25 14.2463V6.24634C3.25 4.58948 4.59315 3.24634 6.25 3.24634H11.25V2.24634Z" fill="#E7EAF3"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.8747 6.46551C18.306 6.81052 18.3759 7.43981 18.0309 7.87107L14.0309 12.8711C13.7068 13.2761 13.1269 13.3662 12.6953 13.0784L10.3774 11.5332L7.95711 13.9535C7.56658 14.344 6.93342 14.344 6.54289 13.9535C6.15237 13.563 6.15237 12.9298 6.54289 12.5393L9.54289 9.53927C9.87996 9.2022 10.4081 9.14991 10.8047 9.41432L13.0419 10.9058L16.4691 6.62168C16.8141 6.19042 17.4434 6.1205 17.8747 6.46551Z" fill="#056E62"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,4 @@
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.25 15.8782V12.2466C16.25 10.5897 14.9069 9.24658 13.25 9.24658H6.41187V5.7729C6.41187 4.37765 7.54294 3.24658 8.93818 3.24658H20.7277C22.1229 3.24658 23.254 4.37765 23.254 5.7729V13.3518L23.2706 18.0334C23.2716 18.3095 23.0486 18.5342 22.7724 18.5352C22.6392 18.5356 22.5113 18.4829 22.4171 18.3887L19.9065 15.8782H16.25Z" fill="#056E62"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.23511 18.2466V13.2466C2.23511 12.142 3.13054 11.2466 4.23511 11.2466H12.2351C13.3397 11.2466 14.2351 12.142 14.2351 13.2466V18.2466C14.2351 19.3512 13.3397 20.2466 12.2351 20.2466H4.35084L3.10699 21.4371C2.90749 21.628 2.59099 21.6211 2.40005 21.4216C2.31098 21.3286 2.26126 21.2047 2.26126 21.0759V18.5708C2.24405 18.4653 2.23511 18.357 2.23511 18.2466ZM6.75005 14.2466C6.47391 14.2466 6.25005 14.4704 6.25005 14.7466C6.25005 15.0227 6.47391 15.2466 6.75005 15.2466H11.7501C12.0262 15.2466 12.2501 15.0227 12.2501 14.7466C12.2501 14.4704 12.0262 14.2466 11.7501 14.2466H6.75005ZM9.75005 16.2466C9.47391 16.2466 9.25005 16.4704 9.25005 16.7466C9.25005 17.0227 9.47391 17.2466 9.75005 17.2466H11.7501C12.0262 17.2466 12.2501 17.0227 12.2501 16.7466C12.2501 16.4704 12.0262 16.2466 11.7501 16.2466H9.75005Z" fill="#CFD5F0"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,11 @@
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_234_461)">
<path d="M4.3952 13.661L13.7515 0.70623C14.3703 -0.150674 15.7223 0.413481 15.5485 1.45612L14.2501 9.24653H19.2944C20.1101 9.24653 20.5827 10.1707 20.105 10.832L10.7488 23.7868C10.1299 24.6437 8.77795 24.0796 8.95172 23.0369L10.2501 15.2465H5.20588C4.39011 15.2465 3.91757 14.3224 4.3952 13.661Z" fill="#E7EAF3"/>
<path d="M4.39508 13.661L13.7513 0.70623C14.3702 -0.150674 15.7222 0.413481 15.5484 1.45612L14.25 9.24653L10.25 15.2465H5.20576C4.38999 15.2465 3.91745 14.3224 4.39508 13.661Z" fill="#056E62"/>
</g>
<defs>
<clipPath id="clip0_234_461">
<rect width="24" height="24" fill="white" transform="translate(0.25 0.24646)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 789 B

View File

@ -0,0 +1,4 @@
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="5.1665" y="9.86475" width="49.1667" height="39.3333" rx="7.37496" fill="#46AEA2"/>
<path d="M27.0703 30.2481L5.1665 16.0106C5.1665 12.6163 7.91809 9.86475 11.3123 9.86475H48.1873C51.5816 9.86475 54.3332 12.6163 54.3332 16.0106L32.4293 30.2481C30.8 31.3071 28.6997 31.3071 27.0703 30.2481Z" fill="#056E62"/>
</svg>

After

Width:  |  Height:  |  Size: 419 B

View File

@ -0,0 +1,12 @@
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_30_1485)">
<path d="M21.3902 20.2576L19.4852 22.1626C19.4852 22.1626 14.5355 24.2839 7.46441 17.2128C0.39334 10.1418 2.51466 5.19201 2.51466 5.19201L4.41959 3.28707C5.28021 2.42645 6.70355 2.52761 7.43381 3.50129L9.25208 5.92565C9.84926 6.72189 9.77008 7.83608 9.06629 8.53987L7.46441 10.1418C7.46441 10.1418 7.46441 11.556 10.2928 14.3844C13.1213 17.2128 14.5355 17.2128 14.5355 17.2128L16.1374 15.6109C16.8411 14.9072 17.9553 14.828 18.7516 15.4251L21.1759 17.2434C22.1496 17.9737 22.2508 19.397 21.3902 20.2576Z" fill="#CFD5F0"/>
<path d="M4.41959 3.287L3.92887 3.77772L8.17151 9.43457L9.06629 8.53979C9.77007 7.83601 9.84926 6.72182 9.25208 5.92558L7.4338 3.50121C6.70354 2.52753 5.28021 2.42638 4.41959 3.287Z" fill="#008574"/>
<path d="M21.3901 20.2578L20.8994 20.7485L15.2426 16.5059L16.1373 15.6111C16.8411 14.9073 17.9553 14.8281 18.7515 15.4253L21.1759 17.2436C22.1496 17.9738 22.2507 19.3972 21.3901 20.2578Z" fill="#008574"/>
</g>
<defs>
<clipPath id="clip0_30_1485">
<rect width="24" height="24" fill="white" transform="translate(0 0.677246)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,11 @@
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_234_443)">
<path d="M2.25 15.2465C2.25 13.5896 3.59315 12.2465 5.25 12.2465H7.25V22.2465H5.25C3.59315 22.2465 2.25 20.9033 2.25 19.2465V15.2465Z" fill="#056E62"/>
<path d="M22.6288 12.7341C22.9444 11.4721 21.9902 10.2494 20.6893 10.2489L14.75 10.2465L15.299 6.95264C15.5645 5.3594 14.719 3.78822 13.2429 3.13222C12.3057 2.71566 11.25 3.40172 11.25 4.42738V6.19412C11.25 6.87533 11.0182 7.53626 10.5926 8.0682L7.25 12.2465V22.2465H17.9078C19.2844 22.2465 20.4843 21.3097 20.8182 19.9743L22.6288 12.7341Z" fill="#E7EAF3"/>
</g>
<defs>
<clipPath id="clip0_234_443">
<rect width="24" height="24" fill="white" transform="translate(0.25 0.24646)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 791 B

View File

@ -0,0 +1,4 @@
<svg width="59" height="60" viewBox="0 0 59 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.253 49.9898L54.6309 28.423C55.1957 27.5757 54.9668 26.431 54.1195 25.8661C53.8166 25.6642 53.4608 25.5565 53.0968 25.5565H41.7918V11.9793C41.7918 10.961 40.9663 10.1355 39.9481 10.1355C39.3316 10.1355 38.7559 10.4436 38.414 10.9565L24.0361 32.5233C23.4712 33.3706 23.7002 34.5153 24.5474 35.0802C24.8503 35.2821 25.2062 35.3898 25.5702 35.3898H36.8751V48.9671C36.8751 49.9853 37.7006 50.8108 38.7189 50.8108C39.3354 50.8108 39.911 50.5027 40.253 49.9898Z" fill="#008574"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.0625 13.2651H23.3542C25.3908 13.2651 27.0418 14.9161 27.0418 16.9526C27.0418 18.9892 25.3908 20.6402 23.3542 20.6402H11.0625C9.02598 20.6402 7.37503 18.9892 7.37503 16.9526C7.37503 14.9161 9.02598 13.2651 11.0625 13.2651ZM11.0625 42.7652H23.3542C25.3908 42.7652 27.0418 44.4162 27.0418 46.4527C27.0418 48.4893 25.3908 50.1403 23.3542 50.1403H11.0625C9.02598 50.1403 7.37503 48.4893 7.37503 46.4527C7.37503 44.4162 9.02598 42.7652 11.0625 42.7652ZM6.14586 28.0152H15.9792C18.0158 28.0152 19.6667 29.6661 19.6667 31.7027C19.6667 33.7393 18.0158 35.3902 15.9792 35.3902H6.14586C4.1093 35.3902 2.45834 33.7393 2.45834 31.7027C2.45834 29.6661 4.1093 28.0152 6.14586 28.0152Z" fill="#E7EAF3"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,3 @@
<svg width="14" height="18" viewBox="0 0 14 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.29291 8.20711C0.683434 8.59763 1.3166 8.59763 1.70712 8.20711L6.00002 3.91421L6.00002 16.5C6.00002 17.0523 6.44773 17.5 7.00002 17.5C7.5523 17.5 8.00002 17.0523 8.00002 16.5V3.91421L12.2929 8.20711C12.6834 8.59763 13.3166 8.59763 13.7071 8.20711C14.0976 7.81658 14.0976 7.18342 13.7071 6.79289L7.70712 0.792893C7.3166 0.402369 6.68343 0.402369 6.29291 0.792893L0.292909 6.79289C-0.0976149 7.18342 -0.0976148 7.81658 0.29291 8.20711Z" fill="#021D54"/>
</svg>

After

Width:  |  Height:  |  Size: 566 B

View File

@ -0,0 +1,4 @@
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.297 15.3039C16.4072 14.2196 17.6731 13.8513 19.0343 14.2447C20.014 14.5278 21.0268 15.2059 22.2179 16.3545C22.8502 16.9642 23.2161 17.784 23.2478 18.6618C23.2795 19.5396 22.9735 20.3836 22.3868 21.0373C21.1027 22.4651 19.0613 23.0308 17.2324 23.0308C16.9213 23.0308 16.605 23.015 16.2838 22.9835C16.226 22.9854 16.1672 22.9823 16.108 22.9738C15.9791 22.9553 15.8502 22.9351 15.7213 22.9132C15.0159 22.8062 14.2886 22.6265 13.5428 22.3754C10.6703 21.4402 7.85976 19.6058 5.34129 16.7285C5.29613 16.6769 5.25722 16.6219 5.22451 16.5646C2.73283 13.6092 1.26548 10.1692 1.25012 7.09665C1.23974 5.01246 1.90306 3.22976 3.18018 1.95263C3.81441 1.3209 4.74156 1.00125 5.61889 1.0329C6.49675 1.06455 7.31644 1.43047 7.92609 2.06269C9.0747 3.25383 9.75278 4.26664 10.0359 5.24625C10.4292 6.6074 10.066 7.8684 8.98155 8.97876L8.98072 8.97962L7.91726 10.0511C9.38465 12.6101 11.6697 14.8956 14.2297 16.3633L15.297 15.3039ZM13.5097 20.2428C14.4424 20.5991 15.377 20.8415 16.3026 20.9809C16.6095 21.0181 16.9073 21.0376 17.194 21.0391C18.7429 21.0492 20.0189 20.5821 20.9018 19.7102C21.3993 19.1525 21.3725 18.3059 20.8355 17.7881C19.7014 16.6944 18.8913 16.2132 18.2605 16.1076C17.6489 16.0052 17.1768 16.2527 16.6966 16.7209L15.1136 18.2921C14.822 18.5816 14.3824 18.6622 14.007 18.495C10.6458 16.9988 7.27576 13.6215 5.78561 10.2737C5.61847 9.89834 5.69909 9.4588 5.98856 9.16714L7.55923 7.58468C8.02738 7.10447 8.27536 6.63196 8.17298 6.0203C8.06741 5.38954 7.5862 4.57937 6.49253 3.44516C5.97392 2.90739 5.13395 2.87611 4.577 3.37305C3.70507 4.256 3.23398 5.53864 3.24166 7.08674C3.25663 10.0847 4.96146 13.5905 7.82586 16.4549C9.45006 18.0788 11.3694 19.4009 13.5097 20.2428Z" fill="#505A8A"/>
<path d="M15.25 4.03076C14.6977 4.03076 14.25 4.47848 14.25 5.03076C14.25 5.58305 14.6977 6.03076 15.25 6.03076H16.8358L13.5429 9.32366C13.1524 9.71418 13.1524 10.3473 13.5429 10.7379C13.9334 11.1284 14.5666 11.1284 14.9571 10.7379L18.25 7.44497V9.03076C18.25 9.58305 18.6977 10.0308 19.25 10.0308C19.8023 10.0308 20.25 9.58305 20.25 9.03076V5.03076C20.25 4.47848 19.8023 4.03076 19.25 4.03076H15.25Z" fill="#505A8A"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,3 @@
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.7071 5.26653C16.0976 5.65705 16.0976 6.29022 15.7071 6.68074L9.41421 12.9736L15.7071 19.2665C16.0976 19.6571 16.0976 20.2902 15.7071 20.6807C15.3166 21.0713 14.6834 21.0713 14.2929 20.6807L7.29289 13.6807C6.90237 13.2902 6.90237 12.6571 7.29289 12.2665L14.2929 5.26653C14.6834 4.876 15.3166 4.876 15.7071 5.26653Z" fill="#505A8A"/>
</svg>

After

Width:  |  Height:  |  Size: 448 B

View File

@ -0,0 +1,3 @@
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.29289 5.26653C7.90237 5.65705 7.90237 6.29022 8.29289 6.68074L14.5858 12.9736L8.29289 19.2665C7.90237 19.6571 7.90237 20.2902 8.29289 20.6807C8.68342 21.0713 9.31658 21.0713 9.70711 20.6807L16.7071 13.6807C17.0976 13.2902 17.0976 12.6571 16.7071 12.2665L9.70711 5.26653C9.31658 4.876 8.68342 4.876 8.29289 5.26653Z" fill="#505A8A"/>
</svg>

After

Width:  |  Height:  |  Size: 448 B

View File

@ -0,0 +1,3 @@
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.25 2.03077C16.9068 2.03077 18.25 3.37391 18.25 5.03077V6.03077H19.25C20.9068 6.03077 22.25 7.37392 22.25 9.03077L22.25 19.0308C22.25 20.6876 20.9068 22.0308 19.25 22.0308L9.24999 22.0308C7.59314 22.0308 6.24999 20.6876 6.24999 19.0308V18.0308H5.24999C3.59314 18.0308 2.24999 16.6876 2.24999 15.0308L2.24999 5.03076C2.24999 3.37391 3.59314 2.03076 5.24999 2.03076L15.25 2.03077ZM20.25 9.03077C20.25 8.47849 19.8023 8.03077 19.25 8.03077H18.25V15.0308C18.25 16.6876 16.9068 18.0308 15.25 18.0308H11.2528C10.7005 18.0308 10.25 17.5831 10.25 17.0308C10.25 16.4785 10.6977 16.0308 11.25 16.0308H15.25C15.8023 16.0308 16.25 15.583 16.25 15.0308L16.25 5.03077C16.25 4.47848 15.8023 4.03077 15.25 4.03077L5.23632 4.03076C4.68404 4.03076 4.23632 4.47848 4.23632 5.03076L4.23632 15.0308C4.23632 15.583 4.68404 16.0308 5.23632 16.0308L7.24999 16.0308C7.80228 16.0308 8.24999 16.4785 8.24999 17.0308V19.0308C8.24999 19.583 8.69771 20.0308 9.24999 20.0308L19.25 20.0308C19.8023 20.0308 20.25 19.583 20.25 19.0308L20.25 9.03077Z" fill="#505A8A"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12V16C11 16.5523 11.4477 17 12 17C12.5523 17 13 16.5523 13 16V12Z" fill="#CFD5F0"/>
<path d="M12 7C12.5523 7 13 7.44772 13 8C13 8.55228 12.5523 9 12 9C11.4477 9 11 8.55228 11 8C11 7.44772 11.4477 7 12 7Z" fill="#CFD5F0"/>
<path d="M1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23C5.92487 23 1 18.0751 1 12ZM12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3Z" fill="#CFD5F0"/>
</svg>

After

Width:  |  Height:  |  Size: 646 B

View File

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 6C8.68629 6 6 8.68629 6 12C6 15.3137 8.68629 18 12 18C15.3137 18 18 15.3137 18 12C18 8.68629 15.3137 6 12 6ZM8 12C8 9.79086 9.79086 8 12 8C14.2091 8 16 9.79086 16 12C16 14.2091 14.2091 16 12 16C9.79086 16 8 14.2091 8 12Z" fill="currentColor"/>
<path d="M18 7C18.5523 7 19 6.55228 19 6C19 5.44772 18.5523 5 18 5C17.4477 5 17 5.44772 17 6C17 6.55228 17.4477 7 18 7Z" fill="currentColor"/>
<path d="M14.0438 1H9.95624C8.5932 0.999992 7.50917 0.999986 6.63458 1.07144C5.73898 1.14462 4.9753 1.29768 4.27606 1.65396C3.14709 2.2292 2.2292 3.14709 1.65396 4.27606C1.29768 4.9753 1.14462 5.73898 1.07144 6.63458C0.999986 7.50917 0.999992 8.59319 1 9.95623V14.0438C0.999992 15.4068 0.999986 16.4908 1.07144 17.3654C1.14462 18.261 1.29768 19.0247 1.65396 19.7239C2.2292 20.8529 3.14709 21.7708 4.27606 22.346C4.9753 22.7023 5.73898 22.8554 6.63458 22.9286C7.50914 23 8.59313 23 9.95613 23H14.0438C15.4068 23 16.4909 23 17.3654 22.9286C18.261 22.8554 19.0247 22.7023 19.7239 22.346C20.8529 21.7708 21.7708 20.8529 22.346 19.7239C22.7023 19.0247 22.8554 18.261 22.9286 17.3654C23 16.4909 23 15.4069 23 14.0439V9.95622C23 8.59322 23 7.50914 22.9286 6.63458C22.8554 5.73898 22.7023 4.9753 22.346 4.27606C21.7708 3.14709 20.8529 2.2292 19.7239 1.65396C19.0247 1.29768 18.261 1.14462 17.3654 1.07144C16.4908 0.999986 15.4068 0.999992 14.0438 1ZM5.18404 3.43597C5.55435 3.24729 6.02552 3.12787 6.79744 3.0648C7.58104 3.00078 8.58337 3 10 3H14C15.4166 3 16.419 3.00078 17.2026 3.0648C17.9745 3.12787 18.4457 3.24729 18.816 3.43597C19.5686 3.81947 20.1805 4.43139 20.564 5.18404C20.7527 5.55435 20.8721 6.02552 20.9352 6.79744C20.9992 7.58104 21 8.58337 21 10V14C21 15.4166 20.9992 16.419 20.9352 17.2026C20.8721 17.9745 20.7527 18.4457 20.564 18.816C20.1805 19.5686 19.5686 20.1805 18.816 20.564C18.4457 20.7527 17.9745 20.8721 17.2026 20.9352C16.419 20.9992 15.4166 21 14 21H10C8.58337 21 7.58104 20.9992 6.79744 20.9352C6.02552 20.8721 5.55435 20.7527 5.18404 20.564C4.43139 20.1805 3.81947 19.5686 3.43597 18.816C3.24729 18.4457 3.12787 17.9745 3.0648 17.2026C3.00078 16.419 3 15.4166 3 14V10C3 8.58337 3.00078 7.58104 3.0648 6.79744C3.12787 6.02552 3.24729 5.55435 3.43597 5.18404C3.81947 4.43139 4.43139 3.81947 5.18404 3.43597Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,3 @@
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 11.1772C4.44772 11.1772 4 11.625 4 12.1772C4 12.7295 4.44772 13.1772 5 13.1772C16.6687 13.1772 7.33133 13.1772 19 13.1772C19.5523 13.1772 20 12.7295 20 12.1772C20 11.625 19.5523 11.1772 19 11.1772C7.33133 11.1772 16.6687 11.1772 5 11.1772Z" fill="#8890B8"/>
</svg>

After

Width:  |  Height:  |  Size: 373 B

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.53 6.152C9.22173 5.95933 8.83319 5.94913 8.51523 6.12536C8.19728 6.30158 8 6.63647 8 7V16.9846C7.99841 17.0819 8.01099 17.18 8.0385 17.2758C8.07256 17.3947 8.1281 17.5046 8.20065 17.601C8.28729 17.7165 8.39514 17.8085 8.5153 17.8749C8.63523 17.9415 8.7703 17.9841 8.91403 17.9964C9.03419 18.0068 9.15682 17.9957 9.27575 17.9615C9.37153 17.9341 9.46142 17.8927 9.54311 17.8398L17.53 12.848C17.8224 12.6653 18 12.3448 18 12C18 11.6552 17.8224 11.3347 17.53 11.152L9.53 6.152ZM10 15.1958V8.80425L15.1132 12L10 15.1958Z" fill="#056E62"/>
<path d="M12 2C9.91293 2 7.98492 2.16973 6.43398 2.37024C3.72437 2.72057 1.64092 4.82716 1.30213 7.53263C1.13587 8.8603 1 10.4249 1 12C1 13.5751 1.13587 15.1397 1.30213 16.4674C1.64092 19.1728 3.72437 21.2794 6.43398 21.6298C7.98492 21.8303 9.91293 22 12 22C14.0871 22 16.0151 21.8303 17.566 21.6298C20.2756 21.2794 22.3591 19.1728 22.6979 16.4674C22.8641 15.1397 23 13.5751 23 12C23 10.4249 22.8641 8.86031 22.6979 7.53263C22.3591 4.82717 20.2756 2.72057 17.566 2.37024C16.0151 2.16973 14.0871 2 12 2ZM6.69042 4.35374C8.17778 4.16144 10.0179 4 12 4C13.9821 4 15.8222 4.16144 17.3096 4.35374C19.1095 4.58645 20.4872 5.97472 20.7134 7.78114C20.873 9.05563 21 10.533 21 12C21 13.467 20.873 14.9444 20.7134 16.2189C20.4872 18.0253 19.1095 19.4135 17.3096 19.6463C15.8222 19.8386 13.9821 20 12 20C10.0179 20 8.17778 19.8386 6.69042 19.6463C4.89045 19.4135 3.51284 18.0253 3.28663 16.2189C3.12703 14.9444 3 13.467 3 12C3 10.533 3.12703 9.05563 3.28663 7.78114C3.51284 5.97472 4.89045 4.58645 6.69042 4.35374Z" fill="#056E62"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,3 @@
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 4.17725C11.4477 4.17725 11 4.62496 11 5.17725V11.1772H5C4.44772 11.1772 4 11.625 4 12.1772C4 12.7295 4.44772 13.1772 5 13.1772H11V19.1772C11 19.7295 11.4477 20.1772 12 20.1772C12.5523 20.1772 13 19.7295 13 19.1772V13.1772H19C19.5523 13.1772 20 12.7295 20 12.1772C20 11.625 19.5523 11.1772 19 11.1772H13V5.17725C13 4.62496 12.5523 4.17725 12 4.17725Z" fill="#8890B8"/>
</svg>

After

Width:  |  Height:  |  Size: 484 B

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.8738 4.00152C20.8559 3.99853 20.8312 3.99829 20.7943 4.01419L3.08901 11.6464C2.92188 11.7227 2.99609 11.875 3.20557 11.953L6.33288 13.057C6.85367 13.2408 7.12682 13.812 6.94299 14.3328C6.75915 14.8536 6.18794 15.1268 5.66715 14.9429L2.57985 13.8531C0.631184 13.3252 0.42504 10.6169 2.29729 9.80981L20.0026 2.17757C21.6235 1.47885 23.345 2.92579 22.9416 4.64133L19.2496 20.3434C18.9102 21.7871 17.2443 22.4605 15.9977 21.6571L9.9955 17.8416C8.94605 17.1619 8.69721 15.7326 9.45431 14.738C9.46673 14.7217 9.47966 14.7057 9.49307 14.6902L13.2431 10.3465C13.604 9.92841 14.2354 9.88209 14.6535 10.243C15.0715 10.6039 15.1179 11.2354 14.757 11.6534L11.0365 15.9629C10.9964 16.0297 11.0152 16.1177 11.0809 16.1616L17.0807 19.9756C17.1651 20.0303 17.2793 19.9854 17.3027 19.8857L20.9947 4.18356C21.004 4.14408 20.9994 4.11941 20.9933 4.10198C20.9857 4.0808 20.9704 4.05694 20.946 4.03647C20.9216 4.01601 20.8956 4.00517 20.8738 4.00152Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.42426 7C8.15312 6.99999 7.89212 7.10944 7.73608 7.33119C7.43981 7.75221 7 8.54714 7 9.55619C7 13 11 17 14.5019 17C15.4969 17 16.2722 16.5589 16.6807 16.2626C16.8954 16.1068 17 15.851 17 15.5858V15.118C17 14.7392 16.786 14.393 16.4472 14.2236L14.7896 13.3948C14.3385 13.1692 13.7901 13.3149 13.5103 13.7345L13.4237 13.8645C13.1735 14.2397 12.698 14.4084 12.3003 14.1959C11.2861 13.6542 10.3458 12.7139 9.80403 11.6997C9.59155 11.302 9.7603 10.8264 10.1355 10.5763L10.2655 10.4897C10.6851 10.2099 10.8307 9.66147 10.6052 9.21039L9.77638 7.5528C9.60699 7.21404 9.26076 7.00005 8.88201 7.00002L8.42426 7Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 3C7.02944 3 3 7.02944 3 12C3 13.98 3.70529 15.9091 4.80549 17.4084C4.98144 17.6482 5.04153 17.9538 4.96945 18.2424L4.40453 20.5037L6.76985 19.7511C7.03537 19.6666 7.32411 19.6967 7.56647 19.8342C8.87421 20.5761 10.3861 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3ZM1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23C10.1899 23 8.47951 22.562 6.97152 21.7857L3.3032 22.9529C2.95777 23.0628 2.5799 22.9772 2.3156 22.7291C2.0513 22.481 1.94196 22.1093 2.02982 21.7576L2.91854 18.2001C1.74583 16.4491 1 14.2695 1 12Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

28
components.d.ts vendored Normal file
View File

@ -0,0 +1,28 @@
/* eslint-disable */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
IDuoFlipChart: typeof import('~icons/duo/flip-chart')['default']
IDuoGroupChat: typeof import('~icons/duo/group-chat')['default']
IDuoLightning: typeof import('~icons/duo/lightning')['default']
IDuoMail: typeof import('~icons/duo/mail')['default']
IDuoPhone: typeof import('~icons/duo/phone')['default']
IDuoThumbsUp: typeof import('~icons/duo/thumbs-up')['default']
IDuoThunderMove: typeof import('~icons/duo/thunder-move')['default']
IMonoArrowUp: typeof import('~icons/mono/arrow-up')['default']
IMonoChevronLeft: typeof import('~icons/mono/chevron-left')['default']
IMonoChevronRight: typeof import('~icons/mono/chevron-right')['default']
IMonoInfo: typeof import('~icons/mono/info')['default']
IMonoInstagram: typeof import('~icons/mono/instagram')['default']
IMonoPlay: typeof import('~icons/mono/play')['default']
IMonoTelegram: typeof import('~icons/mono/telegram')['default']
IMonoWhatsapp: typeof import('~icons/mono/whatsapp')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}

111
components/app-footer.vue Normal file
View File

@ -0,0 +1,111 @@
<template>
<footer class="footer">
<div class="container">
<div class="footer__inner">
<div class="footer__documents">
<a href="/documents/privacy-policy.pdf" target="_blank">Политика конфиденциальности</a>
<a href="/documents/policy.pdf" target="_blank">Публичная оферта</a>
<a href="/documents/payment-policy.pdf" target="_blank">Политика оплаты</a>
</div>
<div class="footer__copyright">
@ 2024-2025 Quantum. Все права защищены
</div>
<div class="footer__socials">
<SocialLink href="https://www.instagram.com/samatk7/?igsh=Mm9keGxnbXBrdGQ4">
<IMonoInstagram />
</SocialLink>
<SocialLink href="https://t.me/+vOQDGC3VoUJmY2Qy">
<IMonoTelegram />
</SocialLink>
<SocialLink href="https://api.whatsapp.com/send/?phone=7077407714&text&type=phone_number&app_absent=0">
<IMonoWhatsapp />
</SocialLink>
</div>
<a class="footer__dev" href="https://4keller.com/" target="_blank" rel="noopener noreferrer">
<svg xmlns="http://www.w3.org/2000/svg" height="29" viewBox="0 0 75 30">
<path fill="currentColor" d="M26.465 10.712H23.93l-4.174 9.176v-9.176h-2.317v18.465h2.317v-4.871l1.77-3.58l2.863 8.451h2.644L22.859 18.03zm3.125 0h-1.203v18.465h7.649V26.89h-5.332v-5.732h4.72V18.87h-4.72V13h5.332v-2.289zm10.599.001h-2.316v18.465h7.386V26.89h-5.07zm9.244 0h-2.338v18.465h7.408V26.89h-5.07zm8.108 0h-1.202v18.465h7.626V26.89h-5.31v-5.732h4.699V18.87h-4.699V13h5.31v-2.289zM75 29.177l-2.622-8.292c.022 0 .022-.023.043-.023a3.55 3.55 0 0 0 1.552-1.472c.35-.657.524-1.428.524-2.334v-2.152c0-.884-.174-1.631-.524-2.266a3.34 3.34 0 0 0-1.552-1.427c-.677-.317-1.486-.499-2.447-.499H65.8v18.465h2.316v-7.816h1.989l2.316 7.816zm-6.862-16.199h2.054c.416 0 .765.09 1.071.25c.306.158.525.407.678.747c.153.317.24.725.24 1.178v1.88c0 .43-.087.793-.24 1.11a1.6 1.6 0 0 1-.678.703c-.306.159-.656.25-1.07.25h-2.055zM0 .2v22.09L5.18.2zm3.65 23.155h4.72v-10.83h3.627v10.83h1.333V.2H8.916zm9.702 4.35h-1.355v1.472h1.355zm-4.982 0H0v1.472h8.37zM19.23.268h-1.813v7.295h1.814q.49 0 .852-.204c.24-.136.437-.317.546-.543q.197-.375.197-.884v-4.01c0-.521-.131-.929-.415-1.2c-.284-.295-.678-.454-1.18-.454m.372 5.528a.67.67 0 0 1-.131.43c-.087.114-.218.16-.372.16h-.502V1.468h.502c.154 0 .285.045.372.158a.67.67 0 0 1 .131.43zM25.087.268h-.524v7.295h3.125v-1.2h-1.923V4.55h1.682v-1.2h-1.682V1.49h1.923V.268zm9.201 3.738a2 2 0 0 0-.416-.34l-.459-.272c-.174-.09-.35-.181-.502-.272a1.8 1.8 0 0 1-.394-.294a.56.56 0 0 1-.153-.408v-.476c0-.158.044-.294.153-.385q.164-.136.394-.136c.175 0 .284.045.393.159a.67.67 0 0 1 .131.43v.408l1.202-.023V1.99c0-.567-.153-1.02-.459-1.315C33.872.358 33.458.2 32.911.2s-.984.158-1.29.475c-.305.318-.458.748-.458 1.315v.385c0 .294.043.543.13.77q.132.306.328.544a3.6 3.6 0 0 0 .438.385c.152.113.327.204.48.272c.175.09.328.158.46.249c.13.09.261.181.349.272a.6.6 0 0 1 .13.385v.634c0 .182-.043.318-.152.43c-.087.114-.24.16-.415.16s-.306-.046-.416-.16c-.109-.112-.152-.248-.152-.43v-.385H31.14v.385c0 .386.065.703.218.975c.131.271.35.475.612.611s.59.204.962.204c.546 0 .983-.158 1.289-.476s.459-.747.459-1.336v-.612c0-.295-.044-.544-.11-.725a1.2 1.2 0 0 0-.283-.521M39.532.268H38.33v7.295h1.202zm5.508 4.554h.59v.997c0 .18-.044.317-.154.407a.63.63 0 0 1-.415.159q-.262 0-.393-.136c-.11-.09-.153-.226-.153-.385V2.08c0-.18.044-.34.13-.453c.088-.113.22-.158.394-.158c.153 0 .262.045.35.158c.087.09.131.25.131.408v.566h1.224v-.566c0-.385-.066-.702-.197-.997a1.5 1.5 0 0 0-.59-.634a2 2 0 0 0-.918-.227c-.371 0-.677.068-.94.227a1.5 1.5 0 0 0-.59.634q-.196.408-.196 1.02v3.806c0 .385.065.702.218.951c.132.272.35.476.612.612c.263.136.59.204.962.204c.371 0 .677-.068.94-.226c.262-.136.458-.363.59-.635c.13-.272.196-.612.196-.997V3.598h-1.77v1.224zm7.954-.068L51.486.268h-.962v7.295h1.115V3.327l1.508 4.236h.961V.268h-1.114zM65.21 3.78c.328-.068.568-.182.765-.408q.261-.306.262-.816v-.77c0-.476-.131-.838-.393-1.11C65.58.404 65.21.29 64.729.29h-1.814v7.295h1.923c.481 0 .853-.136 1.093-.408c.262-.272.393-.634.393-1.133v-.997c0-.362-.087-.657-.284-.86a1.4 1.4 0 0 0-.83-.408m-.612-2.334c.131 0 .24.045.328.113c.087.09.109.204.109.34v.793a.7.7 0 0 1-.11.408a.44.44 0 0 1-.327.136h-.48v-1.79zm.524 4.486c0 .136-.043.272-.13.34c-.088.09-.198.113-.35.113h-.547V4.369h.525c.153 0 .284.045.371.158c.088.09.131.25.131.43zM71.984.268l-.612 3.036l-.677-3.036h-1.246l1.333 4.509v2.786h1.18V4.777l1.29-4.509z" />
</svg>
</a>
</div>
</div>
</footer>
</template>
<script setup lang="ts">
</script>
<style lang="scss">
.footer {
padding-bottom: 60px;
margin-top: 100px;
@include mobile {
margin-top: 40px;
padding-bottom: 48px;
}
&__inner {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-areas:
'documents copyright'
'socials dev';
padding-inline: 60px;
gap: 32px;
@include mobile {
grid-template-areas:
'documents documents'
'copyright copyright'
'socials dev';
padding-inline: 16px;
column-gap: 16px;
}
}
&__documents {
grid-area: documents;
display: inline-flex;
align-items: center;
gap: 24px;
@include mobile {
align-items: flex-start;
flex-direction: column;
}
}
&__copyright {
@include font(16px, 400, 20px);
grid-area: copyright;
justify-self: flex-end;
color: $color-gray-600;
@include mobile {
@include font(14px, 400, 18px);
text-align: center;
justify-self: stretch;
}
}
&__socials {
grid-area: socials;
display: inline-flex;
align-items: center;
gap: 24px;
padding-inline: 12px;
font-size: 24px;
}
&__dev {
grid-area: dev;
justify-self: flex-end;
color: $color-gray-600;
}
}
</style>

105
components/app-header.vue Normal file
View File

@ -0,0 +1,105 @@
<template>
<header class="header">
<div class="container">
<div class="header__inner">
<NuxtImg src="/logo.svg" alt="one2one" class="header__logo" draggable="false" />
<hr class="header__divider desktop-only">
<a href="#how-it-works" class="header__demo desktop-only">
<IMonoPlay />
<span>Демо видео о продукте</span>
</a>
<div class="header__socials">
<SocialLink href="https://www.instagram.com/samatk7/?igsh=Mm9keGxnbXBrdGQ4">
<IMonoInstagram />
</SocialLink>
<SocialLink href="https://t.me/+vOQDGC3VoUJmY2Qy">
<IMonoTelegram />
</SocialLink>
<SocialLink href="https://api.whatsapp.com/send/?phone=7077407714&text&type=phone_number&app_absent=0">
<IMonoWhatsapp />
</SocialLink>
</div>
<hr class="header__divider">
<PhoneNumber />
</div>
</div>
</header>
</template>
<script lang="ts" setup>
import SocialLink from '~/components/social-link.vue'
</script>
<style lang="scss">
.header {
position: sticky;
top: 0;
background-color: $color-gray-200;
z-index: 100;
&__inner {
display: flex;
align-items: center;
height: 80px;
padding: 16px 24px;
justify-content: flex-start;
@include mobile {
padding: 12px 0;
height: unset;
}
}
&__logo {
height: 21px;
@include mobile {
height: 16px;
}
}
&__divider {
height: 21px;
width: 1px;
border: none;
background-color: $color-gray-400;
margin-inline: 24px;
@include mobile {
margin-inline: 12px;
}
}
&__demo {
@include font(12px, 500, 15px);
display: flex;
align-items: center;
gap: 8px;
color: $color-gray-700;
> svg {
font-size: 24px;
color: $color-green-500;
}
}
&__socials {
display: flex;
align-items: center;
gap: 24px;
margin-left: auto;
font-size: 24px;
@include mobile {
font-size: 16px;
gap: 16px;
}
}
}
</style>

View File

@ -0,0 +1,106 @@
<template>
<div class="any-questions">
<div class="container">
<div class="any-questions__inner">
<div class="any-questions__left">
<IDuoMail class="any-questions__icon" />
<div>
<div class="any-questions__title">
Остались вопросы?
</div>
<a class="any-questions__link" href="mailto:support@quantumbot.kz">
support@quantumbot.kz
</a>
</div>
</div>
<div class="any-questions__right">
<p class="any-questions__call-us">
Позвоните нам
</p>
<PhoneNumber class="any-questions__phone-number" />
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
</script>
<style lang="scss">
.any-questions {
&__inner {
display: flex;
align-items: center;
justify-content: space-between;
padding: 56px 60px;
border-radius: 12px;
background-color: $color-gray-200;
@include mobile {
flex-direction: column;
align-items: unset;
gap: 24px;
padding: 24px 16px;
}
}
&__left {
display: flex;
align-items: center;
gap: 32px;
@include mobile {
gap: 12px;
}
}
&__icon {
flex-shrink: 0;
font-size: 69px;
}
&__title {
@include font(32px, 600, 41px);
color: $color-black;
text-transform: uppercase;
@include mobile {
@include font(20px, 600, 24px);
text-transform: unset;
}
}
&__link {
@include font(16px, 400, 20px);
color: $color-gray-600;
@include mobile {
@include font(14px, 300, 17px);
}
}
&__right {}
&__call-us {
color: $color-gray-600;
margin-bottom: 8px;
}
&__phone-number {
@include font(20px, 700, 26px, 'phone-number', true);
@include mobile {
@include font(16px, 700, 20px, 'phone-number', true);
}
}
}
</style>

View File

@ -0,0 +1,59 @@
<template>
<div class="faq">
<div class="container">
<div class="faq__inner">
<div class="faq__title">
Вопросы и ответы
</div>
<div class="faq__items">
<UiAccordion>
<UiAccordionItem title="Как подключить сервис к моему аккаунту Kaspi.kz?">
Достаточно добавить аккаунт на Kaspi.kz с ограниченными правами и подключить его к нашему сервису. Весь процесс регистрации занимает 2 минуты
</UiAccordionItem>
<UiAccordionItem title="Какой уровень безопасности данных обеспечивает ваш сервис?">
Мы обеспечиваем высокий уровень безопасности, используя современные методы шифрования данных. Все ваши учетные данные и информация о товарах надежно защищены и не передаются третьим лицам.
</UiAccordionItem>
<UiAccordionItem title="Подходит ли ваш сервис для всех категорий товаров?">
Да, наш сервис поддерживает все категории товаров, представленных на Kaspi.kz. Вы можете регулировать цены как для небольшого ассортимента, так и для крупной базы товаров.
</UiAccordionItem>
<UiAccordionItem title="Как быстро я увижу результаты?">
Многие наши клиенты отмечают увеличение продаж уже в первые минуты использования сервиса. Точные результаты зависят от конкуренции в вашей категории и динамики рынка.
</UiAccordionItem>
</UiAccordion>
</div>
</div>
</div>
</div>
</template>
<style lang="scss">
.faq {
&__inner {
border-radius: 12px;
padding: 40px;
background-color: $color-gray-200;
@include mobile {
padding: 16px;
}
}
&__title {
@include font(32px, 900, 45px);
text-transform: uppercase;
margin-bottom: 40px;
@include mobile {
@include font(20px, 900, 28px);
margin-bottom: 24px;
}
}
}
</style>

View File

@ -0,0 +1,226 @@
<template>
<div class="homepage-hero">
<div class="container">
<div class="homepage-hero__description">
Приложение для автоматической регулировки цен товаров
</div>
<h1 class="homepage-hero__title">
Автоматизация цен <br> <strong>для максимальной прибыли</strong> на <b>Kaspi.kz</b>
</h1>
<div class="homepage-hero__subtitle">
Помогаем увеличивать продажи и оставаться конкурентоспособными
</div>
<div class="homepage-hero__actions desktop-only">
<UiButton type="secondary" href="#hero-cards">
Узнать подробности
</UiButton>
<InfoButton />
</div>
<NuxtImg height="650" class="homepage-hero__image desktop-only" src="/hero-desktop.png" draggable="false" />
<NuxtImg height="650" class="homepage-hero__image mobile-only" src="/hero-mobile.png" draggable="false" />
<div class="homepage-hero__actions mobile-only">
<UiButton type="secondary">
Узнать подробности
</UiButton>
<InfoButton />
</div>
<div id="hero-cards" class="homepage-hero__cards">
<div class="homepage-hero-card">
<IDuoThumbsUp class="homepage-hero-card__icon" />
<div class="homepage-hero-card__title">
Удобно
</div>
<div class="homepage-hero-card__description">
Автоматическое обновление цен в реальном времени
</div>
</div>
<div class="homepage-hero-card">
<IDuoLightning class="homepage-hero-card__icon" />
<div class="homepage-hero-card__title">
Быстро
</div>
<div class="homepage-hero-card__description">
Простота интеграции с Kaspi.kz
</div>
</div>
<div class="homepage-hero-card">
<IDuoFlipChart class="homepage-hero-card__icon" />
<div class="homepage-hero-card__title">
Выгодно
</div>
<div class="homepage-hero-card__description">
Увеличение продаж за счет конкурентных цен
</div>
</div>
<div class="homepage-hero-card">
<IDuoGroupChat class="homepage-hero-card__icon" />
<div class="homepage-hero-card__title">
Без проблем
</div>
<div class="homepage-hero-card__description">
Поддержка 24/7
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
</script>
<style lang="scss">
.homepage-hero {
text-align: center;
&__description {
@include font(15px, 300, 18px);
color: $color-gray-600;
margin-bottom: 24px;
@include mobile {
@include font(11px, 300, 13px);
margin-bottom: 16px;
}
}
&__title {
@include font(36px, 600, 46px);
text-transform: uppercase;
@include mobile {
@include font(18px, 600, 23px);
}
strong {
font-weight: 800;
}
b {
color: $color-green-500;
}
}
&__subtitle {
@include font(24px, 400, 31px);
color: $color-gray-700;
margin-top: 24px;
@include mobile {
@include font(14px, 400, 18px);
margin-top: 16px;
}
}
&__image {
@include mobile {
height: auto;
margin-top: 32px;
}
}
&__actions {
display: flex;
align-items: center;
justify-content: center;
gap: 40px;
padding-blocK: 16px;
margin-top: 24px;
@include mobile {
flex-direction: column;
align-items: unset;
gap: 16px;
padding: 0;
margin-top: 32px;
}
}
&__cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 60px;
padding-inline: 78px;
@include mobile {
display: flex;
flex-direction: column;
gap: 24px;
margin-top: 24px;
padding: 0;
}
}
}
.homepage-hero-card {
text-align: left;
@include mobile {
display: grid;
grid-template-columns: 24px 1fr;
padding: 16px;
border-radius: 12px;
background-color: $color-gray-200;
column-gap: 12px;
row-gap: 16px;
}
&__icon {
font-size: 40px;
@include mobile {
font-size: 24px;
}
}
&__title {
@include font(22px, 600, 26px);
margin-top: 24px;
@include mobile {
@include font(20px, 600, 24px);
flex: 1;
margin-top: 0;
}
}
&__description {
@include font(16px, 300, 19px);
margin-top: 24px;
@include mobile {
@include font(12px, 300, 14px);
margin-top: 0;
grid-column: span 2;
}
}
}
</style>

View File

@ -0,0 +1,217 @@
<template>
<HomepageSection
id="how-it-works"
class="how-it-works"
title="Как работает наш сервис"
description="Четыре простых шага для увеличения ваших продаж"
>
<div class="how-it-works__cards">
<div class="how-it-works-card">
<div class="how-it-works-card__title">
Подключение
</div>
<div class="how-it-works-card__description">
Подключите свой аккаунт Kaspi.kz <br>
к нашему сервису
</div>
</div>
<div class="how-it-works-card">
<div class="how-it-works-card__title">
Настройка
</div>
<div class="how-it-works-card__description">
Настройте минимальные цены <br>
для товаров
</div>
</div>
<div class="how-it-works-card">
<div class="how-it-works-card__title">
Мониторинг
</div>
<div class="how-it-works-card__description">
Наш сервис автоматически мониторит <br>
конкурентов и обновляет цены
</div>
</div>
<div class="how-it-works-card">
<div class="how-it-works-card__title">
Прирост
</div>
<div class="how-it-works-card__description">
Получите кратный прирост <br>
продаж и прибыли
</div>
</div>
</div>
<div class="how-it-works-demo">
<div class="how-it-works-demo__title">
Демо видео о продукте
</div>
<iframe
class="how-it-works-demo__video"
src="https://www.youtube.com/embed/dQw4w9WgXcQ?si=dZCL9UFbyboiQzSl"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen
/>
<ul class="how-it-works-demo__list">
<li>Ознакомьтесь с интерфейсом приложения для ПК</li>
<li>Оцените удобство мобильного интерфейса</li>
<li>Просмотрите пример добавления товаров и управления ими через приложение</li>
</ul>
<UiButton class="how-it-works-demo__action">
Попробовать бесплатно
</UiButton>
</div>
</HomepageSection>
</template>
<script setup lang="ts">
</script>
<style lang="scss">
.how-it-works {
&__cards {
gap: 40px;
@include desktop {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
@include mobile {
display: flex;
flex-direction: column;
}
}
}
.how-it-works-card {
position: relative;
padding: 32px 16px;
background-color: $color-gray-300;
border-radius: 12px;
text-align: center;
counter-increment: how-it-works-card;
&::before {
@include font(24px, 600, 40px);
content: counter(how-it-works-card);
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, -50%);
padding-inline: 16px;
border-radius: 12px;
background-color: $color-black;
color: $color-white;
min-width: 46px;
}
@include mobile {
padding: 24px 16px;
}
&__title {
@include font(20px, 700, 24px);
}
&__description {
@include font(14px, 400, 17px);
color: $color-gray-700;
margin-top: 10px;
}
}
.how-it-works-demo {
padding: 40px 100px;
background-color: $color-gray-300;
margin-top: 60px;
border-radius: 12px;
@include desktop {
display: grid;
justify-items: flex-start;
grid-template-columns: 485px 1fr;
grid-template-rows: 1fr auto 1fr;
grid-template-areas:
'title video'
'list video'
'action video';
gap: 24px;
}
@include mobile {
padding: 16px;
margin-top: 32px;
}
&__title {
@include font(28px, 700, 39px);
grid-area: title;
@include desktop {
align-self: flex-end;
}
@include mobile {
@include font(20px, 700, 28px);
text-align: center;
margin-bottom: 24px;
}
}
&__video {
grid-area: video;
aspect-ratio: 16 / 9;
@include desktop {
justify-self: center;
height: 270px;
}
@include mobile {
width: 100%;
}
}
&__list {
@include font(16px, 400, 29px);
color: $color-gray-700;
grid-area: list;
@include mobile {
@include font(12px, 400, 22px);
margin-top: 24px;
}
}
&__action {
grid-area: action;
@include mobile {
width: 100%;
margin-top: 24px;
}
}
}
</style>

View File

@ -0,0 +1,216 @@
<template>
<HomepageSection
class="results"
title="Результаты наших клиентов"
description="Усредненные улучшения, которые отмечают наши клиенты"
>
<div class="results__cards">
<div class="result-card">
<p class="result-card__text">
<strong>Увеличение продаж</strong>
</p>
<p class="result-card__sub">
+ 100%
</p>
<NuxtImg class="result-card__image" src="/results/1.png" height="110px" loading="lazy" />
</div>
<div class="result-card">
<p class="result-card__text">
<strong>Увеличение прибыли</strong>
</p>
<p class="result-card__sub">
на 15%
</p>
<NuxtImg class="result-card__image" src="/results/2.png" height="110px" loading="lazy" />
</div>
<div class="result-card">
<p class="result-card__text">
<strong>+30% конверсия</strong>
заказов
</p>
<p class="result-card__sub">
заказов
</p>
<NuxtImg class="result-card__image" src="/results/3.png" height="110px" loading="lazy" />
</div>
<div class="result-card">
<p class="result-card__text">
<strong>Экономия 10+ часов</strong>
</p>
<p class="result-card__sub">
в неделю на ручном <br> мониторинге
</p>
<NuxtImg class="result-card__image" src="/results/4.png" height="110px" loading="lazy" />
</div>
</div>
<div class="results__contact-us results-contact-us">
<div class="results-contact-us__left">
<IDuoThunderMove class="results-contact-us__icon" />
<div class="results-contact-us__title">
Хотите таких же результатов?
</div>
<div class="results-contact-us__description">
Подключите наш сервис уже сегодня и начните увеличивать свои продажи на Kaspi.kz
</div>
</div>
<InfoButton class="results-contact-us__action" />
</div>
</HomepageSection>
</template>
<script setup lang="ts">
import InfoButton from '~/components/info-button.vue'
</script>
<style lang="scss">
.results {
&__cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 32px;
@include mobile {
display: flex;
flex-direction: column;
}
}
&__contact-us {
margin-top: 40px;
@include desktop {
display: flex;
align-items: center;
justify-content: space-between;
}
@include mobile {
margin-top: 32px;
}
}
}
.result-card {
position: relative;
padding: 24px;
background-color: $color-gray-300;
border-radius: 12px;
@include desktop {
height: 200px;
}
@include mobile {
display: grid;
grid-template-columns: 50px 1fr;
grid-template-rows: auto 1fr;
column-gap: 24px;
padding: 16px;
}
&__text {
@include font(18px, 800, 25px);
text-transform: uppercase;
@include mobile {
@include font(15px, 800, 21px);
grid-row: 1;
grid-column: 2;
}
}
&__sub {
@include font(18px, 500, 25px);
@include mobile {
@include font(15px, 500, 21px);
grid-column: 2;
grid-row: 2;
}
}
&__image {
@include desktop {
position: absolute;
bottom: 12px;
right: 24px;
}
@include mobile {
height: auto;
grid-column: 1;
grid-row: span 2;
}
}
}
.results-contact-us {
background-color: $color-gray-200;
border-radius: 12px;
padding: 32px 40px;
@include mobile {
padding: 16px;
}
&__left {
display: grid;
grid-template-columns: 59px 1fr;
column-gap: 24px;
@include mobile {
column-gap: 12px;
row-gap: 16px;
}
}
&__icon {
font-size: 59px;
@include desktop {
grid-row: span 2;
}
}
&__title {
@include font(22px, 700, 31px);
@include mobile {
@include font(20px, 600, 24px);
align-self: center;
}
}
&__description {
@include font(20px, 300, 28px);
@include mobile {
@include font(14px, 300, 17px);
grid-column: span 2;
}
}
&__action {
@include mobile {
margin-top: 24px;
}
}
}
</style>

View File

@ -0,0 +1,165 @@
<template>
<HomepageSection
class="reviews"
title="Отзывы"
description="Честно о нас"
>
<div class="reviews__inner">
<div class="reviews-navigation-card desktop-only">
<div class="reviews-navigation-card__arrows">
<button class="reviews-navigation-card__arrow" type="button" @click="move('<')">
<IMonoChevronLeft />
</button>
<button class="reviews-navigation-card__arrow" type="button" @click="move('>')">
<IMonoChevronRight />
</button>
</div>
<div>
<div class="reviews-navigation-card__title">
1000+
</div>
<div class="reviews-navigation-card__subtitle">
селлеров уже выбирают нас
</div>
</div>
<InfoButton class="reviews-navigation-card__action" />
</div>
<div class="mobile-only">
<div class="reviews__title">
1000+
</div>
<div class="reviews__subtitle">
селлеров уже выбирают нас
</div>
</div>
<Splide ref="splideEl" class="reviews__carousel" :options="splideOptions">
<SplideSlide v-for="i in 6" :key="i">
<NuxtImg class="reviews__review" :src="`/reviews/${i}.png`" height="416" />
</SplideSlide>
</Splide>
<InfoButton class="reviews__action mobile-only" />
</div>
</HomepageSection>
</template>
<script setup lang="ts">
import type { Options as SplideOptions } from '@splidejs/vue-splide'
import { Splide, SplideSlide } from '@splidejs/vue-splide'
const splideEl = ref()
const splideOptions = computed(() => ({
gap: 40,
autoWidth: true,
focus: 0,
omitEnd: true,
arrows: false,
pagination: false,
padding: { right: 40 },
type: 'loop',
breakpoints: {
480: {
padding: 0,
gap: 16,
},
},
} as SplideOptions))
function move(dir: '<' | '>'): void {
splideEl.value.go(dir)
}
</script>
<style lang="scss">
.reviews {
&__inner {
background-color: $color-gray-300;
border-radius: 12px;
@include desktop {
display: grid;
grid-template-columns: 270px 1fr;
gap: 24px;
padding: 40px 0 40px 40px;
}
@include mobile {
padding: 16px;
}
}
&__title {
@include font(32px, 700, 38px);
color: $color-gray-700;
}
&__subtitle {
@include font(14px, 400, 17px);
color: $color-gray-600;
margin-bottom: 24px;
}
&__review {
border-radius: 15px;
}
&__action {
margin-top: 24px;
}
}
.reviews-navigation-card {
display: flex;
flex-direction: column;
justify-content: space-between;
background-color: $color-gray-200;
border: 1px solid $color-gray-400;
border-radius: 15px;
padding: 24px;
&__arrows {
display: flex;
gap: 8px;
}
&__arrow {
cursor: pointer;
color: $color-gray-700;
transition: color $transition-duration $transition-easing;
font-size: 24px;
background: none;
border: none;
padding: 0;
&:hover,
&:focus {
color: $color-green-400;
}
&:active {
color: $color-green-500;
}
}
&__title {
@include font(48px, 700, 58px);
color: $color-gray-700;
}
&__subtitle {
@include font(20px, 400, 24px);
color: $color-gray-600;
}
}
</style>

View File

@ -0,0 +1,53 @@
<template>
<div class="homepage-section">
<div class="container">
<div class="homepage-section__title">
{{ title }}
</div>
<p class="homepage-section__description">
{{ description }}
</p>
<div class="homepage-section__content">
<slot />
</div>
</div>
</div>
</template>
<script setup lang="ts">
defineProps<{
title: string
description: string
}>()
</script>
<style lang="scss">
.homepage-section {
&__title {
@include font(36px, 700, 46px);
text-align: center;
@include mobile {
@include font(20px, 700, 26px);
}
}
&__description {
@include font(14px, 300, 17px);
text-align: center;
margin-top: 8px;
@include mobile {
@include font(13px, 300, 16px);
}
}
&__content {
margin-top: 40px;
}
}
</style>

View File

@ -0,0 +1,94 @@
<template>
<HomepageSection
class="tariff"
title="Единый тариф"
description="Получите полный доступ ко всему функционалу за фиксированную цену"
>
<div class="tariff__inner">
<div class="tariff__header">
<NuxtImg class="mobile-only" src="/price-bg.png" height="35px" />
<div class="tariff__price">
30 000 / месяц
</div>
</div>
<ul class="tariff__list">
<li>Безлимитное количество товаров</li>
<li>Поддержка 24/7</li>
<li>Высокая скорость обновления цен</li>
</ul>
<UiButton class="tariff__action">
Попробовать 14 дней бесплатно
</UiButton>
</div>
</HomepageSection>
</template>
<script setup>
const img = useImage()
const desktopBg = computed(() => {
return `url('${img('/price-bg.png', { height: '352px' })}')`
})
</script>
<style lang="scss">
.tariff {
&__inner {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
height: 400px;
padding: 24px 100px;
border-radius: 12px;
background-color: $color-gray-200;
@include desktop {
background-image: v-bind(desktopBg);
background-repeat: no-repeat;
background-position: center right 128px;
}
@include mobile {
padding: 16px;
height: unset;
}
}
&__header {
display: flex;
align-items: center;
gap: 24px;
}
&__price {
@include font(28px, 700, 39px);
@include mobile {
@include font(20px, 700, 28px);
}
}
&__list {
@include font(16px, 400, 29px);
color: $color-gray-700;
margin-top: 24px;
@include mobile {
@include font(12px, 400, 22px);
}
}
&__action {
margin-top: 24px;
@include mobile {
width: 100%;
}
}
}
</style>

View File

@ -0,0 +1,80 @@
<template>
<div class="info-button">
<UiButton class="info-button__button">
<slot>Начать бесплатно</slot>
</UiButton>
<Tooltip distance="8" placement="bottom-end">
<IMonoInfo class="info-button__info-trigger desktop-only" />
<template #popper>
Зарегистрируйтесь и получите <br>
7 дней бесплатной подписки для <br>
ознакомления с сервисом
</template>
</Tooltip>
<p class="info-button__hint mobile-only">
Зарегистрируйтесь и получите 14 дней бесплатной подписки для ознакомления с сервисом
</p>
</div>
</template>
<script setup lang="ts">
import { Tooltip } from 'floating-vue'
// const infoTriggerEl = ref()
// const tooltipEl = ref()
// const tooltipArrowEl = ref()
//
// const { floatingStyles: tooltipStyles, middlewareData } = useFloating(
// infoTriggerEl,
// tooltipEl,
// {
// placement: 'bottom-end',
// middleware: [offset(8), arrow({ element: tooltipArrowEl })],
// },
// )
//
// const arrowStyles = computed(() => {
// if (!middlewareData.value.arrow) {
// return {
// position: 'absolute',
// }
// }
//
// return {
// position: 'absolute',
// left: `${middlewareData.value.arrow.x ?? 0}px`,
// top: `${middlewareData.value.arrow.y ?? 0}px`,
// }
// })
</script>
<style lang="scss">
.info-button {
@include desktop {
display: flex;
align-items: center;
gap: 8px;
}
&__button {
width: 100%;
}
&__info-trigger {
flex-shrink: 0;
font-size: 24px;
color: $color-gray-500;
}
&__hint {
@include font(11px, 500, 14px);
text-align: center;
margin-top: 8px;
color: $color-gray-600;
}
}
</style>

View File

@ -0,0 +1,35 @@
<template>
<div class="phone-number">
<IDuoPhone class="phone-number__icon" />
<a href="tel:+77054002066" class="phone-number__number">
+7 (705) 400 20 66
</a>
</div>
</template>
<script setup lang="ts">
</script>
<style lang="scss">
.phone-number {
display: inline-flex;
align-items: center;
gap: 8px;
&__icon {
font-size: 24px;
flex-shrink: 0;
@include mobile {
display: none;
}
}
&__number {
@include font(14px, 700, 18px, 'phone-number');
color: $color-black;
}
}
</style>

View File

@ -0,0 +1,48 @@
<template>
<Transition name="scroll-to-top">
<button v-show="y > 100" class="scroll-to-top desktop-only" type="button" @click="y = 0">
<IMonoArrowUp />
</button>
</Transition>
</template>
<script lang="ts" setup>
import { useWindowScroll } from '@vueuse/core'
const { y } = useWindowScroll()
</script>
<style lang="scss">
.scroll-to-top {
position: fixed;
right: 60px;
bottom: 60px;
border-radius: 16px;
color: $color-gray-700;
width: 64px;
height: 64px;
transition: $transition-duration $transition-easing;
transition-property: background-color, color;
z-index: 1000;
background-color: $color-gray-300;
outline: none;
border: none;
cursor: pointer;
&:hover {
color: $color-black;
background-color: $color-gray-400;
}
}
.scroll-to-top-leave-active,
.scroll-to-top-enter-active {
transition: transform $transition-duration $transition-easing;
}
.scroll-to-top-enter-from,
.scroll-to-top-leave-to {
transform: scale(0);
}
</style>

View File

@ -0,0 +1,12 @@
<template>
<a class="social-link" target="_blank" rel="noopener noreferrer">
<slot />
</a>
</template>
<style lang="scss">
.social-link {
display: inline-block;
color: $color-gray-600;
}
</style>

View File

@ -0,0 +1,4 @@
import type { InjectionKey } from 'vue'
import type { AccordionContext } from './types'
export const accordionContextKey: InjectionKey<AccordionContext> = Symbol('UI_ACCORDION')

View File

@ -0,0 +1,78 @@
<template>
<div :class="[cn.b()]">
<slot />
</div>
</template>
<script setup lang="ts">
import type { AccordionActiveId, AccordionModelValue } from './types'
import { castArray } from 'lodash-es'
import { provide, ref, watch } from 'vue'
import { accordionContextKey } from './constants'
export interface Props {
multiple?: boolean
modelValue?: AccordionModelValue
}
defineOptions({
name: 'UiAccordion',
})
const props = withDefaults(defineProps<Props>(), {
multiple: false,
})
const emit = defineEmits<{
'update:modelValue': [value: Props['modelValue']]
}>()
const cn = useClassname('ui-accordion')
const activeItems = ref(castArray(props.modelValue))
function setActiveItems(_activeItems: AccordionActiveId[]) {
activeItems.value = _activeItems
const value = !props.multiple ? activeItems.value[0] : activeItems.value
emit('update:modelValue', value)
}
function handleItemClick(id: AccordionActiveId) {
if (!props.multiple) {
setActiveItems([activeItems.value[0] === id ? '' : id])
}
else {
const _activeItems = [...activeItems.value]
const index = _activeItems.indexOf(id)
if (index > -1)
_activeItems.splice(index, 1)
else
_activeItems.push(id)
setActiveItems(_activeItems)
}
}
function isActive(id: AccordionActiveId) {
return activeItems.value.includes(id)
}
watch(
() => props.modelValue,
value => (activeItems.value = castArray(value)),
{ deep: true },
)
provide(accordionContextKey, {
activeItems,
handleItemClick,
isActive,
})
</script>
<style lang="scss">
@use 'styles';
</style>

View File

@ -0,0 +1,65 @@
<template>
<div :class="[cn.b(), cn.is('focused', focused), cn.is('active', isActive)]">
<button
:class="[cn.e('head')]"
type="button"
@focus="focused = true"
@blur="focused = false"
@click="handleClick"
@keydown.space.enter.stop.prevent="handleClick"
>
<slot name="title">
<h3 :class="[cn.e('title')]">
{{ title }}
</h3>
</slot>
<Component :is="isActive ? IMonoMinus : IMonoPlus" :class="cn.e('icon')" />
</button>
<UiCollapseTransition>
<div
v-show="isActive"
:class="[cn.e('wrapper')]"
>
<div :class="[cn.e('content')]">
<slot />
</div>
</div>
</UiCollapseTransition>
</div>
</template>
<script setup lang="ts">
import IMonoMinus from '~icons/mono/minus'
import IMonoPlus from '~icons/mono/plus'
import { computed, inject, ref } from 'vue'
import UiCollapseTransition from '~/components/ui/collapse-transition/index.vue'
import { accordionContextKey } from './constants'
export interface Props {
id?: string | number
title?: string
}
defineOptions({
name: 'UiAccordionItem',
})
const props = withDefaults(defineProps<Props>(), {
id: () => useId(),
title: '',
})
const accordion = inject(accordionContextKey)
const cn = useClassname('ui-accordion-item')
const focused = ref(false)
const isActive = computed(() => accordion?.isActive(props.id) ?? false)
function handleClick() {
accordion?.handleItemClick(props.id)
}
</script>

View File

@ -0,0 +1,89 @@
@use '../../../styles/variables' as *;
@use '../../../styles/mixins' as *;
.ui-accordion {
display: flex;
flex-direction: column;
gap: 16px;
}
.ui-accordion-item {
$self: &;
border-radius: 12px;
background-color: $color-white;
transition: outline-color $transition-duration $transition-easing;
-webkit-tap-highlight-color: transparent;
outline: 1px solid $color-gray-500;
outline-offset: -1px;
&:focus,
&:hover,
&.is-active {
outline-width: 2px;
outline-offset: -2px;
}
&.is-active {
outline-color: $color-gray-600;
}
&__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
width: 100%;
text-align: left;
border: none;
padding: 24px;
background: transparent;
transition: padding $transition-duration $transition-easing;
cursor: pointer;
outline: none;
user-select: none;
color: $color-black;
@include mobile {
padding: 16px;
}
#{$self}.is-active & {
padding: 24px 24px 4px;
@include mobile {
padding: 16px 16px 4px;
}
}
}
&__title {
@include font(18px, 600, 23px);
@include mobile {
@include font(13px, 600, 17px);
}
}
&__icon {
flex-shrink: 0;
font-size: 24px;
color: $color-gray-600;
}
&__wrapper {
will-change: height;
overflow: hidden;
}
&__content {
@include font(14px, 400, 18px);
color: $color-gray-700;
padding: 0 24px 24px;
@include mobile {
padding: 0 16px 16px;
}
}
}

View File

@ -0,0 +1,10 @@
import type { Ref } from 'vue'
export type AccordionActiveId = string | number
export type AccordionModelValue = AccordionActiveId | AccordionActiveId[]
export interface AccordionContext {
activeItems: Ref<AccordionActiveId[]>
handleItemClick: (id: AccordionActiveId) => void
isActive: (id: AccordionActiveId) => boolean
}

View File

@ -0,0 +1,105 @@
<template>
<Component
:is="as"
:class="[
cn.b(),
cn.m(type),
cn.is('one-icon-only', oneIconOnly),
cn.has('icon', hasIcon),
cn.is('disabled', disabled),
]"
:disabled="disabled"
v-bind="attributes"
@click="handleClick"
>
<span
v-if="hasLeftIcon && displayIcon"
:class="[cn.e('icon'), cn.em('icon', 'left')]"
>
<slot name="left-icon" />
</span>
<span
v-if="!iconOnly"
:class="[cn.e('content')]"
><slot /></span>
<span
v-if="hasRightIcon && displayIcon"
:class="[cn.e('icon'), cn.em('icon', 'right')]"
>
<slot name="right-icon" />
</span>
</Component>
</template>
<script setup lang="ts">
import type { ButtonHTMLAttributes } from 'vue'
export interface Props {
type?: 'primary' | 'secondary'
nativeType?: ButtonHTMLAttributes['type']
disabled?: boolean
}
defineOptions({
name: 'UiButton',
})
const props = withDefaults(defineProps<Props>(), {
type: 'primary',
nativeType: 'button',
disabled: false,
})
const emit = defineEmits<{
click: [e: Event]
}>()
const slots = useSlots()
const attrs = useAttrs()
const cn = useClassname('ui-button')
const as = computed(() => (attrs.href ? 'a' : 'button'))
const attributes = computed(() => {
if (as.value === 'button') {
return {
type: props.nativeType,
...attrs,
}
}
return attrs
})
const hasLeftIcon = computed(() => !!slots['left-icon'])
const hasRightIcon = computed(() => !!slots['right-icon'])
const hasIcon = computed(() => hasLeftIcon.value || hasRightIcon.value)
const oneIconOnly = computed(
() =>
((hasLeftIcon.value && !hasRightIcon.value)
|| (!hasLeftIcon.value && hasRightIcon.value))
&& !slots.default,
)
const iconOnly = computed(
() => hasIcon.value && !slots.default,
)
const displayIcon = computed(() => hasIcon.value)
function handleClick(event: Event) {
if (props.disabled) {
event.stopPropagation()
event.preventDefault()
}
else {
emit('click', event)
}
}
</script>
<style lang="scss">
@use 'styles';
</style>

View File

@ -0,0 +1,113 @@
@use "sass:list";
@use '../../../styles/mixins' as *;
@use '../../../styles/variables' as *;
$button-types: 'filled', 'outlined', 'ghost', 'link';
$button-colors: 'primary', 'secondary';
/* prettier-ignore */
$button-variants: [
[
'primary',
(
'color': $color-white,
'background': $color-green-500,
'hover-background': $color-green-400,
'active-background': $color-gray-600,
)
],
[
'secondary',
(
'color': $color-green-500,
'background': $color-gray-300,
'hover-background': $color-gray-400,
'active-background': $color-gray-500,
)
]
];
.ui-button {
$self: &;
@include font(16px, 500, 20px);
--spinner-size: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
position: relative;
cursor: pointer;
outline: none;
padding: 13px 23px;
background: var(--button-background, transparent);
color: var(--button-color, inherit);
border-radius: 12px;
width: var(--button-width, unset);
height: var(--button-height, 48px);
border: 1px solid var(--button-border-color, transparent);
white-space: nowrap;
transition: $transition-duration $transition-easing;
transition-property: background-color, border-color, color, opacity;
&:visited {
color: var(--button-color, inherit);
}
&:hover,
&:focus-visible {
--button-icon-color: var(--button-hover-color, var(--button-color));
background: var(--button-hover-background, var(--button-background));
color: var(--button-hover-color, var(--button-color));
border-color: var(--button-hover-border-color, transparent);
}
&:active {
--button-icon-color: var(--button-active-color, var(--button-color));
background: var(--button-active-background, var(--button-background));
color: var(--button-active-color, var(--button-color));
border-color: var(--button-active-border-color, transparent);
}
&.is-disabled {
background: var(--button-disabled-background, transparent);
color: var(--button-disabled-color);
border-color: var(--button-disabled-border-color, transparent);
cursor: not-allowed;
}
&__icon {
color: var(--button-icon-color, inherit);
line-height: 1;
transition: color $transition-duration $transition-easing;
#{$self}.is-one-icon-only:not(#{$self}--link) & {
color: inherit;
}
}
&.is-one-icon-only {
padding: 0;
width: var(--button-width, var(--button-height, 40px));
}
@each $variant in $button-variants {
$color: list.nth($variant, 1);
$scheme: list.nth($variant, 2);
&--#{$color} {
@each $property, $value in $scheme {
--button-#{$property}: var(--button-#{$color}-#{$property});
}
}
/* prettier-ignore */
@include element-variant('button', $color, $scheme);
}
}

View File

@ -0,0 +1,103 @@
<template>
<Transition :name="ns.b()" v-on="bindings">
<slot />
</Transition>
</template>
<script lang="ts" setup>
import type { RendererElement } from 'vue'
defineOptions({
name: 'UiCollapseTransition',
})
const emit = defineEmits<{
expanded: []
collapsed: []
}>()
const ns = useClassname('ui-collapse-transition')
const bindings = {
beforeEnter(el: RendererElement) {
if (!el.dataset)
el.dataset = {}
el.dataset.oldPaddingTop = el.style.paddingTop
el.dataset.oldPaddingBottom = el.style.paddingBottom
el.dataset.elExistsHeight = el.style.height ?? undefined
el.style.maxHeight = 0
el.style.paddingTop = 0
el.style.paddingBottom = 0
},
enter(el: RendererElement) {
requestAnimationFrame(() => {
el.dataset.oldOverflow = el.style.overflow
if (el.dataset.elExistsHeight)
el.style.maxHeight = el.dataset.elExistsHeight
else if (el.scrollHeight !== 0)
el.style.maxHeight = `${el.scrollHeight}px`
else
el.style.maxHeight = 0
el.style.paddingTop = el.dataset.oldPaddingTop
el.style.paddingBottom = el.dataset.oldPaddingBottom
el.style.overflow = 'hidden'
})
},
afterEnter(el: RendererElement) {
el.style.maxHeight = ''
el.style.overflow = el.dataset.oldOverflow
emit('expanded')
},
enterCancelled(el: RendererElement) {
reset(el)
},
beforeLeave(el: RendererElement) {
if (!el.dataset)
el.dataset = {}
el.dataset.oldPaddingTop = el.style.paddingTop
el.dataset.oldPaddingBottom = el.style.paddingBottom
el.dataset.oldOverflow = el.style.overflow
el.style.maxHeight = `${el.scrollHeight}px`
el.style.overflow = 'hidden'
},
leave(el: RendererElement) {
if (el.scrollHeight !== 0) {
el.style.maxHeight = 0
el.style.paddingTop = 0
el.style.paddingBottom = 0
}
},
afterLeave(el: RendererElement) {
reset(el)
emit('collapsed')
},
leaveCancelled(el: RendererElement) {
reset(el)
},
}
function reset(el: RendererElement) {
el.style.maxHeight = ''
el.style.overflow = el.dataset.oldOverflow
el.style.paddingTop = el.dataset.oldPaddingTop
el.style.paddingBottom = el.dataset.oldPaddingBottom
}
</script>
<style lang="scss">
@use 'styles';
</style>

View File

@ -0,0 +1,24 @@
@use '../../../styles/variables' as *;
.ui-collapse-transition {
transition-property: height, padding-top, padding-bottom;
transition-timing-function: var(--collapse-transition-easing, $transition-easing);
transition-duration: var(--collapse-transition-duration, $transition-duration);
}
.ui-collapse-transition-leave-active,
.ui-collapse-transition-enter-active {
transition-property: max-height, padding-top, padding-bottom, opacity;
transition-timing-function: var(--collapse-transition-easing, $transition-easing);
transition-duration: var(--collapse-transition-duration, $transition-duration);
}
.ui-collapse-transition-enter-to,
.ui-collapse-transition-leave-from {
opacity: 1;
}
.ui-collapse-transition-enter-from,
.ui-collapse-transition-leave-to {
opacity: 0;
}

View File

@ -0,0 +1,128 @@
<template>
<Transition
:name="cn.b()"
@before-leave="$emit('close')"
@after-leave="$emit('destroy')"
>
<UiAlert
v-show="visible"
:id="id"
:class="[cn.b(), verticalSide, horizontalSide]"
:title="title"
:type="type"
@mouseenter="clearTimer"
@mouseleave="startTimer"
>
<slot />
</UiAlert>
</Transition>
</template>
<script setup lang="ts">
import type { NotificationPlacement, NotificationType } from './types'
import { computed, onMounted, ref } from 'vue'
export interface Props {
duration?: number
id?: string | number
offset?: number
placement?: NotificationPlacement
type?: NotificationType
title?: string
zIndex?: number
}
defineOptions({
name: 'UiNotification',
})
const props = withDefaults(defineProps<Props>(), {
type: 'neutral',
duration: 5000,
offset: 0,
placement: 'top-right',
zIndex: 0,
})
defineEmits(['close', 'destroy'])
const cn = useClassname('ui-notification')
const timerId = ref()
const visible = ref(false)
const verticalSide = computed(() => props.placement.split('-')[0])
const horizontalSide = computed(() => props.placement.split('-')[1])
function close() {
visible.value = false
}
function startTimer() {
if (props.duration > 0) {
timerId.value = setTimeout(() => {
if (visible.value)
close()
}, props.duration)
}
}
function clearTimer() {
if (timerId.value)
clearTimeout(timerId.value)
}
onMounted(() => {
startTimer()
visible.value = true
})
defineExpose({
visible,
close,
})
</script>
<style lang="scss">
.ui-notification {
position: fixed;
transition-duration: $transition-duration;
transition-property: opacity, transform, left, right, top, bottom;
z-index: calc(8000 + v-bind('zIndex'));
width: 400px;
transform-origin: right top;
@include mobile {
width: calc(100% - 32px);
}
&.top {
top: calc(v-bind('`${offset}px`') + 80px);
@include mobile {
top: auto;
bottom: v-bind('`${offset}px`');
}
}
&.bottom {
bottom: v-bind('`${offset}px`');
}
&.left {
left: 32px;
@include mobile {
left: 16px;
}
}
&.right {
right: 32px;
@include mobile {
right: 16px;
}
}
}
</style>

View File

@ -0,0 +1,132 @@
import { createVNode, render } from 'vue'
import { defu } from 'defu'
import NotificationConstructor from './notification.vue'
import type {
NotificationOptions,
NotificationPlacement,
NotificationQueue,
} from './types'
const notifications: Record<NotificationPlacement, NotificationQueue> = {
'top-left': [],
'top-right': [],
'bottom-left': [],
'bottom-right': [],
}
const GAP_SIZE = 16
let SEED = 1
const DEFAULT_OPTIONS: NotificationOptions = {
text: '',
placement: 'top-right',
duration: 5000,
onClose: () => {},
}
const notify = function (options: NotificationOptions, context = null) {
// if (process.server) return { close: () => undefined };
options = defu(options, DEFAULT_OPTIONS)
const orientedNotifications = notifications[options.placement!]
const id = options.id
? `${options.placement!}_${options.id}`
: `notification_${SEED++}`
const idx = orientedNotifications.findIndex(
({ vm }) => vm.component?.props.id === id,
)
if (idx > -1)
return
let verticalOffset = options.offset || 0
notifications[options.placement!].forEach(({ vm }) => {
verticalOffset += (vm.el?.offsetHeight || 0) + GAP_SIZE
})
verticalOffset += GAP_SIZE
const userOnClose = options.onClose
const props = {
...options,
offset: verticalOffset,
id,
onClose: () => {
close(id, options.placement!, userOnClose)
},
}
const container = document.createElement('div')
const vm = createVNode(
NotificationConstructor,
props,
options.text
? {
default: () => options.text,
}
: null,
)
vm.appContext = context ?? notify._context
vm.props!.onDestroy = () => {
render(null, container)
}
render(vm, container)
notifications[options.placement!].push({ vm })
document.body.appendChild(container.firstElementChild!)
return {
close: () => {
vm.component!.exposed!.close()
},
}
}
export function close(
id: NotificationOptions['id'],
placement: NotificationOptions['placement'],
userOnClose: NotificationOptions['onClose'],
) {
const orientedNotifications = notifications[placement!]
const idx = orientedNotifications.findIndex(
({ vm }) => vm.component?.props.id === id,
)
if (idx === -1)
return
const { vm } = orientedNotifications[idx]
if (!vm)
return
userOnClose?.(vm)
const removedHeight = vm.el!.offsetHeight
const verticalPos = placement!.split('-')[0]
orientedNotifications.splice(idx, 1)
if (orientedNotifications.length < 1)
return
for (let i = idx; i < orientedNotifications.length; i++) {
const { el, component } = orientedNotifications[i].vm
const styles = getComputedStyle(el as Element)
const pos = Number.parseInt(styles.getPropertyValue(verticalPos), 10)
component!.props.offset = pos - removedHeight - GAP_SIZE
}
}
export function closeAll() {
for (const orientedNotifications of Object.values(notifications)) {
orientedNotifications.forEach(({ vm }) => {
vm.component!.exposed!.close()
})
}
}
notify.closeAll = closeAll
notify._context = null
export default notify

View File

@ -0,0 +1,33 @@
import type { VNode } from 'vue'
export const NOTIFICATION_TYPES = [
'neutral',
'positive',
'warning',
'negative',
] as const
export type NotificationType = (typeof NOTIFICATION_TYPES)[number]
export type NotificationPlacement =
| 'top-right'
| 'top-left'
| 'bottom-right'
| 'bottom-left'
export interface NotificationOptions {
type?: NotificationType
text: string
title?: string
duration?: number
placement?: NotificationPlacement
id?: string | number
offset?: number
onClose?: (vm?: VNode) => void
}
export interface NotificationItem {
vm: VNode
}
export type NotificationQueue = NotificationItem[]

View File

@ -0,0 +1,44 @@
import { useField } from 'vee-validate'
import { computed, ref, toRef } from 'vue'
import type { ComponentInternalInstance } from 'vue'
interface Props {
id: string
label?: string
disabled?: boolean
modelValue?: boolean | string | number
trueValue?: boolean | string | number
falseValue?: boolean | string | number
required?: boolean
}
export default (props: Props, slots: ComponentInternalInstance['slots']) => {
const { checked, errorMessage, handleChange } = useField(
toRef(props, 'id'),
computed(() => ({
required: props.required,
})),
{
type: 'checkbox',
initialValue: props.modelValue,
checkedValue: props.trueValue ?? true,
uncheckedValue: props.falseValue ?? false,
syncVModel: true,
},
)
const focused = ref(false)
const active = computed(() => !props.disabled)
const invalid = computed(() => active.value && !!errorMessage.value)
const hasLabel = computed(() => !!props.label || slots.default)
return {
focused,
checked,
active,
invalid,
hasLabel,
handleChange,
}
}

View File

@ -0,0 +1,63 @@
const namespace = 'ui'
const statePrefix = 'is-'
function _bem(namespace: string, block: string, blockSuffix: string, element: string, modifier: string) {
let cls = `${block}`
if (blockSuffix)
cls += `-${blockSuffix}`
if (element)
cls += `__${element}`
if (modifier)
cls += `--${modifier}`
return cls
}
export default (block: string) => {
const b = (blockSuffix: string = '') => _bem(namespace, block, blockSuffix, '', '')
const e = (element: string) =>
element ? _bem(namespace, block, '', element, '') : ''
const m = (modifier: string) =>
modifier ? _bem(namespace, block, '', '', modifier) : ''
const be = (blockSuffix: string, element: string) =>
blockSuffix && element
? _bem(namespace, block, blockSuffix, element, '')
: ''
const em = (element: string, modifier: string) =>
element && modifier ? _bem(namespace, block, '', element, modifier) : ''
const bm = (blockSuffix: string, modifier: string) =>
blockSuffix && modifier
? _bem(namespace, block, blockSuffix, '', modifier)
: ''
const bem = (blockSuffix: string, element: string, modifier: string) =>
blockSuffix && element && modifier
? _bem(namespace, block, blockSuffix, element, modifier)
: ''
const is = (name: string, ...args: any[]) => {
const state = args.length >= 1 ? args[0] : true
return name && state ? `${statePrefix}${name}` : ''
}
const has = (name: string, ...args: any[]) => {
const state = args.length >= 1 ? args[0] : true
return name && state ? `has-${name}` : ''
}
const exp = (condition: boolean, trueState: string, falseState: string = '') => {
return condition ? trueState : falseState
}
return {
namespace,
b,
e,
m,
be,
em,
bm,
bem,
is,
has,
exp,
}
}

View File

@ -0,0 +1,3 @@
import notify from '../components/ui/notification/notify'
export default () => notify

40
composables/use-radio.ts Normal file
View File

@ -0,0 +1,40 @@
import { useField } from 'vee-validate'
import { computed, ref, toRef } from 'vue'
import type { ComponentInternalInstance } from 'vue'
interface Props {
id: string
value: string | number
label?: string
disabled?: boolean
modelValue?: string | number
required?: boolean
}
export default (props: Props, slots: ComponentInternalInstance['slots']) => {
const { checked, handleChange } = useField(
toRef(props, 'id'),
computed(() => ({
required: props.required,
})),
{
type: 'radio',
initialValue: props.modelValue,
checkedValue: props.value,
syncVModel: true,
},
)
const focused = ref(false)
const active = computed(() => !props.disabled)
const hasLabel = computed(() => !!props.label || slots.default)
return {
focused,
active,
checked,
hasLabel,
handleChange,
}
}

11
eslint.config.js Normal file
View File

@ -0,0 +1,11 @@
import antfu from '@antfu/eslint-config'
export default antfu({
overrides: {
vue: {
'vue/block-order': ['error', {
order: ['template', 'script', 'style'],
}],
},
},
})

155
nuxt.config.ts Normal file
View File

@ -0,0 +1,155 @@
import { promises as fs } from 'node:fs'
import {
cleanupSVG,
deOptimisePaths,
parseColors,
runSVGO,
SVG,
} from '@iconify/tools'
import { createResolver } from '@nuxt/kit'
import IconsResolver from 'unplugin-icons/resolver'
import ViteComponents from 'unplugin-vue-components/vite'
const { resolve } = createResolver(import.meta.url)
export default defineNuxtConfig({
devtools: { enabled: true },
// ssr: false,
pages: false,
app: {
head: {
htmlAttrs: {
lang: 'ru',
},
title: 'Quantum - бот для автоматизации цен на Kaspi.kz',
meta: [
{
name: 'description',
content: 'Интеллектуальный сервис для продавцов на Kaspi.kz, который автоматически корректирует цены на товары, помогая оптимизировать продажи и улучшить позиции в выдаче.',
},
{
property: 'og:title',
content: 'Quantum - бот для автоматизации цен на Kaspi.kz',
},
{
property: 'og:description',
content: 'Интеллектуальный сервис для продавцов на Kaspi.kz, который автоматически корректирует цены на товары, помогая оптимизировать продажи и улучшить позиции в выдаче.',
},
{
property: 'og:url',
content: 'https://quantumbot.kz',
},
{
property: 'og:type',
content: 'website',
},
{
property: 'og:site_name',
content: 'Quantum',
},
// TODO
// {
// property: 'og:image',
// content: 'https://one2oneacademy.ru/android-chrome-192x192.png',
// },
{
name: 'theme-color',
content: '#F9FAFF',
},
],
link: [
{
rel: 'manifest',
href: '/site.webmanifest',
},
],
},
},
modules: [
'@nuxtjs/google-fonts',
'@nuxt/image',
[
'unplugin-icons/nuxt',
{
customCollections: {
mono: async (name: string) => {
const filename = `./assets/icons/mono/${name}.svg`
const content = await fs.readFile(filename, 'utf8')
const svg = new SVG(content)
cleanupSVG(svg)
parseColors(svg, {
defaultColor: 'currentColor',
callback: () => {
return 'currentColor'
},
})
runSVGO(svg)
deOptimisePaths(svg)
return svg.toMinifiedString({
width: '1em',
height: '1em',
})
},
duo: async (name: string) => {
const filename = `./assets/icons/duo/${name}.svg`
const content = await fs.readFile(filename, 'utf8')
const svg = new SVG(content)
cleanupSVG(svg)
runSVGO(svg)
deOptimisePaths(svg)
return svg.toMinifiedString({
width: '1em',
height: '1em',
})
},
},
},
],
],
googleFonts: {
families: {
Onest: [300, 400, 500, 600, 700, 800, 900],
},
},
image: {
quality: 100,
densities: [1, 2],
format: ['webp'],
},
css: [resolve('./styles/index.scss'), 'vue-final-modal/style.css'],
vite: {
plugins: [
ViteComponents({
resolvers: [
IconsResolver({
customCollections: ['mono', 'duo'],
}),
],
dts: true,
}),
],
css: {
preprocessorOptions: {
scss: {
api: 'modern-compiler',
additionalData: `
@use "${resolve('./styles/mixins')}" as *;
@use "${resolve('./styles/variables')}" as *;
`,
},
},
},
},
compatibilityDate: '2024-11-23',
})

48
package.json Normal file
View File

@ -0,0 +1,48 @@
{
"name": "nuxt-app",
"type": "module",
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev --host",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
"deploy": "push-dir --dir=dist --branch=gh-pages --cleanup"
},
"dependencies": {
"@floating-ui/vue": "^1.1.5",
"@splidejs/vue-splide": "^0.6.12",
"@vee-validate/rules": "^4.14.7",
"@vueuse/core": "^11.3.0",
"floating-vue": "^5.2.2",
"imask": "^7.6.1",
"js-sha256": "^0.11.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"swiper": "^11.1.15",
"uuid": "^11.0.3",
"vee-validate": "^4.14.7",
"vue-final-modal": "^4.5.5"
},
"devDependencies": {
"@antfu/eslint-config": "^3.9.2",
"@iconify/tools": "^4.0.7",
"@nuxt/devtools": "latest",
"@nuxt/image": "^1.8.1",
"@nuxtjs/google-fonts": "^3.2.0",
"@types/lodash-es": "^4.17.12",
"@types/uuid": "^10.0.0",
"eslint": "^9.15.0",
"nuxt": "latest",
"nuxt-svgo": "^4.0.9",
"push-dir": "^0.4.1",
"sass": "^1.81.0",
"typescript": "^5.7.2",
"unplugin-icons": "^0.20.1",
"unplugin-vue-components": "^0.27.4",
"vue": "latest",
"vue-router": "latest",
"vue-tsc": "^2.1.10"
}
}

7
plugins/notify.ts Normal file
View File

@ -0,0 +1,7 @@
import notify from '../components/ui/notification/notify'
export default defineNuxtPlugin((nuxtApp) => {
notify._context = nuxtApp.vueApp._context
return {}
})

View File

@ -0,0 +1,7 @@
import { createVfm } from 'vue-final-modal'
export default defineNuxtPlugin((nuxtApp) => {
const vfm = createVfm() as any
nuxtApp.vueApp.use(vfm)
})

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

BIN
public/documents/policy.pdf Normal file

Binary file not shown.

Binary file not shown.

BIN
public/favicon-96x96.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

3
public/favicon.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

BIN
public/hero-desktop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

BIN
public/hero-mobile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 KiB

1
public/keller-logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="29px" height="29px" viewBox="0 0 75 30"><path fill="currentColor" d="M26.465 10.712H23.93l-4.174 9.176v-9.176h-2.317v18.465h2.317v-4.871l1.77-3.58l2.863 8.451h2.644L22.859 18.03zm3.125 0h-1.203v18.465h7.649V26.89h-5.332v-5.732h4.72V18.87h-4.72V13h5.332v-2.289zm10.599.001h-2.316v18.465h7.386V26.89h-5.07zm9.244 0h-2.338v18.465h7.408V26.89h-5.07zm8.108 0h-1.202v18.465h7.626V26.89h-5.31v-5.732h4.699V18.87h-4.699V13h5.31v-2.289zM75 29.177l-2.622-8.292c.022 0 .022-.023.043-.023a3.55 3.55 0 0 0 1.552-1.472c.35-.657.524-1.428.524-2.334v-2.152c0-.884-.174-1.631-.524-2.266a3.34 3.34 0 0 0-1.552-1.427c-.677-.317-1.486-.499-2.447-.499H65.8v18.465h2.316v-7.816h1.989l2.316 7.816zm-6.862-16.199h2.054c.416 0 .765.09 1.071.25c.306.158.525.407.678.747c.153.317.24.725.24 1.178v1.88c0 .43-.087.793-.24 1.11a1.6 1.6 0 0 1-.678.703c-.306.159-.656.25-1.07.25h-2.055zM0 .2v22.09L5.18.2zm3.65 23.155h4.72v-10.83h3.627v10.83h1.333V.2H8.916zm9.702 4.35h-1.355v1.472h1.355zm-4.982 0H0v1.472h8.37zM19.23.268h-1.813v7.295h1.814q.49 0 .852-.204c.24-.136.437-.317.546-.543q.197-.375.197-.884v-4.01c0-.521-.131-.929-.415-1.2c-.284-.295-.678-.454-1.18-.454m.372 5.528a.67.67 0 0 1-.131.43c-.087.114-.218.16-.372.16h-.502V1.468h.502c.154 0 .285.045.372.158a.67.67 0 0 1 .131.43zM25.087.268h-.524v7.295h3.125v-1.2h-1.923V4.55h1.682v-1.2h-1.682V1.49h1.923V.268zm9.201 3.738a2 2 0 0 0-.416-.34l-.459-.272c-.174-.09-.35-.181-.502-.272a1.8 1.8 0 0 1-.394-.294a.56.56 0 0 1-.153-.408v-.476c0-.158.044-.294.153-.385q.164-.136.394-.136c.175 0 .284.045.393.159a.67.67 0 0 1 .131.43v.408l1.202-.023V1.99c0-.567-.153-1.02-.459-1.315C33.872.358 33.458.2 32.911.2s-.984.158-1.29.475c-.305.318-.458.748-.458 1.315v.385c0 .294.043.543.13.77q.132.306.328.544a3.6 3.6 0 0 0 .438.385c.152.113.327.204.48.272c.175.09.328.158.46.249c.13.09.261.181.349.272a.6.6 0 0 1 .13.385v.634c0 .182-.043.318-.152.43c-.087.114-.24.16-.415.16s-.306-.046-.416-.16c-.109-.112-.152-.248-.152-.43v-.385H31.14v.385c0 .386.065.703.218.975c.131.271.35.475.612.611s.59.204.962.204c.546 0 .983-.158 1.289-.476s.459-.747.459-1.336v-.612c0-.295-.044-.544-.11-.725a1.2 1.2 0 0 0-.283-.521M39.532.268H38.33v7.295h1.202zm5.508 4.554h.59v.997c0 .18-.044.317-.154.407a.63.63 0 0 1-.415.159q-.262 0-.393-.136c-.11-.09-.153-.226-.153-.385V2.08c0-.18.044-.34.13-.453c.088-.113.22-.158.394-.158c.153 0 .262.045.35.158c.087.09.131.25.131.408v.566h1.224v-.566c0-.385-.066-.702-.197-.997a1.5 1.5 0 0 0-.59-.634a2 2 0 0 0-.918-.227c-.371 0-.677.068-.94.227a1.5 1.5 0 0 0-.59.634q-.196.408-.196 1.02v3.806c0 .385.065.702.218.951c.132.272.35.476.612.612c.263.136.59.204.962.204c.371 0 .677-.068.94-.226c.262-.136.458-.363.59-.635c.13-.272.196-.612.196-.997V3.598h-1.77v1.224zm7.954-.068L51.486.268h-.962v7.295h1.115V3.327l1.508 4.236h.961V.268h-1.114zM65.21 3.78c.328-.068.568-.182.765-.408q.261-.306.262-.816v-.77c0-.476-.131-.838-.393-1.11C65.58.404 65.21.29 64.729.29h-1.814v7.295h1.923c.481 0 .853-.136 1.093-.408c.262-.272.393-.634.393-1.133v-.997c0-.362-.087-.657-.284-.86a1.4 1.4 0 0 0-.83-.408m-.612-2.334c.131 0 .24.045.328.113c.087.09.109.204.109.34v.793a.7.7 0 0 1-.11.408a.44.44 0 0 1-.327.136h-.48v-1.79zm.524 4.486c0 .136-.043.272-.13.34c-.088.09-.198.113-.35.113h-.547V4.369h.525c.153 0 .284.045.371.158c.088.09.131.25.131.43zM71.984.268l-.612 3.036l-.677-3.036h-1.246l1.333 4.509v2.786h1.18V4.777l1.29-4.509z"></path></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

11
public/logo.svg Normal file
View File

@ -0,0 +1,11 @@
<svg width="124" height="22" viewBox="0 0 124 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21 14.5014H14.0014V21.5H21V14.5014Z" fill="#008574"/>
<path d="M6.99859 0.5C3.13319 0.5 0 3.6332 0 7.49861V14.4972C0 18.3626 3.13319 21.4958 6.99859 21.4958L13.9972 14.4972H6.99859V7.49861H13.9972V14.4972L20.9958 7.49861C20.9958 3.6332 17.8626 0.5 13.9972 0.5H6.99859Z" fill="#008574"/>
<path d="M34.3017 21.4747C33.1778 21.4747 32.1208 21.2559 31.1222 20.8182C30.1279 20.3805 29.2463 19.7745 28.4775 19.0044C27.7088 18.2342 27.1072 17.342 26.6643 16.3278C26.2214 15.3178 26 14.2446 26 13.1168C26 11.9889 26.2214 10.92 26.6643 9.91418C27.1072 8.91257 27.713 8.0246 28.4775 7.25025C29.2421 6.48011 30.1237 5.8741 31.1222 5.43642C32.1166 4.99874 33.1778 4.77991 34.3017 4.77991C35.4255 4.77991 36.5076 4.99874 37.502 5.43642C38.4964 5.8741 39.3779 6.48011 40.1467 7.25025C40.9112 8.0246 41.5129 8.90836 41.9474 9.91418C42.3819 10.9158 42.5992 11.9847 42.5992 13.1168C42.5992 14.2488 42.3777 15.343 41.9349 16.3531C41.492 17.3631 40.8862 18.251 40.1216 19.017C39.3529 19.7829 38.4713 20.3805 37.4769 20.8224C36.4826 21.2601 35.4214 21.4789 34.2975 21.4789L34.3017 21.4747ZM34.3017 18.5372C35.0286 18.5372 35.7138 18.3941 36.3531 18.108C36.9923 17.8218 37.5605 17.4262 38.0619 16.9254C38.559 16.4246 38.9559 15.8438 39.2484 15.1831C39.5409 14.5224 39.6871 13.8322 39.6871 13.1168C39.6871 12.4013 39.5409 11.6943 39.2484 11.0504C38.9559 10.4066 38.559 9.83422 38.0619 9.32921C37.5647 8.8284 36.9923 8.4286 36.3447 8.13402C35.6971 7.83943 35.0161 7.69213 34.3058 7.69213C33.5956 7.69213 32.9146 7.83943 32.267 8.13402C31.6194 8.4286 31.047 8.8284 30.5498 9.32921C30.0526 9.83001 29.6599 10.4024 29.3758 11.0504C29.0917 11.6943 28.9497 12.3845 28.9497 13.1168C28.9497 13.849 29.0917 14.5224 29.3758 15.1831C29.6599 15.8438 30.0526 16.4246 30.5498 16.9254C31.047 17.4262 31.6194 17.8218 32.267 18.108C32.9146 18.3941 33.5956 18.5372 34.3058 18.5372H34.3017ZM39.7331 21.2853L33.4243 14.2152H36.9087L43.1924 21.2853H39.7289H39.7331Z" fill="#021D54"/>
<path d="M48.8369 21.4999C47.9512 21.4999 47.2242 21.3063 46.656 20.915C46.0878 20.5236 45.6616 19.9849 45.3859 19.2905C45.1101 18.5961 44.9723 17.8049 44.9723 16.9127V10.7264H47.9136V16.9127C47.9136 17.3252 47.9679 17.6913 48.0807 18.0111C48.1893 18.331 48.3731 18.5751 48.628 18.7518C48.8787 18.9286 49.2129 19.0127 49.6224 19.0127C50.1906 19.0127 50.6627 18.8655 51.0345 18.5709C51.4064 18.2763 51.6821 17.8765 51.8659 17.363C52.0456 16.8538 52.1375 16.3025 52.1375 15.7134V10.7222H55.0788V21.2558H52.1375V19.7997C51.774 20.3258 51.3144 20.7382 50.763 21.0412C50.2073 21.3442 49.568 21.4957 48.8411 21.4957L48.8369 21.4999Z" fill="#021D54"/>
<path d="M62.4028 21.5C61.421 21.5 60.5394 21.2475 59.7581 20.7341C58.9769 20.2249 58.3585 19.5473 57.9073 18.7056C57.4561 17.8597 57.2305 16.9633 57.2305 16.008C57.2305 15.0527 57.4561 14.1353 57.9073 13.3104C58.3585 12.4814 58.9727 11.8038 59.7581 11.282C60.5394 10.7559 61.421 10.495 62.4028 10.495C63.1298 10.495 63.815 10.6339 64.4542 10.9116C65.0934 11.1894 65.6449 11.6144 66.1045 12.191V10.7559H69.0208V21.2643H66.1045V19.8293C65.8371 20.1954 65.5029 20.5026 65.1102 20.7509C64.7132 20.9992 64.2829 21.1844 63.8192 21.3106C63.3512 21.4369 62.8833 21.5 62.407 21.5H62.4028ZM63.2092 19.0381C63.7607 19.0381 64.2578 18.895 64.6924 18.6088C65.1269 18.3226 65.4695 17.9481 65.7243 17.4852C65.975 17.0223 66.1045 16.5299 66.1045 16.0038C66.1045 15.4778 65.9792 14.9643 65.7243 14.5098C65.4695 14.0553 65.1269 13.685 64.6924 13.3988C64.2578 13.1126 63.7648 12.9695 63.2092 12.9695C62.6535 12.9695 62.1605 13.1126 61.726 13.3988C61.2915 13.685 60.9489 14.0553 60.694 14.5098C60.4392 14.9643 60.3138 15.4609 60.3138 16.0038C60.3138 16.5467 60.4392 17.0223 60.694 17.4852C60.9447 17.9481 61.2915 18.3226 61.726 18.6088C62.1605 18.895 62.6535 19.0381 63.2092 19.0381Z" fill="#021D54"/>
<path d="M72.0038 21.26V10.7263H74.9451V12.2077C75.2919 11.6985 75.7431 11.2861 76.2988 10.9789C76.8503 10.6674 77.4854 10.5117 78.1956 10.5117C79.0981 10.5117 79.8334 10.7011 80.4016 11.0714C80.9698 11.446 81.396 11.9762 81.6801 12.658C81.9642 13.344 82.1062 14.1562 82.1062 15.0947V21.2558H79.1649V15.0947C79.1649 14.6823 79.1106 14.3161 78.9978 13.9963C78.885 13.6764 78.7053 13.4324 78.4505 13.2556C78.1956 13.0789 77.8656 12.9947 77.4561 12.9947C76.9172 12.9947 76.4618 13.142 76.0816 13.4366C75.7014 13.7312 75.4173 14.131 75.2292 14.6318C75.0412 15.1326 74.9451 15.6881 74.9451 16.2899V21.26H72.0038Z" fill="#021D54"/>
<path d="M86.397 21.2601V13.0916H83.9529V10.7264H86.397V6.69055H89.3132V10.7264H91.7574V13.0916H89.3132V21.2601H86.397Z" fill="#021D54"/>
<path d="M97.711 21.4999C96.8253 21.4999 96.0983 21.3063 95.5301 20.915C94.9619 20.5236 94.5357 19.9849 94.26 19.2905C93.9842 18.5961 93.8464 17.8049 93.8464 16.9127V10.7264H96.7877V16.9127C96.7877 17.3252 96.842 17.6913 96.9548 18.0111C97.0634 18.331 97.2473 18.5751 97.5021 18.7518C97.7528 18.9286 98.087 19.0127 98.4965 19.0127C99.0647 19.0127 99.5368 18.8655 99.9086 18.5709C100.28 18.2763 100.556 17.8765 100.74 17.363C100.92 16.8538 101.012 16.3025 101.012 15.7134V10.7222H103.953V21.2558H101.012V19.7997C100.648 20.3258 100.189 20.7382 99.6371 21.0412C99.0814 21.3442 98.4422 21.4957 97.7152 21.4957L97.711 21.4999Z" fill="#021D54"/>
<path d="M106.698 21.2349V10.7265H109.639V12.1363C109.986 11.6439 110.404 11.2441 110.897 10.9411C111.386 10.6381 111.95 10.4866 112.58 10.4866C113.291 10.4866 113.93 10.6381 114.49 10.9411C115.05 11.2441 115.505 11.7154 115.852 12.3509C116.295 11.7154 116.804 11.2441 117.381 10.9411C117.958 10.6381 118.643 10.4866 119.432 10.4866C120.318 10.4866 121.049 10.6886 121.626 11.0968C122.202 11.505 122.637 12.0605 122.929 12.7675C123.222 13.4745 123.368 14.2784 123.368 15.1663V21.2349H120.452V15.1663C120.452 14.7665 120.397 14.4004 120.285 14.0679C120.172 13.7355 119.996 13.4661 119.75 13.2683C119.503 13.0705 119.161 12.9695 118.718 12.9695C118.275 12.9695 117.891 13.0916 117.569 13.3399C117.243 13.5882 116.997 13.8996 116.821 14.2826C116.646 14.6655 116.562 15.0611 116.562 15.4778V21.2349H113.646V15.1663C113.646 14.7665 113.592 14.4004 113.479 14.0679C113.366 13.7355 113.19 13.4661 112.944 13.2683C112.697 13.0705 112.355 12.9695 111.912 12.9695C111.469 12.9695 111.056 13.1252 110.713 13.4367C110.374 13.7481 110.111 14.1311 109.932 14.5814C109.748 15.0359 109.652 15.4946 109.635 15.9533V21.2307H106.694L106.698 21.2349Z" fill="#021D54"/>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
public/price-bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
public/results/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

BIN
public/results/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

BIN
public/results/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
public/results/4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

BIN
public/reviews/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

BIN
public/reviews/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
public/reviews/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

BIN
public/reviews/4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 KiB

BIN
public/reviews/5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
public/reviews/6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

21
public/site.webmanifest Normal file
View File

@ -0,0 +1,21 @@
{
"name": "Quantum",
"short_name": "Quantum",
"icons": [
{
"src": "/web-app-manifest-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/web-app-manifest-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"theme_color": "#F9FAFF",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

18
styles/floating-vue.scss Normal file
View File

@ -0,0 +1,18 @@
@use 'mixins' as *;
@use 'variables/color' as *;
/* Tooltip */
.v-popper--theme-tooltip .v-popper__inner {
@include font(11px, 500, 14px);
text-align: left;
border-radius: 8px;
padding: 4px 8px;
background-color: $color-black;
color: $color-gray-200;
}
.v-popper--theme-tooltip .v-popper__arrow-outer {
border-color: $color-black;
}

60
styles/index.scss Normal file
View File

@ -0,0 +1,60 @@
@use 'floating-vue/dist/style.css';
@use '@splidejs/vue-splide/css/core';
@use 'mixins';
@use "normalize";
@use "utility";
@use "typography";
@use "floating-vue";
*,
*::before,
*::after {
box-sizing: border-box;
}
html, body {
scroll-behavior: smooth;
}
body {
@include font(16px, 400, 20px);
font-family: "Onest", sans-serif;
color: $color-black;
background-color: $color-white;
}
p, h1, h2, h3, h4, h5, h6 {
margin: 0;
}
a {
color: $color-green-500;
text-decoration: none;
transition: color $transition-duration $transition-easing;
outline: none;
&:focus,
&:hover {
color: $color-green-400;
}
&:active {
color: $color-green-500;
}
}
button {
outline: none;
}
.container {
max-width: 1440px;
margin: 0 auto;
padding-inline: 20px;
@include mobile {
padding-inline: 16px;
}
}

View File

@ -0,0 +1,11 @@
@mixin mobile {
@media (max-width: 480px) {
@content;
}
}
@mixin desktop {
@media (min-width: 481px) {
@content;
}
}

View File

@ -0,0 +1,48 @@
@use "sass:meta";
@use "sass:list";
@use "sass:string";
@mixin element-variant(
$namespace,
$variant: '',
$map: ()
) {
@if meta.type-of($map) == 'map' {
$withDarkVariant: false;
$variantPrefix: if(string.length($variant) > 0, '-' + $variant, '');
@at-root {
:root, .light-theme {
@each $property, $value in $map {
@if meta.type-of($value) == 'list' {
@if $withDarkVariant == false and list.length($value) > 1 {
$withDarkVariant: true;
}
$value: list.nth($value, 1);
}
--#{$namespace}#{$variantPrefix}-#{$property}: #{$value};
}
}
@if $withDarkVariant {
.dark-theme {
@each $property, $value in $map {
@if meta.type-of($value) == 'list' and list.length($value) > 1 {
--#{$namespace}#{$variantPrefix}-#{$property}: #{list.nth($value, 2)};
}
}
}
}
}
@if (string.length($variant) > 0) {
&-#{$variantPrefix} {
@each $property, $value in $map {
--#{$namespace}-#{$property}: var(--#{$namespace}#{$variantPrefix}-#{$property});
}
}
}
}
}

3
styles/mixins/index.scss Normal file
View File

@ -0,0 +1,3 @@
@forward "element-variant";
@forward "typography";
@forward "adaptive";

View File

@ -0,0 +1,17 @@
@mixin font($size, $weight, $lineHeight, $namespace: null, $onlyVars: false) {
@if ($namespace) {
@if ($onlyVars) {
--#{$namespace}-font-size: #{$size};
--#{$namespace}-font-weight: #{$weight};
--#{$namespace}-line-height: #{$lineHeight};
} @else {
font-size: var(--#{$namespace}-font-size, $size);
font-weight: var(--#{$namespace}-font-weight, $weight);
line-height: var(--#{$namespace}-line-height, $lineHeight);
}
} @else {
font-size: $size;
font-weight: $weight;
line-height: $lineHeight;
}
}

363
styles/normalize.scss vendored Normal file
View File

@ -0,0 +1,363 @@
/*! 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, 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: bold;
}
/**
* 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, 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, video {
border-style: none;
vertical-align: middle;
font-style: italic;
max-width: 100%;
max-height: 100%;
}
svg {
color: inherit;
vertical-align: middle;
}
/* 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;
}
ul {
margin: 0;
padding-inline-start: 20px;
}

22
styles/typography.scss Normal file
View File

@ -0,0 +1,22 @@
@use "./mixins" as *;
p, h1, h2, h3, h4, h5, h6 {
margin: 0;
}
//a {
// @include font(18px, 500, 23px);
//
// color: inherit;
// text-decoration: none;
// transition: color .2s ease-out;
// text-underline-offset: 2px;
//
// &:hover {
// color: #EE6A32;
// }
//
// &:active {
// color: #F57B47;
// }
//}

13
styles/utility.scss Normal file
View File

@ -0,0 +1,13 @@
@use './mixins' as *;
.desktop-only {
@include mobile {
display: none !important;
}
}
.mobile-only {
@include desktop {
display: none !important;
}
}

View File

@ -0,0 +1,12 @@
$color-gray-200: #F9FAFF;
$color-gray-300: #f6f7fb;
$color-gray-400: #e7eaf3;
$color-gray-500: #cfd5f0;
$color-gray-600: #8890b8;
$color-gray-700: #505A8A;
$color-green-400: #008574;
$color-green-500: #056e62;
$color-white: #FFFFFF;
$color-black: #021d54;

View File

@ -0,0 +1,2 @@
@forward "color";
@forward "transitions";

View File

@ -0,0 +1,2 @@
$transition-duration: .2s;
$transition-easing: ease-out;

4
tsconfig.json Normal file
View File

@ -0,0 +1,4 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
}

10721
yarn.lock Normal file

File diff suppressed because it is too large Load Diff