first commit - static hello page
This commit is contained in:
commit
d86f7f1952
16
assets/gitea.svg
Normal file
16
assets/gitea.svg
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<svg width="160" height="160" viewBox="0 0 160 160" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="160" height="160" rx="44" fill="url(#bg)"/>
|
||||||
|
<path d="M49 60h53c16 0 28 12 28 28s-12 28-28 28H73c-22 0-40-18-40-40 0-9 7-16 16-16z" fill="white"/>
|
||||||
|
<path d="M65 76h34c8 0 14 6 14 14s-6 14-14 14H78c-13 0-24-11-24-24 0-2 1-4 4-4h7z" fill="#5D3A1A" fill-opacity=".16"/>
|
||||||
|
<circle cx="67" cy="86" r="7" fill="#5D3A1A"/>
|
||||||
|
<circle cx="101" cy="86" r="7" fill="#5D3A1A"/>
|
||||||
|
<path d="M75 102c7 5 16 5 24 0" stroke="#5D3A1A" stroke-width="6" stroke-linecap="round"/>
|
||||||
|
<path d="M103 57l13-13M113 45h14v14" stroke="#FEF3C7" stroke-width="7" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M49 60c-5-13-4-23 3-30 9 5 15 13 18 24" fill="white"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="bg" x1="18" y1="14" x2="140" y2="150" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#A3E635"/>
|
||||||
|
<stop offset="1" stop-color="#65A30D"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 987 B |
14
assets/mail.svg
Normal file
14
assets/mail.svg
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg width="160" height="160" viewBox="0 0 160 160" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="160" height="160" rx="44" fill="url(#bg)"/>
|
||||||
|
<rect x="32" y="45" width="96" height="70" rx="20" fill="white"/>
|
||||||
|
<path d="M38 55l35.5 29.5a10 10 0 0013 0L122 55" stroke="#EA580C" stroke-width="9" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M45 108l26-25M115 108L89 83" stroke="#FDBA74" stroke-width="8" stroke-linecap="round"/>
|
||||||
|
<circle cx="119" cy="42" r="14" fill="#FDE68A"/>
|
||||||
|
<path d="M113 42h12M119 36v12" stroke="#EA580C" stroke-width="4" stroke-linecap="round"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="bg" x1="18" y1="14" x2="140" y2="150" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FB7185"/>
|
||||||
|
<stop offset="1" stop-color="#F97316"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 826 B |
17
assets/nextcloud.svg
Normal file
17
assets/nextcloud.svg
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<svg width="160" height="160" viewBox="0 0 160 160" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="160" height="160" rx="44" fill="url(#bg)"/>
|
||||||
|
<circle cx="80" cy="80" r="38" fill="white" fill-opacity=".18"/>
|
||||||
|
<circle cx="80" cy="80" r="23" fill="white"/>
|
||||||
|
<circle cx="43" cy="82" r="20" fill="white"/>
|
||||||
|
<circle cx="117" cy="82" r="20" fill="white"/>
|
||||||
|
<circle cx="43" cy="82" r="10" fill="#1687F2"/>
|
||||||
|
<circle cx="117" cy="82" r="10" fill="#1687F2"/>
|
||||||
|
<circle cx="80" cy="80" r="12" fill="#1687F2"/>
|
||||||
|
<path d="M37 116c22 16 64 21 90-4" stroke="white" stroke-opacity=".38" stroke-width="8" stroke-linecap="round"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="bg" x1="18" y1="14" x2="140" y2="150" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#38BDF8"/>
|
||||||
|
<stop offset="1" stop-color="#2563EB"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 860 B |
18
assets/test-net.svg
Normal file
18
assets/test-net.svg
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<svg width="160" height="160" viewBox="0 0 160 160" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="160" height="160" rx="44" fill="url(#bg)"/>
|
||||||
|
<path d="M42 96l38-42 38 42" stroke="white" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M42 96h76" stroke="white" stroke-opacity=".45" stroke-width="10" stroke-linecap="round"/>
|
||||||
|
<circle cx="42" cy="96" r="16" fill="white"/>
|
||||||
|
<circle cx="80" cy="54" r="16" fill="white"/>
|
||||||
|
<circle cx="118" cy="96" r="16" fill="white"/>
|
||||||
|
<circle cx="42" cy="96" r="7" fill="#0F766E"/>
|
||||||
|
<circle cx="80" cy="54" r="7" fill="#0F766E"/>
|
||||||
|
<circle cx="118" cy="96" r="7" fill="#0F766E"/>
|
||||||
|
<path d="M55 119c16 9 35 11 55 0" stroke="#A7F3D0" stroke-width="8" stroke-linecap="round"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="bg" x1="18" y1="14" x2="140" y2="150" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#34D399"/>
|
||||||
|
<stop offset="1" stop-color="#0F766E"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 984 B |
166
index.html
Normal file
166
index.html
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Стартовая страница для навигации по личной экосистеме Bratskih Tech."
|
||||||
|
/>
|
||||||
|
<title>Bratskih Tech Hub</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<link rel="stylesheet" href="styles.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="ambient ambient-one"></div>
|
||||||
|
<div class="ambient ambient-two"></div>
|
||||||
|
|
||||||
|
<main class="shell">
|
||||||
|
<section class="hero" aria-labelledby="page-title">
|
||||||
|
<div class="hero-content">
|
||||||
|
<p class="eyebrow">Personal ecosystem</p>
|
||||||
|
<h1 id="page-title">Bratskih Tech Hub</h1>
|
||||||
|
<p class="lead">
|
||||||
|
Быстрый старт для твоих сервисов: облако, почта, тестовая сеть и
|
||||||
|
репозитории в одном аккуратном месте.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-panel" aria-label="Настройки внешнего вида">
|
||||||
|
<div class="control-group">
|
||||||
|
<span>Тема</span>
|
||||||
|
<button class="segmented is-active" type="button" data-theme="dark">
|
||||||
|
Темная
|
||||||
|
</button>
|
||||||
|
<button class="segmented" type="button" data-theme="light">
|
||||||
|
Светлая
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<span>Вид</span>
|
||||||
|
<button class="segmented is-active" type="button" data-view="cards">
|
||||||
|
Карточки
|
||||||
|
</button>
|
||||||
|
<button class="segmented" type="button" data-view="list">
|
||||||
|
Список
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group accent-group">
|
||||||
|
<span>Акцент</span>
|
||||||
|
<button
|
||||||
|
class="accent-swatch is-active"
|
||||||
|
type="button"
|
||||||
|
data-accent="violet"
|
||||||
|
aria-label="Фиолетовый акцент"
|
||||||
|
></button>
|
||||||
|
<button
|
||||||
|
class="accent-swatch"
|
||||||
|
type="button"
|
||||||
|
data-accent="cyan"
|
||||||
|
aria-label="Голубой акцент"
|
||||||
|
></button>
|
||||||
|
<button
|
||||||
|
class="accent-swatch"
|
||||||
|
type="button"
|
||||||
|
data-accent="lime"
|
||||||
|
aria-label="Лаймовый акцент"
|
||||||
|
></button>
|
||||||
|
<button
|
||||||
|
class="accent-swatch"
|
||||||
|
type="button"
|
||||||
|
data-accent="rose"
|
||||||
|
aria-label="Розовый акцент"
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="services cards-view" aria-label="Сервисы экосистемы">
|
||||||
|
<a
|
||||||
|
class="service-card"
|
||||||
|
href="https://nextcloud.bratskih.tech"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<span class="shine"></span>
|
||||||
|
<img src="assets/nextcloud.svg" alt="" loading="lazy" />
|
||||||
|
<div>
|
||||||
|
<p class="service-kicker">Cloud storage</p>
|
||||||
|
<h2>Nextcloud</h2>
|
||||||
|
<p>
|
||||||
|
Личное облако для файлов, заметок, календарей и синхронизации
|
||||||
|
между устройствами.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<span class="arrow" aria-hidden="true">→</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
class="service-card"
|
||||||
|
href="https://mail.bratskih.tech"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<span class="shine"></span>
|
||||||
|
<img src="assets/mail.svg" alt="" loading="lazy" />
|
||||||
|
<div>
|
||||||
|
<p class="service-kicker">Mailbox</p>
|
||||||
|
<h2>Mail</h2>
|
||||||
|
<p>
|
||||||
|
Почтовый шлюз для рабочей переписки, уведомлений и управления
|
||||||
|
сообщениями.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<span class="arrow" aria-hidden="true">→</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
class="service-card"
|
||||||
|
href="https://test-net.bratskih.tech"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<span class="shine"></span>
|
||||||
|
<img src="assets/test-net.svg" alt="" loading="lazy" />
|
||||||
|
<div>
|
||||||
|
<p class="service-kicker">Lab network</p>
|
||||||
|
<h2>Test Net</h2>
|
||||||
|
<p>
|
||||||
|
Песочница для экспериментов, тестовых окружений и проверки новых
|
||||||
|
идей.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<span class="arrow" aria-hidden="true">→</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
class="service-card"
|
||||||
|
href="https://gitea.bratskih.tech"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<span class="shine"></span>
|
||||||
|
<img src="assets/gitea.svg" alt="" loading="lazy" />
|
||||||
|
<div>
|
||||||
|
<p class="service-kicker">Git forge</p>
|
||||||
|
<h2>Gitea</h2>
|
||||||
|
<p>
|
||||||
|
Домашняя кузница кода: репозитории, задачи, ревью и история
|
||||||
|
проектов.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<span class="arrow" aria-hidden="true">→</span>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
117
script.js
Normal file
117
script.js
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
const STORAGE_KEY = "bratskih-hub-preferences";
|
||||||
|
|
||||||
|
const defaultPreferences = {
|
||||||
|
theme: window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark",
|
||||||
|
view: "cards",
|
||||||
|
accent: "violet",
|
||||||
|
};
|
||||||
|
|
||||||
|
const preferences = {
|
||||||
|
...defaultPreferences,
|
||||||
|
...readPreferences(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const services = document.querySelector(".services");
|
||||||
|
const themeButtons = document.querySelectorAll("[data-theme]");
|
||||||
|
const viewButtons = document.querySelectorAll("[data-view]");
|
||||||
|
const accentButtons = document.querySelectorAll("[data-accent]");
|
||||||
|
const cards = document.querySelectorAll(".service-card");
|
||||||
|
|
||||||
|
applyPreferences(preferences);
|
||||||
|
|
||||||
|
themeButtons.forEach((button) => {
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
preferences.theme = button.dataset.theme;
|
||||||
|
applyPreferences(preferences);
|
||||||
|
savePreferences(preferences);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
viewButtons.forEach((button) => {
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
preferences.view = button.dataset.view;
|
||||||
|
applyPreferences(preferences);
|
||||||
|
savePreferences(preferences);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
accentButtons.forEach((button) => {
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
preferences.accent = button.dataset.accent;
|
||||||
|
applyPreferences(preferences);
|
||||||
|
savePreferences(preferences);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
cards.forEach((card) => {
|
||||||
|
let leaveTimer;
|
||||||
|
|
||||||
|
card.addEventListener("pointerenter", () => {
|
||||||
|
window.clearTimeout(leaveTimer);
|
||||||
|
card.classList.add("is-hovered");
|
||||||
|
});
|
||||||
|
|
||||||
|
card.addEventListener("pointermove", (event) => {
|
||||||
|
window.clearTimeout(leaveTimer);
|
||||||
|
card.classList.add("is-hovered");
|
||||||
|
|
||||||
|
const rect = card.getBoundingClientRect();
|
||||||
|
const x = event.clientX - rect.left;
|
||||||
|
const y = event.clientY - rect.top;
|
||||||
|
const xPercent = clamp((x / rect.width) * 100, 8, 92);
|
||||||
|
const yPercent = clamp((y / rect.height) * 100, 8, 92);
|
||||||
|
const rotateY = ((xPercent - 50) / 50) * 2.6;
|
||||||
|
const rotateX = ((50 - yPercent) / 50) * 2.6;
|
||||||
|
|
||||||
|
card.style.setProperty("--card-x", `${xPercent}%`);
|
||||||
|
card.style.setProperty("--card-y", `${yPercent}%`);
|
||||||
|
card.style.setProperty("--tilt-x", `${rotateX}deg`);
|
||||||
|
card.style.setProperty("--tilt-y", `${rotateY}deg`);
|
||||||
|
});
|
||||||
|
|
||||||
|
card.addEventListener("pointerleave", () => {
|
||||||
|
leaveTimer = window.setTimeout(() => {
|
||||||
|
card.classList.remove("is-hovered");
|
||||||
|
card.style.setProperty("--card-x", "50%");
|
||||||
|
card.style.setProperty("--card-y", "50%");
|
||||||
|
card.style.setProperty("--tilt-x", "0deg");
|
||||||
|
card.style.setProperty("--tilt-y", "0deg");
|
||||||
|
}, 80);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function applyPreferences(nextPreferences) {
|
||||||
|
document.documentElement.dataset.theme = nextPreferences.theme;
|
||||||
|
document.documentElement.dataset.accent = nextPreferences.accent;
|
||||||
|
|
||||||
|
services.classList.toggle("cards-view", nextPreferences.view === "cards");
|
||||||
|
services.classList.toggle("list-view", nextPreferences.view === "list");
|
||||||
|
|
||||||
|
setActiveButton(themeButtons, "theme", nextPreferences.theme);
|
||||||
|
setActiveButton(viewButtons, "view", nextPreferences.view);
|
||||||
|
setActiveButton(accentButtons, "accent", nextPreferences.accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setActiveButton(buttons, key, value) {
|
||||||
|
buttons.forEach((button) => {
|
||||||
|
const isActive = button.dataset[key] === value;
|
||||||
|
button.classList.toggle("is-active", isActive);
|
||||||
|
button.setAttribute("aria-pressed", String(isActive));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function readPreferences() {
|
||||||
|
try {
|
||||||
|
return JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function savePreferences(nextPreferences) {
|
||||||
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(nextPreferences));
|
||||||
|
}
|
||||||
|
|
||||||
|
function clamp(value, min, max) {
|
||||||
|
return Math.min(Math.max(value, min), max);
|
||||||
|
}
|
||||||
462
styles.css
Normal file
462
styles.css
Normal file
@ -0,0 +1,462 @@
|
|||||||
|
:root {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bg: #080a12;
|
||||||
|
--bg-soft: #111827;
|
||||||
|
--panel: rgba(15, 23, 42, 0.72);
|
||||||
|
--panel-strong: rgba(15, 23, 42, 0.9);
|
||||||
|
--text: #f8fafc;
|
||||||
|
--muted: #aeb8c8;
|
||||||
|
--border: rgba(255, 255, 255, 0.12);
|
||||||
|
--shadow: 0 24px 80px rgba(0, 0, 0, 0.34);
|
||||||
|
--accent: #8b5cf6;
|
||||||
|
--accent-2: #22d3ee;
|
||||||
|
--accent-rgb: 139, 92, 246;
|
||||||
|
--card-x: 50%;
|
||||||
|
--card-y: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] {
|
||||||
|
color-scheme: light;
|
||||||
|
--bg: #f5f7fb;
|
||||||
|
--bg-soft: #ffffff;
|
||||||
|
--panel: rgba(255, 255, 255, 0.76);
|
||||||
|
--panel-strong: rgba(255, 255, 255, 0.96);
|
||||||
|
--text: #111827;
|
||||||
|
--muted: #5f6b7a;
|
||||||
|
--border: rgba(15, 23, 42, 0.12);
|
||||||
|
--shadow: 0 24px 70px rgba(56, 65, 91, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-accent="cyan"] {
|
||||||
|
--accent: #06b6d4;
|
||||||
|
--accent-2: #3b82f6;
|
||||||
|
--accent-rgb: 6, 182, 212;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-accent="lime"] {
|
||||||
|
--accent: #84cc16;
|
||||||
|
--accent-2: #14b8a6;
|
||||||
|
--accent-rgb: 132, 204, 22;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-accent="rose"] {
|
||||||
|
--accent: #f43f5e;
|
||||||
|
--accent-2: #f97316;
|
||||||
|
--accent-rgb: 244, 63, 94;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at top left, rgba(var(--accent-rgb), 0.24), transparent 36rem),
|
||||||
|
radial-gradient(circle at bottom right, rgba(34, 211, 238, 0.18), transparent 32rem),
|
||||||
|
linear-gradient(135deg, var(--bg), var(--bg-soft));
|
||||||
|
color: var(--text);
|
||||||
|
font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
transition: background 220ms ease, color 220ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
a {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ambient {
|
||||||
|
position: fixed;
|
||||||
|
z-index: -1;
|
||||||
|
width: 28rem;
|
||||||
|
height: 28rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
filter: blur(18px);
|
||||||
|
opacity: 0.42;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ambient-one {
|
||||||
|
top: -10rem;
|
||||||
|
right: -7rem;
|
||||||
|
background: rgba(var(--accent-rgb), 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ambient-two {
|
||||||
|
bottom: -12rem;
|
||||||
|
left: -10rem;
|
||||||
|
background: rgba(34, 211, 238, 0.32);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell {
|
||||||
|
width: min(1180px, calc(100% - 32px));
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 48px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(320px, 430px);
|
||||||
|
gap: 28px;
|
||||||
|
align-items: end;
|
||||||
|
margin-bottom: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-content,
|
||||||
|
.control-panel,
|
||||||
|
.service-card {
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
background: var(--panel);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
backdrop-filter: blur(22px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-content {
|
||||||
|
min-height: 330px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: clamp(28px, 5vw, 56px);
|
||||||
|
border-radius: 38px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-content::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background:
|
||||||
|
linear-gradient(120deg, rgba(var(--accent-rgb), 0.28), transparent 34%),
|
||||||
|
url("data:image/svg+xml,%3Csvg width='140' height='140' viewBox='0 0 140 140' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='white' stroke-opacity='.09'%3E%3Cpath d='M0 70h140M70 0v140'/%3E%3Ccircle cx='70' cy='70' r='38'/%3E%3C/g%3E%3C/svg%3E");
|
||||||
|
mask-image: linear-gradient(to bottom, black, transparent 88%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-content > * {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow,
|
||||||
|
.service-kicker {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--accent);
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.14em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
max-width: 760px;
|
||||||
|
margin: 14px 0 18px;
|
||||||
|
font-size: clamp(2.8rem, 9vw, 6.8rem);
|
||||||
|
line-height: 0.88;
|
||||||
|
letter-spacing: -0.08em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead {
|
||||||
|
max-width: 660px;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: clamp(1rem, 2vw, 1.22rem);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-panel {
|
||||||
|
display: grid;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 18px;
|
||||||
|
border-radius: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
min-height: 48px;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 18px;
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-group span {
|
||||||
|
min-width: 68px;
|
||||||
|
padding-left: 6px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segmented,
|
||||||
|
.accent-swatch {
|
||||||
|
border: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 160ms ease, background 160ms ease, color 160ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segmented {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 34px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segmented:hover,
|
||||||
|
.accent-swatch:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.segmented.is-active {
|
||||||
|
background: linear-gradient(135deg, var(--accent), var(--accent-2));
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent-group {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent-swatch {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 999px;
|
||||||
|
box-shadow: inset 0 0 0 4px rgba(255, 255, 255, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent-swatch[data-accent="violet"] {
|
||||||
|
background: linear-gradient(135deg, #8b5cf6, #22d3ee);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent-swatch[data-accent="cyan"] {
|
||||||
|
background: linear-gradient(135deg, #06b6d4, #3b82f6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent-swatch[data-accent="lime"] {
|
||||||
|
background: linear-gradient(135deg, #84cc16, #14b8a6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent-swatch[data-accent="rose"] {
|
||||||
|
background: linear-gradient(135deg, #f43f5e, #f97316);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent-swatch.is-active {
|
||||||
|
border-color: var(--text);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.services {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
gap: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card {
|
||||||
|
--tilt-x: 0deg;
|
||||||
|
--tilt-y: 0deg;
|
||||||
|
position: relative;
|
||||||
|
min-height: 300px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 22px;
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 30px;
|
||||||
|
text-decoration: none;
|
||||||
|
overflow: hidden;
|
||||||
|
transform-origin: center;
|
||||||
|
transform: perspective(900px) rotateX(var(--tilt-x)) rotateY(var(--tilt-y));
|
||||||
|
will-change: transform;
|
||||||
|
transition:
|
||||||
|
transform 260ms cubic-bezier(0.22, 1, 0.36, 1),
|
||||||
|
border-color 220ms ease,
|
||||||
|
background 220ms ease,
|
||||||
|
box-shadow 220ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: inherit;
|
||||||
|
background: radial-gradient(
|
||||||
|
circle at var(--card-x) var(--card-y),
|
||||||
|
rgba(var(--accent-rgb), 0.24),
|
||||||
|
transparent 34%
|
||||||
|
);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 180ms ease;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card.is-hovered,
|
||||||
|
.service-card:focus-visible {
|
||||||
|
border-color: rgba(var(--accent-rgb), 0.52);
|
||||||
|
background: var(--panel-strong);
|
||||||
|
box-shadow: 0 28px 90px rgba(var(--accent-rgb), 0.18);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card.is-hovered::before,
|
||||||
|
.service-card:focus-visible::before {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card img {
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
object-fit: contain;
|
||||||
|
filter: drop-shadow(0 16px 28px rgba(0, 0, 0, 0.24));
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card h2 {
|
||||||
|
margin: 7px 0 10px;
|
||||||
|
font-size: 1.45rem;
|
||||||
|
letter-spacing: -0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card p:not(.service-kicker) {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--muted);
|
||||||
|
line-height: 1.62;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
position: absolute;
|
||||||
|
right: 22px;
|
||||||
|
bottom: 18px;
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(var(--accent-rgb), 0.14);
|
||||||
|
color: var(--accent);
|
||||||
|
font-size: 1.35rem;
|
||||||
|
transition: transform 180ms ease, background 180ms ease, color 180ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card.is-hovered .arrow,
|
||||||
|
.service-card:focus-visible .arrow {
|
||||||
|
transform: translateX(4px);
|
||||||
|
background: linear-gradient(135deg, var(--accent), var(--accent-2));
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shine {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: linear-gradient(
|
||||||
|
110deg,
|
||||||
|
transparent 0%,
|
||||||
|
rgba(255, 255, 255, 0.16) 42%,
|
||||||
|
transparent 64%
|
||||||
|
);
|
||||||
|
transform: translateX(-130%);
|
||||||
|
transition: transform 540ms ease;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card.is-hovered .shine,
|
||||||
|
.service-card:focus-visible .shine {
|
||||||
|
transform: translateX(130%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.services.list-view {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.services.list-view .service-card {
|
||||||
|
min-height: 132px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 78px minmax(0, 1fr) 52px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.services.list-view .arrow {
|
||||||
|
position: static;
|
||||||
|
justify-self: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1040px) {
|
||||||
|
.hero,
|
||||||
|
.services {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-content {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 680px) {
|
||||||
|
.shell {
|
||||||
|
width: min(100% - 22px, 1180px);
|
||||||
|
padding: 22px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero,
|
||||||
|
.services {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-content {
|
||||||
|
min-height: 300px;
|
||||||
|
border-radius: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-panel {
|
||||||
|
border-radius: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-group {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-group span {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent-group span {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.services.list-view .service-card {
|
||||||
|
grid-template-columns: 58px minmax(0, 1fr);
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.services.list-view .arrow {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card {
|
||||||
|
min-height: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card img {
|
||||||
|
width: 62px;
|
||||||
|
height: 62px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
scroll-behavior: auto !important;
|
||||||
|
transition-duration: 0.01ms !important;
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user