changeset 76:35b1abc37969

Updating my website to use seobeo library.
author June Park <parkjune1995@gmail.com>
date Wed, 31 Dec 2025 14:11:21 -0800
parents ae6a88e6e484
children c348ac875294
files .claude/settings.local.json color_game/BUILD color_game/main.c dowa/BUILD mrjunejune/pages/base.css mrjunejune/pages/index.html mrjunejune/pages/index.js mrjunejune/pages/markdown_to_html/index.html mrjunejune/pages/parts/headers.html mrjunejune/pages/public/epi_favicon.svg mrjunejune/pages/react/index.html mrjunejune/pages/resume.css mrjunejune/pages/resume/index.html mrjunejune/pages/tools/index.css mrjunejune/pages/tools/index.html mrjunejune/pages/tools/markdown_to_html/index.css mrjunejune/pages/tools/markdown_to_html/index.html seobeo/s_web.c
diffstat 18 files changed, 604 insertions(+), 1404 deletions(-) [+]
line wrap: on
line diff
--- a/.claude/settings.local.json	Wed Dec 31 11:20:08 2025 -0800
+++ b/.claude/settings.local.json	Wed Dec 31 14:11:21 2025 -0800
@@ -2,7 +2,10 @@
   "permissions": {
     "allow": [
       "Bash(ls:*)",
-      "Bash(hg status:*)"
+      "Bash(hg status:*)",
+      "Bash(hg:*)",
+      "Bash(sqlite3:*)",
+      "Bash(bazel build:*)"
     ]
   }
 }
--- a/color_game/BUILD	Wed Dec 31 11:20:08 2025 -0800
+++ b/color_game/BUILD	Wed Dec 31 14:11:21 2025 -0800
@@ -5,6 +5,7 @@
   srcs = ["main.c"],
   deps = [
     "//third_party/raylib:raylib",
+    "//dowa:dowa_generic",
   ],
   static = True
 )
--- a/color_game/main.c	Wed Dec 31 11:20:08 2025 -0800
+++ b/color_game/main.c	Wed Dec 31 14:11:21 2025 -0800
@@ -1,913 +1,17 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <math.h>
 #include "third_party/raylib/include/raylib.h"
-
+#include "dowa/dowa.h"
 
 #define INIT_SCREEN_WIDTH 1200
 #define INIT_SCREEN_HEIGHT 700
-#define PLAYER_SPEED 200.0f
-#define PLAYER_RADIUS 20.0f
-#define PLAYER_MAX_HEALTH 100.0f
-
-#define MONSTER_SPEED 100.0f
-#define MONSTER_RADIUS 15.0f
-
-#define BULLET_SPEED 300.0f
-#define BULLET_RADIUS 5.0f
-#define BULLET_LIFETIME 3.0f
-#define SHOOT_INTERVAL 0.5f
-#define HIT_PROBABILITY 0.7f  // 70% aim accuracy (0.0 = random, 1.0 = perfect)
-                              
-#define MAX_PASSIVE_NODES 12
-#define PASSIVE_TREE_RADIUS 250.0f
-#define PASSIVE_NODE_RADIUS 20.0f
-
-#define MONSTER_WEIGHT 10.0f
-#define MONSTER_CONTACT_DAMAGE 5.0f
-
-#define BASE_DAMAGE 10.0f
-#define BOSS_HEALTH_MULTIPLIER 5.0f
-#define BOSS_MONSTER_WEIGHT 10.0f
-#define BOSS_SPAWN_TIME 10.0f
-
-#define COLOR_UNLOCK_TIME 5.0f
-#define MAX_DAMAGE_NUMBERS 100
-#define DAMAGE_NUMBER_LIFETIME 1.0f
-#define EXP_PER_MONSTER 10.0f
-#define EXP_PER_BOSS 50.0f
-#define EXP_TO_LEVEL 100.0f
-
-typedef struct Player {
-  Vector2 position;
-  float health;
-  float maxHealth;
-  float invulnerabilityTimer;
-  int unlockedBulletTypes[MAX_PASSIVE_NODES];
-  int unlockedCount;
-  int passivePoints;
-  int level;
-  float experience;
-  float expToNextLevel;
-  Color color;
-} Player;
-
-typedef struct DamageNumber {
-  Vector2 position;
-  float damage;
-  float lifetime;
-  bool active;
-  Color color;
-} DamageNumber;
-
-typedef struct Map {
-  float width;
-  float height;
-} Map;
-
-typedef struct Monster {
-  Vector2 position;
-  float hue;
-  float saturation;
-  float health;
-  float maxHealth;
-  float weight;
-  bool alive;
-  bool hasCollision;
-  bool isBoss;
-} Monster;
-
-typedef struct Bullet {
-  Vector2 position;
-  Vector2 velocity;
-  float lifetime;
-  float hue;
-  float damage;
-  bool active;
-} Bullet;
-
-typedef struct PassiveNode {
-  float angle;
-  float hue;
-  char description[64];
-  float damageBonus;
-  bool unlocked;
-} PassiveNode;
-
-float RandomFloat(float min, float max)
-{
-  return min + ((float)rand() / (float)RAND_MAX) * (max - min);
-}
-
-float CalculateColorDamage(float bulletHue, float monsterHue, float baseDamage)
-{
-  // Calculate hue difference (0-180 degrees)
-  float diff = fabs(bulletHue - monsterHue);
-  if (diff > 180.0f) diff = 360.0f - diff;
-
-  // Opposite colors (180 degrees) = 2x damage
-  // Same color (0 degrees) = 0.5x damage
-  // Linear scale between them
-  float damageMultiplier = 0.5f + (diff / 180.0f) * 1.5f;
-
-  return baseDamage * damageMultiplier;
-}
-
-void SpawnDamageNumber(DamageNumber* damageNumbers, int maxCount, Vector2 position, float damage, Color color)
-{
-  for (int i = 0; i < maxCount; i++)
-  {
-    if (!damageNumbers[i].active)
-    {
-      damageNumbers[i].position = position;
-      damageNumbers[i].damage = damage;
-      damageNumbers[i].lifetime = DAMAGE_NUMBER_LIFETIME;
-      damageNumbers[i].active = true;
-      damageNumbers[i].color = color;
-      break;
-    }
-  }
-}
-
-void UpdateDamageNumbers(DamageNumber* damageNumbers, int count, float deltaTime)
-{
-  for (int i = 0; i < count; i++)
-  {
-    if (!damageNumbers[i].active) continue;
-
-    damageNumbers[i].lifetime -= deltaTime;
-    damageNumbers[i].position.y -= 30.0f * deltaTime; // Float upward
-
-    if (damageNumbers[i].lifetime <= 0)
-    {
-      damageNumbers[i].active = false;
-    }
-  }
-}
-
-void AddExperience(Player* player, float exp)
-{
-  player->experience += exp;
-
-  // Check for level up
-  while (player->experience >= player->expToNextLevel)
-  {
-    player->experience -= player->expToNextLevel;
-    player->level++;
-    player->passivePoints++;
-    // Increase exp requirement for next level
-    player->expToNextLevel = EXP_TO_LEVEL * player->level;
-  }
-}
-
-void InitializePassiveTree(PassiveNode* nodes)
-{
-  const char* nodeDescriptions[MAX_PASSIVE_NODES] = {
-    "Red Bullet",      // 0°
-    "Orange Bullet",   // 30°
-    "Yellow Bullet",   // 60°
-    "Chartreuse",      // 90°
-    "Green Bullet",    // 120°
-    "Spring Green",    // 150°
-    "Cyan Bullet",     // 180°
-    "Azure Bullet",    // 210°
-    "Blue Bullet",     // 240°
-    "Violet Bullet",   // 270°
-    "Magenta Bullet",  // 300°
-    "Rose Bullet"      // 330°
-  };
-
-  for (int i = 0; i < MAX_PASSIVE_NODES; i++)
-  {
-    nodes[i].angle = (360.0f / MAX_PASSIVE_NODES) * i;
-    nodes[i].hue = nodes[i].angle;
-    snprintf(nodes[i].description, sizeof(nodes[i].description), "%s", nodeDescriptions[i]);
-    nodes[i].damageBonus = 5.0f + (i * 2.0f); // Scaling damage bonus
-    nodes[i].unlocked = false;
-  }
-}
-
-void HandlePlayerMovement(Player* player, Map* map, float deltaTime)
-{
-  Vector2 movement = {0};
-
-  if (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))    movement.y -= 1.0f;
-  if (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))  movement.y += 1.0f;
-  if (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))  movement.x -= 1.0f;
-  if (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT)) movement.x += 1.0f;
-
-  // Normalize diagonal movement
-  float length = sqrtf(movement.x * movement.x + movement.y * movement.y);
-  if (length > 0)
-  {
-    movement.x /= length;
-    movement.y /= length;
-  }
-
-  // Apply movement
-  player->position.x += movement.x * PLAYER_SPEED * deltaTime;
-  player->position.y += movement.y * PLAYER_SPEED * deltaTime;
-
-  // Keep player within map bounds
-  if (player->position.x < PLAYER_RADIUS) player->position.x = PLAYER_RADIUS;
-  if (player->position.x > map->width - PLAYER_RADIUS)
-    player->position.x = map->width - PLAYER_RADIUS;
-  if (player->position.y < PLAYER_RADIUS) player->position.y = PLAYER_RADIUS;
-  if (player->position.y > map->height - PLAYER_RADIUS)
-    player->position.y = map->height - PLAYER_RADIUS;
-}
-
-void UpdateMonsters(Monster* monsters, int monsterCount, Vector2 playerPos, float deltaTime)
-{
-  for (int i = 0; i < monsterCount; i++)
-  {
-    if (!monsters[i].alive) continue;
-
-    // Calculate direction to player
-    Vector2 direction = {
-      playerPos.x - monsters[i].position.x,
-      playerPos.y - monsters[i].position.y
-    };
-
-    // Normalize direction
-    float length = sqrtf(direction.x * direction.x + direction.y * direction.y);
-    if (length > 0)
-    {
-      direction.x /= length;
-      direction.y /= length;
-    }
-
-    // Move toward player
-    Vector2 newPos = {
-      monsters[i].position.x + direction.x * MONSTER_SPEED * deltaTime,
-      monsters[i].position.y + direction.y * MONSTER_SPEED * deltaTime
-    };
-
-    // Check collision with other monsters if this monster has collision enabled
-    if (monsters[i].hasCollision)
-    {
-      bool canMove = true;
-      for (int j = 0; j < monsterCount; j++)
-      {
-        if (i == j || !monsters[j].alive || !monsters[j].hasCollision) continue;
-
-        float dx = newPos.x - monsters[j].position.x;
-        float dy = newPos.y - monsters[j].position.y;
-        float distance = sqrtf(dx * dx + dy * dy);
-
-        if (distance < MONSTER_RADIUS * 2)
-        {
-          // If this monster is heavier, push the other monster away
-          if (monsters[i].weight > monsters[j].weight)
-          {
-            // Calculate push direction (away from current monster)
-            Vector2 pushDir = {dx, dy};
-            float pushLength = sqrtf(pushDir.x * pushDir.x + pushDir.y * pushDir.y);
-            if (pushLength > 0)
-            {
-              pushDir.x /= pushLength;
-              pushDir.y /= pushLength;
-            }
-
-            // Push the lighter monster away
-            float pushForce = (monsters[i].weight - monsters[j].weight) * MONSTER_SPEED * deltaTime * 0.5f;
-            monsters[j].position.x += pushDir.x * pushForce;
-            monsters[j].position.y += pushDir.y * pushForce;
-          }
-          else
-          {
-            // This monster is lighter or equal weight, can't move through
-            canMove = false;
-            break;
-          }
-        }
-      }
-
-      if (canMove)
-      {
-        monsters[i].position = newPos;
-      }
-    }
-    else
-    {
-      monsters[i].position = newPos;
-    }
-  }
-}
-
-void CheckPlayerMonsterCollision(Player* player, Monster* monsters, int monsterCount, float deltaTime)
-{
-  if (player->invulnerabilityTimer > 0)
-  {
-    player->invulnerabilityTimer -= deltaTime;
-    return;
-  }
-
-  for (int i = 0; i < monsterCount; i++)
-  {
-    if (!monsters[i].alive) continue;
-
-    float dx = player->position.x - monsters[i].position.x;
-    float dy = player->position.y - monsters[i].position.y;
-    float distance = sqrtf(dx * dx + dy * dy);
-
-    if (distance < PLAYER_RADIUS + MONSTER_RADIUS)
-    {
-      player->health -= MONSTER_CONTACT_DAMAGE;
-      player->invulnerabilityTimer = 0.5f; // 0.5 second invulnerability
-      if (player->health < 0) player->health = 0;
-      break;
-    }
-  }
-}
-
-void UpdateBullets(Bullet* bullets, int bulletCount, float deltaTime)
-{
-  for (int i = 0; i < bulletCount; i++)
-  {
-    if (!bullets[i].active) continue;
-
-    // Update position
-    bullets[i].position.x += bullets[i].velocity.x * deltaTime;
-    bullets[i].position.y += bullets[i].velocity.y * deltaTime;
-
-    // Update lifetime
-    bullets[i].lifetime -= deltaTime;
-    if (bullets[i].lifetime <= 0)
-    {
-      bullets[i].active = false;
-    }
-  }
-}
-
-int CheckCollisions(Bullet* bullets, int bulletCount, Monster* monsters, int monsterCount, Player* player, DamageNumber* damageNumbers)
-{
-  int bossesKilled = 0;
-
-  for (int i = 0; i < bulletCount; i++)
-  {
-    if (!bullets[i].active) continue;
-
-    for (int j = 0; j < monsterCount; j++)
-    {
-      if (!monsters[j].alive) continue;
-
-      // Check distance between bullet and monster
-      float dx = bullets[i].position.x - monsters[j].position.x;
-      float dy = bullets[i].position.y - monsters[j].position.y;
-      float distance = sqrtf(dx * dx + dy * dy);
-
-      if (distance < BULLET_RADIUS + MONSTER_RADIUS)
-      {
-        // Calculate damage based on color difference
-        float damage = CalculateColorDamage(bullets[i].hue, monsters[j].hue, bullets[i].damage);
-        monsters[j].health -= damage;
-
-        // Spawn damage number
-        Color damageColor = damage > bullets[i].damage * 1.5f ? ORANGE : WHITE;
-        SpawnDamageNumber(damageNumbers, MAX_DAMAGE_NUMBERS, monsters[j].position, damage, damageColor);
-
-        // Check if monster died
-        if (monsters[j].health <= 0)
-        {
-          monsters[j].alive = false;
-
-          // Award experience
-          float expGain = monsters[j].isBoss ? EXP_PER_BOSS : EXP_PER_MONSTER;
-          AddExperience(player, expGain);
-
-          // Award passive point if boss was killed
-          if (monsters[j].isBoss)
-          {
-            player->passivePoints++;
-            bossesKilled++;
-          }
-        }
-
-        bullets[i].active = false;
-        break;
-      }
-    }
-  }
-
-  return bossesKilled;
-}
-
-Vector2 FindNearestMonster(Monster* monsters, int monsterCount, Vector2 playerPos)
-{
-  float minDistance = INFINITY;
-  Vector2 nearestPos = playerPos;
-  bool found = false;
-
-  for (int i = 0; i < monsterCount; i++)
-  {
-    if (!monsters[i].alive) continue;
-
-    float dx = monsters[i].position.x - playerPos.x;
-    float dy = monsters[i].position.y - playerPos.y;
-    float distance = sqrtf(dx * dx + dy * dy);
-
-    if (distance < minDistance)
-    {
-      minDistance = distance;
-      nearestPos = monsters[i].position;
-      found = true;
-    }
-
-    if (found)
-      break;
-  }
-
-  if (!found)
-  {
-    // No monsters, return random direction
-    float angle = RandomFloat(0.0f, 2.0f * PI);
-    return (Vector2){
-      playerPos.x + cosf(angle) * 100.0f,
-      playerPos.y + sinf(angle) * 100.0f
-    };
-  }
-
-  return nearestPos;
-}
 
 int main()
 {
-  InitWindow(INIT_SCREEN_WIDTH, INIT_SCREEN_HEIGHT, "color game");
-  SetTargetFPS(60);
-  srand(time(NULL));
-
-  // --- All Global Variables ---
-  Map map = {
-    .width = INIT_SCREEN_WIDTH * 3,
-    .height = INIT_SCREEN_HEIGHT * 3
-  };
-
-  Player player = {
-    .position = {map.width / 2, map.height / 2},
-    .color = BLUE,
-    .health = PLAYER_MAX_HEALTH,
-    .maxHealth = PLAYER_MAX_HEALTH,
-    .unlockedCount = 0,
-    .passivePoints = 3,
-    .invulnerabilityTimer = 0.0f,
-    .level = 1,
-    .experience = 0.0f,
-    .expToNextLevel = EXP_TO_LEVEL
-  };
-
-  PassiveNode passiveTree[MAX_PASSIVE_NODES];
-  InitializePassiveTree(passiveTree);
-  passiveTree[0].unlocked = true;
-  player.unlockedBulletTypes[player.unlockedCount++] = 0;
-
-  // Initialize damage numbers array
-  DamageNumber damageNumbers[MAX_DAMAGE_NUMBERS];
-  for (int i = 0; i < MAX_DAMAGE_NUMBERS; i++)
-  {
-    damageNumbers[i].active = false;
-  }
-
-  Camera2D camera = {0};
-  camera.target = player.position;
-  camera.offset = (Vector2){INIT_SCREEN_WIDTH / 2.0f, INIT_SCREEN_HEIGHT / 2.0f};
-  camera.rotation = 0.0f;
-  camera.zoom = 1.0f;
-
-  bool menuOpen = false;
-  bool showMonsterColors = false;
-
-  Vector2 menuCenter = {
-    .x = INIT_SCREEN_WIDTH / 2,
-    .y = INIT_SCREEN_HEIGHT / 2
-  };
-
-  float gameTime = 0.0f;
-  float currentMonsterHue = 0.0f;
-  float currentMonsterSaturation = 0.0f; // Start grayscale
-  int bossesDefeated = 0;
-  float nextBossTime = BOSS_SPAWN_TIME;
-  bool colorUnlocked = false;
-
-  // Initialize monsters array
-  #define MAX_MONSTERS 1000
-  Monster live_monsters[MAX_MONSTERS];
-  int monsterCount = 0;
-
-  // Spawn initial monsters (grayscale)
-  for (int i = 0; i < 30; i++)
-  {
-    live_monsters[monsterCount++] = (Monster){
-      .position = {RandomFloat(0, map.width), RandomFloat(0, map.height)},
-      .hue = currentMonsterHue,
-      .saturation = currentMonsterSaturation,
-      .weight = MONSTER_WEIGHT,
-      .health = 50.0f,
-      .maxHealth = 50.0f,
-      .alive = true,
-      .hasCollision = true,
-      .isBoss = false
-    };
-  }
-
-  // Initialize bullets array
-  #define MAX_BULLETS 200
-  Bullet bullets[MAX_BULLETS];
-  int bulletCount = 0;
-  for (int i = 0; i < MAX_BULLETS; i++)
-  {
-    bullets[i].active = false;
-  }
-
-  // Shooting timer
-  float shootTimer = 0.0f;
-  float hitProbability = HIT_PROBABILITY;
-
-  while (!WindowShouldClose())
+  Vector2 pos = {.x=INIT_SCREEN_WIDTH/2, .y=INIT_SCREEN_HEIGHT/2};
+  InitWindow(INIT_SCREEN_WIDTH, INIT_SCREEN_HEIGHT, "Color Game");
+  while(!WindowShouldClose())
   {
-    float deltaTime = GetFrameTime();
-
-    if (IsKeyPressed(KEY_P))
-    {
-      menuOpen = !menuOpen;
-    }
-
-    if (IsKeyPressed(KEY_R))
-    {
-      showMonsterColors = !showMonsterColors;
-    }
-
-    // Handle passive tree menu interactions
-    if (menuOpen)
-    {
-      if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
-      {
-        Vector2 mousePos = GetMousePosition();
-
-        // Check if clicked on a passive node
-        for (int i = 0; i < MAX_PASSIVE_NODES; i++)
-        {
-          float angleRad = passiveTree[i].angle * DEG2RAD;
-          Vector2 nodePos = {
-            menuCenter.x + cosf(angleRad) * PASSIVE_TREE_RADIUS,
-            menuCenter.y + sinf(angleRad) * PASSIVE_TREE_RADIUS
-          };
-
-          float dx = mousePos.x - nodePos.x;
-          float dy = mousePos.y - nodePos.y;
-          float distance = sqrtf(dx * dx + dy * dy);
-
-          if (distance < PASSIVE_NODE_RADIUS && !passiveTree[i].unlocked && player.passivePoints > 0)
-          {
-            // Unlock the node
-            passiveTree[i].unlocked = true;
-            player.unlockedBulletTypes[player.unlockedCount++] = i;
-            player.passivePoints--;
-            break;
-          }
-        }
-      }
-    }
-
-    // Only handle game logic when menu is closed
-    if (!menuOpen)
-    {
-      // Update game time and progression
-      gameTime += deltaTime;
-
-      // Unlock color after COLOR_UNLOCK_TIME
-      if (!colorUnlocked && gameTime >= COLOR_UNLOCK_TIME)
-      {
-        colorUnlocked = true;
-        currentMonsterSaturation = 1.0f;
-      }
-
-      // Spawn boss and unlock new color
-      if (gameTime >= nextBossTime && monsterCount < MAX_MONSTERS)
-      {
-        // Generate new color for this boss cycle
-        currentMonsterHue = RandomFloat(0, 360);
-        nextBossTime += BOSS_SPAWN_TIME;
-
-        // Spawn boss monster
-        live_monsters[monsterCount++] = (Monster){
-          .position = {RandomFloat(0, map.width), RandomFloat(0, map.height)},
-          .hue = currentMonsterHue,
-          .saturation = currentMonsterSaturation,
-          .health = 50.0f * BOSS_HEALTH_MULTIPLIER,
-          .maxHealth = 50.0f * BOSS_HEALTH_MULTIPLIER,
-          .weight = BOSS_MONSTER_WEIGHT,
-          .alive = true,
-          .hasCollision = true,
-          .isBoss = true
-        };
-      }
-
-      // Spawn regular monsters periodically
-      static float monsterSpawnTimer = 0.0f;
-      monsterSpawnTimer += deltaTime;
-      if (monsterSpawnTimer >= 3.0f && monsterCount < MAX_MONSTERS)
-      {
-        monsterSpawnTimer = 0.0f;
-        live_monsters[monsterCount++] = (Monster){
-          .position = {RandomFloat(0, map.width), RandomFloat(0, map.height)},
-          .hue = currentMonsterHue,
-          .saturation = currentMonsterSaturation,
-          .health = 50.0f,
-          .maxHealth = 50.0f,
-          .alive = true,
-          .hasCollision = true,
-          .isBoss = false
-        };
-      }
-
-      HandlePlayerMovement(&player, &map, deltaTime);
-
-      // Check player-monster collision
-      CheckPlayerMonsterCollision(&player, live_monsters, monsterCount, deltaTime);
-
-      // Update shoot timer and shoot bullets
-      shootTimer += deltaTime;
-      if (shootTimer >= SHOOT_INTERVAL && player.unlockedCount > 0)
-      {
-        shootTimer = 0.0f;
-
-        // Find inactive bullet slot
-        for (int i = 0; i < MAX_BULLETS; i++)
-        {
-          if (!bullets[i].active)
-          {
-            // Choose random bullet type from unlocked types
-            int randomIndex = (int)RandomFloat(0, player.unlockedCount);
-            int nodeIndex = player.unlockedBulletTypes[randomIndex];
-            PassiveNode* selectedNode = &passiveTree[nodeIndex];
-
-            // Find nearest monster to aim at
-            Vector2 targetPos = FindNearestMonster(live_monsters, monsterCount, player.position);
-
-            // Calculate direction to target
-            Vector2 direction = {
-              targetPos.x - player.position.x,
-              targetPos.y - player.position.y
-            };
-
-            // Normalize
-            float length = sqrtf(direction.x * direction.x + direction.y * direction.y);
-            if (length > 0)
-            {
-              direction.x /= length;
-              direction.y /= length;
-            }
-
-            // Calculate base angle
-            float baseAngle = atan2f(direction.y, direction.x);
-
-            // Add random deviation based on accuracy
-            float maxDeviation = (1.0f - hitProbability) * PI;
-            float deviation = RandomFloat(-maxDeviation, maxDeviation);
-            float finalAngle = baseAngle + deviation;
-
-            bullets[i] = (Bullet){
-              .position = player.position,
-              .velocity = {
-                cosf(finalAngle) * BULLET_SPEED,
-                sinf(finalAngle) * BULLET_SPEED
-              },
-              .lifetime = BULLET_LIFETIME,
-              .hue = selectedNode->hue,
-              .damage = BASE_DAMAGE + selectedNode->damageBonus,
-              .active = true
-            };
-            break;
-          }
-        }
-      }
-
-      // Update monsters
-      UpdateMonsters(live_monsters, monsterCount, player.position, deltaTime);
-
-      // Update bullets
-      UpdateBullets(bullets, MAX_BULLETS, deltaTime);
-
-      // Update damage numbers
-      UpdateDamageNumbers(damageNumbers, MAX_DAMAGE_NUMBERS, deltaTime);
-
-      // Check collisions
-      int bossKills = CheckCollisions(bullets, MAX_BULLETS, live_monsters, monsterCount, &player, damageNumbers);
-      bossesDefeated += bossKills;
-
-      // Update camera to follow player
-      camera.target = player.position;
-
-      // Clamp camera to map edges
-      float minX = INIT_SCREEN_WIDTH / 2.0f;
-      float maxX = map.width - INIT_SCREEN_WIDTH / 2.0f;
-      float minY = INIT_SCREEN_HEIGHT / 2.0f;
-      float maxY = map.height - INIT_SCREEN_HEIGHT / 2.0f;
-
-      if (camera.target.x < minX) camera.target.x = minX;
-      if (camera.target.x > maxX) camera.target.x = maxX;
-      if (camera.target.y < minY) camera.target.y = minY;
-      if (camera.target.y > maxY) camera.target.y = maxY;
-    }
-
-    // --- Drawings ---
     BeginDrawing();
-      ClearBackground(RAYWHITE);
-
-      if (menuOpen)
-      {
-        // Draw color wheel background
-        for (int i = 0; i < 360; i++)
-        {
-          Color color = ColorFromHSV((float)i, 1.0f, 1.0f);
-          DrawCircleSector(
-            menuCenter,
-            PASSIVE_TREE_RADIUS + 30.0f,
-            (float)i,
-            (float)(i + 1),
-            1,
-            color
-          );
-        }
-
-        // Draw passive tree nodes
-        for (int i = 0; i < MAX_PASSIVE_NODES; i++)
-        {
-          float angleRad = passiveTree[i].angle * DEG2RAD;
-          Vector2 nodePos = {
-            menuCenter.x + cosf(angleRad) * PASSIVE_TREE_RADIUS,
-            menuCenter.y + sinf(angleRad) * PASSIVE_TREE_RADIUS
-          };
-
-          Color nodeColor = ColorFromHSV(passiveTree[i].hue, 1.0f, 1.0f);
-
-          if (passiveTree[i].unlocked)
-          {
-            // Draw unlocked node
-            DrawCircleV(nodePos, PASSIVE_NODE_RADIUS, nodeColor);
-            DrawCircleV(nodePos, PASSIVE_NODE_RADIUS - 3, WHITE);
-            DrawCircleV(nodePos, PASSIVE_NODE_RADIUS - 6, nodeColor);
-          }
-          else
-          {
-            // Draw locked node
-            DrawCircleV(nodePos, PASSIVE_NODE_RADIUS, DARKGRAY);
-            DrawCircleV(nodePos, PASSIVE_NODE_RADIUS - 3, GRAY);
-          }
-
-          // Draw node info on hover
-          Vector2 mousePos = GetMousePosition();
-          float dx = mousePos.x - nodePos.x;
-          float dy = mousePos.y - nodePos.y;
-          float distance = sqrtf(dx * dx + dy * dy);
-
-          if (distance < PASSIVE_NODE_RADIUS)
-          {
-            DrawText(passiveTree[i].description, nodePos.x - 50, nodePos.y - 40, 12, BLACK);
-            DrawText(TextFormat("+%.0f dmg", passiveTree[i].damageBonus), nodePos.x - 30, nodePos.y - 28, 10, DARKGRAY);
-          }
-        }
-
-        // Draw menu instructions
-        DrawText("PASSIVE TREE MENU", menuCenter.x - 100, menuCenter.y - 350, 20, BLACK);
-        DrawText("Click nodes to unlock bullet types", menuCenter.x - 120, menuCenter.y - 330, 16, DARKGRAY);
-        DrawText(TextFormat("Passive Points Available: %d", player.passivePoints), menuCenter.x - 120, menuCenter.y - 310, 16, PURPLE);
-        DrawText("Defeat bosses to earn passive points!", menuCenter.x - 120, menuCenter.y - 290, 14, DARKGRAY);
-        DrawText("Press P to close", menuCenter.x - 70, menuCenter.y + 320, 18, BLACK);
-      }
-      else
-      {
-        // Draw game world (with camera)
-        BeginMode2D(camera);
-
-          // Draw map background
-          DrawRectangle(0, 0, map.width, map.height, (Color){240, 240, 240, 255});
-
-          // Draw map border
-          DrawRectangleLinesEx((Rectangle){0, 0, map.width, map.height}, 5.0f, DARKGRAY);
-
-          // Draw grid to show map scale
-          for (int i = 0; i <= map.width; i += 100)
-          {
-            DrawLine(i, 0, i, map.height, LIGHTGRAY);
-          }
-          for (int i = 0; i <= map.height; i += 100)
-          {
-            DrawLine(0, i, map.width, i, LIGHTGRAY);
-          }
-
-          // Draw monsters
-          for (int i = 0; i < monsterCount; i++)
-          {
-            if (live_monsters[i].alive)
-            {
-              Color monsterColor = ColorFromHSV(live_monsters[i].hue, live_monsters[i].saturation, 1.0f);
-              float radius = live_monsters[i].isBoss ? MONSTER_RADIUS * 2 : MONSTER_RADIUS;
-
-              DrawCircleV(live_monsters[i].position, radius, monsterColor);
-
-              // Draw boss crown indicator
-              if (live_monsters[i].isBoss)
-              {
-                DrawCircleV(live_monsters[i].position, radius - 5, YELLOW);
-                DrawCircleV(live_monsters[i].position, radius - 10, monsterColor);
-              }
-
-              // Draw health bar
-              float healthPercent = live_monsters[i].health / live_monsters[i].maxHealth;
-              Vector2 barPos = {live_monsters[i].position.x - 15, live_monsters[i].position.y - radius - 10};
-              DrawRectangle(barPos.x, barPos.y, 30, 4, DARKGRAY);
-              DrawRectangle(barPos.x, barPos.y, 30 * healthPercent, 4, GREEN);
-
-              // Draw color label if toggle is on
-              if (showMonsterColors)
-              {
-                char colorText[32];
-                snprintf(colorText, sizeof(colorText), "H:%.0f S:%.1f", live_monsters[i].hue, live_monsters[i].saturation);
-                DrawText(colorText, live_monsters[i].position.x - 25, live_monsters[i].position.y + radius + 5, 10, BLACK);
-              }
-            }
-          }
-
-          // Draw bullets
-          for (int i = 0; i < MAX_BULLETS; i++)
-          {
-            if (bullets[i].active)
-            {
-              Color bulletColor = ColorFromHSV(bullets[i].hue, 1.0f, 1.0f);
-              DrawCircleV(bullets[i].position, BULLET_RADIUS, bulletColor);
-            }
-          }
-
-          // Draw damage numbers
-          for (int i = 0; i < MAX_DAMAGE_NUMBERS; i++)
-          {
-            if (damageNumbers[i].active)
-            {
-              printf("True\n");
-              float alpha = damageNumbers[i].lifetime / DAMAGE_NUMBER_LIFETIME;
-              Color color = damageNumbers[i].color;
-              color.a = (unsigned char)(255 * alpha);
-              DrawText(TextFormat("%.0f", damageNumbers[i].damage),
-                       damageNumbers[i].position.x - 10,
-                       damageNumbers[i].position.y,
-                       20,
-                       color);
-            }
-          }
-
-          // Draw player (flash when invulnerable)
-          if (player.invulnerabilityTimer <= 0 || ((int)(player.invulnerabilityTimer * 10) % 2 == 0))
-          {
-            DrawCircleV(player.position, PLAYER_RADIUS, player.color);
-          }
-
-        EndMode2D();
-
-        // Draw UI (screen space)
-        // Timer at top center
-        int minutes = (int)(gameTime / 60);
-        int seconds = (int)gameTime % 60;
-        DrawText(TextFormat("Time: %02d:%02d", minutes, seconds), INIT_SCREEN_WIDTH / 2 - 60, 10, 30, BLACK);
-
-        // Player health bar
-        float healthPercent = player.health / player.maxHealth;
-        DrawRectangle(10, 10, 200, 20, DARKGRAY);
-        DrawRectangle(10, 10, 200 * healthPercent, 20, RED);
-        DrawText(TextFormat("HP: %.0f/%.0f", player.health, player.maxHealth), 15, 12, 16, WHITE);
-
-        // Experience bar
-        float expPercent = player.experience / player.expToNextLevel;
-        DrawRectangle(10, 35, 200, 15, DARKGRAY);
-        DrawRectangle(10, 35, 200 * expPercent, 15, SKYBLUE);
-        DrawText(TextFormat("Lvl %d", player.level), 15, 36, 12, WHITE);
-        DrawText(TextFormat("%.0f/%.0f XP", player.experience, player.expToNextLevel), 70, 36, 12, WHITE);
-
-        // Passive points
-        DrawText(TextFormat("Passive Points: %d", player.passivePoints), 10, 55, 18, PURPLE);
-
-        // Controls
-        DrawText("WASD: Move | P: Menu | R: Toggle Colors", 10, 80, 16, DARKGRAY);
-
-        // Count alive monsters and bosses
-        int aliveCount = 0;
-        int bossCount = 0;
-        for (int i = 0; i < monsterCount; i++)
-        {
-          if (live_monsters[i].alive)
-          {
-            aliveCount++;
-            if (live_monsters[i].isBoss) bossCount++;
-          }
-        }
-        DrawText(TextFormat("Monsters: %d | Bosses: %d", aliveCount, bossCount), 10, 105, 18, DARKGRAY);
-        DrawText(TextFormat("Unlocked bullets: %d/%d", player.unlockedCount, MAX_PASSIVE_NODES), 10, 130, 16, DARKGRAY);
-        DrawText(TextFormat("Bosses defeated: %d", bossesDefeated), 10, 155, 16, DARKGRAY);
-      }
-
+      DrawPixelV(pos, RED);
     EndDrawing();
   }
-  return 0;
 }
--- a/dowa/BUILD	Wed Dec 31 11:20:08 2025 -0800
+++ b/dowa/BUILD	Wed Dec 31 14:11:21 2025 -0800
@@ -18,7 +18,6 @@
   hdrs = [
     "dowa.h",
     "dowa_internal.h",
-    "stb_ds.h"
   ],
   visibility = ["//visibility:public"],
 )
--- a/mrjunejune/pages/base.css	Wed Dec 31 11:20:08 2025 -0800
+++ b/mrjunejune/pages/base.css	Wed Dec 31 14:11:21 2025 -0800
@@ -208,3 +208,52 @@
   display: flex;
   justify-content: center;
 }
+
+/* Auto dark mode based on system preference */
+@media (prefers-color-scheme: dark) {
+  :root:not(.light-mode) {
+    --white: hsl(224, 10%, 10%);
+    --black: hsl(0, 0%, 90%);
+    --darkgray: #cccccc;
+    --lightgray: #666666;
+    --green: #3d1ea0;
+    --orange: #025ccc;
+    --purple: #2b5b06;
+    --red: #04bb7a;
+    --blue: #932f0e;
+    --darktext: #bebebe;
+    --awesome: #23cade;
+    --accent: #ffcc00;
+    --accent-dark: #ffb275;
+    --gray: 159, 140, 96;
+    --gray-light: 26, 22, 15;
+    --gray-dark: 221, 214, 198;
+    --gray-gradient: rgba(26, 22, 15, 50%), #000;
+    --box-shadow: 0 -2px -6px rgba(159, 140, 96, 25%),
+      0 -8px -24px rgba(159, 140, 96, 33%), 0 -16px -32px rgba(159, 140, 96, 33%);
+  }
+}
+
+/* Dark mode toggle button */
+.dark-mode-toggle {
+  position: fixed;
+  top: 20px;
+  left: 20px;
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+  background: var(--white);
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 20px;
+  transition: all 0.3s ease;
+  z-index: 1000;
+  box-shadow: 0 2px 8px rgba(0,0,0,0.2);
+}
+
+.dark-mode-toggle:hover {
+  transform: scale(1.1);
+  box-shadow: 0 4px 12px rgba(0,0,0,0.3);
+}
--- a/mrjunejune/pages/index.html	Wed Dec 31 11:20:08 2025 -0800
+++ b/mrjunejune/pages/index.html	Wed Dec 31 14:11:21 2025 -0800
@@ -1,243 +1,18 @@
 <!doctype html>
 <html lang="en">
   <head>
-    <BaseHead title="Resume" description="June's resume" />
     <link rel="stylesheet" href="base.css" />
     <link rel="stylesheet" href="resume.css" />
   </head>
   <body>
      <main>
        <div id="header"></div>
-       <div>
-         <p>Hi, my name is Juntae, but most people call me <b>June</b>.</p>
-         <p>I am a software engineer with experience spanning a wide range of companies, from small startups to FAANG. </p> 
-         <p>Feel free to check out my <a href="/resume.pdf"> resume </a> below, and if you're interested, don’t hesitate to contact me for contract work ranging from web/app development to embedded programming.</p>
-       </div>
-       <div class="info">
-         <p><span class="header-firstname-style"> JUNTAE </span><span class="header-lastname-style">PARK</span><p>
-         <p class="header-position-style"> FULL STACK DEVELOPER · SOFTWARE ENGINEER </p>
-         <div class="header-address-style"> 
-           Bay Area, CA, USA
-         </div>
-         <div class="header-social-style">
-           <p>📱(US) 650-531-1728 |📱(CA) 437-580-8026 | <a href="mailto:[email protected]"> ✉️  [email protected] </a>| <a href="https://github.com/mrjunejune"> 
-                <svg viewBox="0 0 16 16" aria-hidden="true" width="12" height="12">
-                  <path
-						fill="currentColor"
-						d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
-                        ></path>
-                </svg> mrjunejune
-           </a>| <a href="https://www.linkedin.com/in/junepark"> 
-              <svg width="12" height="12" fill="currentColor" class="bi bi-linkedin" viewBox="0 0 16 16">
-                <path d="M0 1.146C0 .513.526 0 1.175 0h13.65C15.474 0 16 .513 16 1.146v13.708c0 .633-.526 1.146-1.175 1.146H1.175C.526 16 0 15.487 0 14.854zm4.943 12.248V6.169H2.542v7.225zm-1.2-8.212c.837 0 1.358-.554 1.358-1.248-.015-.709-.52-1.248-1.342-1.248S2.4 3.226 2.4 3.934c0 .694.521 1.248 1.327 1.248zm4.908 8.212V9.359c0-.216.016-.432.08-.586.173-.431.568-.878 1.232-.878.869 0 1.216.662 1.216 1.634v3.865h2.401V9.25c0-2.22-1.184-3.252-2.764-3.252-1.274 0-1.845.7-2.165 1.193v.025h-.016l.016-.025V6.169h-2.4c.03.678 0 7.225 0 7.225z"/>
-              </svg>
-             junepark
-             </a>
-           </p>
-         </div>
-       </div>
-
-       <div class="sub-header">
-         <h2 class="section-style"> Summary </h2>
-         <div class="line"></div>
-       </div>
-       <p class="paragraph-style">Software Engineer with 8 years of hands-on experience across diverse tech stacks, from early-stage startups to FANG-scale systems. Adept in designing and delivering robust software solutions using modern languages, frameworks, and cloud platforms.  Open to impactful work.</p>
-       <div class="sub-header">
-
-         <h2 class="section-style"> Skills </h2>
-         <div class="line"></div>
-       </div>
-       <div class="paragraph-style">
-         <p>
-           <span class="entry-title-style"> Programming Languages:</span>
-           <span class="skill-type-style"> TypeScript, Python, C++/C, Ruby, Java, MATLAB </span>
-         </p>
-         <p> 
-           <span class="entry-title-style">Tools & Platforms:</span>
-           <span class="skill-type-style">Bazel, PostgresSQL, Mercurial, Git, Pands, Raylib, XCode</span>
-         </p>
-         <p>
-           <span class="entry-title-style"> Web Frameworks: </span>
-           <span class="skill-type-style"> Django, Rails, React, Flask</span>
-         </p>
-         <p>
-           <span class="entry-title-style"> DevOp:</span>
-           <span class="skill-type-style"> Plummi, Heroku, DigitalOcean, AWS, Google Cloud </span>
-         </p>
-         <p>
-           <span class="entry-title-style"> Language:</span>
-           <span class="skill-type-style"> English, Korean, Japanese </span>
-         </p>
-       </div>
-
-       <!-- Experiences -->
-      <div class="sub-header">
-         <h2 class="section-style"> Experience </h2>
-         <div class="line"></div>
-       </div>
-       <div class="flex-box">
-         <p class="entry-title-style">
-           <a href="https://www.meta.com/">Meta</a>
-         </p>
-         <p class="entry-location-style">San Francisco, CA, USA</p>
-       </div>
-       <div class="flex-box">
-         <p class="entry-position-style">SOFTWARE ENGINEER</p>
-         <p class="entry-date-style">Oct, 2024 - Present</p>
-       </div>
-         <ul class="description-style">
-           <li>
-             Took initiative on Channel Value Rule, targeting the 16% of ad traffic with both app and web destinations to improve value attribution and ROI.
-           </li>
-           <li>
-             Built full-stack features using React and Hack/GraphQL, contributing to scalable, production-ready systems.
-           </li>
-           <li>
-             Partnered with data science to design A/B tests and analyze revenue impact of ads destination.
-           </li>
-           <li>
-             Proposed and implemented alpha improvements to internal testing infrastructure, reducing test time by 50% and enhancing developer velocity.
-           </li>
-         </ul>
-       <div class="flex-box">
-         <p class="entry-title-style">
-           <a href="https://www.wmg.com/">Warner Music Group</a>
-         </p>
-         <p class="entry-location-style">Toronto, ON, Canada</p>
-       </div>
-       <div class="flex-box">
-         <p class="entry-position-style">TECHNICAL LEAD ENGINEER</p>
-         <p class="entry-date-style">July, 2023 - Sept, 2024</p>
-       </div>
-       <ul class="description-style">
-         <li>
-           Implements <a href="https://bazel.build/">bazel </a>structure for the company for TypeScript and JavaScript code base for hermiticity and stablishing standards for JavaScript and
-         </li>
-         <li>
-           TypeScript testing and code structures.
-         </li>
-         <li>
-           Led a team of five engineers in building GraphQL endpoints for client-facing applications using Apollo and AppSync, supporting over 2000 RPS and auto scaling depending on request values.
-         </li>
-         <li>
-           Improved application response times by up to 85% for graphQL response by updating database schema and SQL queries, eliminating N+1 queries and lack of indexes.
-         </li>
-         <li>
-           Developed CI/CD pipelines for backend structures.
-         </li>
-         <li>
-           Designed infrastructure for pub/sub, caching, and media processing logic.
-         </li>
+       <h1> Useful scripts </h1>
+       <ul>
+         <li> <a href="/resume"> Resume </a> </li>
+         <li> <a href="/tools"> Tools </a> </li>
        </ul>
-
-       <div class="flex-box">
-         <p class="entry-title-style">
-           <a href="https://www.google.com/">Google</a>
-         </p>
-         <p class="entry-location-style">Toronto, ON, Canada</p>
-       </div>
-       <div class="flex-box">
-         <p class="entry-position-style">SOFTWARE ENGINEER</p>
-         <p class="entry-date-style">Feb, 2022 - July 2023</p>
-       </div>
-       <ul class="description-style">
-         <li>
-           Implements and maintained new features relating to App Script across google workspace platform including Gmail, sheets, and Docs.</li>
-         <li>
-           Improved a response time and render time of App Script hover card components.</li>
-         <li>
-           Collaborated with a team of developers to ensure timely and accurate delivery of features.</li>
-         <li>
-           Conducted user testing and gathered feedback to iterate on features for optimal user experience.</li>
-       </ul>
-
-       <div class="flex-box">
-         <p class="entry-title-style">
-           <a href="https://www.everlywell.com/">Everlywell</a>
-         </p>
-         <p class="entry-location-style">Toronto, ON, Canada</p>
-       </div>
-       <div class="flex-box">
-         <p class="entry-position-style">SOFTWARE ENGINEER</p>
-         <p class="entry-date-style">December, 2020 - Jan, 2022</p>
-       </div>
-       <ul class="description-style">
-         <li>
-           Maintained Amazon amplify apps to create and deploy React web applications for companies such as <a href="https://brooklynnets.everlywell.com/">NBA</a>, <a href="https://tinder.everlywell.com/">Tinder</a>, and other companies for COVID-19 at-home test kits.</li>
-         <li>
-           Implemented a script that helps accurately access and refund unused covid test kits; helping company save up to 200,000 USD.</li>
-         <li>
-           Created several Rails controllers for internal purposes; mocking end to end user experience for QA, mass refund features for CX department, and more, ultimately reducing support tickets amount by 50 percent.</li>
-         <li>
-           Implemented an audit table to help debug problems and logged which process was responsible for the change of the record using PaperTrail gems</li>
-       </ul>
-
-       <div class="flex-box">
-         <p class="entry-title-style">
-           <a href="https://www.spiria.com/">Spiria</a>
-         </p>
-         <p class="entry-location-style">Oakville, ON, Canada</p>
-       </div>
-       <div class="flex-box">
-         <p class="entry-position-style">SOFTWARE ENGINEER</p>
-         <p class="entry-date-style">October, 2018 - October, 2020</p>
-       </div>
-       <ul class="description-style">
-         <li>
-           Constructed RESTful API endpoints in multiple different frameworks such as Django, Ruby on Rails, and Flask and automated API documentation process using swagger.
-         </li>
-         <li>
-           Designed custom rake tasks for importing production data into newly updated data structure to meet client's needs.
-         </li>
-         <li>
-           Maintained or updated staging/productions servers. Debugged problems in production postgres database using ssh and postgres console on Heroku or AWS servers
-         </li>
-         <li>
-           Collaborated in creating automation python scripts for websites and application using selenium covering for QA eliminating 80% of QA's manual work
-         </li>
-       </ul>
-
-       <div class="flex-box">
-         <p class="entry-title-style">
-           <a href="https://www.apexscore.ai/">Apex Score</a>
-         </p>
-         <p class="entry-location-style">Oakville, ON, Canada</p>
-       </div>
-       <div class="flex-box">
-         <p class="entry-position-style">SOFTWARE ENGINEER</p>
-         <p class="entry-date-style">September, 2019 - October, 2020</p>
-       </div>
-       <ul class="description-style">
-         <li>
-           Developed custom Shapley value regression model to calculate importance of independent variables of data sets using sklearn, pandas, and numpy.
-         </li>
-         <li>
-           Created custom image uploader to Amazon s3 bucket using boto3 library.
-         </li>
-         <li>
-           Built RESTful API application using Flask framework and automated extensive API documentation pages using flask-restplus, pytest, and swagger, covering 95% of the code base.
-         </li>
-         <li>
-           Created an interactive graph using D3.js in Vue.js with data from Flask backend API. 
-         </li>
-       </ul>
-
-      <div class="sub-header">
-         <h2 class="section-style"> Education </h2>
-         <div class="line"></div>
-       </div>
-       <div class="flex-box">
-         <p class="entry-title-style">
-           University of British Columbia
-         </p>
-         <p class="entry-location-style">Kelowna, British Columbia</p>
-       </div>
-       <div class="flex-box">
-         <p class="entry-position-style">BACHELOR OF SCIENCE IN PHYSICS</p>
-         <p class="entry-date-style">2014 - 2018</p>
-       </div>
-       <div id="footer"></div>
      </main>
   </body>
-  <script href="index.js"></script>
+  <script src="index.js"></script>
 </html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/pages/index.js	Wed Dec 31 14:11:21 2025 -0800
@@ -0,0 +1,62 @@
+const STORAGE_KEY = 'theme-preference';
+// Get stored preference or detect system preference
+function getThemePreference()
+{
+  const stored = localStorage.getItem(STORAGE_KEY);
+  if (stored) {
+    return stored;
+  }
+  return 'auto';
+}
+
+function applyTheme(preference)
+{
+  const root = document.documentElement;
+
+  if (preference === 'light') {
+    root.classList.add('light-mode');
+    root.classList.remove('dark');
+  } else if (preference === 'dark') {
+    root.classList.remove('light-mode');
+    root.classList.add('dark');
+  } else {
+    // Auto - remove manual overrides and let CSS media query handle it
+    root.classList.remove('light-mode', 'dark');
+  }
+}
+
+function populateHeader()
+{
+  fetch("/parts/headers.html")
+    .then(res => res.text())
+    .then(headerHTML => {
+      const range = document.createRange();
+      const fragment = range.createContextualFragment(headerHTML);
+      header.appendChild(fragment);
+      
+      const toggle = document.querySelector('#themeToggle');
+      if (!toggle) return;
+
+      toggle.addEventListener('click', function() {
+        let preference = getThemePreference();
+
+        // Cycle through: auto -> light -> dark -> auto
+        if (preference === 'auto') {
+          preference = 'light';
+        } else if (preference === 'light') {
+          preference = 'dark';
+        } else {
+          preference = 'auto';
+        }
+
+        localStorage.setItem(STORAGE_KEY, preference);
+        applyTheme(preference);
+      });
+    });
+}
+populateHeader();
+
+(function() {
+  const currentPreference = getThemePreference();
+  applyTheme(currentPreference);
+})();
--- a/mrjunejune/pages/markdown_to_html/index.html	Wed Dec 31 11:20:08 2025 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,257 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Markdown to HTML Converter</title>
-    <style>
-        * {
-            margin: 0;
-            padding: 0;
-            box-sizing: border-box;
-        }
-        
-        body {
-            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
-            line-height: 1.6;
-            padding: 20px;
-            max-width: 1200px;
-            margin: 0 auto;
-            background: #f5f5f5;
-        }
-        
-        .container {
-            display: grid;
-            grid-template-columns: 1fr 1fr;
-            gap: 20px;
-            margin-top: 20px;
-        }
-        
-        .panel {
-            background: white;
-            border-radius: 8px;
-            padding: 20px;
-            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
-        }
-        
-        h1 {
-            color: #333;
-            margin-bottom: 20px;
-        }
-        
-        textarea {
-            width: 100%;
-            height: 500px;
-            padding: 15px;
-            border: 1px solid #ddd;
-            border-radius: 4px;
-            font-family: 'Courier New', monospace;
-            font-size: 14px;
-            resize: vertical;
-        }
-        
-        #output {
-            min-height: 500px;
-            padding: 15px;
-            border: 1px solid #ddd;
-            border-radius: 4px;
-            background: #fafafa;
-        }
-        
-        #output h1, #output h2, #output h3, #output h4, #output h5, #output h6 {
-            margin: 20px 0 10px 0;
-            color: #333;
-        }
-        
-        #output h1 { font-size: 2em; }
-        #output h2 { font-size: 1.5em; }
-        #output h3 { font-size: 1.3em; }
-        
-        #output p {
-            margin: 10px 0;
-        }
-        
-        #output ul, #output ol {
-            margin: 10px 0;
-            padding-left: 30px;
-        }
-        
-        #output li {
-            margin: 5px 0;
-        }
-        
-        #output code {
-            background: #f4f4f4;
-            padding: 2px 6px;
-            border-radius: 3px;
-            font-family: 'Courier New', monospace;
-            font-size: 0.9em;
-        }
-        
-        #output pre {
-            background: #282c34;
-            color: #abb2bf;
-            padding: 15px;
-            border-radius: 4px;
-            overflow-x: auto;
-            margin: 10px 0;
-        }
-        
-        #output pre code {
-            background: none;
-            color: inherit;
-            padding: 0;
-        }
-        
-        #output blockquote {
-            border-left: 4px solid #ddd;
-            padding-left: 15px;
-            margin: 10px 0;
-            color: #666;
-            font-style: italic;
-        }
-        
-        #output a {
-            color: #0066cc;
-            text-decoration: none;
-        }
-        
-        #output a:hover {
-            text-decoration: underline;
-        }
-        
-        #output hr {
-            border: none;
-            border-top: 2px solid #ddd;
-            margin: 20px 0;
-        }
-        
-        #output img {
-            max-width: 100%;
-            height: auto;
-        }
-        
-        #output strong {
-            font-weight: bold;
-        }
-        
-        #output em {
-            font-style: italic;
-        }
-        
-        #output del {
-            text-decoration: line-through;
-        }
-        
-        button {
-            background: #0066cc;
-            color: white;
-            border: none;
-            padding: 10px 20px;
-            border-radius: 4px;
-            cursor: pointer;
-            font-size: 16px;
-            margin-top: 10px;
-        }
-        
-        button:hover {
-            background: #0052a3;
-        }
-        
-        .header {
-            text-align: center;
-            margin-bottom: 30px;
-        }
-        
-        .label {
-            font-weight: bold;
-            margin-bottom: 10px;
-            color: #555;
-        }
-        
-        @media (max-width: 768px) {
-            .container {
-                grid-template-columns: 1fr;
-            }
-        }
-    </style>
-</head>
-<body>
-    <div class="header">
-        <h1>Markdown to HTML Converter</h1>
-    </div>
-    
-    <div class="container">
-        <div class="panel">
-            <div class="label">Markdown Input</div>
-            <textarea id="input" placeholder="Type your markdown here..."># Welcome to Markdown Converter
-
-## Features
-
-This converter supports:
-
-- **Bold text** and *italic text*
-- [Links](https://example.com)
-- `inline code`
-- ~~strikethrough~~
-
-### Lists
-
-1. Ordered lists
-2. Like this one
-3. With numbers
-
-- Unordered lists
-- Use dashes
-- Or asterisks
-
-### Code Blocks
-
-```
-function example() {
-    return "Hello World";
-}
-```
-
-### Blockquotes
-
-> This is a blockquote
-> It can span multiple lines
-
----
-
-### Images
-
-![Alt text](https://via.placeholder.com/150)
-
-**Try editing this text!**</textarea>
-        </div>
-        
-        <div class="panel">
-            <div class="label">HTML Output</div>
-            <div id="output"></div>
-        </div>
-    </div>
-
-    <script src="/markdown_to_html.js"></script>
-    <script>
-        const input = document.getElementById('input');
-        const output = document.getElementById('output');
-        
-        function convert() {
-            // Clear previous output
-            output.innerHTML = '';
-            
-            // Convert and render
-            const markdown = input.value;
-            renderMarkdown(output, markdown);
-        }
-        
-        // Convert on input
-        input.addEventListener('input', convert);
-        
-        // Initial conversion
-        convert();
-    </script>
-</body>
-</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/pages/parts/headers.html	Wed Dec 31 14:11:21 2025 -0800
@@ -0,0 +1,103 @@
+<header>
+	<nav>
+      <h2 class="header-logo">
+        <div id="themeToggle">
+          <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_favicon.svg" height="50" width="50">
+        </div>
+        <a href="/">MrJuneJune</a>
+      </h2>
+		<div class="internal-links">
+			<a href="/resume">Resume</a>
+			<a href="/tools">Tools</a>
+    </div>
+    <div class="social-links">
+			<a href="https://github.com/mrjunejune" target="_blank">
+				<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32"
+					><path
+						fill="currentColor"
+						d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
+					></path></svg>
+			</a>
+		</div>
+	</nav>
+</header>
+<style>
+  :root {
+    --header-background: var(--white);
+    --header-color: rgb(var(--black));
+    --link-hover-accent: var(--awesome);
+  	--social-link-hover: rgb(var(--gray-dark));
+  }
+  .internal-links {
+    width: 33%;
+  }
+  .header-logo {
+    display: flex;
+    align-items: center;
+  }
+  header {
+  	margin: auto;
+  	padding: 0 1em;
+      background: var(--header-background);
+      color: rgb(var(--header-color));
+      font-family: "Atkinson", sans-serif;
+      box-shadow: 0 2px 8px rgba(var(--black), 5%);
+      width: 720px;
+      max-width: calc(100% - 2em);
+
+  }
+  h2 {
+  	margin: 0;
+  	font-size: 1em;
+  }
+  h2 a,
+  h2 a.active {
+  	text-decoration: none;
+  }
+  nav {
+  	display: flex;
+  	align-items: center;
+  	justify-content: space-between;
+  }
+  nav a {
+  	padding: 1em 0.5em;
+  	color: rgb(var(--header-color));
+  	border-bottom: 4px solid transparent;
+  	text-decoration: none;
+  }
+  nav a.active {
+  	text-decoration: none;
+  	border-bottom-color: var(--link-hover-accent);
+  }
+  .social-links,
+  .social-links a {
+    display: flex;
+  }
+  .social-links a {
+  	text-decoration: none;
+  }
+  .social-links a:hover {
+  	color: rgb(var(--social-link-hover));
+  }
+  @media (max-width: 720px) {
+  	.social-links {
+  		display: none;
+  	}
+  .internal-links {
+    width: 45%;
+  }
+  }
+  :global(.dark) img {
+    -webkit-filter: invert(1); /* safari 6.0 - 9.0 */
+            filter: invert(1);
+  }
+  #themeToggle {
+    background: var(--header-background);
+    display: flex;
+    align-items: center;
+    border-radius: 25px;
+    filter: invert(1);
+    cursor: pointer;
+  }
+</style>
+<script>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/pages/public/epi_favicon.svg	Wed Dec 31 14:11:21 2025 -0800
@@ -0,0 +1,1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M480 61.676c-65.352 3.638-138.815 25.227-196 57.6-11.861 6.715-27 16.474-27 17.405 0 .237 2.137 1.368 4.75 2.512 14.222 6.231 42.394 31.71 48.988 44.307 5.495 10.494 4.881 10.123 12.522 7.58 3.624-1.206 7.106-2.816 7.737-3.577 1.435-1.729-1.38-8.514-6.475-15.605l-3.469-4.829 6.723-1.972c32.582-9.556 57.173-11.806 78.724-7.203 11.98 2.559 26.998 6.779 30.323 8.521l2.402 1.259-2.934 3.413c-8.126 9.454-8.169 11.028-.366 13.39 7.393 2.239 8.071 1.964 11.901-4.822 9.554-16.93 30.233-34.024 46.145-38.148 25.494-6.606 44.109 5.197 49.67 31.493 2.418 11.433 2.48 36.884.12 49.5-2.313 12.364-9.761 30.151-9.761 23.31 0-.654-2.312-3.326-5.137-5.937l-5.138-4.748 1.787-6.812c2.295-8.75 3.114-29.676 1.528-39.076-2.24-13.282-10.827-26.014-17.793-26.382-14.95-.791-25.126 4.922-39.651 22.261-4.728 5.644-8.596 10.778-8.596 11.409 0 .631 2.601 1.87 5.781 2.754 11.487 3.194 38.237 29.194 52.698 51.221 21.515 32.772 26.077 48.378 23.968 82l-.69 11-7.628 3.737c-13.344 6.536-20.277 11.78-34.636 26.198-14.424 14.485-15.155 15.48-30.676 41.762-19.022 32.21-28.896 39.661-60.709 45.811-9.418 1.821-17.405 3.028-17.751 2.682-1.119-1.119 2.629-5.69 4.666-5.69 2.193 0 2.54-1.037.898-2.679-.594-.594-1.509-2.992-2.035-5.331-1.51-6.723.144-10.107 7.015-14.348 14.951-9.229 22.589-26.208 16.511-36.706-8.069-13.936-39.102-17.863-55.473-7.019-15.981 10.586-11.123 31.762 10.061 43.853 8.596 4.906 9.786 7.664 6.394 14.813-6.421 13.531-36.628 8.633-56.586-9.176-10.288-9.179-19.186-13.709-40.291-20.507-12.951-4.172-21.531-7.583-27-10.733-26.918-15.504-44.949-51.465-44.961-89.667-.007-19.581 1.256-25.168 9.493-42 14.633-29.902 21.691-40.528 36.886-55.539 14.879-14.698 22.735-20.004 34.908-23.575 2.649-.778-8.235-16.649-17.501-25.522-12.791-12.248-27.567-15.952-37.476-9.394-10.29 6.809-14.653 32.459-10.364 60.919 1.487 9.862 1.485 9.894-.756 12.101-1.235 1.217-3.996 5.034-6.136 8.482l-3.89 6.268-1.197-2.87c-7.317-17.536-9.437-29.656-9.513-54.37-.056-18.431.146-18.327-11.882-6.13-84.423 85.612-132.212 208.921-129.84 335.023.296 15.734 1.005 31.532 1.574 35.107.57 3.575 1.779 12.125 2.688 19 2.7 20.416 12.262 61.766 18.27 79 3.73 10.7 21.351 54.395 24.258 60.155 26.073 51.65 63.635 101.432 102.512 135.859 112.842 99.93 275.616 136.433 422.851 94.827 62.631-17.698 121.569-48.458 165.006-86.116 50.075-43.413 80.734-82.379 108.367-137.725 45.51-91.152 60.845-182.62 46.729-278.719-16.696-113.662-77.415-222.37-161.043-288.322C706.95 87.914 598.564 55.077 480 61.676m-77.5 110.218c-6.584 2.864-8.364 12.697-3.227 17.833 3.988 3.988 11.466 3.988 15.454 0 8.189-8.189-1.594-22.457-12.227-17.833m165.829 26.677c-.203 11.355-1.424 19.512-5.498 36.708-2.478 10.464-2.797 13.057-1.76 14.307.875 1.054.909 1.432.11 1.22-.637-.168-2.895 3.228-5.018 7.547l-3.859 7.852 2.89 5.337c5.631 10.396 9.539 27.43 10.499 45.755.537 10.264.825 11.713 2.212 11.146 3.369-1.376 8.023-2.561 20.095-5.118 43.382-9.186 83.883 1.289 121.631 31.459 11.243 8.986 13.709 11.685 15.424 16.881 1.432 4.341.557 5.382-6.483 7.707-8.671 2.864-10.496 2.386-19.229-5.047-43.361-36.902-91.568-43.282-140.752-18.629-67.455 33.813-93.641 110.336-64.599 188.782 5.456 14.739 6.129 18.181 3.258 16.673-.962-.505-3.775-1.25-6.25-1.655-6.359-1.04-11.233-3.772-15.157-8.496-4.07-4.9-7.561-6.227-23.485-8.925-28.18-4.774-55.527-4.348-49.808.776 1.397 1.252 63.877 22.527 78.45 26.714 6.875 1.975 16.1 4.674 20.5 5.997 7.561 2.274 24.123 5.969 40.245 8.98 14.427 2.694 30.448 3.61 43.444 2.484 24.412-2.117 52.155-8.086 62.565-13.462 2.61-1.348 7.221-3.414 10.246-4.592 13.501-5.253 24.785-12.554 40.53-26.222 9.041-7.849 13.959-8.917 20.565-4.465 6.319 4.258 4.503 8.16-10.39 22.333-23.864 22.71-45.207 34.015-82.705 43.807-29.153 7.613-68.643 7.834-100.888.563-28.457-6.416-84.426-24.209-106.004-33.699-1.112-.489-1.252.132-.696 3.093 1.948 10.384-2.174 10.406-17.938.094-19.536-12.779-33.427-48.046-29.029-73.702 1.062-6.201-1.052-7.241-10.636-5.232-39.021 8.18-73.803 42.13-92.366 90.159-12.866 33.29-9.733 76.033 8.672 118.299 3.233 7.425 5.88 14.881 5.882 16.568.005 5.298 2.835 12.898 6.741 18.109 29.585 39.467 32.761 42.629 56.923 56.671l11.162 6.486.665-5.732c1.026-8.841.945-8.811 11.621-4.277 12.007 5.1 17.126 6.521 34.891 9.686 29.65 5.283 31 5.891 31 13.965 0 8.837-4.178 11.009-23.34 12.133-27.862 1.636-7.167 11.636 31.764 15.349 32.58 3.108 83.367-.759 117.148-8.918 82.76-19.99 158.557-84.659 185.236-158.04 10.984-30.213 17.063-74.127 12.832-92.697-4.221-18.523-1.206-29.303 8.194-29.303 5.223 0 8.413 3.692 11.05 12.791 9.334 32.199 5.8 72.506-10.361 118.157l-1.574 4.449 2.275-2.049c1.252-1.127 6.389-5.483 11.417-9.68 38.033-31.752 64.557-87.69 69.446-146.457 3.768-45.289-9.505-111.996-30.729-154.445C792.625 323.3 739.829 268.802 683 236.013c-4.125-2.38-11.55-6.697-16.5-9.593-16.836-9.849-45.322-22.184-67.5-29.229-5.544-1.761-29.307-8.202-30.2-8.185-.165.003-.377 4.307-.471 9.565M270.972 314.533c-.699.843-1.491 3.818-1.76 6.611l-.488 5.077 6.888 6.497c13.291 12.536 34.395 19.184 50.345 15.858 13.558-2.827 18.262-7.612 14.848-15.104-1.933-4.242-5.639-5.538-13.991-4.891-13.667 1.059-29.042-3.054-41.44-11.085-7.275-4.713-12.121-5.71-14.402-2.963m211.1 2.104c-12.012 8.09-24.057 11.762-43.42 13.237-11.93.908-12.612 1.302-12.637 7.298-.02 4.825 5.865 10.12 13.205 11.881 14.461 3.469 39.169-4.646 52.03-17.088 6.892-6.668 7.393-9.516 2.677-15.237-3.815-4.629-5.101-4.639-11.855-.091m-291.247 56.806c-28.105 11.678-38.549 38.155-18.825 47.726 9.221 4.474 10.895 4.608 11.631.927.826-4.129 3.326-4.665 12.71-2.724 8.08 1.671 13.193 1.265 24.68-1.958 7.911-2.22 7.899-2.075.871-10.036-7.463-8.455-13.377-17.308-18.788-28.128-4.595-9.187-4.385-9.088-12.279-5.807m188.472 26.543c-9.263 1.969-10.571 4.869-3.797 8.413 7.789 4.076 26.631 1.35 28.269-4.09 1.049-3.483-15.047-6.326-24.472-4.323m-145.13 35.115c-39.091 8.207-58.828 22.807-58.448 43.234.164 8.829 2.685 12.802 11.368 17.915 8.627 5.081 11.312 5.069 10.448-.045-.767-4.536.648-8.715 4.216-12.455 5.397-5.657 12.663-2.586 16.98 7.178 3.07 6.944 3.428 7.16 10.769 6.523 3.3-.286 19.422-1.176 35.827-1.978 27.587-1.348 30.367-1.653 37-4.055 13.369-4.84 35.15-11.412 39.923-12.045 6.432-.854 6.052-1.471-3.281-5.325-7.398-3.054-15.515-8.422-31.639-20.925-5.548-4.302-8.527-5.557-29.538-12.454-26.218-8.604-28.048-8.838-43.625-5.568m223.113 27.317c-9.416 7.923-33.235 17.849-50.78 21.162-4.95.935-9.562 1.847-10.25 2.028-.773.204-1.263 2.153-1.285 5.111-.019 2.629-.679 7.706-1.465 11.281-1.448 6.584-2.013 20.154-.885 21.281.338.338 1.705-.1 3.038-.973 3.813-2.499 13.084-4.276 22.347-4.284 10.711-.01 43.521 3.944 46.136 5.56.816.504 1.006.228.589-.858-2.549-6.643-5.051-39.823-4.058-53.816.701-9.872.679-9.913-3.387-6.492" fill-rule="evenodd"/></svg>
\ No newline at end of file
--- a/mrjunejune/pages/react/index.html	Wed Dec 31 11:20:08 2025 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-<html lang="en">
-  <head>
-    <BaseHead title="Resume" description="June's resume" />
-  </head>
-  <body>
-    <div id="root"></div>
-    <p> Hello </p>
-  </body>
-  <script src="/hello.js"></script>
-</html>
--- a/mrjunejune/pages/resume.css	Wed Dec 31 11:20:08 2025 -0800
+++ b/mrjunejune/pages/resume.css	Wed Dec 31 14:11:21 2025 -0800
@@ -13,7 +13,7 @@
 
 .line {
   flex-grow: 1;
-  border-bottom: 1px solid #333; /* Adjust color and thickness as needed */
+  border-bottom: 1px solid var(--darkgray);
 }
 
 .header-firstname-style {
--- a/mrjunejune/pages/resume/index.html	Wed Dec 31 11:20:08 2025 -0800
+++ b/mrjunejune/pages/resume/index.html	Wed Dec 31 14:11:21 2025 -0800
@@ -6,6 +6,7 @@
     <link rel="stylesheet" href="resume.css" />
   </head>
   <body>
+    <button class="dark-mode-toggle" aria-label="Toggle dark mode">🔄</button>
     <main>
       <div class="info">
         <p><span class="header-firstname-style"> JUNTAE </span><span class="header-lastname-style">PARK</span><p>
@@ -232,6 +233,7 @@
       </div>
       <div id="footer"></div>
     </main>
+    <script src="/dark-mode.js"></script>
     <script href="index.js"></script>
   </body>
 </html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/pages/tools/index.css	Wed Dec 31 14:11:21 2025 -0800
@@ -0,0 +1,63 @@
+body {
+  line-height: 1.6;
+  padding: 20px;
+  max-width: 1200px;
+  margin: 0 auto;
+  background: rgb(var(--gray-light));
+}
+
+.title {
+  color: var(--darkgray);
+}
+
+.nav-list {
+  list-style: none;
+  padding: 0;
+}
+
+.nav-list li {
+  display: flex;
+  align-items: center;
+  margin-bottom: 20px;
+  cursor: pointer;
+  group; 
+}
+
+/* The Mini Sphere */
+.sphere {
+  width: 12px;
+  height: 12px;
+  border-radius: 50%;
+  margin-right: 15px;
+  background: var(--darkgray);
+  box-shadow: 0 0 10px rgba(68, 136, 255, 0.5);
+  transition: all 0.3s ease;
+  opacity: 0.5; /* Dim when not active */
+}
+
+/* The Text */
+.nav-list a {
+  color: var(--darkgray);
+  text-decoration: none;
+  font-size: 1.2rem;
+  transition: color 0.3s ease;
+}
+
+/* Hover Effects */
+.nav-list li:hover .sphere {
+  opacity: 1;
+  transform: scale(1.5);
+  box-shadow: 0 0 20px var(--awesome);
+  animation: pulse 1.5s infinite; /* Only pulses on hover */
+}
+
+.nav-list li:hover a {
+  color: var(--awesome);
+}
+
+/* Simple pulse animation for the list version */
+@keyframes pulse {
+  0% { box-shadow: 0 0 0 0 rgba(68, 136, 255, 0.7); }
+  70% { box-shadow: 0 0 0 10px rgba(68, 136, 255, 0); }
+  100% { box-shadow: 0 0 0 0 rgba(68, 136, 255, 0); }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/pages/tools/index.html	Wed Dec 31 14:11:21 2025 -0800
@@ -0,0 +1,18 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <link rel="stylesheet" href="/base.css" />
+    <link rel="stylesheet" href="/resume.css" />
+    <link rel="stylesheet" href="/tools/index.css" />
+  </head>
+  <body>
+     <div id="header"></div>
+     <main>
+       <h1 class="title"> Tools </h1>
+       <ul class="nav-list">
+         <li><div class="sphere"></div><a href="/tools/markdown_to_html">MarkDown to HTML</a></li>
+       </ul>
+     </main>
+  </body>
+  <script src="/index.js"></script>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/pages/tools/markdown_to_html/index.css	Wed Dec 31 14:11:21 2025 -0800
@@ -0,0 +1,183 @@
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+
+body {
+  line-height: 1.6;
+  padding: 20px;
+  max-width: 1200px;
+  margin: 0 auto;
+  background: rgb(var(--gray-light));
+}
+
+button {
+  background: var(--accent);
+  color: var(--white);
+  border: none;
+  padding: 10px 20px;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 16px;
+  margin-top: 10px;
+}
+
+.title button {
+  margin-top: 0px;
+}
+
+button:hover {
+  background: var(--accent-dark);
+}
+
+h1 {
+  color: var(--darkgray);
+  margin-bottom: 20px;
+}
+
+textarea {
+  width: 100%;
+  height: 500px;
+  padding: 15px;
+  border: 1px solid rgb(var(--gray-light));
+  border-radius: 4px;
+  font-family: 'Courier New', monospace;
+  font-size: 14px;
+  resize: vertical;
+}
+
+.container {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 20px;
+  margin-top: 20px;
+}
+
+.title {
+  display: grid;
+  place-items: center;
+  grid-template-columns: 1fr 1fr;
+  margin-bottom: 10px;
+}
+
+.panel {
+  background: var(--white);
+  border-radius: 8px;
+  padding: 20px;
+  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+}
+
+#output {
+  min-height: 500px;
+  padding: 15px;
+  border: 1px solid rgb(var(--gray-light));
+  border-radius: 4px;
+  background: rgb(var(--gray-light));
+}
+
+#output h1, #output h2, #output h3, #output h4, #output h5, #output h6 {
+  margin: 20px 0 10px 0;
+  color: var(--darkgray);
+}
+
+#output h1 { font-size: 2em; }
+#output h2 { font-size: 1.5em; }
+#output h3 { font-size: 1.3em; }
+
+#output p {
+  margin: 10px 0;
+}
+
+#output ul, #output ol {
+  margin: 10px 0;
+  padding-left: 30px;
+}
+
+#output li {
+  margin: 5px 0;
+}
+
+#output code {
+  background: rgb(var(--gray-light));
+  padding: 2px 6px;
+  border-radius: 3px;
+  font-family: 'Courier New', monospace;
+  font-size: 0.9em;
+}
+
+#output pre {
+  background: #282c34;
+  color: #abb2bf;
+  padding: 15px;
+  border-radius: 4px;
+  overflow-x: auto;
+  margin: 10px 0;
+}
+
+#output pre code {
+  background: none;
+  color: inherit;
+  padding: 0;
+}
+
+#output blockquote {
+  border-left: 4px solid rgb(var(--gray-light));
+  padding-left: 15px;
+  margin: 10px 0;
+  color: var(--gray);
+  font-style: italic;
+}
+
+#output a {
+  color: var(--accent);
+  text-decoration: none;
+}
+
+#output a:hover {
+  text-decoration: underline;
+}
+
+#output hr {
+  border: none;
+  border-top: 2px solid rgb(var(--gray-light));
+  margin: 20px 0;
+}
+
+#output img {
+  max-width: 100%;
+  height: auto;
+}
+
+#output strong {
+  font-weight: bold;
+}
+
+#output em {
+  font-style: italic;
+}
+
+#output del {
+  text-decoration: line-through;
+}
+
+.header {
+  text-align: center;
+  margin-bottom: 30px;
+}
+
+.label {
+  font-weight: bold;
+  margin-bottom: 10px;
+  color: var(--gray);
+}
+
+.title .label {
+  margin-bottom: 0px;
+}
+
+@media (max-width: 768px) {
+  .container {
+    grid-template-columns: 1fr;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/pages/tools/markdown_to_html/index.html	Wed Dec 31 14:11:21 2025 -0800
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Markdown to HTML Converter</title>
+    <link rel="stylesheet" href="/base.css" />
+    <link rel="stylesheet" href="markdown_to_html/index.css" />
+</head>
+<body>
+    <div id="header"></div>
+    <div class="header">
+        <h1>Markdown to HTML Converter</h1>
+    </div>
+    
+    <div class="container">
+        <div class="panel">
+            <div class="label">Markdown Input</div>
+            <textarea id="input" placeholder="Type your markdown here..."># Welcome to Markdown Converter
+
+## Features
+
+This converter supports:
+
+- **Bold text** and *italic text*
+- [Links](https://example.com)
+- `inline code`
+- ~~strikethrough~~
+
+### Lists
+
+1. Ordered lists
+2. Like this one
+3. With numbers
+
+- Unordered lists
+- Use dashes
+- Or asterisks
+
+### Code Blocks
+
+```
+function example() {
+    return "Hello World";
+}
+```
+
+### Blockquotes
+
+> This is a blockquote
+> It can span multiple lines
+
+---
+
+### Images
+
+![Alt text](https://via.placeholder.com/150)
+
+**Try editing this text!**</textarea>
+        </div>
+        
+        <div class="panel">
+            <div class="title">
+              <div class="label">HTML Output</div>
+              <button id="copy"> Copy </button>
+            </div>
+            <div id="output"></div>
+        </div>
+    </div>
+
+    <script src="/dark-mode.js"></script>
+    <script src="/markdown_to_html.js"></script>
+    <script>
+        function convert() {
+          output.innerHTML = '';
+          const markdown = input.value;
+          renderMarkdown(output, markdown);
+        }
+        input.addEventListener('input', convert);
+        
+        convert();
+
+        copy.addEventListener('click', () => {
+          const htmlBlob = new Blob([output.innerHTML], { type: 'text/html'});
+          const textBlob = new Blob([output.innerText], { type: 'text/plain'});
+          const data = [new ClipboardItem({
+            'text/html': htmlBlob,
+            'text/plain': textBlob
+          })];
+          navigator.clipboard.write(data).then(() => {
+            copy.textContent = "Copied!";
+            setTimeout(() => {
+              copy.textContent = "Copy";
+              copy.classList.remove('success');
+            }, 1000);
+          }).catch(err => {
+            console.error('Failed to copy: ', err);
+          });
+        });
+    </script>
+</body>
+<script src="/index.js"></script>
+</html>
--- a/seobeo/s_web.c	Wed Dec 31 11:20:08 2025 -0800
+++ b/seobeo/s_web.c	Wed Dec 31 14:11:21 2025 -0800
@@ -29,9 +29,9 @@
   );
 }
 
-// Load file from disk and cache it
 static char* Seobeo_Web_LoadFile(const char *file_path, size_t *p_file_size)
 {
+  printf("file_path: %s\n", file_path);
   char full_path[1024];
   snprintf(full_path, sizeof(full_path), "%s/%s", g_folder_path, file_path);
 
@@ -60,8 +60,9 @@
   return p_content;
 }
 
-void Seobeo_Web_Header_Generate(void *buffer, int status,
-                                       const char *content_type, const int content_length)
+void Seobeo_Web_Header_Generate(
+    void *buffer, int status,
+    const char *content_type, const int content_length)
 {
   const char *status_text;
   switch(status)