commit 152f1d60b52d8e969bd4cc967008c1b5e6ec80ab Author: aibrash Date: Wed Nov 4 16:35:52 2026 +0500 Сайт-визитка: главная, архитектура, проекты - Главная: фото, факты, соцсети, форма связи, анимации - Архитектура: карточки проектов и отдельные страницы (Тихий двор, павильон, «Лофт») - Проекты: Aman's World (скриншоты, описание) и хоумлаб - Меню из config.json, шаблон новых страниц, черновики training/work/fixed - Чистый HTML/CSS/JS без сборки, шрифты и картинки локально Co-Authored-By: Claude Sonnet 5.5 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f5d0c3d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +.DS_Store +*.log diff --git a/README.md b/README.md new file mode 100644 index 0000000..37f45f5 --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +# Сайт-визитка + +Одна страница на чистом HTML + CSS + JS, без сборки и бэкенда. + +``` +web/index.html главная страница +web/arch/ список проектов, у каждого своя страница: web/arch/<проект>/ +web/projects/ список проектов, у каждого своя страница: web/projects/<проект>/ (сейчас Aman's World) +web/training|work|fixed/ заготовки будущих страниц (черновики) +templates/page.html шаблон новой страницы +web/config.json всё содержимое: имя, статус, описание, теги, почта, соцсети, фото +web/css, js, fonts стили, скрипт, шрифты (локально) +web/og/home.png превью для соцсетей 1200x630 +web/404.html страница 404 +``` + +## Что править +Только `web/config.json`. Всё, где `username`, `example.com` и `TODO`, заменить своим. +Скриншоты игры лежат в `web/img/proj/`. Картинки архитектуры лежат в `web/img/arch/` в трёх размерах: `имя.webp`, `имя-md.webp`, `имя-sm.webp` (телефонам отдаются лёгкие). +Новый проект в архитектуре: скопируйте любую страницу `web/arch/<проект>/index.html`, замените текст и картинки, добавьте карточку в `web/arch/index.html` и адрес в `sitemap.xml`. +Фото: положите файл в `web/img/` и впишите путь в `photo` (например `"/img/me.webp"`, квадрат от 192 px). +Если `photo` пустое, показываются инициалы. + +## Страницы +Опубликованы `web/arch/` и `web/projects/` (в обеих карточки ведут на страницы проектов). Заготовки (черновики) лежат в `web/training/` (Тренировки), `web/work/` (Работа), `web/fixed/` (Фикс). Сейчас это черновики: их нет в меню, а поисковикам они закрыты (`noindex`). + +Опубликовать готовую страницу: +1. Заполните текст в `web/<имя>/index.html` (абзацы, заголовки `h2`, ссылки оформятся сами). +2. В `web/config.json` в `pages` у нужной записи уберите `"draft": true`: она появится в меню на всех страницах. +3. В самой странице удалите строку ``. +4. Добавьте адрес в `web/sitemap.xml`. + +Новая страница с нуля: скопируйте `templates/page.html` в `web/<имя>/index.html`, правьте так же, а в `pages` добавьте `{ "label": "Название", "url": "/<имя>/" }`. +Пока в `pages` нет опубликованных страниц, на главной меню не показывается. + +## Запуск локально +```bash +python3 -m http.server 8787 --directory web # открыть http://localhost:8787 +``` + +## Выкладка +Это просто статика: скопируйте папку `web/` на сервер и раздайте любым веб-сервером. +Пример для Caddy (HTTPS получится сам): +``` +my.domain { + root * /srv/visit-site/web + encode zstd gzip + file_server + handle_errors { + rewrite * /404.html + file_server + } + header { + Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; object-src 'none'; frame-ancestors 'none'" + X-Content-Type-Options "nosniff" + Referrer-Policy "strict-origin-when-cross-origin" + -Server + } +} +``` +Потом подставьте домен в превью и поисковые файлы: +```bash +sed -i 's|https://example.com|https://my.domain|g' web/index.html web/robots.txt web/sitemap.xml +``` +Если сервер стоит дома, лучше публиковать через Cloudflare Tunnel или держать его в отдельной сети, а не рядом с остальными устройствами. + +## Проверено +Ширина 360 px без горизонтального скролла, вес страницы около 38 КБ вместе со шрифтами, Lighthouse (mobile и desktop) см. ниже в сообщении. diff --git a/templates/page.html b/templates/page.html new file mode 100644 index 0000000..bd0e818 --- /dev/null +++ b/templates/page.html @@ -0,0 +1,48 @@ + + + + + + +Название страницы: Аман Ибраш + + + + + + + + + + + + + + + + + + + + +
+
+

Название страницы

+
+

Здесь текст. Абзацы, заголовки второго уровня и ссылки оформятся сами.

+

Подзаголовок

+

Ещё один абзац. Ссылка на главную.

+
+
+
+ + + + diff --git a/web/404.html b/web/404.html new file mode 100644 index 0000000..0a5b22b --- /dev/null +++ b/web/404.html @@ -0,0 +1,22 @@ + + + + + +404: такой страницы нет + + + + + + + +
+
+

404

+

Такой страницы нет. Либо ссылка устарела, либо я её переименовал. Скорее второе.

+ +
+
+ + diff --git a/web/arch/exhibition-pavilion/index.html b/web/arch/exhibition-pavilion/index.html new file mode 100644 index 0000000..d69fb79 --- /dev/null +++ b/web/arch/exhibition-pavilion/index.html @@ -0,0 +1,86 @@ + + + + + +Выставочный павильон: Аман Ибраш + + + + + + + + + + + + + + + + + + + + + + + +
+

← Архитектура

+
+
  • 2026
  • Алматы
  • общественное здание
+

Выставочный павильон

+

Галерея в парке

+
+ + + +
  • 2этажа
  • 26м длина здания
  • 2галереи
  • 1терраса для скульптур
+ +
+

Витрина, которую видно с аллеи ещё до входа.

+

Первый этаж как витрина

Павильон стоит в парке и работает как витрина: первый этаж почти целиком остеклён, поэтому экспозицию видно с аллеи ещё до входа. Входишь, и сразу попадаешь в холл, из которого открываются обе галереи.

Спокойный план

Из холла гости идут в две галереи, а лестница ведёт на второй уровень с открытой террасой для скульптур. Служебные помещения собраны в одном крыле, поэтому залы остаются свободными и чистыми для выставок.

Три материала

Фасад собран из трёх материалов: стеклянная витрина, плоскости бетона и тёплые деревянные ламели. Они держат один спокойный ритм и не спорят с парком вокруг.

+
+ +
+

Кадры

+ +
+ +
+

Чертежи и схемы

+ +
+ + + Следующий проект + Жилой комплекс «Лофт» + + +
+ + + + + diff --git a/web/arch/index.html b/web/arch/index.html new file mode 100644 index 0000000..00b37b0 --- /dev/null +++ b/web/arch/index.html @@ -0,0 +1,76 @@ + + + + + +Архитектура: Аман Ибраш + + + + + + + + + + + + + + + + + + + + + + + +
+
+

Архитектура

+

Рисую здания

+

Я студент KazGASA в Алматы. Здесь проекты, которыми горжусь больше всего. Выбери любой и загляни внутрь.

+
+ +
+ +
Тихий двор
+
+
  • 2026
  • Конаев
+

Тихий двор

+

Бетон, дерево и двор, в который приходят небо и свет.

+ Смотреть проект +
+
+ +
Выставочный павильон
+
+
  • 2026
  • Алматы
+

Выставочный павильон

+

Стеклянная витрина среди деревьев и терраса для скульптур.

+ Смотреть проект +
+
+ +
Жилой комплекс «Лофт»
+
+
  • 2026
  • Алматы
+

Жилой комплекс «Лофт»

+

Красный кирпич и общий двор, свободный от машин.

+ Смотреть проект +
+
+
+ + +
+ + + + + diff --git a/web/arch/loft-complex/index.html b/web/arch/loft-complex/index.html new file mode 100644 index 0000000..c2a0839 --- /dev/null +++ b/web/arch/loft-complex/index.html @@ -0,0 +1,72 @@ + + + + + +Жилой комплекс «Лофт»: Аман Ибраш + + + + + + + + + + + + + + + + + + + + + + + +
+

← Архитектура

+
+
  • 2026
  • Алматы
  • жилой комплекс
+

Жилой комплекс «Лофт»

+

Три корпуса вокруг двора без машин

+
+ + + +
  • 3корпуса
  • 2,11га участок
  • 7этажей, корпус 1
  • 1подземный паркинг
+ +
+

Красный кирпич, арки на первом этаже и двор, где можно гулять.

+

Двор без машин

Комплекс стоит на участке 2,11 га в Турксибском районе Алматы. Три корпуса образуют общий двор, и машин в нём нет: парковка спрятана под землёй, а на поверхности остались площадки и озеленение.

Кирпич и арки

На первом этаже коммерция за арочными стальными витражами. Выше идут жилые этажи с окнами в стальных рамах. Фасад корпуса 1 выполнен из красного лицевого кирпича, а стальной пояс отделяет коммерческий этаж от квартир.

+
+ +
+

Чертежи и схемы

+ +
+ + + Следующий проект + Тихий двор + + +
+ + + + + diff --git a/web/arch/quiet-courtyard/index.html b/web/arch/quiet-courtyard/index.html new file mode 100644 index 0000000..067dd3e --- /dev/null +++ b/web/arch/quiet-courtyard/index.html @@ -0,0 +1,106 @@ + + + + + +Тихий двор: Аман Ибраш + + + + + + + + + + + + + + + + + + + + + + + +
+

← Архитектура

+
+
  • 2026
  • Конаев
  • жилой дом
+

Тихий двор

+

Дом в Конаеве вокруг внутреннего двора

+
+ + + +
  • 1этаж
  • 32м² внутренний двор
  • 41м² гостиная-кухня
  • 3спальни
+ +
+

Дом, который отвернулся от улицы и повернулся к небу.

+

Улица остаётся снаружи

Со стороны улицы это глухая бетонная стена с одной узкой щелью света. Она прячет вход от чужих глаз: чтобы попасть в дом, нужно пройти вдоль сада и только потом повернуть к двери. Рядом под бетонным навесом стоит машина, а сам дом остаётся спокойным и закрытым.

Всё собрано вокруг двора

Внутри всё держится на дворе площадью 32 м². Утреннее солнце заходит в него, галерея обходит двор по кругу, а комнаты открываются на гравий, воду и тонкие деревья. Над центром плана стоит зенитный фонарь: он приносит верхний свет в самую глубину дома.

Бетон и тёплое дерево

Главный материал один: бетон с отпечатками опалубки. Рядом с ним тёплое дерево дверей и потолочных обшивок, чёрный металл рам и зелень. Проект сделан в диалоге с архитектурой Тадао Андо: меньше деталей, больше света, тишины и тени.

+
+ +
+

Кадры

+ +
+ +
+

Чертежи и схемы

+ +
+ + + Следующий проект + Выставочный павильон + + +
+ + + + + diff --git a/web/config.json b/web/config.json new file mode 100644 index 0000000..4c39710 --- /dev/null +++ b/web/config.json @@ -0,0 +1,80 @@ +{ + "_note": "TODO: заменить всё своим. Страница читает только этот файл.", + "name": "Аман Ибраш", + "photo": "/img/me.webp", + "status": "на связи", + "tagline": "Админю серверы, качаюсь, кручу педали на фиксе и рисую здания. Скучать некогда.", + "tags": [ + { + "emoji": "🏋️", + "text": "тренировки" + }, + { + "emoji": "💻", + "text": "работа" + }, + { + "emoji": "🚴", + "text": "фикс" + } + ], + "cta": "Хочешь пообщаться? Пиши, отвечу в течение пары часов.", + "email": "amnibrash@gmail.com", + "socials": [ + { + "label": "Telegram", + "url": "https://t.me/aibrash", + "handle": "@aibrash" + }, + { + "label": "Instagram", + "url": "https://www.instagram.com/aman_ibrash/", + "handle": "@aman_ibrash" + }, + { + "label": "TikTok", + "url": "https://www.tiktok.com/@aman_ibrash", + "handle": "@aman_ibrash" + } + ], + "_pages_note": "Страницы лежат в web/<имя>/. \"draft\": true прячет страницу из меню. Чтобы опубликовать: уберите draft, удалите noindex в самой странице, добавьте адрес в sitemap.xml.", + "pages": [ + { + "label": "Архитектура", + "url": "/arch/" + }, + { + "label": "Проекты", + "url": "/projects/" + }, + { + "label": "Тренировки", + "url": "/training/", + "draft": true + }, + { + "label": "Работа", + "url": "/work/", + "draft": true + }, + { + "label": "Фикс", + "url": "/fixed/", + "draft": true + } + ], + "facts": [ + { + "emoji": "🎓", + "text": "студент KazGASA" + }, + { + "emoji": "📍", + "text": "Алматы" + }, + { + "emoji": "🎂", + "text": "18 лет" + } + ] +} diff --git a/web/css/style.css b/web/css/style.css new file mode 100644 index 0000000..6b46f6f --- /dev/null +++ b/web/css/style.css @@ -0,0 +1,307 @@ +/* ===== Шрифт (локально) ===== */ +@font-face { font-family: 'Onest'; font-weight: 100 900; font-display: swap; + src: url('/fonts/onest-cyrillic-wght-normal.woff2') format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } +@font-face { font-family: 'Onest'; font-weight: 100 900; font-display: swap; + src: url('/fonts/onest-latin-wght-normal.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } + +:root { + --bg: #f4f6fb; --card: #ffffff; --ink: #121a2b; --muted: #55627a; --line: #e2e8f3; + --accent: #3556ff; --accent-soft: #e8edff; --pop: #ffd84d; --good: #1fb865; + --font: 'Onest', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; + --shadow: 0 1px 2px rgba(18, 26, 43, .05), 0 10px 30px rgba(18, 26, 43, .07); + color-scheme: light; +} +*, *::before, *::after { box-sizing: border-box; } +html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; } +body { margin: 0; min-height: 100vh; min-height: 100dvh; display: grid; grid-template-columns: minmax(0, 1fr); place-items: center; padding: 2rem 1.1rem; background: var(--bg); color: var(--ink); font: 400 1.0625rem/1.55 var(--font); overflow-x: hidden; position: relative; } +h1, h2, p, ul { margin: 0; } +ul { padding: 0; list-style: none; } +a { color: inherit; } +:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; border-radius: 12px; } + +/* мягкие цветные пятна на фоне: только CSS, без картинок */ +.blob { position: fixed; border-radius: 50%; filter: blur(60px); opacity: .55; pointer-events: none; z-index: 0; } +.blob--a { width: 22rem; height: 22rem; background: var(--pop); top: -8rem; right: -6rem; } +.blob--b { width: 20rem; height: 20rem; background: #b9c8ff; bottom: -8rem; left: -7rem; } + +.page { position: relative; z-index: 1; width: 100%; min-width: 0; max-width: 30rem; display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.75rem; } + +/* ===== Блок «обо мне» ===== */ +.hero { display: grid; grid-template-columns: minmax(0, 1fr); justify-items: start; gap: .6rem; } +.avatar { width: 6.5rem; height: 6.5rem; border-radius: 30px; background: var(--pop); display: grid; place-items: center; overflow: hidden; transform: rotate(-4deg); box-shadow: var(--shadow); margin-bottom: .6rem; } +.avatar span { font-size: 2.4rem; font-weight: 800; letter-spacing: -.04em; } +.avatar img { width: 100%; height: 100%; object-fit: cover; } +.hello { font-size: 1.15rem; color: var(--muted); font-weight: 500; } +.wave { display: inline-block; transform-origin: 70% 70%; } +h1 { font-size: clamp(2.5rem, 12vw, 3.4rem); font-weight: 800; letter-spacing: -.035em; line-height: 1.02; } +.tagline { color: var(--muted); font-size: 1.15rem; max-width: 26rem; } +.tags { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .4rem; } +.tags li { display: inline-flex; align-items: center; gap: .15rem; padding: .45rem .9rem; background: var(--card); border: 1px solid var(--line); border-radius: 99px; font-weight: 600; font-size: .95rem; box-shadow: 0 1px 2px rgba(18, 26, 43, .04); } + +/* ===== Блок «давай общаться» ===== */ +.talk { display: grid; grid-template-columns: minmax(0, 1fr); gap: .8rem; padding: 1.5rem; background: var(--card); border-radius: 28px; box-shadow: var(--shadow); } +.status { display: inline-flex; align-items: center; gap: .5rem; justify-self: start; font-size: .85rem; font-weight: 600; color: #127a43; background: #e5f7ec; padding: .25rem .7rem; border-radius: 99px; } +.dot { width: .55rem; height: .55rem; border-radius: 50%; background: var(--good); } +h2 { font-size: 1.6rem; font-weight: 800; letter-spacing: -.025em; line-height: 1.1; } +.cta { color: var(--muted); } +.links { display: grid; grid-template-columns: minmax(0, 1fr); gap: .6rem; margin-top: .6rem; } +.link { min-width: 0; display: flex; align-items: center; gap: .85rem; min-height: 3.75rem; padding: .6rem 1rem .6rem .7rem; background: var(--bg); border: 1px solid var(--line); border-radius: 18px; text-decoration: none; -webkit-tap-highlight-color: transparent; transition: transform .15s, background .15s, border-color .15s, box-shadow .15s; } +.link .ic { flex: none; width: 2.4rem; height: 2.4rem; display: grid; place-items: center; border-radius: 12px; background: var(--accent-soft); color: var(--accent); font-weight: 800; } +.link .lk { font-weight: 700; white-space: nowrap; } +.link .lh { min-width: 0; margin-left: auto; color: var(--muted); font-size: .92rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.link::after { content: '→'; color: var(--accent); font-weight: 700; } +.link:hover { transform: translateY(-2px); background: var(--card); border-color: var(--accent); box-shadow: var(--shadow); } +.link:active { transform: scale(.98); } +.link--mail { background: var(--accent); border-color: var(--accent); color: #fff; } +.link--mail .ic { background: rgba(255, 255, 255, .2); color: #fff; } +.link--mail .lh, .link--mail::after { color: rgba(255, 255, 255, .9); } +.link--mail:hover { background: #2443e0; border-color: #2443e0; } + +/* ===== Компьютер: две колонки ===== */ +@media (min-width: 56rem) { + body { padding: 3rem 2rem; } + .page { max-width: 62rem; grid-template-columns: minmax(0, 1fr) 26rem; align-items: center; gap: 4.5rem; } + .avatar { width: 9rem; height: 9rem; border-radius: 40px; } + .avatar span { font-size: 3.3rem; } + h1 { font-size: clamp(3.4rem, 6vw, 4.6rem); } + .tagline { font-size: 1.3rem; } + .tags li { font-size: 1rem; } + .talk { padding: 2rem; } +} +@media (min-width: 40rem) and (max-width: 55.99rem) { .page { max-width: 34rem; } .talk { padding: 2rem; } } + +/* ===== Анимация: один вход + приветственная «волна» ===== */ +@keyframes rise { from { opacity: 0; transform: translateY(1rem); } to { opacity: 1; transform: none; } } +@keyframes wave { 0%, 60%, 100% { transform: rotate(0); } 10%, 30% { transform: rotate(16deg); } 20%, 40% { transform: rotate(-10deg); } 50% { transform: rotate(8deg); } } +@keyframes pop { from { opacity: 0; transform: rotate(-4deg) scale(.7); } to { opacity: 1; transform: rotate(-4deg) scale(1); } } +@media (prefers-reduced-motion: no-preference) { + .avatar { animation: pop .6s cubic-bezier(.3, 1.4, .5, 1) backwards; } + .hero > :not(.avatar):not(.tagline), .talk { animation: rise .6s cubic-bezier(.22, .8, .2, 1) both; } + .hello { animation-delay: .1s; } h1 { animation-delay: .16s; } .tagline { animation-delay: .22s; } .tags { animation-delay: .28s; } + .talk { animation-delay: .34s; } + .links li { animation: rise .5s cubic-bezier(.22, .8, .2, 1) both; } + .links li:nth-child(1) { animation-delay: .5s; } .links li:nth-child(2) { animation-delay: .56s; } + .links li:nth-child(3) { animation-delay: .62s; } .links li:nth-child(4) { animation-delay: .68s; } + .wave { animation: wave 1.8s .9s 2; } +} +@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; } } + +/* пока JS подгружает config.json, страница скрыта (вёрстка не прыгает); анимация входа стартует после показа */ +html.js:not(.ready) body[data-hide] .page { visibility: hidden; } +html.js:not(.ready) body[data-hide] .page * { animation-play-state: paused !important; } + +/* ===== Верхнее меню и внутренние страницы ===== */ +.topnav { position: absolute; top: 0; left: 50%; transform: translateX(-50%); z-index: 3; width: 100%; max-width: 62rem; display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: .7rem 1.1rem; } +.topnav:empty { display: none; } +.topnav .brand { font-weight: 800; letter-spacing: -.02em; text-decoration: none; white-space: nowrap; min-height: 2.75rem; display: inline-flex; align-items: center; } +.topnav ul { display: flex; gap: .25rem; overflow-x: auto; scrollbar-width: none; min-width: 0; } +.topnav ul::-webkit-scrollbar { display: none; } +.topnav .pill { display: inline-flex; align-items: center; min-height: 2.75rem; padding: 0 .95rem; border-radius: 99px; text-decoration: none; font-weight: 600; font-size: .95rem; color: var(--muted); white-space: nowrap; transition: background .15s, color .15s; } +.topnav .pill:hover { color: var(--ink); } +.topnav .pill[aria-current='page'] { background: var(--card); color: var(--ink); box-shadow: var(--shadow); } +body.has-nav { padding-top: 5rem; } +body.doc { place-items: start center; } +.page.page--doc { max-width: 40rem; grid-template-columns: minmax(0, 1fr); align-items: start; } +.doc-card { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem; padding: 1.5rem; background: var(--card); border-radius: 28px; box-shadow: var(--shadow); } +.doc-card h1 { font-size: clamp(2rem, 8vw, 2.8rem); } +.prose { display: grid; gap: 1rem; color: var(--muted); } +.prose h2 { color: var(--ink); margin-top: .5rem; } +.prose a { color: var(--accent); } +@media (min-width: 56rem) { .doc-card { padding: 2.5rem; } body.has-nav { padding-top: 6rem; } } + +/* с настоящим фото аватар крупнее, чтобы лицо было видно */ +.avatar.has-photo { width: 8.5rem; height: 8.5rem; background: var(--card); } +.avatar.has-photo img { object-position: 50% 22%; } +@media (min-width: 56rem) { .avatar.has-photo { width: 13rem; height: 13rem; } } + + +/* ===== Факты, слова тэглайна, живые детали ===== */ +.facts { display: flex; flex-wrap: wrap; gap: .35rem .9rem; font-size: .95rem; font-weight: 600; color: var(--ink); } +.facts li { display: inline-flex; align-items: center; gap: .3rem; } +.em { font-style: normal; } +.tagline .w { display: inline-block; } +.avatar { transition: transform .35s cubic-bezier(.3, 1.4, .5, 1); } +.avatar:hover { transform: rotate(3deg) scale(1.05); } +.link { position: relative; overflow: hidden; } +.link::before { content: ''; position: absolute; inset: 0 auto 0 -60%; width: 40%; background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .55), transparent); transform: skewX(-18deg) translateX(0); opacity: 0; pointer-events: none; } +.link:hover::before { opacity: 1; transform: skewX(-18deg) translateX(420%); transition: transform .8s ease, opacity .2s; } + +/* ===== Внутренние страницы: шапка, проекты, галерея ===== */ +.page.page--wide { max-width: 62rem; grid-template-columns: minmax(0, 1fr); align-items: start; gap: 3.5rem; } +.page-intro { display: grid; gap: .6rem; justify-items: start; } +.page-intro h1 { font-size: clamp(2.6rem, 11vw, 4.4rem); } +.lead { font-size: 1.2rem; color: var(--muted); max-width: 40rem; } +.proj { display: grid; gap: 1.5rem; } +.proj-head { display: grid; gap: .7rem; justify-items: start; max-width: 40rem; } +.proj-head h2 { font-size: clamp(1.8rem, 7vw, 2.6rem); } +.proj-head p { color: var(--muted); } +.meta { display: flex; flex-wrap: wrap; gap: .4rem; } +.meta li { padding: .25rem .7rem; border-radius: 99px; background: var(--accent-soft); color: var(--accent); font-weight: 700; font-size: .82rem; } +.gallery { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.1rem; } +.shot { margin: 0; min-width: 0; } +.shot a { display: block; overflow: hidden; border-radius: 22px; background: var(--card); box-shadow: var(--shadow); } +.shot img { width: 100%; height: auto; transition: transform .7s cubic-bezier(.22, .8, .2, 1); } +.shot a:hover img { transform: scale(1.03); } +.shot--plan img { padding: .8rem; background: #fff; } +.shot--portrait { max-width: 26rem; } +.shot figcaption { margin-top: .55rem; padding-inline: .35rem; font-size: .9rem; color: var(--muted); } +.soon { display: grid; gap: .3rem; padding: 1.4rem 1.5rem; border: 2px dashed var(--line); border-radius: 24px; color: var(--muted); } +.soon .hello { font-size: 1rem; color: var(--ink); font-weight: 700; } +@media (min-width: 48rem) { + .gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.4rem; } + .shot--lead, .shot--plan { grid-column: 1 / -1; } + .shot--portrait { grid-column: auto; } + .page.page--wide { gap: 5rem; } +} + +/* ===== Страница проектов: игра ===== */ +.game-feature { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.5rem; } +.game-shot { position: relative; display: block; border-radius: 26px; overflow: hidden; background: #0a0a0c; box-shadow: var(--shadow); } +.game-shot img { width: 100%; height: auto; transition: transform .8s cubic-bezier(.22, .8, .2, 1); } +.game-shot:hover img { transform: scale(1.04); } +.play { position: absolute; right: 1rem; bottom: 1rem; width: 3.4rem; height: 3.4rem; display: grid; place-items: center; border-radius: 50%; background: rgba(255, 255, 255, .92); color: #121a2b; font-size: 1.2rem; padding-left: .2rem; box-shadow: 0 10px 30px rgba(0, 0, 0, .35); transition: transform .25s cubic-bezier(.3, 1.4, .5, 1); } +.game-shot:hover .play { transform: scale(1.12); } +.game-body { display: grid; gap: .9rem; justify-items: start; } +.game-body h2 { font-size: clamp(2rem, 8vw, 3rem); } +.game-body p { color: var(--muted); max-width: 40rem; } +.game-body .lead { color: var(--ink); font-weight: 500; } +.actions { margin-top: .5rem; } +.btn { display: inline-flex; align-items: center; justify-content: center; min-height: 3.25rem; padding: 0 1.7rem; border-radius: 99px; font: 700 1.05rem var(--font); text-decoration: none; transition: transform .15s, box-shadow .2s, background .15s; -webkit-tap-highlight-color: transparent; } +.btn--primary { background: var(--accent); color: #fff; box-shadow: 0 8px 22px rgba(53, 86, 255, .35); } +.btn--primary:hover { background: #2443e0; transform: translateY(-2px); box-shadow: 0 12px 28px rgba(53, 86, 255, .4); } +.btn:active { transform: scale(.97); } +.game-extra { grid-column: 1 / -1; } +@media (min-width: 56rem) { + .game-feature { grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); gap: 2.5rem 3rem; align-items: center; } + .game-extra { grid-column: 1 / -1; max-width: 42rem; } +} + +/* ===== Красивые анимации ===== */ +@keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } } +@keyframes drift-a { from { transform: translate(0, 0) scale(1); } to { transform: translate(-3rem, 4rem) scale(1.12); } } +@keyframes drift-b { from { transform: translate(0, 0) scale(1); } to { transform: translate(4rem, -3rem) scale(1.1); } } +@keyframes word { from { opacity: 0; transform: translateY(.7em) rotate(2deg); } to { opacity: 1; transform: none; } } +@keyframes pop-in { from { opacity: 0; transform: scale(.9) translateY(1rem); } to { opacity: 1; transform: none; } } +@media (prefers-reduced-motion: no-preference) { + .blob { will-change: transform; } + .blob--a { animation: drift-a 16s ease-in-out infinite alternate; } + .blob--b { animation: drift-b 20s ease-in-out infinite alternate; } + .tagline .w { animation: word .55s cubic-bezier(.22, .8, .2, 1) backwards; animation-delay: calc(.35s + var(--i, 0) * .06s); } + .facts li { animation: rise .5s cubic-bezier(.22, .8, .2, 1) backwards; } + .facts li:nth-child(1) { animation-delay: .25s; } .facts li:nth-child(2) { animation-delay: .3s; } .facts li:nth-child(3) { animation-delay: .35s; } + .tags li { animation: pop-in .5s cubic-bezier(.3, 1.4, .5, 1) backwards, float 4.5s ease-in-out infinite; } + .tags li:nth-child(1) { animation-delay: .8s, 1.4s; } .tags li:nth-child(2) { animation-delay: .9s, 2s; } .tags li:nth-child(3) { animation-delay: 1s, 2.6s; } + .page-intro > * { animation: rise .6s cubic-bezier(.22, .8, .2, 1) backwards; } + .page-intro > :nth-child(2) { animation-delay: .08s; } .page-intro > :nth-child(3) { animation-delay: .16s; } + .proj:first-of-type .proj-head > *, .game-body > * { animation: rise .6s cubic-bezier(.22, .8, .2, 1) backwards; } + .game-body > :nth-child(2) { animation-delay: .06s; } .game-body > :nth-child(3) { animation-delay: .12s; } .game-body > :nth-child(4) { animation-delay: .18s; } + .play { animation: pulse-ring 2.4s ease-out infinite; } + @keyframes pulse-ring { 0% { box-shadow: 0 10px 30px rgba(0, 0, 0, .35), 0 0 0 0 rgba(255, 255, 255, .6); } 70%, 100% { box-shadow: 0 10px 30px rgba(0, 0, 0, .35), 0 0 0 1.4rem rgba(255, 255, 255, 0); } } +} +/* появление по мере прокрутки: только там, где браузер умеет, без поддержки просто статика */ +@supports (animation-timeline: view()) { + @media (prefers-reduced-motion: no-preference) { + .shot, .proj-head, .soon, .game-extra { animation: scroll-in linear both; animation-timeline: view(); animation-range: entry 0% entry 55%; } + .shot img { animation: img-settle linear both; animation-timeline: view(); animation-range: entry 0% cover 55%; } + @keyframes scroll-in { from { opacity: 0; transform: translateY(2.2rem) scale(.97); } to { opacity: 1; transform: none; } } + @keyframes img-settle { from { scale: 1.12; } to { scale: 1; } } + } +} +/* плавные переходы между страницами (Chrome, Safari 18.2+; в остальных просто обычный переход) */ +@view-transition { navigation: auto; } +::view-transition-old(root) { animation: .22s ease both vt-out; } +::view-transition-new(root) { animation: .32s ease both vt-in; } +@keyframes vt-out { to { opacity: 0; } } +@keyframes vt-in { from { opacity: 0; } } +@media (prefers-reduced-motion: reduce) { ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; } } + +/* компактное меню на узких телефонах, чтобы влезали все пункты */ +@media (max-width: 30rem) { + .topnav { gap: .4rem; padding-inline: .8rem; } + .topnav .brand { font-size: .95rem; } + .topnav ul { gap: .1rem; } + .topnav .pill { padding: 0 .65rem; font-size: .9rem; } +} + +/* первый кадр страницы виден сразу: без появления по прокрутке (быстрее LCP) */ +@supports (animation-timeline: view()) { .proj:first-of-type .shot--lead, .proj:first-of-type .shot--lead img, .proj:first-of-type .proj-head { animation: none; } } + +/* ===== Список проектов (карточки) ===== */ +.works { display: grid; gap: 1.4rem; } +.work { display: grid; min-width: 0; overflow: hidden; border-radius: 28px; background: var(--card); color: inherit; text-decoration: none; box-shadow: var(--shadow); transition: transform .4s cubic-bezier(.22, .8, .2, 1), box-shadow .4s; -webkit-tap-highlight-color: transparent; } +.work .pic { aspect-ratio: 4 / 3; overflow: hidden; background: var(--bg); } +.work .pic img { width: 100%; height: 100%; object-fit: cover; transition: transform .9s cubic-bezier(.22, .8, .2, 1); } +.work .info { display: grid; gap: .55rem; justify-items: start; padding: 1.2rem 1.4rem 1.5rem; } +.work h2 { font-size: clamp(1.5rem, 6vw, 2rem); } +.work p { color: var(--muted); } +.work .more { margin-top: .3rem; font-weight: 700; color: var(--accent); } +.work .more span { display: inline-block; transition: transform .25s; } +.work:hover { transform: translateY(-5px); box-shadow: 0 20px 46px rgba(18, 26, 43, .15); } +.work:hover .pic img { transform: scale(1.06); } +.work:hover .more span { transform: translateX(5px); } +.work:active { transform: scale(.985); } +@media (min-width: 48rem) { + .works { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.6rem; } + .work--lead { grid-column: 1 / -1; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); } + .work--lead .pic { aspect-ratio: auto; min-height: 24rem; } + .work--lead .info { align-content: center; padding: 2.2rem; } + .work--lead h2 { font-size: 2.6rem; } +} + +/* ===== Страница проекта ===== */ +.back { font-weight: 600; } +.back a { color: var(--muted); text-decoration: none; } +.back a:hover { color: var(--accent); } +.case-head { display: grid; gap: .8rem; justify-items: start; margin-top: -1.2rem; } +.case-head h1 { font-size: clamp(2.5rem, 11vw, 4.6rem); } +.gallery--one { grid-template-columns: minmax(0, 1fr); } +.shot--cover a { border-radius: 28px; } +.case-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .8rem; } +.stat { display: grid; gap: .1rem; padding: 1rem 1.1rem; border-radius: 20px; background: var(--card); box-shadow: var(--shadow); } +.stat b { font-size: clamp(1.8rem, 8vw, 2.4rem); font-weight: 800; letter-spacing: -.03em; line-height: 1.1; color: var(--accent); font-variant-numeric: tabular-nums; } +.stat span { color: var(--muted); font-size: .9rem; } +.case-body { display: grid; gap: 1.1rem; max-width: 44rem; } +.case-body .intro { font-size: clamp(1.4rem, 5.5vw, 2rem); font-weight: 700; letter-spacing: -.02em; line-height: 1.2; color: var(--ink); max-width: none; } +.case-body h2 { margin-top: 1rem; font-size: clamp(1.4rem, 5vw, 1.8rem); } +.case-body p { color: var(--muted); font-size: 1.1rem; max-width: none; } +.case-block { display: grid; gap: 1.2rem; } +.block-title { font-size: clamp(1.5rem, 6vw, 2rem); } +.next-case { position: relative; display: grid; gap: .4rem; padding: 1.7rem; border-radius: 28px; background: var(--ink); color: #fff; text-decoration: none; overflow: hidden; transition: transform .3s cubic-bezier(.22, .8, .2, 1); } +.next-case .muted-w { color: rgba(255, 255, 255, .7); font-weight: 600; } +.next-title { font-size: clamp(1.8rem, 7vw, 3rem); font-weight: 800; letter-spacing: -.03em; line-height: 1.05; max-width: 90%; } +.next-arrow { position: absolute; right: 1.5rem; bottom: 1.4rem; font-size: 2rem; transition: transform .3s; } +.next-case:hover { transform: translateY(-3px); } +.next-case:hover .next-arrow { transform: translateX(8px); } +@media (min-width: 48rem) { + .case-head { margin-top: -2rem; } + .case-stats { grid-template-columns: repeat(4, minmax(0, 1fr)); } + .shot--portrait { max-width: none; } + .next-case { padding: 2.4rem; } +} + +/* первый кадр страницы проекта виден сразу, без анимации появления (быстрее LCP) */ +@supports (animation-timeline: view()) { .gallery--one .shot, .gallery--one .shot img, .case-head, .back { animation: none; } } + +/* если кадров нечётное число, последний занимает всю строку */ +@media (min-width: 48rem) { .gallery > .shot:last-child:nth-child(odd) { grid-column: 1 / -1; } } + +/* подсказка под кнопкой «Играть» */ +.actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1.1rem; } +.note { font-size: .92rem; color: var(--muted); max-width: 24rem; } + +/* на узких телефонах в шапке только имя, фамилия прячется, чтобы влезали все пункты */ +@media (max-width: 26rem) { .topnav .brand-rest { display: none; } } + +/* плитки «что живёт на сервере» */ +.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: .9rem; } +.tile { display: grid; gap: .4rem; align-content: start; padding: 1.2rem 1.3rem; border-radius: 22px; background: var(--card); box-shadow: var(--shadow); color: inherit; text-decoration: none; transition: transform .3s cubic-bezier(.22, .8, .2, 1), box-shadow .3s; } +.tile h3 { font-size: 1.2rem; font-weight: 800; letter-spacing: -.02em; } +.tile p { color: var(--muted); font-size: .98rem; } +a.tile:hover { transform: translateY(-4px); box-shadow: 0 16px 36px rgba(18, 26, 43, .14); } +.tile .more { color: var(--accent); font-weight: 700; } + +/* последняя плитка: «список открытый» */ +.tile--more { background: transparent; box-shadow: none; border: 2px dashed var(--line); align-content: center; } +.tile--more h3 { color: var(--accent); } diff --git a/web/favicon.svg b/web/favicon.svg new file mode 100644 index 0000000..475487e --- /dev/null +++ b/web/favicon.svg @@ -0,0 +1 @@ +А diff --git a/web/fixed/index.html b/web/fixed/index.html new file mode 100644 index 0000000..63df79d --- /dev/null +++ b/web/fixed/index.html @@ -0,0 +1,40 @@ + + + + + +Фикс: Аман Ибраш + + + + + + + + + + + + + + + + + + + + + +
+
+

Фикс

+
+

Тут будет про фикс: байк, маршруты и километры.

+

Страница в работе, скоро заполню. А пока вернись на главную.

+
+
+
+ + + + diff --git a/web/fonts/LICENSE-Onest.txt b/web/fonts/LICENSE-Onest.txt new file mode 100644 index 0000000..1d466e3 --- /dev/null +++ b/web/fonts/LICENSE-Onest.txt @@ -0,0 +1,93 @@ +Copyright 2021 The Onest Project Authors (https://github.com/simpals/onest) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/web/fonts/onest-cyrillic-wght-normal.woff2 b/web/fonts/onest-cyrillic-wght-normal.woff2 new file mode 100644 index 0000000..7a139b0 Binary files /dev/null and b/web/fonts/onest-cyrillic-wght-normal.woff2 differ diff --git a/web/fonts/onest-latin-wght-normal.woff2 b/web/fonts/onest-latin-wght-normal.woff2 new file mode 100644 index 0000000..bd5ac33 Binary files /dev/null and b/web/fonts/onest-latin-wght-normal.woff2 differ diff --git a/web/img/arch/house-axo-md.webp b/web/img/arch/house-axo-md.webp new file mode 100644 index 0000000..e7eeb9c Binary files /dev/null and b/web/img/arch/house-axo-md.webp differ diff --git a/web/img/arch/house-axo-sm.webp b/web/img/arch/house-axo-sm.webp new file mode 100644 index 0000000..2762a46 Binary files /dev/null and b/web/img/arch/house-axo-sm.webp differ diff --git a/web/img/arch/house-axo.webp b/web/img/arch/house-axo.webp new file mode 100644 index 0000000..5d76ec8 Binary files /dev/null and b/web/img/arch/house-axo.webp differ diff --git a/web/img/arch/house-concept-md.webp b/web/img/arch/house-concept-md.webp new file mode 100644 index 0000000..868d21e Binary files /dev/null and b/web/img/arch/house-concept-md.webp differ diff --git a/web/img/arch/house-concept-sm.webp b/web/img/arch/house-concept-sm.webp new file mode 100644 index 0000000..413a18b Binary files /dev/null and b/web/img/arch/house-concept-sm.webp differ diff --git a/web/img/arch/house-concept.webp b/web/img/arch/house-concept.webp new file mode 100644 index 0000000..9b61a67 Binary files /dev/null and b/web/img/arch/house-concept.webp differ diff --git a/web/img/arch/house-corridor-md.webp b/web/img/arch/house-corridor-md.webp new file mode 100644 index 0000000..3c27f11 Binary files /dev/null and b/web/img/arch/house-corridor-md.webp differ diff --git a/web/img/arch/house-corridor-sm.webp b/web/img/arch/house-corridor-sm.webp new file mode 100644 index 0000000..7b3e936 Binary files /dev/null and b/web/img/arch/house-corridor-sm.webp differ diff --git a/web/img/arch/house-corridor.webp b/web/img/arch/house-corridor.webp new file mode 100644 index 0000000..fa428e4 Binary files /dev/null and b/web/img/arch/house-corridor.webp differ diff --git a/web/img/arch/house-courtyard-md.webp b/web/img/arch/house-courtyard-md.webp new file mode 100644 index 0000000..8139c51 Binary files /dev/null and b/web/img/arch/house-courtyard-md.webp differ diff --git a/web/img/arch/house-courtyard-sm.webp b/web/img/arch/house-courtyard-sm.webp new file mode 100644 index 0000000..7e70da4 Binary files /dev/null and b/web/img/arch/house-courtyard-sm.webp differ diff --git a/web/img/arch/house-courtyard.webp b/web/img/arch/house-courtyard.webp new file mode 100644 index 0000000..320aba8 Binary files /dev/null and b/web/img/arch/house-courtyard.webp differ diff --git a/web/img/arch/house-entry-md.webp b/web/img/arch/house-entry-md.webp new file mode 100644 index 0000000..87c29f2 Binary files /dev/null and b/web/img/arch/house-entry-md.webp differ diff --git a/web/img/arch/house-entry-sm.webp b/web/img/arch/house-entry-sm.webp new file mode 100644 index 0000000..f5a84d6 Binary files /dev/null and b/web/img/arch/house-entry-sm.webp differ diff --git a/web/img/arch/house-entry.webp b/web/img/arch/house-entry.webp new file mode 100644 index 0000000..968e48a Binary files /dev/null and b/web/img/arch/house-entry.webp differ diff --git a/web/img/arch/house-gallery-md.webp b/web/img/arch/house-gallery-md.webp new file mode 100644 index 0000000..c523363 Binary files /dev/null and b/web/img/arch/house-gallery-md.webp differ diff --git a/web/img/arch/house-gallery-sm.webp b/web/img/arch/house-gallery-sm.webp new file mode 100644 index 0000000..986f6ff Binary files /dev/null and b/web/img/arch/house-gallery-sm.webp differ diff --git a/web/img/arch/house-gallery.webp b/web/img/arch/house-gallery.webp new file mode 100644 index 0000000..b5a9ca5 Binary files /dev/null and b/web/img/arch/house-gallery.webp differ diff --git a/web/img/arch/house-plan-md.webp b/web/img/arch/house-plan-md.webp new file mode 100644 index 0000000..00127bd Binary files /dev/null and b/web/img/arch/house-plan-md.webp differ diff --git a/web/img/arch/house-plan-sm.webp b/web/img/arch/house-plan-sm.webp new file mode 100644 index 0000000..4b60c55 Binary files /dev/null and b/web/img/arch/house-plan-sm.webp differ diff --git a/web/img/arch/house-plan.webp b/web/img/arch/house-plan.webp new file mode 100644 index 0000000..1ddb9e7 Binary files /dev/null and b/web/img/arch/house-plan.webp differ diff --git a/web/img/arch/house-pond-md.webp b/web/img/arch/house-pond-md.webp new file mode 100644 index 0000000..ffb934c Binary files /dev/null and b/web/img/arch/house-pond-md.webp differ diff --git a/web/img/arch/house-pond-sm.webp b/web/img/arch/house-pond-sm.webp new file mode 100644 index 0000000..56e19a6 Binary files /dev/null and b/web/img/arch/house-pond-sm.webp differ diff --git a/web/img/arch/house-pond.webp b/web/img/arch/house-pond.webp new file mode 100644 index 0000000..2d22e38 Binary files /dev/null and b/web/img/arch/house-pond.webp differ diff --git a/web/img/arch/house-street-md.webp b/web/img/arch/house-street-md.webp new file mode 100644 index 0000000..41a2705 Binary files /dev/null and b/web/img/arch/house-street-md.webp differ diff --git a/web/img/arch/house-street-sm.webp b/web/img/arch/house-street-sm.webp new file mode 100644 index 0000000..9123801 Binary files /dev/null and b/web/img/arch/house-street-sm.webp differ diff --git a/web/img/arch/house-street.webp b/web/img/arch/house-street.webp new file mode 100644 index 0000000..328ba10 Binary files /dev/null and b/web/img/arch/house-street.webp differ diff --git a/web/img/arch/loft-0-md.webp b/web/img/arch/loft-0-md.webp new file mode 100644 index 0000000..66cc542 Binary files /dev/null and b/web/img/arch/loft-0-md.webp differ diff --git a/web/img/arch/loft-0-sm.webp b/web/img/arch/loft-0-sm.webp new file mode 100644 index 0000000..c56c588 Binary files /dev/null and b/web/img/arch/loft-0-sm.webp differ diff --git a/web/img/arch/loft-0.webp b/web/img/arch/loft-0.webp new file mode 100644 index 0000000..1bcdeeb Binary files /dev/null and b/web/img/arch/loft-0.webp differ diff --git a/web/img/arch/loft-facade-md.webp b/web/img/arch/loft-facade-md.webp new file mode 100644 index 0000000..5e04428 Binary files /dev/null and b/web/img/arch/loft-facade-md.webp differ diff --git a/web/img/arch/loft-facade-sm.webp b/web/img/arch/loft-facade-sm.webp new file mode 100644 index 0000000..2347c5c Binary files /dev/null and b/web/img/arch/loft-facade-sm.webp differ diff --git a/web/img/arch/loft-facade.webp b/web/img/arch/loft-facade.webp new file mode 100644 index 0000000..88fb18d Binary files /dev/null and b/web/img/arch/loft-facade.webp differ diff --git a/web/img/arch/pav-1-md.webp b/web/img/arch/pav-1-md.webp new file mode 100644 index 0000000..b571b5a Binary files /dev/null and b/web/img/arch/pav-1-md.webp differ diff --git a/web/img/arch/pav-1-sm.webp b/web/img/arch/pav-1-sm.webp new file mode 100644 index 0000000..b8150f3 Binary files /dev/null and b/web/img/arch/pav-1-sm.webp differ diff --git a/web/img/arch/pav-1.webp b/web/img/arch/pav-1.webp new file mode 100644 index 0000000..45b36d1 Binary files /dev/null and b/web/img/arch/pav-1.webp differ diff --git a/web/img/arch/pav-2-md.webp b/web/img/arch/pav-2-md.webp new file mode 100644 index 0000000..54b952c Binary files /dev/null and b/web/img/arch/pav-2-md.webp differ diff --git a/web/img/arch/pav-2-sm.webp b/web/img/arch/pav-2-sm.webp new file mode 100644 index 0000000..39b1cba Binary files /dev/null and b/web/img/arch/pav-2-sm.webp differ diff --git a/web/img/arch/pav-2.webp b/web/img/arch/pav-2.webp new file mode 100644 index 0000000..6b9bde2 Binary files /dev/null and b/web/img/arch/pav-2.webp differ diff --git a/web/img/arch/pav-3-md.webp b/web/img/arch/pav-3-md.webp new file mode 100644 index 0000000..ef7a361 Binary files /dev/null and b/web/img/arch/pav-3-md.webp differ diff --git a/web/img/arch/pav-3-sm.webp b/web/img/arch/pav-3-sm.webp new file mode 100644 index 0000000..8c13839 Binary files /dev/null and b/web/img/arch/pav-3-sm.webp differ diff --git a/web/img/arch/pav-3.webp b/web/img/arch/pav-3.webp new file mode 100644 index 0000000..39b6660 Binary files /dev/null and b/web/img/arch/pav-3.webp differ diff --git a/web/img/arch/pav-plan-md.webp b/web/img/arch/pav-plan-md.webp new file mode 100644 index 0000000..9ca8d14 Binary files /dev/null and b/web/img/arch/pav-plan-md.webp differ diff --git a/web/img/arch/pav-plan-sm.webp b/web/img/arch/pav-plan-sm.webp new file mode 100644 index 0000000..d3e3ac3 Binary files /dev/null and b/web/img/arch/pav-plan-sm.webp differ diff --git a/web/img/arch/pav-plan.webp b/web/img/arch/pav-plan.webp new file mode 100644 index 0000000..e2ea329 Binary files /dev/null and b/web/img/arch/pav-plan.webp differ diff --git a/web/img/me.webp b/web/img/me.webp new file mode 100644 index 0000000..ac0f856 Binary files /dev/null and b/web/img/me.webp differ diff --git a/web/img/proj/game-ascend-md.webp b/web/img/proj/game-ascend-md.webp new file mode 100644 index 0000000..170aae5 Binary files /dev/null and b/web/img/proj/game-ascend-md.webp differ diff --git a/web/img/proj/game-ascend-sm.webp b/web/img/proj/game-ascend-sm.webp new file mode 100644 index 0000000..2952172 Binary files /dev/null and b/web/img/proj/game-ascend-sm.webp differ diff --git a/web/img/proj/game-ascend.webp b/web/img/proj/game-ascend.webp new file mode 100644 index 0000000..4d72860 Binary files /dev/null and b/web/img/proj/game-ascend.webp differ diff --git a/web/img/proj/game-hub-md.webp b/web/img/proj/game-hub-md.webp new file mode 100644 index 0000000..29761fd Binary files /dev/null and b/web/img/proj/game-hub-md.webp differ diff --git a/web/img/proj/game-hub-sm.webp b/web/img/proj/game-hub-sm.webp new file mode 100644 index 0000000..60b753f Binary files /dev/null and b/web/img/proj/game-hub-sm.webp differ diff --git a/web/img/proj/game-hub.webp b/web/img/proj/game-hub.webp new file mode 100644 index 0000000..0f3ad71 Binary files /dev/null and b/web/img/proj/game-hub.webp differ diff --git a/web/img/proj/game-sector3-md.webp b/web/img/proj/game-sector3-md.webp new file mode 100644 index 0000000..e0eab7b Binary files /dev/null and b/web/img/proj/game-sector3-md.webp differ diff --git a/web/img/proj/game-sector3-sm.webp b/web/img/proj/game-sector3-sm.webp new file mode 100644 index 0000000..9522625 Binary files /dev/null and b/web/img/proj/game-sector3-sm.webp differ diff --git a/web/img/proj/game-sector3.webp b/web/img/proj/game-sector3.webp new file mode 100644 index 0000000..7c93c8a Binary files /dev/null and b/web/img/proj/game-sector3.webp differ diff --git a/web/img/proj/game-splash-md.webp b/web/img/proj/game-splash-md.webp new file mode 100644 index 0000000..d6f5618 Binary files /dev/null and b/web/img/proj/game-splash-md.webp differ diff --git a/web/img/proj/game-splash-sm.webp b/web/img/proj/game-splash-sm.webp new file mode 100644 index 0000000..7e76a30 Binary files /dev/null and b/web/img/proj/game-splash-sm.webp differ diff --git a/web/img/proj/game-splash.webp b/web/img/proj/game-splash.webp new file mode 100644 index 0000000..d19217d Binary files /dev/null and b/web/img/proj/game-splash.webp differ diff --git a/web/img/proj/homelab-cover.svg b/web/img/proj/homelab-cover.svg new file mode 100644 index 0000000..faf735d --- /dev/null +++ b/web/img/proj/homelab-cover.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + файлы + игра + сайт + + + + + diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..ac683d5 --- /dev/null +++ b/web/index.html @@ -0,0 +1,54 @@ + + + + + +Аман Ибраш: давай общаться + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +

Привет

+

Аман Ибраш

+

Админю серверы, качаюсь, кручу педали на фиксе и рисую здания. Скучать некогда.

+
    +
      +
      + +
      +

      на связи

      +

      Давай общаться

      +

      Хочешь пообщаться? Пиши, отвечу в течение пары дней.

      + +
      +
      + + + + + diff --git a/web/js/boot.js b/web/js/boot.js new file mode 100644 index 0000000..69e553e --- /dev/null +++ b/web/js/boot.js @@ -0,0 +1,10 @@ +// Подключается в : помечает «JS работает», сразу запрашивает config.json и даёт общий сигнал готовности +(function () { + var root = document.documentElement; + root.className += ' js'; + window.siteReady = function () { root.classList.add('ready'); }; + setTimeout(window.siteReady, 2000); // страховка: страница не останется скрытой + window.siteConfig = fetch('/config.json') + .then(function (r) { if (!r.ok) throw 0; return r.json(); }) + .catch(function () { return null; }); +})(); diff --git a/web/js/card.js b/web/js/card.js new file mode 100644 index 0000000..0a668c7 --- /dev/null +++ b/web/js/card.js @@ -0,0 +1,52 @@ +// Заполняет страницу из config.json +(function () { + var $ = function (id) { return document.getElementById(id); }; + var make = function (tag, cls, text) { var n = document.createElement(tag); if (cls) n.className = cls; if (text) n.textContent = text; return n; }; + var safe = function (u) { return /^https?:\/\//i.test(u) ? u : '#'; }; + + window.siteConfig.then(function (c) { + if (!c) return; + if (c.name) { + $('name').textContent = c.name; + document.title = c.name + ': давай общаться'; + $('initials').textContent = c.name.split(/\s+/).map(function (w) { return w.charAt(0); }).join('').slice(0, 2).toUpperCase(); + } + if (c.status) $('status').textContent = c.status; + if (c.tagline) { + // текст раскладывается на слова: каждое появляется по очереди (стили в css, .w) + var tg = $('tagline'); tg.textContent = ''; + c.tagline.split(/\s+/).forEach(function (word, i, all) { + var w = make('span', 'w', word); w.style.setProperty('--i', i); tg.appendChild(w); + if (i < all.length - 1) tg.appendChild(document.createTextNode(' ')); + }); + } + (c.facts || []).forEach(function (f) { + var li = make('li'); li.appendChild(make('span', 'em', f.emoji || '')); li.appendChild(document.createTextNode(' ' + f.text)); + $('facts').appendChild(li); + }); + if (c.cta) $('cta').textContent = c.cta; + if (c.photo && /^\/[\w\-./]+$/.test(c.photo)) { + var img = document.createElement('img'); + img.src = c.photo; img.alt = ''; img.width = 480; img.height = 480; img.fetchPriority = 'high'; + $('avatar').textContent = ''; $('avatar').classList.add('has-photo'); $('avatar').appendChild(img); + } + (c.tags || []).forEach(function (t) { + var li = make('li'); + li.appendChild(make('span', 'em', t.emoji || '')); + li.appendChild(document.createTextNode(' ' + t.text)); + $('tags').appendChild(li); + }); + + var list = $('links'), mailLi = $('mail').parentNode; + (c.socials || []).forEach(function (s) { + var a = make('a', 'link'); + a.href = safe(s.url); a.target = '_blank'; a.rel = 'me noopener'; + a.appendChild(make('span', 'ic', (s.label || '?').charAt(0))); + a.appendChild(make('span', 'lk', s.label)); + a.appendChild(make('span', 'lh', s.handle || '')); + var li = make('li'); li.appendChild(a); + list.insertBefore(li, mailLi); + }); + if (c.email) { $('mail').href = 'mailto:' + c.email; $('mail').querySelector('.lh').textContent = c.email; } + }).then(window.siteReady); +})(); diff --git a/web/js/count.js b/web/js/count.js new file mode 100644 index 0000000..649bd15 --- /dev/null +++ b/web/js/count.js @@ -0,0 +1,20 @@ +// Числа в карточках проекта «набегают» от нуля, когда появляются на экране. Без JS и при «уменьшить движение» остаются как есть. +(function () { + var nodes = document.querySelectorAll('[data-count]'); + if (!nodes.length || !('IntersectionObserver' in window) || matchMedia('(prefers-reduced-motion: reduce)').matches) return; + var fmt = function (n, dec) { return n.toFixed(dec).replace('.', ','); }; + var run = function (el) { + var raw = el.getAttribute('data-count'), end = parseFloat(raw), dec = (raw.split('.')[1] || '').length; + if (isNaN(end)) return; + var t0 = performance.now(), dur = 900; + (function step(now) { + var k = Math.min(1, (now - t0) / dur), e = 1 - Math.pow(1 - k, 3); + el.textContent = fmt(end * e, dec); + if (k < 1) requestAnimationFrame(step); + })(t0); + }; + var io = new IntersectionObserver(function (items) { + items.forEach(function (it) { if (it.isIntersecting) { run(it.target); io.unobserve(it.target); } }); + }, { threshold: 0.6 }); + nodes.forEach(function (n) { io.observe(n); }); +})(); diff --git a/web/js/nav.js b/web/js/nav.js new file mode 100644 index 0000000..4de195d --- /dev/null +++ b/web/js/nav.js @@ -0,0 +1,31 @@ +// Верхнее меню из config.json: pages. На главной показывается, только если есть страницы; на остальных всегда. +(function () { + window.siteConfig.then(function (c) { + var nav = document.getElementById('topnav'); + if (nav && c) { + var pages = (c.pages || []).filter(function (p) { return !p.draft; }); // черновики в меню не попадают + var norm = function (p) { return p.replace(/\/+$/, '') + '/'; }; + var here = norm(location.pathname); + var home = here === '/'; + if (pages.length || !home) { + var brand = document.createElement('a'); + brand.className = 'brand'; brand.href = '/'; + var parts = (c.name || 'Главная').split(' '); + brand.appendChild(document.createTextNode(parts[0])); + if (parts.length > 1) { var rest = document.createElement('span'); rest.className = 'brand-rest'; rest.textContent = ' ' + parts.slice(1).join(' '); brand.appendChild(rest); } + nav.appendChild(brand); + var ul = document.createElement('ul'); + pages.forEach(function (p) { + if (!/^\/[\w\-./]*$/.test(p.url)) return; // только внутренние адреса + var a = document.createElement('a'); + a.className = 'pill'; a.href = p.url; a.textContent = p.label; + var u = norm(p.url); + if (u === here || (u !== '/' && here.indexOf(u) === 0)) a.setAttribute('aria-current', 'page'); + var li = document.createElement('li'); li.appendChild(a); ul.appendChild(li); + }); + nav.appendChild(ul); + document.body.classList.add('has-nav'); + } + } + }).then(window.siteReady); +})(); diff --git a/web/og/arch-exhibition-pavilion.jpg b/web/og/arch-exhibition-pavilion.jpg new file mode 100644 index 0000000..b8a598b Binary files /dev/null and b/web/og/arch-exhibition-pavilion.jpg differ diff --git a/web/og/arch-loft-complex.jpg b/web/og/arch-loft-complex.jpg new file mode 100644 index 0000000..c47b1fa Binary files /dev/null and b/web/og/arch-loft-complex.jpg differ diff --git a/web/og/arch-quiet-courtyard.jpg b/web/og/arch-quiet-courtyard.jpg new file mode 100644 index 0000000..fb85f08 Binary files /dev/null and b/web/og/arch-quiet-courtyard.jpg differ diff --git a/web/og/arch.jpg b/web/og/arch.jpg new file mode 100644 index 0000000..b8a598b Binary files /dev/null and b/web/og/arch.jpg differ diff --git a/web/og/home.png b/web/og/home.png new file mode 100644 index 0000000..d72447f Binary files /dev/null and b/web/og/home.png differ diff --git a/web/og/projects-amans-world.jpg b/web/og/projects-amans-world.jpg new file mode 100644 index 0000000..79d0d05 Binary files /dev/null and b/web/og/projects-amans-world.jpg differ diff --git a/web/og/projects-homelab.jpg b/web/og/projects-homelab.jpg new file mode 100644 index 0000000..b21226b Binary files /dev/null and b/web/og/projects-homelab.jpg differ diff --git a/web/og/projects.jpg b/web/og/projects.jpg new file mode 100644 index 0000000..ac8b941 Binary files /dev/null and b/web/og/projects.jpg differ diff --git a/web/projects/amans-world/index.html b/web/projects/amans-world/index.html new file mode 100644 index 0000000..8e396a0 --- /dev/null +++ b/web/projects/amans-world/index.html @@ -0,0 +1,81 @@ + + + + + +Aman's World: Аман Ибраш + + + + + + + + + + + + + + + + + + + + + + + +
      +

      ← Проекты

      +
      +
      • 3D
      • в браузере
      • только на ПК
      • Godot 4
      +

      Aman's World

      +

      Паркур и гонки в неоновом городе. Запускается прямо в браузере.

      +
      Играть

      Работает только на компьютере. Можно играть с геймпадом (Xbox).

      +
      + + + +
      • 5порталов в хабе
      • 8чекпоинтов, Sector 3 Run
      • 13чекпоинтов, Ascend Only
      • 40МБ при первом запуске
      + +
      +

      Игра, в которую можно зайти по ссылке. Без установки и без регистрации.

      +

      Неоновый хаб

      Ты начинаешь в хабе: зал с неоновыми колоннами, вращающейся скульптурой-атомом и порталами на карты. Над гоночными порталами висят таблицы рекордов. Перед входом вводишь ник до 16 символов.

      Паркур

      Движение собрано под паркур: бег, прыжок, двойной прыжок, рывок, скольжение, бег по стенам, прыжок от стены и подтягивание на уступы. На трассах лежат полосы-ускорители.

      Две гоночные трассы

      Sector 3 Run гонит по вечернему оранжевому городу, на трассе 8 чекпоинтов. Ascend Only ведёт всё выше по фиолетовому ночному городу, там 13 чекпоинтов. Перед стартом нажимаешь R, то есть «готов», а счётчик (0/1) показывает, сколько игроков готово. Время идёт, и побеждает самый быстрый.

      На чём играть

      Игра работает только на компьютере, на телефоне она не запустится. Управлять можно клавиатурой и мышью или геймпадом вроде Xbox.

      Песочница и Backrooms

      Ещё из хаба можно уйти в песочницу, где просто отрабатываешь движение, и на карту Backrooms.

      Под капотом

      Игру я сделал сам на движке Godot 4 и выложил на свой сервер. При первом запуске браузер загружает около 40 МБ, зато потом всё работает быстро.

      +
      + +
      +

      Кадры из игры

      + +
      + + +
      + + + + + diff --git a/web/projects/homelab/index.html b/web/projects/homelab/index.html new file mode 100644 index 0000000..b164bd2 --- /dev/null +++ b/web/projects/homelab/index.html @@ -0,0 +1,71 @@ + + + + + +Хоумлаб: Аман Ибраш + + + + + + + + + + + + + + + + + + + + + + + +
      +

      ← Проекты

      +
      +
      • дома
      • Linux
      • Fedora
      +

      Хоумлаб

      +

      Мой домашний сервер и Linux, на котором я всё делаю.

      +
      + + + +
      +

      Свой маленький интернет у меня дома.

      +

      Что это

      +

      Хоумлаб — это мой домашний сервер. Я работаю системным администратором и расту в сторону DevOps, поэтому держу дома место, где можно пробовать всё на настоящем железе.

      +

      Дома можно спокойно пробовать, ломать и чинить. Так я учусь автоматизации и всему, что окружает современный сервер.

      +
      + +
      +

      Что на нём живёт

      +
      +

      Nextcloud

      Мои файлы и проекты лежат у меня, а не в чужом облаке.

      +

      Aman's World

      Моя браузерная игра запускается с этого сервера.

      Смотреть игру →
      +

      DuckDNS

      Бесплатные адреса, чтобы до домашнего сервера можно было достучаться снаружи.

      +

      Этот сайт

      Он тоже переезжает сюда. Выкладку хочу автоматизировать через CI.

      +

      И много чего ещё

      Список растёт: я постоянно что-то поднимаю и пробую.

      +
      +
      + +
      +

      Fedora Linux

      +

      На своём компьютере я пользуюсь Fedora Linux. В ней я учусь, работаю и делаю свои проекты, в том числе этот сайт.

      +
      +
      + + + + + diff --git a/web/projects/index.html b/web/projects/index.html new file mode 100644 index 0000000..b938e3c --- /dev/null +++ b/web/projects/index.html @@ -0,0 +1,67 @@ + + + + + +Проекты: Аман Ибраш + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +

      Проекты

      +

      Что я делаю

      +

      Кроме учёбы и работы я собираю свои штуки. Выбери проект и загляни внутрь.

      +
      + +
      + +
      Aman's World: старт трассы Ascend Only
      +
      +
      • 3D
      • в браузере
      • только на ПК
      • Godot 4
      +

      Aman's World

      +

      Паркур и гонки в неоновом городе. Запускается прямо в браузере, ничего ставить не надо.

      + Смотреть проект +
      +
      + +
      Иллюстрация: домашний сервер с тремя сервисами
      +
      +
      • дома
      • Linux
      • Fedora
      +

      Хоумлаб

      +

      Мой домашний сервер: файлы, игра и эксперименты. И Linux, на котором я всё делаю.

      + Смотреть проект +
      +
      +
      + + +
      + + + + + diff --git a/web/robots.txt b/web/robots.txt new file mode 100644 index 0000000..54a57c2 --- /dev/null +++ b/web/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://example.com/sitemap.xml diff --git a/web/sitemap.xml b/web/sitemap.xml new file mode 100644 index 0000000..87663fb --- /dev/null +++ b/web/sitemap.xml @@ -0,0 +1,11 @@ + + + https://example.com/ + https://example.com/arch/ + https://example.com/projects/ + https://example.com/arch/quiet-courtyard/ + https://example.com/arch/exhibition-pavilion/ + https://example.com/arch/loft-complex/ + https://example.com/projects/amans-world/ + https://example.com/projects/homelab/ + diff --git a/web/training/index.html b/web/training/index.html new file mode 100644 index 0000000..4a83b43 --- /dev/null +++ b/web/training/index.html @@ -0,0 +1,40 @@ + + + + + +Тренировки: Аман Ибраш + + + + + + + + + + + + + + + + + + + + + +
      +
      +

      Тренировки

      +
      +

      Тут будет про тренировки: что делаю, как часто и во сколько встаю.

      +

      Страница в работе, скоро заполню. А пока вернись на главную.

      +
      +
      +
      + + + + diff --git a/web/work/index.html b/web/work/index.html new file mode 100644 index 0000000..cd53c6a --- /dev/null +++ b/web/work/index.html @@ -0,0 +1,40 @@ + + + + + +Работа: Аман Ибраш + + + + + + + + + + + + + + + + + + + + + +
      +
      +

      Работа

      +
      +

      Тут будет про работу: чем занимаюсь и куда расту.

      +

      Страница в работе, скоро заполню. А пока вернись на главную.

      +
      +
      +
      + + + +