# Landing page — guest onboarding site ## Summary Apex `ulicraft.net` serves a static page guiding guests through joining: download launcher → add account → import modpack → connect. Built with **Astro + TypeScript** in `./landing/`; `pnpm run build` emits straight into `./www/`, which Caddy serves at the apex (behind host nginx). No runtime framework — output is plain HTML/CSS plus one tiny vanilla script (copy-to-clipboard + scroll reveal). The visual is the **"Carved from stone"** pixel design ("Claude Design", `landing/design/`): dark overworld palette, MC-GUI bevel buttons, pixel type, creeper mark, server-list panel. That design was a React/Babel-via-CDN prototype for a *fictional public SMP*; it was **ported to static Astro** with the real modded-LAN content and vendored assets. ## Stack & wiring - `landing/` — Astro project (own `package.json`, `tsconfig`). pnpm only. - `astro.config.mjs` → `outDir: "../www"`. Build overwrites `www/`; `www/` is gitignored (generated artifact). - `BASE_DOMAIN` env read at build time in `src/data/site.ts` (`process.env.BASE_DOMAIN ?? "ulicraft.net"`). Bake before deploy: `BASE_DOMAIN=ulicraft.net pnpm run build`. - Logo: wide wordmark from the design at `landing/public/logo.png` (hero + favicon). `image-rendering: pixelated` keeps it crisp. ### Source layout ``` landing/ ├── design/ # "Claude Design" prototype (reference, not built) ├── public/ │ ├── logo.png # hero wordmark (from design/assets/ulicraft-logo.png) │ └── fonts/ # vendored woff2 + fonts.css (self-contained) └── src/ ├── data/site.ts # single source: content + theme knobs ├── styles/main.css # ported design system (mood/hero/bevels) ├── components/ # Creeper, PixelIcon, CopyChip, ServerListPanel └── pages/index.astro # composition + copy/reveal script + dust ``` ## Fonts — vendored locally The design pulled fonts from Google Fonts CDN; we vendor them instead to keep the page self-contained. `tooling/fetch-fonts.sh` downloads the woff2 + a URL-rewritten `fonts.css` into `landing/public/fonts/`. `index.astro` links `/fonts/fonts.css`. Re-run the script only if the font set changes (keep families in sync with `main.css` `--font-*`): Pixelify Sans, Space Grotesk, Press Start 2P, Silkscreen, VT323. ## Theme config (replaces the design's in-browser TweaksPanel) `site.ts` `theme` is baked at build — edit + rebuild to change: - `mood`: `grass` (default) | `nether` | `end` - `hero`: `centered` (default) | `split` | `spotlight` - `headFont`: `pixelify` (default) | `8bit` | `silkscreen` - `dust`: floating hero particles (CSS animation; respects reduced-motion) `index.astro` sets `` + `--font-head` from these. ## Languages (i18n) Three locales, static, build-time — **Spanish** (default, `/`), **English** (`/en/`), **Euskera** (`/eu/`). - One template `src/pages/[...lang].astro` with `getStaticPaths` emits all three routes (`/`, `/en/`, `/eu/`). No runtime/JS routing. The default locale lives at the bare root: its `getStaticPaths` entry is `{ lang: undefined }` → `locale: "es"`; en/eu get an explicit prefix. The `LANG_*_PATH` maps in `ui.ts` mirror this (es → no prefix). Changing the default = swap which locale is `undefined` in all four pages' `getStaticPaths` + the `LANG_*_PATH` maps. - Translatable copy lives in `src/i18n/ui.ts` (`ui[locale]`), keyed identically across locales. `site.ts` holds only language-neutral config (URLs, theme, launcher files, stat/feature *structure*). - Product / in-game literals never translated — `site.ts` `LITERALS` (`authlib-injector`, `Multiplayer`, `Add Server`). OS names + version too. - Sentences with links/kbd are split into parts (pre/post/link, pa/pb) so markup stays in the template, not the strings. - Nav has a pixel `EN · ES · EU` switcher (`.lang-switch`); active locale highlighted. `` + `` set per page. - Adding a locale: extend the `Lang` union + `ui`/`LANG_*` maps in `ui.ts`, add a `getStaticPaths` entry. No template change. ## Content (real modded-LAN flow, all in site.ts) - Hero: wordmark, eyebrow `Modded LAN · NeoForge 1.21.1`, tagline, sub, server address `mc.${BASE_DOMAIN}` (copy chip), "How to Join". - Status: static server-list panel (no fake live count — **option B**) + 4 honest stat tiles (Mods / NeoForge ver / Player slots / Backups). - Features ×4: kitchen-sink pack · self-hosted Drasl identity+skins · one-click packwiz · built-to-last (homelab + 6h backups). - How to Join ×4: **1** Fjord launcher (3 OS) → **2** authlib account (`auth.${BASE_DOMAIN}/authlib-injector`) + register link → **3** packwiz import (`pack.${BASE_DOMAIN}/pack.toml`) → **4** connect. - Footer: Mojang trademark disclaimer. ## Launcher download links (Option A — stable symlinks) Page links to fixed names so they survive launcher version bumps: - `fjord-windows-setup.exe` - `fjord-macos.dmg` - `fjord-linux-x86_64.AppImage` `tooling/fetch-launcher.sh` must create these as symlinks under `launcher/latest/` pointing at the real versioned assets. **Keep the names in sync** between `site.ts` and the script. Mounted so they resolve at `/launcher/latest/…` (see Caddy `10-static.caddy`). ## Assets — where to drop images All static images live under `landing/public/` and are referenced by an absolute `/path` (Astro copies `public/` to the site root verbatim). After changing any, rebuild the landing (`pnpm run build`). - **Header / hero logo** — `landing/public/logo.png` (current 707×148). Referenced as `/logo.png` in the hero (`[...lang].astro`, the `.hero-logo img`). Replace the file; keep a wide transparent PNG. Update the `width`/`height` attrs if the aspect changes. - **Favicon** — `landing/public/favicon.png` (square, ~175×175). Referenced as `/favicon.png` in the `` of all four pages (`[...lang].astro`, `register`, `account`, `fjord`). Replace the file. - **Server-status icon** — NOT a file you place here. The ServerCard pulls the live server icon over the SLP query (`ServerCard.astro`, the favicon data-URI), falling back to the `Creeper.astro` SVG. To change it, set `server-icon.png` on the Minecraft server (64×64), not in the landing. - **Features icons** — two options (`PixelIcon.astro`, fed by `site.ts` `features[]`): 1. *Procedural glyph* (default): pick `glyph ∈ shield|diamond|orb|heart` and `gold` per feature in `site.ts`. Add a glyph by editing the 7×7 `GLYPHS` map. 2. *Custom image*: drop a file in `landing/public/icons/.(png|svg)` and add `img: "/icons/.png"` to that feature's entry in `site.ts` `features[]`. `PixelIcon` renders the image (`.picon-img`) instead of the glyph. ## Tasks - [x] Vendor fonts (`tooling/fetch-fonts.sh`) → `public/fonts/` - [x] Port design → static Astro (components + main.css + index.astro) - [x] Real content in `site.ts`; theme knobs replace TweaksPanel - [x] i18n: en/es/eu via `[...lang].astro` + `i18n/ui.ts`; nav language switcher - [x] `BASE_DOMAIN=ulicraft.net pnpm run build` → verified `www/` output + screenshot - [ ] Extend `fetch-launcher.sh`: symlink stable names → versioned files - [ ] Confirm ingress serves apex from `./www` and `/launcher/latest/*` from `./launcher` - [ ] Optional: try `mood: nether` / `hero: split` for party day - [ ] Optional: wire real player count (option C) via mc-monitor JSON if wanted ```