# How the TekTinkers Portal is built

Public notes for anyone who wants to borrow, steal, remix, or learn from [portal.tektinkers.com](https://portal.tektinkers.com/).

This document covers the **public page**: HTML structure, CSS, JavaScript, and the small PHP JSON endpoints the front-end already calls. It does **not** include login credentials, crypto, network information, internal topography/topology, databases, or other private infrastructure.

- Live page: https://portal.tektinkers.com/
- This write-up: https://portal.tektinkers.com/built.html
- Markdown copy: https://portal.tektinkers.com/built.md

You may save, scrape, print, or mirror this page. Techniques here are meant to be reused.

Brand logos, character art, and Astrozaar game sprites still belong to their projects. Reuse the **methods**. Ask before republishing those images as your own brand.

---

## What this includes

- Layout, palette, type, and page chrome
- Starmap views, hash URLs, crumbs, visit buttons
- SVG connector traces
- Canvas “logo spheres” (spin, lighting, readable facing)
- Corona / sun glow around hub globes
- Universe starfield: sage parallax layers from Astrozaar (no shooting stars), on every desktop galaxy view, behind the Zemew and Astro Gaming scene fields
- Universe UFO Easter egg: Astrozaar `ufo_02` crosses the main Universe view after 20 seconds
- Zemew starfield: stars, shooting stars, meteors, space station, black hole
- Astro Gaming starfield: circular canvas field matching asrtogaming.com (twinkling stars + purple/cyan shooting stars), plus the floating astronaut
- Live status badges (public JSON only)
- Reduced-motion and mobile behavior

## What this leaves out

- Login credentials: usernames, passwords, API keys, cookies, session secrets
- Crypto, certificates, and other secrets
- Network information: IPs, internal hostnames, firewalls, reverse-proxy config, server topography/topology
- Databases, CMS, feed ingest internals
- Server OS and anything behind auth
- Step-by-step attack or exploit material

If a file is already served to browsers (HTML, CSS, JS, images), you can fetch it directly. That is the intended public source.

---

## Stack

The portal is a **static HTML page** plus two small PHP JSON proxies.

| Piece | Role |
| --- | --- |
| `index.html` | Markup for chrome, starmap nodes, visit links |
| `css/style.css` | Site chrome, palette, header/footer |
| `css/portal-network.css` | Starmap layout, spheres, corona, views |
| `js/portal-network.js` | Views, traces, spheres, starfield, astronaut, UFO Easter egg |
| `js/mc-status.js` | Polls public status JSON every 5s |
| PHP under `/api/` | Returns `{ online, players }` JSON |

No framework, no bundler. Cache-bust with `?v=` on CSS/JS after changes.

Fonts: [Syne](https://fonts.google.com/specimen/Syne) (display) and [DM Sans](https://fonts.google.com/specimen/DM+Sans) (body).

---

## Public files you can fetch

Front-end (always public):

- https://portal.tektinkers.com/
- https://portal.tektinkers.com/css/style.css
- https://portal.tektinkers.com/css/portal-network.css
- https://portal.tektinkers.com/js/portal-network.js
- https://portal.tektinkers.com/js/mc-status.js
- Images under https://portal.tektinkers.com/images/ (including [/images/ufo_02.png](https://portal.tektinkers.com/images/ufo_02.png))

Status JSON (already called by the page):

- `GET /api/mc_server_status.php` → Minecraft Java status for the TinkersCraft badge
- `GET /api/astrozaar_status.php` → live player count for the Astrozaar.io badge

Example shape:

```json
{ "online": true, "players": { "online": 3, "max": 20 } }
```

```json
{ "online": true, "players": 7 }
```

Those endpoints are read-only JSON with a few seconds of cache. They do not expose accounts or admin surfaces.

---

## Palette and type

CSS variables live in `css/style.css`.

| Token | Hex | Use |
| --- | --- | --- |
| Coral | `#e25445` | Accents, flares |
| Cream | `#f4e8c0` | Body text |
| Teal | `#65a5a3` | Links, traces, corona |
| Gold | `#c78441` | Highlights, hover |
| Sage | `#77866e` | Secondary glow |

Background is deep green-black (`#1a2826` / `#243330`) with a radial teal wash and a cream grain overlay.

```css
:root {
  --bg-deep: #1a2826;
  --text: #f4e8c0;
  --accent-teal: #65a5a3;
  --accent-coral: #e25445;
  --accent-gold: #c78441;
  --accent-sage: #77866e;
  --font-display: "Syne", system-ui, sans-serif;
  --font-body: "DM Sans", system-ui, sans-serif;
}
```

---

## Page chrome

Behind the content:

1. `.bg-flares` — large blurred coral / gold / sage blobs
2. `.grain` — tiled noise, low opacity
3. `.topo-particles` — slow drifting dots

Then a sticky header, a short hero (`.gateway`), the Universe map (`.topo`), and a footer. The public notes are linked from the footer in coral, with no underline. On desktop, Visit buttons overlay the top-right of the map so they do not push the constellation or the back control down.

The homepage is framed as the map of this digital world, not a directory of links. Hero copy: eyebrow **TekTinkers Universe**, title **The map of this digital world**, lead about zooming a galaxy and entering a world. The document title is `TekTinkers Universe`. Visit / Join buttons are how you leave a world on the map; they are not the page’s identity.

---

## Starmap HTML contract

The map is `#topo-map`. Every planet/hub is a `.node` with data attributes:

| Attribute | Meaning |
| --- | --- |
| `data-node` | Unique id (`zemew`, `weather`, `astroio`, …) |
| `data-in` | Space-separated views where it is visible (`galaxy`, `zemew`, …) |
| `data-parent` | Parent node id (used for traces) |
| `data-hub` | If set, clicking zooms to that view instead of leaving the site |
| `data-bow="flip"` | Reverse the connector curve (Recall Arena) |

Layout classes:

- `node--live` / `node--soon`
- `node--west` `node--east` `node--north` `node--south` — label side
- `node--hub` — universe-level hub (zooms into that galaxy)
- `node--star` — applied in JS to the current system’s sun
- `node--sub` — moons (Astrozaar.io, NachoNut)

Positions on desktop are CSS custom properties:

```css
.topo__map[data-view="zemew"] .node[data-node="zemewstudios"] {
  --nx: 31%;
  --ny: 32%;
}
```

The node is `position: absolute; left: var(--nx); top: var(--ny)`.

Marker types:

- **Sphere:** `.node__marker.node__sphere.js-logo-sphere` + wrap image + 128×128 canvas
- **Photo:** `.node__marker.node__photo` + `object-fit: cover` (ZemewHosting / PC / LAN)
- **Icon:** plain `.node__marker` + transparent PNG (Astrozaar rocket)

---

## Views and URL hash

`js/portal-network.js` keeps a `VIEWS` table. Internally the top view id is still `galaxy` (hash is empty). Under the TekTinkers globe the caption is the usual **TekTinkers** label with **Universe** below it at 75% of the previous title size. In TekTinkers Galaxy the **Network** title is omitted, so the stack is **TekTinkers** plus **Galaxy**. Zooming a hub opens that network’s **Galaxy** (`tektinkers`, `zemew`, `astrogaming`). TekTinkers Galaxy includes Weather, Tools, Cloud, TinkersTunes (`/images/tunes_sphere_wrap.png`, two Subsonic logos on a slate wrap), Recall, and Gaming (`/images/gaming_sphere_wrap.png`, Twitch purple `#9146ff` with a controller on both faces, links to https://tektinkers.com/gaming/). Top-right Visit buttons in that view cover the hub plus each of those spheres (`#topo-visits-tektinkers`).

`setView(name)`:

1. Sets `#topo-map[data-view="…"]`
2. Shows nodes whose `data-in` includes that view
3. Marks the current sun with `node--star`
4. Rebuilds crumbs and Visit buttons
5. Starts/stops view-only effects (astronaut, starfield, UFO Easter egg)
6. Writes `#tektinkers`, `#zemew`, `#astrogaming` (Universe clears the hash)

CSS then shows/hides nodes:

```css
.topo__map[data-view] .node { display: none; }
.topo__map[data-view="zemew"] .node[data-in~="zemew"],
.topo__map .node.is-in-view { display: flex; }
```

Below `1100px` the map becomes a stacked list (no constellation coordinates).

`prefers-reduced-motion: reduce` freezes spins, starfield motion, the astronaut, and the UFO Easter egg.

---

## Connector traces

An SVG overlay `#topo-svg` sits behind nodes (`z-index: 1`). JS draws a path from each visible node to its parent (or to the current sun).

- Live links: solid gradient stroke
- Coming-soon: dashed, lower opacity
- Hover/focus: `.is-hot` brightens the related traces

The algorithm is “center of marker → parent marker,” with a slight curve. `data-bow="flip"` mirrors that curve (Recall Arena). Resize and view changes schedule a redraw.

Layer order (back to front):

1. Sage starfield canvas (`z-index: 0`)
2. Universe UFO Easter egg (`#topo-ufo`, also `z-index: 0`, immediately after that canvas in the DOM)
3. Traces (`z-index: 1`)
4. Hub / corona / flares (`z-index: 3`)
5. Nodes, labels, buttons (`z-index: 4`)
6. Visit chrome (desktop overlay `z-index: 8`)

The saucer therefore crosses in front of the stars and behind spheres, connecting lines, text, corona, flares, and Visit buttons.

---

## Logo spheres

Any `.js-logo-sphere` with a source image and a canvas is turned into a spinning globe.

### Texture

- Default: treat the PNG as a **billboard** on a sphere, keep the logo readable (`data-facing="readable"` flips the back so text is not mirrored).
- `data-map="wrap"`: sample as a full equirectangular wrap (weather, recall, gaming, TinkersTunes).
- `data-spin-ms="21500"`: seconds per revolution (attribute is milliseconds).
- `data-spin="-1"`: reverse direction.
- `data-tilt`: axial tilt in degrees.
- `data-zoom`: crop in on a logo.

### Lighting

Each pixel on the disk has a normal. Light is either:

- **Viewer** — highlight in the middle (the “sun” you are looking at)
- **Hub sun** — direction toward another globe’s on-screen position

In Universe view: Zemew and Astro Gaming take light from TekTinkers. In Zemew view: the Zemew globe is viewer-lit; ZemewStudios and NachoNut take light from Zemew. In Astro Gaming view: the hub is viewer-lit; Twitch / Discord / TikTok / All Out take light from the Astro Gaming sun.

Dark-side lift (halfway toward full brightness) on Zemew, Astro Gaming, ZemewStudios, NachoNut, and Gaming so logos stay readable. Their CSS `::after` shade is also halved.

CSS `::after` on `.node__sphere` adds a specular blob and a shade gradient using `--sun-x`, `--sun-y`, `--sun-shade`.

### Corona

`.topo__hub-flames` is a stack of blurred conic gradients and radial masks around the globe (not a particle system). The full `.topo__hub-flames--grand` corona (halo, wide glow, spinning core, rim, licks, and seven solar flares) is on the TekTinkers sun in Universe view, the Zemew sun in Zemew Galaxy, and the Astro Gaming sun in Astro Gaming Galaxy. In Universe view the Zemew and Astro Gaming globes use the same grand corona at half strength (`.topo__hub-flames--soft`: `isolation: isolate`, opacity `0.29` vs `0.58`, flares use normal blend instead of screen) so TekTinkers stays the brightest sun. Flares on those two globes, and on the Zemew and Astro Gaming galaxy suns, are at half opacity of the TekTinkers jets. Their flare angles, delays, and lick rotation are offset so the three suns do not erupt in lockstep. In Astro Gaming Galaxy the flares use extra blur and normal blend (not screen) so they ease into the black circular field. The black-hole punch in Zemew uses the globe box, not the 28rem flame box, so the hole still passes behind the sphere.

---

## Universe starfield

On every desktop galaxy view. Canvas `#topo-universe-stars` fills the map (`left/top: 0; width/height: 100%` — a canvas will not stretch from `inset` alone) with a radial fade around the constellation, behind traces and globes. No black fill — sage dots over the existing map. JS sizes the bitmap from the map box, not the canvas’s intrinsic 300×150 default. It sits behind the view-specific scene canvases (same `z-index: 0`, earlier in the DOM): `#topo-zaar-stars` in Zemew (Astrozaar artist’s corner) and `#topo-astro-stars` in Astro Gaming (the circular asrtogaming.com field). The fade uses a white-to-transparent mask (and `mask-mode: alpha`) so Chrome does not treat `-webkit-mask-image` as a luminance mask and punch the stars out.

The pattern matches [astrozaar.io](https://astrozaar.io/): three parallax layers at depths `0.3` / `0.6` / `1.0` (counts 90 / 120 / 150, the game’s 3:4:5 ratio scaled to the map), radius `0.5–2`, alpha `0.3–1`, twinkle `0.4 + 0.6 * abs(sin(t/400 + offset))`. A slow camera drift makes nearer stars move faster. Colors are sage-family (`#5d6a56`, `#77866e`, `#8d9c83`, `#c4d0b8`) instead of the game’s white/cyan/pink/yellow. No shooting stars, meteors, or other props.

---

## Universe UFO Easter egg

Main Universe view only (`data-view="galaxy"`), desktop, and not when `prefers-reduced-motion` is on. Markup is `#topo-ufo`, an `img` of [/images/ufo_02.png](https://portal.tektinkers.com/images/ufo_02.png) placed immediately after `#topo-universe-stars`. After **20 seconds** on Universe it makes a left→right or right→left pass, then later random passes (about 18–40 seconds apart). Leaving Universe resets the 20-second wait.

It uses Astrozaar’s green `ufo_02` cruise, not the beam craft:

- Draw size **48 CSS px** (the game’s in-arena size)
- `tickSpeed = 4 + Math.random() * 2`, speed `(tickSpeed * 20) / 60` CSS px per ~16ms frame (~80–120 px/s)
- Heading wiggle is the game’s `(Math.random() - 0.5) * 0.5`, but only on the **20 tps** server tick (not every animation frame) and clamped to about ±22° of the travel heading so the pass stays left→right or right→left
- Spawns off one side on a nearly horizontal heading, flips the sprite when going left, exits the far side
- `pointer-events: none`, same `z-index: 0` as the star canvases, later in the DOM: in front of the sage stars, behind traces, globes, labels, buttons, corona, and flares

Hidden on the mobile list layout (`max-width: 1099px`).

---

## Zemew starfield

Only in `data-view="zemew"`, desktop. `#topo-zaar-stars` is a canvas behind the traces and in front of the sage Universe field.

Draw order each frame:

1. Parallax star dots + twinkle
2. Black hole (large, slow, counter-clockwise spin)
3. Meteors (Astrozaar sprites, edge-to-edge, tumble)
4. Space station (no spin, slower than meteors, above rocks)
5. Shooting-star streaks
6. Fade all four canvas edges
7. Soft punch-out where the Zemew corona sits so the hole passes *behind* the sun

Meteor / station / black-hole speeds are tuned to match the Astrozaar menu field: rocks about 20–50 CSS px/s with a small spin; station ~18–28 px/s; black hole slower still, diameter a bit under the short axis of the field.

Four-edge fade: `destination-out` linear gradients on left/right/top/bottom (~20% of the canvas) so objects ease in instead of clipping on a hard box.

Sprites (public images):

- `/images/meteor02_128.png`
- `/images/meteor04_128.png`
- `/images/meteor05_128.png`
- `/images/spacestation_01.png`
- `/images/blackhole_03.png`

Astrozaar.io is a separate game. Those sprites are shown here as atmosphere, not as a license to ship the game.

---

## Astro Gaming starfield

Only in `data-view="astrogaming"`, desktop. Canvas `#topo-astro-stars` sits behind traces and globes, and in front of the sage Universe field (`z-index: 0`).

The box is a centered square: `left: 50%; top: 48%;` with `width` and `height` both `min(90vh, 70vw)`, then `transform: translate(-50%, -50%)`. `border-radius: 50%` plus a CSS mask make it a soft circle instead of a clipped oval. Mask stops are **white** (not black): Chrome’s `-webkit-mask-image` is a luminance mask, so black would hide the whole field.

```css
mask-image: radial-gradient(
  circle closest-side at 50% 50%,
  #fff 0%,
  #fff 40%,
  transparent 90%
);
```

The field matches [asrtogaming.com](https://asrtogaming.com/): black fill, about 200 twinkling white stars with purple/cyan glow (`#ad1fff` / `#39c1ed`), and shooting stars from the edges with long colored tails. JS sizes the bitmap from the canvas box after the view is shown. The teal map grid (`.topo__map::before`) is hidden in this view so the field reads as space.

---

## Astronaut

Astro Gaming view only. `/images/astro_astronaut.png` is absolutely positioned and bounced inside the map with a slow spin — same idea as the floater on asrtogaming.com. It sits with the starfield (`z-index: 0`, `pointer-events: none`), in front of the canvas pixels but behind traces, globes, coronas, and labels. Hidden with CSS except `data-view="astrogaming"`.

---

## Status badges (PHP, public contract)

`js/mc-status.js` finds `#tc-mc-status` and `#az-arena-status`, then `fetch`es JSON every 5 seconds (`cache: "no-store"`). On success it sets `ONLINE` plus a player count; on failure, `OFFLINE`.

PHP side is only a **read-only proxy**:

1. Set `Content-Type: application/json`
2. Optional few-second file cache
3. Probe a **public** game endpoint
4. Return a small object (`online`, `players`)

No database. No sessions. CORS `*` because the payload is not private.

If you reuse this, point the probe at **your** public game, not someone else’s private host.

---

## Borrowing recipes

**Constellation of links:** nodes + `data-in` + `data-parent` + one `data-view` on the map + CSS `--nx/--ny`.

**Spinning logo planet:** square PNG, `border-radius: 50%`, canvas overlay, sample UV from a unit sphere, multiply by a Lambert term.

**Sun from another planet:** `getBoundingClientRect()` of both globes, build a light vector, write `--sun-x` / `--sun-y` for the CSS highlight.

**Starfield layer:** one canvas, `pointer-events: none`, `z-index` under SVG traces. Universe uses Astrozaar’s three depth layers and twinkle, in sage, with no streaks. Zemew fades edges in the draw loop with `destination-out`. Astro Gaming uses a square canvas, `border-radius: 50%`, and a circular `mask-image` so the field never shows a flat box edge. Mask gradients use white, not black — Chrome’s prefixed mask is luminance, and black would hide the canvas.

**Side-to-side Easter egg:** an `img` after the star canvas, same `z-index`, cruise on a clamped heading, flip when going left.

**Live “ONLINE 3/20”:** tiny JSON endpoint + 5s poll. Never put secrets in that JSON.

Copy the CSS/JS from the live URLs above. Change art, names, and URLs to yours.

---

## Reduced motion and mobile

- `prefers-reduced-motion: reduce` — no sphere spin, no starfield motion, astronaut parked, no UFO
- `max-width: 1099px` — vertical list, starfield hidden, no absolute constellation

Respect both if you fork the map.

---

## Credits

- TekTinkers portal UI and this write-up: meant to be learned from
- Zemew, NachoNut, TinkersCraft, Astro Gaming: their own sites and marks
- Astrozaar meteors, station, black hole, and `ufo_02`: from the Astrozaar game, used here as scene dressing

Questions about reuse of **art** (not code): ask the project that owns the image.

---

Last updated: 26 August 2026. These notes are updated when the public portal page changes.
