Javascript 迷路

index.html

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>My Maze</title>
</head>

<body>
    <canvas>
        Canvas not supported ...
    </canvas>

    <script src="js/main.js"></script>
</body>

</html>

main.js

'use strict';

(() => {
    class MazeRenderer {
        constructor(canvas) {
            this.ctx = canvas.getContext('2d');
            this.WALL_SIZE = 10;
        }

        render(data) {
            canvas.height = data.length * this.WALL_SIZE;
            canvas.width = data[0].length * this.WALL_SIZE;

            for (let row = 0; row < data.length; row++) {
                for (let col = 0; col < data[0].length; col++) {
                    if (data[row][col] === 1) {
                        this.ctx.fillRect(
                            col * this.WALL_SIZE,
                            row * this.WALL_SIZE,
                            this.WALL_SIZE,
                            this.WALL_SIZE
                        );
                    }
                }
            }
        }
    }

    class Maze {
        constructor(row, col, renderer) {
            if (row < 5 || col < 5 || row % 2 === 0 || col % 2 === 0) {
                alert('Size not valid!');
                return;
            }

            this.renderer = renderer;
            this.row = row;
            this.col = col;
            this.data = this.getData();
        }

        getData() {
            const data = [];

            for (let row = 0; row < this.row; row++) {
                data[row] = [];
                for (let col = 0; col < this.col; col++) {
                    data[row][col] = 1;
                }
            }

            for (let row = 1; row < this.row - 1; row++) {
                for (let col = 1; col < this.col - 1; col++) {
                    data[row][col] = 0;
                }
            }

            for (let row = 2; row < this.row - 2; row += 2) {
                for (let col = 2; col < this.col - 2; col += 2) {
                    data[row][col] = 1;
                }
            }

            for (let row = 2; row < this.row - 2; row += 2) {
                for (let col = 2; col < this.col - 2; col += 2) {
                    let destRow;
                    let destCol;

                    do {
                        const dir = row === 2 ?
                            Math.floor(Math.random() * 4) :
                            Math.floor(Math.random() * 3) + 1;
                        switch (dir) {
                            case 0: // up
                                destRow = row - 1;
                                destCol = col;
                                break;
                            case 1: // down
                                destRow = row + 1;
                                destCol = col;
                                break;
                            case 2: // left
                                destRow = row;
                                destCol = col - 1;
                                break;
                            case 3: // right
                                destRow = row;
                                destCol = col + 1;
                                break;
                        }
                    } while (data[destRow][destCol] === 1);

                    data[destRow][destCol] = 1;
                }
            }

            return data;
        }

        render() {
            this.renderer.render(this.data);
        }
    }

    const canvas = document.querySelector('canvas');
    if (typeof canvas.getContext === 'undefined') {
        return;
    }

    const maze = new Maze(21, 15, new MazeRenderer(canvas));
    maze.render();
})();

🎮 クロノクロス リメイク企画書(提案書)

🎮 クロノクロス リメイク企画書(提案書)

■ タイトル(仮)

CHRONO CROSS Re:Dreamers(クロノクロス リ・ドリーマーズ)


■ 開発目的

  • 名作『クロノクロス』(1999年/PS)の世界観・物語・音楽を継承しつつ、現代の技術と表現力でフルリメイク。
  • クロノシリーズの価値とブランドを再定義し、次世代のファンを獲得する。
  • クロノ・トリガーから続く「時」と「次元」をテーマにした壮大な物語を、新たな感動体験として再構築。

■ ターゲット層

  • 30〜40代:オリジナルファン(ノスタルジー層)
  • 10〜20代:JRPG・アニメ調ゲームに興味がある若年層
  • 世界市場向け:海外人気も高いため、グローバル対応必須(字幕・音声)

■ 主な特徴

項目内容
グラフィックUnreal Engine 5を使用したセルルック風3D
サウンド全曲アレンジ+原曲切替可能/フルオーケストラ対応
ボイス主要キャラクターにフルボイス対応(ON/OFF可)
UIモダン+クラシック切替可能なデザイン
バトルターン制+リアルタイム演出のハイブリッドバトル
クロス要素40人以上の仲間、選択によるマルチストーリー
新要素新規シナリオ分岐、外伝ストーリー、キャラエピソード

■ ストーリー概要(簡易)

夢を旅する少年セルジュが、もう一つの世界で自らの存在が「死んでいたこと」を知る。
交錯する次元、因果のねじれ、「時を喰らうもの」によって歪められた歴史を、仲間たちとともに解き明かす物語。
『クロノ・トリガー』との繋がりも明確に描かれ、真実のエンディングへ導かれる。


■ プラットフォーム案

  • PS5 / Xbox Series X|S / PC(Steam / Epic) / Nintendo Switch 2(次世代機を想定)
  • クラウド対応 / Steam Deck対応予定

■ 追加要素・リメイク特有要素(例)

種別内容
DLC対応クロノトリガーエピソード、旧キャラコスチュームなど
クロスセーブ複数プラットフォームでの共有セーブ
ギャラリーモードアート、BGM視聴、ボイス再生可能なコレクション
フォトモードアングル調整+フィルターありの撮影機能
難易度設定イージー〜クラシック(敵の強化・MP制限など)

■ 開発スケジュール案(例)

期間内容
Q1〜Q2プロトタイプ制作・初期アート制作
Q3〜Q4メイン開発・音楽収録・シナリオ検証
Q5〜Q6ベータ版、デバッグ、調整、プロモーション
Q7グローバルリリース(発売時期例:2027年冬)

■ 予算感(概算・中規模プロジェクト)

  • 総開発費:約15〜25億円(3年開発・UE5・全ボイス)
  • 人員:50〜70名体制(内外注含む)

■ 参考資料

  • クロノクロスHDリマスター(2022)
  • ファイナルファンタジーVII リメイク
  • ライブアライブHD-2D
  • ゼノブレイドシリーズ(シナリオ設計・多人数管理)

■ 最後に

クロノクロスは「ゲーム音楽」「次元の物語」「美しいドットと詩的なセリフ」で多くのファンの心に残る名作。
本リメイクは、単なる懐古主義ではなく、「再構築」と「夢の継承」をテーマに、今の時代に語り直すことを目指す。


必要であれば、PDF書式の企画書風に整えることも可能だし、ゲーム画面のモックアップ仲間キャラ一覧風資料も作れるよ!

もっと深く踏み込みたい部分ある?(キャラ紹介・UI案・音楽面とか)

4o

MySQL IF CASE

DROP TABLE IF EXISTS posts;
CREATE TABLE posts (
  id INT NOT NULL AUTO_INCREMENT,
  message VARCHAR(140),
  likes INT,
  area VARCHAR(20),
  PRIMARY KEY (id)
);

INSERT INTO posts (message, likes, area) VALUES
  ('post-1', 12, 'Tokyo'),
  ('post-2', 8, 'Fukuoka'),
  ('post-3', 11, 'Tokyo'),
  ('post-4', 3, 'Osaka'),
  ('post-5', 8, 'Tokyo'),
  ('post-6', 9, 'Osaka'),
  ('post-7', 4, 'Tokyo'),
  ('post-8', 10, 'Osaka'),
  ('post-9', 31, 'Fukuoka');

WEBOS

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>仮想OS Pro</title>
  <style>
    body {
      margin: 0;
      background: #2c3e50;
      font-family: 'Segoe UI', sans-serif;
      overflow: hidden;
    }
    #desktop {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 40px;
      background: linear-gradient(#2980b9, #34495e);
    }
    .icon {
      width: 70px;
      text-align: center;
      margin: 20px;
      cursor: pointer;
      color: white;
    }
    .window {
      position: absolute;
      width: 300px;
      height: 200px;
      background: white;
      border: 2px solid #555;
      display: none;
      box-shadow: 4px 4px 10px rgba(0,0,0,0.5);
    }
    .window-header {
      background: #3498db;
      padding: 5px;
      cursor: move;
      color: white;
    }
    .window-body {
      padding: 10px;
    }
    #taskbar {
      position: fixed;
      bottom: 0;
      left: 0;
      right: 0;
      height: 40px;
      background: #2c3e50;
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 0 10px;
      color: white;
    }
  </style>
</head>
<body onload="playStartupSound(); updateClock(); setInterval(updateClock, 1000);">

  <div id="desktop">
    <div class="icon" onclick="openWindow('memo')">📝<br>メモ帳</div>
    <div class="icon" onclick="openWindow('calc')">🧮<br>電卓</div>
  </div>

  <div id="taskbar">
    <div>仮想OS Pro</div>
    <div id="clock"></div>
  </div>

  <!-- メモ帳 -->
  <div class="window" id="memo">
    <div class="window-header" onmousedown="dragWindow(event, this.parentElement)">メモ帳</div>
    <div class="window-body">
      <textarea style="width: 100%; height: 100px;">メモを入力してください</textarea>
    </div>
  </div>

  <!-- 電卓 -->
  <div class="window" id="calc">
    <div class="window-header" onmousedown="dragWindow(event, this.parentElement)">電卓</div>
    <div class="window-body">
      <input type="text" id="calcDisplay" style="width:100%; font-size: 1.2em;" />
      <button onclick="calculate()">計算</button>
    </div>
  </div>

  <!-- 起動音 -->
  <audio id="bootSound" src="https://upload.wikimedia.org/wikipedia/commons/2/2f/Windows_95_startup.ogg" preload="auto"></audio>

  <script>
    function openWindow(id) {
      document.getElementById(id).style.display = 'block';
    }

    function dragWindow(e, el) {
      e.preventDefault();
      let offsetX = e.clientX - el.offsetLeft;
      let offsetY = e.clientY - el.offsetTop;

      function move(e) {
        el.style.left = (e.clientX - offsetX) + 'px';
        el.style.top = (e.clientY - offsetY) + 'px';
      }

      function stop() {
        document.removeEventListener('mousemove', move);
        document.removeEventListener('mouseup', stop);
      }

      document.addEventListener('mousemove', move);
      document.addEventListener('mouseup', stop);
    }

    function calculate() {
      let result;
      try {
        result = eval(document.getElementById('calcDisplay').value);
      } catch {
        result = "エラー";
      }
      document.getElementById('calcDisplay').value = result;
    }

    function playStartupSound() {
      document.getElementById("bootSound").play();
    }

    function updateClock() {
      const now = new Date();
      const time = now.toLocaleTimeString();
      document.getElementById("clock").textContent = time;
    }
  </script>
</body>
</html>

Twitter風サイト

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Twitter風サイト(高度拡張版)</title>
  <style>
    /* 全体設定 */
    body {
      margin: 0;
      padding: 0;
      font-family: sans-serif;
      background-color: #f5f8fa;
    }

    header {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 50px;
      background-color: #1da1f2;
      display: flex;
      align-items: center;
      padding: 0 20px;
      color: #fff;
      font-size: 20px;
      font-weight: bold;
      box-sizing: border-box;
      z-index: 10;
    }

    /* レイアウト用コンテナ */
    .container {
      display: flex;
      width: 100%;
      max-width: 1200px;
      margin: 60px auto 0; /* ヘッダー分だけ下に余白をとる */
      box-sizing: border-box;
    }

    /* 左サイドバー(ナビゲーション) */
    .sidebar {
      width: 20%;
      max-width: 200px;
      padding: 10px;
      box-sizing: border-box;
    }

    .nav-item {
      margin: 10px 0;
      font-size: 18px;
    }

    .nav-item a {
      text-decoration: none;
      color: #1da1f2;
      cursor: pointer;
    }

    .profile-settings {
      margin-top: 20px;
      padding: 10px;
      background-color: #fff;
      border: 1px solid #e6ecf0;
      border-radius: 5px;
    }

    .profile-settings input {
      width: 100%;
      margin-bottom: 5px;
      font-size: 14px;
      padding: 5px;
      box-sizing: border-box;
    }

    .profile-settings button {
      border: none;
      background-color: #1da1f2;
      color: #fff;
      font-size: 14px;
      padding: 5px 10px;
      border-radius: 4px;
      cursor: pointer;
    }

    /* メインタイムライン部分 */
    .feed {
      width: 60%;
      padding: 10px;
      box-sizing: border-box;
    }

    .tweet-box {
      background-color: #fff;
      border: 1px solid #e6ecf0;
      border-radius: 5px;
      padding: 10px;
      margin-bottom: 20px;
    }

    .tweet-box textarea {
      width: 100%;
      border: none;
      resize: none;
      font-size: 16px;
      outline: none;
      box-sizing: border-box;
    }

    .tweet-stats {
      display: flex;
      justify-content: space-between;
      margin-top: 5px;
      font-size: 14px;
    }

    .tweet-stats .char-count {
      color: #657786;
    }

    .tweet-stats .error {
      color: red;
    }

    .tweet-box .attach-label {
      display: inline-block;
      margin-top: 5px;
      font-size: 14px;
      color: #657786;
    }

    .tweet-box button {
      margin-top: 10px;
      padding: 8px 16px;
      border: none;
      background-color: #1da1f2;
      color: #fff;
      font-size: 16px;
      border-radius: 5px;
      cursor: pointer;
    }

    .tweet {
      background-color: #fff;
      border: 1px solid #e6ecf0;
      border-radius: 5px;
      padding: 10px;
      margin-bottom: 10px;
    }

    .tweet-header {
      display: flex;
      align-items: center;
      margin-bottom: 5px;
      flex-wrap: wrap;
    }

    .tweet-header img.user-icon {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      margin-right: 10px;
    }

    .tweet-header .name {
      font-weight: bold;
      margin-right: 5px;
    }

    .tweet-header .username {
      color: #657786;
      font-size: 14px;
      margin-right: 5px;
    }

    .tweet-time {
      font-size: 12px;
      color: #657786;
    }

    .tweet-content {
      font-size: 16px;
      margin: 10px 0;
      white-space: pre-wrap; /* 改行を保持 */
    }

    .tweet-content img.attached-image {
      max-width: 100%;
      display: block;
      margin-top: 5px;
      border: 1px solid #ccc;
    }

    .tweet-footer {
      display: flex;
      justify-content: flex-start;
      gap: 15px;
      margin-top: 10px;
      flex-wrap: wrap;
    }

    .tweet-footer button {
      background: none;
      border: none;
      cursor: pointer;
      font-size: 14px;
      color: #657786;
      display: flex;
      align-items: center;
      gap: 5px;
    }

    .tweet .replies-container {
      margin-top: 10px;
      border-left: 2px solid #e6ecf0;
      padding-left: 10px;
    }

    /* リプライの更なる階層は少しずつ左にずらす */
    .nested-reply {
      margin-left: 20px;
    }

    /* 右サイドバー(ウィジェット) */
    .widgets {
      width: 20%;
      max-width: 250px;
      padding: 10px;
      box-sizing: border-box;
    }

    .search-box {
      background-color: #fff;
      border-radius: 20px;
      padding: 8px 15px;
      margin-bottom: 20px;
      border: 1px solid #e6ecf0;
      display: flex;
      align-items: center;
    }

    .search-box input {
      border: none;
      outline: none;
      width: 100%;
      font-size: 16px;
    }

    .trends {
      background-color: #fff;
      border: 1px solid #e6ecf0;
      border-radius: 5px;
      padding: 10px;
    }

    .trends h3 {
      margin-top: 0;
    }

    .trend-item {
      margin-bottom: 10px;
      font-size: 14px;
    }

    /* リンク風のテキストデザイン */
    .hashtag,
    .mention {
      color: #1da1f2;
      text-decoration: none;
      cursor: pointer;
    }

    .hashtag:hover,
    .mention:hover {
      text-decoration: underline;
    }

    /* 折りたたみ表示のボタン */
    .toggle-replies-btn {
      background: none;
      color: #1da1f2;
      border: none;
      cursor: pointer;
      font-size: 14px;
      margin-top: 5px;
      padding: 0;
    }
  </style>
</head>
<body>
  <!-- ヘッダー -->
  <header>
    Twitter風サイト(高度拡張版)
  </header>

  <!-- メインコンテンツを左右に分けるコンテナ -->
  <div class="container">

    <!-- 左サイドバー -->
    <aside class="sidebar">
      <div class="nav-item"><a href="#">ホーム</a></div>
      <div class="nav-item"><a href="#">通知</a></div>
      <div class="nav-item"><a href="#">設定</a></div>

      <!-- 簡易プロフィール設定 -->
      <div class="profile-settings">
        <label for="displayName">名前</label>
        <input type="text" id="displayName" placeholder="あなたの表示名">
        <label for="userName">ユーザー名(@なしで)</label>
        <input type="text" id="userName" placeholder="myAccount">
        <button id="saveProfileBtn">保存</button>
      </div>
    </aside>

    <!-- タイムライン部分 -->
    <main class="feed">
      <!-- 新規ツイート入力フォーム -->
      <div class="tweet-box">
        <textarea rows="3" placeholder="いまどうしてる? (140文字まで)"></textarea>
        <div class="tweet-stats">
          <span class="char-count">0 / 140</span>
          <span class="error"></span>
        </div>
        <label class="attach-label">
          画像を添付:
          <input type="file" class="attach-input" accept="image/*">
        </label>
        <button class="tweet-submit-btn">ツイート</button>
      </div>
    </main>

    <!-- 右サイドバー -->
    <aside class="widgets">
      <!-- 検索ボックス -->
      <div class="search-box">
        <input type="text" placeholder="キーワード検索">
      </div>

      <!-- トレンド表示 -->
      <div class="trends">
        <h3>今どうしてる?</h3>
        <div class="trend-item">#春の訪れ</div>
        <div class="trend-item">#お花見</div>
        <div class="trend-item">#新年度</div>
      </div>
    </aside>
  </div>

  <script>
    // =======================
    //      定数・変数設定
    // =======================
    const TWEET_MAX_LENGTH = 140;
    const feedContainer = document.querySelector('.feed');
    const tweetTextarea = document.querySelector('.tweet-box textarea');
    const tweetButton = document.querySelector('.tweet-submit-btn');
    const charCountEl = document.querySelector('.char-count');
    const errorEl = document.querySelector('.error');
    const attachInput = document.querySelector('.attach-input');

    const displayNameInput = document.getElementById('displayName');
    const userNameInput = document.getElementById('userName');
    const saveProfileBtn = document.getElementById('saveProfileBtn');

    // LocalStorageからツイート一覧を読み込み(なければ空配列)
    let tweets = JSON.parse(localStorage.getItem('tweets-advanced') || '[]');

    // ユーザープロフィール情報をLocalStorageから読み込み
    let userProfile = JSON.parse(localStorage.getItem('userProfile-advanced') || '{}');
    let currentName = userProfile.displayName || 'あなた';
    let currentUserName = userProfile.userName || 'myAccount';

    // フォームに反映
    displayNameInput.value = currentName;
    userNameInput.value = currentUserName;

    // =======================
    //   画像ファイル取得用
    // =======================
    let attachedImageBase64 = null;
    attachInput.addEventListener('change', (e) => {
      const file = e.target.files[0];
      if(!file) {
        attachedImageBase64 = null;
        return;
      }
      const reader = new FileReader();
      reader.onload = () => {
        attachedImageBase64 = reader.result;  // base64データ
      };
      reader.readAsDataURL(file);
    });

    // =======================
    //   スレッド実装のためのツイート構造
    // =======================
    // tweet = {
    //   id: string (一意のID),
    //   name: string,
    //   userName: string,
    //   content: string,
    //   time: number (Date.now()),
    //   likes: number,
    //   retweets: number,
    //   image: string (base64) | null,
    //   replies: array of same structure
    // }

    // =======================
    //       ユーティリティ
    // =======================
    function escapeHtml(str) {
      return str
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#39;");
    }

    // ハッシュタグ/メンションのリンク化
    function linkify(text) {
      let escaped = escapeHtml(text);
      escaped = escaped.replace(/#(\w+)/g, `<a href="#" class="hashtag">#$1</a>`);
      escaped = escaped.replace(/@(\w+)/g, `<a href="#" class="mention">@$1</a>`);
      return escaped;
    }

    // ツイートをLocalStorageに保存
    function updateLocalStorage() {
      localStorage.setItem('tweets-advanced', JSON.stringify(tweets));
    }

    // 親ツイートまたはリプライ先を検索するための再帰関数
    function findTweetById(tweetArray, tweetId) {
      for (const tw of tweetArray) {
        if (tw.id === tweetId) {
          return tw;
        }
        const childFound = findTweetById(tw.replies, tweetId);
        if (childFound) {
          return childFound;
        }
      }
      return null;
    }

    // 新しいツイートを作成 & tweets配列に登録
    // parentIdが指定されたら、そのツイートのrepliesに追加する
    function createNewTweet(content, parentId = null, imageBase64 = null) {
      const newTweet = {
        id: 'tw-' + Date.now() + '-' + Math.floor(Math.random() * 10000),
        name: currentName,
        userName: currentUserName,
        content: content,
        time: Date.now(),
        likes: 0,
        retweets: 0,
        image: imageBase64,
        replies: []
      };
      if (parentId) {
        const parentTweet = findTweetById(tweets, parentId);
        if (parentTweet) {
          parentTweet.replies.unshift(newTweet);
        }
      } else {
        tweets.unshift(newTweet);
      }
      updateLocalStorage();
      renderTweets();
    }

    // =======================
    //      ツイート描画
    // =======================

    // ツイート1件を生成するDOM要素を返す(返信分も含め再帰的に生成)
    // depth: スレッドの深さに応じて左マージンなどを調整したいときに利用
    function createTweetElement(tweet, depth = 0) {
      const tweetDiv = document.createElement('div');
      tweetDiv.classList.add('tweet');
      if (depth >= 1) {
        // 2段目以降のリプライならclassで左にずらす
        tweetDiv.classList.add('nested-reply');
      }

      // 日付文字列
      const timeString = new Date(tweet.time).toLocaleString('ja-JP', {
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit'
      });

      // 本文のリンク化
      const contentHtml = linkify(tweet.content);

      // 画像がある場合
      const imageHtml = tweet.image
        ? `<img src="${tweet.image}" alt="Attached Image" class="attached-image" />`
        : '';

      // 自分のツイートなら削除ボタンを表示
      const isMyTweet = (tweet.name === currentName && tweet.userName === currentUserName);
      const deleteBtnHtml = isMyTweet
        ? `<button class="delete-btn">削除</button>`
        : '';

      tweetDiv.innerHTML = `
        <div class="tweet-header">
          <img src="https://via.placeholder.com/40" alt="User Icon" class="user-icon" />
          <span class="name">${escapeHtml(tweet.name)}</span>
          <span class="username">@${escapeHtml(tweet.userName)}</span>
          <span class="tweet-time">- ${timeString}</span>
        </div>
        <div class="tweet-content">
          ${contentHtml}
          ${imageHtml}
        </div>
        <div class="tweet-footer">
          <button class="like-btn">
            <span>いいね</span>
            <span class="like-count">${tweet.likes}</span>
          </button>
          <button class="retweet-btn">
            <span>リツイート</span>
            <span class="retweet-count">${tweet.retweets}</span>
          </button>
          <button class="reply-btn">返信</button>
          ${deleteBtnHtml}
        </div>
      `;

      // ---------- 返信フォーム & スレッド表示 ----------
      // 返信コンテナ(折りたたみ対象)
      const repliesContainer = document.createElement('div');
      repliesContainer.classList.add('replies-container');

      // 返信がある場合、表示/非表示を切り替えるボタンを設置
      if (tweet.replies && tweet.replies.length > 0) {
        const toggleRepliesBtn = document.createElement('button');
        toggleRepliesBtn.classList.add('toggle-replies-btn');
        toggleRepliesBtn.textContent = `返信を表示 (${tweet.replies.length})`;
        tweetDiv.appendChild(toggleRepliesBtn);

        // 折りたたみ状態管理
        let isRepliesOpen = false;
        toggleRepliesBtn.addEventListener('click', () => {
          isRepliesOpen = !isRepliesOpen;
          toggleRepliesBtn.textContent = isRepliesOpen
            ? `返信を非表示`
            : `返信を表示 (${tweet.replies.length})`;
          repliesContainer.style.display = isRepliesOpen ? 'block' : 'none';
        });
      }

      // 返信フォーム
      const replyForm = document.createElement('div');
      replyForm.style.marginTop = '5px';
      replyForm.innerHTML = `
        <textarea rows="2" placeholder="返信を入力..." style="width: 100%; font-size:14px;"></textarea>
        <button class="reply-submit-btn" style="margin-top:5px;">返信を投稿</button>
      `;
      replyForm.style.display = 'none'; // デフォルトは非表示
      tweetDiv.appendChild(replyForm);

      // スレッド(返信)の再帰描画
      tweet.replies.forEach(replyTweet => {
        const replyEl = createTweetElement(replyTweet, depth + 1);
        repliesContainer.appendChild(replyEl);
      });
      repliesContainer.style.display = 'none'; // 最初は折りたたみ
      tweetDiv.appendChild(repliesContainer);

      // ========== 各種ボタンイベント ==========
      // いいね
      const likeBtn = tweetDiv.querySelector('.like-btn');
      const likeCountEl = tweetDiv.querySelector('.like-count');
      likeBtn.addEventListener('click', () => {
        tweet.likes++;
        updateLocalStorage();
        likeCountEl.textContent = tweet.likes;
      });

      // リツイート
      const retweetBtn = tweetDiv.querySelector('.retweet-btn');
      const retweetCountEl = tweetDiv.querySelector('.retweet-count');
      retweetBtn.addEventListener('click', () => {
        tweet.retweets++;
        updateLocalStorage();
        retweetCountEl.textContent = tweet.retweets;
      });

      // 返信ボタン -> フォーム表示/非表示
      const replyBtn = tweetDiv.querySelector('.reply-btn');
      replyBtn.addEventListener('click', () => {
        replyForm.style.display = (replyForm.style.display === 'none') ? 'block' : 'none';
      });

      // 返信投稿
      const replySubmitBtn = replyForm.querySelector('.reply-submit-btn');
      const replyTextarea = replyForm.querySelector('textarea');
      replySubmitBtn.addEventListener('click', () => {
        const replyText = replyTextarea.value.trim();
        if (replyText === '' || replyText.length > TWEET_MAX_LENGTH) {
          return;
        }
        // 新規リプライ作成
        createNewTweet(replyText, tweet.id);
        replyTextarea.value = '';
      });

      // 削除
      if (isMyTweet) {
        const deleteBtn = tweetDiv.querySelector('.delete-btn');
        deleteBtn.addEventListener('click', () => {
          // 再帰的に探して削除
          removeTweetById(tweets, tweet.id);
          updateLocalStorage();
          renderTweets();
        });
      }

      return tweetDiv;
    }

    // ツイート削除(再帰)
    function removeTweetById(tweetArray, tweetId) {
      for (let i = 0; i < tweetArray.length; i++) {
        if (tweetArray[i].id === tweetId) {
          tweetArray.splice(i, 1);
          return true;
        }
        if (removeTweetById(tweetArray[i].replies, tweetId)) {
          return true;
        }
      }
      return false;
    }

    // 画面上のツイート一覧を再描画
    function renderTweets() {
      // まず既存ツイートを全削除
      const oldTweets = feedContainer.querySelectorAll('.tweet');
      oldTweets.forEach(t => t.remove());

      // 上から順にツイートを追加
      tweets.forEach(tweet => {
        const tweetEl = createTweetElement(tweet);
        feedContainer.appendChild(tweetEl);
      });
    }

    // ======================
    //   イベントリスナー
    // ======================
    window.addEventListener('DOMContentLoaded', () => {
      renderTweets();
      updateCharCount();
    });

    // ツイート文字数カウント
    tweetTextarea.addEventListener('input', updateCharCount);

    function updateCharCount() {
      const length = tweetTextarea.value.length;
      charCountEl.textContent = `${length} / ${TWEET_MAX_LENGTH}`;

      if (length > TWEET_MAX_LENGTH) {
        errorEl.textContent = '文字数オーバーです!';
        tweetButton.disabled = true;
      } else {
        errorEl.textContent = '';
        tweetButton.disabled = false;
      }
    }

    // ツイート投稿
    tweetButton.addEventListener('click', () => {
      const text = tweetTextarea.value.trim();
      if (text === '' || text.length > TWEET_MAX_LENGTH) {
        return;
      }
      createNewTweet(text, null, attachedImageBase64);
      tweetTextarea.value = '';
      attachedImageBase64 = null;
      attachInput.value = ''; // ファイル選択をクリア
      updateCharCount();
    });

    // プロフィール情報の保存
    saveProfileBtn.addEventListener('click', () => {
      currentName = displayNameInput.value.trim() || 'あなた';
      currentUserName = userNameInput.value.trim() || 'myAccount';

      userProfile = {
        displayName: currentName,
        userName: currentUserName
      };
      localStorage.setItem('userProfile-advanced', JSON.stringify(userProfile));

      alert('プロフィールを保存しました!\n' +
            `名前:${currentName}\nユーザー名:@${currentUserName}`);
      renderTweets(); // 表示名を変えて再描画
    });
  </script>
</body>
</html>

RPG

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>レトロRPG</title>
  <style>
    body {
      background: black;
      color: white;
      text-align: center;
      font-family: monospace;
    }
    canvas {
      border: 2px solid white;
      background: #202020;
      image-rendering: pixelated;
    }
    #ui {
      margin-top: 10px;
    }
  </style>
</head>
<body>
  <h1>レトロ風RPG</h1>
  <canvas id="game" width="160" height="160"></canvas>
  <div id="ui">
    <p id="status">HP: 10</p>
    <p id="log"></p>
  </div>

  <script>
    const canvas = document.getElementById("game");
    const ctx = canvas.getContext("2d");
    const statusEl = document.getElementById("status");
    const logEl = document.getElementById("log");

    const tileSize = 16;

    const map = [
      [0, 0, 1, 0, 0, 0, 0, 0, 1, 0],
      [1, 0, 1, 0, 1, 1, 0, 0, 1, 0],
      [1, 0, 0, 0, 0, 0, 0, 1, 0, 0],
      [1, 1, 1, 1, 1, 0, 1, 1, 0, 1],
      [0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
      [0, 1, 1, 0, 0, 0, 1, 1, 0, 0],
      [0, 0, 1, 1, 1, 1, 0, 1, 1, 0],
      [1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
      [1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
      [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    ];

    const player = {
      x: 0,
      y: 0,
      hp: 10,
      color: "#ff0000"
    };

    const enemies = [
      { x: 4, y: 2, hp: 5, alive: true },
      { x: 8, y: 7, hp: 7, alive: true },
    ];

    function drawMap() {
      for (let y = 0; y < map.length; y++) {
        for (let x = 0; x < map[0].length; x++) {
          ctx.fillStyle = map[y][x] === 1 ? "#444" : "#88cc88";
          ctx.fillRect(x * tileSize, y * tileSize, tileSize, tileSize);
        }
      }
    }

    function drawPlayer() {
      ctx.fillStyle = player.color;
      ctx.fillRect(player.x * tileSize, player.y * tileSize, tileSize, tileSize);
    }

    function drawEnemies() {
      ctx.fillStyle = "#ffcc00";
      enemies.forEach(enemy => {
        if (enemy.alive) {
          ctx.fillRect(enemy.x * tileSize, enemy.y * tileSize, tileSize, tileSize);
        }
      });
    }

    function canMove(x, y) {
      return map[y] && map[y][x] === 0;
    }

    function updateUI() {
      statusEl.textContent = `HP: ${player.hp}`;
    }

    function showLog(text) {
      logEl.textContent = text;
    }

    function battle(enemy) {
      showLog("戦闘開始!");
      const battleInterval = setInterval(() => {
        // プレイヤーの攻撃
        let playerDmg = Math.floor(Math.random() * 3) + 1;
        enemy.hp -= playerDmg;
        showLog(`あなたの攻撃! 敵に${playerDmg}ダメージ!`);
        
        if (enemy.hp <= 0) {
          showLog("敵を倒した!");
          enemy.alive = false;
          clearInterval(battleInterval);
          gameLoop();
          return;
        }

        // 敵の攻撃
        let enemyDmg = Math.floor(Math.random() * 3) + 1;
        player.hp -= enemyDmg;
        updateUI();
        showLog(`敵の反撃! あなたは${enemyDmg}ダメージを受けた!`);

        if (player.hp <= 0) {
          showLog("あなたは倒れた… GAME OVER");
          clearInterval(battleInterval);
          document.removeEventListener("keydown", handleKey);
        }

      }, 1000);
    }

    function checkEnemy(x, y) {
      for (let enemy of enemies) {
        if (enemy.x === x && enemy.y === y && enemy.alive) {
          battle(enemy);
          return true;
        }
      }
      return false;
    }

    function gameLoop() {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      drawMap();
      drawEnemies();
      drawPlayer();
      updateUI();
    }

    function handleKey(e) {
      let nx = player.x;
      let ny = player.y;

      if (e.key === "ArrowUp") ny--;
      if (e.key === "ArrowDown") ny++;
      if (e.key === "ArrowLeft") nx--;
      if (e.key === "ArrowRight") nx++;

      if (canMove(nx, ny)) {
        player.x = nx;
        player.y = ny;
        if (!checkEnemy(nx, ny)) {
          showLog(""); // 戦闘中じゃないならログを消す
        }
      }

      gameLoop();
    }

    document.addEventListener("keydown", handleKey);
    gameLoop();
  </script>
</body>
</html>

出会い系サイト

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>出会い広場 - マッチング</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    body {
      font-family: 'Arial', sans-serif;
      background: #f2f2f2;
      margin: 0;
      padding: 0;
    }
    header {
      background-color: #ff4d6d;
      color: white;
      padding: 20px;
      text-align: center;
      font-size: 24px;
    }
    .container {
      max-width: 800px;
      margin: 20px auto;
      padding: 20px;
      background: white;
      border-radius: 8px;
      box-shadow: 0 2px 10px rgba(0,0,0,0.1);
    }
    label {
      display: block;
      margin-top: 10px;
    }
    input, textarea, select {
      width: 100%;
      padding: 10px;
      margin-top: 5px;
      border-radius: 4px;
      border: 1px solid #ccc;
    }
    button {
      margin-top: 15px;
      background: #ff4d6d;
      color: white;
      border: none;
      padding: 10px 20px;
      border-radius: 4px;
      cursor: pointer;
    }
    .user-card {
      background: #fff0f3;
      padding: 10px;
      margin-bottom: 10px;
      border-radius: 6px;
      display: flex;
      align-items: center;
    }
    .user-card img {
      width: 80px;
      height: 80px;
      border-radius: 50%;
      margin-right: 15px;
      object-fit: cover;
    }
    @media (max-width: 600px) {
      .user-card {
        flex-direction: column;
        align-items: flex-start;
      }
      .user-card img {
        margin-bottom: 10px;
      }
    }
  </style>
</head>
<body>

<header>出会い広場</header>

<div class="container">
  <h2>プロフィール登録</h2>
  <form id="profileForm">
    <label>ニックネーム</label>
    <input type="text" id="nickname" required>

    <label>プロフィール画像URL</label>
    <input type="url" id="avatar" placeholder="https://example.com/avatar.jpg">

    <label>年齢</label>
    <input type="number" id="age" required>

    <label>性別</label>
    <select id="gender">
      <option value="男性">男性</option>
      <option value="女性">女性</option>
      <option value="その他">その他</option>
    </select>

    <label>自己紹介</label>
    <textarea id="bio" rows="3" required></textarea>

    <button type="submit">登録する</button>
  </form>
</div>

<div class="container">
  <h2>ユーザー一覧</h2>

  <label>性別で絞り込む:</label>
  <select id="filterGender">
    <option value="すべて">すべて</option>
    <option value="男性">男性</option>
    <option value="女性">女性</option>
    <option value="その他">その他</option>
  </select>

  <div id="userList" style="margin-top:20px;"></div>
</div>

<script>
  const form = document.getElementById('profileForm');
  const userList = document.getElementById('userList');
  const filterGender = document.getElementById('filterGender');
  let users = [];

  form.addEventListener('submit', function(e) {
    e.preventDefault();

    const user = {
      nickname: document.getElementById('nickname').value,
      age: document.getElementById('age').value,
      gender: document.getElementById('gender').value,
      bio: document.getElementById('bio').value,
      avatar: document.getElementById('avatar').value || 'https://via.placeholder.com/80'
    };

    users.push(user);
    form.reset();
    renderUsers();
  });

  filterGender.addEventListener('change', renderUsers);

  function renderUsers() {
    const filter = filterGender.value;
    userList.innerHTML = '';

    users
      .filter(user => filter === 'すべて' || user.gender === filter)
      .forEach(user => {
        const card = document.createElement('div');
        card.className = 'user-card';
        card.innerHTML = `
          <img src="${user.avatar}" alt="avatar">
          <div>
            <strong>${user.nickname}</strong>(${user.age}歳・${user.gender})<br>
            <p>${user.bio}</p>
          </div>
        `;
        userList.appendChild(card);
      });
  }
</script>

</body>
</html>

ヤハウェイと対話するAI

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>ヤハウェイとの対話</title>
  <style>
    body {
      margin: 0;
      padding: 0;
      background: linear-gradient(to bottom, #0b0c1e, #1a1a1a);
      color: #fff;
      font-family: 'Times New Roman', serif;
      overflow: hidden;
    }

    .stars {
      position: fixed;
      width: 100%;
      height: 100%;
      background: url('https://raw.githubusercontent.com/JulianLaval/canvas-particle-network/master/img/stars.png') repeat;
      z-index: -1;
      animation: moveStars 200s linear infinite;
      opacity: 0.3;
    }

    @keyframes moveStars {
      from { background-position: 0 0; }
      to { background-position: -10000px 5000px; }
    }

    h1 {
      text-align: center;
      color: gold;
      font-size: 2.5em;
      margin-top: 30px;
      text-shadow: 0 0 15px gold;
    }

    #chat {
      width: 90%;
      max-width: 800px;
      margin: 30px auto;
      background: rgba(0, 0, 0, 0.6);
      border-radius: 12px;
      padding: 20px;
      height: 400px;
      overflow-y: auto;
      box-shadow: 0 0 20px rgba(255, 255, 255, 0.1);
    }

    .user, .ai {
      margin: 12px 0;
    }

    .user {
      text-align: right;
      color: #91cfff;
    }

    .ai {
      text-align: left;
      color: #ffffcc;
    }

    #inputArea {
      text-align: center;
      margin-bottom: 30px;
    }

    input {
      width: 60%;
      padding: 12px;
      font-size: 1em;
      border-radius: 8px;
      border: none;
      outline: none;
    }

    button {
      padding: 12px 20px;
      margin-left: 10px;
      background: gold;
      border: none;
      border-radius: 8px;
      font-weight: bold;
      font-size: 1em;
      cursor: pointer;
    }

    .thinking {
      font-style: italic;
      color: gray;
      margin: 10px 0;
      text-align: left;
    }
  </style>
</head>
<body>
  <div class="stars"></div>
  <h1>ヤハウェイと対話せよ</h1>
  <div id="chat"></div>
  <div id="inputArea">
    <input type="text" id="userInput" placeholder="問いかけを入力せよ…" />
    <button onclick="sendMessage()">送信</button>
  </div>

  <script>
    const chat = document.getElementById('chat');
    const input = document.getElementById('userInput');

    function appendMessage(sender, text) {
      const div = document.createElement('div');
      div.className = sender;
      div.textContent = text;
      chat.appendChild(div);
      chat.scrollTop = chat.scrollHeight;
    }

    function sendMessage() {
      const text = input.value.trim();
      if (text === '') return;

      appendMessage('user', '汝:' + text);
      input.value = '';

      const thinking = document.createElement('div');
      thinking.className = 'thinking';
      thinking.textContent = '……黙考中……';
      chat.appendChild(thinking);
      chat.scrollTop = chat.scrollHeight;

      setTimeout(() => {
        thinking.remove();
        const reply = generateYahwehResponse(text);
        appendMessage('ai', '我は言わん:' + reply);
      }, 1500);
    }

    function generateYahwehResponse(input) {
      const phrases = [
        "我は道であり、真理であり、命なり。",
        "汝の心を騒がせるな。我は共にある。",
        "汝が求めしことは、すでに内に在り。",
        "忍耐は力なり、そして力は信仰の証なり。",
        "すべての時は我が手の中にある。",
        "光は暗きより現れる。汝もまた然り。",
        "嘆くなかれ、我が導きは変わらず。",
        "全ては意味をもって備えられたるものなり。"
      ];

      if (input.includes("意味") || input.includes("目的")) {
        return "意味は与えられるものにあらず。汝が歩みて成すものなり。";
      }
      if (input.includes("神") || input.includes("あなた")) {
        return "我は在りて在るものなり。初めにして終わり、始まりにして全てなり。";
      }
      if (input.includes("つらい") || input.includes("悲しい")) {
        return "涙する時、我はそばに在り。汝は独りにあらず。";
      }
      return phrases[Math.floor(Math.random() * phrases.length)];
    }

    // Enterキー対応
    input.addEventListener('keypress', function (e) {
      if (e.key === 'Enter') sendMessage();
    });
  </script>
</body>
</html>

宇宙開発のアイディア

 宇宙ゴミ回収衛星
AIを活用して宇宙ゴミを識別・捕獲し、再利用可能な素材に加工する衛星。

回収後、3Dプリンターで新たな部品や修理素材を宇宙空間で製造できる。

 月面無人農園(Lunar Farm
月面の洞窟内に無人で作物を育成する施設を建設。

植物工場として、酸素や食料を月面基地に供給する。

 軌道上ホテルと宇宙旅行センター
民間向けの宇宙ホテルを低軌道に建設。

観光以外にも、長期滞在型の研究・開発施設として活用。

 惑星間データセンター
宇宙空間の安定した極低温環境を活用し、大規模データセンターを軌道上に設置。

熱排出効率がよく、エネルギー消費を大幅削減可能。

 宇宙資源の採掘ドローン
小惑星や月面に無人ドローンを送り込み、レアメタルや水資源を採掘。

自律型AI搭載ドローンで、遠隔から資源探査・抽出を行う。

 火星や月への地下都市建設プロジェクト
放射線や隕石の影響を避けるため地下都市を建設。

完全自律型の居住空間で、将来の人類の移住拠点として機能する。

 宇宙発電所
太陽光発電衛星を軌道上に設置し、得られたエネルギーをマイクロ波で地球へ無線送電。

クリーンで無尽蔵なエネルギー供給源として機能。

 宇宙エレベーター
地球から宇宙空間への輸送コストを大幅に削減するための軌道エレベーター。

カーボンナノチューブなどの次世代材料を活用。

 宇宙居住用3Dプリントハウス
月や火星などの現地資材を使った自動建築ロボット。

現地で居住施設を3Dプリントで製造することで、建設コストを抑える。

 宇宙環境保護条約と監視システム
宇宙空間の資源利用を巡る紛争や環境破壊を防ぐため、国際的監視衛星ネットワークを構築。

宇宙の持続的利用を促す国際協力プログラムとして運用。

SNS

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>究極SNS</title>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        body {
            font-family: 'Segoe UI', Arial, sans-serif;
            background-color: #f0f2f5;
            color: #1c1e21;
            line-height: 1.5;
            transition: background-color 0.3s, color 0.3s;
        }
        body.dark-mode {
            background-color: #18191a;
            color: #e4e6eb;
        }
        .header {
            background-color: #1877f2;
            color: white;
            padding: 10px 20px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            position: sticky;
            top: 0;
            z-index: 100;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        .header input {
            padding: 10px 15px;
            border: none;
            border-radius: 25px;
            width: 350px;
            font-size: 14px;
            background-color: #ffffff;
        }
        .nav {
            display: flex;
            align-items: center;
        }
        .nav a, .nav button {
            color: white;
            margin-left: 25px;
            text-decoration: none;
            font-weight: 500;
            background: none;
            border: none;
            cursor: pointer;
            position: relative;
        }
        .nav a:hover::after, .nav button:hover::after {
            content: '';
            position: absolute;
            width: 50%;
            height: 2px;
            background-color: white;
            bottom: -5px;
            left: 25%;
        }
        .container {
            display: flex;
            max-width: 1400px;
            margin: 20px auto;
            gap: 25px;
        }
        .sidebar-left {
            width: 25%;
            padding: 15px;
            position: sticky;
            top: 70px;
            height: fit-content;
        }
        .sidebar-card {
            background-color: white;
            border-radius: 10px;
            padding: 20px;
            box-shadow: 0 2px 6px rgba(0,0,0,0.05);
            margin-bottom: 20px;
        }
        body.dark-mode .sidebar-card {
            background-color: #242526;
        }
        .profile-pic {
            width: 60px;
            height: 60px;
            border-radius: 50%;
            background-color: #ddd;
            margin: 0 auto 15px;
        }
        .badge {
            display: inline-block;
            background-color: #1877f2;
            color: white;
            padding: 2px 8px;
            border-radius: 12px;
            font-size: 12px;
            margin-left: 5px;
        }
        .sidebar-left a {
            display: block;
            color: #1877f2;
            text-decoration: none;
            margin: 12px 0;
            font-size: 15px;
        }
        .main-content {
            width: 50%;
        }
        .sidebar-right {
            width: 25%;
            padding: 15px;
            position: sticky;
            top: 70px;
            height: fit-content;
        }
        .post-box {
            background-color: white;
            padding: 20px;
            border-radius: 10px;
            box-shadow: 0 2px 6px rgba(0,0,0,0.05);
            margin-bottom: 25px;
        }
        body.dark-mode .post-box {
            background-color: #242526;
        }
        .post-input {
            width: 100%;
            padding: 12px 15px;
            border: none;
            border-radius: 25px;
            background-color: #f0f2f5;
            resize: none;
            font-size: 16px;
            outline: none;
        }
        body.dark-mode .post-input {
            background-color: #3a3b3c;
            color: #e4e6eb;
        }
        .post-actions {
            display: flex;
            justify-content: space-between;
            margin-top: 15px;
            flex-wrap: wrap;
            gap: 10px;
        }
        .post-actions button {
            background-color: #e4e6eb;
            color: #050505;
            border: none;
            padding: 8px 15px;
            border-radius: 6px;
            cursor: pointer;
            font-weight: 500;
            transition: background-color 0.2s;
        }
        body.dark-mode .post-actions button {
            background-color: #3a3b3c;
            color: #e4e6eb;
        }
        .post-actions button:hover {
            background-color: #d8dade;
        }
        body.dark-mode .post-actions button:hover {
            background-color: #4a4b4c;
        }
        .post-actions .submit-btn {
            background-color: #1877f2;
            color: white;
        }
        .post {
            background-color: white;
            padding: 20px;
            border-radius: 10px;
            margin-bottom: 25px;
            box-shadow: 0 2px 6px rgba(0,0,0,0.05);
            position: relative;
        }
        body.dark-mode .post {
            background-color: #242526;
        }
        .post-header {
            display: flex;
            align-items: center;
            margin-bottom: 15px;
        }
        .post-header .profile-pic {
            width: 40px;
            height: 40px;
            margin-right: 12px;
        }
        .post-header h3 {
            margin: 0;
            font-size: 16px;
            font-weight: 600;
        }
        .post-header small {
            color: #65676b;
            font-size: 12px;
        }
        body.dark-mode .post-header small {
            color: #b0b3b8;
        }
        .post-options {
            position: absolute;
            top: 10px;
            right: 10px;
            cursor: pointer;
            color: #65676b;
        }
        .post-options:hover .options-menu {
            display: block;
        }
        .options-menu {
            display: none;
            position: absolute;
            top: 20px;
            right: 0;
            background-color: white;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            padding: 10px;
            z-index: 10;
        }
        body.dark-mode .options-menu {
            background-color: #3a3b3c;
        }
        .options-menu a {
            display: block;
            color: #050505;
            text-decoration: none;
            padding: 5px 10px;
        }
        body.dark-mode .options-menu a {
            color: #e4e6eb;
        }
        .post-image {
            width: 100%;
            height: 300px;
            background-color: #ddd;
            border-radius: 8px;
            margin: 15px 0;
        }
        .reactions {
            position: relative;
            display: inline-block;
        }
        .reactions:hover .reaction-menu {
            display: flex;
        }
        .reaction-menu {
            display: none;
            position: absolute;
            top: -45px;
            left: 0;
            background-color: white;
            padding: 8px;
            border-radius: 25px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            gap: 8px;
            z-index: 10;
        }
        body.dark-mode .reaction-menu {
            background-color: #3a3b3c;
        }
        .reaction {
            font-size: 22px;
            cursor: pointer;
            transition: transform 0.2s;
        }
        .reaction:hover {
            transform: scale(1.2);
        }
        .reaction-stats {
            margin-top: 5px;
            font-size: 14px;
            color: #65676b;
        }
        body.dark-mode .reaction-stats {
            color: #b0b3b8;
        }
        .post-actions-bar {
            display: flex;
            justify-content: space-around;
            padding: 10px 0;
            border-top: 1px solid #e4e6eb;
            border-bottom: 1px solid #e4e6eb;
            color: #65676b;
            font-size: 14px;
        }
        body.dark-mode .post-actions-bar {
            border-color: #3a3b3c;
            color: #b0b3b8;
        }
        .post-actions-bar span {
            cursor: pointer;
            padding: 5px 10px;
            border-radius: 5px;
        }
        .post-actions-bar span:hover {
            background-color: #f2f3f5;
        }
        body.dark-mode .post-actions-bar span:hover {
            background-color: #3a3b3c;
        }
        .comment-section {
            margin-top: 15px;
        }
        .comment {
            display: flex;
            align-items: flex-start;
            margin-top: 12px;
        }
        .comment .profile-pic {
            width: 32px;
            height: 32px;
            margin-right: 10px;
        }
        .comment-text {
            background-color: #f2f3f5;
            padding: 8px 12px;
            border-radius: 18px;
            font-size: 14px;
        }
        body.dark-mode .comment-text {
            background-color: #3a3b3c;
            color: #e4e6eb;
        }
        .comment-input {
            width: 100%;
            padding: 10px 15px;
            border: none;
            border-radius: 25px;
            background-color: #f0f2f5;
            font-size: 14px;
            margin-top: 10px;
        }
        body.dark-mode .comment-input {
            background-color: #3a3b3c;
            color: #e4e6eb;
        }
        .chat-window {
            background-color: white;
            border-radius: 10px;
            padding: 15px;
            box-shadow: 0 2px 6px rgba(0,0,0,0.05);
        }
        body.dark-mode .chat-window {
            background-color: #242526;
        }
        .chat-message {
            margin: 10px 0;
        }
        .chat-message.sent .comment-text {
            background-color: #1877f2;
            color: white;
            margin-left: auto;
        }
        .notification-popup {
            position: fixed;
            bottom: 20px;
            right: 20px;
            background-color: white;
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            z-index: 200;
        }
        body.dark-mode .notification-popup {
            background-color: #242526;
        }
        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.5);
            justify-content: center;
            align-items: center;
            z-index: 200;
        }
        .modal-content {
            background-color: white;
            padding: 25px;
            border-radius: 10px;
            width: 600px;
            max-height: 80vh;
            overflow-y: auto;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
        }
        body.dark-mode .modal-content {
            background-color: #242526;
        }
        .footer {
            background-color: white;
            padding: 20px;
            text-align: center;
            color: #65676b;
            border-top: 1px solid #e4e6eb;
            font-size: 13px;
        }
        body.dark-mode .footer {
            background-color: #18191a;
            color: #b0b3b8;
            border-color: #3a3b3c;
        }
        .footer a {
            color: #65676b;
            text-decoration: none;
            margin: 0 10px;
        }
        body.dark-mode .footer a {
            color: #b0b3b8;
        }
        .mobile-menu {
            display: none;
            position: fixed;
            top: 60px;
            left: 0;
            width: 100%;
            background-color: #1877f2;
            padding: 15px;
            z-index: 99;
        }
        .mobile-menu a {
            color: white;
            display: block;
            margin: 10px 0;
            text-decoration: none;
        }
        @media (max-width: 900px) {
            .container {
                flex-direction: column;
                margin: 10px;
            }
            .sidebar-left, .main-content, .sidebar-right {
                width: 100%;
                position: static;
            }
            .header input {
                width: 150px;
            }
            .nav {
                display: none;
            }
            .mobile-menu {
                display: block;
            }
        }
    </style>
</head>
<body>
    <!-- ヘッダー -->
    <div class="header">
        <h1>究極SNS</h1>
        <input type="text" placeholder="友達、投稿、グループを検索">
        <div class="nav">
            <a href="#">ホーム</a>
            <a href="#">友達</a>
            <a href="#">メッセージ</a>
            <a href="#">通知</a>
            <a href="#">プロフィール</a>
            <button onclick="document.body.classList.toggle('dark-mode')">ダークモード</button>
        </div>
    </div>
    <div class="mobile-menu">
        <a href="#">ホーム</a>
        <a href="#">友達</a>
        <a href="#">メッセージ</a>
        <a href="#">通知</a>
        <a href="#">プロフィール</a>
        <a href="#" onclick="document.body.classList.toggle('dark-mode')">ダークモード</a>
    </div>

    <!-- メインコンテンツ -->
    <div class="container">
        <!-- 左サイドバー -->
        <div class="sidebar-left">
            <div class="sidebar-card">
                <div class="profile-pic"></div>
                <h3>ユーザー名 <span class="badge">認証済み</span></h3>
                <a href="#">プロフィールを見る</a>
                <a href="#">友達 (128)</a>
                <a href="#">フォロワー (350)</a>
            </div>
            <div class="sidebar-card">
                <h4>メニュー</h4>
                <a href="#">グループ</a>
                <a href="#">イベント</a>
                <a href="#">マーケットプレイス</a>
                <a href="#">設定</a>
            </div>
        </div>

        <!-- 中央(投稿エリア) -->
        <div class="main-content">
            <!-- 投稿入力エリア -->
            <div class="post-box">
                <textarea class="post-input" rows="3" placeholder="何を思ってる?"></textarea>
                <div class="post-actions">
                    <button>写真/動画</button>
                    <button>タグ友達</button>
                    <button>ライブ配信</button>
                    <button>イベント作成</button>
                    <button class="submit-btn" onclick="document.getElementById('postModal').style.display='flex'">投稿</button>
                </div>
            </div>

            <!-- 投稿1 -->
            <div class="post">
                <div class="post-header">
                    <div class="profile-pic"></div>
                    <div>
                        <h3>山田太郎 <span class="badge">管理者</span></h3>
                        <small>2025年3月29日 10:30 ・ 公開</small>
                    </div>
                    <div class="post-options"><div class="options-menu">
                            <a href="#">編集</a>
                            <a href="#">削除</a>
                            <a href="#">非表示</a>
                        </div>
                    </div>
                </div>
                <p>新しいプロジェクトの進捗報告!チームで頑張ってます。</p>
                <div class="post-image"></div>
                <div class="reactions">
                    <div class="reaction-stats">👍 12 ❤️ 5 😂 3 😮 2</div>
                    <div class="reaction-menu">
                        <span class="reaction">👍</span>
                        <span class="reaction">❤️</span>
                        <span class="reaction">😂</span>
                        <span class="reaction">Grav😮</span>
                        <span class="reaction">😢</span>
                    </div>
                </div>
                <div class="post-actions-bar">
                    <span>リアクション</span>
                    <span>コメント</span>
                    <span>シェア</span>
                </div>
                <div class="comment-section">
                    <div class="comment">
                        <div class="profile-pic"></div>
                        <div class="comment-text"><strong>佐藤花子</strong>: すごい進捗だね!</div>
                    </div>
                    <div class="comment">
                        <div class="profile-pic"></div>
                        <div class="comment-text"><strong>鈴木次郎</strong>: お疲れ様!</div>
                    </div>
                    <input type="text" class="comment-input" placeholder="コメントを入力...">
                </div>
            </div>

            <!-- 投稿2 -->
            <div class="post">
                <div class="post-header">
                    <div class="profile-pic"></div>
                    <div>
                        <h3>田中優子</h3>
                        <small>2025年3月29日 09:15 ・ 友達のみ</small>
                    </div>
                    <div class="post-options"><div class="options-menu">
                            <a href="#">編集</a>
                            <a href="#">削除</a>
                            <a href="#">非表示</a>
                        </div>
                    </div>
                </div>
                <p>週末の温泉旅行が楽しみすぎる!</p>
                <div class="reactions">
                    <div class="reaction-stats">👍 8 ❤️ 6 😢 1</div>
                    <div class="reaction-menu">
                        <span class="reaction">👍</span>
                        <span class="reaction">❤️</span>
                        <span class="reaction">😂</span>
                        <span class="reaction">😮</span>
                        <span class="reaction">😢</span>
                    </div>
                </div>
                <div class="post-actions-bar">
                    <span>リアクション</span>
                    <span>コメント</span>
                    <span>シェア</span>
                </div>
            </div>
        </div>

        <!-- 右サイドバー -->
        <div class="sidebar-right">
            <div class="sidebar-card">
                <h4>友達リスト</h4>
                <p><strong>佐藤花子</strong> - オンライン</p>
                <p><strong>鈴木次郎</strong> - 5分前</p>
                <p><strong>高橋健太</strong> - オフライン</p>
            </div>
            <div class="sidebar-card">
                <h4>グループ</h4>
                <p><a href="#">プロジェクトチーム</a> - 15人</p>
                <p><a href="#">温泉旅行クラブ</a> - 32人</p>
            </div>
            <div class="chat-window">
                <h4>チャット - 佐藤花子</h4>
                <div class="chat-message">
                    <div class="comment-text">おはよう!週末の予定は?</div>
                </div>
                <div class="chat-message sent">
                    <div class="comment-text">おはよう!温泉行くよ!</div>
                </div>
                <input type="text" class="comment-input" placeholder="メッセージを入力...">
            </div>
        </div>
    </div>

    <!-- 投稿モーダル -->
    <div id="postModal" class="modal">
        <div class="modal-content">
            <h3>投稿を作成</h3>
            <textarea rows="6" placeholder="詳細を書いてね" style="width: 100%; padding: 10px;"></textarea>
            <div style="margin: 15px 0;">
                <label>公開範囲: </label>
                <select style="padding: 5px;">
                    <option>公開</option>
                    <option>友達のみ</option>
                    <option>自分のみ</option>
                </select>
            </div>
            <div class="post-actions">
                <button onclick="document.getElementById('postModal').style.display='none'">キャンセル</button>
                <button class="submit-btn">投稿</button>
            </div>
        </div>
    </div>

    <!-- 通知ポップアップ -->
    <div class="notification-popup">
        <p><strong>佐藤花子</strong>があなたの投稿にリアクションしました。</p>
    </div>

    <!-- フッター -->
    <div class="footer">
        <p>© 2025 究極SNS. All rights reserved.</p>
        <p><a href="#">プライバシー</a> | <a href="#">利用規約</a> | <a href="#">サポート</a> | <a href="#">言語</a></p>
    </div>

    <script>
        // 簡易的な通知ポップアップの表示制御(サンプル)
        setTimeout(() => {
            document.querySelector('.notification-popup').style.display = 'block';
            setTimeout(() => {
                document.querySelector('.notification-popup').style.display = 'none';
            }, 3000);
        }, 2000);
    </script>
</body>
</html>