From fe58290728ded4057cd73a6c00548491c7012d49 Mon Sep 17 00:00:00 2001 From: Kartios Date: Tue, 11 Aug 2026 18:24:53 -0700 Subject: [PATCH] California gets roads, traffic, and a car to follow --- .gitea/workflows/gates.yml | 21 + ARCHITECTURE.md | 25 +- ASSET_RESEARCH.md | 92 ++++ BUILD_PLAN.md | 165 ++++++++ README.md | 19 +- index.html | 75 +++- package.json | 2 + scripts/check-dependency-licenses.mjs | 64 +++ src/assets/actors/common.ts | 70 +++ src/assets/actors/crow.ts | 161 +++++++ src/assets/actors/dog.ts | 171 ++++++++ src/assets/actors/humanoid.ts | 261 ++++++++++++ src/assets/actors/index.ts | 40 ++ src/assets/vehicles/index.ts | 18 + src/assets/vehicles/modelX.ts | 589 ++++++++++++++++++++++++++ src/cities/california.ts | 149 +++++++ src/engine/blocks.ts | 67 ++- src/engine/buildingGlyph.ts | 406 ++++++++++++++++++ src/engine/markers.ts | 21 + src/engine/roadTraffic.ts | 256 +++++++++++ src/engine/scene.ts | 65 ++- src/engine/structures.ts | 56 ++- src/engine/types.ts | 33 ++ src/input/vehicle.ts | 81 ++++ src/interiors/types.ts | 11 +- src/interiors/walker.ts | 329 ++++++++++++++ src/main.ts | 211 +++++++-- src/offices/sites.ts | 33 ++ src/test/actorAssets.test.ts | 113 +++++ src/test/buildingGlyph.test.ts | 59 +++ src/test/californiaCity.test.ts | 33 ++ src/test/californiaTransport.test.ts | 139 ++++++ src/test/office.test.ts | 36 +- src/test/roadTraffic.test.ts | 83 ++++ src/test/vehicleAsset.test.ts | 80 ++++ src/test/vehicleController.test.ts | 183 ++++++++ src/test/vehicleInput.test.ts | 48 +++ src/test/vehicleSim.test.ts | 102 +++++ src/test/walker.test.ts | 131 ++++++ src/transport/california.ts | 380 +++++++++++++++++ src/transport/types.ts | 94 ++++ src/transport/vehicleController.ts | 436 +++++++++++++++++++ src/transport/vehicleSim.ts | 253 +++++++++++ 43 files changed, 5593 insertions(+), 68 deletions(-) create mode 100644 ASSET_RESEARCH.md create mode 100644 BUILD_PLAN.md create mode 100644 scripts/check-dependency-licenses.mjs create mode 100644 src/assets/actors/common.ts create mode 100644 src/assets/actors/crow.ts create mode 100644 src/assets/actors/dog.ts create mode 100644 src/assets/actors/humanoid.ts create mode 100644 src/assets/actors/index.ts create mode 100644 src/assets/vehicles/index.ts create mode 100644 src/assets/vehicles/modelX.ts create mode 100644 src/cities/california.ts create mode 100644 src/engine/buildingGlyph.ts create mode 100644 src/engine/roadTraffic.ts create mode 100644 src/input/vehicle.ts create mode 100644 src/interiors/walker.ts create mode 100644 src/test/actorAssets.test.ts create mode 100644 src/test/buildingGlyph.test.ts create mode 100644 src/test/californiaCity.test.ts create mode 100644 src/test/californiaTransport.test.ts create mode 100644 src/test/roadTraffic.test.ts create mode 100644 src/test/vehicleAsset.test.ts create mode 100644 src/test/vehicleController.test.ts create mode 100644 src/test/vehicleInput.test.ts create mode 100644 src/test/vehicleSim.test.ts create mode 100644 src/test/walker.test.ts create mode 100644 src/transport/california.ts create mode 100644 src/transport/types.ts create mode 100644 src/transport/vehicleController.ts create mode 100644 src/transport/vehicleSim.ts diff --git a/.gitea/workflows/gates.yml b/.gitea/workflows/gates.yml index 9245a89..180d49e 100644 --- a/.gitea/workflows/gates.yml +++ b/.gitea/workflows/gates.yml @@ -13,6 +13,7 @@ # clean-clone a fresh checkout installs, builds and passes its tests # zero-config-boot the API answers health when handed nothing at all # no-binary-art src/** carries no committed binary assets +# dependency-terms every locked package uses a reviewed permissive license # # They are deliberately independent and run in parallel: a broken build should # not hide a licensing regression. @@ -120,3 +121,23 @@ jobs: - name: no binary art under src/ run: node scripts/check-no-binaries.mjs + + # ---- Job 4: package terms stay inside the reviewed perimeter ---- + # + # Lockfile metadata makes this dependency-free. A new license stops here and + # gets reviewed explicitly instead of arriving transitively with a routine + # version bump. + dependency-terms: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "24" + + - name: dependency license allowlist + run: node scripts/check-dependency-licenses.mjs + + - name: SPDX SBOM can be generated from the lockfile + run: npm sbom --package-lock-only --sbom-format=spdx > /tmp/tera.spdx.json diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index d16ec8c..73ed4fb 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,6 +1,6 @@ # Tera — architecture -**Tera** is the map view: a city from above, rendered in three.js, public and +**Tera** is the map view: California and its cities from above, rendered in three.js, public and self-hostable. **Spaces** is the other half — the offices you walk into. They are one engine and one asset library, seen from outside and from inside. @@ -78,9 +78,11 @@ lumbridge-simulate/ │ └── scene.ts # lights, sky, camera flights, render loop ├── src/interiors/ # the inside of the world — floorplans, desks, presence ├── src/assets/ # the library. Original meshes and materials, Apache 2.0. +├── src/transport/ # serialisable route packs + renderer-free fixed-step sim ├── src/cities/ # data packs. Pure geography, no code. +│ ├── california.ts # state/corridor scale; LA ↔ SF continuity │ ├── sf.ts # ~1000 lines of coastline, hills, districts, landmarks -│ └── la.ts # LA / OC / Riverside +│ └── socal.ts # LA / OC / Riverside ├── src/adapters/ # where outside data plugs in │ └── workie.ts # Workie API -> Marker[] └── src/main.ts # the standalone demo app @@ -97,6 +99,25 @@ and a city share the projection, camera, lighting and render loop, and differ only in what they put in the scene. That is why `scene.ts` owns the loop and knows nothing about terrain specifically. +`transport` also stays outside the renderer. A `TransportPack` is plain JSON and +`VehicleSimulation` returns plain geographic poses. Three.js enters only in the +`roadTraffic` render layer, which projects poses through the active `World`. +That separation lets a future authoritative session server replay the same +route simulation without importing a browser or a GPU. + +### 2.1 Product/control-plane boundary + +Tera/Spaces is the authoritative renderer and simulation client. The separate +`lumbridgecorp` service remains the control plane: identity, tenant registry, +membership, billing, provisioning, and short-lived launch grants. It must not +grow a second world renderer or own frame-by-frame simulation. + +The public Tera build remains zero-config and useful without any service. Hosted +presence, multiplayer, profiles, webcam faces, and media surfaces arrive through +adapters whose absence cannot stop the city, office, traffic, or sky from +running. This is the replacement path for the retired Phaser World 1; it is not +a migration of that renderer. + --- ## 3. Three rules that keep Apache 2.0 honest diff --git a/ASSET_RESEARCH.md b/ASSET_RESEARCH.md new file mode 100644 index 0000000..485af2f --- /dev/null +++ b/ASSET_RESEARCH.md @@ -0,0 +1,92 @@ +# Asset intake research + +Tera can learn from an open-source asset without turning that project into a +dependency. The default is to internalise the capability: identify the smallest +useful visual rule, rebuild it against Tera's contracts, and keep the result +code-only and deterministic. Copy source only when a rewrite would add no value, +and copy no binary art into `src/**`. + +This review was performed on 2026-08-11 for the first office exteriors. + +## California corridor vehicle + +The black corridor vehicle is an original procedural Three.js asset authored in +`src/assets/vehicles/modelX.ts`. An AI-generated image was used only as a +non-shipping art-direction reference for stance, surface hierarchy, camera +distance, and late-afternoon contrast. No pixels, mesh, logo, texture, or source +file from that reference is distributed by Tera. The implementation is an +unbadged code-generated crossover with two explicit detail tiers, shared +geometry/material cloning, named wheel joints, and deterministic tests. + +The corridor route pack is also hand-authored. Its nodes are coarse simulation +waypoints, not navigation data, and each segment records its own provenance and +fitness note in `src/transport/california.ts`. I-5 is presented honestly with +its I-580 and I-80 connection into San Francisco. + +## Actor set + +The customizable humanoid, anonymous office dog, and anonymous Tera crow under +`src/assets/actors/` are original procedural TypeScript. They use no downloaded +mesh, texture, rig, animation, logo, or motion-capture data. Each asset exposes +named pose joints, metre-scale metrics, shared-resource cloning, and explicit +disposal so a later presence layer can animate many actors without changing the +asset or ownership contracts. A caller may provide a face texture to the +humanoid, but that texture remains caller-owned and is never bundled by core. + +## Sources assessed + +| Source | Licence and useful part | Decision for Tera | +| --- | --- | --- | +| [BasicProceduralBuilding](https://github.com/achrefelouafi/BasicProceduralBuilding/tree/f97984d1c2e5fae484a6c7d0dc01f721c117a6ba) | MIT. A façade grid classifies ground, window, corner, and roof cells, then instances pieces from a Blender-authored GLB kit. | Keep the façade grammar. Do not import the GLB, its coordinate conversion, or its per-kit-mesh runtime. Tera's office glyph implementation is original code at map scale. | +| [KayKit City Builder Bits 1.0](https://github.com/KayKit-Game-Assets/KayKit-City-Builder-Bits-1.0/tree/63976910ca04d16f0fc531b9c614244be8128713) | CC0 low-poly city pieces in GLTF, OBJ, and FBX, with a shared texture atlas. | Good silhouette reference and a viable source for a future optional art pack. Do not place its binaries in core `src/**`; core remains code-only. | +| [img2threejs](https://github.com/img2threejs/img2threejs) | Apache-2.0 procedural Three.js reconstruction code. | Workflow reference only. Its image-to-scene problem is broader than office exteriors. | +| [Claude-of-Duty](https://github.com/mshumer/Claude-of-Duty) | MIT, code-generated materials and modular Three.js buildings. | Reference for generated-material and browser-QA techniques. It is too large and game-specific to copy wholesale. | +| [Khronos glTF Sample Assets](https://github.com/KhronosGroup/glTF-Sample-Assets/blob/main/Models/Models.md) | A useful interoperability corpus, but every model has its own licence. | Test fixtures only after checking the individual asset's licence; it is not one uniformly reusable art library. | +| [pmndrs market](https://github.com/pmndrs/market) | CC0 asset registry and delivery tooling. | Discovery source, not a runtime dependency. Tera does not need an asset API to draw three map glyphs. | +| [open-source-3D-assets](https://github.com/ToxSam/open-source-3D-assets) | A catalogue of downloadable assets with licences recorded per entry. | Discovery source only; verify the original file and licence before any intake. | + +## Pilot decision: office buildings on the city board + +The reusable idea from BasicProceduralBuilding is its grammar, not its kit. +At Tera's city scale, one-metre modules are usually sub-pixel. Recreating them +as individual geometry would spend triangles and draw calls on information the +camera cannot retain. The Tera-native implementation therefore uses: + +- a small serialisable `BuildingGlyph` on the existing generic `Marker` type; +- pure layouts for tower, hangar, courtyard, and block silhouettes; +- one instanced window mesh per destination, plus a few solid shell pieces; +- seeded façade variation, so reloads never reshuffle the skyline; +- reserved city footprints, so the stable glyph supersedes an anonymous block + or a coarser hand-authored landmark at the same address instead of nesting; +- the existing marker raycaster and office-open path, so no office semantics + enter the engine. + +No third-party source or binary asset was copied for this pilot, so no new +third-party attribution is required in `NOTICE`. The links above record the +research provenance and the implementation comments record the design lineage. + +## Intake rules + +Before copying any future asset or source file: + +1. Pin the upstream URL and commit, and record exactly which files are copied. +2. Prefer CC0 for artistic output. Apache-2.0, MIT, and BSD code can be used only + with their required licence and notice text preserved. A Tera rewrite does + not erase upstream authorship when source was actually copied. +3. Do not intake unlicensed work, non-commercial licences, share-alike content, + logos, trademarks, or scraped map/imagery data. +4. Keep binary art out of `src/**`. A future optional art pack belongs behind a + registry or self-hosted `/art/*` URL and must keep its own provenance ledger. +5. Copy the smallest coherent part, translate it to Tera's data and disposal + conventions, add deterministic tests, and verify it in the GPU browser. +6. Update `NOTICE` in the same change whenever copied source or bundled art has + an attribution or licence-preservation requirement. + +## Useful next targets + +The office catalogue already covers the common furniture. The most valuable +remaining procedural pieces are circulation and place-making rather than more +chairs: a stair/landing asset for multi-storey packs, a kitchen island/service +counter, and a small exterior plaza kit (planters, bollards, and benches). Each +is simple enough to author in Tera's metre-based asset registry; use a CC0 kit +such as KayKit as shape reference only when it materially improves the result. diff --git a/BUILD_PLAN.md b/BUILD_PLAN.md new file mode 100644 index 0000000..daa4639 --- /dev/null +++ b/BUILD_PLAN.md @@ -0,0 +1,165 @@ +# Tera / Spaces build plan + +This is the execution order for the California world. It is intentionally a +dependency plan rather than a feature wish list: each milestone leaves behind a +tested substrate the next one can safely use. + +## North star + +One continuous California world at three nested scales: + +1. **State / corridor** — Los Angeles to San Francisco, route traffic, flyover. +2. **City / exterior** — detailed Bay Area and Southern California boards, + office buildings as real destinations. +3. **Office / metre scale** — walkable interiors, people, rooms, and screens. + +Every scale supports **Observe**. Selected actors and vehicles additionally +support **Play**. Public geometry and ambient simulation work from a static +clone; identity, private presence, realtime state, webcam faces, and shared +screens are hosted capabilities layered on top. + +## Work lanes + +| Lane | Owns | Can run in parallel with | +| --- | --- | --- | +| World + simulation | transport packs, cells, origin rebasing, controllers | assets, UI, service contracts | +| Asset craft | vehicles, humanoids, dog, crow, buildings, animation | every milestone once interfaces are frozen | +| Product + camera | chapters, follow/chase/walk cameras, input, accessibility | simulation and assets | +| Realtime + media | session authority, interest zones, ACLs, WebRTC surfaces | solo play after state contracts exist | +| Quality | deterministic replays, browser captures, performance budgets, provenance | continuous; never a final cleanup phase | + +The critical path is world coordinates → deterministic control → walkable actor +contract → authoritative sessions → private media. Art can stay one milestone +ahead; realtime/media must not jump ahead of the actor and access contracts. + +## M0 — contracts and provenance + +Status: **in progress**. + +- Tera/Spaces is the renderer; `lumbridgecorp` is the control plane. +- Apache-2.0 remains the outbound code license. +- Every copied asset/data item gets a pinned source, license, hash, and intake + note. Original procedural assets record their design lineage. +- Add an automated asset-manifest gate, dependency license allowlist, and SBOM. + +Exit gate: a clean clone builds and tests without private services or binary +art; every shipped dependency, route, font, and asset has recorded provenance. + +## M1 — California roads and passive Model X traffic + +Status: **implemented and rendered on desktop and mobile; performance-budget +instrumentation remains**. + +- Coarse California board plus detailed Bay Area and SoCal boards. +- Serializable US-101 and I-5/I-580/I-80 route graphs. +- Procedural black Model X with follow and corridor detail tiers. +- Fixed-step deterministic traffic, both carriageways, instanced background + vehicles, and a chase/follow camera selected from route chapters. + +Exit gate: one hero completes either route on the road; the same seed and frame +sequence reproduce the same poses; route switching leaks no GPU resources; the +declared desktop/mobile frame budgets pass in browser capture. + +## M2 — solo playable driving + +Status: **deterministic control core plus keyboard, standard gamepad, touch, +chase, and driver-height camera integration implemented and rendered on desktop +and mobile**. The vehicle owns a controller state separate from its render rig. + +- Keyboard, gamepad, and touch input through one normalized action map. +- Chase and driver-height cameras. +- Assisted route following as a state machine; manual input takes over and can + hand back cleanly. +- Route guardrails, recovery/reset, pause and background-tab recovery. +- Fixed input traces for deterministic replay tests. + +Exit gate: finish both routes manually or assisted; no NaNs, ordinary-speed +tunneling, or tab-resume jumps; a recorded input trace replays identically. + +## M3 — walkable offices and actors + +Status: **collision/controller foundation and procedural actor assets +implemented; office camera/input integration and identity handoff pending**. + +- First-/third-person walker against the existing `Plan.blocked` wall segments. +- Door traversal and city ↔ office identity handoff. +- Customizable procedural humanoid for signed-in members. +- Anonymous office visitor is a dog; anonymous Tera visitor is a crow. +- Profiles begin as generated materials and parameters, not uploaded binaries. + +Exit gate: every room reachable through doors but never through walls; identity +survives scene changes; anonymous clients receive no private presence; 30-minute +soak produces no stuck or out-of-bounds actor. + +## M4 — authoritative multiplayer + +- Dedicated realtime session service; do not merge it into the control plane. +- Server-authoritative actor/vehicle poses, input validation, interpolation, + reconnect, and interest zones keyed by Tera cell / Office / Floor / Room. +- `lumbridgecorp` issues short-lived launch grants and revalidates membership. +- Delta snapshots around 10–15 Hz for nearby dynamic actors; media never rides + on this socket. + +Exit gate: two browsers converge within the declared latency; reconnect restores +the right cell; revocation ejects promptly; malformed and speed-hack state is +rejected; selected cell concurrency passes a repeatable load test. + +## M5 — profile and webcam faces + +- Generated profile face is the default. +- Webcam face is opt-in and ephemeral: explicit contextual permission, active + indicator, one-click stop, no recording/storage by default. +- Server-enforced visibility capabilities; unauthorized clients never receive + the private object or track. + +Exit gate: revocation and tab close stop every track; anonymous/unapproved peers +cannot subscribe; the full product works without a camera. + +## M6 — office media surfaces + +- `MediaSurface` records identify office/room/screen, ACL, source, and state. +- WebRTC SFU track after authorization and explicit viewer opt-in; Three.js gets + a video texture only while subscribed. +- Share tab/window by default, muted viewer autoplay, visible broadcast state, + presenter kill switch, late join and reconnect. + +Exit gate: no unauthorized subscription; revoke ends tracks and disposes GPU +textures; bandwidth adaptation and safe public placeholders work. + +## M7 — playable aircraft + +Keep today's aircraft ambient until vehicle, actor, camera, and network +abstractions are proven. A later `PlayableAircraft` reuses flight-source data for +the world but owns a separate controller and authority model. + +Exit gate: route/altitude bounds, camera and control handoff, multiplayer +validation, and no regression to ambient live ADS-B rendering. + +## Performance gates + +- Named benchmark scenes: p95 frame at or below 16.7 ms desktop and 33.3 ms on + the selected supported mobile tier. +- Hard budgets per scale for resident cells, triangles, draw calls, dynamic + actors, GPU memory, and media textures. +- Fixed simulation tick separated from rendering; instancing, LOD, pooling, + frustum/distance culling, and explicit resource disposal. +- Degrade shadows, traffic density, and far detail before input, access control, + identity, or privacy enforcement. + +## Immediate parallel build + +The California driving vertical slice now proves the transport, vehicle, +controller, input, camera, and responsive UI contracts. Continue in these lanes: + +1. **World:** corridor cell streaming, origin rebasing, city/office destination + transitions, and deterministic route-completion scenarios. +2. **Office:** integrate the walker with first-/third-person cameras, doors, + actor possession, and identity-preserving city ↔ office transitions. +3. **Assets:** add driver-view interior hints and animation/state adapters for + the existing humanoid, dog, and crow rigs; keep the code-only asset pipeline. +4. **Platform:** define versioned launch-grant, session, interest-zone, actor, + vehicle, and media-capability schemas plus their threat model—without coupling + the renderer to a hosted service. +5. **Quality:** add repeatable frame-time/draw-call budgets, screenshot baselines, + long-route and office soak tests, asset-manifest CI, and cross-browser input + coverage. diff --git a/README.md b/README.md index 4360ac5..e3e49a1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Tera -The map view of **Lumbridge Simulate** — cities from above, in three.js. +The map view of **Lumbridge Simulate** — California from above, in three.js. Its other half, **Spaces**, is the offices you walk into: one engine and one asset library, seen from outside and from inside. @@ -12,13 +12,18 @@ Apache 2.0. Runs at [tera.lumbridgecorp.com](https://tera.lumbridgecorp.com). ## What it is -An engine plus data packs. The engine renders terrain, coastline, a built city -on real street grids, bridges, roads, markers and air traffic. A *city pack* is +An engine plus data packs. The default board joins Los Angeles and San Francisco +with live deterministic traffic on US-101 and on the honest I-5 → I-580 → I-80 +approach. Choose either route chapter to follow the procedural black Model X. + +The engine renders terrain, coastline, built cities on authored street grids, +bridges, roads, markers, road traffic and air traffic. A *city pack* is pure data — coastlines, hills, districts, landmarks, camera chapters — so adding a city is a data contribution anyone can review, not a fork. -San Francisco ships today. Los Angeles / Orange County / Riverside is next; New -York after that. +California, the detailed Bay Area, and Los Angeles / Orange County / Riverside +ship today. The corridor is intentionally sparse; detailed cities remain their +own boards rather than forcing a 600 km world into one full-resolution mesh. A **plan view** sits top right: the board drawn flat, with the footprint of the camera's own frustum on it, so you can see where you are looking from outside @@ -128,6 +133,8 @@ answer is an RTL-SDR receiver: first-party data with nothing to comply with. ``` src/engine/ renderer — terrain, blocks, structures, markers, flights, scene, minimap src/cities/ data packs — pure geography, no code +src/transport/ serializable route packs and renderer-independent simulation +src/assets/ original procedural asset library src/adapters/ where outside data plugs in src/tools/ instruments — god-only, dynamically imported, never statically ``` @@ -144,3 +151,5 @@ what silently undoes it. ## Licence Apache License 2.0 — see [LICENSE](LICENSE) and [NOTICE](NOTICE). + +The ordered build plan and parallel work lanes live in [BUILD_PLAN.md](BUILD_PLAN.md). diff --git a/index.html b/index.html index b012477..2517d2d 100644 --- a/index.html +++ b/index.html @@ -60,7 +60,7 @@ @@ -80,7 +80,7 @@ * here is what the browser gets. It also means the first paint needs * nothing but this document — which is the entire reason the boot card at * the bottom of the body can be on screen before a single module has been - * fetched, let alone before the Bay Area heightfield has been built. + * fetched, let alone before the California heightfield has been built. * * Two constraints shape every rule below. * @@ -666,6 +666,54 @@ color: var(--ink-3); } + /* ---- Touch driving --------------------------------------------------- + Keyboard and standard gamepads need no chrome. Coarse pointers do, and + only while a route chapter owns the follow camera. Full words rather + than mystery glyphs: the controls disappear everywhere space is tight + except on the devices that cannot drive without them. */ + .drive-controls { display: none; } + @media (pointer: coarse) { + .drive-controls:not([hidden]) { + position: fixed; + left: 50%; + bottom: calc(var(--s3) + env(safe-area-inset-bottom)); + transform: translateX(-50%); + z-index: 5; + width: min(23rem, calc(100vw - var(--s4) * 2)); + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: var(--s1); + padding: var(--s1); + border: 1px solid var(--hairline); + border-radius: var(--r); + background: var(--glass-strong); + backdrop-filter: var(--blur); + -webkit-backdrop-filter: var(--blur); + box-shadow: var(--shadow); + } + .drive-control { + min-height: 48px; + border: 1px solid var(--hairline); + border-radius: var(--r-sm); + background: rgba(255, 255, 255, 0.08); + color: var(--ink); + font: inherit; + font-size: 10px; + letter-spacing: 0.05em; + text-transform: uppercase; + touch-action: none; + user-select: none; + -webkit-user-select: none; + } + .drive-control[aria-pressed="true"] { + border-color: rgba(242, 177, 52, 0.65); + background: rgba(242, 177, 52, 0.28); + color: var(--amber-ink); + } + /* The source line is useful but cannot sit underneath the controls. */ + body:has(.drive-controls:not([hidden])) .source { bottom: 7.5rem; } + } + /* ---- Responsive ------------------------------------------------------- Two breakpoints and no more. At 900 the left column stops being furniture and becomes a sheet you open; at 600 the plan stops fitting beside the @@ -835,15 +883,15 @@ - +
-

San Francisco

+

California

Tera · Lumbridge Simulate

@@ -870,6 +918,7 @@
19 chapters + choose 2 or 3 to follow [ ] city O office M plan @@ -886,6 +935,17 @@
+ +