Nimbin[12]?SDL & Graphics / sdl_runtime_compiler / commits / 9ae64e7

sdl_runtime_compiler git · main

SDL3 game for running and compiling code at runtime

sdl3 c++ compiler dlopen cmake · first commit 2026-04-19 · last commit 2026-07-03 (3 months ago) · synced 3 days ago · upstream: git.ide3.de/hsnr/sdl-runtime-compiler

C++ 72.3% C 26.2%
git clone https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler.gitwget https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler/archive/sdl_runtime_compiler.tar.gz

Refractor prefix private member vars

Christian Immanuel · 2026-06-01 08:25 · 9ae64e70b55c1e04e20b0d098518ec93084deb2d

 src/core/game.cpp                   |  50 ++-
 src/core/game.hpp                   |   9 +-
 src/entities/plate.hpp              |   8 +-
 src/entities/player.cpp             |   8 +-
 src/entities/player.hpp             |  20 +-
 src/entities/world.cpp              |  36 +--
 src/entities/world.hpp              |  16 +-
 src/rendering/floor_mandelbrot.cpp  |  44 +--
 src/rendering/floor_mandelbrot.hpp  |   6 +-
 src/rendering/gl_renderer.cpp       | 440 +++++++++++++-------------
 src/rendering/gl_renderer.hpp       | 152 ++++-----
 src/states/game_state.cpp           | 614 ++++++++++++++++++------------------
 src/states/game_state.hpp           |  77 +++--
 src/states/menu_state.cpp           | 154 ++++-----
 src/states/menu_state.hpp           |  48 ++-
 src/systems/celebration_system.cpp  |  38 +--
 src/systems/celebration_system.hpp  |  10 +-
 src/systems/compiler_system.cpp     |  33 +-
 src/systems/compiler_system.hpp     |  25 +-
 src/systems/freelook_system.hpp     |   4 +-
 src/systems/hint_system.cpp         |  22 +-
 src/systems/hint_system.hpp         |  16 +-
 src/systems/input_system.cpp        |   4 +-
 src/systems/level_runner_system.cpp |   4 +-
 src/systems/level_runner_system.hpp |  20 +-
 src/systems/mod_loader_system.cpp   |  46 +--
 src/systems/mod_loader_system.hpp   |  43 +--
 src/systems/particle_system.cpp     |  18 +-
 src/systems/particle_system.hpp     |   6 +-
 src/systems/physics_system.cpp      |  16 +-
 src/systems/physics_system.hpp      |  19 +-
 src/systems/plate_system.hpp        |   7 +-
 src/systems/pult_system.cpp         |  12 +-
 src/systems/pult_system.hpp         |   4 +-
 src/systems/word_ai_system.cpp      |   4 +-
 src/systems/word_ai_system.hpp      |  31 +-
 src/systems/word_anim.hpp           |  10 +-
 src/text_3d/text_label.cpp          | 116 +++----
 src/text_3d/text_label.hpp          |  59 ++--
 src/wall/editor_wall.cpp            |  20 +-
 src/wall/editor_wall.hpp            |   6 +-
 src/wall/wall_surfaces.cpp          |  14 +-
 src/wall/wall_surfaces.hpp          |   7 +-
 43 files changed, 1133 insertions(+), 1163 deletions(-)

diff --git a/src/core/game.cpp b/src/core/game.cpp
index fbcd20e..adeae78 100644
--- a/src/core/game.cpp
+++ b/src/core/game.cpp
@@ -9,7 +9,6 @@
 #include "states/menu_state.hpp"
 #include "systems/input_system.hpp"
 #include "systems/physics_system.hpp"
-#include "rendering/gl_renderer.hpp"
 
 
 Game::~Game() = default;
@@ -17,17 +16,13 @@ Game::~Game() = default;
 bool
 Game::init()
 {
-    {
-        std::error_code ec;
-        std::filesystem::create_directories("/tmp/game_mod_include", ec);
-        std::filesystem::copy_file(
-            "src/systems/game_mod_abi.h",
-            "/tmp/game_mod_include/game_mod_abi.h",
-            std::filesystem::copy_options::overwrite_existing, ec);
-        if (ec)
-            SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
+    std::error_code ec;
+    std::filesystem::create_directories("/tmp/game_mod_include", ec);
+    std::filesystem::copy_file("src/systems/game_mod_abi.h",
+                               "/tmp/game_mod_include/game_mod_abi.h",
+                               std::filesystem::copy_options::overwrite_existing, ec);
+    if (ec) SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
                         "Could not stage game_mod_abi.h: %s", ec.message().c_str());
-    }
 
 
     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
@@ -38,60 +33,53 @@ Game::init()
     SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
     SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
 
-    window = SDL_CreateWindow("game", 640, 480,
-            SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
-    if (!window) {
+    _window = SDL_CreateWindow("game", 640, 480, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
+    if (!_window) {
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_CreateWindow failed: %s", SDL_GetError());
         return false;
     }
 
-    SDL_SetWindowRelativeMouseMode(window, true);
+    SDL_SetWindowRelativeMouseMode(_window, true);
 
-    gl_renderer = cpputils::make_unique<GlRenderer>();
-    if (!gl_renderer->init(window))
-        return false;
+    _gl_renderer = cpputils::make_unique<GlRenderer>();
+    if (!_gl_renderer->init(_window)) return false;
 
-    SDL_StartTextInput(window);
+    SDL_StartTextInput(_window);
 
-    changeState(cpputils::make_unique<MenuState>());
+    change_State(cpputils::make_unique<MenuState>());
     return true;
 }
 
 
 void
-Game::changeState(cpputils::unique_ptr<IState> next)
+Game::change_State(cpputils::unique_ptr<IState> next)
 {
-    if (m_state)
-        m_state->on_Exit(*this);
+    if (m_state) m_state->on_Exit(*this);
 
     m_state = std::move(next);
 
-    if (m_state)
-        m_state->on_Enter(*this);
+    if (m_state) m_state->on_Enter(*this);
 }
 
 
 void
 Game::handle_Event(const SDL_Event& event)
 {
-    if (m_state)
-        m_state->handle_Event(*this, event);
+    if (m_state) m_state->handle_Event(*this, event);
 }
 
 
 void
 Game::update(double dt_ns)
 {
-    if (m_state)
-        m_state->update(dt_ns * 1e-9);
+    if (m_state) m_state->update(dt_ns * 1e-9);
 }
 
 
 void
 Game::render()
 {
-    if (m_state)
-        m_state->render(*window, *gl_renderer);
+    if (m_state) m_state->render(*_window, *_gl_renderer);
 }
 
 
diff --git a/src/core/game.hpp b/src/core/game.hpp
index b11eeb1..0845c02 100644
--- a/src/core/game.hpp
+++ b/src/core/game.hpp
@@ -6,20 +6,19 @@
 
 #include "states/istate.hpp"
 #include "utils/globals.hpp"
+#include "rendering/gl_renderer.hpp"
 
 
-class GlRenderer;
-
 class Game
 {
-    GlRendererPtr gl_renderer{};
-    SDL_Window* window{};
+    GlRendererPtr _gl_renderer{};
+    SDL_Window*   _window     {};
 
     cpputils::unique_ptr<IState> m_state;
 public:
     ~Game();
     bool init();
-    void changeState(cpputils::unique_ptr<IState> next);
+    void change_State(cpputils::unique_ptr<IState> next);
 
     void handle_Event(const SDL_Event& event);
     void update(double dt_ns);
diff --git a/src/entities/plate.hpp b/src/entities/plate.hpp
index 188d5da..1b1b043 100644
--- a/src/entities/plate.hpp
+++ b/src/entities/plate.hpp
@@ -14,9 +14,9 @@ class GlRenderer;
 // when stepped on.
 struct Plate final : public SolidBox
 {
-    static constexpr float HALF_W   = 0.6f;   // plate is wide and short
-    static constexpr float HALF_H   = 0.05f;
-    static constexpr float HALF_D   = 0.6f;
+    static constexpr float _HALF_W   = 0.6f;   // plate is wide and short
+    static constexpr float _HALF_H   = 0.05f;
+    static constexpr float _HALF_D   = 0.6f;
 
     bool   active    { false };   // player currently standing on it
     bool   was_active{ false };   // for edge detection
@@ -27,7 +27,7 @@ struct Plate final : public SolidBox
     Plate(Vec3 world_pos)
         : SolidBox(
               world_pos,
-              Vec3{ HALF_W, HALF_H, HALF_D },
+              Vec3{ _HALF_W, _HALF_H, _HALF_D },
               SDL_FColor{ 0.3f, 1.0f, 0.6f, 1.0f },   // bright cyan-green
               SDL_FColor{ 0.6f, 1.0f, 0.8f, 1.0f },
               false)                                  // not interactable like a pult
diff --git a/src/entities/player.cpp b/src/entities/player.cpp
index 179db30..61c08c9 100644
--- a/src/entities/player.cpp
+++ b/src/entities/player.cpp
@@ -51,13 +51,13 @@ Player::compute_Movement(const FrameInput& input)
 
     double n = dirX * dirX + dirZ * dirZ;
 
-    acc_x = 0;
-    acc_z = 0;
+    _acc_x = 0;
+    _acc_z = 0;
 
     if (n != 0) {
         double inv = 1.0 / SDL_sqrt(n);
-        acc_x = MOVE_ACCEL * ( c*dirX + s*dirZ) * inv;
-        acc_z = MOVE_ACCEL * (-s*dirX + c*dirZ) * inv;
+        _acc_x = MOVE_ACCEL * ( c*dirX + s*dirZ) * inv;
+        _acc_z = MOVE_ACCEL * (-s*dirX + c*dirZ) * inv;
     }
 }
 
diff --git a/src/entities/player.hpp b/src/entities/player.hpp
index ddee694..7dd0a3e 100644
--- a/src/entities/player.hpp
+++ b/src/entities/player.hpp
@@ -19,6 +19,8 @@ struct PlayerCollision
 
 struct Player
 {
+    double _acc_x{};
+    double _acc_z{};
 public:
     Player();
 
@@ -37,18 +39,10 @@ public:
              MOVE_ACCEL    = 60.0;
 
     void   compute_Movement(const FrameInput& input);
-    void   add_Look(int dx, int dy);
-
-    void   apply_Collision(const PlayerCollision& col);
-
+    void   add_Look        (int dx, int dy);
+    void   apply_Collision (const PlayerCollision& col);
     Vec3   interpolated_Pos(double alpha) const;
-
-    double get_AccX() const { return acc_x; }
-    double get_AccZ() const { return acc_z; }
-
-    void   respawn(const Vec3& spawn_pos);
-
-private:
-    double acc_x{};
-    double acc_z{};
+    double get_AccX        () const { return _acc_x; }
+    double get_AccZ        () const { return _acc_z; }
+    void   respawn         (const Vec3& spawn_pos);
 };
diff --git a/src/entities/world.cpp b/src/entities/world.cpp
index 485ea5c..9d7adda 100644
--- a/src/entities/world.cpp
+++ b/src/entities/world.cpp
@@ -3,14 +3,14 @@
 World::World()
 {
     initEdges();
-    wall_surfaces.generate(world_scale);
+    _wall_surfaces.generate(_world_scale);
 }
 
 
 void
 World::initEdges()
 {
-    const float r = world_scale;
+    const float r = _world_scale;
 
     const int map[24] = {
         0,1 , 1,3 , 3,2 , 2,0 ,
@@ -30,21 +30,21 @@ World::initEdges()
             if (j == 2) { a.z = va; b.z = vb; }
         }
 
-        edges[i] = { a, b };
+        _edges[i] = { a, b };
     }
 
-    size_t scale = static_cast<size_t>(world_scale);
+    size_t scale = static_cast<size_t>(_world_scale);
     for (size_t i = 0; i < scale; i++) {
-        float d = static_cast<float>(i * 2) - world_scale;
+        float d = static_cast<float>(i * 2) - _world_scale;
 
         // line z axis
-        edges[12 + i] = {
+        _edges[12 + i] = {
             { -r, -r, d },
             {  r, -r, d }
         };
 
         // line x axis
-        edges[12 + scale + i] = {
+        _edges[12 + scale + i] = {
             { d, -r, -r },
             { d, -r,  r }
         };
@@ -76,11 +76,11 @@ World::resolvePlayer(const Player& player) const
     PlayerCollision out;
     out.vel = player.vel;
 
-    const double min = -world_scale + player.radius;
-    const double max =  world_scale - player.radius;
+    const double min = -_world_scale + player.radius;
+    const double max =  _world_scale - player.radius;
 
-    const double minY = player.height - world_scale;
-    const double maxY = world_scale;
+    const double minY = player.height - _world_scale;
+    const double maxY = _world_scale;
 
     out.pos.x = sweepAxis(player.pos.x, min,  max,  out.hit_x);
     out.pos.y = sweepAxis(player.pos.y, minY, maxY, out.hit_y);
@@ -102,12 +102,12 @@ World::draw(GlRenderer& gl, const Camera& camera, const Light& light) const
     Mat4 model = Mat4::identity();
     Mat4 mvp   = Mat4::mul(proj, Mat4::mul(view, model));
 
-    // Box edges: dark grey
+    // Box _edges: dark grey
     if (show_wireframe) {
         DynArray<float> pts;
-        pts.reserve(map_box_edges_len * 6);
-        for (size_t k = 0; k < map_box_edges_len; k++) {
-            const Edge& e = edges[k];
+        pts.reserve(_map_box_edges_len * 6);
+        for (size_t k = 0; k < _map_box_edges_len; k++) {
+            const Edge& e = _edges[k];
             pts.push_back((float)e.a.x); pts.push_back((float)e.a.y); pts.push_back((float)e.a.z);
             pts.push_back((float)e.b.x); pts.push_back((float)e.b.y); pts.push_back((float)e.b.z);
         }
@@ -117,7 +117,7 @@ World::draw(GlRenderer& gl, const Camera& camera, const Light& light) const
     }
 
     // Crosshair: 2D overlay in NDC. Uses identity MVP, then restores the
-    // world MVP so subsequent draws (wall_surfaces) aren't affected.
+    // world MVP so subsequent draws (_wall_surfaces) aren't affected.
     {
         Mat4 ident = Mat4::identity();
         gl.set_MVP(model, ident);
@@ -137,7 +137,7 @@ World::draw(GlRenderer& gl, const Camera& camera, const Light& light) const
     }
 
     // ── Solid wall surfaces ─────────────────────────────────────────────
-    wall_surfaces.draw(gl, camera, light);
+    _wall_surfaces.draw(gl, camera, light);
 }
 
 
@@ -148,4 +148,4 @@ World::spawnPoint() const
 
 float
 World::scale() const
-{ return world_scale; }
+{ return _world_scale; }
diff --git a/src/entities/world.hpp b/src/entities/world.hpp
index ca06547..4e21969 100644
--- a/src/entities/world.hpp
+++ b/src/entities/world.hpp
@@ -22,23 +22,25 @@ struct SweepResult {
 class World
 {
     static inline constexpr int
-    map_box_scale{static_cast<int>(WORLD_BOX_SCALE)};
+    _map_box_scale     {static_cast<int>(WORLD_BOX_SCALE)};
 
     static inline constexpr int
-    map_box_edges_len{12 + map_box_scale * 2};
+    _map_box_edges_len {12 + _map_box_scale * 2};
 
     float
-    world_scale{WORLD_BOX_SCALE};
+    _world_scale       {WORLD_BOX_SCALE};
 
-    WallSurfaces wall_surfaces;
+    WallSurfaces
+    _wall_surfaces;
 
-    std::array<Edge, map_box_edges_len>
-    edges{};
+    std::array<Edge, _map_box_edges_len>
+    _edges             {};
 
     void
     initEdges();
 public:
-    bool show_wireframe { false };
+    bool
+    show_wireframe { false };
 
     World();
 
diff --git a/src/rendering/floor_mandelbrot.cpp b/src/rendering/floor_mandelbrot.cpp
index 78ddb99..f5836a9 100644
--- a/src/rendering/floor_mandelbrot.cpp
+++ b/src/rendering/floor_mandelbrot.cpp
@@ -13,8 +13,8 @@ FloorMandelbrot::generate(float  world_scale,
                           double center_y,
                           double width)
 {
-    verts_.clear();
-    indices_.clear();
+    _verts.clear();
+    _indices.clear();
 
     // Single quad covering the floor. We use the existing 9-floats-per-vertex
     // layout: [pos(3) | normal(3) | color(3)], but here we *abuse* the color
@@ -34,33 +34,33 @@ FloorMandelbrot::generate(float  world_scale,
     };
 
     for (const V& v : corners) {
-        verts_.push_back(v.x);
-        verts_.push_back(v.y);
-        verts_.push_back(v.z);
+        _verts.push_back(v.x);
+        _verts.push_back(v.y);
+        _verts.push_back(v.z);
         // Normal — flat up.
-        verts_.push_back(0.0f);
-        verts_.push_back(1.0f);
-        verts_.push_back(0.0f);
+        _verts.push_back(0.0f);
+        _verts.push_back(1.0f);
+        _verts.push_back(0.0f);
         // "Color" slot carries the complex-plane coord.
-        verts_.push_back(static_cast<float>(v.cx));
-        verts_.push_back(static_cast<float>(v.cy));
-        verts_.push_back(0.0f);
+        _verts.push_back(static_cast<float>(v.cx));
+        _verts.push_back(static_cast<float>(v.cy));
+        _verts.push_back(0.0f);
     }
 
     // Two triangles: BL,BR,TR  +  BL,TR,TL
-    indices_.push_back(0);
-    indices_.push_back(1);
-    indices_.push_back(2);
-    indices_.push_back(0);
-    indices_.push_back(2);
-    indices_.push_back(3);
+    _indices.push_back(0);
+    _indices.push_back(1);
+    _indices.push_back(2);
+    _indices.push_back(0);
+    _indices.push_back(2);
+    _indices.push_back(3);
 }
 
 
 void
 FloorMandelbrot::draw(GlRenderer& gl, const Camera& cam, const Light& light) const
 {
-    if (verts_.empty()) return;
+    if (_verts.empty()) return;
 
     Mat4 view  = buildViewMat4(cam);
     Mat4 proj  = gl.projection();
@@ -74,9 +74,9 @@ FloorMandelbrot::draw(GlRenderer& gl, const Camera& cam, const Light& light) con
 
     gl.enable_MandelbrotMode(true);
     gl.set_ColorUseVertex(false);
-    gl.draw_MeshColored(verts_.data(),
-                       static_cast<int>(verts_.size() / 9),
-                       indices_.data(),
-                       static_cast<int>(indices_.size()));
+    gl.draw_MeshColored(_verts.data(),
+                       static_cast<int>(_verts.size() / 9),
+                       _indices.data(),
+                       static_cast<int>(_indices.size()));
     gl.enable_MandelbrotMode(false);
 }
diff --git a/src/rendering/floor_mandelbrot.hpp b/src/rendering/floor_mandelbrot.hpp
index c223ee2..b8424c1 100644
--- a/src/rendering/floor_mandelbrot.hpp
+++ b/src/rendering/floor_mandelbrot.hpp
@@ -16,6 +16,8 @@ class Camera;
 // transitions and clear "ring" regions around the set boundary. Inside
 // the set is a near-black indigo.
 class FloorMandelbrot {
+    DynArray<float> _verts;
+    DynArray<int>   _indices;
 public:
     void generate(float  world_scale,
                   float  floor_y,
@@ -24,8 +26,4 @@ public:
                   double width    =  3.0);
 
     void draw(GlRenderer& gl, const Camera& cam, const Light& light) const;
-
-private:
-    DynArray<float> verts_;
-    DynArray<int>   indices_;
 };
diff --git a/src/rendering/gl_renderer.cpp b/src/rendering/gl_renderer.cpp
index 50290df..4d5c6ce 100644
--- a/src/rendering/gl_renderer.cpp
+++ b/src/rendering/gl_renderer.cpp
@@ -408,9 +408,9 @@ Mat4 buildModelMat4(const Vec3& pos, float yaw, float pitch)
 // ─────────────────────────────────────────────────────────────────────────────
 bool GlRenderer::init(SDL_Window* window)
 {
-    m_ctx = SDL_GL_CreateContext(window);
-    if (!m_ctx) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "GL context creation failed: %s", SDL_GetError()); return false; }
-    SDL_GL_MakeCurrent(window, m_ctx);
+    _ctx = SDL_GL_CreateContext(window);
+    if (!_ctx) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "GL context creation failed: %s", SDL_GetError()); return false; }
+    SDL_GL_MakeCurrent(window, _ctx);
     SDL_GL_SetSwapInterval(0);
 
     int samples = 0;
@@ -429,55 +429,55 @@ bool GlRenderer::init(SDL_Window* window)
     glDeleteShader(vert);
     glDeleteShader(frag);
 
-    m_uMVP       = glGetUniformLocation(m_prog, "u_MVP");
-    m_uNormalMat = glGetUniformLocation(m_prog, "u_NormalMat");
-    m_uColor     = glGetUniformLocation(m_prog, "u_Color");
-    m_uBodyColor = glGetUniformLocation(m_prog, "u_BodyColor");
-    m_uBackDim   = glGetUniformLocation(m_prog, "u_BackDim");
-    m_uUseFront  = glGetUniformLocation(m_prog, "u_UseFront");
-    m_uLightDir  = glGetUniformLocation(m_prog, "u_LightDir");
-    m_uAmbient   = glGetUniformLocation(m_prog, "u_Ambient");
-    m_uIntensity = glGetUniformLocation(m_prog, "u_Intensity");
-    m_uWireframe = glGetUniformLocation(m_prog, "u_Wireframe");
-    m_uUseVertexColor = glGetUniformLocation(m_prog, "u_UseVertexColor");
-    m_uMandelbrot     = glGetUniformLocation(m_prog, "u_Mandelbrot");
-    m_uWallPaper      = glGetUniformLocation(m_prog, "u_WallPaper");
-    m_uTextLineSpacing= glGetUniformLocation(m_prog, "u_TextLineSpacing");
-
-
-
-    m_uModel           = glGetUniformLocation(m_prog, "u_Model");
-    m_uSunColor        = glGetUniformLocation(m_prog, "u_SunColor");
-    m_uFlashPos        = glGetUniformLocation(m_prog, "u_FlashPos");
-    m_uFlashDir        = glGetUniformLocation(m_prog, "u_FlashDir");
-    m_uFlashColor      = glGetUniformLocation(m_prog, "u_FlashColor");
-    m_uFlashRange      = glGetUniformLocation(m_prog, "u_FlashRange");
-    m_uFlashInnerCos   = glGetUniformLocation(m_prog, "u_FlashInnerCos");
-    m_uFlashOuterCos   = glGetUniformLocation(m_prog, "u_FlashOuterCos");
-    m_uFlashIntensity  = glGetUniformLocation(m_prog, "u_FlashIntensity");
-
-    glGenVertexArrays(1, &m_vao); glGenBuffers(1, &m_vbo); glGenBuffers(1, &m_ebo);
-    glBindVertexArray(m_vao);
-    glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
+    _uMVP       = glGetUniformLocation(_prog, "u_MVP");
+    _uNormalMat = glGetUniformLocation(_prog, "u_NormalMat");
+    _uColor     = glGetUniformLocation(_prog, "u_Color");
+    _uBodyColor = glGetUniformLocation(_prog, "u_BodyColor");
+    _uBackDim   = glGetUniformLocation(_prog, "u_BackDim");
+    _uUseFront  = glGetUniformLocation(_prog, "u_UseFront");
+    _uLightDir  = glGetUniformLocation(_prog, "u_LightDir");
+    _uAmbient   = glGetUniformLocation(_prog, "u_Ambient");
+    _uIntensity = glGetUniformLocation(_prog, "u_Intensity");
+    _uWireframe = glGetUniformLocation(_prog, "u_Wireframe");
+    _uUseVertexColor = glGetUniformLocation(_prog, "u_UseVertexColor");
+    _uMandelbrot     = glGetUniformLocation(_prog, "u_Mandelbrot");
+    _uWallPaper      = glGetUniformLocation(_prog, "u_WallPaper");
+    _uTextLineSpacing= glGetUniformLocation(_prog, "u_TextLineSpacing");
+
+
+
+    _uModel           = glGetUniformLocation(_prog, "u_Model");
+    _uSunColor        = glGetUniformLocation(_prog, "u_SunColor");
+    _uFlashPos        = glGetUniformLocation(_prog, "u_FlashPos");
+    _uFlashDir        = glGetUniformLocation(_prog, "u_FlashDir");
+    _uFlashColor      = glGetUniformLocation(_prog, "u_FlashColor");
+    _uFlashRange      = glGetUniformLocation(_prog, "u_FlashRange");
+    _uFlashInnerCos   = glGetUniformLocation(_prog, "u_FlashInnerCos");
+    _uFlashOuterCos   = glGetUniformLocation(_prog, "u_FlashOuterCos");
+    _uFlashIntensity  = glGetUniformLocation(_prog, "u_FlashIntensity");
+
+    glGenVertexArrays(1, &_vao); glGenBuffers(1, &_vbo); glGenBuffers(1, &_ebo);
+    glBindVertexArray(_vao);
+    glBindBuffer(GL_ARRAY_BUFFER, _vbo);
     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)0);
     glEnableVertexAttribArray(0);
     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)(3*sizeof(float)));
     glEnableVertexAttribArray(1);
     glBindVertexArray(0);
 
-    glGenVertexArrays(1, &m_line_vao); glGenBuffers(1, &m_line_vbo);
-    glBindVertexArray(m_line_vao);
-    glBindBuffer(GL_ARRAY_BUFFER, m_line_vbo);
+    glGenVertexArrays(1, &_line_vao); glGenBuffers(1, &_line_vbo);
+    glBindVertexArray(_line_vao);
+    glBindBuffer(GL_ARRAY_BUFFER, _line_vbo);
     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0);
     glEnableVertexAttribArray(0);
     glVertexAttrib3f(1, 0.0f, 0.0f, 1.0f);
     glBindVertexArray(0);
 
-    glGenVertexArrays(1, &m_vao_c);
-    glGenBuffers     (1, &m_vbo_c);
-    glGenBuffers     (1, &m_ebo_c);
-    glBindVertexArray(m_vao_c);
-    glBindBuffer(GL_ARRAY_BUFFER, m_vbo_c);
+    glGenVertexArrays(1, &_vao_c);
+    glGenBuffers     (1, &_vbo_c);
+    glGenBuffers     (1, &_ebo_c);
+    glBindVertexArray(_vao_c);
+    glBindBuffer(GL_ARRAY_BUFFER, _vbo_c);
     // 9 floats per vertex: pos(3) + normal(3) + color(3)
     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float),
             (void*)0);
@@ -502,8 +502,8 @@ bool GlRenderer::init(SDL_Window* window)
     create_CompositeProgram_();
  
     // Lookup the new emissive uniforms on the main program.
-    m_uEmissiveColor    = glGetUniformLocation(m_prog, "u_EmissiveColor");
-    m_uEmissiveStrength = glGetUniformLocation(m_prog, "u_EmissiveStrength");
+    _uEmissiveColor    = glGetUniformLocation(_prog, "u_EmissiveColor");
+    _uEmissiveStrength = glGetUniformLocation(_prog, "u_EmissiveStrength");
 
 
     { // thats for the menu
@@ -512,10 +512,10 @@ bool GlRenderer::init(SDL_Window* window)
             -0.5f,-0.5f, 0.0f,
             0.5f,-0.5f, 0.0f
         };
-        glGenVertexArrays(1, &m_menu_vao);
-        glGenBuffers(1, &m_menu_vbo);
-        glBindVertexArray(m_menu_vao);
-        glBindBuffer(GL_ARRAY_BUFFER, m_menu_vbo);
+        glGenVertexArrays(1, &_menu_vao);
+        glGenBuffers(1, &_menu_vbo);
+        glBindVertexArray(_menu_vao);
+        glBindBuffer(GL_ARRAY_BUFFER, _menu_vbo);
         glBufferData(GL_ARRAY_BUFFER, sizeof(tri), tri, GL_STATIC_DRAW);
         glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0);
         glEnableVertexAttribArray(0);
@@ -531,29 +531,29 @@ bool GlRenderer::init(SDL_Window* window)
 void
 GlRenderer::begin_Frame(int w, int h)
 {
-    m_view_w = w; m_view_h = h;
-    m_aspect = (h != 0) ? (float)w/(float)h : 1.0f;
+    _view_w = w; _view_h = h;
+    _aspect = (h != 0) ? (float)w/(float)h : 1.0f;
 
     // Lazy-create or resize the FBO to match the window.
-    if (m_scene_fbo == 0) create_FBO_(w, h);
+    if (_scene_fbo == 0) create_FBO_(w, h);
     else                  resize_FBO_(w, h);
 
     // Bind the FBO as the render target.
-    glBindFramebuffer(GL_FRAMEBUFFER, m_scene_fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, _scene_fbo);
     glViewport(0, 0, w, h);
     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
-    glUseProgram(m_prog);
-    glUniform1i(m_uMandelbrot, 0);
-    glUniform1i(m_uWallPaper,  0);
-    glUniform1i(m_uUseFront,   0);
+    glUseProgram(_prog);
+    glUniform1i(_uMandelbrot, 0);
+    glUniform1i(_uWallPaper,  0);
+    glUniform1i(_uUseFront,   0);
     Mat4 ident = Mat4::identity();
-    glUniformMatrix4fv(m_uModel, 1, GL_FALSE, ident.m);
-    glUniform1i(m_uUseVertexColor, 0);
-    glUniform3f(m_uEmissiveColor, 0.0f, 0.0f, 0.0f);
-    glUniform1f(m_uEmissiveStrength, 0.0f);
-    m_proj = Mat4::perspective(SDL_PI_F/3.0f, m_aspect, 0.1f, 200.0f);
+    glUniformMatrix4fv(_uModel, 1, GL_FALSE, ident.m);
+    glUniform1i(_uUseVertexColor, 0);
+    glUniform3f(_uEmissiveColor, 0.0f, 0.0f, 0.0f);
+    glUniform1f(_uEmissiveStrength, 0.0f);
+    _proj = Mat4::perspective(SDL_PI_F/3.0f, _aspect, 0.1f, 200.0f);
 }
 
 void
@@ -567,26 +567,26 @@ GlRenderer::end_Frame(SDL_Window* window)
 
     // Composite scene + bloom to screen.
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
-    glViewport(0, 0, m_view_w, m_view_h);
+    glViewport(0, 0, _view_w, _view_h);
     glDisable(GL_DEPTH_TEST);
 
-    glUseProgram(m_composite_prog);
+    glUseProgram(_composite_prog);
     glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_2D, m_scene_color_tex);
-    glUniform1i(m_uComposeScene, 0);
+    glBindTexture(GL_TEXTURE_2D, _scene_color_tex);
+    glUniform1i(_uComposeScene, 0);
     glActiveTexture(GL_TEXTURE1);
-    glBindTexture(GL_TEXTURE_2D, m_blur_tex[1]);   // final bloom
-    glUniform1i(m_uComposeBloom, 1);
-    glUniform1f(m_uComposeStrength, m_bloom_strength);
+    glBindTexture(GL_TEXTURE_2D, _blur_tex[1]);   // final bloom
+    glUniform1i(_uComposeBloom, 1);
+    glUniform1f(_uComposeStrength, _bloom_strength);
 
     { FP_ZONE("end_Frame.composite");
-        glBindVertexArray(m_fullscreen_vao);
+        glBindVertexArray(_fullscreen_vao);
         glDrawArrays(GL_TRIANGLES, 0, 6);
         glBindVertexArray(0);
 
         glActiveTexture(GL_TEXTURE0);                  // reset to default unit
         glEnable(GL_DEPTH_TEST);
-        glUseProgram(m_prog);
+        glUseProgram(_prog);
     }
 
     { FP_ZONE("end_Frame.swap");
@@ -599,8 +599,8 @@ GlRenderer::end_Frame(SDL_Window* window)
 // ─────────────────────────────────────────────────────────────────────────────
 void GlRenderer::set_MVP(const Mat4& model, const Mat4& mvp)
 {
-    glUniformMatrix4fv(m_uMVP,   1, GL_FALSE, mvp.m);
-    glUniformMatrix4fv(m_uModel, 1, GL_FALSE, model.m);
+    glUniformMatrix4fv(_uMVP,   1, GL_FALSE, mvp.m);
+    glUniformMatrix4fv(_uModel, 1, GL_FALSE, model.m);
 }
 
 void GlRenderer::set_NormalMat(const Mat4& model)
@@ -614,73 +614,73 @@ void GlRenderer::set_NormalMat(const Mat4& model)
         model.m[4], model.m[5], model.m[6],
         model.m[8], model.m[9], model.m[10]
     };
-    glUniformMatrix3fv(m_uNormalMat, 1, GL_FALSE, n);
+    glUniformMatrix3fv(_uNormalMat, 1, GL_FALSE, n);
 }
 
 
 void GlRenderer::set_Color(float r, float g, float b)
-{ glUniform3f(m_uColor, r, g, b); }
+{ glUniform3f(_uColor, r, g, b); }
 
 void GlRenderer::set_Light(const Light& light)
 {
     // Sun
-    glUniform3f(m_uLightDir,
+    glUniform3f(_uLightDir,
         (float)light.direction.x,
         (float)light.direction.y,
         (float)light.direction.z);
-    glUniform3f(m_uSunColor,
+    glUniform3f(_uSunColor,
         (float)light.sun_color.x,
         (float)light.sun_color.y,
         (float)light.sun_color.z);
-    glUniform1f(m_uIntensity, light.sun_intensity);
+    glUniform1f(_uIntensity, light.sun_intensity);
 
     // Ambient
-    glUniform3f(m_uAmbient,
+    glUniform3f(_uAmbient,
         (float)light.ambient.x,
         (float)light.ambient.y,
         (float)light.ambient.z);
 
     // Flashlight
-    glUniform3f(m_uFlashPos,
+    glUniform3f(_uFlashPos,
         (float)light.flashlight_pos.x,
         (float)light.flashlight_pos.y,
         (float)light.flashlight_pos.z);
-    glUniform3f(m_uFlashDir,
+    glUniform3f(_uFlashDir,
         (float)light.flashlight_dir.x,
         (float)light.flashlight_dir.y,
         (float)light.flashlight_dir.z);
-    glUniform3f(m_uFlashColor,
+    glUniform3f(_uFlashColor,
         (float)light.flashlight_color.x,
         (float)light.flashlight_color.y,
         (float)light.flashlight_color.z);
-    glUniform1f(m_uFlashRange,     light.flashlight_range);
-    glUniform1f(m_uFlashInnerCos,  light.flashlight_inner_cos);
-    glUniform1f(m_uFlashOuterCos,  light.flashlight_outer_cos);
-    glUniform1f(m_uFlashIntensity, light.flashlight_intensity);
+    glUniform1f(_uFlashRange,     light.flashlight_range);
+    glUniform1f(_uFlashInnerCos,  light.flashlight_inner_cos);
+    glUniform1f(_uFlashOuterCos,  light.flashlight_outer_cos);
+    glUniform1f(_uFlashIntensity, light.flashlight_intensity);
 }
 
 void GlRenderer::set_Wireframe(bool on)
-{ glUniform1i(m_uWireframe, on ? 1 : 0); }
+{ glUniform1i(_uWireframe, on ? 1 : 0); }
 
 void GlRenderer::set_ColorUseVertex(bool on)
-{ glUniform1i(m_uUseVertexColor, on ? 1 : 0); }
+{ glUniform1i(_uUseVertexColor, on ? 1 : 0); }
 
 void GlRenderer::set_TextLineSpacing(float m)
-{ glUniform1f(m_uTextLineSpacing, m); }
+{ glUniform1f(_uTextLineSpacing, m); }
 
 void
 GlRenderer::set_Emissive(float r, float g, float b, float strength)
 {
-    glUniform3f(m_uEmissiveColor, r, g, b);
-    glUniform1f(m_uEmissiveStrength, strength);
+    glUniform3f(_uEmissiveColor, r, g, b);
+    glUniform1f(_uEmissiveStrength, strength);
 }
 
 void
 GlRenderer::set_FaceShade(float r, float g, float b, float back_dim, bool use_front)
 {
-    glUniform3f(m_uBodyColor, r, g, b);
-    glUniform1f(m_uBackDim,   back_dim);
-    glUniform1i(m_uUseFront,  use_front ? 1 : 0);
+    glUniform3f(_uBodyColor, r, g, b);
+    glUniform1f(_uBackDim,   back_dim);
+    glUniform1i(_uUseFront,  use_front ? 1 : 0);
 }
 
 // ─────────────────────────────────────────────────────────────────────────────
@@ -689,10 +689,10 @@ GlRenderer::set_FaceShade(float r, float g, float b, float back_dim, bool use_fr
 void GlRenderer::draw_Mesh(const float* verts,   int vert_count,
                           const int*   indices, int index_count)
 {
-    glBindVertexArray(m_vao);
-    glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
+    glBindVertexArray(_vao);
+    glBindBuffer(GL_ARRAY_BUFFER, _vbo);
     glBufferData(GL_ARRAY_BUFFER, static_cast<size_t>(vert_count)*6*sizeof(float), verts, GL_STREAM_DRAW);
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
     glBufferData(GL_ELEMENT_ARRAY_BUFFER, static_cast<size_t>(index_count)*sizeof(int), indices, GL_STREAM_DRAW);
     glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_INT, nullptr);
     glBindVertexArray(0);
@@ -703,19 +703,19 @@ void
 GlRenderer::draw_MeshColored(const float* verts,   int vert_count,
                             const int*   indices, int index_count)
 {
-    glBindVertexArray(m_vao_c);
-    glBindBuffer(GL_ARRAY_BUFFER, m_vbo_c);
+    glBindVertexArray(_vao_c);
+    glBindBuffer(GL_ARRAY_BUFFER, _vbo_c);
     glBufferData(GL_ARRAY_BUFFER,
                  static_cast<size_t>(vert_count) * 9 * sizeof(float),
                  verts, GL_STATIC_DRAW);
 
-    // Re-point attributes at m_vbo_c — draw_MeshCached may have repointed
+    // Re-point attributes at _vbo_c — draw_MeshCached may have repointed
     // this shared VAO at a per-Text3d VBO.
     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 9*sizeof(float), (void*)0);
     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 9*sizeof(float), (void*)(3*sizeof(float)));
     glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 9*sizeof(float), (void*)(6*sizeof(float)));
 
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo_c);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo_c);
     glBufferData(GL_ELEMENT_ARRAY_BUFFER,
                  static_cast<size_t>(index_count) * sizeof(int),
                  indices, GL_STATIC_DRAW);
@@ -730,7 +730,7 @@ GlRenderer::draw_MeshCached(unsigned int& vbo, unsigned int& ebo, int& index_cou
                             const float* verts, int vert_count,
                             const int* indices, int idx_count)
 {
-    glBindVertexArray(m_vao_c);   // reuse the 9-float pos|normal|color layout
+    glBindVertexArray(_vao_c);   // reuse the 9-float pos|normal|color layout
 
     if (vbo == 0) glGenBuffers(1, &vbo);
     if (ebo == 0) glGenBuffers(1, &ebo);
@@ -748,7 +748,7 @@ GlRenderer::draw_MeshCached(unsigned int& vbo, unsigned int& ebo, int& index_cou
         index_count = idx_count;
     }
 
-    // m_vao_c's attribute pointers were bound to m_vbo_c at init; rebind to ours.
+    // _vao_c's attribute pointers were bound to _vbo_c at init; rebind to ours.
     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 9*sizeof(float), (void*)0);
     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 9*sizeof(float), (void*)(3*sizeof(float)));
     glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 9*sizeof(float), (void*)(6*sizeof(float)));
@@ -769,8 +769,8 @@ GlRenderer::free_MeshBuffers(unsigned int vbo, unsigned int ebo)
 
 void GlRenderer::draw_Lines(const float* points, int point_count)
 {
-    glBindVertexArray(m_line_vao);
-    glBindBuffer(GL_ARRAY_BUFFER, m_line_vbo);
+    glBindVertexArray(_line_vao);
+    glBindBuffer(GL_ARRAY_BUFFER, _line_vbo);
     glBufferData(GL_ARRAY_BUFFER, static_cast<size_t>(point_count)*3*sizeof(float), points, GL_STREAM_DRAW);
     glDrawArrays(GL_LINES, 0, point_count);
     glBindVertexArray(0);
@@ -778,30 +778,30 @@ void GlRenderer::draw_Lines(const float* points, int point_count)
 
 void GlRenderer::draw_Menu()
 {
-    glUseProgram(m_prog);
+    glUseProgram(_prog);
     Mat4 identity = Mat4::identity();
-    glUniformMatrix4fv(m_uMVP, 1, GL_FALSE, identity.m);
+    glUniformMatrix4fv(_uMVP, 1, GL_FALSE, identity.m);
     float n[9] = {1,0,0, 0,1,0, 0,0,1};
-    glUniformMatrix3fv(m_uNormalMat, 1, GL_FALSE, n);
-    glUniform3f(m_uColor,    0.2f, 0.4f, 0.8f);
-    glUniform3f(m_uLightDir, 0.0f, 0.0f, 1.0f);
-    glUniform3f(m_uAmbient,  1.0f, 1.0f, 1.0f);  // full ambient = flat colour
-    glUniform1f(m_uIntensity, 1.0f);
-    glUniform1i(m_uWireframe, 1);
-    glUniform3f(m_uSunColor, 1.0f, 1.0f, 1.0f);
-    glUniform1i(m_uMandelbrot, 0);
-    glUniform1i(m_uWallPaper,  0);
-    glUniform3f(m_uFlashPos,   0.0f, 0.0f, 0.0f);
-    glUniform3f(m_uFlashDir,   0.0f, 0.0f, -1.0f);
-    glUniform3f(m_uFlashColor, 0.0f, 0.0f,  0.0f);
-    glUniform1f(m_uFlashRange,     1.0f);
-    glUniform1f(m_uFlashInnerCos,  0.97f);
-    glUniform1f(m_uFlashOuterCos,  0.90f);
-    glUniform1f(m_uFlashIntensity, 0.0f);
+    glUniformMatrix3fv(_uNormalMat, 1, GL_FALSE, n);
+    glUniform3f(_uColor,    0.2f, 0.4f, 0.8f);
+    glUniform3f(_uLightDir, 0.0f, 0.0f, 1.0f);
+    glUniform3f(_uAmbient,  1.0f, 1.0f, 1.0f);  // full ambient = flat colour
+    glUniform1f(_uIntensity, 1.0f);
+    glUniform1i(_uWireframe, 1);
+    glUniform3f(_uSunColor, 1.0f, 1.0f, 1.0f);
+    glUniform1i(_uMandelbrot, 0);
+    glUniform1i(_uWallPaper,  0);
+    glUniform3f(_uFlashPos,   0.0f, 0.0f, 0.0f);
+    glUniform3f(_uFlashDir,   0.0f, 0.0f, -1.0f);
+    glUniform3f(_uFlashColor, 0.0f, 0.0f,  0.0f);
+    glUniform1f(_uFlashRange,     1.0f);
+    glUniform1f(_uFlashInnerCos,  0.97f);
+    glUniform1f(_uFlashOuterCos,  0.90f);
+    glUniform1f(_uFlashIntensity, 0.0f);
     Mat4 ident_m = Mat4::identity();
-    glUniformMatrix4fv(m_uModel, 1, GL_FALSE, ident_m.m);
+    glUniformMatrix4fv(_uModel, 1, GL_FALSE, ident_m.m);
 
-    glBindVertexArray(m_menu_vao);
+    glBindVertexArray(_menu_vao);
     glDrawArrays(GL_TRIANGLES, 0, 3);
     glBindVertexArray(0);
 }
@@ -812,32 +812,32 @@ GlRenderer::draw_Emissive(const Camera& cam,
                          const float* verts, int vert_count,
                          const int*   indices, int index_count,
                          float r, float g, float b, float strength) {
-    if (!m_emissive_prog || vert_count == 0 || index_count == 0) return;
+    if (!_emissive_prog || vert_count == 0 || index_count == 0) return;
 
     // Push state: switch program, enable additive blending, disable depth
     // write (we still depth-TEST so halos respect occluders, but we don't
     // want halos blocking each other in the depth buffer).
-    glUseProgram(m_emissive_prog);
+    glUseProgram(_emissive_prog);
 
     // The vertex shader needs an MVP. We use the current proj * view * I —
     // verts are already in world space.
     Mat4 view = buildViewMat4(cam);
-    Mat4 mvp  = Mat4::mul(m_proj, view);
-    glUniformMatrix4fv(m_uEmissiveMVP, 1, GL_FALSE, mvp.m);
+    Mat4 mvp  = Mat4::mul(_proj, view);
+    glUniformMatrix4fv(_uEmissiveMVP, 1, GL_FALSE, mvp.m);
 
-    glUniform3f(m_uEmissiveColor, r, g, b);
-    glUniform1f(m_uEmissiveStrength, strength);
+    glUniform3f(_uEmissiveColor, r, g, b);
+    glUniform1f(_uEmissiveStrength, strength);
 
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE);   // additive
     glDepthMask(GL_FALSE);                // no depth write
 
-    glBindVertexArray(m_emissive_vao);
-    glBindBuffer(GL_ARRAY_BUFFER, m_emissive_vbo);
+    glBindVertexArray(_emissive_vao);
+    glBindBuffer(GL_ARRAY_BUFFER, _emissive_vbo);
     glBufferData(GL_ARRAY_BUFFER,
                  static_cast<size_t>(vert_count) * 5 * sizeof(float),
                  verts, GL_STREAM_DRAW);
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_emissive_ebo);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _emissive_ebo);
     glBufferData(GL_ELEMENT_ARRAY_BUFFER,
                  static_cast<size_t>(index_count) * sizeof(int),
                  indices, GL_STREAM_DRAW);
@@ -847,7 +847,7 @@ GlRenderer::draw_Emissive(const Camera& cam,
     // Pop state.
     glDisable(GL_BLEND);
     glDepthMask(GL_TRUE);
-    glUseProgram(m_prog);    // restore main program
+    glUseProgram(_prog);    // restore main program
 }
 
 // ─────────────────────────────────────────────────────────────────────────────
@@ -869,26 +869,26 @@ GLuint GlRenderer::compileShader(GLenum type, const char* src)
 
 bool GlRenderer::linkProgram(GLuint vert, GLuint frag)
 {
-    m_prog = glCreateProgram();
-    glAttachShader(m_prog, vert); glAttachShader(m_prog, frag);
-    glLinkProgram(m_prog);
-    GLint ok; glGetProgramiv(m_prog, GL_LINK_STATUS, &ok);
+    _prog = glCreateProgram();
+    glAttachShader(_prog, vert); glAttachShader(_prog, frag);
+    glLinkProgram(_prog);
+    GLint ok; glGetProgramiv(_prog, GL_LINK_STATUS, &ok);
     if (!ok) {
-        char log[512]; glGetProgramInfoLog(m_prog,512,nullptr,log);
+        char log[512]; glGetProgramInfoLog(_prog,512,nullptr,log);
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Program link error: %s", log);
-        glDeleteProgram(m_prog); m_prog=0; return false;
+        glDeleteProgram(_prog); _prog=0; return false;
     }
     return true;
 }
 
-float GlRenderer::aspect() const { return m_aspect; }
-const Mat4& GlRenderer::projection() const { return m_proj; }
+float GlRenderer::aspect() const { return _aspect; }
+const Mat4& GlRenderer::projection() const { return _proj; }
 
 void GlRenderer::enable_MandelbrotMode(bool on)
-{ glUniform1i(m_uMandelbrot, on ? 1 : 0); }
+{ glUniform1i(_uMandelbrot, on ? 1 : 0); }
 
 void GlRenderer::enable_WallPaperMode(bool on)
-{ glUniform1i(m_uWallPaper, on ? 1 : 0); }
+{ glUniform1i(_uWallPaper, on ? 1 : 0); }
 
 
 
@@ -903,10 +903,10 @@ GlRenderer::create_FullscreenQuad_()
          1.0f,  1.0f,
         -1.0f,  1.0f,
     };
-    glGenVertexArrays(1, &m_fullscreen_vao);
-    glGenBuffers(1, &m_fullscreen_vbo);
-    glBindVertexArray(m_fullscreen_vao);
-    glBindBuffer(GL_ARRAY_BUFFER, m_fullscreen_vbo);
+    glGenVertexArrays(1, &_fullscreen_vao);
+    glGenBuffers(1, &_fullscreen_vbo);
+    glBindVertexArray(_fullscreen_vao);
+    glBindBuffer(GL_ARRAY_BUFFER, _fullscreen_vbo);
     glBufferData(GL_ARRAY_BUFFER, sizeof(quad_verts), quad_verts, GL_STATIC_DRAW);
     glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), (void*)0);
     glEnableVertexAttribArray(0);
@@ -918,53 +918,53 @@ GlRenderer::create_BlitProgram_()
 {
     GLuint v = compileShader(GL_VERTEX_SHADER,   kBlitVertSrc);
     GLuint f = compileShader(GL_FRAGMENT_SHADER, kBlitFragSrc);
-    m_blit_prog = glCreateProgram();
-    glAttachShader(m_blit_prog, v);
-    glAttachShader(m_blit_prog, f);
-    glLinkProgram(m_blit_prog);
-    GLint ok=0; glGetProgramiv(m_blit_prog, GL_LINK_STATUS, &ok);
+    _blit_prog = glCreateProgram();
+    glAttachShader(_blit_prog, v);
+    glAttachShader(_blit_prog, f);
+    glLinkProgram(_blit_prog);
+    GLint ok=0; glGetProgramiv(_blit_prog, GL_LINK_STATUS, &ok);
     if (!ok) {
-        char log[512]; glGetProgramInfoLog(m_blit_prog, 512, nullptr, log);
+        char log[512]; glGetProgramInfoLog(_blit_prog, 512, nullptr, log);
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Blit program link error: %s", log);
     }
     glDeleteShader(v);
     glDeleteShader(f);
-    m_uBlitTex = glGetUniformLocation(m_blit_prog, "u_SceneTex");
+    _uBlitTex = glGetUniformLocation(_blit_prog, "u_SceneTex");
 }
 
 void
 GlRenderer::create_FBO_(int w, int h)
 {
-    glGenFramebuffers(1, &m_scene_fbo);
-    glBindFramebuffer(GL_FRAMEBUFFER, m_scene_fbo);
+    glGenFramebuffers(1, &_scene_fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, _scene_fbo);
 
     // Color attachment: RGBA8 texture (LDR for now; future HDR upgrade
     // would use GL_RGBA16F here).
-    glGenTextures(1, &m_scene_color_tex);
-    glBindTexture(GL_TEXTURE_2D, m_scene_color_tex);
+    glGenTextures(1, &_scene_color_tex);
+    glBindTexture(GL_TEXTURE_2D, _scene_color_tex);
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
-                           GL_TEXTURE_2D, m_scene_color_tex, 0);
+                           GL_TEXTURE_2D, _scene_color_tex, 0);
 
     // Depth attachment: renderbuffer (we don't sample depth in postprocess,
     // so renderbuffer is fine and slightly cheaper than a depth texture).
-    glGenRenderbuffers(1, &m_scene_depth_rbo);
-    glBindRenderbuffer(GL_RENDERBUFFER, m_scene_depth_rbo);
+    glGenRenderbuffers(1, &_scene_depth_rbo);
+    glBindRenderbuffer(GL_RENDERBUFFER, _scene_depth_rbo);
     glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, w, h);
     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
-                              GL_RENDERBUFFER, m_scene_depth_rbo);
+                              GL_RENDERBUFFER, _scene_depth_rbo);
 
     GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
     if (status != GL_FRAMEBUFFER_COMPLETE)
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Scene FBO incomplete: 0x%x", status);
 
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
-    m_fbo_w = w;
-    m_fbo_h = h;
+    _fbo_w = w;
+    _fbo_h = h;
 }
 
 void
@@ -972,12 +972,12 @@ GlRenderer::create_ExtractProgram_()
 {
     GLuint v = compileShader(GL_VERTEX_SHADER,   kBlitVertSrc);
     GLuint f = compileShader(GL_FRAGMENT_SHADER, kExtractFragSrc);
-    m_extract_prog = glCreateProgram();
-    glAttachShader(m_extract_prog, v);
-    glAttachShader(m_extract_prog, f);
-    glLinkProgram(m_extract_prog);
+    _extract_prog = glCreateProgram();
+    glAttachShader(_extract_prog, v);
+    glAttachShader(_extract_prog, f);
+    glLinkProgram(_extract_prog);
     glDeleteShader(v); glDeleteShader(f);
-    m_uExtractTex = glGetUniformLocation(m_extract_prog, "u_SceneTex");
+    _uExtractTex = glGetUniformLocation(_extract_prog, "u_SceneTex");
 }
 
 void
@@ -985,14 +985,14 @@ GlRenderer::create_BlurProgram_()
 {
     GLuint v = compileShader(GL_VERTEX_SHADER,   kBlitVertSrc);
     GLuint f = compileShader(GL_FRAGMENT_SHADER, kBlurFragSrc);
-    m_blur_prog = glCreateProgram();
-    glAttachShader(m_blur_prog, v);
-    glAttachShader(m_blur_prog, f);
-    glLinkProgram(m_blur_prog);
+    _blur_prog = glCreateProgram();
+    glAttachShader(_blur_prog, v);
+    glAttachShader(_blur_prog, f);
+    glLinkProgram(_blur_prog);
     glDeleteShader(v); glDeleteShader(f);
-    m_uBlurTex       = glGetUniformLocation(m_blur_prog, "u_BlurTex");
-    m_uBlurDir       = glGetUniformLocation(m_blur_prog, "u_BlurDir");
-    m_uBlurTexelSize = glGetUniformLocation(m_blur_prog, "u_BlurTexelSize");
+    _uBlurTex       = glGetUniformLocation(_blur_prog, "u_BlurTex");
+    _uBlurDir       = glGetUniformLocation(_blur_prog, "u_BlurDir");
+    _uBlurTexelSize = glGetUniformLocation(_blur_prog, "u_BlurTexelSize");
 }
 
 void
@@ -1002,7 +1002,7 @@ GlRenderer::create_BloomFBOs_(int scene_w, int scene_h)
     int h = scene_h / 4;
     if (w < 1) w = 1;
     if (h < 1) h = 1;
-    m_blur_w = w; m_blur_h = h;
+    _blur_w = w; _blur_h = h;
 
     auto make_fbo = [&](GLuint& fbo, GLuint& tex) {
         glGenFramebuffers(1, &fbo);
@@ -1022,9 +1022,9 @@ GlRenderer::create_BloomFBOs_(int scene_w, int scene_h)
             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Bloom FBO incomplete: 0x%x", s);
     };
 
-    make_fbo(m_bright_fbo, m_bright_tex);
-    make_fbo(m_blur_fbo[0], m_blur_tex[0]);
-    make_fbo(m_blur_fbo[1], m_blur_tex[1]);
+    make_fbo(_bright_fbo, _bright_tex);
+    make_fbo(_blur_fbo[0], _blur_tex[0]);
+    make_fbo(_blur_fbo[1], _blur_tex[1]);
 
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
 }
@@ -1032,13 +1032,13 @@ GlRenderer::create_BloomFBOs_(int scene_w, int scene_h)
 void
 GlRenderer::destroy_BloomFBOs_()
 {
-    if (m_bright_tex)   { glDeleteTextures(1, &m_bright_tex);    m_bright_tex = 0; }
-    if (m_bright_fbo)   { glDeleteFramebuffers(1, &m_bright_fbo); m_bright_fbo = 0; }
+    if (_bright_tex)   { glDeleteTextures(1, &_bright_tex);    _bright_tex = 0; }
+    if (_bright_fbo)   { glDeleteFramebuffers(1, &_bright_fbo); _bright_fbo = 0; }
     for (int i = 0; i < 2; ++i) {
-        if (m_blur_tex[i]) { glDeleteTextures(1, &m_blur_tex[i]); m_blur_tex[i] = 0; }
-        if (m_blur_fbo[i]) { glDeleteFramebuffers(1, &m_blur_fbo[i]); m_blur_fbo[i] = 0; }
+        if (_blur_tex[i]) { glDeleteTextures(1, &_blur_tex[i]); _blur_tex[i] = 0; }
+        if (_blur_fbo[i]) { glDeleteFramebuffers(1, &_blur_fbo[i]); _blur_fbo[i] = 0; }
     }
-    m_blur_w = 0; m_blur_h = 0;
+    _blur_w = 0; _blur_h = 0;
 }
 
 void
@@ -1046,7 +1046,7 @@ GlRenderer::resize_BloomFBOs_(int scene_w, int scene_h)
 {
     int want_w = scene_w / 4; if (want_w < 1) want_w = 1;
     int want_h = scene_h / 4; if (want_h < 1) want_h = 1;
-    if (want_w == m_blur_w && want_h == m_blur_h) return;
+    if (want_w == _blur_w && want_h == _blur_h) return;
     destroy_BloomFBOs_();
     create_BloomFBOs_(scene_w, scene_h);
 }
@@ -1054,55 +1054,55 @@ GlRenderer::resize_BloomFBOs_(int scene_w, int scene_h)
 void
 GlRenderer::run_BloomPipeline_()
 {
-    { FP_ZONE("bloom.resize"); resize_BloomFBOs_(m_fbo_w, m_fbo_h); }
+    { FP_ZONE("bloom.resize"); resize_BloomFBOs_(_fbo_w, _fbo_h); }
 
     glDisable(GL_DEPTH_TEST);
-    glBindVertexArray(m_fullscreen_vao);
+    glBindVertexArray(_fullscreen_vao);
 
     { FP_ZONE("bloom.extract");
-        glBindFramebuffer(GL_FRAMEBUFFER, m_bright_fbo);
-        glViewport(0, 0, m_blur_w, m_blur_h);
-        glUseProgram(m_extract_prog);
+        glBindFramebuffer(GL_FRAMEBUFFER, _bright_fbo);
+        glViewport(0, 0, _blur_w, _blur_h);
+        glUseProgram(_extract_prog);
         glActiveTexture(GL_TEXTURE0);
-        glBindTexture(GL_TEXTURE_2D, m_scene_color_tex);
-        glUniform1i(m_uExtractTex, 0);
+        glBindTexture(GL_TEXTURE_2D, _scene_color_tex);
+        glUniform1i(_uExtractTex, 0);
         glDrawArrays(GL_TRIANGLES, 0, 6);
     }
 
 
     // ── Blur ping-pong ──────────────────────────────────────────────────
-    // First iteration reads from m_bright_tex, then ping-pongs between
-    // m_blur_tex[0] and m_blur_tex[1]. 4 full iterations = 8 passes.
+    // First iteration reads from _bright_tex, then ping-pongs between
+    // _blur_tex[0] and _blur_tex[1]. 4 full iterations = 8 passes.
     { FP_ZONE("bloom.blur");
         const int ITERATIONS = 2;
 
-        glUseProgram(m_blur_prog);
-        glUniform2f(m_uBlurTexelSize, 1.0f / static_cast<float>(m_blur_w),
-                                      1.0f / static_cast<float>(m_blur_h));
+        glUseProgram(_blur_prog);
+        glUniform2f(_uBlurTexelSize, 1.0f / static_cast<float>(_blur_w),
+                                      1.0f / static_cast<float>(_blur_h));
 
-        GLuint src_tex = m_bright_tex;
+        GLuint src_tex = _bright_tex;
         for (int i = 0; i < ITERATIONS; ++i) {
             // Horizontal pass: src → blur_tex[0] via blur_fbo[0]
-            glBindFramebuffer(GL_FRAMEBUFFER, m_blur_fbo[0]);
+            glBindFramebuffer(GL_FRAMEBUFFER, _blur_fbo[0]);
             glBindTexture(GL_TEXTURE_2D, src_tex);
-            glUniform1i(m_uBlurTex, 0);
-            glUniform2f(m_uBlurDir, 1.0f, 0.0f);
+            glUniform1i(_uBlurTex, 0);
+            glUniform2f(_uBlurDir, 1.0f, 0.0f);
             glDrawArrays(GL_TRIANGLES, 0, 6);
 
             // Vertical pass: blur_tex[0] → blur_tex[1] via blur_fbo[1]
-            glBindFramebuffer(GL_FRAMEBUFFER, m_blur_fbo[1]);
-            glBindTexture(GL_TEXTURE_2D, m_blur_tex[0]);
-            glUniform1i(m_uBlurTex, 0);
-            glUniform2f(m_uBlurDir, 0.0f, 1.0f);
+            glBindFramebuffer(GL_FRAMEBUFFER, _blur_fbo[1]);
+            glBindTexture(GL_TEXTURE_2D, _blur_tex[0]);
+            glUniform1i(_uBlurTex, 0);
+            glUniform2f(_uBlurDir, 0.0f, 1.0f);
             glDrawArrays(GL_TRIANGLES, 0, 6);
 
             // Next iteration's input is blur_tex[1]. We swap the names so the
             // final result always ends up in blur_tex[0] for compositing.
-            src_tex = m_blur_tex[1];
+            src_tex = _blur_tex[1];
         }
     }
 
-    // After loop: final bloom is in m_blur_tex[1]. We'll sample it directly.
+    // After loop: final bloom is in _blur_tex[1]. We'll sample it directly.
     glBindVertexArray(0);
     glEnable(GL_DEPTH_TEST);
 }
@@ -1112,29 +1112,29 @@ GlRenderer::create_CompositeProgram_()
 {
     GLuint v = compileShader(GL_VERTEX_SHADER,   kBlitVertSrc);
     GLuint f = compileShader(GL_FRAGMENT_SHADER, kCompositeFragSrc);
-    m_composite_prog = glCreateProgram();
-    glAttachShader(m_composite_prog, v);
-    glAttachShader(m_composite_prog, f);
-    glLinkProgram(m_composite_prog);
+    _composite_prog = glCreateProgram();
+    glAttachShader(_composite_prog, v);
+    glAttachShader(_composite_prog, f);
+    glLinkProgram(_composite_prog);
     glDeleteShader(v); glDeleteShader(f);
-    m_uComposeScene    = glGetUniformLocation(m_composite_prog, "u_SceneTex");
-    m_uComposeBloom    = glGetUniformLocation(m_composite_prog, "u_BloomTex");
-    m_uComposeStrength = glGetUniformLocation(m_composite_prog, "u_BloomStrength");
+    _uComposeScene    = glGetUniformLocation(_composite_prog, "u_SceneTex");
+    _uComposeBloom    = glGetUniformLocation(_composite_prog, "u_BloomTex");
+    _uComposeStrength = glGetUniformLocation(_composite_prog, "u_BloomStrength");
 }
 
 void
 GlRenderer::destroy_FBO_()
 {
-    if (m_scene_color_tex) { glDeleteTextures(1, &m_scene_color_tex);    m_scene_color_tex = 0; }
-    if (m_scene_depth_rbo) { glDeleteRenderbuffers(1, &m_scene_depth_rbo); m_scene_depth_rbo = 0; }
-    if (m_scene_fbo)       { glDeleteFramebuffers(1, &m_scene_fbo);       m_scene_fbo = 0; }
-    m_fbo_w = 0; m_fbo_h = 0;
+    if (_scene_color_tex) { glDeleteTextures     (1, &_scene_color_tex); _scene_color_tex = 0; }
+    if (_scene_depth_rbo) { glDeleteRenderbuffers(1, &_scene_depth_rbo); _scene_depth_rbo = 0; }
+    if (_scene_fbo)       { glDeleteFramebuffers (1, &_scene_fbo);       _scene_fbo       = 0; }
+    _fbo_w = 0; _fbo_h = 0;
 }
 
 void
 GlRenderer::resize_FBO_(int w, int h)
 {
-    if (w == m_fbo_w && h == m_fbo_h) return;
+    if (w == _fbo_w && h == _fbo_h) return;
     destroy_FBO_();
     create_FBO_(w, h);
 }
diff --git a/src/rendering/gl_renderer.hpp b/src/rendering/gl_renderer.hpp
index e43da87..3810468 100644
--- a/src/rendering/gl_renderer.hpp
+++ b/src/rendering/gl_renderer.hpp
@@ -34,95 +34,95 @@ Mat4 buildModelMat4(const Vec3& pos, float yaw, float pitch);
 
 
 class GlRenderer {
-    int   m_view_w{1}, m_view_h{1};
-    float m_aspect{1.0f};
-    Mat4  m_proj;
-    SDL_GLContext m_ctx{};
-    GLuint        m_prog{};
-
-    GLint m_uMVP       {-1},
-          m_uNormalMat {-1},
-          m_uColor     {-1};
-
-    GLint m_uBodyColor {-1},
-          m_uBackDim   {-1},
-          m_uUseFront  {-1};
-
-    GLint m_uLightDir  {-1},
-          m_uAmbient   {-1},
-          m_uIntensity {-1},
-          m_uSunColor  {-1};
-
-    GLint m_uFlashPos       {-1},
-          m_uFlashDir       {-1},
-          m_uFlashColor     {-1},
-          m_uFlashRange     {-1},
-          m_uFlashInnerCos  {-1},
-          m_uFlashOuterCos  {-1},
-          m_uFlashIntensity {-1};
-
-    GLint m_uModel          {-1};
-    GLint m_uWireframe      {-1};
-    GLint m_uMandelbrot     {-1};
-    GLint m_uWallPaper      {-1};
-    GLint m_uTextLineSpacing{-1};
-
-    GLuint m_emissive_prog{},
-           m_emissive_vao{},
-           m_emissive_vbo{},
-           m_emissive_ebo{};
-
-    GLint  m_uEmissiveMVP    {-1};
-
-    GLuint m_vao{}, m_vbo{}, m_ebo{};
-    GLuint m_vao_c{},  m_vbo_c{},  m_ebo_c{};   // VAO/VBO/EBO for colored mesh
-    GLint  m_uUseVertexColor{-1};
-
-    GLuint m_line_vao{}, m_line_vbo{};
-    GLuint m_menu_vao{}, m_menu_vbo{};
+    int   _view_w{1}, _view_h{1};
+    float _aspect{1.0f};
+    Mat4  _proj;
+    SDL_GLContext _ctx{};
+    GLuint        _prog{};
+
+    GLint _uMVP       {-1},
+          _uNormalMat {-1},
+          _uColor     {-1};
+
+    GLint _uBodyColor {-1},
+          _uBackDim   {-1},
+          _uUseFront  {-1};
+
+    GLint _uLightDir  {-1},
+          _uAmbient   {-1},
+          _uIntensity {-1},
+          _uSunColor  {-1};
+
+    GLint _uFlashPos       {-1},
+          _uFlashDir       {-1},
+          _uFlashColor     {-1},
+          _uFlashRange     {-1},
+          _uFlashInnerCos  {-1},
+          _uFlashOuterCos  {-1},
+          _uFlashIntensity {-1};
+
+    GLint _uModel          {-1};
+    GLint _uWireframe      {-1};
+    GLint _uMandelbrot     {-1};
+    GLint _uWallPaper      {-1};
+    GLint _uTextLineSpacing{-1};
+
+    GLuint _emissive_prog{},
+           _emissive_vao{},
+           _emissive_vbo{},
+           _emissive_ebo{};
+
+    GLint  _uEmissiveMVP    {-1};
+
+    GLuint _vao{}, _vbo{}, _ebo{};
+    GLuint _vao_c{},  _vbo_c{},  _ebo_c{};   // VAO/VBO/EBO for colored mesh
+    GLint  _uUseVertexColor{-1};
+
+    GLuint _line_vao{}, _line_vbo{};
+    GLuint _menu_vao{}, _menu_vbo{};
 
 
     // Scene FBO: where the main rendering pass writes.
-    GLuint m_scene_fbo       {0};
-    GLuint m_scene_color_tex {0};
-    GLuint m_scene_depth_rbo {0};
-    int    m_fbo_w           {0};
-    int    m_fbo_h           {0};
+    GLuint _scene_fbo       {0};
+    GLuint _scene_color_tex {0};
+    GLuint _scene_depth_rbo {0};
+    int    _fbo_w           {0};
+    int    _fbo_h           {0};
 
     // Fullscreen-quad geometry for compositing passes.
-    GLuint m_fullscreen_vao  {0};
-    GLuint m_fullscreen_vbo  {0};
+    GLuint _fullscreen_vao  {0};
+    GLuint _fullscreen_vbo  {0};
 
     // Blit program: samples scene texture, writes to screen unchanged.
-    GLuint m_blit_prog       {0};
-    GLint  m_uBlitTex        {-1};
+    GLuint _blit_prog       {0};
+    GLint  _uBlitTex        {-1};
 
         // Half-resolution FBOs for the blur ping-pong.
-    GLuint m_bright_fbo      {0};
-    GLuint m_bright_tex      {0};
-    GLuint m_blur_fbo[2]     {0, 0};
-    GLuint m_blur_tex[2]     {0, 0};
-    int    m_blur_w          {0};
-    int    m_blur_h          {0};
+    GLuint _bright_fbo      {0};
+    GLuint _bright_tex      {0};
+    GLuint _blur_fbo[2]     {0, 0};
+    GLuint _blur_tex[2]     {0, 0};
+    int    _blur_w          {0};
+    int    _blur_h          {0};
 
     // Programs
-    GLuint m_extract_prog    {0};
-    GLuint m_blur_prog       {0};
-    GLuint m_composite_prog  {0};
+    GLuint _extract_prog    {0};
+    GLuint _blur_prog       {0};
+    GLuint _composite_prog  {0};
 
     // Uniforms
-    GLint  m_uExtractTex     {-1};
-    GLint  m_uBlurTex        {-1};
-    GLint  m_uBlurDir        {-1};       // (1,0) or (0,1)
-    GLint  m_uBlurTexelSize  {-1};       // (1/w, 1/h)
-    GLint  m_uComposeScene   {-1};
-    GLint  m_uComposeBloom   {-1};
-    GLint  m_uComposeStrength{-1};       // how much bloom to add (default 1.0)
-    float  m_bloom_strength { 0.6f };
+    GLint  _uExtractTex     {-1};
+    GLint  _uBlurTex        {-1};
+    GLint  _uBlurDir        {-1};       // (1,0) or (0,1)
+    GLint  _uBlurTexelSize  {-1};       // (1/w, 1/h)
+    GLint  _uComposeScene   {-1};
+    GLint  _uComposeBloom   {-1};
+    GLint  _uComposeStrength{-1};       // how much bloom to add (default 1.0)
+    float  _bloom_strength { 0.6f };
 
     // Emissive uniforms on the main program
-    GLint  m_uEmissiveColor    {-1};
-    GLint  m_uEmissiveStrength {-1};
+    GLint  _uEmissiveColor    {-1};
+    GLint  _uEmissiveStrength {-1};
 
     void create_FBO_(int w, int h);
     void resize_FBO_(int w, int h);
@@ -155,7 +155,7 @@ public:
     void set_ColorUseVertex (bool on);
     void set_TextLineSpacing(float m);
     void set_Emissive  (float r, float g, float b, float strength);
-    void set_BloomStrength  (float s) { m_bloom_strength = s; }
+    void set_BloomStrength  (float s) { _bloom_strength = s; }
 
     void enable_MandelbrotMode(bool on);
     void enable_WallPaperMode (bool on);
@@ -189,5 +189,5 @@ public:
                        const int*   indices, int index_count,
                        float r, float g, float b, float strength);
 
-    GLuint program() const { return m_prog; }
+    GLuint program() const { return _prog; }
 };
diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp
index fae00be..82d9257 100644
--- a/src/states/game_state.cpp
+++ b/src/states/game_state.cpp
@@ -28,59 +28,59 @@
 GameState::GameState(size_t starting_level)
 
 {
-    ctx.player = cpputils::make_unique<Player>();
-    ctx.world  = cpputils::make_unique<World>();
-    ctx.camera = cpputils::make_unique<Camera>();
-    ctx.light  = cpputils::make_unique<Light>();
-
-    input_sys       = cpputils::make_unique<InputSystem>();
-    physics_sys     = cpputils::make_unique<PhysicsSystem>();
-    projectiles_sys = cpputils::make_unique<ProjectileSystem>();
-    compiler_sys    = cpputils::make_unique<CompilerSystem>();
-    word_anim_sys   = cpputils::make_unique<WordAnimSystem>();
-    freelook_sys    = cpputils::make_unique<FreelookSystem>();
-    particle_sys    = cpputils::make_unique<ParticleSystem>();
-    mod_loader_sys      = cpputils::make_unique<ModLoader>();
-    celebration_sys = cpputils::make_unique<CelebrationSystem>();
-
-    floor_sys       = cpputils::make_unique<FloorMandelbrot>();
-    floor_sys->generate(ctx.world->scale(), -ctx.world->scale());
-
-    word_ai_sys     = cpputils::make_unique<WordAiSystem>();
-    word_ai_sys->set_WorldScale(ctx.world->scale());
-
-    pult_sys        = cpputils::make_unique<PultSystem>();
-    pult_sys->init(ctx.scenery, ctx.buttons, ctx.world->scale());
+    _ctx.player = cpputils::make_unique<Player>();
+    _ctx.world  = cpputils::make_unique<World>();
+    _ctx.camera = cpputils::make_unique<Camera>();
+    _ctx.light  = cpputils::make_unique<Light>();
+
+    _input_sys       = cpputils::make_unique<InputSystem>();
+    _physics_sys     = cpputils::make_unique<PhysicsSystem>();
+    _projectiles_sys = cpputils::make_unique<ProjectileSystem>();
+    _compiler_sys    = cpputils::make_unique<CompilerSystem>();
+    _word_anim_sys   = cpputils::make_unique<WordAnimSystem>();
+    _freelook_sys    = cpputils::make_unique<FreelookSystem>();
+    _particle_sys    = cpputils::make_unique<ParticleSystem>();
+    _mod_loader_sys      = cpputils::make_unique<ModLoader>();
+    _celebration_sys = cpputils::make_unique<CelebrationSystem>();
+
+    _floor_sys       = cpputils::make_unique<FloorMandelbrot>();
+    _floor_sys->generate(_ctx.world->scale(), -_ctx.world->scale());
+
+    _word_ai_sys     = cpputils::make_unique<WordAiSystem>();
+    _word_ai_sys->set_WorldScale(_ctx.world->scale());
+
+    _pult_sys        = cpputils::make_unique<PultSystem>();
+    _pult_sys->init(_ctx.scenery, _ctx.buttons, _ctx.world->scale());
 
     if (starting_level >= level_Count()) starting_level = 0;
-    level_runner_sys    = cpputils::make_unique<LevelRunner>(get_Level(starting_level));
+    _level_runner_sys    = cpputils::make_unique<LevelRunner>(get_Level(starting_level));
 
-    DynArray<std::string> tokens = level_runner_sys->grants_for_step();
+    DynArray<std::string> tokens = _level_runner_sys->grants_for_step();
     step_Spawn(tokens);
 
-    scene_Setup(level_runner_sys->current().scene);
+    scene_Setup(_level_runner_sys->current().scene);
     step_SettingsApply();
     infowall_StepdetailsShow();
 
-    hint_sys        = cpputils::make_unique<HintSystem>();
-    if (level_runner_sys && !level_runner_sys->complete())
-        hint_sys->set_Hint(level_runner_sys->current().hint);
+    _hint_sys        = cpputils::make_unique<HintSystem>();
+    if (_level_runner_sys && !_level_runner_sys->complete())
+        _hint_sys->set_Hint(_level_runner_sys->current().hint);
 
-    if (level_runner_sys && level_runner_sys->dynamic_lib())
+    if (_level_runner_sys && _level_runner_sys->dynamic_lib())
         sandbox_LoadFileToWall();
 
     /*
-    ctx.scenery.push_back(
+    _ctx.scenery.push_back(
         cpputils::make_unique<SolidBox>(
         Vec3{0, -15, -5},
         Vec3{1.0, 0.5, 1.0}
     ));
-    ctx.scenery.push_back(
+    _ctx.scenery.push_back(
         cpputils::make_unique<SolidBox>(
         Vec3{3, -15, -5},
         Vec3{1.0, 0.5, 1.0}
     ));
-    ctx.scenery.back()->body.mode = PhysicsMode::Dynamic;
+    _ctx.scenery.back()->body.mode = PhysicsMode::Dynamic;
     */
     debug_SpawnWords(0);
 }
@@ -107,26 +107,26 @@ GameState::debug_SpawnWords([[maybe_unused]]size_t n)
 std::tuple<Vec3, Vec3, double>
 GameState::anchorframe_Current() const
 {
-    if (freelook_sys->state.active)
-        return { freelook_sys->state.frozen_anchor,
-                 freelook_sys->state.frozen_right,
-                 freelook_sys->state.frozen_glyph_yaw };
-
-    return { CarryMath::line_Anchor(*ctx.camera, CarryMath::CARRY_DIST),
-             ctx.camera->right,
-             SDL_atan2(-ctx.camera->fwd.x, -ctx.camera->fwd.z) };
+    if (_freelook_sys->state.active)
+        return { _freelook_sys->state.frozen_anchor,
+                 _freelook_sys->state.frozen_right,
+                 _freelook_sys->state.frozen_glyph_yaw };
+
+    return { CarryMath::line_Anchor(*_ctx.camera, CarryMath::CARRY_DIST),
+             _ctx.camera->right,
+             SDL_atan2(-_ctx.camera->fwd.x, -_ctx.camera->fwd.z) };
 }
 
 
 void
 GameState::carriedline_Update()
 {
-    if (carried.empty()) return;
+    if (_carried.empty()) return;
 
     auto [anchor, right, glyph_yaw] = anchorframe_Current();
 
-    DynArray<Vec3>             positions = CarryMath::slot_positions(carried, anchor, right, CarryMath::CARRY_GAP);
-    DynArray<CarriedWordNode*> nodes     = carried.reverse_nodes();
+    DynArray<Vec3>             positions = CarryMath::slot_positions(_carried, anchor, right, CarryMath::CARRY_GAP);
+    DynArray<CarriedWordNode*> nodes     = _carried.reverse_nodes();
 
     for (size_t i = 0; i < nodes.size() && i < positions.size(); ++i) {
         if (nodes[i]->word) {
@@ -140,22 +140,22 @@ GameState::carriedline_Update()
 void
 GameState::pult_DispatchAction(PultAction act)
 {
-    step_ledger.buttons_pressed.push_back(act);
+    _step_ledger.buttons_pressed.push_back(act);
 
-    const double floor_y = -static_cast<double>(ctx.world->scale());
+    const double floor_y = -static_cast<double>(_ctx.world->scale());
 
     switch (act) {
         case PultAction::ScrollEditorUp:
-            TextWallScroll::scroll_Up(ctx.wall_texts, Wall::Role::Editor);
-            editor_wall.scroll(TextWallScroll::SCROLL_STEP);
+            TextWallScroll::scroll_Up(_ctx.wall_texts, Wall::Role::Editor);
+            _editor_wall.scroll(TextWallScroll::SCROLL_STEP);
             break;
         case PultAction::ScrollEditorDown:
-            TextWallScroll::scroll_Down(ctx.wall_texts, Wall::Role::Editor);
-            editor_wall.scroll(-TextWallScroll::SCROLL_STEP);
+            TextWallScroll::scroll_Down(_ctx.wall_texts, Wall::Role::Editor);
+            _editor_wall.scroll(-TextWallScroll::SCROLL_STEP);
             break;
         case PultAction::ClearEditor:
-            TextWallScroll::clear_ToFloor(ctx.wall_texts, ctx.words, Wall::Role::Editor, floor_y);
-            editor_wall.clear();
+            TextWallScroll::clear_ToFloor(_ctx.wall_texts, _ctx.words, Wall::Role::Editor, floor_y);
+            _editor_wall.clear();
             break;
         case PultAction::Compile:
             on_Compile();
@@ -166,12 +166,12 @@ GameState::pult_DispatchAction(PultAction act)
 void
 GameState::on_Click()
 {
-    PickResult r = PickSystem::pick(*ctx.camera, ctx.words, ctx.scenery, ctx.wall_texts);
+    PickResult r = PickSystem::pick(*_ctx.camera, _ctx.words, _ctx.scenery, _ctx.wall_texts);
     if (!r.hit()) return;
 
     if (r.kind == PickKind::Scenery && r.scenery_obj) {
         if (std::optional<PultAction> act =
-                pult_sys->try_Activate(r.scenery_obj, ctx.buttons); act)
+                _pult_sys->try_Activate(r.scenery_obj, _ctx.buttons); act)
             pult_DispatchAction(*act);
     }
 }
@@ -179,36 +179,36 @@ GameState::on_Click()
 bool
 GameState::on_Pick()
 {
-    PickResult r = PickSystem::pick(*ctx.camera, ctx.words, ctx.scenery, ctx.wall_texts);
+    PickResult r = PickSystem::pick(*_ctx.camera, _ctx.words, _ctx.scenery, _ctx.wall_texts);
     if (!r.hit()) return false;
 
     // ── Wall text: pluck off the wall, animate into carry ───────────
     if (r.kind == PickKind::Wall) {
-        if (ctx.wall_texts[r.wall_index] && ctx.wall_texts[r.wall_index]->text3d)
-            editor_wall.on_WordRemoved(ctx.wall_texts[r.wall_index]->text3d.get());
+        if (_ctx.wall_texts[r.wall_index] && _ctx.wall_texts[r.wall_index]->text3d)
+            _editor_wall.on_WordRemoved(_ctx.wall_texts[r.wall_index]->text3d.get());
 
-        Text3dPtr base = std::move(ctx.wall_texts[r.wall_index]->text3d);
-        ctx.wall_texts.erase(ctx.wall_texts.begin() + static_cast<std::ptrdiff_t>(r.wall_index));
+        Text3dPtr base = std::move(_ctx.wall_texts[r.wall_index]->text3d);
+        _ctx.wall_texts.erase(_ctx.wall_texts.begin() + static_cast<std::ptrdiff_t>(r.wall_index));
         if (!base) return false;
 
         if (base->type == ObjectType::Text) {
-            step_ledger.picked_up.push_back(base->text);
+            _step_ledger.picked_up.push_back(base->text);
             base->set_Look(Text3d::color_keep, Text3d::depth_floor);
-            TextWall::show_TokenInfo(ctx.wall_texts, base->text, ctx.world->scale());
+            TextWall::show_TokenInfo(_ctx.wall_texts, base->text, _ctx.world->scale());
         }
         carry_BeginAnim(std::move(base), /*with_pitch=*/true);
         return true;
     }
 
-    // ── Word: same flow, takes Text3dPtr out of ctx.words ───────────
+    // ── Word: same flow, takes Text3dPtr out of _ctx.words ───────────
     if (r.kind == PickKind::Word) {
-        Text3dPtr word = std::move(ctx.words[r.word_index]);
-        ctx.words.erase(ctx.words.begin() + static_cast<std::ptrdiff_t>(r.word_index));
+        Text3dPtr word = std::move(_ctx.words[r.word_index]);
+        _ctx.words.erase(_ctx.words.begin() + static_cast<std::ptrdiff_t>(r.word_index));
         if (!word) return false;
 
-        step_ledger.picked_up.push_back(word->text);
+        _step_ledger.picked_up.push_back(word->text);
         word->set_Look(Text3d::color_keep, Text3d::depth_floor);
-        TextWall::show_TokenInfo(ctx.wall_texts, word->text, ctx.world->scale());
+        TextWall::show_TokenInfo(_ctx.wall_texts, word->text, _ctx.world->scale());
 
         carry_BeginAnim(std::move(word), /*with_pitch=*/false);
         return true;
@@ -220,53 +220,53 @@ GameState::on_Pick()
 void
 GameState::on_DropLast()
 {
-    if (carried.empty()) return;
+    if (_carried.empty()) return;
 
-    Text3dPtr word = carried.pop_back();
+    Text3dPtr word = _carried.pop_back();
     if (!word) return;
 
     infowall_Clear();
 
     word->body.mode = PhysicsMode::Dynamic;
-    word->body.vel  = { ctx.camera->fwd.x * 1.5, 0.8, ctx.camera->fwd.z * 1.5 };
+    word->body.vel  = { _ctx.camera->fwd.x * 1.5, 0.8, _ctx.camera->fwd.z * 1.5 };
 
     WordAnim_Drop drop;
     drop.word = std::move(word);
-    anims_drop.push_back(std::move(drop));
+    _anims_drop.push_back(std::move(drop));
 
-    if (!carried.empty())
-        TextWall::show_TokenInfo(ctx.wall_texts, carried.last->word->text, ctx.world->scale());
+    if (!_carried.empty())
+        TextWall::show_TokenInfo(_ctx.wall_texts, _carried.last->word->text, _ctx.world->scale());
     else
         infowall_StepdetailsShow();
 
-    step_ledger.dropped = true;
+    _step_ledger.dropped = true;
 }
 
 
 bool
 GameState::on_ShootOne()
 {
-    if (carried.empty()) return false;
+    if (_carried.empty()) return false;
 
-    Text3dPtr word = carried.pop_back();
+    Text3dPtr word = _carried.pop_back();
     if (!word) return false;
 
     infowall_Clear();
 
-    freelook_sys->state.active  = false;
-    freelook_sys->state.lerping = false;
+    _freelook_sys->state.active  = false;
+    _freelook_sys->state.lerping = false;
 
     DynArray<Text3dPtr> single;
     single.push_back(std::move(word));
-    projectiles_sys->launch(projectiles, std::move(single), ctx.camera->fwd);
+    _projectiles_sys->launch(_projectiles, std::move(single), _ctx.camera->fwd);
 
-    if (!carried.empty() && carried.last && carried.last->word)
-        TextWall::show_TokenInfo(ctx.wall_texts,
-                                 carried.last->word->text,
-                                 ctx.world->scale());
+    if (!_carried.empty() && _carried.last && _carried.last->word)
+        TextWall::show_TokenInfo(_ctx.wall_texts,
+                                 _carried.last->word->text,
+                                 _ctx.world->scale());
     else infowall_StepdetailsShow();
 
-    step_ledger.shot = true;
+    _step_ledger.shot = true;
     return true;
 }
 
@@ -274,41 +274,41 @@ GameState::on_ShootOne()
 void
 GameState::on_ShootAll()
 {
-    if (carried.empty()) return;
+    if (_carried.empty()) return;
 
     infowall_Clear();
 
-    freelook_sys->state.active  = false;
-    freelook_sys->state.lerping = false;
+    _freelook_sys->state.active  = false;
+    _freelook_sys->state.lerping = false;
 
     DynArray<Text3dPtr> all_words;
-    for (CarriedWordNode* node : carried.nodes())
+    for (CarriedWordNode* node : _carried.nodes())
         if (node->word)
             all_words.push_back(std::move(node->word));
 
-    carried = CarriedLine();
+    _carried = CarriedLine();
     if (!all_words.empty())
-        projectiles_sys->launch(projectiles, std::move(all_words), ctx.camera->fwd);
+        _projectiles_sys->launch(_projectiles, std::move(all_words), _ctx.camera->fwd);
 
     infowall_StepdetailsShow();
 
-    step_ledger.shot = true;
+    _step_ledger.shot = true;
 }
 
 
 void GameState::on_Compile() {
-    std::string src = TextWall::extract_Text(ctx.wall_texts, Wall::Role::Editor);
+    std::string src = TextWall::extract_Text(_ctx.wall_texts, Wall::Role::Editor);
 
-    if (level_runner_sys && !level_runner_sys->complete() && level_runner_sys->dynamic_lib()) {
+    if (_level_runner_sys && !_level_runner_sys->complete() && _level_runner_sys->dynamic_lib()) {
         const std::string path = sandbox_CanonicalPath();
         std::string stripped;
         for (char c : src) if (!std::isspace((unsigned char)c)) { stripped = src; break; }
         if (!path.empty() && !stripped.empty())
             SourceTokens::write_File(path, src);
 
-        mod_loader_sys->build(ctx.wall_texts, src, ctx.world->scale(), true);
+        _mod_loader_sys->build(_ctx.wall_texts, src, _ctx.world->scale(), true);
     } else {
-        compiler_sys->compile(ctx.wall_texts, src);
+        _compiler_sys->compile(_ctx.wall_texts, src);
     }
 }
 
@@ -316,8 +316,8 @@ void GameState::on_Compile() {
 void
 GameState::on_DebugPrint()
 {
-    std::print("TEXT::: {0}\n", TextWall::extract_Text(ctx.wall_texts, Wall::Role::Program));
-    for (TextWallPtr& wt : ctx.wall_texts) {
+    std::print("TEXT::: {0}\n", TextWall::extract_Text(_ctx.wall_texts, Wall::Role::Program));
+    for (TextWallPtr& wt : _ctx.wall_texts) {
         if (wt->text3d->type == ObjectType::Text) {
             SDL_Log("wall text '%s' y=%.3f", wt->text3d->text.c_str(), wt->text3d->pos.y);
         }
@@ -328,9 +328,9 @@ GameState::on_DebugPrint()
 void
 GameState::on_Jump()
 {
-    if (ctx.player->on_ground) {
-        ctx.player->vel.y     = Player::JUMP_VELOCITY;
-        ctx.player->on_ground = false;
+    if (_ctx.player->on_ground) {
+        _ctx.player->vel.y     = Player::JUMP_VELOCITY;
+        _ctx.player->on_ground = false;
     }
 }
 
@@ -338,26 +338,26 @@ GameState::on_Jump()
 void
 GameState::on_FreelookStart()
 {
-    Vec3   anchor = CarryMath::line_Anchor(*ctx.camera, CarryMath::CARRY_DIST);
-    double yaw    = SDL_atan2(-ctx.camera->fwd.x, -ctx.camera->fwd.z);
-    freelook_sys->begin(anchor, ctx.camera->right, yaw);
+    Vec3   anchor = CarryMath::line_Anchor(*_ctx.camera, CarryMath::CARRY_DIST);
+    double yaw    = SDL_atan2(-_ctx.camera->fwd.x, -_ctx.camera->fwd.z);
+    _freelook_sys->begin(anchor, _ctx.camera->right, yaw);
 }
 
 
 void
 GameState::on_FreelookEnd()
 {
-    if (freelook_sys->state.active)
-        freelook_sys->end(ctx.player->yaw, ctx.player->pitch,
-                          ctx.camera->pos,
-                          ctx.player->yaw, ctx.player->pitch);
+    if (_freelook_sys->state.active)
+        _freelook_sys->end(_ctx.player->yaw, _ctx.player->pitch,
+                          _ctx.camera->pos,
+                          _ctx.player->yaw, _ctx.player->pitch);
 }
 
 void
 GameState::on_NumberAdjust(int delta)
 {
-    if (carried.empty() || !carried.last || !carried.last->word) return;
-    Text3d* w = carried.last->word.get();
+    if (_carried.empty() || !_carried.last || !_carried.last->word) return;
+    Text3d* w = _carried.last->word.get();
 
     // Parse the text as an integer; bail if it isn't one.
     const std::string& s = w->text;
@@ -380,14 +380,14 @@ GameState::on_TextStart(FrameInput& input)
 {
     input.text_mode = true;
     input.text_buffer.clear();
-    typing_words.clear();
-    typing_last.clear();
+    _typing_words.clear();
+    _typing_last.clear();
 }
 void
 GameState::on_TextCommit(FrameInput& input)
 {
-    typing_words.clear();
-    typing_last.clear();
+    _typing_words.clear();
+    _typing_last.clear();
     input.text_mode = false;
     input.text_buffer.clear();
 }
@@ -395,7 +395,7 @@ void
 GameState::on_TextCancel(FrameInput& input)
 {
     typing_ClearPreview();
-    typing_last.clear();
+    _typing_last.clear();
     input.text_mode = false;
     input.text_buffer.clear();
 }
@@ -408,7 +408,7 @@ GameState::step_Spawn(const DynArray<std::string>& tokens)
     static constexpr int   per_row = 8;
     static constexpr float row_gap = 3.0f;
 
-    const double floor_y = -static_cast<double>(ctx.world->scale());
+    const double floor_y = -static_cast<double>(_ctx.world->scale());
     const double hover_y = floor_y + 1.5;
 
     for (size_t i = 0; i < tokens.size(); ++i) {
@@ -424,7 +424,7 @@ GameState::step_Spawn(const DynArray<std::string>& tokens)
 
         Text3dPtr obj = make_Text3d(tokens[i], Text3d::depth_floor, 0.003f, pos);
         obj->body.hover_height = hover_y;
-        ctx.words.push_back(std::move(obj));
+        _ctx.words.push_back(std::move(obj));
     }
 }
 
@@ -437,7 +437,7 @@ GameState::projectile_LandOnFloor(ProjectileResult& result)
         w->set_Look(Text3d::color_keep, Text3d::depth_floor);
         w->body.mode = PhysicsMode::Dynamic;
         w->body.vel  = result.vel;
-        ctx.words.push_back(std::move(w));
+        _ctx.words.push_back(std::move(w));
     }
 }
 
@@ -453,16 +453,16 @@ GameState::projectile_LandOnWall(ProjectileResult& result)
         w->body.mode = PhysicsMode::Static;
         w->body.vel  = Vec3{0, 0, 0};
 
-        TextWallPtr wt = TextWall::from_Drop(std::move(w), ctx.world->scale());
+        TextWallPtr wt = TextWall::from_Drop(std::move(w), _ctx.world->scale());
 
         // Editor-wall snap: rewrite position to align with row.
         if (wt && wt->text3d && wt->role == Wall::Role::Editor) {
             const Vec3 impact = wt->text3d->pos;
-            const Vec3 snapped = editor_wall.on_WordPlaced(wt->text3d.get(), impact);
+            const Vec3 snapped = _editor_wall.on_WordPlaced(wt->text3d.get(), impact);
             wt->text3d->pos = snapped;
         }
 
-        ctx.wall_texts.push_back(std::move(wt));
+        _ctx.wall_texts.push_back(std::move(wt));
     }
 }
 
@@ -471,18 +471,18 @@ void
 GameState::walltext_HandleShootDown(ProjectileResult& result,
                                   DynArray<size_t>& to_erase)
 {
-    if (result.wall_text_index < ctx.wall_texts.size()) {
-        TextWallPtr& wt = ctx.wall_texts[result.wall_text_index];
+    if (result.wall_text_index < _ctx.wall_texts.size()) {
+        TextWallPtr& wt = _ctx.wall_texts[result.wall_text_index];
         if (wt && wt->text3d) {
-            editor_wall.on_WordRemoved(wt->text3d.get());
+            _editor_wall.on_WordRemoved(wt->text3d.get());
 
             Text3dPtr orphan = std::move(wt->text3d);
             orphan->body.mode         = PhysicsMode::Dynamic;
             orphan->body.vel          = { 0.0, 1.5, 0.0 };
-            orphan->body.hover_height = -static_cast<double>(ctx.world->scale()) + 1.5;
+            orphan->body.hover_height = -static_cast<double>(_ctx.world->scale()) + 1.5;
             orphan->yaw               = 0.0f;
             orphan->set_Look(Text3d::color_keep, Text3d::depth_floor);
-            ctx.words.push_back(std::move(orphan));
+            _ctx.words.push_back(std::move(orphan));
             to_erase.push_back(result.wall_text_index);
         }
     }
@@ -494,7 +494,7 @@ GameState::walltext_HandleShootDown(ProjectileResult& result,
         }
         w->body.mode = PhysicsMode::Dynamic;
         w->body.vel  = result.vel;
-        ctx.words.push_back(std::move(w));
+        _ctx.words.push_back(std::move(w));
     }
 }
 
@@ -502,16 +502,16 @@ GameState::walltext_HandleShootDown(ProjectileResult& result,
 void
 GameState::handle_Event(Game& game, const SDL_Event& event)
 {
-    input_sys->handle_Event(game, event, frame_input);
+    _input_sys->handle_Event(game, event, _frame_input);
 }
 
 
 void
 GameState::update(double dt)
 {
-    FrameInput& input = frame_input;
+    FrameInput& input = _frame_input;
 
-    step_age += dt;
+    _step_age += dt;
 
     { FP_ZONE("update.input_dispatch");
         for (InputCommand cmd : input.commands) {
@@ -535,87 +535,87 @@ GameState::update(double dt)
         if (input.text_mode) {
             input.forward = input.backward = input.left = input.right = false;
 
-            if (input.text_buffer != typing_last) {
+            if (input.text_buffer != _typing_last) {
                 typing_RebuildPreview(input.text_buffer);
-                typing_last = input.text_buffer;
+                _typing_last = input.text_buffer;
             }
         }
-        if (pick_cooldown > 0.0) pick_cooldown -= dt;
-        if (input.pick && pick_cooldown <= 0.0)
-            { if (on_Pick()) pick_cooldown = 0.15; }
+        if (_pick_cooldown > 0.0) _pick_cooldown -= dt;
+        if (input.pick && _pick_cooldown <= 0.0)
+            { if (on_Pick()) _pick_cooldown = 0.15; }
 
-        if (shoot_cooldown > 0.0) shoot_cooldown -= dt;
-        if (input.shoot && shoot_cooldown <= 0.0)
-            { if (on_ShootOne()) shoot_cooldown = 0.15; }
+        if (_shoot_cooldown > 0.0) _shoot_cooldown -= dt;
+        if (input.shoot && _shoot_cooldown <= 0.0)
+            { if (on_ShootOne()) _shoot_cooldown = 0.15; }
     }
 
 
-    const bool freelook_active = freelook_sys->state.active ||
-                                 freelook_sys->state.lerping;
+    const bool freelook_active = _freelook_sys->state.active ||
+                                 _freelook_sys->state.lerping;
     if (!freelook_active)
-        ctx.player->add_Look(input.look_x, input.look_y);
+        _ctx.player->add_Look(input.look_x, input.look_y);
 
-    freelook_sys->update(dt, ctx.player->yaw, ctx.player->pitch);
+    _freelook_sys->update(dt, _ctx.player->yaw, _ctx.player->pitch);
 
-    accumulator += dt;
-    ctx.player->compute_Movement(input);
+    _accumulator += dt;
+    _ctx.player->compute_Movement(input);
 
     // why do i still have that MAX_SUBSTEPS and not whatever frames we get
     int steps = 0;
-    while (accumulator >= dt && steps < MAX_SUBSTEPS) {
+    while (_accumulator >= dt && steps < MAX_SUBSTEPS) {
         FP_ZONE("update.fixed_substep");
         substep(dt);
-        accumulator -= dt;
+        _accumulator -= dt;
         ++steps;
     }
 
-    accumulator = SDL_min(accumulator, dt * MAX_SUBSTEPS);
-    alpha       = accumulator / dt;
+    _accumulator = SDL_min(_accumulator, dt * MAX_SUBSTEPS);
+    _alpha       = _accumulator / dt;
 
     { FP_ZONE("update.post_step");
-        ctx.camera->syncFromPlayer(*ctx.player, alpha);
+        _ctx.camera->syncFromPlayer(*_ctx.player, _alpha);
 
-        for (TextWallPtr& wt : ctx.wall_texts) {
+        for (TextWallPtr& wt : _ctx.wall_texts) {
             if (!wt || !wt->text3d) continue;
-            wt->text3d->pitch = Wall::tilt_TowardEye(wt->text3d->pos, ctx.camera->pos);
+            wt->text3d->pitch = Wall::tilt_TowardEye(wt->text3d->pos, _ctx.camera->pos);
         }
 
         carriedline_Update();
 
-        const double floor_y = -static_cast<double>(ctx.world->scale());
-        word_anim_sys->update(anims_add, anims_drop, carried, ctx.words, dt, floor_y);
+        const double floor_y = -static_cast<double>(_ctx.world->scale());
+        _word_anim_sys->update(_anims_add, _anims_drop, _carried, _ctx.words, dt, floor_y);
 
-        pult_sys->update(dt, *ctx.camera);
+        _pult_sys->update(dt, *_ctx.camera);
 
-        plate_sys->update(dt, *ctx.player, step_plates);
+        _plate_sys->update(dt, *_ctx.player, _step_plates);
     }
 
 
     { FP_ZONE("update.particles");
         DynArray<GameObject*> particle_colliders;
-        particle_colliders.reserve(ctx.scenery.size());
-        for (const GameObjectPtr& s : ctx.scenery)
+        particle_colliders.reserve(_ctx.scenery.size());
+        for (const GameObjectPtr& s : _ctx.scenery)
             if (s) particle_colliders.push_back(s.get());
-        particle_sys->update(dt, particle_colliders, ctx.world->scale());
+        _particle_sys->update(dt, particle_colliders, _ctx.world->scale());
     }
 
-    { FP_ZONE("update.compiler_sys");
-        compiler_sys->update(ctx.wall_texts, ctx.world->scale());
-        if (compiler_sys->last_compile_ok) {
-            compiler_sys->last_compile_ok = false;
-            step_ledger.compile_ok = true;
+    { FP_ZONE("update._compiler_sys");
+        _compiler_sys->update(_ctx.wall_texts, _ctx.world->scale());
+        if (_compiler_sys->last_compile_ok) {
+            _compiler_sys->last_compile_ok = false;
+            _step_ledger.compile_ok = true;
         }
 
-        mod_loader_sys->update(ctx.wall_texts, ctx.world->scale());
-        if (mod_loader_sys->last_build_ok) {
-            mod_loader_sys->last_build_ok = false;
-            mod_Apply(mod_loader_sys->load_Objects());
+        _mod_loader_sys->update(_ctx.wall_texts, _ctx.world->scale());
+        if (_mod_loader_sys->last_build_ok) {
+            _mod_loader_sys->last_build_ok = false;
+            mod_Apply(_mod_loader_sys->load_Objects());
             SourceTokens::copy_File(sandbox_CanonicalPath(), sandbox_BackupPath());
         }
     }
 
     { FP_ZONE("update.goals");
-        celebration_sys->tick(dt, *particle_sys);
+        _celebration_sys->tick(dt, *_particle_sys);
         level_TryAdvance();
     }
 
@@ -632,70 +632,70 @@ GameState::render(SDL_Window& window, GlRenderer& renderer)
     SDL_GetWindowSizeInPixels(&window, &w, &h);
 
     { FP_ZONE("render.light_setup");
-        const double yaw_r   = BINRAD * static_cast<double>(ctx.camera->yaw);
-        const double pitch_r = BINRAD * static_cast<double>(ctx.camera->pitch);
+        const double yaw_r   = BINRAD * static_cast<double>(_ctx.camera->yaw);
+        const double pitch_r = BINRAD * static_cast<double>(_ctx.camera->pitch);
         const double cy = SDL_cos(yaw_r),   sy = SDL_sin(yaw_r);
         const double cp = SDL_cos(pitch_r), sp = SDL_sin(pitch_r);
-        ctx.light->flashlight_pos = ctx.camera->pos;
-        ctx.light->flashlight_dir = Vec3{ -sy*cp, sp, -cy*cp };
+        _ctx.light->flashlight_pos = _ctx.camera->pos;
+        _ctx.light->flashlight_dir = Vec3{ -sy*cp, sp, -cy*cp };
     }
 
     { FP_ZONE("render.begin_Frame"); renderer.begin_Frame(w, h); }
-    { FP_ZONE("render.set_Light");   renderer.set_Light(*ctx.light); }
+    { FP_ZONE("render.set_Light");   renderer.set_Light(*_ctx.light); }
 
-    { FP_ZONE("render.world");        ctx.world ->draw(renderer, *ctx.camera, *ctx.light); }
-    { FP_ZONE("render.editor_wall");  editor_wall.draw(renderer, *ctx.camera, *ctx.light); }
+    { FP_ZONE("render.world");        _ctx.world ->draw(renderer, *_ctx.camera, *_ctx.light); }
+    { FP_ZONE("render._editor_wall");  _editor_wall.draw(renderer, *_ctx.camera, *_ctx.light); }
 
     { FP_ZONE("render.scenery");
-        for (const GameObjectPtr& s : ctx.scenery)
-            if (s) s->draw(renderer, *ctx.camera, *ctx.light);
+        for (const GameObjectPtr& s : _ctx.scenery)
+            if (s) s->draw(renderer, *_ctx.camera, *_ctx.light);
     }
 
     { FP_ZONE("render.words");
-        for (const Text3dPtr& t : ctx.words)
-            if (t) t->draw(renderer, *ctx.camera, *ctx.light);
+        for (const Text3dPtr& t : _ctx.words)
+            if (t) t->draw(renderer, *_ctx.camera, *_ctx.light);
     }
 
     { FP_ZONE("render.wall_texts");
-        for (const TextWallPtr& wt : ctx.wall_texts)
-            wt->draw(renderer, *ctx.camera, *ctx.light);
+        for (const TextWallPtr& wt : _ctx.wall_texts)
+            wt->draw(renderer, *_ctx.camera, *_ctx.light);
     }
 
-    { FP_ZONE("render.carried");
-        DynArray<CarriedWordNode*> nodes = carried.nodes();
+    { FP_ZONE("render._carried");
+        DynArray<CarriedWordNode*> nodes = _carried.nodes();
         for (const CarriedWordNode* node : nodes)
             if (node && node->word)
-                node->word->draw(renderer, *ctx.camera, *ctx.light);
+                node->word->draw(renderer, *_ctx.camera, *_ctx.light);
     }
 
     { FP_ZONE("render.anims");
-        for (const WordAnim_Add& a : anims_add)
-            if (a.word) a.word->draw(renderer, *ctx.camera, *ctx.light);
-        for (const WordAnim_Drop& d : anims_drop)
-            if (d.word) d.word->draw(renderer, *ctx.camera, *ctx.light);
+        for (const WordAnim_Add& a : _anims_add)
+            if (a.word) a.word->draw(renderer, *_ctx.camera, *_ctx.light);
+        for (const WordAnim_Drop& d : _anims_drop)
+            if (d.word) d.word->draw(renderer, *_ctx.camera, *_ctx.light);
     }
 
     { FP_ZONE("render.ai_stolen");
-        for (const Text3dPtr& t : ctx.words) {
+        for (const Text3dPtr& t : _ctx.words) {
             if (!t) continue;
             for (const Text3dPtr& s : t->ai_stolen)
-                if (s) s->draw(renderer, *ctx.camera, *ctx.light);
+                if (s) s->draw(renderer, *_ctx.camera, *_ctx.light);
         }
     }
 
-    { FP_ZONE("render.projectiles");
-        for (const Projectile& proj : projectiles)
+    { FP_ZONE("render._projectiles");
+        for (const Projectile& proj : _projectiles)
             for (const Text3dPtr& wd : proj.words)
-                if (wd) wd->draw(renderer, *ctx.camera, *ctx.light);
+                if (wd) wd->draw(renderer, *_ctx.camera, *_ctx.light);
     }
 
-    { FP_ZONE("render.hint");        hint_sys    ->draw(renderer, *ctx.camera, *ctx.light); }
-    { FP_ZONE("render.pult_labels"); pult_sys    ->draw_Labels(renderer, *ctx.camera, *ctx.light); }
-    { FP_ZONE("render.particles");   particle_sys->draw(renderer, *ctx.camera, *ctx.light); }
-    { FP_ZONE("render.floor_sys");   floor_sys   ->draw(renderer, *ctx.camera, *ctx.light); }
+    { FP_ZONE("render.hint");        _hint_sys    ->draw(renderer, *_ctx.camera, *_ctx.light); }
+    { FP_ZONE("render.pult_labels"); _pult_sys    ->draw_Labels(renderer, *_ctx.camera, *_ctx.light); }
+    { FP_ZONE("render.particles");   _particle_sys->draw(renderer, *_ctx.camera, *_ctx.light); }
+    { FP_ZONE("render._floor_sys");   _floor_sys   ->draw(renderer, *_ctx.camera, *_ctx.light); }
 
     { FP_ZONE("render.celebration");
-        celebration_sys->draw(renderer, *ctx.camera, *ctx.light);
+        _celebration_sys->draw(renderer, *_ctx.camera, *_ctx.light);
     }
 
     { FP_ZONE("render.end_Frame"); renderer.end_Frame(&window); }
@@ -705,25 +705,25 @@ GameState::render(SDL_Window& window, GlRenderer& renderer)
 void
 GameState::infowall_Clear()
 {
-    ctx.wall_texts.erase(
-        std::remove_if(ctx.wall_texts.begin(), ctx.wall_texts.end(),
+    _ctx.wall_texts.erase(
+        std::remove_if(_ctx.wall_texts.begin(), _ctx.wall_texts.end(),
             [](const TextWallPtr& wt) {
                 return wt->role == Wall::Role::Info;
             }),
-        ctx.wall_texts.end());
+        _ctx.wall_texts.end());
 }
 
 
 void
 GameState::step_SettingsApply()
 {
-    if (!level_runner_sys || level_runner_sys->complete()) {
-        word_ai_sys->set_MaxActiveAi(0);
+    if (!_level_runner_sys || _level_runner_sys->complete()) {
+        _word_ai_sys->set_MaxActiveAi(0);
         return;
     }
 
-    const LevelStep& step = level_runner_sys->current();
-    word_ai_sys->set_MaxActiveAi(step.max_ai_words);
+    const LevelStep& step = _level_runner_sys->current();
+    _word_ai_sys->set_MaxActiveAi(step.max_ai_words);
 }
 
 void
@@ -731,12 +731,12 @@ GameState::infowall_StepdetailsShow()
 {
     infowall_Clear();
 
-    if (!level_runner_sys || level_runner_sys->complete()) return;
+    if (!_level_runner_sys || _level_runner_sys->complete()) return;
 
-    const std::string& details = level_runner_sys->current().details;
+    const std::string& details = _level_runner_sys->current().details;
     if (details.empty()) return;
 
-    const float      world_scale    = ctx.world->scale();
+    const float      world_scale    = _ctx.world->scale();
     Wall::Transform  wall_transform = Wall::get_Transform(Wall::Role::Info, world_scale);
 
     constexpr size_t MAX_INFO_LINE_CHARS = 92;
@@ -755,7 +755,7 @@ GameState::infowall_StepdetailsShow()
                                   pos, Text3d::color_white);
         t->yaw = wall_transform.yaw;
 
-        ctx.wall_texts.push_back(cpputils::make_unique<TextWall>(
+        _ctx.wall_texts.push_back(cpputils::make_unique<TextWall>(
             TextWall{ wall_transform.wall_position, Wall::Role::Info, std::move(t) }));
     }
 }
@@ -768,23 +768,23 @@ GameState::scene_Setup(const Scene& scene)
         Text3dPtr t = make_Text3d(pw.text, Text3d::depth_floor, 0.003f,
                                   pw.pos, pw.color);
         t->body.hover_height = pw.pos.y;
-        ctx.words.push_back(std::move(t));
+        _ctx.words.push_back(std::move(t));
     }
 
     for (const PlacedPlate& pp : scene.plates) {
         auto plate = cpputils::make_unique<Plate>(pp.pos);
         Plate* raw = plate.get();
-        step_plates .push_back({ raw, pp.tag });
-        step_scenery.push_back(raw);
-        ctx.scenery .push_back(std::move(plate));
+        _step_plates .push_back({ raw, pp.tag });
+        _step_scenery.push_back(raw);
+        _ctx.scenery .push_back(std::move(plate));
     }
 
     for (const PlacedPlatform& pl : scene.platforms) {
         auto box = cpputils::make_unique<SolidBox>(pl.pos, pl.half_extents,
                                                    pl.fill_color, pl.wire_color);
         GameObject* raw = box.get();
-        step_scenery.push_back(raw);
-        ctx.scenery .push_back(std::move(box));
+        _step_scenery.push_back(raw);
+        _ctx.scenery .push_back(std::move(box));
     }
 
     for (const PlacedAi& a : scene.ai_seeds) {
@@ -811,7 +811,7 @@ GameState::scene_Setup(const Scene& scene)
                 break;
         }
 
-        ctx.words.push_back(std::move(t));
+        _ctx.words.push_back(std::move(t));
     }
 }
 
@@ -819,78 +819,78 @@ GameState::scene_Setup(const Scene& scene)
 void
 GameState::scene_Teardown()
 {
-    if (!step_scenery.empty()) {
-        std::unordered_set<GameObject*> scenery_set(step_scenery.begin(), step_scenery.end());
-        ctx.scenery.erase(
-            std::remove_if(ctx.scenery.begin(), ctx.scenery.end(),
+    if (!_step_scenery.empty()) {
+        std::unordered_set<GameObject*> scenery_set(_step_scenery.begin(), _step_scenery.end());
+        _ctx.scenery.erase(
+            std::remove_if(_ctx.scenery.begin(), _ctx.scenery.end(),
                 [&scenery_set](const GameObjectPtr& sp) {
                     return scenery_set.count(sp.get()) > 0;
                 }),
-            ctx.scenery.end());
+            _ctx.scenery.end());
     }
 
     mod_Clear();
 
-    step_scenery.clear();
-    step_plates.clear();
+    _step_scenery.clear();
+    _step_plates.clear();
 }
 
 
 void
 GameState::substep(double dt)
 {
-    ctx.player->prev_pos = ctx.player->pos;
+    _ctx.player->prev_pos = _ctx.player->pos;
 
     { FP_ZONE("subst.phys_player");
-        physics_sys->update_Player(*ctx.player,
-                                    ctx.player->get_AccX(),
-                                    ctx.player->get_AccZ(),
+        _physics_sys->update_Player(*_ctx.player,
+                                    _ctx.player->get_AccX(),
+                                    _ctx.player->get_AccZ(),
                                     dt);
     }
 
     DynArray<GameObject*> colliders;
     { FP_ZONE("subst.build_colliders");
-        colliders.reserve(ctx.scenery.size() + ctx.words.size());
-        for (const GameObjectPtr& p : ctx.scenery) if (p) colliders.push_back(p.get());
-        for (const Text3dPtr&     p : ctx.words)   if (p) colliders.push_back(p.get());
+        colliders.reserve(_ctx.scenery.size() + _ctx.words.size());
+        for (const GameObjectPtr& p : _ctx.scenery) if (p) colliders.push_back(p.get());
+        for (const Text3dPtr&     p : _ctx.words)   if (p) colliders.push_back(p.get());
     }
 
     { FP_ZONE("subst.phys_objects");
-        physics_sys->update_Objects(colliders, dt, 25.0, ctx.world->scale());
+        _physics_sys->update_Objects(colliders, dt, 25.0, _ctx.world->scale());
     }
 
     { FP_ZONE("subst.word_ai");
         DynArray<Text3dPtr> ai_dropped;
-        word_ai_sys->update(ctx.words, *ctx.player, carried, ai_dropped, dt);
+        _word_ai_sys->update(_ctx.words, *_ctx.player, _carried, ai_dropped, dt);
         for (Text3dPtr& d : ai_dropped) {
-            if (d) ctx.words.push_back(std::move(d));
+            if (d) _ctx.words.push_back(std::move(d));
         }
     }
 
     { FP_ZONE("subst.phys_projectiles");
-        physics_sys->update_Projectiles(projectiles, dt);
+        _physics_sys->update_Projectiles(_projectiles, dt);
     }
 
     DynArray<ProjectileContactEvent> projectile_contacts;
 
-    DynArray<TextLabel*> all_labels = pult_sys->labels();
+    DynArray<TextLabel*> all_labels = _pult_sys->labels();
     { FP_ZONE("subst.hint_and_labels");
-        hint_sys->update(dt, *ctx.camera);
+        _hint_sys->update(dt, *_ctx.camera);
         for (TextLabel* lbl : all_labels)
-            lbl->update(dt, *ctx.camera);
+            lbl->update(dt, *_ctx.camera);
 
-        all_labels.push_back(&hint_sys->get_Label());
+        all_labels.push_back(&_hint_sys->get_Label());
     }
 
     { FP_ZONE("subst.resolve_projectiles");
-        physics_sys->resolve_Projectiles(projectiles, &colliders, &all_labels, &projectile_contacts);
+        _physics_sys->resolve_Projectiles(_projectiles, &colliders, &all_labels, &projectile_contacts);
     }
 
     DynArray<WallTextHitTest> wall_text_hits;
     { FP_ZONE("subst.build_wall_hits");
-        wall_text_hits.reserve(ctx.wall_texts.size());
-        for (size_t i = 0; i < ctx.wall_texts.size(); ++i) {
-            const TextWallPtr& wt = ctx.wall_texts[i];
+        wall_text_hits.reserve(_ctx.wall_texts.size());
+        for (size_t i = 0; i < _ctx.wall_texts.size(); ++i) {
+            const TextWallPtr& wt = _ctx.wall_texts[i];
             if (!wt || !wt->text3d) continue;
             wall_text_hits.push_back({
                     wt->wall_position,
@@ -903,8 +903,8 @@ GameState::substep(double dt)
 
     DynArray<ProjectileResult> proj_results;
     { FP_ZONE("subst.projectile_sys");
-        projectiles_sys->update(projectiles, proj_results,
-                                wall_text_hits, dt, ctx.world->scale());
+        _projectiles_sys->update(_projectiles, proj_results,
+                                wall_text_hits, dt, _ctx.world->scale());
     }
 
     DynArray<size_t> wall_text_to_erase;
@@ -922,20 +922,20 @@ GameState::substep(double dt)
 
         std::sort(wall_text_to_erase.begin(), wall_text_to_erase.end(), std::greater<size_t>{});
         for (size_t idx : wall_text_to_erase) {
-            if (idx < ctx.wall_texts.size())
-                ctx.wall_texts.erase(ctx.wall_texts.begin() + static_cast<std::ptrdiff_t>(idx));
+            if (idx < _ctx.wall_texts.size())
+                _ctx.wall_texts.erase(_ctx.wall_texts.begin() + static_cast<std::ptrdiff_t>(idx));
         }
 
         for (const ProjectileContactEvent& ev : projectile_contacts) {
             if (std::optional<PultAction> act =
-                    pult_sys->handle_ProjectileContact(ev, ctx.buttons); act) {
+                    _pult_sys->handle_ProjectileContact(ev, _ctx.buttons); act) {
                 pult_DispatchAction(*act);
                 continue;
             }
 
             if (ev.other && ev.other->type == ObjectType::Text) {
                 Text3d* word = static_cast<Text3d*>(ev.other);
-                step_ledger.ai_defeated.push_back(word->text);
+                _step_ledger.ai_defeated.push_back(word->text);
 
                 word->ai_state       = Text3d::Ai::Stunned;
                 word->ai_state_timer = 0.0;
@@ -946,10 +946,10 @@ GameState::substep(double dt)
     }
 
     { FP_ZONE("subst.player_collision");
-        PlayerCollision col = ctx.world->resolvePlayer(*ctx.player);
-        ctx.player->apply_Collision(col);
+        PlayerCollision col = _ctx.world->resolvePlayer(*_ctx.player);
+        _ctx.player->apply_Collision(col);
 
-        physics_sys->resolve_PlayerVsObjects(*ctx.player, carried, colliders);
+        _physics_sys->resolve_PlayerVsObjects(*_ctx.player, _carried, colliders);
     }
 }
 
@@ -959,19 +959,19 @@ GameState::goal_ContextBuild() const
 {
     GoalContext gctx;
 
-    for (const StepPlate& sp : step_plates)
+    for (const StepPlate& sp : _step_plates)
         if (sp.plate && sp.plate->active)
             gctx.active_plate_tags.push_back(sp.tag);
 
-    gctx.buttons_pressed_this_step   = step_ledger.buttons_pressed;
-    gctx.dropped_this_step           = step_ledger.dropped;
-    gctx.shot_this_step              = step_ledger.shot;
-    gctx.compile_ok_this_step        = step_ledger.compile_ok;
-    gctx.ai_words_defeated_this_step = step_ledger.ai_defeated;
-    gctx.picked_up_this_step         = step_ledger.picked_up;
+    gctx.buttons_pressed_this_step   = _step_ledger.buttons_pressed;
+    gctx.dropped_this_step           = _step_ledger.dropped;
+    gctx.shot_this_step              = _step_ledger.shot;
+    gctx.compile_ok_this_step        = _step_ledger.compile_ok;
+    gctx.ai_words_defeated_this_step = _step_ledger.ai_defeated;
+    gctx.picked_up_this_step         = _step_ledger.picked_up;
 
     if (gctx.compile_ok_this_step) {
-        std::string src = TextWall::extract_Text(ctx.wall_texts,
+        std::string src = TextWall::extract_Text(_ctx.wall_texts,
                                                  Wall::Role::Editor);
         gctx.editor_normalized = LevelCheck::normalize(src);
     }
@@ -983,9 +983,9 @@ GameState::goal_ContextBuild() const
 bool
 GameState::level_TryAdvance()
 {
-    if (celebration_sys->active()) {
-        if (celebration_sys->progress() >= 1.0) {
-            celebration_sys->finish(*particle_sys);
+    if (_celebration_sys->active()) {
+        if (_celebration_sys->progress() >= 1.0) {
+            _celebration_sys->finish(*_particle_sys);
             level_Advance();
             return true;
         }
@@ -993,15 +993,15 @@ GameState::level_TryAdvance()
     }
 
     GoalContext gctx = goal_ContextBuild();
-    if (!goal_Satisfied(level_runner_sys->current().goal, gctx))
+    if (!goal_Satisfied(_level_runner_sys->current().goal, gctx))
         return false;
-    if (step_age < SETTLE_TIME_FOR_STEP)
+    if (_step_age < SETTLE_TIME_FOR_STEP)
         return false;
-    if (!level_runner_sys || level_runner_sys->complete())
+    if (!_level_runner_sys || _level_runner_sys->complete())
         return false;
 
     auto [anchor, right, glyph_yaw] = anchorframe_Current();
-    celebration_sys->start(*ctx.camera, glyph_yaw);
+    _celebration_sys->start(*_ctx.camera, glyph_yaw);
     return false;
 }
 
@@ -1010,46 +1010,46 @@ void
 GameState::level_Advance()
 {
     scene_Teardown();
-    step_ledger.clear();
-
-    if (level_runner_sys->advance()) {
-        if (level_runner_sys->current().reset_carry) {
-            ctx.words  .clear();
-            projectiles.clear();
-            anims_add  .clear();
-            anims_drop .clear();
-            carried = CarriedLine();
+    _step_ledger.clear();
+
+    if (_level_runner_sys->advance()) {
+        if (_level_runner_sys->current().reset_carry) {
+            _ctx.words  .clear();
+            _projectiles.clear();
+            _anims_add  .clear();
+            _anims_drop .clear();
+            _carried = CarriedLine();
         }
 
-        DynArray<std::string> tokens = level_runner_sys->grants_for_step();
+        DynArray<std::string> tokens = _level_runner_sys->grants_for_step();
         step_Spawn(tokens);
-        scene_Setup(level_runner_sys->current().scene);
+        scene_Setup(_level_runner_sys->current().scene);
         step_SettingsApply();
         infowall_StepdetailsShow();
-        hint_sys->set_Hint(level_runner_sys->current().hint);
+        _hint_sys->set_Hint(_level_runner_sys->current().hint);
     } else {
-        hint_sys->clear();
+        _hint_sys->clear();
         infowall_StepdetailsShow();
     }
-    step_age = 0.0;
+    _step_age = 0.0;
 }
 
 
 void
 GameState::mod_Clear()
 {
-    if (mod_scenery.empty()) { return; }
+    if (_mod_scenery.empty()) { return; }
 
-    std::unordered_set<GameObject*> set(mod_scenery.begin(), mod_scenery.end());
-    ctx.scenery.erase(
-        std::remove_if(ctx.scenery.begin(), ctx.scenery.end(),
+    std::unordered_set<GameObject*> set(_mod_scenery.begin(), _mod_scenery.end());
+    _ctx.scenery.erase(
+        std::remove_if(_ctx.scenery.begin(), _ctx.scenery.end(),
             [&set](const GameObjectPtr& sp) { return set.count(sp.get()) > 0; }),
-        ctx.scenery.end());
-    ctx.words.erase(
-        std::remove_if(ctx.words.begin(), ctx.words.end(),
+        _ctx.scenery.end());
+    _ctx.words.erase(
+        std::remove_if(_ctx.words.begin(), _ctx.words.end(),
             [&set](const Text3dPtr& wp) { return set.count(wp.get()) > 0; }),
-        ctx.words.end());
-    mod_scenery.clear();
+        _ctx.words.end());
+    _mod_scenery.clear();
 }
 
 
@@ -1066,21 +1066,21 @@ GameState::mod_Apply(const DynArray<ModObject>& objs)
                 Vec3{ o.hx, o.hy, o.hz },
                 SDL_FColor{ o.r, o.g, o.b, 1.0f });
             box->body.mode = o.dynamic ? PhysicsMode::Dynamic : PhysicsMode::Static;
-            mod_scenery.push_back(box.get());
-            ctx.scenery.push_back(std::move(box));
+            _mod_scenery.push_back(box.get());
+            _ctx.scenery.push_back(std::move(box));
         } else { // MOD_TEXT
             Text3dPtr t = make_Text3d(o.text, Text3d::depth_floor, 0.003f,
                                       Vec3{ o.x, o.y, o.z },
                                       SDL_FColor{ o.r, o.g, o.b, 1.0f });
             t->body.mode = PhysicsMode::Static;   // lib text just sits where placed
-            mod_scenery.push_back(t.get());
-            ctx.words.push_back(std::move(t));
+            _mod_scenery.push_back(t.get());
+            _ctx.words.push_back(std::move(t));
         }
     }
 }
 
 std::string GameState::sandbox_CanonicalPath() const {
-    return level_runner_sys ? level_runner_sys->source_file() : std::string{};
+    return _level_runner_sys ? _level_runner_sys->source_file() : std::string{};
 }
 std::string GameState::sandbox_BackupPath() const {
     std::string c = sandbox_CanonicalPath();
@@ -1101,13 +1101,13 @@ GameState::sandbox_LoadFileToWall()
     }
 
     // Clear any existing editor-wall words first.
-    editor_wall.clear();
-    ctx.wall_texts.erase(
-        std::remove_if(ctx.wall_texts.begin(), ctx.wall_texts.end(),
+    _editor_wall.clear();
+    _ctx.wall_texts.erase(
+        std::remove_if(_ctx.wall_texts.begin(), _ctx.wall_texts.end(),
             [](const TextWallPtr& wt){ return wt->role == Wall::Role::Editor; }),
-        ctx.wall_texts.end());
+        _ctx.wall_texts.end());
 
-    const float ws = ctx.world->scale();
+    const float ws = _ctx.world->scale();
     const double x_wall = -static_cast<double>(ws) + 0.05;   // NegX wall surface
     Wall::Transform tf = Wall::get_Transform(Wall::Role::Editor, ws);
 
@@ -1125,10 +1125,10 @@ GameState::sandbox_LoadFileToWall()
             t->yaw = tf.yaw;
 
             // Snap into the editor wall's line tracking, like a thrown word.
-            Vec3 snapped = editor_wall.on_WordPlaced(t.get(), Vec3{ x_wall, y, z });
+            Vec3 snapped = _editor_wall.on_WordPlaced(t.get(), Vec3{ x_wall, y, z });
             t->pos = snapped;
 
-            ctx.wall_texts.push_back(cpputils::make_unique<TextWall>(TextWall{
+            _ctx.wall_texts.push_back(cpputils::make_unique<TextWall>(TextWall{
                 Wall::Position::NegX, Wall::Role::Editor, std::move(t)
             }));
 
@@ -1142,7 +1142,7 @@ GameState::sandbox_LoadFileToWall()
 
 
 void GameState::sandbox_Restore() {
-    if (!level_runner_sys || !level_runner_sys->dynamic_lib()) return;
+    if (!_level_runner_sys || !_level_runner_sys->dynamic_lib()) return;
     if (SourceTokens::copy_File(sandbox_BackupPath(), sandbox_CanonicalPath()))
         sandbox_LoadFileToWall();
 }
@@ -1162,8 +1162,8 @@ GameState::typing_RebuildPreview(const std::string& s)
                                   anchor, Text3d::color_floor);
         t->yaw = static_cast<float>(glyph_yaw);
         t->body.mode = PhysicsMode::Carried;
-        typing_words.push_back(t.get());   // track raw ptr for later removal
-        carried.push(std::move(t));
+        _typing_words.push_back(t.get());   // track raw ptr for later removal
+        _carried.push(std::move(t));
     }
 }
 
@@ -1173,11 +1173,11 @@ GameState::typing_ClearPreview()
 {
     // Preview words are the most recently pushed → at the tail. Pop exactly
     // as many as we added; let them be destroyed (don't re-add to scene).
-    for (size_t i = 0; i < typing_words.size(); ++i) {
-        if (carried.empty()) break;
-        carried.pop_back();   // returns Text3dPtr; dropping it frees the word
+    for (size_t i = 0; i < _typing_words.size(); ++i) {
+        if (_carried.empty()) break;
+        _carried.pop_back();   // returns Text3dPtr; dropping it frees the word
     }
-    typing_words.clear();
+    _typing_words.clear();
 }
 
 
@@ -1199,6 +1199,6 @@ GameState::carry_BeginAnim(Text3dPtr word, bool with_pitch)
     anim.target_pos  = anchor;
     anim.yaw_target  = static_cast<float>(glyph_yaw);
 
-    anims_add.push_back(std::move(anim));
+    _anims_add.push_back(std::move(anim));
 }
 
diff --git a/src/states/game_state.hpp b/src/states/game_state.hpp
index 7fb2fe0..3c8ffe5 100644
--- a/src/states/game_state.hpp
+++ b/src/states/game_state.hpp
@@ -58,45 +58,44 @@ struct StepPlate {
 
 class GameState : public IState
 {
-    GameContext ctx;
-    FrameInput  frame_input;
-    CarriedLine carried;
-    EditorWall  editor_wall;
-
-    StepLedger               step_ledger;
-
-    double pick_cooldown  { 0.0 };
-    double shoot_cooldown { 0.0 };
-    double step_age       { 0.0 };
-    double accumulator    { 0.0 };
-    double alpha          { 0.0 };
-
-    DynArray<Text3d*> typing_words;
-    std::string       typing_last;
-
-    DynArray<Projectile>    projectiles;
-    DynArray<WordAnim_Add>  anims_add;
-    DynArray<WordAnim_Drop> anims_drop;
-
-    DynArray<GameObject*> mod_scenery;
-    DynArray<GameObject*> step_scenery;
-    DynArray<StepPlate>   step_plates;
-
-    cpputils::unique_ptr<InputSystem>      input_sys;
-    cpputils::unique_ptr<PhysicsSystem>    physics_sys;
-    cpputils::unique_ptr<ProjectileSystem> projectiles_sys;
-    cpputils::unique_ptr<WordAnimSystem>   word_anim_sys;
-    cpputils::unique_ptr<CompilerSystem>   compiler_sys;
-    cpputils::unique_ptr<FreelookSystem>   freelook_sys;
-    cpputils::unique_ptr<PultSystem>       pult_sys;
-    cpputils::unique_ptr<HintSystem>       hint_sys;
-    cpputils::unique_ptr<WordAiSystem>     word_ai_sys;
-    cpputils::unique_ptr<PlateSystem>      plate_sys;
-    cpputils::unique_ptr<ParticleSystem>   particle_sys;
-    cpputils::unique_ptr<FloorMandelbrot>  floor_sys;
-    cpputils::unique_ptr<LevelRunner>      level_runner_sys;
-    cpputils::unique_ptr<ModLoader>        mod_loader_sys;
-    cpputils::unique_ptr<CelebrationSystem>celebration_sys;
+    GameContext _ctx;
+    FrameInput  _frame_input;
+    CarriedLine _carried;
+    EditorWall  _editor_wall;
+    StepLedger  _step_ledger;
+
+    double _pick_cooldown  { 0.0 };
+    double _shoot_cooldown { 0.0 };
+    double _step_age       { 0.0 };
+    double _accumulator    { 0.0 };
+    double _alpha          { 0.0 };
+
+    DynArray<Text3d*> _typing_words;
+    std::string       _typing_last;
+
+    DynArray<Projectile>    _projectiles;
+    DynArray<WordAnim_Add>  _anims_add;
+    DynArray<WordAnim_Drop> _anims_drop;
+
+    DynArray<GameObject*> _mod_scenery;
+    DynArray<GameObject*> _step_scenery;
+    DynArray<StepPlate>   _step_plates;
+
+    cpputils::unique_ptr<InputSystem>      _input_sys;
+    cpputils::unique_ptr<PhysicsSystem>    _physics_sys;
+    cpputils::unique_ptr<ProjectileSystem> _projectiles_sys;
+    cpputils::unique_ptr<WordAnimSystem>   _word_anim_sys;
+    cpputils::unique_ptr<CompilerSystem>   _compiler_sys;
+    cpputils::unique_ptr<FreelookSystem>   _freelook_sys;
+    cpputils::unique_ptr<PultSystem>       _pult_sys;
+    cpputils::unique_ptr<HintSystem>       _hint_sys;
+    cpputils::unique_ptr<WordAiSystem>     _word_ai_sys;
+    cpputils::unique_ptr<PlateSystem>      _plate_sys;
+    cpputils::unique_ptr<ParticleSystem>   _particle_sys;
+    cpputils::unique_ptr<FloorMandelbrot>  _floor_sys;
+    cpputils::unique_ptr<LevelRunner>      _level_runner_sys;
+    cpputils::unique_ptr<ModLoader>        _mod_loader_sys;
+    cpputils::unique_ptr<CelebrationSystem>_celebration_sys;
 
 
     bool on_Pick         ();
diff --git a/src/states/menu_state.cpp b/src/states/menu_state.cpp
index 007edc1..127a956 100644
--- a/src/states/menu_state.cpp
+++ b/src/states/menu_state.cpp
@@ -12,9 +12,9 @@
 // ─────────────────────────────────────────────────────────────────────────
 // Layout: three columns at -90° / 0° / +90°. Left holds "Open File", right
 // holds "Quit", middle holds the level list — Tutorial at eye level, every
-// further level stacked downward. Camera stands at world origin.
+// further level stacked downward. Camera stands at _world origin.
 //
-// W/S scroll the level list (the list moves; the camera does not). At
+// W/S scroll the level list (the list moves; the _camera does not). At
 // scroll = 0 the first level sits at eye level. At scroll = N the (N+1)th
 // level does.
 // ─────────────────────────────────────────────────────────────────────────
@@ -70,19 +70,19 @@ slot_PosForColumn(double column_angle_rad, double y)
 
 MenuState::MenuState()
 {
-    world  = cpputils::make_unique<World>();
-    camera = cpputils::make_unique<Camera>();
-    light  = cpputils::make_unique<Light>();
+    _world  = cpputils::make_unique<World>();
+    _camera = cpputils::make_unique<Camera>();
+    _light  = cpputils::make_unique<Light>();
 
-    camera->pos   = { 0.0, EYE_Y, 0.0 };
-    camera->yaw   = 0;
-    camera->pitch = 0;
+    _camera->pos   = { 0.0, EYE_Y, 0.0 };
+    _camera->yaw   = 0;
+    _camera->pitch = 0;
 
-    title = make_Text3d("Runtime Compiler",
+    _title = make_Text3d("Runtime Compiler",
                         TEXT_DEPTH, TITLE_SCALE,
                         slot_PosForColumn(COLUMN_MID_RAD, TITLE_Y),
                         COLOR_TITLE);
-    title->body.mode = PhysicsMode::Static;
+    _title->body.mode = PhysicsMode::Static;
 
     build_Items();
 }
@@ -91,14 +91,14 @@ MenuState::MenuState()
 bool
 MenuState::is_LevelItem(size_t idx) const
 {
-    return idx < items.size() && items[idx].kind == ItemKind::StartLevel;
+    return idx < _items.size() && _items[idx].kind == ItemKind::StartLevel;
 }
 
 
 void
 MenuState::build_Items()
 {
-    items.clear();
+    _items.clear();
 
     // ── Left column: Open File ───────────────────────────────────────────
     {
@@ -106,12 +106,12 @@ MenuState::build_Items()
         mi.label    = "Open File";
         mi.kind     = ItemKind::OpenFile;
         mi.slot_pos = slot_PosForColumn(COLUMN_LEFT_RAD, EYE_Y);
-        items.push_back(std::move(mi));
+        _items.push_back(std::move(mi));
     }
 
     // ── Middle column: levels, stacked top-to-bottom ─────────────────────
     // We DON'T set slot_pos.y here; vertical position is recomputed each
-    // frame in update() based on scroll_current. The X/Z components (the
+    // frame in update() based on _scroll_current. The X/Z components (the
     // middle column) are fixed.
     for (size_t i = 0; i < level_Count(); ++i) {
         MenuItem mi;
@@ -119,7 +119,7 @@ MenuState::build_Items()
         mi.kind        = ItemKind::StartLevel;
         mi.level_index = i;
         mi.slot_pos    = slot_PosForColumn(COLUMN_MID_RAD, EYE_Y);   // y replaced each frame
-        items.push_back(std::move(mi));
+        _items.push_back(std::move(mi));
     }
 
     // ── Right column: Quit ───────────────────────────────────────────────
@@ -128,17 +128,17 @@ MenuState::build_Items()
         mi.label    = "Quit";
         mi.kind     = ItemKind::Quit;
         mi.slot_pos = slot_PosForColumn(COLUMN_RIGHT_RAD, EYE_Y);
-        items.push_back(std::move(mi));
+        _items.push_back(std::move(mi));
     }
 
     // Build Text3d objects.
-    for (size_t i = 0; i < items.size(); ++i) {
-        const auto color = (i == selected) ? COLOR_SELECTED : COLOR_NORMAL;
-        items[i].text3d = make_Text3d(items[i].label,
+    for (size_t i = 0; i < _items.size(); ++i) {
+        const auto color = (i == _selected) ? COLOR_SELECTED : COLOR_NORMAL;
+        _items[i].text3d = make_Text3d(_items[i].label,
                                       TEXT_DEPTH, ITEM_SCALE,
-                                      items[i].slot_pos,
+                                      _items[i].slot_pos,
                                       color);
-        items[i].text3d->body.mode = PhysicsMode::Static;
+        _items[i].text3d->body.mode = PhysicsMode::Static;
     }
 }
 
@@ -151,16 +151,16 @@ MenuState::handle_Event(Game& game, const SDL_Event& e)
         const double dx_rad = static_cast<double>(e.motion.xrel) * MOUSE_SENSITIVITY;
         const double dy_rad = static_cast<double>(e.motion.yrel) * MOUSE_SENSITIVITY;
 
-        const double cur_yaw = yaw_ToRadians(camera->yaw);
-        camera->yaw = yaw_FromRadians(cur_yaw - dx_rad);
+        const double cur_yaw = yaw_ToRadians(_camera->yaw);
+        _camera->yaw = yaw_FromRadians(cur_yaw - dx_rad);
 
         constexpr int32_t PITCH_MAX = 0x40000000;
         const int32_t dpitch = static_cast<int32_t>(
             dy_rad * (2147483648.0 / SDL_PI_D));
-        int32_t p = camera->pitch - dpitch;
+        int32_t p = _camera->pitch - dpitch;
         if (p >  PITCH_MAX) p =  PITCH_MAX;
         if (p < -PITCH_MAX) p = -PITCH_MAX;
-        camera->pitch = p;
+        _camera->pitch = p;
         return;
     }
 
@@ -170,7 +170,7 @@ MenuState::handle_Event(Game& game, const SDL_Event& e)
     {
         const size_t hit = pick_FromCrosshair();
         if (hit != static_cast<size_t>(-1)) {
-            selected = hit;
+            _selected = hit;
             activate(game);
         }
         return;
@@ -184,7 +184,7 @@ MenuState::handle_Event(Game& game, const SDL_Event& e)
             // Only scroll the level list when the player is looking at a
             // level. Other columns ignore W/S.
             if (is_LevelItem(pick_FromCrosshair())) {
-                if (scroll_target > 0.0) scroll_target -= 1.0;
+                if (_scroll_target > 0.0) _scroll_target -= 1.0;
             }
             break;
 
@@ -193,7 +193,7 @@ MenuState::handle_Event(Game& game, const SDL_Event& e)
             if (is_LevelItem(pick_FromCrosshair())) {
                 const double max_scroll =
                     static_cast<double>(level_Count() > 0 ? level_Count() - 1 : 0);
-                if (scroll_target < max_scroll) scroll_target += 1.0;
+                if (_scroll_target < max_scroll) _scroll_target += 1.0;
             }
             break;
 
@@ -201,7 +201,7 @@ MenuState::handle_Event(Game& game, const SDL_Event& e)
         case SDLK_SPACE: {
             const size_t hit = pick_FromCrosshair();
             if (hit != static_cast<size_t>(-1)) {
-                selected = hit;
+                _selected = hit;
                 activate(game);
             }
             break;
@@ -220,13 +220,13 @@ MenuState::handle_Event(Game& game, const SDL_Event& e)
 void
 MenuState::activate(Game& game)
 {
-    if (selected >= items.size()) return;
+    if (_selected >= _items.size()) return;
 
-    const MenuItem& mi = items[selected];
+    const MenuItem& mi = _items[_selected];
 
     switch (mi.kind) {
         case ItemKind::StartLevel:
-            game.changeState(cpputils::make_unique<GameState>(mi.level_index));
+            game.change_State(cpputils::make_unique<GameState>(mi.level_index));
             break;
 
         case ItemKind::OpenFile:
@@ -246,8 +246,8 @@ MenuState::activate(Game& game)
 size_t
 MenuState::pick_FromCrosshair() const
 {
-    const double yaw   = yaw_ToRadians(camera->yaw);
-    const double pitch = static_cast<double>(camera->pitch) * BINRAD;
+    const double yaw   = yaw_ToRadians(_camera->yaw);
+    const double pitch = static_cast<double>(_camera->pitch) * BINRAD;
     const double cy = SDL_cos(yaw),  sy = SDL_sin(yaw);
     const double cp = SDL_cos(pitch), sp = SDL_sin(pitch);
 
@@ -257,16 +257,16 @@ MenuState::pick_FromCrosshair() const
     size_t best   = static_cast<size_t>(-1);
     double best_t = 1e9;
 
-    for (size_t i = 0; i < items.size(); ++i) {
-        if (!items[i].text3d || !items[i].text3d->loaded) continue;
+    for (size_t i = 0; i < _items.size(); ++i) {
+        if (!_items[i].text3d || !_items[i].text3d->loaded) continue;
 
-        const Vec3 mn = items[i].text3d->aabb_min();
-        const Vec3 mx = items[i].text3d->aabb_max();
+        const Vec3 mn = _items[i].text3d->aabb_min();
+        const Vec3 mx = _items[i].text3d->aabb_max();
 
         double tmin = 0.0, tmax = 1e9;
-        const double ox = camera->pos.x;
-        const double oy = camera->pos.y;
-        const double oz = camera->pos.z;
+        const double ox = _camera->pos.x;
+        const double oy = _camera->pos.y;
+        const double oz = _camera->pos.z;
 
         auto slab = [&](double o, double d, double lo, double hi) -> bool {
             if (SDL_fabs(d) < 1e-9) return o >= lo && o <= hi;
@@ -296,32 +296,32 @@ MenuState::pick_FromCrosshair() const
 void
 MenuState::update(double dt)
 {
-    t += dt;
+    _t += dt;
 
     // ── Scroll lerp ──────────────────────────────────────────────────────
     {
         const double k = 1.0 - SDL_exp(-SCROLL_LERP_RATE * dt);
-        scroll_current += (scroll_target - scroll_current) * k;
+        _scroll_current += (_scroll_target - _scroll_current) * k;
     }
 
-    // ── Reposition level items based on scroll ───────────────────────────
-    // For each level i: y = EYE_Y - (i - scroll_current) * SPACING.
-    // At scroll_current = 0, i=0 sits at EYE_Y, i=1 below it, etc.
-    // As scroll_current grows, the whole list slides upward, so further
+    // ── Reposition level _items based on scroll ───────────────────────────
+    // For each level i: y = EYE_Y - (i - _scroll_current) * SPACING.
+    // At _scroll_current = 0, i=0 sits at EYE_Y, i=1 below it, etc.
+    // As _scroll_current grows, the whole list slides upward, so further
     // levels reach eye level.
     {
         size_t level_n = 0;
-        for (MenuItem& mi : items) {
+        for (MenuItem& mi : _items) {
             if (mi.kind != ItemKind::StartLevel) continue;
-            const double y = EYE_Y - (static_cast<double>(level_n) - scroll_current)
+            const double y = EYE_Y - (static_cast<double>(level_n) - _scroll_current)
                                      * LEVEL_SPACING;
             mi.text3d->pos = { mi.slot_pos.x, y, mi.slot_pos.z };
             ++level_n;
         }
     }
 
-    // ── Non-level items stay at their fixed slot positions ───────────────
-    for (MenuItem& mi : items) {
+    // ── Non-level _items stay at their fixed slot positions ───────────────
+    for (MenuItem& mi : _items) {
         if (mi.kind == ItemKind::StartLevel) continue;
         mi.text3d->pos = mi.slot_pos;
     }
@@ -329,30 +329,30 @@ MenuState::update(double dt)
     // ── Crosshair-driven selection (mouse hover) ─────────────────────────
     {
         const size_t hover = pick_FromCrosshair();
-        if (hover != static_cast<size_t>(-1) && hover != selected) {
-            items[selected].text3d->set_Color(COLOR_NORMAL);
-            items[hover].text3d->set_Color(COLOR_SELECTED);
-            selected = hover;
+        if (hover != static_cast<size_t>(-1) && hover != _selected) {
+            _items[_selected].text3d->set_Color(COLOR_NORMAL);
+            _items[hover].text3d->set_Color(COLOR_SELECTED);
+            _selected = hover;
         }
     }
 
-    // ── Billboard: every item faces the camera ───────────────────────────
-    for (MenuItem& mi : items) {
+    // ── Billboard: every item faces the _camera ───────────────────────────
+    for (MenuItem& mi : _items) {
         if (!mi.text3d) continue;
-        const double dx = camera->pos.x - mi.text3d->pos.x;
-        const double dz = camera->pos.z - mi.text3d->pos.z;
+        const double dx = _camera->pos.x - mi.text3d->pos.x;
+        const double dz = _camera->pos.z - mi.text3d->pos.z;
         mi.text3d->yaw = static_cast<float>(SDL_atan2(dx, dz));
     }
-    if (title) {
-        const double dx = camera->pos.x - title->pos.x;
-        const double dz = camera->pos.z - title->pos.z;
-        title->yaw = static_cast<float>(SDL_atan2(dx, dz));
+    if (_title) {
+        const double dx = _camera->pos.x - _title->pos.x;
+        const double dz = _camera->pos.z - _title->pos.z;
+        _title->yaw = static_cast<float>(SDL_atan2(dx, dz));
     }
 
-    // ── Bob the selected item ────────────────────────────────────────────
-    if (selected < items.size() && items[selected].text3d) {
-        const double bob = SDL_sin(t * 3.0) * 0.10;
-        items[selected].text3d->pos.y += bob;
+    // ── Bob the _selected item ────────────────────────────────────────────
+    if (_selected < _items.size() && _items[_selected].text3d) {
+        const double bob = SDL_sin(_t * 3.0) * 0.10;
+        _items[_selected].text3d->pos.y += bob;
     }
 }
 
@@ -363,25 +363,25 @@ MenuState::render(SDL_Window& window, GlRenderer& renderer)
     int w, h;
     SDL_GetWindowSizeInPixels(&window, &w, &h);
 
-    // Update flashlight: pos = camera, dir = where the camera is looking.
+    // Update flashlight: pos = _camera, dir = where the _camera is looking.
     {
-        const double yaw_r   = BINRAD * static_cast<double>(camera->yaw);
-        const double pitch_r = BINRAD * static_cast<double>(camera->pitch);
+        const double yaw_r   = BINRAD * static_cast<double>(_camera->yaw);
+        const double pitch_r = BINRAD * static_cast<double>(_camera->pitch);
         const double cy = SDL_cos(yaw_r),   sy = SDL_sin(yaw_r);
         const double cp = SDL_cos(pitch_r), sp = SDL_sin(pitch_r);
 
-        light->flashlight_pos = camera->pos;
-        light->flashlight_dir = Vec3{ -sy * cp, sp, -cy * cp };
+        _light->flashlight_pos = _camera->pos;
+        _light->flashlight_dir = Vec3{ -sy * cp, sp, -cy * cp };
     }
 
     renderer.begin_Frame(w, h);
-    renderer.set_Light(*light);
+    renderer.set_Light(*_light);
 
-    world->draw(renderer, *camera, *light);
+    _world->draw(renderer, *_camera, *_light);
 
-    if (title) title->draw(renderer, *camera, *light);
-    for (const MenuItem& mi : items)
-        if (mi.text3d) mi.text3d->draw(renderer, *camera, *light);
+    if (_title) _title->draw(renderer, *_camera, *_light);
+    for (const MenuItem& mi : _items)
+        if (mi.text3d) mi.text3d->draw(renderer, *_camera, *_light);
 
     renderer.end_Frame(&window);
 }
diff --git a/src/states/menu_state.hpp b/src/states/menu_state.hpp
index c536606..f789845 100644
--- a/src/states/menu_state.hpp
+++ b/src/states/menu_state.hpp
@@ -13,14 +13,6 @@
 
 class MenuState : public IState
 {
-public:
-    MenuState();
-
-    void handle_Event(Game&, const SDL_Event&) override;
-    void update(double dt)                     override;
-    void render(SDL_Window&, GlRenderer&)      override;
-
-private:
     enum class ItemKind : uint8_t {
         OpenFile,
         StartLevel,
@@ -35,28 +27,30 @@ private:
         Vec3        slot_pos {};       // base position in the column (no scroll/bob)
     };
 
-    void   build_Items();
-    void   activate(Game& game);
-    size_t pick_FromCrosshair() const;
-    bool   is_LevelItem(size_t idx) const;
+    cpputils::unique_ptr<World>  _world;
+    cpputils::unique_ptr<Camera> _camera;
+    cpputils::unique_ptr<Light>  _light;
 
-    static uint32_t yaw_FromRadians(double rad);
-    static double   yaw_ToRadians(uint32_t yaw);
+    Text3dPtr          _title;
+    DynArray<MenuItem> _items;
+    size_t             _selected{0};
 
-    cpputils::unique_ptr<World>  world;
-    cpputils::unique_ptr<Camera> camera;
-    cpputils::unique_ptr<Light>  light;
+    double _scroll_current{0.0};
+    double _scroll_target {0.0};
 
-    Text3dPtr             title;
-    DynArray<MenuItem> items;
-    size_t                selected{0};
+    double _t{0.0};
 
-    // Vertical scroll of the level list.
-    // 0 = Tutorial (first level) at eye level.
-    // i = level i at eye level.
-    // Camera doesn't move; the items reposition themselves each frame.
-    double scroll_current{0.0};
-    double scroll_target {0.0};
+    void   build_Items  ();
+    void   activate     (Game& game);
+    size_t pick_FromCrosshair() const;
+    bool   is_LevelItem (size_t idx) const;
+
+    static uint32_t yaw_FromRadians(double rad);
+    static double   yaw_ToRadians  (uint32_t yaw);
+public:
+    MenuState();
 
-    double t{0.0};
+    void handle_Event(Game&, const SDL_Event&) override;
+    void update(double dt)                     override;
+    void render(SDL_Window&, GlRenderer&)      override;
 };
diff --git a/src/systems/celebration_system.cpp b/src/systems/celebration_system.cpp
index 080f93e..2066b8b 100644
--- a/src/systems/celebration_system.cpp
+++ b/src/systems/celebration_system.cpp
@@ -5,9 +5,9 @@
 void
 CelebrationSystem::start(const Camera& cam, double& glyph_yaw)
 {
-    active_     = true;
-    t_          = 0.0;
-    next_burst_ = 0.0;
+    _active     = true;
+    _t          = 0.0;
+    _next_burst = 0.0;
 
     Vec3 banner_pos {
         cam.pos.x + cam.fwd.x * 10.0,
@@ -18,30 +18,30 @@ CelebrationSystem::start(const Camera& cam, double& glyph_yaw)
     if (banner_pos.y < static_cast<double>(-WORLD_BOX_SCALE+1.0f) )
         banner_pos.y = -WORLD_BOX_SCALE+1.0f;
 
-    banner_ = make_Text3d("Success",
+    _banner = make_Text3d("Success",
                          Text3d::depth_floor * 2.0f,
                          0.012f, banner_pos,
                          Text3d::color_white);
-    banner_->yaw       = static_cast<float>(glyph_yaw);
-    banner_->body.mode = PhysicsMode::Static;
+    _banner->yaw       = static_cast<float>(glyph_yaw);
+    _banner->body.mode = PhysicsMode::Static;
 }
 
 
 void
 CelebrationSystem::tick(double dt, ParticleSystem& particle_sys)
 {
-    if (!active_) return;
+    if (!_active) return;
 
-    t_ += dt;
+    _t += dt;
 
     // Slow banner spin
-    if (banner_)
-        banner_->yaw += static_cast<float>(dt * 0.6);
+    if (_banner)
+        _banner->yaw += static_cast<float>(dt * 0.6);
 
     // Fireworks bursts on a schedule. We aim for ~7 bursts across the
     // duration, with slight jitter to avoid mechanical pacing.
-    if (t_ >= next_burst_ &&
-        t_ <  duration_ - 0.3)
+    if (_t >= _next_burst &&
+        _t <  _duration - 0.3)
     {
         // Random position across the play area, biased to upper half so
         // bursts read as airborne fireworks not floor_sys explosions.
@@ -74,7 +74,7 @@ CelebrationSystem::tick(double dt, ParticleSystem& particle_sys)
         particle_sys.emit_FireworkBurst(center, SDL_FColor{r, g, b, 1.0f}, 80);
 
         // Next burst in 0.5..1.0s.
-        next_burst_ = t_ + 0.5 + rnd() * 0.5;
+        _next_burst = _t + 0.5 + rnd() * 0.5;
     }
 }
 
@@ -82,8 +82,8 @@ CelebrationSystem::tick(double dt, ParticleSystem& particle_sys)
 void
 CelebrationSystem::finish(ParticleSystem& particle_sys)
 {
-    active_ = false;
-    banner_.reset();
+    _active = false;
+    _banner.reset();
     particle_sys.clear();
 }
 
@@ -91,20 +91,20 @@ CelebrationSystem::finish(ParticleSystem& particle_sys)
 bool
 CelebrationSystem::active() const
 {
-    return active_;
+    return _active;
 }
 
 
 double
 CelebrationSystem::progress() const
 {
-    return (duration_ > 0.0) ? (t_ / duration_) : 1.0;
+    return (_duration > 0.0) ? (_t / _duration) : 1.0;
 }
 
 
 void
 CelebrationSystem::draw(GlRenderer& gl, const Camera& cam, const Light& light) const
 {
-    if (!active_ || !banner_) return;
-    banner_->draw(gl, cam, light);
+    if (!_active || !_banner) return;
+    _banner->draw(gl, cam, light);
 }
diff --git a/src/systems/celebration_system.hpp b/src/systems/celebration_system.hpp
index 36dbe45..244de1c 100644
--- a/src/systems/celebration_system.hpp
+++ b/src/systems/celebration_system.hpp
@@ -9,11 +9,11 @@ class  GlRenderer;
 
 
 class CelebrationSystem {
-    bool   active_     = false;
-    double t_          = 0,
-           duration_   = 3.5,
-           next_burst_ = 0;
-    Text3dPtr banner_;
+    bool      _active     = false;
+    double    _t          = 0,
+              _duration   = 3.5,
+              _next_burst = 0;
+    Text3dPtr _banner;
 public:
     void   start(const Camera& cam, double& glyph_yaw);
     void   tick(double dt, ParticleSystem& particle_sys);
diff --git a/src/systems/compiler_system.cpp b/src/systems/compiler_system.cpp
index a0375fe..68ee01b 100644
--- a/src/systems/compiler_system.cpp
+++ b/src/systems/compiler_system.cpp
@@ -14,41 +14,46 @@ CompilerSystem::compile(TextWallList& texts, const std::string& src)
     // Clear previous output from PosX wall
     texts.erase(
         std::remove_if(texts.begin(), texts.end(),
-            [](const TextWallPtr& wt) { return wt->wall_position == Wall::position_of(Wall::Role::Compiler); }),
+            [](const TextWallPtr& wt) {
+                return wt->wall_position == Wall::position_of(Wall::Role::Compiler);
+            }),
         texts.end());
-    count = 0;
+    _count = 0;
 
     // Write source to temp file
     FILE* f = fopen("/tmp/game_prog.c", "w");
-    if (!f) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "CompilerSystem: could not write temp file"); return; }
+    if (!f) {
+        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "CompilerSystem: could not write temp file");
+        return;
+    }
     fputs(src.c_str(), f);
     fclose(f);
 
-    // Open gcc pipe — stderr redirected to stdout so we get errors too
-    if (pipe) { pclose(pipe); pipe = nullptr; }
-    pipe = popen("LC_ALL=C gcc /tmp/game_prog.c -o /tmp/game_prog 2>&1", "r");
-    if (!pipe) SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "CompilerSystem: popen failed");
+    // Open gcc _pipe — stderr redirected to stdout so we get errors too
+    if (_pipe) { pclose(_pipe); _pipe = nullptr; }
+    _pipe = popen("LC_ALL=C gcc /tmp/game_prog.c -o /tmp/game_prog 2>&1", "r");
+    if (!_pipe) SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "CompilerSystem: popen failed");
 }
 
 
 void
 CompilerSystem::update(TextWallList& texts, float world_scale)
 {
-    if (!pipe) return;
+    if (!_pipe) return;
 
     char buf[512];
     // Non-blocking: try to read a line, but don't stall the frame
     // popen is blocking by default so we set fd non-blocking
-    int fd = fileno(pipe);
+    int fd = fileno(_pipe);
     fd_set set; FD_ZERO(&set); FD_SET(fd, &set);
     struct timeval tv{0, 0};  // zero timeout = poll
 
     while (select(fd+1, &set, nullptr, nullptr, &tv) > 0) {
-        if (!fgets(buf, sizeof(buf), pipe)) {
+        if (!fgets(buf, sizeof(buf), _pipe)) {
             // EOF — compile finished
-            pclose(pipe);
-            pipe = nullptr;
-            if (count == 0)
+            pclose(_pipe);
+            _pipe = nullptr;
+            if (_count == 0)
                 push_Line(texts, "OK", world_scale);
             return;
         }
@@ -89,5 +94,5 @@ CompilerSystem::push_Line(TextWallList& texts, const std::string& line, float wo
     });
 
     texts.push_back(std::move(wt));
-    count++;
+    _count++;
 }
diff --git a/src/systems/compiler_system.hpp b/src/systems/compiler_system.hpp
index 1ddd14f..aa7e405 100644
--- a/src/systems/compiler_system.hpp
+++ b/src/systems/compiler_system.hpp
@@ -7,22 +7,19 @@
 
 struct CompilerSystem
 {
-    static inline float line_height = 0.4f;
-    static inline float start_y     = -15.0f; // bottom of PosX wall
-
-    bool last_compile_ok{false};
-
-    // Call once per frame — reads available lines from gcc, spawns TextWalls
-    void update(TextWallList&, float world_scale);
-
-    // Trigger a compile — writes src to /tmp, opens popen pipe
-    void compile(TextWallList&, const std::string& src);
-
-    bool is_Running() const { return pipe != nullptr; }
 
 private:
-    FILE* pipe  {nullptr};
-    int   count {0};       // how many output lines so far
+    FILE* _pipe  {nullptr};
+    int   _count {0};
 
     void push_Line(TextWallList&, const std::string& line, float world_scale);
+public:
+    static inline float line_height = 0.4f;
+    static inline float start_y     = -WORLD_BOX_SCALE+1;
+
+    bool last_compile_ok{false};
+
+    void update    (TextWallList&, float world_scale);
+    void compile   (TextWallList&, const std::string& src);
+    bool is_Running() const { return _pipe != nullptr; }
 };
diff --git a/src/systems/freelook_system.hpp b/src/systems/freelook_system.hpp
index e9769f9..4cde0e8 100644
--- a/src/systems/freelook_system.hpp
+++ b/src/systems/freelook_system.hpp
@@ -29,8 +29,8 @@ public:
     // Call when shift released — starts lerp back to face anchor
     void
     end(uint32_t player_yaw, int32_t player_pitch,
-             const Vec3& camera_pos,
-             uint32_t& out_yaw, int32_t& out_pitch);
+        const Vec3& camera_pos,
+        uint32_t& out_yaw, int32_t& out_pitch);
 
     // Call every frame
     void
diff --git a/src/systems/hint_system.cpp b/src/systems/hint_system.cpp
index c043a89..6ab4e3b 100644
--- a/src/systems/hint_system.cpp
+++ b/src/systems/hint_system.cpp
@@ -3,32 +3,32 @@
 
 HintSystem::HintSystem()
 {
-    label_.set_Centre(Vec3{ 0.0, static_cast<double>(-WORLD_BOX_SCALE) + 2.5, 0.0 });
-    label_.set_Billboard(true);
-    label_.set_Style(Text3d::color_yellow, Text3d::depth_wall, 0.003f);
-    label_.set_Collidable(true);
+    _label.set_Centre(Vec3{ 0.0, static_cast<double>(-WORLD_BOX_SCALE) + 2.5, 0.0 });
+    _label.set_Billboard(true);
+    _label.set_Style(Text3d::color_yellow, Text3d::depth_wall, 0.003f);
+    _label.set_Collidable(true);
 }
 
 
 void
 HintSystem::set_Hint(const std::string& hint_text)
 {
-    if (hint_text == current_hint_) return;
-    current_hint_ = hint_text;
-    label_.set_Text(hint_text);
+    if (hint_text == _current_hint) return;
+    _current_hint = hint_text;
+    _label.set_Text(hint_text);
 }
 
 
 void
 HintSystem::clear()
 {
-    current_hint_.clear();
-    label_.clear();
+    _current_hint.clear();
+    _label.clear();
 }
 
 void
-HintSystem::update(double dt, const Camera& camera) { label_.update(dt, camera); }
+HintSystem::update(double dt, const Camera& camera) { _label.update(dt, camera); }
 
 void
 HintSystem::draw(GlRenderer& gl, const Camera& camera, const Light& light) const
-{ label_.draw(gl, camera, light); }
+{ _label.draw(gl, camera, light); }
diff --git a/src/systems/hint_system.hpp b/src/systems/hint_system.hpp
index 63c25e5..740a7d2 100644
--- a/src/systems/hint_system.hpp
+++ b/src/systems/hint_system.hpp
@@ -8,18 +8,18 @@
 class GlRenderer;
 
 class HintSystem {
-    TextLabel    label_;
-    std::string  current_hint_;
+    TextLabel   _label;
+    std::string _current_hint;
 
 public:
-    HintSystem();
+    HintSystem   ();
 
     void set_Hint(const std::string& hint_text);
-    void clear();
-    void update(double dt, const Camera& camera);
-    void draw(GlRenderer& gl, const Camera& camera, const Light& light) const;
+    void clear   ();
+    void update  (double dt, const Camera& camera);
+    void draw    (GlRenderer& gl, const Camera& camera, const Light& light) const;
     TextLabel&
-         get_Label()       { return label_; }
+         get_Label()       { return _label; }
     const TextLabel&
-         get_Label() const { return label_; }
+         get_Label() const { return _label; }
 };
diff --git a/src/systems/input_system.cpp b/src/systems/input_system.cpp
index 19cff5d..30213ab 100644
--- a/src/systems/input_system.cpp
+++ b/src/systems/input_system.cpp
@@ -47,7 +47,7 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
                 case SDLK_D: input.right    = true; break;
                 case SDLK_E: input.pick     = true; break;
 
-                case SDLK_F1:     game.changeState(cpputils::make_unique<GameState>());  break;
+                case SDLK_F1:     game.change_State(cpputils::make_unique<GameState>());  break;
                 case SDLK_F2:     input.commands.push_back(InputCommand::SandboxRestore);break;
                 case SDLK_SPACE:  input.commands.push_back(InputCommand::Jump);          break;
                 case SDLK_R:      input.commands.push_back(InputCommand::Drop);          break;
@@ -57,7 +57,7 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
                 case SDLK_T:      input.commands.push_back(InputCommand::TextStart);     break;
                 case SDLK_LSHIFT:
                 case SDLK_RSHIFT: input.commands.push_back(InputCommand::FreelookStart); break;
-                case SDLK_ESCAPE: game.changeState(cpputils::make_unique<MenuState>());  break;
+                case SDLK_ESCAPE: game.change_State(cpputils::make_unique<MenuState>());  break;
                 case SDLK_PLUS:
                     if (e.key.mod & SDL_KMOD_CTRL)
                         input.commands.push_back(InputCommand::NumberInc);
diff --git a/src/systems/level_runner_system.cpp b/src/systems/level_runner_system.cpp
index 6345e38..400468e 100644
--- a/src/systems/level_runner_system.cpp
+++ b/src/systems/level_runner_system.cpp
@@ -1,10 +1,10 @@
 #include "level_runner_system.hpp"
 
-LevelRunner::LevelRunner(const Level& l) : level(&l) {}
+LevelRunner::LevelRunner(const Level& l) : _level(&l) {}
 
 bool LevelRunner::advance()
 {
-    current_step++;
+    _current_step++;
     return !complete();
 }
 
diff --git a/src/systems/level_runner_system.hpp b/src/systems/level_runner_system.hpp
index 277bbef..9936b83 100644
--- a/src/systems/level_runner_system.hpp
+++ b/src/systems/level_runner_system.hpp
@@ -2,24 +2,22 @@
 #include "levels/level.hpp"
 
 class LevelRunner {
+    const Level* _level;
+    size_t       _current_step{0};
 public:
-    explicit LevelRunner(const Level& level);
+    explicit LevelRunner(const Level& _level);
 
     // Call after a successful compile
-    // Returns true if there are more steps, false if level complete
+    // Returns true if there are more steps, false if _level complete
     bool advance();
 
-    bool               complete()    const { return current_step >= level->steps.size(); }
-    size_t             step()        const { return current_step; }
-    const LevelStep&   current()     const { return level->steps[current_step]; }
-    bool               dynamic_lib() const { return level->dynamic_lib; }
-    const std::string& source_file() const { return level->source_file; }
+    bool               complete()    const { return _current_step >= _level->steps.size(); }
+    size_t             step()        const { return _current_step; }
+    const LevelStep&   current()     const { return _level->steps[_current_step]; }
+    bool               dynamic_lib() const { return _level->dynamic_lib; }
+    const std::string& source_file() const { return _level->source_file; }
 
     // All tokens to spawn for the current step (resolved — no choices yet)
     // Choices are returned as multiple Text3d objects placed near each other
     DynArray<std::string> grants_for_step() const;
-
-private:
-    const Level* level;
-    size_t       current_step{0};
 };
diff --git a/src/systems/mod_loader_system.cpp b/src/systems/mod_loader_system.cpp
index c8566dd..335dd8b 100644
--- a/src/systems/mod_loader_system.cpp
+++ b/src/systems/mod_loader_system.cpp
@@ -74,7 +74,7 @@ ModLoader::build(TextWallList& texts, const std::string& user_src, float world_s
                 return wt->wall_position == Wall::position_of(Wall::Role::Compiler);
             }),
         texts.end());
-    count_ = 0;
+    _count = 0;
 
     // Write wrapped source.
     FILE* f = fopen(MOD_SRC_PATH, "w");
@@ -90,7 +90,7 @@ ModLoader::build(TextWallList& texts, const std::string& user_src, float world_s
 
     // Unique output path each build — dlopen() caches handles by path, so a
     // fresh path guarantees the next checkpoint actually loads new code.
-    pending_so_ = "/tmp/game_mod_" + std::to_string(build_serial_++) + ".so";
+    _pending_so = "/tmp/game_mod_" + std::to_string(_build_serial++) + ".so";
 
     // Build a shared library. -fPIC + -shared; stderr→stdout for error capture.
     std::string cmd = "LC_ALL=C gcc -shared -fPIC -O0 -I";
@@ -98,12 +98,12 @@ ModLoader::build(TextWallList& texts, const std::string& user_src, float world_s
     cmd += " ";
     cmd += MOD_SRC_PATH;
     cmd += " -o ";
-    cmd += pending_so_;
+    cmd += _pending_so;
     cmd += " 2>&1";
 
-    if (pipe_) { pclose(pipe_); pipe_ = nullptr; }
-    pipe_ = popen(cmd.c_str(), "r");
-    if (!pipe_)
+    if (_pipe) { pclose(_pipe); _pipe = nullptr; }
+    _pipe = popen(cmd.c_str(), "r");
+    if (!_pipe)
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ModLoader: popen failed");
 }
 
@@ -111,21 +111,21 @@ ModLoader::build(TextWallList& texts, const std::string& user_src, float world_s
 void
 ModLoader::update(TextWallList& texts, float world_scale)
 {
-    if (!pipe_) return;
+    if (!_pipe) return;
 
     char buf[512];
-    int  fd = fileno(pipe_);
+    int  fd = fileno(_pipe);
     fd_set set; FD_ZERO(&set); FD_SET(fd, &set);
     struct timeval tv{0, 0};   // poll, never block the frame
 
     while (select(fd + 1, &set, nullptr, nullptr, &tv) > 0) {
-        if (!fgets(buf, sizeof(buf), pipe_)) {
+        if (!fgets(buf, sizeof(buf), _pipe)) {
             // EOF — build finished. No error lines means success.
-            pclose(pipe_);
-            pipe_ = nullptr;
-            if (count_ == 0) {
+            pclose(_pipe);
+            _pipe = nullptr;
+            if (_count == 0) {
                 last_build_ok = true;
-                last_so_path_ = pending_so_;
+                _last_so_path = _pending_so;
                 push_Line(texts, "BUILD OK", world_scale);
             }
             return;
@@ -162,26 +162,26 @@ ModLoader::push_Line(TextWallList& texts, const std::string& line, float world_s
         Wall::Role::Compiler,
         std::move(t)
     }));
-    count_++;
+    _count++;
 }
 
 DynArray<ModObject>
 ModLoader::load_Objects()
 {
     DynArray<ModObject> result;
-    if (last_so_path_.empty()) return result;
+    if (_last_so_path.empty()) return result;
 
     // Close the previous handle before loading the new one. Safe because the
     // engine — not the .so — owns every object we spawn from these PODs, so
     // nothing live points into the unmapped library after dlclose.
-    if (handle_) {
-        dlclose(handle_);
-        handle_ = nullptr;
+    if (_handle) {
+        dlclose(_handle);
+        _handle = nullptr;
     }
 
     dlerror();   // clear stale error
-    handle_ = dlopen(last_so_path_.c_str(), RTLD_NOW | RTLD_LOCAL);
-    if (!handle_) {
+    _handle = dlopen(_last_so_path.c_str(), RTLD_NOW | RTLD_LOCAL);
+    if (!_handle) {
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
                      "ModLoader: dlopen failed: %s", dlerror());
         return result;
@@ -190,7 +190,7 @@ ModLoader::load_Objects()
     using BuildFn = int(*)(ModObject*, int);
     // The cast-through-void* dance silences -Wpedantic on function-pointer casts.
     BuildFn game_build =
-        reinterpret_cast<BuildFn>(dlsym(handle_, "game_build"));
+        reinterpret_cast<BuildFn>(dlsym(_handle, "game_build"));
     if (!game_build) {
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
                      "ModLoader: dlsym(game_build) failed: %s", dlerror());
@@ -209,6 +209,6 @@ ModLoader::load_Objects()
 
 ModLoader::~ModLoader()
 {
-    if (handle_) dlclose(handle_);
-    if (pipe_)   pclose(pipe_);
+    if (_handle) dlclose(_handle);
+    if (_pipe)   pclose(_pipe);
 }
diff --git a/src/systems/mod_loader_system.hpp b/src/systems/mod_loader_system.hpp
index b1f2925..5e19860 100644
--- a/src/systems/mod_loader_system.hpp
+++ b/src/systems/mod_loader_system.hpp
@@ -9,31 +9,34 @@
 
 class ModLoader
 {
+    FILE*       _pipe  { nullptr };
+    int         _count { 0 };
+    int         _build_serial { 0 };
+    std::string _pending_so;
+    std::string _last_so_path;
+
+    void*       _handle { nullptr };   // current dlopen handle
+
+    static constexpr float line_height = 0.25f;
+    static constexpr float start_y     = -15.0f;
+
+    static std::string
+         wrap_Source(const std::string& user_body);
+    void push_Line  (TextWallList& texts, const std::string& line, float world_scale);
+
 public:
-    void build(TextWallList& texts, const std::string& user_src, float world_scale, bool full_source);
-    void update(TextWallList& texts, float world_scale);
-    bool is_Running() const { return pipe_ != nullptr; }
     bool last_build_ok { false };
-    const std::string& last_so_path() const { return last_so_path_; }
+
+    void build       (TextWallList& texts, const std::string& user_src, float world_scale, bool full_source);
+    void update      (TextWallList& texts, float world_scale);
+    bool is_Running  () const { return _pipe != nullptr; }
+    const std::string&
+         last_so_path() const { return _last_so_path; }
 
     // Checkpoint 2: dlopen the last successful .so, call game_build,
     // return the spawned descriptions. Empty vector on any failure.
-    DynArray<ModObject> load_Objects();
+    DynArray<ModObject>
+         load_Objects();
 
     ~ModLoader();   // dlclose on teardown
-
-private:
-    static std::string wrap_Source(const std::string& user_body);
-    void push_Line(TextWallList& texts, const std::string& line, float world_scale);
-
-    FILE*       pipe_  { nullptr };
-    int         count_ { 0 };
-    int         build_serial_ { 0 };
-    std::string pending_so_;
-    std::string last_so_path_;
-
-    void*       handle_ { nullptr };   // current dlopen handle
-
-    static constexpr float line_height = 0.25f;
-    static constexpr float start_y     = -15.0f;
 };
diff --git a/src/systems/particle_system.cpp b/src/systems/particle_system.cpp
index c2fdd83..07abea3 100644
--- a/src/systems/particle_system.cpp
+++ b/src/systems/particle_system.cpp
@@ -35,7 +35,7 @@ ParticleSystem::emit_FireworkBurst(const Vec3& center,
                                    SDL_FColor color,
                                    int count)
 {
-    particles_.reserve(particles_.size() + static_cast<size_t>(count));
+    _particles.reserve(_particles.size() + static_cast<size_t>(count));
 
     for (int i = 0; i < count; ++i) {
         Particle p;
@@ -56,7 +56,7 @@ ParticleSystem::emit_FireworkBurst(const Vec3& center,
         p.spin = static_cast<float>((frand() - 0.5) * 12.0);   // ± 6 rad/s
         p.alive = true;
 
-        particles_.push_back(p);
+        _particles.push_back(p);
     }
 }
 
@@ -68,7 +68,7 @@ ParticleSystem::update(double dt,
 {
     const double ws = static_cast<double>(world_scale);
 
-    for (Particle& p : particles_) {
+    for (Particle& p : _particles) {
         if (!p.alive) continue;
 
         p.age += dt;
@@ -108,17 +108,17 @@ ParticleSystem::update(double dt,
     }
 
     // Compact: drop dead particles.
-    particles_.erase(
-        std::remove_if(particles_.begin(), particles_.end(),
+    _particles.erase(
+        std::remove_if(_particles.begin(), _particles.end(),
             [](const Particle& p) { return !p.alive; }),
-        particles_.end());
+        _particles.end());
 }
 
 
 void
 ParticleSystem::draw(GlRenderer& gl, const Camera& cam, const Light& light) const
 {
-    if (particles_.empty()) return;
+    if (_particles.empty()) return;
 
     // Glow look: high ambient, low diffuse contribution. Same trick Plate uses.
     Light glow = light;
@@ -132,7 +132,7 @@ ParticleSystem::draw(GlRenderer& gl, const Camera& cam, const Light& light) cons
     gl.set_Wireframe(false);
     gl.set_Light(glow);
 
-    for (const Particle& p : particles_) {
+    for (const Particle& p : _particles) {
         // Fade out over the last 30% of lifetime.
         double tail   = p.lifetime * 0.3;
         double t_left = p.lifetime - p.age;
@@ -155,5 +155,5 @@ ParticleSystem::draw(GlRenderer& gl, const Camera& cam, const Light& light) cons
 void
 ParticleSystem::clear()
 {
-    particles_.clear();
+    _particles.clear();
 }
diff --git a/src/systems/particle_system.hpp b/src/systems/particle_system.hpp
index 13127ec..8aa7633 100644
--- a/src/systems/particle_system.hpp
+++ b/src/systems/particle_system.hpp
@@ -38,6 +38,7 @@ struct Particle {
 // They do NOT collide with words, AI, or each other — by design.
 class ParticleSystem
 {
+    DynArray<Particle> _particles;
 public:
     void update(double dt,
                 const DynArray<GameObject*>& solid_colliders,
@@ -52,8 +53,5 @@ public:
     // Clear all particles — called when celebration ends.
     void clear();
 
-    bool empty() const { return particles_.empty(); }
-
-private:
-    DynArray<Particle> particles_;
+    bool empty() const { return _particles.empty(); }
 };
diff --git a/src/systems/physics_system.cpp b/src/systems/physics_system.cpp
index 6862242..cf59310 100644
--- a/src/systems/physics_system.cpp
+++ b/src/systems/physics_system.cpp
@@ -232,7 +232,7 @@ PhysicsSystem::update_Objects(const DynArray<GameObject*>& objects,
     // which is the bulk of the old O(n²) work.
     { FP_ZONE("phys.pairs");
         // Build grid over ALL objects (passive included — movers must find them).
-        grid_.build(objects, static_cast<double>(world_scale));
+        _grid.build(objects, static_cast<double>(world_scale));
 
         // Only active bodies initiate collision tests.
         DynArray<GameObject*> active;
@@ -491,20 +491,20 @@ PhysicsSystem::collide_Grid(const DynArray<GameObject*>& active)
     for (GameObject* a : active) {
         if (!a) continue;
 
-        int cx = static_cast<int>((a->pos.x - grid_.min_x) * grid_.inv);
-        int cz = static_cast<int>((a->pos.z - grid_.min_z) * grid_.inv);
-        cx = cx < 0 ? 0 : (cx >= grid_.nx ? grid_.nx - 1 : cx);
-        cz = cz < 0 ? 0 : (cz >= grid_.nz ? grid_.nz - 1 : cz);
+        int cx = static_cast<int>((a->pos.x - _grid.min_x) * _grid.inv);
+        int cz = static_cast<int>((a->pos.z - _grid.min_z) * _grid.inv);
+        cx = cx < 0 ? 0 : (cx >= _grid.nx ? _grid.nx - 1 : cx);
+        cz = cz < 0 ? 0 : (cz >= _grid.nz ? _grid.nz - 1 : cz);
 
         for (int dz = -1; dz <= 1; ++dz) {
             const int ncz = cz + dz;
-            if (ncz < 0 || ncz >= grid_.nz) continue;
+            if (ncz < 0 || ncz >= _grid.nz) continue;
 
             for (int dx = -1; dx <= 1; ++dx) {
                 const int ncx = cx + dx;
-                if (ncx < 0 || ncx >= grid_.nx) continue;
+                if (ncx < 0 || ncx >= _grid.nx) continue;
 
-                const auto& cell = grid_.cells[static_cast<size_t>(grid_.index(ncx, ncz))];
+                const auto& cell = _grid.cells[static_cast<size_t>(_grid.index(ncx, ncz))];
                 for (GameObject* b : cell) {
                     if (b == a) continue;
 
diff --git a/src/systems/physics_system.hpp b/src/systems/physics_system.hpp
index 0bc1a22..e919f3b 100644
--- a/src/systems/physics_system.hpp
+++ b/src/systems/physics_system.hpp
@@ -17,13 +17,6 @@ struct ProjectileContactEvent {
 };
 
 class PhysicsSystem {
-    static void resolve_WorldBounds(GameObject& obj, float world_scale);
-    bool resolve_SingleBoxCollision(Projectile& proj,
-                                    const Vec3& box_min, const Vec3& box_max,
-                                    const Vec3& word_he, double restitution,
-                                    Vec3& cur, Vec3& prev, Vec3& dir,
-                                    Collision::SweptResult& out_sw);
-    void try_Pair(GameObject* a, GameObject* b);
     // Uniform spatial grid scratch, reused each frame to avoid reallocation.
     struct SpatialGrid {
         double cell = 2.0;
@@ -35,7 +28,17 @@ class PhysicsSystem {
         void build(const DynArray<GameObject*>& objs, double world_scale);
         int  index(int cx, int cz) const { return cz * nx + cx; }
     };
-    SpatialGrid grid_;
+
+    SpatialGrid _grid;
+
+    static void
+         resolve_WorldBounds(GameObject& obj, float world_scale);
+    bool resolve_SingleBoxCollision(Projectile& proj,
+                                    const Vec3& box_min, const Vec3& box_max,
+                                    const Vec3& word_he, double restitution,
+                                    Vec3& cur, Vec3& prev, Vec3& dir,
+                                    Collision::SweptResult& out_sw);
+    void try_Pair(GameObject* a, GameObject* b);
     void collide_Grid(const DynArray<GameObject*>& active);
 
 public:
diff --git a/src/systems/plate_system.hpp b/src/systems/plate_system.hpp
index 26fd991..bfd408a 100644
--- a/src/systems/plate_system.hpp
+++ b/src/systems/plate_system.hpp
@@ -9,12 +9,7 @@ struct StepPlate;
 class PlateSystem
 {
 public:
-    // Updates each plate: bob/pulse animation, and detects whether the player
-    // is standing on each one. Sets plate->active based on player AABB overlap.
-    void update(double dt,
-                const Player& player,
+    void update(double dt, const Player& player,
                 const DynArray<StepPlate>& plates);
-
-    // True if the player is currently standing on any plate.
     bool any_Active(const DynArray<StepPlate>& plates) const;
 };
diff --git a/src/systems/pult_system.cpp b/src/systems/pult_system.cpp
index 83ff7da..6863408 100644
--- a/src/systems/pult_system.cpp
+++ b/src/systems/pult_system.cpp
@@ -9,7 +9,7 @@
 void
 PultSystem::init(DynArray<GameObjectPtr>& scenery,
                  DynArray<PultButton*>&   buttons_out,
-                 float                       world_scale)
+                 float                    world_scale)
 {
     const float  floor_y = -world_scale;
     const float  pult_y  = floor_y + Pult::H;
@@ -37,7 +37,7 @@ PultSystem::init(DynArray<GameObjectPtr>& scenery,
     {
         auto owned = cpputils::make_unique<Pult>(name, make_pult_pos(role, along));
         Pult* raw  = owned.get();
-        pults_.push_back(raw);
+        _pults.push_back(raw);
         scenery.push_back(std::move(owned));
         return raw;
     };
@@ -103,7 +103,7 @@ PultSystem::handle_ProjectileContact(const ProjectileContactEvent&    ev,
 void
 PultSystem::update(double dt, const Camera& camera)
 {
-    for (Pult* p : pults_) {
+    for (Pult* p : _pults) {
         p->tick(dt);
         p->label.update(dt, camera);
     }
@@ -113,7 +113,7 @@ PultSystem::update(double dt, const Camera& camera)
 void
 PultSystem::draw_Labels(GlRenderer& gl, const Camera& camera, const Light& light) const
 {
-    for (const Pult* p : pults_)
+    for (const Pult* p : _pults)
         p->label.draw(gl, camera, light);
 }
 
@@ -122,7 +122,7 @@ DynArray<TextLabel*>
 PultSystem::labels()
 {
     DynArray<TextLabel*> out;
-    out.reserve(pults_.size());
-    for (Pult* p : pults_) out.push_back(&p->label);
+    out.reserve(_pults.size());
+    for (Pult* p : _pults) out.push_back(&p->label);
     return out;
 }
diff --git a/src/systems/pult_system.hpp b/src/systems/pult_system.hpp
index 2abdb47..a1e89d0 100644
--- a/src/systems/pult_system.hpp
+++ b/src/systems/pult_system.hpp
@@ -14,6 +14,7 @@ struct ProjectileContactEvent;
 
 class PultSystem
 {
+    DynArray<Pult*> _pults;
 public:
     void init(DynArray<GameObjectPtr>& scenery,
               DynArray<PultButton*>&   buttons_out,
@@ -28,7 +29,4 @@ public:
     void draw_Labels(GlRenderer& gl, const Camera& camera, const Light& light) const;
 
     DynArray<TextLabel*> labels();
-
-private:
-    DynArray<Pult*> pults_;
 };
diff --git a/src/systems/word_ai_system.cpp b/src/systems/word_ai_system.cpp
index 63c4680..c141d4b 100644
--- a/src/systems/word_ai_system.cpp
+++ b/src/systems/word_ai_system.cpp
@@ -91,7 +91,7 @@ WordAiSystem::update_Idle(Text3d& w, const Player& p, int active_ai_count, doubl
     if (w.body.mode != PhysicsMode::Hover) return;
 
     // Hard cap: don't start new AI behavior if we're already at the limit.
-    if (active_ai_count >= max_active_ai) return;
+    if (active_ai_count >= _max_active_ai) return;
 
     Vec3 to_player = p.pos - w.pos;
     double dist_sq = to_player.x*to_player.x +
@@ -362,7 +362,7 @@ WordAiSystem::apply_Locomotion(Text3d& w, const Vec3& target_vel, double dt)
     w.pos.z += w.body.vel.z * dt;
 
     // Clamp to world bounds
-    const double ws = static_cast<double>(world_scale);
+    const double ws = static_cast<double>(_world_scale);
 
     auto clamp_axis = [](double& p, double& v, double he,
                          double lo, double hi) {
diff --git a/src/systems/word_ai_system.hpp b/src/systems/word_ai_system.hpp
index d01349b..c4cea5e 100644
--- a/src/systems/word_ai_system.hpp
+++ b/src/systems/word_ai_system.hpp
@@ -7,6 +7,20 @@ struct Player;
 struct CarriedLine;
 
 class WordAiSystem {
+    static constexpr double TRIGGER_RANGE     =  6.0;
+    static constexpr double IDLE_AGGRO_ROLL   =  0.4;   // per-second chance × aggressiveness
+    static constexpr double FLEE_BIAS         =  0.35;  // fraction of state changes that are Flee vs Attack
+    static constexpr double STUNNED_DURATION  =  1.2;
+    static constexpr double FLEE_ESCAPE_DIST  = 10.0;
+    static constexpr double FLEE_ESCAPE_TIME  =  2.0;
+    static constexpr double STEAL_PROBABILITY =  0.5;   // chance per contact
+    static constexpr double VELOCITY_LERP_RATE = 6.0;
+    static constexpr double WIGGLE_AMPLITUDE   = 0.8;
+
+    float _world_scale;
+    int   _max_active_ai = 5;
+
+
     void update_Idle    (Text3d& w, const Player& p, int active_ai_count, double dt);
     void update_Attack  (Text3d& w, const Player& p, CarriedLine& carried,
                          DynArray<Text3dPtr>& all_words,
@@ -26,22 +40,9 @@ class WordAiSystem {
     void enter_Idle     (Text3d& w);
 
     void apply_Locomotion(Text3d& w, const Vec3& target_vel, double dt);
-
-    static constexpr double TRIGGER_RANGE     =  6.0;
-    static constexpr double IDLE_AGGRO_ROLL   =  0.4;   // per-second chance × aggressiveness
-    static constexpr double FLEE_BIAS         =  0.35;  // fraction of state changes that are Flee vs Attack
-    static constexpr double STUNNED_DURATION  =  1.2;
-    static constexpr double FLEE_ESCAPE_DIST  = 10.0;
-    static constexpr double FLEE_ESCAPE_TIME  =  2.0;
-    static constexpr double STEAL_PROBABILITY =  0.5;   // chance per contact
-    static constexpr double VELOCITY_LERP_RATE = 6.0;
-    static constexpr double WIGGLE_AMPLITUDE   = 0.8;
-
-    float world_scale;
-    int   max_active_ai = 5;
 public:
-    void set_MaxActiveAi(int n) { max_active_ai = n; }
-    void set_WorldScale(float ws) { world_scale = ws; }
+    void set_MaxActiveAi(int n)    { _max_active_ai = n; }
+    void set_WorldScale (float ws) { _world_scale = ws; }
     void update(DynArray<Text3dPtr>& words,
                 const Player& player,
                 CarriedLine& carried,
diff --git a/src/systems/word_anim.hpp b/src/systems/word_anim.hpp
index a867546..ac53435 100644
--- a/src/systems/word_anim.hpp
+++ b/src/systems/word_anim.hpp
@@ -18,14 +18,14 @@ struct WordAnim_Add {
 
 struct WordAnim_Drop {
     Text3dPtr word;
-    bool          complete{false};
+    bool      complete{false};
 };
 
 class WordAnimSystem {
 public:
     void update(DynArray<WordAnim_Add>&  anims_add,
-            DynArray<WordAnim_Drop>& anims_drop,
-            CarriedLine&                carried,
-            DynArray<Text3dPtr>&     words,
-            double dt, double floor_y);
+                DynArray<WordAnim_Drop>& anims_drop,
+                CarriedLine&             carried,
+                DynArray<Text3dPtr>&     words,
+                double dt, double floor_y);
 };
diff --git a/src/text_3d/text_label.cpp b/src/text_3d/text_label.cpp
index b363e0e..f1df0e7 100644
--- a/src/text_3d/text_label.cpp
+++ b/src/text_3d/text_label.cpp
@@ -19,9 +19,9 @@ split_words(const std::string& text)
 void
 TextLabel::set_Text(const std::string& text)
 {
-    if (text == current_text_) return;
-    current_text_ = text;
-    rebuild_(text);
+    if (text == _current_text) return;
+    _current_text = text;
+    rebuild(text);
 }
 
 
@@ -30,7 +30,7 @@ TextLabel::set_Text(const std::string& text,
                     SDL_FColor color, float depth, float scale)
 {
     set_Style(color, depth, scale);
-    current_text_.clear();
+    _current_text.clear();
     set_Text(text);
 }
 
@@ -38,54 +38,54 @@ TextLabel::set_Text(const std::string& text,
 void
 TextLabel::clear()
 {
-    words_.clear();
-    offsets_.clear();
-    anchor_pos_.clear();
-    jiggle_off_.clear();
-    jiggle_vel_.clear();
-    current_text_.clear();
+    _words.clear();
+    _offsets.clear();
+    _anchor_pos.clear();
+    _jiggle_off.clear();
+    _jiggle_vel.clear();
+    _current_text.clear();
 }
 
 
 void
 TextLabel::set_Centre(const Vec3& p)
 {
-    mode_       = PositionMode::Absolute;
-    centre_     = p;
-    anchor_ptr_ = nullptr;
+    _mode       = PositionMode::Absolute;
+    _centre     = p;
+    _anchor_ptr = nullptr;
 }
 
 
 void
 TextLabel::attach_To(const Vec3* anchor, const Vec3& offset)
 {
-    mode_          = PositionMode::Anchor;
-    anchor_ptr_    = anchor;
-    attach_offset_ = offset;
+    _mode          = PositionMode::Anchor;
+    _anchor_ptr    = anchor;
+    _attach_offset = offset;
 }
 
 
 void
 TextLabel::detach()
 {
-    mode_       = PositionMode::Absolute;
-    anchor_ptr_ = nullptr;
+    _mode       = PositionMode::Absolute;
+    _anchor_ptr = nullptr;
 }
 
 void
 TextLabel::set_Style(SDL_FColor color, float depth, float scale)
 {
-    style_color_ = color;
-    style_depth_ = depth;
-    style_scale_ = scale;
+    _style_color = color;
+    _style_depth = depth;
+    _style_scale = scale;
 }
 
 
 void
 TextLabel::set_Collidable(bool on)
 {
-    collidable_ = on;
-    for (Text3dPtr& w : words_) {
+    _collidable = on;
+    for (Text3dPtr& w : _words) {
         if (!w) continue;
         w->solid        = on;
         w->carriable    = false;
@@ -96,13 +96,13 @@ TextLabel::set_Collidable(bool on)
 
 
 void
-TextLabel::rebuild_(const std::string& text)
+TextLabel::rebuild(const std::string& text)
 {
-    words_.clear();
-    offsets_.clear();
-    anchor_pos_.clear();
-    jiggle_off_.clear();
-    jiggle_vel_.clear();
+    _words.clear();
+    _offsets.clear();
+    _anchor_pos.clear();
+    _jiggle_off.clear();
+    _jiggle_vel.clear();
 
     DynArray<std::string> tokens = split_words(text);
     if (tokens.empty()) return;
@@ -111,8 +111,8 @@ TextLabel::rebuild_(const std::string& text)
     DynArray<Tmp> tmp;
     tmp.reserve(tokens.size());
     for (const std::string& tok : tokens) {
-        tmp.push_back({ make_Text3d(tok, style_depth_, style_scale_,
-                                    Vec3{0,0,0}, style_color_) });
+        tmp.push_back({ make_Text3d(tok, _style_depth, _style_scale,
+                                    Vec3{0,0,0}, _style_color) });
     }
 
     float total_w = 0.0f;
@@ -122,19 +122,19 @@ TextLabel::rebuild_(const std::string& text)
     float x_cur = -total_w * 0.5f;
     for (auto& t : tmp) {
         float he = t.obj->half_extent;
-        offsets_.push_back(static_cast<double>(x_cur + he));
+        _offsets.push_back(static_cast<double>(x_cur + he));
 
         t.obj->carriable    = false;
         t.obj->interactable = false;
-        t.obj->solid        = collidable_;
-        t.obj->body.mode    = collidable_ ? PhysicsMode::Static
+        t.obj->solid        = _collidable;
+        t.obj->body.mode    = _collidable ? PhysicsMode::Static
                                           : PhysicsMode::Hover;
         t.obj->body.restitution = 0.55;
 
-        words_.push_back(std::move(t.obj));
-        anchor_pos_.push_back(Vec3{});
-        jiggle_off_.push_back(Vec3{});
-        jiggle_vel_.push_back(Vec3{});
+        _words.push_back(std::move(t.obj));
+        _anchor_pos.push_back(Vec3{});
+        _jiggle_off.push_back(Vec3{});
+        _jiggle_vel.push_back(Vec3{});
 
         x_cur += he * 2.0f + WORD_GAP;
     }
@@ -142,25 +142,25 @@ TextLabel::rebuild_(const std::string& text)
 
 
 Vec3
-TextLabel::current_Centre_() const
+TextLabel::current_Centre() const
 {
-    if (mode_ == PositionMode::Anchor && anchor_ptr_) {
+    if (_mode == PositionMode::Anchor && _anchor_ptr) {
         return {
-            anchor_ptr_->x + attach_offset_.x,
-            anchor_ptr_->y + attach_offset_.y,
-            anchor_ptr_->z + attach_offset_.z
+            _anchor_ptr->x + _attach_offset.x,
+            _anchor_ptr->y + _attach_offset.y,
+            _anchor_ptr->z + _attach_offset.z
         };
     }
-    return centre_;
+    return _centre;
 }
 
 
 void
 TextLabel::update(double dt, const Camera& camera)
 {
-    if (words_.empty()) return;
+    if (_words.empty()) return;
 
-    Vec3 centre = current_Centre_();
+    Vec3 centre = current_Centre();
 
     // ── Compute orientation ─────────────────────────────────────────────
     //
@@ -176,7 +176,7 @@ TextLabel::update(double dt, const Camera& camera)
     float yaw_for_glyphs   = 0.0f;
     float pitch_for_glyphs = 0.0f;
 
-    if (billboard_) {
+    if (_billboard) {
         Vec3 forward = camera.pos - centre;
         double flen = length(forward);
         if (flen < 0.0001) return;
@@ -204,17 +204,17 @@ TextLabel::update(double dt, const Camera& camera)
         // what we want when the camera is above.
         pitch_for_glyphs = -SDL_asinf(static_cast<float>(forward.y));
     } else {
-        yaw_for_glyphs   = face_yaw_;
+        yaw_for_glyphs   = _face_yaw;
         pitch_for_glyphs = 0.0f;
-        right = { SDL_cos(face_yaw_), 0.0, -SDL_sin(face_yaw_) };
+        right = { SDL_cos(_face_yaw), 0.0, -SDL_sin(_face_yaw) };
     }
 
     // ── Place each word at anchor + jiggle, apply shared orientation ────
-    for (size_t i = 0; i < words_.size(); ++i) {
-        Text3d& t = *words_[i];
+    for (size_t i = 0; i < _words.size(); ++i) {
+        Text3d& t = *_words[i];
         if (!t.loaded) continue;
 
-        double off = offsets_[i];
+        double off = _offsets[i];
 
         // Line stays on the horizontal `right` axis through centre.
         Vec3 anchor{
@@ -222,11 +222,11 @@ TextLabel::update(double dt, const Camera& camera)
             centre.y + right.y * off,
             centre.z + right.z * off
         };
-        anchor_pos_[i] = anchor;
+        _anchor_pos[i] = anchor;
 
         // Spring-damp the jiggle
-        Vec3& off3 = jiggle_off_[i];
-        Vec3& vel3 = jiggle_vel_[i];
+        Vec3& off3 = _jiggle_off[i];
+        Vec3& vel3 = _jiggle_vel[i];
 
         Vec3 acc{
             -JIGGLE_STIFF * off3.x - JIGGLE_DAMP * vel3.x,
@@ -259,7 +259,7 @@ TextLabel::update(double dt, const Camera& camera)
 void
 TextLabel::draw(GlRenderer& gl, const Camera& camera, const Light& light) const
 {
-    for (const Text3dPtr& w : words_) {
+    for (const Text3dPtr& w : _words) {
         if (!w || !w->loaded) continue;
         w->draw(gl, camera, light);
     }
@@ -269,7 +269,7 @@ TextLabel::draw(GlRenderer& gl, const Camera& camera, const Light& light) const
 void
 TextLabel::kick(size_t word_index, const Vec3& projectile_vel, const Vec3& normal)
 {
-    if (word_index >= words_.size()) return;
+    if (word_index >= _words.size()) return;
 
     double v_into = -(projectile_vel.x * normal.x +
                       projectile_vel.y * normal.y +
@@ -278,7 +278,7 @@ TextLabel::kick(size_t word_index, const Vec3& projectile_vel, const Vec3& norma
 
     double mag = v_into * KICK_STRENGTH;
 
-    Vec3& vel3 = jiggle_vel_[word_index];
+    Vec3& vel3 = _jiggle_vel[word_index];
     vel3.x += -normal.x * mag;
     vel3.y += -normal.y * mag;
     vel3.z += -normal.z * mag;
diff --git a/src/text_3d/text_label.hpp b/src/text_3d/text_label.hpp
index 37baa79..0e768fb 100644
--- a/src/text_3d/text_label.hpp
+++ b/src/text_3d/text_label.hpp
@@ -10,12 +10,36 @@
 class GlRenderer;
 
 class TextLabel {
-public:
     enum class PositionMode : uint8_t {
         Absolute,
         Anchor,
     };
 
+    DynArray<Text3dPtr> _words;
+    DynArray<double>    _offsets;
+    DynArray<Vec3>      _anchor_pos;
+    DynArray<Vec3>      _jiggle_off;
+    DynArray<Vec3>      _jiggle_vel;
+
+    PositionMode  _mode           { PositionMode::Absolute };
+    Vec3          _centre         {};
+    const Vec3*   _anchor_ptr     { nullptr };
+    Vec3          _attach_offset  {};
+
+    bool   _billboard  { true };
+    float  _face_yaw   { 0.0f };
+
+    SDL_FColor  _style_color  { 1.0f, 1.0f, 0.0f, 1.0f };
+    float       _style_depth  { 0.06f };
+    float       _style_scale  { 0.003f };
+
+    bool        _collidable   { true };
+    std::string _current_text;
+
+    void rebuild(const std::string& text);
+    Vec3 current_Centre() const;
+
+public:
     static constexpr float  WORD_GAP        = 0.25f;
     static constexpr double JIGGLE_STIFF    = 80.0;
     static constexpr double JIGGLE_DAMP     = 9.0;
@@ -31,40 +55,15 @@ public:
     void set_Centre(const Vec3& p);
     void attach_To(const Vec3* anchor, const Vec3& offset = {});
     void detach();
-    void set_Billboard(bool on) { billboard_ = on; }
-    void set_FaceYaw(float y)  { face_yaw_ = y;  }
+    void set_Billboard(bool on) { _billboard = on; }
+    void set_FaceYaw(float y)  { _face_yaw = y;  }
     void set_Style(SDL_FColor color, float depth, float scale);
     void set_Collidable(bool on);
     void update(double dt, const Camera& camera);
     void draw(GlRenderer& gl, const Camera& camera, const Light& light) const;
     const DynArray<Text3dPtr>&
-         words() const { return words_; }
+         words() const { return _words; }
     void kick(size_t word_index, const Vec3& projectile_vel, const Vec3& normal);
 
-    bool empty() const { return words_.empty(); }
-
-private:
-    DynArray<Text3dPtr> words_;
-    DynArray<double>    offsets_;
-    DynArray<Vec3>      anchor_pos_;
-    DynArray<Vec3>      jiggle_off_;
-    DynArray<Vec3>      jiggle_vel_;
-
-    PositionMode  mode_           { PositionMode::Absolute };
-    Vec3          centre_         {};
-    const Vec3*   anchor_ptr_     { nullptr };
-    Vec3          attach_offset_  {};
-
-    bool   billboard_  { true };
-    float  face_yaw_   { 0.0f };
-
-    SDL_FColor  style_color_  { 1.0f, 1.0f, 0.0f, 1.0f };
-    float       style_depth_  { 0.06f };
-    float       style_scale_  { 0.003f };
-
-    bool        collidable_   { true };
-    std::string current_text_;
-
-    void rebuild_(const std::string& text);
-    Vec3 current_Centre_() const;
+    bool empty() const { return _words.empty(); }
 };
diff --git a/src/wall/editor_wall.cpp b/src/wall/editor_wall.cpp
index 4f80b3c..a5bc98a 100644
--- a/src/wall/editor_wall.cpp
+++ b/src/wall/editor_wall.cpp
@@ -10,7 +10,7 @@
 void
 EditorWall::clear()
 {
-    lines_.clear();
+    _lines.clear();
 }
 
 
@@ -19,7 +19,7 @@ EditorWall::find_NearestLine(double y)
 {
     Line*  best    = nullptr;
     double best_dy = SNAP_THRESHOLD_Y;
-    for (Line& l : lines_) {
+    for (Line& l : _lines) {
         if (l.words.empty()) continue;
         const double center_y = l.words.front()->pos.y;   // any word on the row; they share y
         const double dy = SDL_fabs(center_y - y);
@@ -87,8 +87,8 @@ EditorWall::on_WordPlaced(Text3d* word, const Vec3& impact_pos)
     if (!target) {
         Line fresh;
         fresh.y = impact_pos.y;
-        lines_.push_back(std::move(fresh));
-        target = &lines_.back();
+        _lines.push_back(std::move(fresh));
+        target = &_lines.back();
     }
 
     const double new_half_z = word->half_extent;
@@ -109,13 +109,13 @@ EditorWall::on_WordRemoved(Text3d* word)
 {
     if (!word) return;
 
-    for (auto it = lines_.begin(); it != lines_.end(); ) {
+    for (auto it = _lines.begin(); it != _lines.end(); ) {
         auto& ws = it->words;
         auto wit = std::find(ws.begin(), ws.end(), word);
         if (wit != ws.end()) {
             ws.erase(wit);
             if (ws.empty()) {
-                it = lines_.erase(it);
+                it = _lines.erase(it);
                 return;
             }
             return;
@@ -128,12 +128,12 @@ EditorWall::on_WordRemoved(Text3d* word)
 void
 EditorWall::draw(GlRenderer& gl, const Camera& cam, const Light& light) const
 {
-    if (lines_.empty()) return;
+    if (_lines.empty()) return;
 
     DynArray<float> verts;
     DynArray<int>   indices;
-    verts.reserve(lines_.size() * 4 * 9);
-    indices.reserve(lines_.size() * 6);
+    verts  .reserve(_lines.size() * 4 * 9);
+    indices.reserve(_lines.size() * 6);
 
     const float ws = WORLD_BOX_SCALE;
     const float x_front = -ws + 0.02f;
@@ -142,7 +142,7 @@ EditorWall::draw(GlRenderer& gl, const Camera& cam, const Light& light) const
     constexpr float cr = 0.40f, cg = 0.50f, cb = 0.78f;
 
     int base = 0;
-    for (const Line& l : lines_) {
+    for (const Line& l : _lines) {
         if (l.words.empty()) continue;
 
         const float y_mid = static_cast<float>(l.y - UNDERLINE_DROP);
diff --git a/src/wall/editor_wall.hpp b/src/wall/editor_wall.hpp
index 0f21ffc..028d60c 100644
--- a/src/wall/editor_wall.hpp
+++ b/src/wall/editor_wall.hpp
@@ -41,7 +41,7 @@ class EditorWall
         size_t insert_index;
     };
 
-    DynArray<Line> lines_;
+    DynArray<Line> _lines;
     Line* find_NearestLine(double y);
 
     // Decide where the new word lands on the line and shift neighbours if a
@@ -77,7 +77,7 @@ public:
     void draw(GlRenderer& gl, const Camera& cam, const Light& light) const;
 
     // Diagnostics.
-    size_t line_Count() const { return lines_.size(); }
+    size_t line_Count() const { return _lines.size(); }
 
-    void scroll(double delta) { for (Line& l : lines_) l.y += delta; }
+    void scroll(double delta) { for (Line& l : _lines) l.y += delta; }
 };
diff --git a/src/wall/wall_surfaces.cpp b/src/wall/wall_surfaces.cpp
index 976d33b..18b7bf4 100644
--- a/src/wall/wall_surfaces.cpp
+++ b/src/wall/wall_surfaces.cpp
@@ -107,8 +107,8 @@ build_Wall(WallSurfaces::WallQuad& out,
 void
 WallSurfaces::generate(float world_scale)
 {
-    walls_.clear();
-    walls_.resize(4);
+    _walls.clear();
+    _walls.resize(4);
 
     struct Spec {
         Wall::Role     role;
@@ -124,9 +124,9 @@ WallSurfaces::generate(float world_scale)
     };
 
     for (size_t i = 0; i < 4; ++i) {
-        walls_[i].role      = specs[i].role;
-        walls_[i].is_editor = specs[i].is_editor;
-        build_Wall(walls_[i], specs[i].pos, world_scale, specs[i].color);
+        _walls[i].role      = specs[i].role;
+        _walls[i].is_editor = specs[i].is_editor;
+        build_Wall(_walls[i], specs[i].pos, world_scale, specs[i].color);
     }
 }
 
@@ -134,7 +134,7 @@ WallSurfaces::generate(float world_scale)
 void
 WallSurfaces::draw(GlRenderer& gl, const Camera& cam, const Light& light) const
 {
-    if (walls_.empty()) return;
+    if (_walls.empty()) return;
 
     Mat4 view  = buildViewMat4(cam);
     Mat4 proj  = gl.projection();
@@ -147,7 +147,7 @@ WallSurfaces::draw(GlRenderer& gl, const Camera& cam, const Light& light) const
     gl.set_Light(light);
     gl.set_TextLineSpacing(Wall::TEXT_LINE_SPACING);
 
-    for (const WallQuad& w : walls_) {
+    for (const WallQuad& w : _walls) {
         gl.set_Color(w.color.r, w.color.g, w.color.b);
         gl.enable_WallPaperMode(w.is_editor);
         gl.set_ColorUseVertex(false);
diff --git a/src/wall/wall_surfaces.hpp b/src/wall/wall_surfaces.hpp
index 0057e3d..6150a17 100644
--- a/src/wall/wall_surfaces.hpp
+++ b/src/wall/wall_surfaces.hpp
@@ -27,14 +27,13 @@ public:
         Wall::Role role;
         DynArray<float> verts;
         DynArray<int>   indices;
-        SDL_FColor         color;
-        bool               is_editor;
+        SDL_FColor      color;
+        bool            is_editor;
     };
 
     void generate(float world_scale);
     void draw(GlRenderer& gl, const Camera& cam, const Light& light) const;
-
 private:
-    DynArray<WallQuad> walls_;
+    DynArray<WallQuad> _walls;
 };