14 Commits

Author SHA1 Message Date
VLADIMIR 7aaa4245c8 fix 2026-10-20 21:05:19 +07:00
VLADIMIR 4e21fdba4e up 2026-10-20 19:38:18 +07:00
VLADIMIR 00bd14bac1 up 2026-10-17 14:40:21 +07:00
VLADIMIR e9a6814ebb up 2026-10-17 14:31:40 +07:00
VLADIMIR 452694ff32 up 2026-10-17 14:26:10 +07:00
VLADIMIR ae0eba134a up 2026-10-17 14:21:16 +07:00
VLADIMIR 14ce58f303 up 2026-10-17 14:13:24 +07:00
VLADIMIR c03be07eab up 2026-10-11 16:05:08 +07:00
VLADIMIR ac1e3550db up 2026-10-10 22:27:52 +07:00
VLADIMIR 0fbb6988b0 fix 2026-10-04 03:53:16 +07:00
VLADIMIR cb77ff3abb fix 2026-10-04 03:38:08 +07:00
VLADIMIR 3e379d03a3 fix 2026-10-04 01:38:08 +07:00
VLADIMIR be685b888a fix 2026-10-03 17:22:32 +07:00
VLADIMIR 4623e0175c fix 2026-10-03 16:30:46 +07:00
18 changed files with 475 additions and 6 deletions
+9 -6
View File
@@ -3,6 +3,7 @@ services:
image: umputun/reproxy:master
container_name: reproxy
hostname: reproxy
restart: unless-stopped
ports:
- "80:8080"
- "443:8443"
@@ -15,16 +16,18 @@ services:
environment:
- TZ=Asia/Novosibirsk
- DOCKER_ENABLED=true
- STATIC_ENABLED=true
- STATIC_RULES=crabs-games.art,^/minecraft/api/readme$,https://git.3crabs.space/api/v1/repos/user-penguin/gmancraft/contents/README.md?ref=main;crabs-games.art,^/minecraft/api/status$,https://api.mcsrvstat.us/3/90.189.218.191:25565
- ASSETS_LOCATION=/web
- DEBUG=true
- SSL_TYPE=auto
- SSL_ACME_FQDN=crabs-games.art
- SSL_ACME_FQDN=crabs-games.art,mail.crabs-games.art,evening-detective-api.crabs-games.art,evening-detective.crabs-games.art
- SSL_ACME_EMAIL=admin@crabs-games.art
- SSL_ACME_LOCATION=/srv/var/ssl
- MAX_SIZE=150M
- HEADER=
X-Frame-Options:SAMEORIGIN,
X-XSS-Protection:1; mode=block;,
Content-Security-Policy:default-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' https://evening-detective-api.crabs-games.art; img-src 'self' https://evening-detective-files.crabs-games.art;
- MAX_SIZE=1G
- TIMEOUT_WRITE=1800s
- TIMEOUT_RESP_HEADER=1800s
- HEADER= X-Frame-Options:SAMEORIGIN, X-XSS-Protection:1; mode=block;, Content-Security-Policy:default-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' https://evening-detective-api.crabs-games.art; img-src 'self' https://evening-detective-api.crabs-games.art; media-src 'self' https://evening-detective-api.crabs-games.art;
networks:
crabs-network:
Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

+64
View File
@@ -0,0 +1,64 @@
const repository = 'https://git.3crabs.space/user-penguin/gmancraft';
function parseLatestBuild(readme) {
// shortcut: формат строки актуальной версии README, при его смене нужен явный manifest.
const line = readme.split('\n').find(line => line.startsWith('Актуальная версия'));
const match = line?.match(/^Актуальная версия\s*—\s*\*\*(\d+\.\d+\.\d+)\*\*:\s*\[Скачать \1\]\((dist\/minecraft-lab-neoforge-spike-\1\/minecraft-lab-neoforge-spike-\1\.mrpack)\)/);
if (!match) throw new Error('Не найдена актуальная сборка');
return { version: match[1], url: `${repository}/raw/branch/main/${match[2]}` };
}
async function loadLatestBuild() {
const statuses = document.querySelectorAll('.build-status');
const downloads = document.querySelectorAll('.build-download');
try {
const response = await fetch('/minecraft/api/readme', {
cache: 'no-store',
signal: AbortSignal.timeout(10000),
});
if (!response.ok) throw new Error('README недоступен');
const data = await response.json();
if (data.encoding !== 'base64' || typeof data.content !== 'string') {
throw new Error('Неизвестный формат README');
}
const readme = new TextDecoder().decode(Uint8Array.from(atob(data.content), char => char.charCodeAt(0)));
const build = parseLatestBuild(readme);
for (const status of statuses) {
status.textContent = `Актуальная версия — ${build.version}`;
}
for (const download of downloads) {
download.href = build.url;
download.textContent = `Скачать сборку ${build.version}`;
download.hidden = false;
}
} catch {
for (const status of statuses) {
status.textContent = 'Не удалось определить актуальную версию. Кнопка откроет репозиторий — выбери актуальную сборку в README.';
}
}
}
async function loadServerStatus() {
const status = document.getElementById('server-status');
try {
const response = await fetch('/minecraft/api/status', {
cache: 'no-store',
signal: AbortSignal.timeout(10000),
});
if (!response.ok) throw new Error('Статус недоступен');
const data = await response.json();
if (data?.online === false) {
status.textContent = 'Сервер недоступен';
} else if (data?.online === true && Number.isSafeInteger(data.players?.online) && data.players.online >= 0) {
status.textContent = `Игроков онлайн: ${data.players.online}`;
} else {
throw new Error('Неизвестный формат статуса');
}
} catch {
status.textContent = 'Не удалось получить онлайн';
}
}
loadLatestBuild();
loadServerStatus();
setInterval(loadServerStatus, 5 * 60 * 1000);
Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

+93
View File
@@ -0,0 +1,93 @@
Copyright 2012 The Press Start 2P Project Authors (cody@zone38.net), with Reserved Font Name "Press Start 2P".
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.
Binary file not shown.
+112
View File
@@ -0,0 +1,112 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="gman-world — уютная техническая сборка Minecraft: механизмы Create, аэронавтика и виноделие.">
<title>gman-world — Minecraft</title>
<link rel="stylesheet" href="/minecraft/style.css?v=20261020-equal-cards">
<script src="/minecraft/app.js?v=20261020-download-visible" defer></script>
<script src="/minecraft/motion.js?v=20261017-steam" defer></script>
</head>
<body>
<svg class="machine-definitions" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><defs>
<g id="cog"><path d="M42 2h16l3 12 11 5 12-6 11 11-6 12 5 11 12 3v16l-12 3-5 11 6 12-11 11-12-6-11 5-3 12H42l-3-12-11-5-12 6L5 92l6-12-5-11-12-3V50l12-3 5-11-6-12 11-11 12 6 11-5z" transform="translate(6 -6) scale(.9)" fill="#ae693a" stroke="#e2b878" stroke-width="3"/><circle cx="50" cy="50" r="27" fill="#302b23" stroke="#d3a361" stroke-width="7"/><path d="M50 24v52M24 50h52M32 32l36 36M32 68l36-36" stroke="#b67b44" stroke-width="8"/><circle cx="50" cy="50" r="10" fill="#e2b878" stroke="#634126" stroke-width="3"/></g>
<g id="wheel"><circle cx="50" cy="50" r="42" fill="#30281c" stroke="#c98b47" stroke-width="7"/><circle cx="50" cy="50" r="30" fill="none" stroke="#875b32" stroke-width="5"/><path d="M50 8v84M8 50h84M20 20l60 60M20 80l60-60" stroke="#c58b4b" stroke-width="6"/><path d="M38 4h24v10H38zM86 38h10v24H86zM38 86h24v10H38zM4 38h10v24H4zM13 13h20v12H13zM75 13h12v20H75zM67 75h20v12H67zM13 67h12v20H13z" fill="#815332" stroke="#dda25d" stroke-width="2"/><circle cx="50" cy="50" r="9" fill="#e6bb77" stroke="#563822" stroke-width="4"/></g>
<g id="propeller"><path d="M46 45C10 37 5 19 18 15c15-5 27 14 32 29C58 9 77 4 82 18c4 14-13 26-31 32 36 9 41 27 27 32-14 4-26-13-29-31-9 36-27 41-31 27-5-14 12-26 27-29z" fill="#cfb37a" stroke="#785232" stroke-width="3"/><circle cx="50" cy="50" r="9" fill="#b5703e" stroke="#f0c589" stroke-width="3"/></g>
</defs></svg>
<main>
<div class="side-machine side-machine-left" aria-hidden="true"><div class="chain-drive"></div><svg class="rotor side-cog" viewBox="0 0 100 100"><use href="#cog"/></svg><svg class="rotor reverse side-cog-small" viewBox="0 0 100 100"><use href="#cog"/></svg><div class="gauge"><i class="needle"></i></div></div>
<div class="side-machine side-machine-right" aria-hidden="true"><div class="pipe vertical-pipe"></div><div class="valve"></div><div class="gauge"><i class="needle"></i></div><div class="steam"><i></i><i></i><i></i></div></div>
<nav class="topbar" aria-label="Главная навигация">
<a class="brand" href="/">CRABS GAMES<span> / </span>GMAN-WORLD</a>
<div class="nav-actions"><a class="nav-link" href="#installation">Как зайти?</a><button id="motion-toggle" class="button" type="button" aria-pressed="false" hidden>Остановить механизмы</button></div>
</nav>
<header class="hero">
<div class="hero-machine" aria-hidden="true"><div class="pipe machine-pipe"></div><div class="gauge"><i class="needle"></i></div><svg class="rotor waterwheel" viewBox="0 0 100 100"><use href="#wheel"/></svg><svg class="rotor hero-cog" viewBox="0 0 100 100"><use href="#cog"/></svg><svg class="rotor reverse hero-cog-small" viewBox="0 0 100 100"><use href="#cog"/></svg><div class="steam"><i></i><i></i><i></i></div><span class="machine-caption">ВОДЯНОЙ ПРИВОД / CREATE</span></div>
<div class="hero-copy">
<p class="eyebrow">ТВОЯ МАСТЕРСКАЯ В MINECRAFT</p>
<h1>GMAN-WORLD</h1>
<p class="intro">Шестерёнки крутятся. Дирижабли взлетают. Виноград растёт. Залетай!</p>
<div class="join-panel">
<p class="server"><span>АДРЕС СЕРВЕРА</span><code>90.189.218.191:25565</code></p>
<p id="server-status" role="status">Проверяем онлайн…</p>
<a id="build-download" class="button build-download" href="https://git.3crabs.space/user-penguin/gmancraft">Скачать сборку</a>
<p id="build-status" class="hint build-status" role="status">Проверяем актуальную версию…</p>
<a class="button telemost" href="https://telemost.yandex.ru/j/76726739040099?skip_app=1" target="_blank" rel="noopener noreferrer">Зайти в Телемост</a>
<noscript><p>Без JavaScript кнопка скачивания откроет репозиторий. Выбери актуальную сборку в README.</p></noscript>
</div>
</div>
</header>
<div class="pipe pipe-bridge" aria-hidden="true"><span class="valve"></span><span class="pipe-joint"></span><div class="steam"><i></i><i></i><i></i></div></div>
<section class="world" aria-labelledby="world-title">
<div class="section-heading"><p class="eyebrow">ЗАШЁЛ НА ПЯТЬ МИНУТ</p><h2 id="world-title">Вот какой здесь вайб</h2></div>
<p class="world-intro">Механизмы Create, полёты и виноделье — повод собраться. А дальше у каждого найдётся своё «сейчас ещё одну штуку доделаю».</p>
<div class="world-grid">
<article class="theme">
<div class="scene"><img src="/minecraft/world-workshop.webp" width="1280" height="1169" loading="lazy" decoding="async" alt="Мастер в цехе склонился над чертежами механизмов."></div>
<div class="theme-copy"><p class="eyebrow">ПРИДУМЫВАЕМ</p><h3>По чертежу. Почти</h3><p>Зашёл поставить пару шестерёнок. Теперь у тебя цех, три схемы переделки и уверенность, что следующий конвейер точно будет последним.</p></div>
</article>
<article class="theme">
<div class="scene"><img src="/minecraft/world-waterwheel.webp" width="1280" height="1169" loading="lazy" decoding="async" alt="Мастер налаживает деревянное водяное колесо у реки."></div>
<div class="theme-copy"><p class="eyebrow">КРУТИМ</p><h3>Крутится — значит, работает</h3><p>Вода шумит, колесо вращается, мастерская оживает. Можно просто постоять и посмотреть. Но ты уже присматриваешь место для второго колеса.</p></div>
</article>
<article class="theme">
<div class="scene"><img src="/minecraft/world-building.webp" width="1280" height="1169" loading="lazy" decoding="async" alt="Улыбающийся строитель укладывает каменный блок на фоне храма и гор."></div>
<div class="theme-copy"><p class="eyebrow">СТРОИМ</p><h3>Ещё один блок — и домой</h3><p>С друзьями даже укладка камня превращается в приключение. Особенно когда строили сарай, а получился храм.</p></div>
</article>
<article class="theme">
<div class="scene"><img src="/minecraft/world-mine.webp" width="1280" height="861" loading="lazy" decoding="async" alt="Два шахтёра с фонарями на касках в тёмном подземном проходе."></div>
<div class="theme-copy"><p class="eyebrow">КОПАЕМ</p><h3>За углём точно алмазы</h3><p>Факелы взяли, кирки взяли, дорогу назад вроде запомнили. Наверху уже утро, но эту жилу надо докопать.</p></div>
</article>
<article class="theme">
<div class="scene"><img src="/minecraft/world-drilling.webp" width="1129" height="1280" loading="lazy" decoding="async" alt="Бурильщик стоит рядом с большой буровой установкой под открытым небом."></div>
<div class="theme-copy"><p class="eyebrow">МАСШТАБИРУЕМ</p><h3>Кирка ушла на пенсию</h3><p>Когда копать руками надоело, начинается инженерия. Бур побольше, склад пошире — теперь главное успевать за собственной машиной.</p></div>
</article>
<article class="theme">
<div class="scene"><img src="/minecraft/world-friendship.webp" width="800" height="800" loading="lazy" decoding="async" alt="Девушка гладит волка на фоне замка и поезда в мире Minecraft."></div>
<div class="theme-copy"><p class="eyebrow">ПРИРУЧАЕМ</p><h3>Зашёл за ресурсами — нашёл друга</h3><p>Пока механизмы работают, можно выдохнуть и погладить волка. Большие приключения подождут.</p></div>
</article>
</div>
</section>
<div class="pipe pipe-bridge lower-pipe" aria-hidden="true"><span class="valve"></span><span class="pipe-joint"></span></div>
<div id="installation" class="downloads">
<section aria-labelledby="build-title">
<p class="eyebrow">НОВЫЙ ИГРОК</p>
<h2 id="build-title">Первый заход</h2>
<p class="hint">Файл .mrpack · импорт через Prism Launcher</p>
<a id="installation-build-download" class="button build-download" href="https://git.3crabs.space/user-penguin/gmancraft">Скачать сборку</a>
<p id="installation-build-status" class="hint build-status" role="status">Проверяем актуальную версию…</p>
<ol>
<li>Скачай и установи <a href="https://github.com/Diegiwg/PrismLauncher-Cracked" target="_blank" rel="noopener noreferrer">Prism Launcher</a>.</li>
<li>Скачай актуальную сборку.</li>
<li>В Prism Launcher выбери <strong>Add Instance → Import</strong> и укажи скачанный .mrpack.</li>
<li>Дождись загрузки модов и запусти Minecraft.</li>
</ol>
</section>
<section aria-labelledby="transfer-title">
<p class="eyebrow">СОХРАНЯЕМ СВОЁ</p>
<h2 id="transfer-title">Обновляешь сборку?</h2>
<p>Карта и метки Xaero, закладки рецептов JEI — из старой сборки в новую.</p>
<a class="button secondary" href="https://git.3crabs.space/user-penguin/gmancraft/raw/branch/main/XaeroMapTransfer.exe">Скачать трансфер</a>
<p class="hint">XaeroMapTransfer.exe · Windows 10/11 · <a href="https://git.3crabs.space/user-penguin/gmancraft/raw/branch/main/XaeroMapTransfer.exe.sha256">SHA-256</a></p>
<ol>
<li>Закрой Minecraft и запусти утилиту.</li>
<li>Укажи старую папку игры слева, новую — справа.</li>
<li>Нажми <strong>«Перенести данные»</strong> и дождись завершения.</li>
</ol>
<p class="notice">После пересоздания серверного мира старую карту переносить не следует. Перед переносом проверь изменения версии в репозитории.</p>
</section>
</div>
<footer>
<a href="https://git.3crabs.space/user-penguin/gmancraft">Что нового? Репозиторий и инструкции ↗</a>
<span>gman-world / crabs-games</span>
</footer>
</main>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

+15
View File
@@ -0,0 +1,15 @@
const motionToggle = document.getElementById('motion-toggle');
const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
function setMotionPaused(isPaused) {
document.documentElement.classList.toggle('motion-running', !isPaused);
motionToggle.setAttribute('aria-pressed', String(isPaused));
motionToggle.textContent = isPaused ? 'Запустить механизмы' : 'Остановить механизмы';
}
setMotionPaused(reducedMotion.matches);
motionToggle.hidden = false;
motionToggle.addEventListener('click', () => {
setMotionPaused(document.documentElement.classList.contains('motion-running'));
});
reducedMotion.addEventListener('change', event => setMotionPaused(event.matches));
Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

+182
View File
@@ -0,0 +1,182 @@
@font-face {
font-family: 'Press Start 2P';
src: url('/minecraft/fonts/PressStart2P-Regular.ttf') format('truetype');
font-display: swap;
font-style: normal;
font-weight: 400;
}
:root {
color-scheme: dark;
font-family: system-ui, sans-serif;
color: #f6ecd4;
background: #26231e;
line-height: 1.65;
--pixel: 'Press Start 2P', monospace;
--muted: #ded3bd;
--copper: #e2ac69;
--edge: #191612;
}
* { box-sizing: border-box; }
html { scroll-padding-top: 24px; }
body { margin: 0; background-image: repeating-linear-gradient(0deg, #0000 0 31px, #0003 31px 34px), repeating-linear-gradient(90deg, #0000 0 63px, #ffffff04 63px 66px); }
main { max-width: 1440px; margin: auto; padding: 18px 16px 28px; }
a { color: #ffe3a0; text-underline-offset: 4px; }
a:hover { color: #fff; }
a:focus-visible { outline: 3px solid #ffdf61; outline-offset: 5px; }
.topbar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 18px; padding: 16px; background: repeating-linear-gradient(0deg, #0000 0 14px, #0002 14px 17px), #68482e; border: 4px solid var(--edge); box-shadow: inset 3px 3px #987048, inset -3px -3px #3d2a1b, 0 5px #0005; }
.brand { color: #fff0c8; font: 400 9px/1.8 var(--pixel); text-decoration: none; text-shadow: 2px 2px #302214; }
.brand span { color: #dba16b; padding: 0 6px; }
.nav-link { font: 400 10px/1.8 var(--pixel); }
.hero { position: relative; isolation: isolate; display: flex; align-items: center; min-height: 670px; margin: 20px 0 44px; padding: 32px 20px; border: 4px solid #191612; box-shadow: inset 0 0 0 3px #ba8b50, 6px 6px #0005; background: #3b4934 url('/minecraft/world-waterwheel.webp') 65% center / cover no-repeat; }
.hero::before { content: ''; position: absolute; inset: 0; z-index: -1; background: linear-gradient(90deg, #171a16ed, #171a16bb 45%, #171a1633); }
.hero-copy { width: 100%; max-width: 600px; }
.eyebrow { color: #f1bf73; font: 400 9px/1.9 var(--pixel); }
h1 { color: #f4e8cb; font: 400 clamp(23px, 4vw, 48px)/1.4 var(--pixel); letter-spacing: -1px; text-shadow: 3px 3px #5b422b, 6px 6px #181711; margin: 24px 0; }
h2 { font: 400 clamp(15px, 2vw, 22px)/1.7 var(--pixel); margin: 16px 0 24px; text-shadow: 2px 2px #0008; }
h3 { font: 400 14px/1.8 var(--pixel); margin: 14px 0; text-shadow: 2px 2px #0008; }
.intro { max-width: 460px; color: #fff3d9; font-size: 18px; margin-bottom: 24px; text-shadow: 1px 2px #111; }
.join-panel { max-width: 430px; background: #494b43f5; border: 4px solid #1c211b; padding: 20px 16px; box-shadow: inset 3px 3px #92927b, inset -3px -3px #30352b, 5px 5px #0005; }
.server { margin: 0; padding: 12px; border: 3px solid #252a22; background: #24291f; box-shadow: inset 2px 2px #10170f, inset -2px -2px #6d725e; }
.server span { display: block; color: #cabd9e; font: 400 8px/1.8 var(--pixel); margin-bottom: 7px; }
code { font: 600 17px/1.6 monospace; color: #fff3ca; overflow-wrap: anywhere; }
#server-status { color: #d2f2b0; font: 600 14px/1.6 monospace; margin: 12px 0 16px; }
.button, .nav-link, footer a { display: inline-block; color: #fff8e7; text-decoration: none; text-align: center; background: #72716a; border: 3px solid #191a16; padding: 13px 16px; box-shadow: inset 3px 3px #b9b7a7, inset -3px -3px #48483e, 3px 3px #0005; text-shadow: 2px 2px #222; }
.button { font: 400 10px/1.9 var(--pixel); }
.button:hover, .nav-link:hover, footer a:hover { background-color: #858b70; color: #ffffbf; box-shadow: inset 3px 3px #d4dab1, inset -3px -3px #535d43, 3px 3px #0005; }
.button:active, .nav-link:active, footer a:active { box-shadow: inset 3px 3px #414535, inset -2px -2px #a0ab83; transform: translate(2px, 2px); }
.button[hidden] { display: none; }
.build-download { display: block; background-color: #526f36; box-shadow: inset 3px 3px #8eac60, inset -3px -3px #2e4924, 3px 3px #0005; }
.build-download[hidden] { display: none; }
.build-download:hover { background-color: #657f42; }
.build-download:active { box-shadow: inset 3px 3px #2e4924, inset -2px -2px #8eac60; }
.telemost { display: block; }
.hint { font-size: 12px; color: var(--muted); overflow-wrap: anywhere; }
.build-status { margin: 12px 0; }
.world { margin-bottom: 34px; }
.world-intro { max-width: 740px; margin: 0 auto 28px; color: var(--muted); text-align: center; }
.section-heading { width: fit-content; max-width: 100%; margin: 0 auto 24px; padding: 16px 24px; text-align: center; border: 4px solid #241b13; background: repeating-linear-gradient(0deg, #0000 0 17px, #0002 17px 20px), #63462e; box-shadow: inset 3px 3px #9b7046, inset -3px -3px #39291d, 5px 5px #0004; }
.section-heading h2 { margin-bottom: 4px; }
.world-grid { display: grid; grid-auto-rows: 1fr; gap: 20px; }
.theme { min-width: 0; padding: 24px; border: 4px solid #33261b; background: #4d493d; box-shadow: inset 3px 3px #a48d67, inset -3px -3px #29271f, 4px 4px #0005; }
.theme-symbol { display: grid; width: 74px; height: 74px; place-items: center; border: 4px solid #25251f; background: #97917c; box-shadow: inset 3px 3px #5b5c4d, inset -3px -3px #c7bca0; }
.theme-symbol svg { image-rendering: pixelated; filter: drop-shadow(3px 3px #0005); }
.theme .eyebrow { margin-top: 24px; }
.theme p:last-child { color: #e7dfc9; font-size: 15px; margin-bottom: 0; }
.aviation { background: #3e4c46; }
.winery { background: #514044; }
.downloads { display: grid; gap: 24px; }
.downloads section { min-width: 0; border: 4px solid #1c1d19; background: repeating-linear-gradient(0deg, #ffffff03 0 3px, #0000 3px 8px), #53554d; padding: 24px 20px; box-shadow: inset 4px 4px #97998a, inset -4px -4px #30332c, 5px 5px #0005; }
.downloads section > p { color: var(--muted); }
.downloads section > .eyebrow { color: #f1bf73; }
ol { padding-left: 22px; margin-top: 24px; }
li { padding-left: 4px; margin-bottom: 14px; }
li::marker { color: #f5c982; font-weight: 800; }
.notice { font-size: 13px; border-left: 4px solid #e7b05f; background: #272b23; padding: 14px; margin-top: 26px; }
footer { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 20px; font-size: 12px; padding: 24px 0 4px; margin-top: 16px; }
footer span { color: #c5c1ac; font-family: monospace; }
@media (min-width: 700px) {
main { padding: 24px 32px 32px; }
.brand { font-size: 12px; }
.hero { padding: 48px; }
.world-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.downloads { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.downloads section { padding: 28px; }
}
@media (min-width: 1100px) {
.hero { min-height: 720px; padding: 56px; }
.hero::before { background: linear-gradient(90deg, #151a14e8, #151a14a3 43%, #151a140d 75%); }
}
/* Copper fittings and moving machinery are decorative layers, clear of controls. */
.machine-definitions { position: absolute; width: 0; height: 0; overflow: hidden; }
main { position: relative; }
body { background-color: #201d19; }
.nav-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
button { cursor: pointer; }
#motion-toggle { font-size: 8px; padding: 12px; }
.topbar, .join-panel, .theme, .downloads section { position: relative; border-color: #3e281a; box-shadow: inset 3px 3px #bc8c56, inset -3px -3px #573820, 5px 5px #0007; }
.topbar::after, .join-panel::after, .theme::after, .downloads section::after { content: ''; position: absolute; inset: 7px; pointer-events: none; background: radial-gradient(circle at 4px 4px, #ffe0a4 0 1px, #a66a3c 2px 3px, #302116 4px, transparent 5px) top left / 10px 10px no-repeat, radial-gradient(circle at 4px 4px, #ffe0a4 0 1px, #a66a3c 2px 3px, #302116 4px, transparent 5px) top right / 10px 10px no-repeat, radial-gradient(circle at 4px 4px, #e5bd79 0 1px, #8a512d 2px 3px, #302116 4px, transparent 5px) bottom left / 10px 10px no-repeat, radial-gradient(circle at 4px 4px, #e5bd79 0 1px, #8a512d 2px 3px, #302116 4px, transparent 5px) bottom right / 10px 10px no-repeat; }
.hero { overflow: hidden; }
.hero-copy { position: relative; z-index: 2; }
.join-panel { background: linear-gradient(125deg, #544536f5, #282e28fa); border-width: 6px; }
.hero-machine { position: absolute; right: 3%; bottom: 24px; width: 350px; height: 300px; pointer-events: none; z-index: 1; background: linear-gradient(0deg, #141815cc, transparent); }
.rotor { animation: machine-turn 28s linear infinite; transform-origin: center; transform-box: fill-box; filter: drop-shadow(4px 5px 2px #0009); }
.reverse { animation-direction: reverse; }
.waterwheel { position: absolute; width: 230px; height: 230px; right: 12px; bottom: 20px; animation-duration: 38s; }
.hero-cog { position: absolute; width: 100px; height: 100px; left: 6px; bottom: 20px; }
.hero-cog-small { position: absolute; width: 70px; height: 70px; left: 68px; bottom: 92px; animation-duration: 19.6s; }
.machine-caption { position: absolute; right: 8px; bottom: 0; font: 8px/1.6 var(--pixel); color: #e9bd7a; text-shadow: 2px 2px #000; }
.pipe { position: relative; height: 24px; border: 3px solid #492c1b; background: linear-gradient(#634124, #d5a067 24%, #a96538 50%, #5f3c26 85%, #9e6439); box-shadow: 0 5px #0005, inset 0 1px #f8cf96; pointer-events: none; }
.pipe-bridge { margin: -20px 18px 32px; }
.pipe-joint, .pipe::before, .pipe::after { content: ''; position: absolute; top: -8px; height: 34px; width: 14px; border: 3px solid #492e1d; background: linear-gradient(90deg, #9a6038, #e5b57d 50%, #6d4026); box-shadow: 2px 2px #0004; }
.pipe::before { left: 12%; }
.pipe::after { right: 12%; }
.pipe-joint { left: 65%; }
.valve { position: absolute; left: 32%; top: -20px; width: 44px; height: 44px; border: 6px solid #ae633c; border-radius: 50%; background: radial-gradient(circle, #d7a16a 0 4px, transparent 5px), linear-gradient(90deg, transparent 43%, #ae633c 43% 57%, transparent 57%), linear-gradient(transparent 43%, #ae633c 43% 57%, transparent 57%); box-shadow: 2px 3px #281a12; }
.machine-pipe { position: absolute; left: 0; right: 0; top: 48px; }
.gauge { position: relative; width: 70px; height: 70px; border: 7px solid #a57040; border-radius: 50%; background: repeating-conic-gradient(from -120deg, #5a4630 0deg 3deg, transparent 3deg 30deg), #ddcea6; box-shadow: inset 0 0 0 5px #eedcb4, 0 0 0 3px #35261b, 4px 5px #0005; }
.gauge::after { content: ''; position: absolute; width: 10px; height: 10px; border-radius: 50%; background: #73432a; left: calc(50% - 5px); top: calc(50% - 5px); }
.needle { position: absolute; width: 24px; height: 3px; left: 50%; top: calc(50% - 1px); background: #8a3429; transform-origin: left center; animation: pressure 7s ease-in-out infinite alternate; }
.hero-machine > .gauge { position: absolute; top: 0; left: 95px; }
.steam { position: absolute; width: 80px; height: 130px; bottom: 35px; right: 16px; pointer-events: none; }
.steam i { position: absolute; width: 30px; height: 30px; left: 20px; bottom: 0; border-radius: 35%; background: #f0e7cd; opacity: 0; animation: steam-rise 5s ease-out infinite; }
.steam i:nth-child(2) { animation-delay: -1.7s; left: 10px; }
.steam i:nth-child(3) { animation-delay: -3.4s; left: 30px; }
.hero-machine > .steam { top: -100px; left: 0; }
.pipe-bridge > .steam { bottom: 10px; right: 22%; }
.world-grid { gap: 24px; }
.theme { padding: 18px; background-image: linear-gradient(135deg, #9c69451a, #0002); }
.scene { position: relative; margin: -2px -2px 22px; border: 3px solid #291e15; box-shadow: 0 0 0 2px #ad794b; overflow: hidden; }
.scene img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; }
.card-rotor { position: absolute; width: 72px; height: 72px; bottom: 8px; right: 10px; background: #29261caa; border: 3px solid #a77a44; border-radius: 50%; }
.aviation .card-rotor { animation-duration: 4s; }
.theme-symbol { width: 52px; height: 52px; border-width: 3px; float: right; margin: 0 0 10px 10px; }
.theme-symbol svg { width: 36px; height: 36px; }
.theme .eyebrow { margin-top: 0; padding-top: 12px; }
.theme h3 { clear: both; }
.lower-pipe { margin-top: 46px; margin-bottom: 40px; }
.downloads section { background-color: #454b43; border-width: 6px; }
.side-machine { display: none; position: absolute; top: 240px; width: 75px; height: 570px; pointer-events: none; }
.side-machine-left { left: -64px; }
.side-machine-right { right: -64px; }
.side-cog { position: absolute; width: 85px; height: 85px; top: 80px; left: -12px; }
.side-cog-small { position: absolute; width: 58px; height: 58px; top: 146px; left: 26px; animation-duration: 19s; }
.side-machine-left > .gauge { position: absolute; top: 260px; left: 0; }
.chain-drive { position: absolute; top: 0; left: 17px; height: 430px; width: 35px; border: 7px solid #82552f; border-radius: 22px; background: repeating-linear-gradient(#b8a07b 0 6px, #292821 6px 14px); background-size: 100% 14px; box-shadow: inset 0 0 0 5px #211b15, 3px 3px #0007; animation: chain-run 1.5s linear infinite; }
.vertical-pipe { position: absolute; left: 20px; top: 30px; width: 24px; height: 500px; background: linear-gradient(90deg, #634124, #d5a067 24%, #a96538 50%, #5f3c26 85%, #9e6439); }
.vertical-pipe::before, .vertical-pipe::after { width: 36px; height: 14px; left: -9px; }
.vertical-pipe::before { top: 80px; }
.vertical-pipe::after { top: 410px; }
.side-machine-right > .valve { top: 100px; left: 10px; }
.side-machine-right > .gauge { top: 210px; left: 0; }
.side-machine-right > .steam { top: -90px; right: 0; }
@keyframes machine-turn { to { transform: rotate(360deg); } }
@keyframes pressure { from { transform: rotate(-130deg); } to { transform: rotate(-50deg); } }
@keyframes steam-rise { 0% { opacity: 0; transform: translate(0, 0) scale(.4); } 15% { opacity: .4; } 100% { opacity: 0; transform: translate(15px, -120px) scale(2.2); } }
@keyframes chain-run { to { background-position-y: -28px; } }
.rotor, .needle, .steam i, .chain-drive { animation-play-state: paused; }
html.motion-running .rotor, html.motion-running .needle, html.motion-running .steam i, html.motion-running .chain-drive { animation-play-state: running; }
@media (max-width: 1099px) {
.hero-machine { width: 180px; height: 170px; right: 12px; bottom: 8px; opacity: .85; }
.waterwheel { width: 130px; height: 130px; right: 0; bottom: 15px; }
.hero-cog { width: 54px; height: 54px; left: 0; bottom: 10px; }
.hero-cog-small { width: 38px; height: 38px; left: 31px; bottom: 48px; }
.hero-machine > .gauge, .hero-machine > .machine-pipe, .machine-caption { display: none; }
.hero-machine > .steam { top: 0; }
.hero { padding-bottom: 170px; }
}
@media (max-width: 699px) {
.steam i:nth-child(3), .pipe-bridge > .steam { display: none; }
.pipe-bridge { margin-left: 8px; margin-right: 8px; }
.scene { margin-bottom: 18px; }
#motion-toggle { font-size: 7px; }
}
@media (min-width: 1580px) { .side-machine { display: block; } }
@media (prefers-reduced-motion: reduce) {
html:not(.motion-running) .rotor, html:not(.motion-running) .needle, html:not(.motion-running) .steam i, html:not(.motion-running) .chain-drive { animation: none; }
.needle { transform: rotate(-90deg); }
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 485 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB