@underpostnet/cyberia 3.3.73 → 3.3.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +17 -5
- package/.github/workflows/coverall.cyberia.ci.yml +10 -0
- package/.github/workflows/docker-image.cyberia-client.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-server.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +1 -1
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +1 -1
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +1 -1
- package/.github/workflows/engine-cyberia.ci.yml +1 -1
- package/.github/workflows/publish.cyberia.ci.yml +44 -0
- package/AGENTS.md +61 -12
- package/CHANGELOG.md +159 -161
- package/CLI-HELP.md +20 -13
- package/Dockerfile +1 -1
- package/Dockerfile.dev +1 -1
- package/Dockerfile.test +2 -2
- package/README.md +28 -28
- package/bin/build.js +96 -21
- package/bin/cyberia.js +1247 -1373
- package/bin/index.js +1247 -1373
- package/compose.env +17 -5
- package/conf.js +7 -4
- package/deploy/cyberia-client/deploy.sh +10 -11
- package/deploy/cyberia-client/package.sh +0 -1
- package/deploy/cyberia-client/state.sh +3 -2
- package/deploy/cyberia-server/deploy.sh +10 -11
- package/deploy/cyberia-server/package.sh +0 -1
- package/deploy/cyberia-server/state.sh +3 -2
- package/deploy/dd-cyberia/deploy.sh +5 -5
- package/deploy/dd-cyberia/init.sh +15 -14
- package/deploy/dd-cyberia/package.sh +0 -1
- package/deploy/dd-cyberia/state.sh +2 -2
- package/deploy/dd-cyberia/sync-deploy.sh +139 -75
- package/deploy/lib/config.sh +29 -0
- package/deploy/lib/github-actions-logging.sh +92 -76
- package/deploy/lib/host.sh +28 -19
- package/deploy/lib/state.sh +4 -1
- package/deploy/pwa-microservices-template/deploy.sh +10 -8
- package/deploy/release/deploy.sh +0 -2
- package/deployment.yaml +1 -1
- package/docker-compose.yml +23 -17
- package/docs/coverage/cyberia/api/cyberia-instance/cyberia-fallback-capture.js.html +1591 -0
- package/docs/coverage/cyberia/api/cyberia-instance/index.html +116 -0
- package/docs/coverage/cyberia/api/cyberia-server-defaults/cyberia-server-defaults.js.html +4138 -0
- package/docs/coverage/cyberia/api/cyberia-server-defaults/index.html +116 -0
- package/docs/coverage/cyberia/api/object-layer/index.html +116 -0
- package/docs/coverage/cyberia/api/object-layer/object-layer.model.js.html +1393 -0
- package/docs/coverage/cyberia/base.css +362 -0
- package/docs/coverage/cyberia/block-navigation.js +82 -0
- package/docs/coverage/cyberia/favicon.png +0 -0
- package/docs/coverage/cyberia/index.html +161 -0
- package/docs/coverage/cyberia/prettify.css +101 -0
- package/docs/coverage/cyberia/prettify.js +937 -0
- package/docs/coverage/cyberia/projects/cyberia/atlas-sprite-sheet-generator.js.html +1315 -0
- package/docs/coverage/cyberia/projects/cyberia/index.html +176 -0
- package/docs/coverage/cyberia/projects/cyberia/instance-backup.js.html +715 -0
- package/docs/coverage/cyberia/projects/cyberia/instance-data.js.html +2182 -0
- package/docs/coverage/cyberia/projects/cyberia/shape-generator.js.html +3265 -0
- package/docs/coverage/cyberia/projects/cyberia/stat-balance.js.html +328 -0
- package/docs/coverage/cyberia/sort-arrow-sprite.png +0 -0
- package/docs/coverage/cyberia/sorter.js +205 -0
- package/hardhat/package-lock.json +2 -2
- package/hardhat/package.json +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +1 -1
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/mongo-express/deployment.yaml +46 -27
- package/manifests/deployment/mongo-express/kustomization.yaml +9 -0
- package/manifests/deployment/mongo-express/mongo-express-nodeport.yaml +18 -0
- package/manifests/deployment/mongo-express/service.yaml +16 -0
- package/manifests/deployment/mongo-express-no-auth/disable-mongodb-auth.yaml +16 -0
- package/manifests/deployment/mongo-express-no-auth/kustomization.yaml +16 -0
- package/package.json +19 -17
- package/scripts/test-monitor.sh +3 -3
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +14 -9
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +36 -5
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +88 -184
- package/src/api/cyberia-action/cyberia-action.router.js +2 -2
- package/src/api/cyberia-action/cyberia-action.service.js +3 -4
- package/src/api/cyberia-audio/cyberia-audio.controller.js +6 -0
- package/src/api/cyberia-audio/cyberia-audio.model.js +61 -0
- package/src/api/cyberia-audio/cyberia-audio.router.js +21 -0
- package/src/api/cyberia-audio/cyberia-audio.service.js +220 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -3
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +10 -19
- package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +12 -34
- package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +15 -32
- package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
- package/src/api/cyberia-entity/cyberia-entity.model.js +2 -0
- package/src/api/cyberia-entity/cyberia-entity.service.js +1 -1
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +8 -2
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +31 -20
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +16 -1
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +466 -3
- package/src/api/cyberia-instance/cyberia-fallback-capture.js +87 -51
- package/src/api/cyberia-instance/cyberia-fallback-world.js +21 -40
- package/src/api/cyberia-instance/cyberia-instance-boot.service.js +5 -5
- package/src/api/cyberia-instance/cyberia-instance-items.js +98 -0
- package/src/api/cyberia-instance/cyberia-instance-map.service.js +34 -141
- package/src/api/cyberia-instance/cyberia-instance.controller.js +1 -4
- package/src/api/cyberia-instance/cyberia-instance.model.js +0 -14
- package/src/api/cyberia-instance/cyberia-instance.router.js +3 -16
- package/src/api/cyberia-instance/cyberia-instance.service.js +3 -43
- package/src/api/cyberia-instance/cyberia-world-generator.js +8 -13
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +49 -75
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +74 -1
- package/src/api/cyberia-map/cyberia-map.service.js +6 -2
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.controller.js +31 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.model.js +56 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.router.js +33 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +189 -0
- package/src/api/cyberia-quest/cyberia-quest.router.js +3 -3
- package/src/api/cyberia-quest/cyberia-quest.service.js +3 -3
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +1 -1
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +403 -188
- package/src/api/cyberia-skill/cyberia-skill.model.js +6 -6
- package/src/api/file/file.ref.js +153 -0
- package/src/api/file/file.ref.json +11 -2
- package/src/api/object-layer/object-layer.model.js +47 -12
- package/src/api/object-layer/object-layer.router.js +10 -3
- package/src/api/object-layer/object-layer.service.js +113 -120
- package/src/cli/app.js +12 -1
- package/src/cli/client.js +2 -2
- package/src/cli/cluster.js +80 -18
- package/src/cli/deploy.js +215 -34
- package/src/cli/docker-compose.js +1 -1
- package/src/cli/fs-selection.js +115 -0
- package/src/cli/fs.js +158 -342
- package/src/cli/index.js +22 -10
- package/src/cli/release.js +3 -1
- package/src/cli/repository.js +35 -9
- package/src/cli/run.js +80 -199
- package/src/cli/test.js +4 -0
- package/src/cli/wireguard.js +32 -31
- package/src/client/components/core/Badge.js +0 -1
- package/src/client/components/core/CssCore.js +8 -1
- package/src/client/components/core/Docs.js +77 -100
- package/src/client/components/core/Modal.js +77 -23
- package/src/client/components/core/ToolTip.js +15 -0
- package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +0 -2
- package/src/client/components/cyberia/ActionEngineCyberia.js +12 -36
- package/src/client/components/cyberia/EntityEngineCyberia.js +492 -121
- package/src/client/components/cyberia/InstanceEngineCyberia.js +129 -160
- package/src/client/components/cyberia/InstanceSelectionView.js +7 -1
- package/src/client/components/cyberia/MapEngineCyberia.js +175 -69
- package/src/client/components/cyberia/ObjectLayerEngine.js +1011 -97
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +81 -155
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +113 -98
- package/src/client/components/cyberia/SharedDefaultsCyberia.js +215 -56
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +0 -34
- package/src/client/components/cyberia-portal/CssCyberiaPortal.js +1 -1
- package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +712 -76
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +0 -4
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +0 -4
- package/src/client/components/default/AppShellDefault.js +1 -4
- package/src/client/components/itemledger/AppShellItemledger.js +0 -2
- package/src/client/components/underpost/AppShellUnderpost.js +1 -1
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +15 -15
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +280 -24
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +51 -29
- package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +52 -53
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +16 -11
- package/src/client/public/cyberia-docs/CYBERIA.md +28 -28
- package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +11 -5
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/STATS-PROGRESSION.md +217 -0
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +20 -12
- package/src/client/services/cyberia-audio/cyberia-audio.service.js +99 -0
- package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +38 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +5 -4
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +0 -40
- package/src/client/services/cyberia-map/cyberia-map.management.js +5 -4
- package/src/client/services/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +138 -0
- package/src/client/services/object-layer/object-layer.management.js +6 -10
- package/src/client/services/object-layer/object-layer.service.js +3 -1
- package/src/client/ssr/views/Cyberia404.js +3 -3
- package/src/client-builder/client-build-docs.js +52 -52
- package/src/client-builder/client-build.js +67 -44
- package/src/client-builder/client-bundle.js +347 -0
- package/src/db/mongo/MongoExpress.js +187 -0
- package/src/grpc/cyberia/grpc-server.js +5 -7
- package/src/index.js +1 -1
- package/src/projects/cyberia/atlas-sprite-sheet-generator.js +143 -146
- package/src/projects/cyberia/atlas-sprite-sheet-store.js +391 -0
- package/src/projects/cyberia/besu-genesis-generator.js +5 -14
- package/src/projects/cyberia/boot-contract-fixtures.js +32 -0
- package/src/projects/cyberia/catalog-cyberia.js +3 -2
- package/src/projects/cyberia/gemini-client.js +3 -3
- package/src/projects/cyberia/generate-saga.js +4 -11
- package/src/projects/cyberia/hot-reload-trigger.js +17 -28
- package/src/projects/cyberia/instance-backup.js +210 -0
- package/src/projects/cyberia/instance-data.js +177 -257
- package/src/projects/cyberia/map-preview-generator.js +32 -68
- package/src/projects/cyberia/object-layer.js +152 -319
- package/src/projects/cyberia/seed-audio.js +139 -0
- package/src/projects/cyberia/semantic-layer-generator-skin.js +2 -8
- package/src/projects/cyberia/semantic-layer-generator.js +2 -18
- package/src/projects/cyberia/stat-balance.js +81 -0
- package/src/projects/cyberia/stat-commands.js +23 -0
- package/src/projects/cyberia/stat-contract-generator.js +101 -0
- package/src/runtime/cyberia-client/Dockerfile +8 -4
- package/src/runtime/cyberia-client/Dockerfile.dev +5 -2
- package/src/runtime/cyberia-server/Dockerfile +9 -2
- package/src/runtime/cyberia-server/Dockerfile.dev +8 -1
- package/src/runtime/engine-cyberia/Dockerfile +1 -1
- package/src/runtime/engine-cyberia/Dockerfile.dev +1 -1
- package/src/runtime/engine-cyberia/Dockerfile.test +2 -2
- package/src/runtime/engine-cyberia/compose.env +17 -5
- package/src/runtime/engine-cyberia/docker-compose.yml +23 -17
- package/src/server/build/coverage.js +113 -42
- package/src/server/build/package.js +55 -12
- package/src/server/build/testing.js +59 -9
- package/src/server/network/middlewares.js +8 -2
- package/src/server/network/underpost-gateway.js +113 -14
- package/src/server/ops/logger.js +70 -35
- package/src/server/runtime/conf.js +40 -9
- package/src/server/runtime/start.js +11 -2
- package/src/server/storage/data-query.js +16 -0
- package/src/server/storage/downloader.js +62 -14
- package/src/server/storage/zip.js +153 -0
- package/test/integration/app/cyberia/atlas-sprite-sheet-store.test.js +402 -0
- package/test/integration/app/cyberia/atlas-sprite-sheet.test.js +86 -0
- package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +34 -0
- package/test/integration/app/cyberia/cyberia-entity-type-default.test.js +842 -0
- package/test/integration/app/cyberia/cyberia-instance-conf-coerce.test.js +53 -0
- package/test/integration/app/cyberia/cyberia-instance-conf-defaults.test.js +16 -22
- package/test/integration/app/cyberia/cyberia-instance-items.test.js +90 -0
- package/test/integration/app/cyberia/cyberia-load.test.js +6 -7
- package/test/integration/app/cyberia/cyberia-map-audio-conf.test.js +256 -0
- package/test/integration/app/cyberia/cyberia-stats.test.js +150 -0
- package/test/integration/app/cyberia/fallback-world-capture.test.js +87 -2
- package/test/integration/app/cyberia/object-layer-item-selection.test.js +56 -0
- package/test/integration/app/cyberia/object-layer-natural-key.test.js +32 -0
- package/test/integration/app/cyberia/seed-audio.test.js +275 -0
- package/test/integration/infra/2-network/wireguard-cli.test.js +33 -9
- package/test/integration/infra/2-network/wireguard-edge.test.js +58 -18
- package/test/integration/infra/3-cluster/mongo-express-deploy.test.js +156 -0
- package/test/integration/infra/4-ingress/api-cross-origin.test.js +79 -0
- package/test/integration/infra/4-ingress/underpost-ingress.test.js +1 -1
- package/test/unit/catalog.test.js +13 -2
- package/test/unit/client-build-docs.test.js +97 -70
- package/test/unit/client-bundle.test.js +313 -0
- package/test/unit/conf-resolution.test.js +58 -7
- package/test/unit/coverage-artifact.test.js +120 -29
- package/test/unit/cyberia/instance-backup.test.js +183 -0
- package/test/unit/cyberia/instance-data.test.js +314 -0
- package/test/unit/cyberia/instance-object-layer-items.test.js +46 -0
- package/test/unit/cyberia/publish-workflow.test.js +22 -0
- package/test/unit/cyberia/stat-balance.test.js +69 -0
- package/test/unit/deploy-legacy-gateway-sweep.test.js +63 -0
- package/test/unit/deploy-log-table.test.js +76 -5
- package/test/unit/downloader-integrity.test.js +98 -0
- package/test/unit/file-reference-registry.test.js +141 -0
- package/test/unit/fs-storage-paths.test.js +596 -0
- package/test/unit/gateway-pod-fetch.test.js +95 -0
- package/test/unit/letsencrypt-issuer.test.js +101 -0
- package/test/unit/logger-redaction.test.js +29 -0
- package/test/unit/package.test.js +70 -16
- package/test/unit/prepare-host.test.js +84 -1
- package/test/unit/release-bump.test.js +3 -1
- package/test/unit/start-options.test.js +12 -0
- package/test/unit/test-tiers.test.js +27 -0
- package/test/unit/zip-archive.test.js +142 -0
- package/vitest.config.js +11 -3
- package/src/api/cyberia-instance/cyberia-fallback-default-items.js +0 -63
- package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +0 -368
|
@@ -1,23 +1,189 @@
|
|
|
1
|
+
// MainBodyCyberiaPortal.js — Cyberia portal landing.
|
|
2
|
+
//
|
|
3
|
+
// A "landing-style" world selector: the hero keeps the pixel-art particle
|
|
4
|
+
// field, grid, logo and title, and instead of a background video it surfaces
|
|
5
|
+
// the first three playable worlds from the same registry the full
|
|
6
|
+
// InstanceSelectionView consumes (`defaultInstanceProvider`, limited to 3).
|
|
7
|
+
//
|
|
8
|
+
// Each world is a space-framed thumbnail card (starfield frame + rotating
|
|
9
|
+
// beam) with a staggered pixel-step entrance and its own float loop. Clicking a
|
|
10
|
+
// card selects it: the selected world's thumbnail becomes the blurred hero
|
|
11
|
+
// backdrop and the single CTA enters that world directly. The full selector
|
|
12
|
+
// modal stays reachable through the secondary "browse all worlds" link.
|
|
13
|
+
|
|
1
14
|
import { range } from '../core/CommonJs.js';
|
|
2
15
|
import { ThemeEvents, darkTheme } from '../core/Css.js';
|
|
3
16
|
import { EventsUI } from '../core/EventsUI.js';
|
|
17
|
+
import { NotificationManager } from '../core/NotificationManager.js';
|
|
4
18
|
import { getProxyPath } from '../core/Router.js';
|
|
5
19
|
import { htmls, s } from '../core/VanillaJs.js';
|
|
20
|
+
import {
|
|
21
|
+
defaultInstanceProvider,
|
|
22
|
+
placeholderThumbnail,
|
|
23
|
+
DEFAULT_CLIENT_BASE_URL,
|
|
24
|
+
STATUS_META,
|
|
25
|
+
} from '../cyberia/InstanceSelectionView.js';
|
|
26
|
+
|
|
27
|
+
const LANDING_WORLD_LIMIT = 3;
|
|
28
|
+
|
|
29
|
+
const statusMeta = (status) => STATUS_META[status] || STATUS_META.offline;
|
|
30
|
+
|
|
31
|
+
const escapeHtml = (value = '') =>
|
|
32
|
+
String(value).replace(
|
|
33
|
+
/[&<>"']/g,
|
|
34
|
+
(c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c],
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const worldCard = (instance, index, selected) => {
|
|
38
|
+
const meta = statusMeta(instance.status);
|
|
39
|
+
return html`
|
|
40
|
+
<button
|
|
41
|
+
class="hero-world hero-world-${index} ${selected ? 'hero-world-selected' : ''} ${
|
|
42
|
+
instance.playable ? '' : 'hero-world-unavailable'
|
|
43
|
+
}"
|
|
44
|
+
data-instance-id="${escapeHtml(instance.id)}"
|
|
45
|
+
type="button"
|
|
46
|
+
title="${escapeHtml(instance.name)}"
|
|
47
|
+
style="--world-index: ${index};"
|
|
48
|
+
>
|
|
49
|
+
<span class="hero-world-frame"></span>
|
|
50
|
+
<span class="hero-world-inner">
|
|
51
|
+
<img
|
|
52
|
+
class="hero-world-img"
|
|
53
|
+
src="${escapeHtml(instance.thumbnailUrl)}"
|
|
54
|
+
alt="${escapeHtml(instance.name)}"
|
|
55
|
+
loading="eager"
|
|
56
|
+
onerror="this.onerror=null;this.src='${placeholderThumbnail()}';"
|
|
57
|
+
/>
|
|
58
|
+
<span class="hero-world-status hero-tone-${meta.tone}">
|
|
59
|
+
<span class="hero-world-dot"></span>${meta.label}
|
|
60
|
+
</span>
|
|
61
|
+
<span class="hero-world-check"><i class="fa-solid fa-check"></i></span>
|
|
62
|
+
<span class="hero-world-caption">
|
|
63
|
+
<span class="hero-world-name">${escapeHtml(instance.name)}</span>
|
|
64
|
+
${instance.code ? html`<span class="hero-world-code">${escapeHtml(instance.code)}</span>` : ''}
|
|
65
|
+
</span>
|
|
66
|
+
</span>
|
|
67
|
+
</button>
|
|
68
|
+
`;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const worldSkeleton = () =>
|
|
72
|
+
range(0, LANDING_WORLD_LIMIT - 1)
|
|
73
|
+
.map(
|
|
74
|
+
(i) => html`
|
|
75
|
+
<div class="hero-world hero-world-${i} hero-world-skeleton" style="--world-index: ${i};">
|
|
76
|
+
<span class="hero-world-frame"></span>
|
|
77
|
+
<span class="hero-world-inner"></span>
|
|
78
|
+
</div>
|
|
79
|
+
`,
|
|
80
|
+
)
|
|
81
|
+
.join('');
|
|
6
82
|
|
|
7
83
|
class MainBodyCyberiaPortal {
|
|
8
84
|
static async instance() {
|
|
85
|
+
const id = 'cyberia-portal-landing';
|
|
86
|
+
|
|
87
|
+
// Per-render landing state: the 3 featured worlds and the one the CTA enters.
|
|
88
|
+
const state = { worlds: [], selectedId: null };
|
|
89
|
+
const selectedWorld = () => state.worlds.find((w) => w.id === state.selectedId) || null;
|
|
90
|
+
|
|
91
|
+
const worldsSel = `.hero-worlds`;
|
|
92
|
+
const ctaSel = `.cta-button`;
|
|
93
|
+
const backdropSel = `.hero-backdrop-img`;
|
|
94
|
+
|
|
95
|
+
const renderCta = () => {
|
|
96
|
+
const cta = s(ctaSel);
|
|
97
|
+
if (!cta) return;
|
|
98
|
+
const world = selectedWorld();
|
|
99
|
+
if (!world) {
|
|
100
|
+
cta.disabled = false;
|
|
101
|
+
cta.classList.remove('cta-button-disabled');
|
|
102
|
+
htmls(ctaSel, html`<i class="fa-solid fa-play"></i> Enter The World`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const meta = statusMeta(world.status);
|
|
106
|
+
cta.disabled = !world.playable;
|
|
107
|
+
cta.classList.toggle('cta-button-disabled', !world.playable);
|
|
108
|
+
htmls(
|
|
109
|
+
ctaSel,
|
|
110
|
+
world.playable
|
|
111
|
+
? html`<i class="fa-solid fa-play"></i> Enter <span class="cta-world-name">${escapeHtml(world.name)}</span>`
|
|
112
|
+
: html`<i class="fa-solid fa-lock"></i> ${escapeHtml(world.name)} is ${meta.cta || meta.label}`,
|
|
113
|
+
);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const renderBackdrop = () => {
|
|
117
|
+
const img = s(backdropSel);
|
|
118
|
+
const world = selectedWorld();
|
|
119
|
+
if (!img || !world) return;
|
|
120
|
+
if (img.getAttribute('src') === world.thumbnailUrl) return;
|
|
121
|
+
img.classList.remove('hero-backdrop-visible');
|
|
122
|
+
img.onload = () => img.classList.add('hero-backdrop-visible');
|
|
123
|
+
img.onerror = () => img.classList.remove('hero-backdrop-visible');
|
|
124
|
+
img.src = world.thumbnailUrl;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const renderWorlds = () => {
|
|
128
|
+
if (!s(worldsSel)) return;
|
|
129
|
+
htmls(worldsSel, state.worlds.map((w, i) => worldCard(w, i, w.id === state.selectedId)).join(''));
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const select = (instanceId) => {
|
|
133
|
+
if (!state.worlds.some((w) => w.id === instanceId)) return;
|
|
134
|
+
state.selectedId = instanceId;
|
|
135
|
+
const cards = s(worldsSel)?.querySelectorAll('.hero-world') || [];
|
|
136
|
+
cards.forEach((card) => card.classList.toggle('hero-world-selected', card.dataset.instanceId === instanceId));
|
|
137
|
+
renderCta();
|
|
138
|
+
renderBackdrop();
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const launch = () => {
|
|
142
|
+
const world = selectedWorld();
|
|
143
|
+
if (!world) return (location.href = `${DEFAULT_CLIENT_BASE_URL}/`);
|
|
144
|
+
if (!world.playable) {
|
|
145
|
+
NotificationManager.Push({
|
|
146
|
+
html: `${world.name} is ${statusMeta(world.status).label}.`,
|
|
147
|
+
status: 'warning',
|
|
148
|
+
});
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
location.href = world.playUrl || `${DEFAULT_CLIENT_BASE_URL}/`;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const load = async () => {
|
|
155
|
+
if (s(worldsSel)) htmls(worldsSel, worldSkeleton());
|
|
156
|
+
try {
|
|
157
|
+
state.worlds = await defaultInstanceProvider({ limit: LANDING_WORLD_LIMIT });
|
|
158
|
+
} catch (err) {
|
|
159
|
+
console.warn('[cyberia-portal-landing] could not load featured worlds', err);
|
|
160
|
+
state.worlds = [];
|
|
161
|
+
}
|
|
162
|
+
const first = state.worlds.find((w) => w.playable) || state.worlds[0];
|
|
163
|
+
state.selectedId = first ? first.id : null;
|
|
164
|
+
const section = s(`.hero-section`);
|
|
165
|
+
if (section) section.classList.toggle('hero-section-no-worlds', state.worlds.length === 0);
|
|
166
|
+
renderWorlds();
|
|
167
|
+
renderCta();
|
|
168
|
+
renderBackdrop();
|
|
169
|
+
};
|
|
170
|
+
|
|
9
171
|
setTimeout(() => {
|
|
10
|
-
EventsUI.onClick(
|
|
11
|
-
|
|
12
|
-
location.href = 'https://client.cyberiaonline.com/';
|
|
13
|
-
});
|
|
14
|
-
EventsUI.onClick('.cta-button-select-instance', () => {
|
|
15
|
-
console.log('or select one button clicked');
|
|
16
|
-
s(`.main-btn-instance-selection`).click();
|
|
17
|
-
});
|
|
18
|
-
});
|
|
172
|
+
EventsUI.onClick(ctaSel, launch);
|
|
173
|
+
EventsUI.onClick('.cta-button-select-instance', () => s(`.main-btn-instance-selection`).click());
|
|
19
174
|
|
|
20
|
-
|
|
175
|
+
const worlds = s(worldsSel);
|
|
176
|
+
if (worlds)
|
|
177
|
+
worlds.addEventListener('click', (event) => {
|
|
178
|
+
const card = event.target.closest?.('.hero-world:not(.hero-world-skeleton)');
|
|
179
|
+
if (!card) return;
|
|
180
|
+
// Second click on the already selected world enters it directly.
|
|
181
|
+
if (card.dataset.instanceId === state.selectedId) return launch();
|
|
182
|
+
select(card.dataset.instanceId);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
load();
|
|
186
|
+
});
|
|
21
187
|
|
|
22
188
|
// Stable pixel-art particle field: positions/motion fixed once so they don't jump on theme change.
|
|
23
189
|
// Colors are theme-driven via CSS custom properties resolved in ThemeEvents.
|
|
@@ -56,12 +222,12 @@ class MainBodyCyberiaPortal {
|
|
|
56
222
|
--footer-bg-color: ${darkTheme ? '#101010' : '#E3E3E3'};
|
|
57
223
|
--card-bg-color: ${darkTheme ? '#2c2c2c' : '#FFFFFF'};
|
|
58
224
|
--card-shadow: ${darkTheme ? '0 8px 25px rgba(0, 0, 0, 0.5)' : '0 8px 25px rgba(0, 0, 0, 0.1)'};
|
|
59
|
-
--btn-primary-bg: ${
|
|
60
|
-
? 'linear-gradient(45deg, #9b59b6, #8e44ad)'
|
|
61
|
-
|
|
62
|
-
--btn-primary-shadow: ${
|
|
63
|
-
? '0 4px 15px rgba(155, 89, 182, 0.4)'
|
|
64
|
-
|
|
225
|
+
--btn-primary-bg: ${
|
|
226
|
+
darkTheme ? 'linear-gradient(45deg, #9b59b6, #8e44ad)' : 'linear-gradient(45deg, #ffcc00, #e6b800)'
|
|
227
|
+
};
|
|
228
|
+
--btn-primary-shadow: ${
|
|
229
|
+
darkTheme ? '0 4px 15px rgba(155, 89, 182, 0.4)' : '0 4px 15px rgba(255, 204, 0, 0.4)'
|
|
230
|
+
};
|
|
65
231
|
|
|
66
232
|
/* Pixel-art particle + title palette (theme aware) */
|
|
67
233
|
--particle-color-1: ${darkTheme ? '#bb8fce' : '#ffcc00'};
|
|
@@ -69,10 +235,39 @@ class MainBodyCyberiaPortal {
|
|
|
69
235
|
--pixel-glow: ${darkTheme ? '14px' : '7px'};
|
|
70
236
|
--title-accent: ${darkTheme ? '#bb8fce' : '#e6b800'};
|
|
71
237
|
--title-shadow: ${darkTheme ? 'rgba(0, 0, 0, 0.85)' : 'rgba(0, 0, 0, 0.55)'};
|
|
72
|
-
--hero-overlay: ${darkTheme
|
|
73
|
-
? 'linear-gradient(180deg, rgba(0, 0, 0, 0.34), rgba(0, 0, 0, 0.5))'
|
|
74
|
-
: 'linear-gradient(180deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.35))'};
|
|
75
238
|
--pixel-grid-color: ${darkTheme ? 'rgba(155, 89, 182, 0.75)' : 'rgba(255, 204, 0, 1)'};
|
|
239
|
+
|
|
240
|
+
/* Hero space backdrop: deep nebula gradient, the selected world's
|
|
241
|
+
thumbnail is blurred on top of it, then a tint keeps text legible */
|
|
242
|
+
--hero-space: ${
|
|
243
|
+
darkTheme
|
|
244
|
+
? 'radial-gradient(ellipse 70% 60% at 20% 20%, rgba(155, 89, 182, 0.45), transparent 60%), radial-gradient(ellipse 60% 50% at 85% 80%, rgba(93, 41, 128, 0.5), transparent 60%), linear-gradient(180deg, #0b0a12, #15111f)'
|
|
245
|
+
: 'radial-gradient(ellipse 70% 60% at 20% 20%, rgba(255, 204, 0, 0.35), transparent 60%), radial-gradient(ellipse 60% 50% at 85% 80%, rgba(230, 150, 0, 0.35), transparent 60%), linear-gradient(180deg, #1c1f33, #2a2340)'
|
|
246
|
+
};
|
|
247
|
+
--hero-overlay: ${
|
|
248
|
+
darkTheme
|
|
249
|
+
? 'linear-gradient(180deg, rgba(8, 6, 14, 0.55), rgba(8, 6, 14, 0.78))'
|
|
250
|
+
: 'linear-gradient(180deg, rgba(16, 14, 30, 0.42), rgba(16, 14, 30, 0.7))'
|
|
251
|
+
};
|
|
252
|
+
--hero-backdrop-opacity: ${darkTheme ? '0.55' : '0.6'};
|
|
253
|
+
|
|
254
|
+
/* World cards */
|
|
255
|
+
--world-frame-bg: ${darkTheme ? '#08060e' : '#12101f'};
|
|
256
|
+
--world-star: ${darkTheme ? 'rgba(255, 255, 255, 0.9)' : 'rgba(255, 244, 200, 0.95)'};
|
|
257
|
+
--world-beam: ${darkTheme ? '#bb8fce' : '#ffcc00'};
|
|
258
|
+
--world-beam-2: ${darkTheme ? '#8e44ad' : '#e6b800'};
|
|
259
|
+
--world-caption-bg: ${
|
|
260
|
+
darkTheme
|
|
261
|
+
? 'linear-gradient(0deg, rgba(8, 6, 14, 0.95), rgba(8, 6, 14, 0))'
|
|
262
|
+
: 'linear-gradient(0deg, rgba(18, 16, 31, 0.95), rgba(18, 16, 31, 0))'
|
|
263
|
+
};
|
|
264
|
+
--world-glow: ${darkTheme ? 'rgba(187, 143, 206, 0.55)' : 'rgba(255, 204, 0, 0.55)'};
|
|
265
|
+
--world-ok: ${darkTheme ? '#43d17a' : '#3ddc84'};
|
|
266
|
+
--world-warn: ${darkTheme ? '#f0b429' : '#ffc233'};
|
|
267
|
+
--world-err: ${darkTheme ? '#ff5b5b' : '#ff6b6b'};
|
|
268
|
+
--world-info: ${darkTheme ? '#c084fc' : '#c9a2ff'};
|
|
269
|
+
--world-muted: ${darkTheme ? '#8a8a96' : '#b0b0bd'};
|
|
270
|
+
--world-accent: ${darkTheme ? '#bb8fce' : '#ffcc00'};
|
|
76
271
|
}
|
|
77
272
|
|
|
78
273
|
.landing-page {
|
|
@@ -90,36 +285,45 @@ class MainBodyCyberiaPortal {
|
|
|
90
285
|
display: flex;
|
|
91
286
|
align-items: center;
|
|
92
287
|
justify-content: center;
|
|
93
|
-
text-align: center;
|
|
94
288
|
min-height: 100vh;
|
|
95
|
-
/* background: url('${getProxyPath()}assets/lore/vectorized/lore8.svg') no-repeat center center/cover;*/
|
|
96
289
|
position: relative;
|
|
97
|
-
padding:
|
|
290
|
+
padding: 4rem 1rem 3.5rem;
|
|
98
291
|
overflow: hidden;
|
|
292
|
+
background: var(--hero-space);
|
|
99
293
|
}
|
|
100
|
-
|
|
294
|
+
/* Selected world thumbnail, blurred and scaled, behind everything */
|
|
295
|
+
.hero-backdrop {
|
|
101
296
|
position: absolute;
|
|
102
297
|
inset: 0;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
298
|
+
z-index: 0;
|
|
299
|
+
overflow: hidden;
|
|
300
|
+
pointer-events: none;
|
|
301
|
+
}
|
|
302
|
+
.hero-backdrop-img {
|
|
303
|
+
position: absolute;
|
|
304
|
+
inset: -6%;
|
|
305
|
+
width: 112%;
|
|
306
|
+
height: 112%;
|
|
109
307
|
object-fit: cover;
|
|
110
308
|
object-position: center center;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
309
|
+
image-rendering: pixelated;
|
|
310
|
+
filter: blur(18px) saturate(1.2);
|
|
311
|
+
opacity: 0;
|
|
312
|
+
transform: scale(1.04);
|
|
313
|
+
transition:
|
|
314
|
+
opacity 1.1s ease,
|
|
315
|
+
transform 12s ease-out;
|
|
316
|
+
}
|
|
317
|
+
.hero-backdrop-img.hero-backdrop-visible {
|
|
318
|
+
opacity: var(--hero-backdrop-opacity);
|
|
319
|
+
transform: scale(1);
|
|
115
320
|
}
|
|
116
|
-
/*
|
|
321
|
+
/* Tint over the backdrop so the white hero text stays legible */
|
|
117
322
|
.hero-section::after {
|
|
118
323
|
content: '';
|
|
119
324
|
position: absolute;
|
|
120
325
|
inset: 0;
|
|
121
326
|
background: var(--hero-overlay);
|
|
122
|
-
|
|
123
327
|
z-index: 1;
|
|
124
328
|
}
|
|
125
329
|
/* Pixel grid overlay (theme aware, slow drift) */
|
|
@@ -137,6 +341,7 @@ class MainBodyCyberiaPortal {
|
|
|
137
341
|
-webkit-mask-image: radial-gradient(circle at center, rgba(0, 0, 0, 1), transparent 92%);
|
|
138
342
|
mask-image: radial-gradient(circle at center, rgba(0, 0, 0, 1), transparent 92%);
|
|
139
343
|
animation: heroGridDrift 34s linear infinite;
|
|
344
|
+
opacity: 0.35;
|
|
140
345
|
}
|
|
141
346
|
|
|
142
347
|
/* Pixel-art particle field */
|
|
@@ -165,15 +370,26 @@ class MainBodyCyberiaPortal {
|
|
|
165
370
|
box-shadow: 0 0 var(--pixel-glow) var(--particle-color-2);
|
|
166
371
|
}
|
|
167
372
|
|
|
168
|
-
|
|
373
|
+
/* Two-column hero: copy + CTA left, world deck right */
|
|
374
|
+
.hero-layout {
|
|
169
375
|
position: relative;
|
|
170
376
|
z-index: 5;
|
|
171
|
-
|
|
377
|
+
display: grid;
|
|
378
|
+
grid-template-columns: minmax(0, 1fr);
|
|
379
|
+
gap: 2.5rem 3rem;
|
|
380
|
+
align-items: center;
|
|
381
|
+
width: 100%;
|
|
382
|
+
max-width: 1180px;
|
|
172
383
|
animation: fadeInUp 1s ease-out;
|
|
173
384
|
}
|
|
385
|
+
.hero-content {
|
|
386
|
+
text-align: center;
|
|
387
|
+
max-width: 620px;
|
|
388
|
+
margin: 0 auto;
|
|
389
|
+
}
|
|
174
390
|
.hero-content .logo-image {
|
|
175
|
-
max-width:
|
|
176
|
-
margin-bottom:
|
|
391
|
+
max-width: 210px;
|
|
392
|
+
margin-bottom: 0.5rem;
|
|
177
393
|
image-rendering: pixelated;
|
|
178
394
|
transform-origin: center bottom;
|
|
179
395
|
animation:
|
|
@@ -184,7 +400,7 @@ class MainBodyCyberiaPortal {
|
|
|
184
400
|
position: relative;
|
|
185
401
|
display: inline-block;
|
|
186
402
|
font-size: 4rem;
|
|
187
|
-
margin
|
|
403
|
+
margin: 10px 0;
|
|
188
404
|
font-weight: 700;
|
|
189
405
|
letter-spacing: 4px;
|
|
190
406
|
color: #fff;
|
|
@@ -194,17 +410,51 @@ class MainBodyCyberiaPortal {
|
|
|
194
410
|
animation: heroTitlePixelIn 1s steps(18, end) 0.55s both;
|
|
195
411
|
}
|
|
196
412
|
.hero-content p {
|
|
197
|
-
font-size: 1.
|
|
198
|
-
margin
|
|
199
|
-
max-width:
|
|
200
|
-
margin-left: auto;
|
|
201
|
-
margin-right: auto;
|
|
413
|
+
font-size: 1.2rem;
|
|
414
|
+
margin: 10px auto 1.8rem;
|
|
415
|
+
max-width: 560px;
|
|
202
416
|
line-height: 1.6;
|
|
417
|
+
color: #fff;
|
|
203
418
|
text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.6);
|
|
204
419
|
animation: heroFadeUp 0.8s steps(6, end) 0.95s both;
|
|
205
420
|
}
|
|
421
|
+
.hero-kicker {
|
|
422
|
+
display: inline-flex;
|
|
423
|
+
align-items: center;
|
|
424
|
+
gap: 8px;
|
|
425
|
+
margin-bottom: 0.9rem;
|
|
426
|
+
padding: 5px 12px;
|
|
427
|
+
border: 2px solid var(--world-accent);
|
|
428
|
+
color: #fff;
|
|
429
|
+
font-size: 0.72rem;
|
|
430
|
+
font-weight: 700;
|
|
431
|
+
letter-spacing: 3px;
|
|
432
|
+
text-transform: uppercase;
|
|
433
|
+
background: rgba(0, 0, 0, 0.35);
|
|
434
|
+
animation: heroFadeUp 0.7s steps(6, end) 1.15s both;
|
|
435
|
+
}
|
|
436
|
+
.hero-kicker-dot {
|
|
437
|
+
width: 8px;
|
|
438
|
+
height: 8px;
|
|
439
|
+
background: var(--world-ok);
|
|
440
|
+
box-shadow: 0 0 10px var(--world-ok);
|
|
441
|
+
animation: heroPulse 1.6s ease-in-out infinite;
|
|
442
|
+
}
|
|
443
|
+
.hero-actions {
|
|
444
|
+
display: flex;
|
|
445
|
+
flex-direction: column;
|
|
446
|
+
align-items: center;
|
|
447
|
+
gap: 10px;
|
|
448
|
+
animation: heroFadeUp 0.8s steps(6, end) 1.25s both;
|
|
449
|
+
}
|
|
206
450
|
|
|
207
451
|
.cta-button {
|
|
452
|
+
position: relative;
|
|
453
|
+
display: inline-flex;
|
|
454
|
+
align-items: center;
|
|
455
|
+
justify-content: center;
|
|
456
|
+
gap: 12px;
|
|
457
|
+
max-width: 100%;
|
|
208
458
|
padding: 15px 35px;
|
|
209
459
|
border: none;
|
|
210
460
|
border-radius: 50px;
|
|
@@ -215,34 +465,280 @@ class MainBodyCyberiaPortal {
|
|
|
215
465
|
cursor: pointer;
|
|
216
466
|
transition:
|
|
217
467
|
transform 0.3s,
|
|
218
|
-
box-shadow 0.3s
|
|
468
|
+
box-shadow 0.3s,
|
|
469
|
+
opacity 0.3s;
|
|
219
470
|
box-shadow: var(--btn-primary-shadow);
|
|
220
471
|
text-transform: uppercase;
|
|
472
|
+
letter-spacing: 1px;
|
|
221
473
|
}
|
|
222
|
-
.cta-button
|
|
474
|
+
.cta-button .cta-world-name {
|
|
475
|
+
max-width: 260px;
|
|
476
|
+
overflow: hidden;
|
|
477
|
+
text-overflow: ellipsis;
|
|
478
|
+
white-space: nowrap;
|
|
479
|
+
}
|
|
480
|
+
.cta-button:hover:not(:disabled) {
|
|
223
481
|
transform: translateY(-5px);
|
|
224
|
-
box-shadow: 0 6px 20px
|
|
482
|
+
box-shadow: 0 6px 20px var(--world-glow);
|
|
483
|
+
}
|
|
484
|
+
.cta-button:disabled,
|
|
485
|
+
.cta-button-disabled {
|
|
486
|
+
cursor: not-allowed;
|
|
487
|
+
opacity: 0.6;
|
|
488
|
+
filter: grayscale(0.5);
|
|
225
489
|
}
|
|
226
490
|
.cta-button-select-instance {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
border: 1px solid var(--primary-color);
|
|
491
|
+
display: inline-flex;
|
|
492
|
+
align-items: center;
|
|
493
|
+
gap: 8px;
|
|
494
|
+
padding: 8px 18px;
|
|
495
|
+
border: 1px solid rgba(255, 255, 255, 0.35);
|
|
233
496
|
border-radius: 50px;
|
|
234
|
-
font-size: 0.
|
|
497
|
+
font-size: 0.9rem;
|
|
235
498
|
cursor: pointer;
|
|
236
499
|
transition:
|
|
237
500
|
transform 0.3s,
|
|
238
|
-
box-shadow 0.3s
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
501
|
+
box-shadow 0.3s,
|
|
502
|
+
border-color 0.3s;
|
|
503
|
+
background: rgba(0, 0, 0, 0.35);
|
|
504
|
+
color: #fff;
|
|
505
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
|
242
506
|
}
|
|
243
507
|
.cta-button-select-instance:hover {
|
|
244
508
|
transform: translateY(-2px);
|
|
245
|
-
|
|
509
|
+
border-color: var(--world-accent);
|
|
510
|
+
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.3);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/* World deck: 3 space-framed thumbnails in a staggered cascade */
|
|
514
|
+
.hero-worlds {
|
|
515
|
+
display: grid;
|
|
516
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
517
|
+
gap: 14px;
|
|
518
|
+
width: 100%;
|
|
519
|
+
max-width: 720px;
|
|
520
|
+
margin: 0 auto;
|
|
521
|
+
align-items: start;
|
|
522
|
+
}
|
|
523
|
+
.hero-section-no-worlds .hero-worlds {
|
|
524
|
+
display: none;
|
|
525
|
+
}
|
|
526
|
+
.hero-world {
|
|
527
|
+
position: relative;
|
|
528
|
+
display: block;
|
|
529
|
+
width: 100%;
|
|
530
|
+
padding: 0;
|
|
531
|
+
border: none;
|
|
532
|
+
background: transparent;
|
|
533
|
+
color: #fff;
|
|
534
|
+
font-family: inherit;
|
|
535
|
+
text-align: left;
|
|
536
|
+
cursor: pointer;
|
|
537
|
+
/* pixel-step entrance (after the title), then an endless gentle float
|
|
538
|
+
whose phase is offset per card so the deck never bobs in unison */
|
|
539
|
+
animation:
|
|
540
|
+
heroWorldIn 0.75s steps(8, end) calc(1.15s + var(--world-index, 0) * 0.16s) both,
|
|
541
|
+
heroWorldFloat calc(5.2s + var(--world-index, 0) * 0.7s) ease-in-out
|
|
542
|
+
calc(1.9s + var(--world-index, 0) * 0.16s) infinite;
|
|
543
|
+
}
|
|
544
|
+
.hero-world:focus-visible {
|
|
545
|
+
outline: 3px solid var(--world-accent);
|
|
546
|
+
outline-offset: 4px;
|
|
547
|
+
}
|
|
548
|
+
/* Space frame: starfield padding ring with a slowly rotating light beam */
|
|
549
|
+
.hero-world-frame {
|
|
550
|
+
position: absolute;
|
|
551
|
+
inset: 0;
|
|
552
|
+
overflow: hidden;
|
|
553
|
+
background-color: var(--world-frame-bg);
|
|
554
|
+
background-image:
|
|
555
|
+
radial-gradient(circle, var(--world-star) 0.9px, transparent 1.4px),
|
|
556
|
+
radial-gradient(circle, var(--world-star) 0.7px, transparent 1.2px),
|
|
557
|
+
radial-gradient(circle, var(--world-star) 1.1px, transparent 1.6px);
|
|
558
|
+
background-size:
|
|
559
|
+
23px 23px,
|
|
560
|
+
37px 41px,
|
|
561
|
+
59px 53px;
|
|
562
|
+
background-position:
|
|
563
|
+
0 0,
|
|
564
|
+
11px 7px,
|
|
565
|
+
29px 19px;
|
|
566
|
+
animation: heroStarDrift 60s linear infinite;
|
|
567
|
+
transition: box-shadow 0.35s ease;
|
|
568
|
+
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.55);
|
|
569
|
+
}
|
|
570
|
+
.hero-world-frame::before {
|
|
571
|
+
content: '';
|
|
572
|
+
position: absolute;
|
|
573
|
+
inset: -75%;
|
|
574
|
+
background: conic-gradient(
|
|
575
|
+
from 0deg,
|
|
576
|
+
transparent 0deg,
|
|
577
|
+
transparent 250deg,
|
|
578
|
+
var(--world-beam-2) 300deg,
|
|
579
|
+
var(--world-beam) 330deg,
|
|
580
|
+
transparent 360deg
|
|
581
|
+
);
|
|
582
|
+
opacity: 0.85;
|
|
583
|
+
animation: heroBeamSpin 7s linear infinite;
|
|
584
|
+
animation-delay: calc(var(--world-index, 0) * -2.3s);
|
|
585
|
+
}
|
|
586
|
+
.hero-world-inner {
|
|
587
|
+
position: relative;
|
|
588
|
+
z-index: 1;
|
|
589
|
+
display: block;
|
|
590
|
+
margin: 6px;
|
|
591
|
+
aspect-ratio: 16 / 11;
|
|
592
|
+
overflow: hidden;
|
|
593
|
+
background: var(--world-frame-bg);
|
|
594
|
+
transform: scale(1);
|
|
595
|
+
transition:
|
|
596
|
+
transform 0.35s cubic-bezier(0.2, 0.9, 0.3, 1.2),
|
|
597
|
+
filter 0.35s ease;
|
|
598
|
+
}
|
|
599
|
+
.hero-world-img {
|
|
600
|
+
display: block;
|
|
601
|
+
width: 100%;
|
|
602
|
+
height: 100%;
|
|
603
|
+
object-fit: cover;
|
|
604
|
+
image-rendering: pixelated;
|
|
605
|
+
transform: scale(1.02);
|
|
606
|
+
transition:
|
|
607
|
+
transform 0.6s ease,
|
|
608
|
+
filter 0.35s ease;
|
|
609
|
+
filter: saturate(0.85) brightness(0.9);
|
|
610
|
+
}
|
|
611
|
+
.hero-world:hover .hero-world-img,
|
|
612
|
+
.hero-world-selected .hero-world-img {
|
|
613
|
+
transform: scale(1.1);
|
|
614
|
+
filter: saturate(1.1) brightness(1);
|
|
615
|
+
}
|
|
616
|
+
.hero-world:hover .hero-world-frame,
|
|
617
|
+
.hero-world-selected .hero-world-frame {
|
|
618
|
+
box-shadow:
|
|
619
|
+
0 0 0 2px var(--world-accent),
|
|
620
|
+
0 0 26px var(--world-glow),
|
|
621
|
+
0 14px 32px rgba(0, 0, 0, 0.6);
|
|
622
|
+
}
|
|
623
|
+
.hero-world-selected .hero-world-frame::before {
|
|
624
|
+
opacity: 1;
|
|
625
|
+
animation-duration: 3.2s;
|
|
626
|
+
}
|
|
627
|
+
.hero-world-selected .hero-world-inner {
|
|
628
|
+
transform: scale(1.03);
|
|
629
|
+
}
|
|
630
|
+
.hero-world-unavailable .hero-world-img {
|
|
631
|
+
filter: grayscale(0.7) brightness(0.65);
|
|
632
|
+
}
|
|
633
|
+
.hero-world-status {
|
|
634
|
+
position: absolute;
|
|
635
|
+
top: 8px;
|
|
636
|
+
left: 8px;
|
|
637
|
+
z-index: 2;
|
|
638
|
+
display: inline-flex;
|
|
639
|
+
align-items: center;
|
|
640
|
+
gap: 6px;
|
|
641
|
+
padding: 3px 8px;
|
|
642
|
+
font-size: 0.62rem;
|
|
643
|
+
font-weight: 700;
|
|
644
|
+
letter-spacing: 1.5px;
|
|
645
|
+
text-transform: uppercase;
|
|
646
|
+
color: #fff;
|
|
647
|
+
background: rgba(0, 0, 0, 0.65);
|
|
648
|
+
border-left: 3px solid var(--tone, var(--world-muted));
|
|
649
|
+
}
|
|
650
|
+
.hero-world-dot {
|
|
651
|
+
width: 7px;
|
|
652
|
+
height: 7px;
|
|
653
|
+
background: var(--tone, var(--world-muted));
|
|
654
|
+
box-shadow: 0 0 8px var(--tone, var(--world-muted));
|
|
655
|
+
animation: heroPulse 1.6s ease-in-out infinite;
|
|
656
|
+
}
|
|
657
|
+
.hero-tone-ok {
|
|
658
|
+
--tone: var(--world-ok);
|
|
659
|
+
}
|
|
660
|
+
.hero-tone-warn {
|
|
661
|
+
--tone: var(--world-warn);
|
|
662
|
+
}
|
|
663
|
+
.hero-tone-err {
|
|
664
|
+
--tone: var(--world-err);
|
|
665
|
+
}
|
|
666
|
+
.hero-tone-info {
|
|
667
|
+
--tone: var(--world-info);
|
|
668
|
+
}
|
|
669
|
+
.hero-tone-muted {
|
|
670
|
+
--tone: var(--world-muted);
|
|
671
|
+
}
|
|
672
|
+
.hero-tone-accent {
|
|
673
|
+
--tone: var(--world-accent);
|
|
674
|
+
}
|
|
675
|
+
.hero-world-check {
|
|
676
|
+
position: absolute;
|
|
677
|
+
top: 6px;
|
|
678
|
+
right: 6px;
|
|
679
|
+
z-index: 2;
|
|
680
|
+
display: flex;
|
|
681
|
+
align-items: center;
|
|
682
|
+
justify-content: center;
|
|
683
|
+
width: 26px;
|
|
684
|
+
height: 26px;
|
|
685
|
+
font-size: 0.8rem;
|
|
686
|
+
color: #111;
|
|
687
|
+
background: var(--world-accent);
|
|
688
|
+
box-shadow: 0 0 12px var(--world-glow);
|
|
689
|
+
opacity: 0;
|
|
690
|
+
transform: scale(0.4);
|
|
691
|
+
transition:
|
|
692
|
+
opacity 0.25s ease,
|
|
693
|
+
transform 0.25s cubic-bezier(0.2, 0.9, 0.3, 1.4);
|
|
694
|
+
}
|
|
695
|
+
.hero-world-selected .hero-world-check {
|
|
696
|
+
opacity: 1;
|
|
697
|
+
transform: scale(1);
|
|
698
|
+
}
|
|
699
|
+
.hero-world-caption {
|
|
700
|
+
position: absolute;
|
|
701
|
+
left: 0;
|
|
702
|
+
right: 0;
|
|
703
|
+
bottom: 0;
|
|
704
|
+
z-index: 2;
|
|
705
|
+
display: flex;
|
|
706
|
+
flex-direction: column;
|
|
707
|
+
gap: 2px;
|
|
708
|
+
padding: 22px 10px 9px;
|
|
709
|
+
background: var(--world-caption-bg);
|
|
710
|
+
}
|
|
711
|
+
.hero-world-name {
|
|
712
|
+
font-size: 0.95rem;
|
|
713
|
+
font-weight: 700;
|
|
714
|
+
letter-spacing: 0.5px;
|
|
715
|
+
white-space: nowrap;
|
|
716
|
+
overflow: hidden;
|
|
717
|
+
text-overflow: ellipsis;
|
|
718
|
+
text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.7);
|
|
719
|
+
}
|
|
720
|
+
.hero-world-code {
|
|
721
|
+
font-size: 0.66rem;
|
|
722
|
+
letter-spacing: 2px;
|
|
723
|
+
text-transform: uppercase;
|
|
724
|
+
color: var(--world-accent);
|
|
725
|
+
white-space: nowrap;
|
|
726
|
+
overflow: hidden;
|
|
727
|
+
text-overflow: ellipsis;
|
|
728
|
+
}
|
|
729
|
+
.hero-world-skeleton {
|
|
730
|
+
cursor: default;
|
|
731
|
+
animation: heroWorldIn 0.75s steps(8, end) calc(1.15s + var(--world-index, 0) * 0.16s) both;
|
|
732
|
+
}
|
|
733
|
+
.hero-world-skeleton .hero-world-inner {
|
|
734
|
+
background: linear-gradient(
|
|
735
|
+
90deg,
|
|
736
|
+
rgba(255, 255, 255, 0.04) 25%,
|
|
737
|
+
rgba(255, 255, 255, 0.12) 37%,
|
|
738
|
+
rgba(255, 255, 255, 0.04) 63%
|
|
739
|
+
);
|
|
740
|
+
background-size: 400% 100%;
|
|
741
|
+
animation: heroShimmer 1.3s ease infinite;
|
|
246
742
|
}
|
|
247
743
|
|
|
248
744
|
/* Features Section */
|
|
@@ -272,6 +768,36 @@ class MainBodyCyberiaPortal {
|
|
|
272
768
|
.features-grid {
|
|
273
769
|
grid-template-columns: repeat(auto-fit, minmax(22%, 1fr)); /* 4 columns */
|
|
274
770
|
}
|
|
771
|
+
.hero-layout {
|
|
772
|
+
grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
|
|
773
|
+
}
|
|
774
|
+
.hero-content {
|
|
775
|
+
text-align: left;
|
|
776
|
+
margin: 0;
|
|
777
|
+
}
|
|
778
|
+
.hero-content p {
|
|
779
|
+
margin-left: 0;
|
|
780
|
+
margin-right: 0;
|
|
781
|
+
}
|
|
782
|
+
.hero-actions {
|
|
783
|
+
align-items: flex-start;
|
|
784
|
+
}
|
|
785
|
+
/* cascade: each card steps down and right, selected one pops forward */
|
|
786
|
+
.hero-worlds {
|
|
787
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
788
|
+
max-width: none;
|
|
789
|
+
padding-top: 24px;
|
|
790
|
+
padding-bottom: 24px;
|
|
791
|
+
}
|
|
792
|
+
.hero-world-0 {
|
|
793
|
+
--card-y: 24px;
|
|
794
|
+
}
|
|
795
|
+
.hero-world-1 {
|
|
796
|
+
--card-y: -12px;
|
|
797
|
+
}
|
|
798
|
+
.hero-world-2 {
|
|
799
|
+
--card-y: 36px;
|
|
800
|
+
}
|
|
275
801
|
}
|
|
276
802
|
.feature-card {
|
|
277
803
|
background: var(--card-bg-color);
|
|
@@ -367,6 +893,73 @@ class MainBodyCyberiaPortal {
|
|
|
367
893
|
}
|
|
368
894
|
}
|
|
369
895
|
|
|
896
|
+
/* World cards: pixel-step drop-in, then a slow bob (offsets are baked in
|
|
897
|
+
via --card-y so the desktop cascade survives the float loop) */
|
|
898
|
+
@keyframes heroWorldIn {
|
|
899
|
+
0% {
|
|
900
|
+
opacity: 0;
|
|
901
|
+
transform: translateY(calc(var(--card-y, 0px) + 48px)) scale(0.82);
|
|
902
|
+
}
|
|
903
|
+
60% {
|
|
904
|
+
opacity: 1;
|
|
905
|
+
transform: translateY(calc(var(--card-y, 0px) - 8px)) scale(1.03);
|
|
906
|
+
}
|
|
907
|
+
100% {
|
|
908
|
+
opacity: 1;
|
|
909
|
+
transform: translateY(var(--card-y, 0px)) scale(1);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
@keyframes heroWorldFloat {
|
|
913
|
+
0%,
|
|
914
|
+
100% {
|
|
915
|
+
transform: translateY(var(--card-y, 0px));
|
|
916
|
+
}
|
|
917
|
+
50% {
|
|
918
|
+
transform: translateY(calc(var(--card-y, 0px) - 9px));
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
@keyframes heroBeamSpin {
|
|
922
|
+
from {
|
|
923
|
+
transform: rotate(0deg);
|
|
924
|
+
}
|
|
925
|
+
to {
|
|
926
|
+
transform: rotate(360deg);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
@keyframes heroStarDrift {
|
|
930
|
+
from {
|
|
931
|
+
background-position:
|
|
932
|
+
0 0,
|
|
933
|
+
11px 7px,
|
|
934
|
+
29px 19px;
|
|
935
|
+
}
|
|
936
|
+
to {
|
|
937
|
+
background-position:
|
|
938
|
+
-46px 46px,
|
|
939
|
+
-63px 89px,
|
|
940
|
+
-89px 125px;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
@keyframes heroShimmer {
|
|
944
|
+
0% {
|
|
945
|
+
background-position: 100% 0;
|
|
946
|
+
}
|
|
947
|
+
100% {
|
|
948
|
+
background-position: 0 0;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
@keyframes heroPulse {
|
|
952
|
+
0%,
|
|
953
|
+
100% {
|
|
954
|
+
opacity: 1;
|
|
955
|
+
transform: scale(1);
|
|
956
|
+
}
|
|
957
|
+
50% {
|
|
958
|
+
opacity: 0.45;
|
|
959
|
+
transform: scale(0.75);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
370
963
|
/* Pixel-art particles rise with drift + spin; grid drifts slowly */
|
|
371
964
|
@keyframes heroParticleFloat {
|
|
372
965
|
0% {
|
|
@@ -402,13 +995,29 @@ class MainBodyCyberiaPortal {
|
|
|
402
995
|
.hero-pixel-grid,
|
|
403
996
|
.hero-content .logo-image,
|
|
404
997
|
.hero-content h1,
|
|
405
|
-
.hero-content p
|
|
998
|
+
.hero-content p,
|
|
999
|
+
.hero-kicker,
|
|
1000
|
+
.hero-kicker-dot,
|
|
1001
|
+
.hero-actions,
|
|
1002
|
+
.hero-world,
|
|
1003
|
+
.hero-world-frame,
|
|
1004
|
+
.hero-world-frame::before,
|
|
1005
|
+
.hero-world-dot,
|
|
1006
|
+
.hero-world-skeleton .hero-world-inner {
|
|
406
1007
|
animation: none !important;
|
|
407
1008
|
}
|
|
1009
|
+
.hero-backdrop-img,
|
|
1010
|
+
.hero-world-inner,
|
|
1011
|
+
.hero-world-img {
|
|
1012
|
+
transition: none !important;
|
|
1013
|
+
}
|
|
408
1014
|
}
|
|
409
1015
|
|
|
410
1016
|
/* Responsive Styles */
|
|
411
1017
|
@media (max-width: 650px) {
|
|
1018
|
+
.hero-section {
|
|
1019
|
+
padding: 3.5rem 1rem 3rem;
|
|
1020
|
+
}
|
|
412
1021
|
.hero-content h1 {
|
|
413
1022
|
font-size: 2.2rem;
|
|
414
1023
|
}
|
|
@@ -416,12 +1025,34 @@ class MainBodyCyberiaPortal {
|
|
|
416
1025
|
font-size: 1rem;
|
|
417
1026
|
}
|
|
418
1027
|
.hero-content .logo-image {
|
|
419
|
-
max-width:
|
|
1028
|
+
max-width: 160px;
|
|
420
1029
|
}
|
|
421
1030
|
.cta-button {
|
|
422
|
-
padding: 12px
|
|
1031
|
+
padding: 12px 24px;
|
|
423
1032
|
font-size: 1rem;
|
|
424
1033
|
}
|
|
1034
|
+
.cta-button .cta-world-name {
|
|
1035
|
+
max-width: 160px;
|
|
1036
|
+
}
|
|
1037
|
+
.hero-worlds {
|
|
1038
|
+
gap: 10px;
|
|
1039
|
+
}
|
|
1040
|
+
.hero-world-inner {
|
|
1041
|
+
margin: 4px;
|
|
1042
|
+
}
|
|
1043
|
+
.hero-world-name {
|
|
1044
|
+
font-size: 0.78rem;
|
|
1045
|
+
}
|
|
1046
|
+
.hero-world-code {
|
|
1047
|
+
display: none;
|
|
1048
|
+
}
|
|
1049
|
+
.hero-world-status {
|
|
1050
|
+
font-size: 0.55rem;
|
|
1051
|
+
padding: 2px 6px;
|
|
1052
|
+
}
|
|
1053
|
+
.hero-world-caption {
|
|
1054
|
+
padding: 16px 7px 6px;
|
|
1055
|
+
}
|
|
425
1056
|
.features-section {
|
|
426
1057
|
padding: 3rem 1rem;
|
|
427
1058
|
}
|
|
@@ -443,21 +1074,26 @@ class MainBodyCyberiaPortal {
|
|
|
443
1074
|
<div class="style-${id}"></div>
|
|
444
1075
|
<div class="landing-page">
|
|
445
1076
|
<section class="hero-section">
|
|
446
|
-
<
|
|
447
|
-
<!-- H.264 (compatibilidad) -->
|
|
448
|
-
<source src="/assets/video/landing-web.mp4" type='video/mp4; codecs="avc1.42E01E"' />
|
|
449
|
-
</video>
|
|
1077
|
+
<div class="hero-backdrop"><img class="hero-backdrop-img" alt="" aria-hidden="true" /></div>
|
|
450
1078
|
<div class="hero-pixel-grid"></div>
|
|
451
1079
|
<div class="hero-particles">${heroParticlesHtml}</div>
|
|
452
|
-
<div class="hero-
|
|
453
|
-
<
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
1080
|
+
<div class="hero-layout">
|
|
1081
|
+
<div class="hero-content">
|
|
1082
|
+
<img src="${getProxyPath()}assets/ui-icons/cyberia-white.png" alt="Cyberia Logo" class="logo-image" />
|
|
1083
|
+
<h1>CYBERIA</h1>
|
|
1084
|
+
<p>
|
|
1085
|
+
An action-packed Hack and Slash MMORPG. Explore a dynamic online sandbox pixel art universe, right from
|
|
1086
|
+
your browser.
|
|
1087
|
+
</p>
|
|
1088
|
+
<div class="hero-kicker"><span class="hero-kicker-dot"></span> Pick a world · Jump in</div>
|
|
1089
|
+
<div class="hero-actions">
|
|
1090
|
+
<button class="cta-button" type="button"><i class="fa-solid fa-play"></i> Enter The World</button>
|
|
1091
|
+
<button class="cta-button-select-instance" type="button">
|
|
1092
|
+
<i class="fa-solid fa-globe"></i> Browse all worlds
|
|
1093
|
+
</button>
|
|
1094
|
+
</div>
|
|
1095
|
+
</div>
|
|
1096
|
+
<div class="hero-worlds">${worldSkeleton()}</div>
|
|
461
1097
|
</div>
|
|
462
1098
|
</section>
|
|
463
1099
|
|