Загрузка данных
/* Базовые настройки и переменные */
:root {
--bg-color: #121212;
--text-color: #e0e0e0;
--accent-color: #8a2be2; /* Тот самый фиолетовый для "фиолетового чуда" */
--card-bg: #1e1e1e;
--nav-bg: rgba(18, 18, 18, 0.9);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
html {
scroll-behavior: smooth; /* Плавная прокрутка по якорям */
}
body {
background-color: var(--bg-color);
color: var(--text-color);
line-height: 1.6;
}
/* Шапка и навигация */
header {
background-color: var(--nav-bg);
backdrop-filter: blur(10px);
position: fixed;
top: 0;
width: 100%;
z-index: 1000;
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 50px;
box-shadow: 0 2px 10px rgba(0,0,0,0.5);
}
.logo p {
font-size: 1.5rem;
font-weight: bold;
color: var(--accent-color);
}
.logo img {
border-radius: 50%;
margin-top: 5px;
}
nav ul {
list-style: none;
display: flex;
gap: 20px;
}
nav a {
color: var(--text-color);
text-decoration: none;
font-weight: 500;
transition: color 0.3s ease;
}
nav a:hover {
color: var(--accent-color);
}
/* Основные секции */
section {
padding: 100px 20px 60px;
max-width: 1200px;
margin: 0 auto;
text-align: center;
}
h1, h2, h3, h4 {
margin-bottom: 20px;
color: #fff;
}
hr {
border: none;
height: 1px;
background-color: #333;
max-width: 800px;
margin: 0 auto;
}
/* Секция Hero */
#hero img {
border-radius: 15px;
margin-top: 20px;
box-shadow: 0 10px 20px rgba(0,0,0,0.5);
transition: transform 0.3s ease;
}
#hero img:hover {
transform: scale(1.02);
}
/* Карточки Навыков и Портфолио */
.skills-container, .portfolio-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin-top: 40px;
}
.skill-card, .portfolio-item, .lab-item {
background-color: var(--card-bg);
padding: 25px;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
transition: transform 0.3s ease, box-shadow 0.3s ease;
text-align: left;
}
.skill-card:hover, .portfolio-item:hover, .lab-item:hover {
transform: translateY(-10px); /* Карточка приподнимается */
box-shadow: 0 10px 25px rgba(138, 43, 226, 0.2); /* Фиолетовое свечение */
}
.skill-card img {
border-radius: 10px;
margin-top: 15px;
width: 100%;
height: auto;
}
/* Кнопки ссылок в портфолио */
.portfolio-item a, .lab-item a {
display: inline-block;
margin-top: 15px;
padding: 10px 20px;
background-color: var(--accent-color);
color: #fff;
text-decoration: none;
border-radius: 5px;
transition: background-color 0.3s ease;
}
.portfolio-item a:hover, .lab-item a:hover {
background-color: #6a1b9a;
}
/* Подвал */
footer {
text-align: center;
padding: 40px 20px;
background-color: #0a0a0a;
margin-top: 50px;
}
.social-links ul {
list-style: none;
display: flex;
justify-content: center;
gap: 15px;
margin-top: 15px;
}
.social-links a {
color: var(--text-color);
text-decoration: none;
transition: color 0.3s ease;
}
.social-links a:hover {
color: var(--accent-color);
}
/* Базовые классы для анимаций из JS */
.hidden {
opacity: 0;
transform: translateY(50px);
transition: all 0.8s ease-out;
}
.show {
opacity: 1;
transform: translateY(0);
}
document.addEventListener("DOMContentLoaded", () => {
// 1. Находим все элементы, которые хотим анимировать
const animatedElements = document.querySelectorAll(
'section h1, section h2, section p, .skill-card, .portfolio-item, .lab-item, #hero img'
);
// 2. Сначала скрываем их все, добавляя класс 'hidden'
animatedElements.forEach((el) => {
el.classList.add('hidden');
});
// 3. Настраиваем "наблюдателя"
const observerOptions = {
root: null,
rootMargin: '0px',
threshold: 0.15 // Элемент начнет появляться, когда на экране будет видно 15% его площади
};
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// Если элемент попал в зону видимости, добавляем класс 'show'
entry.target.classList.add('show');
// Прекращаем наблюдать за ним, чтобы анимация сработала один раз
observer.unobserve(entry.target);
}
});
}, observerOptions);
// 4. Запускаем наблюдение за каждым элементом
animatedElements.forEach((el) => {
observer.observe(el);
});
});