diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 0000000..f1986eb --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,303 @@ +:root { + --bg: #f5f6fa; + --surface: #ffffff; + --text: #1c1e26; + --text-muted: #6b7280; + --accent: #4f5bd5; + --accent-2: #7c3aed; + --border: #e7e9f0; + --shadow: 0 10px 30px rgba(24, 28, 47, 0.06); + --radius: 16px; +} + +@media (prefers-color-scheme: dark) { + :root { + --bg: #0f1117; + --surface: #171923; + --text: #f1f2f6; + --text-muted: #9ca3af; + --accent: #818cf8; + --accent-2: #a78bfa; + --border: #262a38; + --shadow: 0 10px 30px rgba(0, 0, 0, 0.35); + } +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + background: var(--bg); + color: var(--text); + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + line-height: 1.55; + -webkit-font-smoothing: antialiased; +} + +.container { + max-width: 860px; + margin: 0 auto; + padding: 0 20px 80px; +} + +/* Hero */ +.hero { + background: linear-gradient(135deg, var(--accent), var(--accent-2)); + padding: 64px 20px 100px; + color: #fff; +} + +.hero-inner { + max-width: 860px; + margin: 0 auto; + display: flex; + align-items: center; + gap: 32px; + flex-wrap: wrap; +} + +.avatar { + width: 140px; + height: 140px; + border-radius: 50%; + object-fit: cover; + border: 4px solid rgba(255, 255, 255, 0.6); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25); + flex-shrink: 0; +} + +.hero-text { + flex: 1; + min-width: 240px; +} + +.hero-text h1 { + margin: 0 0 4px; + font-size: 2.1rem; + font-weight: 700; +} + +.role { + font-size: 1.1rem; + font-weight: 500; + opacity: 0.92; + margin: 0 0 10px; +} + +.meta { + font-size: 0.92rem; + opacity: 0.85; + margin: 0 0 18px; +} + +.about { + max-width: 560px; + font-size: 0.98rem; + opacity: 0.95; + margin: 0 0 18px; +} + +.contacts { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.contact-icon { + width: 42px; + height: 42px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + background: rgba(255, 255, 255, 0.18); + color: #fff; + text-decoration: none; + font-size: 1.05rem; + transition: background 0.15s ease, transform 0.15s ease; +} + +.contact-icon:hover { + background: rgba(255, 255, 255, 0.32); + transform: translateY(-2px); + color: #fff; +} + +/* Content cards */ +.card { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + box-shadow: var(--shadow); + padding: 28px 30px; + margin-top: -48px; + position: relative; + z-index: 2; +} + +.card + .card { + margin-top: 24px; +} + +.card h2 { + font-size: 1.25rem; + margin: 0 0 20px; + display: flex; + align-items: center; + gap: 10px; +} + +.card h2 i { + color: var(--accent); + font-size: 1.05rem; +} + +/* Skills */ +.skills { + display: flex; + flex-direction: column; + gap: 16px; +} + +.skill-group:last-child { + margin-bottom: 0; +} + +.skill-category { + font-size: 0.8rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.03em; + color: var(--text-muted); + margin-bottom: 8px; +} + +.skill-tags { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.skill-tag { + background: color-mix(in srgb, var(--accent) 12%, transparent); + color: var(--accent); + border-radius: 999px; + padding: 6px 14px; + font-size: 0.85rem; + font-weight: 500; +} + +/* Timeline */ +.timeline { + display: flex; + flex-direction: column; + gap: 4px; +} + +.timeline-item { + display: grid; + grid-template-columns: 170px 1fr; + gap: 16px; + padding: 14px 0; + border-bottom: 1px solid var(--border); +} + +.timeline-item:last-child { + border-bottom: none; +} + +.timeline-period { + color: var(--text-muted); + font-size: 0.85rem; + font-weight: 600; + padding-top: 2px; +} + +.timeline-title { + font-weight: 600; + font-size: 0.98rem; +} + +.timeline-place { + color: var(--text-muted); + font-size: 0.9rem; + margin-top: 2px; +} + +.timeline-item.is-current .timeline-period { + color: var(--accent); +} + +/* Projects */ +.projects { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 16px; +} + +.project-card { + display: block; + background: var(--bg); + border: 1px solid var(--border); + border-radius: 12px; + padding: 18px 20px; + text-decoration: none; + color: var(--text); + transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease; +} + +.project-card:hover { + transform: translateY(-3px); + box-shadow: var(--shadow); + border-color: var(--accent); +} + +.project-title { + font-weight: 600; + margin-bottom: 6px; + color: var(--text); +} + +.project-desc { + font-size: 0.88rem; + color: var(--text-muted); + margin-bottom: 10px; +} + +.project-link { + font-size: 0.85rem; + font-weight: 600; + color: var(--accent); +} + +/* Footer */ +.footer { + text-align: center; + color: var(--text-muted); + font-size: 0.82rem; + padding: 30px 20px 0; +} + +@media (max-width: 600px) { + .hero { + padding: 48px 16px 90px; + text-align: center; + } + .hero-inner { + flex-direction: column; + text-align: center; + } + .contacts { + justify-content: center; + } + .card { + padding: 22px 18px; + margin-top: -40px; + } + .timeline-item { + grid-template-columns: 1fr; + gap: 4px; + } +} diff --git a/assets/js/data.js b/assets/js/data.js new file mode 100644 index 0000000..a3f17e3 --- /dev/null +++ b/assets/js/data.js @@ -0,0 +1,133 @@ +/* + * Все данные визитки собраны здесь. + * Чтобы добавить новую запись — просто скопируй похожий объект внутри + * соответствующего массива и поменяй текст. Ничего в других файлах трогать не нужно. + * Поле "current: true" у опыта/образования подсветит запись как текущую. + */ + +const SITE_DATA = { + profile: { + name: "Георгий Хатунцев", + role: "Программист", + avatar: "img/avatar.png", + gender: "Мужской", + birthDate: "1993-02-20", // возраст в шапке считается от этой даты автоматически + location: "Нижний Тагил, Россия", + // Короткое описание под именем. Можно оставить пустым: about: "" + about: + "Разработчик со опытом в промышленной автоматизации, системном администрировании и веб-разработке.", + }, + + // Контакты — иконка берётся из Font Awesome (класс), ссылка — куда ведёт. + contacts: [ + { icon: "fas fa-phone", label: "Телефон", href: "tel:+79122161573" }, + { icon: "fas fa-envelope", label: "Email", href: "mailto:google_12345@mail.ru" }, + { icon: "fab fa-telegram", label: "Telegram", href: "https://t.me/google_12345" }, + { icon: "fab fa-discord", label: "Discord", href: "https://discord.com/users/337960071940276224" }, + { icon: "fab fa-git-alt", label: "Git", href: "https://git.khatuncev.ru/" }, + ], + + // Навыки сгруппированы по категориям. Чтобы добавить категорию — скопируй объект. + // Чтобы добавить навык — допиши строку в нужный items. Если весь массив пуст — секция не отображается. + skills: [ + { + category: "Языки программирования", + items: ["Watcom C", "C", "C++", "C#", "Java (Spring)", "Go"], + }, + { + category: "Frontend", + items: ["HTML", "CSS", "JavaScript", "Vue 3", "Bootstrap", "Bulma", "Tailwind"], + }, + { + category: "Скриптинг", + items: ["Bash / Shell", "Python"], + }, + { + category: "Администрирование и инфраструктура", + items: ["Nginx", "Apache", "Caddy", "Samba", "DHCPd", "Dnsmasq", "BIND (named)", "Kerberos (krb5)"], + }, + ], + + education: [ + { + title: "Радио-технический факультет, специальность «Программная инженерия» (магистр)", + place: "Уральский федеральный университет имени первого Президента России Б.Н. Ельцина, Екатеринбург", + period: "2021", + }, + { + title: "Радио-технический факультет, специальность «Информационные системы и технологии» (бакалавр)", + place: "Уральский федеральный университет имени первого Президента России Б.Н. Ельцина, Екатеринбург", + period: "2019", + }, + ], + + experience: [ + { + title: "Инженер-программист", + place: 'ООО «Амбрелла»', + period: "2025 — настоящее время", + current: true, + }, + { + title: "Инженер-программист", + place: 'ООО «Инсайрес»', + period: "2024 — 2025", + }, + { + title: "Системный администратор", + place: 'МБУ «Централизованная бухгалтерия образовательных учреждений» г. Верхняя Салда', + period: "2022 — 2024", + }, + { + title: "Программист", + place: "IntelliCAD Technology Consortium", + period: "2022 — 2024", + }, + { + title: "Инженер-программист", + place: 'ООО Компания «Тензор»', + period: "2021 — 2022", + }, + { + title: "Инженер-программист", + place: 'ПАО «Корпорация ВСМПО-АВИСМА» г. Верхняя Салда', + period: "2017 — 2021", + }, + { + title: "Техник", + place: 'ПАО «Корпорация ВСМПО-АВИСМА» г. Верхняя Салда', + period: "2015 — 2017", + }, + { + title: "Системный администратор", + place: 'МБУ «Централизованная бухгалтерия образовательных учреждений» г. Верхняя Салда', + period: "2014-2015", + }, + { + title: "Индивидуальный предприниматель", + place: "", + period: "2012 — 2014", + }, + ], + + projects: [ + { + title: "Текущий статус вакуумно-дуговых печей", + href: "https://khatuncev.ru/demo1", + description: + "Визуальное представление текущего процесса плавки в вакуумно-дуговых печах. Весь цикл представлен в виде гистограммы.", + }, + { + title: "Рабочее место технолога", + href: "https://khatuncev.ru/demo2", + description: + "Визуальное представление паспортов титановых слитков и аналого-дискретных сигналов вакуумно-дуговых печей в процессе плавки.", + }, + { + title: "AdvancedXL", + href: "https://advancedxl.ru/", + description: + "Аналитическая надстройка для Microsoft Excel, которая ускоряет работу с таблицами, формулами и финансовыми моделями в 5 и более раз.", + }, + ], +}; diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..5711944 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,115 @@ +function el(tag, className, html) { + const node = document.createElement(tag); + if (className) node.className = className; + if (html !== undefined) node.innerHTML = html; + return node; +} + +function calcAge(birthDateStr) { + const birth = new Date(birthDateStr); + const today = new Date(); + let age = today.getFullYear() - birth.getFullYear(); + const monthDiff = today.getMonth() - birth.getMonth(); + if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) { + age--; + } + return age; +} + +function pluralizeYears(age) { + const mod10 = age % 10; + const mod100 = age % 100; + if (mod10 === 1 && mod100 !== 11) return `${age} год`; + if ([2, 3, 4].includes(mod10) && ![12, 13, 14].includes(mod100)) return `${age} года`; + return `${age} лет`; +} + +function renderProfile(data) { + document.title = `${data.profile.role} ${data.profile.name}`; + document.getElementById("avatar").src = data.profile.avatar; + document.getElementById("avatar").alt = data.profile.name; + document.getElementById("name").textContent = data.profile.name; + document.getElementById("role").textContent = data.profile.role; + + const metaParts = []; + if (data.profile.gender) metaParts.push(data.profile.gender); + if (data.profile.birthDate) metaParts.push(pluralizeYears(calcAge(data.profile.birthDate))); + if (data.profile.location) metaParts.push(data.profile.location); + document.getElementById("meta").textContent = metaParts.join(" · "); + + const aboutEl = document.getElementById("about"); + if (data.profile.about) { + aboutEl.textContent = data.profile.about; + } else { + aboutEl.remove(); + } +} + +function renderContacts(data) { + const wrap = document.getElementById("contacts"); + data.contacts.forEach((c) => { + const a = el("a", "contact-icon", ``); + a.href = c.href; + a.title = c.label; + a.target = c.href.startsWith("http") ? "_blank" : "_self"; + a.rel = "noopener"; + wrap.appendChild(a); + }); +} + +function renderSkills(data) { + const section = document.getElementById("skills-section"); + const hasSkills = data.skills && data.skills.some((g) => g.items && g.items.length); + if (!hasSkills) { + section.remove(); + return; + } + const wrap = document.getElementById("skills"); + data.skills.forEach((group) => { + if (!group.items || group.items.length === 0) return; + const groupEl = el("div", "skill-group"); + groupEl.appendChild(el("div", "skill-category", group.category)); + const tags = el("div", "skill-tags"); + group.items.forEach((item) => tags.appendChild(el("span", "skill-tag", item))); + groupEl.appendChild(tags); + wrap.appendChild(groupEl); + }); +} + +function renderTimeline(containerId, items) { + const wrap = document.getElementById(containerId); + items.forEach((item) => { + const entry = el("div", "timeline-item" + (item.current ? " is-current" : "")); + entry.appendChild(el("div", "timeline-period", item.period)); + const body = el("div", "timeline-body"); + body.appendChild(el("div", "timeline-title", item.title)); + if (item.place) body.appendChild(el("div", "timeline-place", item.place)); + entry.appendChild(body); + wrap.appendChild(entry); + }); +} + +function renderProjects(data) { + const wrap = document.getElementById("projects"); + data.projects.forEach((p) => { + const card = el("a", "project-card"); + card.href = p.href; + card.target = "_blank"; + card.rel = "noopener"; + card.appendChild(el("div", "project-title", p.title)); + if (p.description) card.appendChild(el("div", "project-desc", p.description)); + card.appendChild(el("div", "project-link", "Открыть проект →")); + wrap.appendChild(card); + }); +} + +(function init() { + renderProfile(SITE_DATA); + renderContacts(SITE_DATA); + renderSkills(SITE_DATA); + renderTimeline("education", SITE_DATA.education); + renderTimeline("experience", SITE_DATA.experience); + renderProjects(SITE_DATA); + document.getElementById("footer").textContent = + `© ${new Date().getFullYear()} ${SITE_DATA.profile.name}`; +})(); diff --git a/index.html b/index.html index 6d9f616..980bf10 100644 --- a/index.html +++ b/index.html @@ -3,80 +3,55 @@ - Программист Георгий Хатунцев - - - - + Визитка + + + + - + -
-
-
- Фото -
-
-

Георгий Хатунцев

-

Пол: Мужской

-

Возраст: 30 лет

-
- - - - - -
+
+
+ +
+

+

+

+

+
+
-

Образование

-
    -
  • Уральский федеральный университет имени первого Президента России Б.Н. Ельцина, Екатеринбург,
    Радио-технический факультет, специальность Программная Инженерия (магистр) (2021 год)
  • -
  • Уральский федеральный университет имени первого Президента России Б.Н. Ельцина, Екатеринбург,
    Радио-технический факультет,специальность Информационные системы и технологии (бакалавр) (2019 год)
  • -
+
-

Опыт работы

-
    -
  • МБУ "Централизованная бухгалтерия образовательны учреждений" г. Верхняя Салда (Системный администратор, 2022 - 2024)
  • -
  • IntelliCAD Technology Consortium (Программист, 2022 - 2024)
  • -
  • ООО Компания "Тензор" (Инженер-программист, 2021 - 2022)
  • -
  • ПАО "Корпорация ВСМПО-АВИСМА" г. Верхняя Салда (Инженер-программист, 2017-2021)
  • -
  • ПАО "Корпорация ВСМПО-АВИСМА" г. Верхняя Салда (Техник, 2015 - 2017)
  • -
  • МБУ "Централизованная бухгалтерия образовательны учреждений" г. Верхняя Салда (Системный администратор, 2015)
  • -
  • Индивидуальный предприниматель (2012 - 2014)
  • -
+
+

Навыки

+
+
-

Проекты

-
    -
  • Текущий статус вакуумно-дуговых печей - Визуальное представление текущего процесса плавки в вакуумно-дуговых печах. Весь цикл представлен в виде гистограммы.
  • -
  • Рабочее Место Технолога - Визуальное представление паспортов титановых слитков и аналого-дискретных сигналов вакуумно-дуговых печей в процесса плавки.
  • -
-
+
+

Образование

+
+
- - - - +
+

Опыт работы

+
+
+ +
+

Проекты

+
+
+ + + + + +