added new start page
This commit is contained in:
122
react_front/src/App.tsx
Normal file
122
react_front/src/App.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import { useState } from 'react'
|
||||
import reactLogo from './assets/react.svg'
|
||||
import viteLogo from './assets/vite.svg'
|
||||
import heroImg from './assets/hero.png'
|
||||
import './App.css'
|
||||
|
||||
function App() {
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
return (
|
||||
<>
|
||||
<section id="center">
|
||||
<div className="hero">
|
||||
<img src={heroImg} className="base" width="170" height="179" alt="" />
|
||||
<img src={reactLogo} className="framework" alt="React logo" />
|
||||
<img src={viteLogo} className="vite" alt="Vite logo" />
|
||||
</div>
|
||||
<div>
|
||||
<h1>Get started</h1>
|
||||
<p>
|
||||
Edit <code>src/App.tsx</code> and save to test <code>HMR</code>
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="counter"
|
||||
onClick={() => setCount((count) => count + 1)}
|
||||
>
|
||||
Count is {count}
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<div className="ticks"></div>
|
||||
|
||||
<section id="next-steps">
|
||||
<div id="docs">
|
||||
<svg className="icon" role="presentation" aria-hidden="true">
|
||||
<use href="/icons.svg#documentation-icon"></use>
|
||||
</svg>
|
||||
<h2>Documentation</h2>
|
||||
<p>Your questions, answered</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://vite.dev/" target="_blank">
|
||||
<img className="logo" src={viteLogo} alt="" />
|
||||
Explore Vite
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://react.dev/" target="_blank">
|
||||
<img className="button-icon" src={reactLogo} alt="" />
|
||||
Learn more
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="social">
|
||||
<svg className="icon" role="presentation" aria-hidden="true">
|
||||
<use href="/icons.svg#social-icon"></use>
|
||||
</svg>
|
||||
<h2>Connect with us</h2>
|
||||
<p>Join the Vite community</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/vitejs/vite" target="_blank">
|
||||
<svg
|
||||
className="button-icon"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use href="/icons.svg#github-icon"></use>
|
||||
</svg>
|
||||
GitHub
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://chat.vite.dev/" target="_blank">
|
||||
<svg
|
||||
className="button-icon"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use href="/icons.svg#discord-icon"></use>
|
||||
</svg>
|
||||
Discord
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://x.com/vite_js" target="_blank">
|
||||
<svg
|
||||
className="button-icon"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use href="/icons.svg#x-icon"></use>
|
||||
</svg>
|
||||
X.com
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://bsky.app/profile/vite.dev" target="_blank">
|
||||
<svg
|
||||
className="button-icon"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use href="/icons.svg#bluesky-icon"></use>
|
||||
</svg>
|
||||
Bluesky
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="ticks"></div>
|
||||
<section id="spacer"></section>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
13
src/App.js
13
src/App.js
@@ -1,13 +1,14 @@
|
||||
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
|
||||
import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
||||
import { AuthProvider } from './context/AuthContext';
|
||||
import { ThemeProvider } from './context/ThemeContext';
|
||||
import { useAuthContext } from './context/AuthContext';
|
||||
import { useUnreadSocket } from './hooks/useUnreadSocket';
|
||||
import LandingPage from './pages/NakhodkaLanding';
|
||||
import RegisterPage from './components/Authentication';
|
||||
import MainFeed from './components/MainFeed';
|
||||
import ChatPage from './components/ChatPage';
|
||||
import ChatListPage from './components/ChatListPage';
|
||||
import ReviewsPage from './components/ReviewsPage';
|
||||
import MainFeed from './pages/MainFeed';
|
||||
import ChatPage from './pages/ChatPage';
|
||||
import ChatListPage from './pages/ChatListPage';
|
||||
import ReviewsPage from './pages/ReviewsPage';
|
||||
import './styles/chat.css';
|
||||
|
||||
function UnreadListener() {
|
||||
@@ -23,7 +24,7 @@ function App() {
|
||||
<UnreadListener />
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<Navigate to="/feed" replace />} />
|
||||
<Route path="/" element={<LandingPage />} />
|
||||
|
||||
<Route path="/register" element={<RegisterPage />} />
|
||||
<Route path="/feed" element={<MainFeed />} />
|
||||
|
||||
10
src/components/landing/FinalCta.jsx
Normal file
10
src/components/landing/FinalCta.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
export default function FinalCta({ onClick }) {
|
||||
return (
|
||||
<section className="nk-final-cta">
|
||||
<h2 className="nk-h2">Кто-то прямо сейчас ищет то, что вы нашли</h2>
|
||||
<button className="nk-cta-primary" onClick={onClick}>
|
||||
Открыть Находку
|
||||
</button>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
7
src/components/landing/Footer.jsx
Normal file
7
src/components/landing/Footer.jsx
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className="nk-footer">
|
||||
© {new Date().getFullYear()} Находка. Возвращаем найденное.
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
50
src/components/landing/Hero.jsx
Normal file
50
src/components/landing/Hero.jsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { useState } from "react";
|
||||
import Navbar from "./Navbar";
|
||||
import RadarBeacon from "./RadarBeacon";
|
||||
|
||||
export default function Hero({ onCtaClick, onLoginClick }) {
|
||||
const [hover, setHover] = useState(false);
|
||||
|
||||
return (
|
||||
<section className="nk-hero">
|
||||
<div className="nk-grid" />
|
||||
<div className="nk-glow" />
|
||||
|
||||
<RadarBeacon />
|
||||
<Navbar onLoginClick={onLoginClick || onCtaClick} />
|
||||
|
||||
<div className="nk-hero-text">
|
||||
<span className="nk-eyebrow">Сервис поиска пропавших питомцев и вещей</span>
|
||||
<h1 className="nk-h1">
|
||||
Потерял — не значит
|
||||
<br />
|
||||
<span className="nk-accent">потерял навсегда</span>
|
||||
</h1>
|
||||
<p className="nk-lead">
|
||||
Разместите объявление о пропаже или находке — Находка сама сопоставит
|
||||
похожие записи по фото, месту и описанию и покажет совпадения рядом с вами.
|
||||
</p>
|
||||
<div className="nk-cta-row">
|
||||
<button
|
||||
className="nk-cta-primary"
|
||||
onMouseEnter={() => setHover(true)}
|
||||
onMouseLeave={() => setHover(false)}
|
||||
onClick={onCtaClick}
|
||||
>
|
||||
Начать поиск
|
||||
<span
|
||||
style={{
|
||||
marginLeft: 8,
|
||||
display: "inline-block",
|
||||
transform: hover ? "translateX(3px)" : "none",
|
||||
transition: "transform .2s",
|
||||
}}
|
||||
>
|
||||
→
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
37
src/components/landing/Icon.jsx
Normal file
37
src/components/landing/Icon.jsx
Normal file
@@ -0,0 +1,37 @@
|
||||
export default function Icon({ type }) {
|
||||
if (type === "paw") {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
|
||||
<circle cx="7" cy="7" r="2.1" />
|
||||
<circle cx="12.3" cy="5.2" r="2.1" />
|
||||
<circle cx="17.2" cy="7.4" r="2.1" />
|
||||
<path d="M12 10.5c-3.4 0-6.2 2.4-6.2 5.4 0 1.7 1.4 2.9 3 2.5 1.2-.3 2-1 3.2-1s2 .7 3.2 1c1.6.4 3-.8 3-2.5 0-3-2.8-5.4-6.2-5.4z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
if (type === "bag") {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M4 10h16l-1.4 10.5a2 2 0 0 1-2 1.5H7.4a2 2 0 0 1-2-1.5L4 10z" />
|
||||
<path d="M8 10V6a4 4 0 0 1 8 0v4" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
if (type === "key") {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" strokeWidth="1.8">
|
||||
<circle cx="8" cy="8" r="4.2" />
|
||||
<path d="M11 11l9 9M16.5 16.5l2.5-2.5M19 19l2-2" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" strokeWidth="1.8">
|
||||
<rect x="7" y="2.5" width="10" height="19" rx="2.2" />
|
||||
<line x1="11" y1="18.5" x2="13" y2="18.5" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
10
src/components/landing/Navbar.jsx
Normal file
10
src/components/landing/Navbar.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
export default function Navbar({ onLoginClick }) {
|
||||
return (
|
||||
<nav className="nk-nav">
|
||||
<span className="nk-logo">Находка</span>
|
||||
<button className="nk-nav-btn" onClick={onLoginClick}>
|
||||
Войти
|
||||
</button>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
27
src/components/landing/RadarBeacon.jsx
Normal file
27
src/components/landing/RadarBeacon.jsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import Icon from "./Icon";
|
||||
import { markers } from "../../config/landingConstants";
|
||||
|
||||
export default function RadarBeacon() {
|
||||
return (
|
||||
<div className="nk-radar">
|
||||
<span className="nk-ring" style={{ animationDelay: "0s" }} />
|
||||
<span className="nk-ring" style={{ animationDelay: "1.3s" }} />
|
||||
<span className="nk-ring" style={{ animationDelay: "2.6s" }} />
|
||||
<div className="nk-core" />
|
||||
|
||||
{markers.map((m) => (
|
||||
<div
|
||||
key={m.id}
|
||||
className="nk-marker"
|
||||
style={{
|
||||
left: `${m.x}%`,
|
||||
top: `${m.y}%`,
|
||||
animationDelay: m.delay,
|
||||
}}
|
||||
>
|
||||
<Icon type={m.icon} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
18
src/components/landing/StepsSection.jsx
Normal file
18
src/components/landing/StepsSection.jsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { steps } from "../../config/landingConstants";
|
||||
|
||||
export default function StepsSection() {
|
||||
return (
|
||||
<section className="nk-steps-section">
|
||||
<h2 className="nk-h2">Как это работает</h2>
|
||||
<div className="nk-steps-grid">
|
||||
{steps.map((s) => (
|
||||
<div className="nk-step-card" key={s.n}>
|
||||
<span className="nk-step-num">{s.n}</span>
|
||||
<h3 className="nk-step-title">{s.title}</h3>
|
||||
<p className="nk-step-text">{s.text}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
32
src/config/landingConstants.js
Normal file
32
src/config/landingConstants.js
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
|
||||
export const markers = [
|
||||
{ id: "paw", x: 14, y: 18, delay: "0.4s", icon: "paw" },
|
||||
{ id: "key", x: 72, y: 22, delay: "1.1s", icon: "key" },
|
||||
{ id: "phone", x: 60, y: 62, delay: "1.8s", icon: "phone" },
|
||||
{ id: "bag", x: 26, y: 70, delay: "2.4s", icon: "bag" },
|
||||
];
|
||||
|
||||
export const steps = [
|
||||
{
|
||||
n: "01",
|
||||
title: "Разместите объявление",
|
||||
text: "Опишите пропажу или находку: фото, место, приметы. Это занимает меньше минуты.",
|
||||
},
|
||||
{
|
||||
n: "02",
|
||||
title: "Система ищет совпадения",
|
||||
text: "Находка сама сравнивает похожие объявления по фото, месту и описанию — по всему городу.",
|
||||
},
|
||||
{
|
||||
n: "03",
|
||||
title: "Свяжитесь и заберите",
|
||||
text: "Как только есть совпадение — пишите в чат прямо в приложении и договоритесь о встрече.",
|
||||
},
|
||||
];
|
||||
|
||||
export const stats = [
|
||||
{ value: "12 480", label: "находок опубликовано" },
|
||||
{ value: "9 200+", label: "воссоединений с хозяевами" },
|
||||
{ value: "24/7", label: "поиск работает без перерыва" },
|
||||
];
|
||||
@@ -16,5 +16,4 @@ html, body, #root {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import { useUnread } from "../context/UnreadContext";
|
||||
import { getPostForChat } from "../stores/chatPostStore";
|
||||
import { API_URL } from "../config/authConstants";
|
||||
import { useDeleteChat } from "../hooks/useDeleteChat";
|
||||
import TrashIcon from "./icons/TrashIcon";
|
||||
import TrashIcon from "../components/icons/TrashIcon";
|
||||
|
||||
export default function ChatListPage() {
|
||||
const navigate = useNavigate();
|
||||
@@ -1,17 +1,17 @@
|
||||
import { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import "../styles/index.css";
|
||||
import { API_URL } from "../config/authConstants";
|
||||
import Sidebar from "./Sidebar";
|
||||
import RightSidebar from "./RightSidebar";
|
||||
import PostOnFeed from "./PostOnFeed";
|
||||
import CreateUpdatePost from "./CreateUpdatePost";
|
||||
import ImageViewer from "./ImageViewer";
|
||||
import MenuIcon from "./icons/MenuIcon";
|
||||
import ProfileAvatar from "./profile/ProfileAvatar";
|
||||
import { ProfileModal } from "./profile";
|
||||
import AuthModal from "./AuthModal";
|
||||
import RulesModal from "./RulesModal";
|
||||
import Sidebar from "../components/Sidebar";
|
||||
import RightSidebar from "../components/RightSidebar";
|
||||
import PostOnFeed from "../components/PostOnFeed";
|
||||
import CreateUpdatePost from "../components/CreateUpdatePost";
|
||||
import ImageViewer from "../components/ImageViewer";
|
||||
import MenuIcon from "../components/icons/MenuIcon";
|
||||
import ProfileAvatar from "../components/profile/ProfileAvatar";
|
||||
import { ProfileModal } from "../components/profile";
|
||||
import AuthModal from "../components/AuthModal";
|
||||
import RulesModal from "../components/RulesModal";
|
||||
import { useAuthContext } from "../context/AuthContext";
|
||||
import { useDeletePost } from "../hooks/useDeletePost";
|
||||
import { useFavorites } from "../hooks/useFavorites";
|
||||
@@ -24,6 +24,7 @@ import { usePostStatus } from "../hooks/usePostStatus";
|
||||
*/
|
||||
export default function MainFeed() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { user, token, isAuthenticated } = useAuthContext();
|
||||
const { deletePost } = useDeletePost();
|
||||
const { toggleFavorite, loadFavorites } = useFavorites();
|
||||
@@ -139,6 +140,14 @@ export default function MainFeed() {
|
||||
}
|
||||
}, [isAuthenticated, token, loadFavorites]);
|
||||
|
||||
// Открываем модалку аутентификации если пришли с лендинга
|
||||
useEffect(() => {
|
||||
if (location.state?.openAuth) {
|
||||
setAuthModalOpen(true);
|
||||
window.history.replaceState({}, document.title);
|
||||
}
|
||||
}, [location.state]);
|
||||
|
||||
// Первичная загрузка при монтировании компонента
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
22
src/pages/NakhodkaLanding.jsx
Normal file
22
src/pages/NakhodkaLanding.jsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import "../styles/NakhodkaLanding.css";
|
||||
import Hero from "../components/landing/Hero";
|
||||
import StepsSection from "../components/landing/StepsSection";
|
||||
import FinalCta from "../components/landing/FinalCta";
|
||||
import Footer from "../components/landing/Footer";
|
||||
|
||||
export default function NakhodkaLanding() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const goToFeed = () => navigate("/feed");
|
||||
const goToFeedWithAuth = () => navigate("/feed", { state: { openAuth: true } });
|
||||
|
||||
return (
|
||||
<div className="nk-page">
|
||||
<Hero onCtaClick={goToFeed} onLoginClick={goToFeedWithAuth} />
|
||||
<StepsSection />
|
||||
<FinalCta onClick={goToFeed} />
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,11 +2,11 @@ import { useState, useCallback, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { API_URL } from "../config/authConstants";
|
||||
import { useAuthContext } from "../context/AuthContext";
|
||||
import MenuIcon from "./icons/MenuIcon";
|
||||
import Sidebar from "./Sidebar";
|
||||
import ProfileAvatar from "./profile/ProfileAvatar";
|
||||
import { ProfileModal } from "./profile";
|
||||
import AuthModal from "./AuthModal";
|
||||
import MenuIcon from "../components/icons/MenuIcon";
|
||||
import Sidebar from "../components/Sidebar";
|
||||
import ProfileAvatar from "../components/profile/ProfileAvatar";
|
||||
import { ProfileModal } from "../components/profile";
|
||||
import AuthModal from "../components/AuthModal";
|
||||
import "../styles/reviews.css";
|
||||
|
||||
/* ── Star rating sub-component ── */
|
||||
337
src/styles/NakhodkaLanding.css
Normal file
337
src/styles/NakhodkaLanding.css
Normal file
@@ -0,0 +1,337 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,600;9..144,700&family=Inter:wght@400;500;600;700&display=swap');
|
||||
|
||||
.nk-page {
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: #0A1F1E;
|
||||
color: #F4F7F6;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
@keyframes beaconPulse {
|
||||
0% {
|
||||
transform: scale(0.6);
|
||||
opacity: 0.55;
|
||||
}
|
||||
70% {
|
||||
transform: scale(1.9);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.9);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes markerIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(6px) scale(0.7);
|
||||
}
|
||||
15% {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
85% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes drift {
|
||||
0% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
100% {
|
||||
background-position: 60px 60px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes riseIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(16px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.nk-hero {
|
||||
position: relative;
|
||||
padding: 28px 6vw 0;
|
||||
min-height: 620px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nk-grid {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: radial-gradient(rgba(244, 247, 246, 0.10) 1px, transparent 1px);
|
||||
background-size: 26px 26px;
|
||||
animation: drift 14s linear infinite;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.nk-glow {
|
||||
position: absolute;
|
||||
top: -20%;
|
||||
right: -10%;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
background: radial-gradient(circle, rgba(245, 166, 35, 0.18) 0%, transparent 70%);
|
||||
filter: blur(10px);
|
||||
}
|
||||
|
||||
.nk-nav {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nk-logo {
|
||||
font-family: 'Fraunces', serif;
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.nk-nav-btn {
|
||||
background: rgba(244, 247, 246, 0.08);
|
||||
border: 1px solid rgba(244, 247, 246, 0.16);
|
||||
color: #F4F7F6;
|
||||
padding: 9px 20px;
|
||||
border-radius: 999px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
.nk-radar {
|
||||
position: absolute;
|
||||
right: 4vw;
|
||||
top: 90px;
|
||||
width: 340px;
|
||||
height: 340px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.nk-ring {
|
||||
position: absolute;
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(245, 166, 35, 0.55);
|
||||
animation: beaconPulse 3.9s ease-out infinite;
|
||||
will-change: transform;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.nk-core {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background: #F5A623;
|
||||
box-shadow: 0 0 22px 6px rgba(245, 166, 35, 0.55);
|
||||
}
|
||||
|
||||
.nk-marker {
|
||||
position: absolute;
|
||||
color: #0A1F1E;
|
||||
background: #F5A623;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
animation: markerIn 3.2s ease-in-out infinite;
|
||||
box-shadow: 0 4px 14px rgba(245, 166, 35, 0.4);
|
||||
will-change: transform;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.nk-hero-text {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
max-width: 620px;
|
||||
margin-top: 90px;
|
||||
animation: riseIn 0.8s ease both;
|
||||
}
|
||||
|
||||
.nk-eyebrow {
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
color: #8FADA8;
|
||||
letter-spacing: 0.04em;
|
||||
margin-bottom: 18px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.nk-h1 {
|
||||
font-family: 'Fraunces', serif;
|
||||
font-weight: 600;
|
||||
font-size: clamp(34px, 5.2vw, 58px);
|
||||
line-height: 1.08;
|
||||
margin: 0 0 22px;
|
||||
}
|
||||
|
||||
.nk-accent {
|
||||
color: #F5A623;
|
||||
}
|
||||
|
||||
.nk-lead {
|
||||
font-size: 17px;
|
||||
line-height: 1.6;
|
||||
color: #C7DAD6;
|
||||
max-width: 500px;
|
||||
margin-bottom: 34px;
|
||||
}
|
||||
|
||||
.nk-cta-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 18px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.nk-cta-primary {
|
||||
background: #F5A623;
|
||||
color: #0A1F1E;
|
||||
border: none;
|
||||
padding: 15px 30px;
|
||||
border-radius: 999px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.nk-cta-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 12px 28px rgba(245, 166, 35, 0.35);
|
||||
}
|
||||
|
||||
.nk-cta-hint {
|
||||
font-size: 13px;
|
||||
color: #7FA39D;
|
||||
}
|
||||
|
||||
|
||||
.nk-steps-section {
|
||||
padding: 0 6vw 60px;
|
||||
}
|
||||
|
||||
.nk-h2 {
|
||||
font-family: 'Fraunces', serif;
|
||||
font-size: clamp(24px, 3vw, 34px);
|
||||
font-weight: 600;
|
||||
margin-bottom: 42px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nk-steps-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 22px;
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.nk-step-card {
|
||||
background: rgba(244, 247, 246, 0.04);
|
||||
border: 1px solid rgba(244, 247, 246, 0.10);
|
||||
border-radius: 20px;
|
||||
padding: 26px 24px;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.nk-step-card:hover {
|
||||
border-color: rgba(244, 247, 246, 0.22);
|
||||
}
|
||||
|
||||
.nk-step-num {
|
||||
font-family: 'Fraunces', serif;
|
||||
font-size: 15px;
|
||||
color: #F5A623;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.nk-step-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin: 12px 0 8px;
|
||||
}
|
||||
|
||||
.nk-step-text {
|
||||
font-size: 14.5px;
|
||||
line-height: 1.55;
|
||||
color: #9FBAB5;
|
||||
}
|
||||
|
||||
.nk-final-cta {
|
||||
text-align: center;
|
||||
padding: 70px 6vw 100px;
|
||||
max-width: 700px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.nk-footer {
|
||||
text-align: center;
|
||||
padding: 24px 0 34px;
|
||||
font-size: 13px;
|
||||
color: #5F827D;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.nk-hero {
|
||||
padding-bottom: 150px;
|
||||
}
|
||||
|
||||
.nk-radar {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
right: 10vw;
|
||||
top: auto;
|
||||
bottom: 100px;
|
||||
}
|
||||
|
||||
.nk-ring {
|
||||
width: 85px;
|
||||
height: 85px;
|
||||
}
|
||||
|
||||
.nk-core {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.nk-marker {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.nk-marker svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.nk-ring, .nk-marker {
|
||||
animation-duration: 8s !important;
|
||||
}
|
||||
|
||||
.nk-grid {
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user