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