/* global React, ReactDOM */
const { useState, useEffect } = React;

/* ============ Section glyphs (small pixel icons) ============ */
const GlyphPixel = ({ cells, color = "#FFD23F", scale = 3 }) => (
  <svg viewBox={`0 0 ${cells[0].length} ${cells.length}`} width={cells[0].length * scale} height={cells.length * scale} shapeRendering="crispEdges">
    {cells.map((row, y) =>
      [...row].map((c, x) => c === '#' ? <rect key={`${x}-${y}`} x={x} y={y} width="1" height="1" fill={color} /> : null)
    )}
  </svg>
);
const GlyphFood = () => (
  <GlyphPixel cells={[
    "   ####   ",
    " ######## ",
    "##########",
    "#  #  #  #",
    "##########",
    "##########",
  ]} />
);
const GlyphBike = () => (
  <GlyphPixel cells={[
    "            ##",
    "            ##",
    "  ##     #####",
    " ####   ######",
    "############# ",
    " ## ##    ##  ",
    "###  ##  ###  ",
    " ##   ## ##   ",
  ]} />
);
const GlyphScreen = () => (
  <GlyphPixel cells={[
    "############",
    "#          #",
    "#  ##   #  #",
    "#  ##   #  #",
    "#       #  #",
    "############",
    "   ######   ",
    "  ########  ",
  ]} />
);
const GlyphStar = () => (
  <GlyphPixel cells={[
    "    ##    ",
    "    ##    ",
    "##########",
    " ######## ",
    "  ######  ",
    " ## ## ## ",
    "##      ##",
  ]} />
);
const GlyphWarn = () => (
  <GlyphPixel cells={[
    "    ##    ",
    "   ####   ",
    "   ####   ",
    "  ##  ##  ",
    "  ##  ##  ",
    " ## ## ## ",
    " ## ## ## ",
    "##########",
  ]} color="#FF3D2E" />
);
const GlyphCog = () => (
  <GlyphPixel cells={[
    "  ##  ##  ",
    "  ##  ##  ",
    "##########",
    "## #### ##",
    "## #### ##",
    "##########",
    "  ##  ##  ",
    "  ##  ##  ",
  ]} color="#21D3F5" />
);
const GlyphCar = () => (
  <GlyphPixel cells={[
    "   ######   ",
    "  ########  ",
    " ########## ",
    "############",
    "## #### ## #",
    "## ###### ##",
    " ##      ## ",
  ]} color="#FF4DA6" />
);

/* ============ Nav ============ */
function Nav() {
  return (
    <nav className="nav">
      <div className="container nav-inner">
        <a href="#top" className="brand">
          <img className="logo" src="assets/logo.webp" alt="DELIVERY KID" />
          <div className="brand-text">
            <span className="en">DELIVERY KID</span>
            <span className="jp">公式サイト</span>
          </div>
        </a>
        <div className="nav-links">
          <a href="#story">STORY</a>
          <a href="#modes">MODES</a>
          <a href="#hazards">HAZARDS</a>
          <a href="#garage">GARAGE</a>
          <a href="#shots">SCREENS</a>
        </div>
        <a href="https://aokikotori.com/" target="_blank" rel="noreferrer" className="nav-cta">OFFICIAL ▸</a>
      </div>
    </nav>
  );
}

/* ============ Hero ============ */
function Hero() {
  return (
    <section className="hero" id="top">
      <div className="container">
        <div className="hero-banner">
          <img className="hero-banner-img" src="assets/top.webp" alt="デリバリーキッド" />
          <div className="hero-banner-vignette" aria-hidden="true"></div>
          <div className="hero-banner-ribbon">
            <span>VER 1.33</span>
            <span>·</span>
            <span>FREE</span>
            <span>·</span>
            <span>WIN / MAC</span>
          </div>
        </div>

        <div className="hero-info">
          <div className="hero-tagline">
            <h1>
              冷めるまでに、届けろ。<br/>
              料理を抱えて、<span className="em">ハイウェイへ。</span>
            </h1>
            <p>
              あなたはフードデリバリー会社「MOTORFOODS」のクルー。
              料理を冷まさず、形も崩さず、お客さまに笑顔をお届けするために——
              制限時間と料理ゲージを意識しながら、行き交う車の間を駆け抜けるバイクアクションです。
            </p>
          </div>

          <div className="hero-stats">
            <div className="row"><span className="k">GENRE</span><span className="v">BIKE ACTION</span></div>
            <div className="row"><span className="k">PRICE</span><span className="v y">FREE</span></div>
            <div className="row"><span className="k">PLAY TIME</span><span className="v">20-30 MIN</span></div>
            <div className="row"><span className="k">STAGES</span><span className="v p">15 + RANDOM</span></div>
          </div>
        </div>

        <a href="https://freegame-mugen.jp/action/game_14863.html" target="_blank" rel="noreferrer" className="hero-cta">
          <span className="hero-cta-shine" aria-hidden="true"></span>
          <span className="hero-cta-en">▸ PLAY FREE NOW</span>
          <span className="hero-cta-jp">ダウンロード・プレイはこちら</span>
          <span className="hero-cta-sub">外部のフリーゲームサイトへ移動します</span>
        </a>
      </div>
    </section>
  );
}

/* ============ Story ============ */
function Story() {
  return (
    <section className="about" id="story">
      <div className="container">
        <div className="about-grid">
          <div className="about-visual">
            <img src="assets/screens/Title.webp" alt="タイトル画面" />
          </div>
          <div className="about-copy">
            <div className="sec-eyebrow"><GlyphFood />STORY</div>
            <h2 className="sec-title">MOTORFOODS、<br/>本日も出動。</h2>
            <p className="lead">
              フードデリバリー会社「<span className="hot">MOTORFOODS</span>」のクルーとなり、
              街のハイウェイを愛車で駆け抜けます。<br/>
              料理を冷まさず、形も崩さず、お客さまに笑顔をお届けするための、シンプルなドライブゲームです。
            </p>
            <p className="lead">
              制限時間と料理ゲージ、お客さまからの★評価。全15コース＋ランダムモード、6車種の乗り換え、3段階の難易度をご用意しています。
              短い時間でサクッと1本、じっくり何時間でも——お好みのスタイルでお楽しみいただけます。
            </p>
            <div className="about-meta">
              <p><strong>Windows</strong> / <strong>Mac</strong> / <strong>Google Chrome</strong> ブラウザ上でお楽しみいただけます。</p>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

/* ============ Modes ============ */
function Modes() {
  const items = [
    {
      num: "MODE 01",
      label: "CHALLENGE MODE",
      title: "通しプレイで腕試し",
      body: "用意されたコースを順番に走り抜ける、本作の中心となるメインモードです。途中で失敗しても、その場でリトライしていただけます。完走時にはまとまった報酬ボーナスをご用意。クリア後には上級コースも解放され、得た資金で新しい車両を購入しながら攻略を進めていけます。",
      img: "assets/screens/StageSelect.webp",
    },
    {
      num: "MODE 02",
      label: "RANDOM MODE",
      title: "毎回違うコース、一本勝負",
      body: "これまでに走ったことのあるコースの中から、毎回ランダムで1本が選ばれるモードです。何が出てくるか分からないからこそ、集中力と適応力が試されます。短い時間でサッと一本走りたい時にもおすすめです。",
      img: "assets/screens/Stage1.webp",
    },
    {
      num: "MODE 03",
      label: "コースプレイ",
      title: "お気に入りのコースを、いつでも個別に",
      body: "お好きなコースを1つ選んでプレイするモードです。タイム短縮を狙ったり、お気に入りのコースをじっくり味わっていただけます。リザルトの収支はゴール直後に累積へ加算されますので、得意なコースを周回して資金集めにお使いいただくのもおすすめです。",
      img: "assets/screens/Result.webp",
    },
  ];
  return (
    <section className="modes" id="modes">
      <div className="container">
        <div className="sec-eyebrow"><GlyphBike />GAME MODES</div>
        <h2 className="sec-title">3つの走り方。</h2>
        <p className="sec-sub">通しで腕を試すもよし、ランダムに賭けるもよし、お気に入りのコースを繰り返すもよし。走破したコースの BGM は、タイトル画面の「サウンドトラック」からいつでもお楽しみいただけます。</p>
        <div className="modes-list">
          {items.map((it, i) => (
            <div className="mode" key={i}>
              <div className="mode-img">
                <div className="num">{it.num}</div>
                <img src={it.img} alt={it.title} />
              </div>
              <div className="mode-body">
                <div className="label">{it.label}</div>
                <h3>{it.title}</h3>
                <p>{it.body}</p>
              </div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

/* ============ Hazards ============ */
function Hazards() {
  const items = [
    {
      tag: "TRAFFIC",
      label: "一般車との衝突",
      body: "左右から追い越してくる一般車に接触すると、料理が大きく傷み、修理費もかかります。さらに一時的に速度が落ちますので、中央寄りのラインを保って走るのが基本です。",
    },
    {
      tag: "POLICE",
      label: "パトカー (BUSTED!)",
      body: "パトカーに接触すると、即レース終了 (BUSTED!) となります。サイレン音で接近をお知らせしますので、聞こえたら早めの車線変更で安全な距離を保ってください。",
    },
    {
      tag: "OFF-ROAD",
      label: "コース外走行",
      body: "路肩より外を走り続けると、料理が少しずつ傷み、速度も低下します。長時間外を走ると料理がゼロになるおそれがありますので、コース内のキープを心がけてください。",
    },
    {
      tag: "CONES",
      label: "コーン破壊",
      body: "コーン地帯では「LEFT / CENTER / RIGHT CONES AHEAD」の警告バナーが事前に表示されます。指示通りに避ければ大丈夫ですが、破壊してしまうと料理がわずかに傷み、本数に応じて修理費がかかります。",
    },
    {
      tag: "TIME UP",
      label: "タイムアップ / 料理ゼロ",
      body: "制限時間切れも、料理ゲージがゼロになっても、配達は失敗となります。残り時間と FOOD ゲージを意識しながら走ってください。",
    },
  ];
  return (
    <section className="hazards" id="hazards">
      <div className="hazards-marquee" aria-hidden="true">
        <div className="hazards-marquee-track">
          {Array(2).fill(0).map((_, i) => (
            <span key={i}>
              CAUTION · CAUTION · CAUTION · CAUTION · CAUTION · CAUTION · CAUTION · CAUTION · CAUTION · CAUTION ·{" "}
            </span>
          ))}
        </div>
      </div>
      <div className="container">
        <div className="sec-eyebrow"><GlyphWarn />HAZARDS</div>
        <h2 className="sec-title">配達を阻む、5つの危険。</h2>
        <p className="sec-sub">シンプルな見た目に反して、ハイウェイには色々な危険が潜んでいます。事前の警告を活用しながら、安全運転で配達を成功させてください。</p>
        <div className="hazards-grid">
          {items.map((h, i) => (
            <div className="hazard" key={i}>
              <div className="hazard-tag">
                <span className="num">{String(i + 1).padStart(2, '0')}</span>
                <span className="lbl">{h.tag}</span>
              </div>
              <h4>{h.label}</h4>
              <p>{h.body}</p>
            </div>
          ))}
        </div>
        <div className="hazards-banner">
          <div className="hazards-banner-img">
            <img src="assets/screens/Patcar.webp" alt="パトカー接近" />
            <div className="hazards-banner-scan"></div>
          </div>
          <div className="hazards-banner-copy">
            <div className="warn-row"><span className="dot"></span>POLICE APPROACHING</div>
            <h3>サイレンが聞こえたら、即避難を。</h3>
            <p>パトカーに接触すると、レースはその場で即終了となります。出現の合図はサイレン音。聞こえたら早めに車線変更で安全な距離を確保してください。HARD難易度では出現頻度が高くなりますので、十分にご注意を。</p>
          </div>
        </div>
      </div>
    </section>
  );
}

/* ============ Garage / Vehicles ============ */
function Garage() {
  const cars = [
    { name: "バイク",       en: "BIKE",        price: "$0",     spec: "初期装備、バランス型" },
    { name: "スポーツ",     en: "SPORT",       price: "$300",   spec: "軽量で加速良し" },
    { name: "アメリカン",   en: "AMERICAN",    price: "$500",   spec: "最高速 180km/h、重め" },
    { name: "スポーツカー", en: "SPORTS CAR",  price: "$1,000", spec: "高最高速 187km/h" },
    { name: "トラック",     en: "TRUCK",       price: "$1,500", spec: "最高速 195km/h、加速はもっさり" },
    { name: "馬",           en: "HORSE",       price: "$3,000", spec: "最高速 127km/h、蹄音付きロマン枠" },
  ];
  return (
    <section className="garage" id="garage">
      <div className="container">
        <div className="garage-head">
          <div>
            <div className="sec-eyebrow"><GlyphCar />GARAGE</div>
            <h2 className="sec-title">稼いで、乗り換える。</h2>
            <p className="sec-sub">初回起動時にお渡しする $500 と、配達で貯めた累積収支で新しい車両を解禁いただけます。加速重視のバイクから、最高速重視のトラック、ロマン枠の馬まで、全6車種をご用意しました。</p>
          </div>
          <div className="garage-feature">
            <img src="assets/screens/Truck.webp" alt="トラック車両" />
            <div className="garage-feature-cap">
              <span>FEATURED</span>
              <strong>TRUCK</strong>
              <small>最高速 195km/h</small>
            </div>
          </div>
        </div>
        <div className="garage-list">
          <div className="garage-row garage-row-head">
            <span>#</span>
            <span>VEHICLE</span>
            <span>JP</span>
            <span>PRICE</span>
            <span className="hide-sm">SPEC</span>
          </div>
          {cars.map((c, i) => (
            <div className="garage-row" key={i}>
              <span className="garage-num">{String(i + 1).padStart(2, '0')}</span>
              <span className="garage-en">{c.en}</span>
              <span className="garage-jp">{c.name}</span>
              <span className={"garage-price " + (i === 0 ? "free" : "")}>{c.price}</span>
              <span className="garage-spec hide-sm">{c.spec}</span>
            </div>
          ))}
        </div>
        <div className="garage-note">
          <span>※</span> 全15コースを完走できるギリギリのラインとなるのが「馬」、上級者向けのロマン枠です。資金を貯めたい時は、得意なコースをコースプレイで繰り返すのが手堅い方法です。
        </div>
      </div>
    </section>
  );
}

/* ============ Features ============ */
function Features() {
  const items = [
    {
      tag: "AUTO ACCEL",
      title: "自動アクセル",
      body: "PAUSE メニュー (ESCキー) から ON にすると、アクセル操作を自動化していただけます。回避操作だけに集中したい時にも便利な機能で、一度有効にすれば次のレースにも引き継がれます。",
    },
    {
      tag: "GHOST",
      title: "ゴースト表示",
      body: "同じコースの過去ベスト走行を、半透明のゴーストとして並走表示します。ベストより遅れている時にだけ控えめに現れる、タイム短縮の良い目安となる機能です。",
    },
    {
      tag: "DIFFICULTY",
      title: "3段階の難易度",
      body: "EASY・NORMAL・HARD の3段階からお選びいただけます。EASY は制限時間が長めで操作アシストも自動でオンに。HARD では敵車もパトカーも増えますが、その分配達報酬も上乗せされます。",
    },
    {
      tag: "SOUNDTRACK",
      title: "BGM コレクション",
      body: "走破したコースの BGM は、タイトル画面の「サウンドトラック」からいつでも試聴いただけます。全15曲を集めていくのも、本作のお楽しみのひとつです。",
    },
  ];
  return (
    <section className="features" id="features">
      <div className="container">
        <div className="sec-eyebrow"><GlyphCog />SYSTEM</div>
        <h2 className="sec-title">親切で、寛容な設計。</h2>
        <p className="sec-sub">難しすぎず、簡単すぎず。リトライやゴースト、自動アクセル、軽量モードなど、お使いの環境や腕前に合わせてプレイをカスタマイズいただけます。</p>
        <div className="features-grid">
          {items.map((f, i) => (
            <div className="feature" key={i}>
              <div className="feature-tag">{f.tag}</div>
              <h3>{f.title}</h3>
              <p>{f.body}</p>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

/* ============ Screenshots ============ */
function Screenshots() {
  const shots = [
    { img: "assets/screens/StageCity1.webp", title: "ナイトハイウェイ",        badge: "01", large: true },
    { img: "assets/screens/Stage1.webp",     title: "朝のハイウェイ",          badge: "02" },
    { img: "assets/screens/Truck.webp",      title: "雨夜のハイウェイ",        badge: "03" },
    { img: "assets/screens/Result.webp",     title: "リザルト",            badge: "04" },
    { img: "assets/screens/Review.webp",     title: "REVIEWS / 戦歴",      badge: "05" },
  ];
  return (
    <section className="shots" id="shots">
      <div className="container">
        <div className="sec-eyebrow"><GlyphScreen />SCREENSHOTS</div>
        <h2 className="sec-title">ゲーム画面</h2>
        <p className="sec-sub">実際のゲーム画面よりお届けします。ピクセルアートで描かれた、レトロな世界観をぜひお楽しみください。</p>
        <div className="shots-grid">
          {shots.map((s, i) => (
            <div className={"shot" + (s.large ? " large" : "")} key={i}>
              <img src={s.img} alt={s.title} />
              <div className="cap">
                <span>{s.title}</span>
                <span className="num">{s.badge} / 05</span>
              </div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

/* ============ Controls / Specs ============ */
function Specs() {
  return (
    <section className="specs" id="specs">
      <div className="container">
        <div className="specs-grid">
          <div className="specs-block">
            <div className="sec-eyebrow"><GlyphStar />CONTROLS</div>
            <h2 className="sec-title small">キー操作</h2>
            <ul className="kbd-list">
              <li>
                <span className="kbd"><i>←</i><i>→</i></span>
                <span>バイクの移動</span>
              </li>
              <li>
                <span className="kbd"><i>Space</i><i>Z</i></span>
                <span>決定・アクセル</span>
              </li>
              <li>
                <span className="kbd"><i>ESC</i><i>X</i></span>
                <span>キャンセル・ブレーキ / PAUSE</span>
              </li>
            </ul>
            <p className="specs-foot">※ ゲームパッドでの動作は未検証となっております。キーボードでの操作を推奨いたします。</p>
          </div>
          <div className="specs-block">
            <div className="sec-eyebrow"><GlyphCog />SYSTEM REQUIREMENTS</div>
            <h2 className="sec-title small">動作環境</h2>
            <ul className="spec-list">
              <li><span className="k">OS</span><span className="v">Windows 8.1/10 (64bit) / macOS 10.13+</span></li>
              <li><span className="k">CPU</span><span className="v">Intel Core i3-4340 相当以上</span></li>
              <li><span className="k">RAM</span><span className="v">8 GB 以上</span></li>
              <li><span className="k">GPU</span><span className="v">OpenGL 対応 / VRAM 1 GB 以上</span></li>
              <li><span className="k">DISPLAY</span><span className="v">1280 × 768 以上</span></li>
              <li><span className="k">INPUT</span><span className="v">KEYBOARD</span></li>
            </ul>
          </div>
        </div>
      </div>
    </section>
  );
}

/* ============ Credits ============ */
function Credits() {
  const rows = [
    ["DIRECTION / DESIGN", "kotonoha*"],
    ["GRAPHICS",           "kotonoha* / ChatGPT"],
    ["BGM · ME",           "Suno"],
    ["SE · 背景音素材",    "OtoLogic / 無料効果音で遊ぼう！ / 効果音ラボ"],
    ["PLUGINS",            "kotonoha* + Claude Code"],
    ["SPECIAL THANKS",     "RiCoChoco"],
    ["ENGINE",             "RPGツクール MZ"],
  ];
  return (
    <section className="credits" id="credits">
      <div className="container">
        <div className="sec-eyebrow"><GlyphStar />CREDITS</div>
        <h2 className="sec-title">スタッフロール</h2>
        <div className="credits-table">
          {rows.map(([k, v], i) => (
            <div className="cr-row" key={i}>
              <span className="cr-k">{k}</span>
              <span className="cr-dots" />
              <span className="cr-v">{v}</span>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

/* ============ Footer ============ */
function Footer() {
  return (
    <footer>
      <div className="container">
        <div className="foot-grid">
          <div className="foot-brand">
            <img src="assets/logo.webp" alt="DELIVERY KID" />
            <p>バイクで料理を運ぶ、シンプルなドライブゲームです。フリーゲーム、インストール不要のフォルダ起動でお楽しみいただけます。</p>
          </div>
          <div className="foot-col">
            <h5>GAME</h5>
            <ul>
              <li><a href="#story">STORY</a></li>
              <li><a href="#modes">MODES</a></li>
              <li><a href="#hazards">HAZARDS</a></li>
              <li><a href="#garage">GARAGE</a></li>
            </ul>
          </div>
          <div className="foot-col">
            <h5>INFO</h5>
            <ul>
              <li><a href="#shots">SCREENS</a></li>
              <li><a href="#specs">SPECS</a></li>
              <li><a href="#credits">CREDITS</a></li>
            </ul>
          </div>
          <div className="foot-col foot-official">
            <h5>OFFICIAL</h5>
            <a href="https://aokikotori.com/" target="_blank" rel="noreferrer" className="foot-official-link">
              <span className="foot-official-domain">aokikotori.com</span>
              <span className="foot-official-name">蒼木ことり (kotonoha*) ▸</span>
            </a>
          </div>
        </div>
        <div className="foot-bottom">
          <div>© 2026 kotonoha* · DELIVERY KID</div>
          <div>VER 1.33</div>
        </div>
      </div>
    </footer>
  );
}

/* ============ Tweaks ============ */
function TweaksController({ tweaks, setTweak }) {
  if (!window.TweaksPanel) return null;
  const { TweaksPanel, TweakSection, TweakColor, TweakToggle } = window;
  return (
    <TweaksPanel title="Tweaks">
      <TweakSection label="Theme">
        <TweakColor
          label="Palette"
          value={tweaks.palette}
          onChange={(v) => setTweak('palette', v)}
          options={[
            ['#060924', '#FF8A1A', '#FFD23F'],
            ['#1A0A1E', '#FF4DA6', '#21D3F5'],
            ['#0A1E0E', '#FFD23F', '#FF3D2E'],
          ]}
        />
        <TweakToggle label="Stars background" value={tweaks.stars} onChange={(v) => setTweak('stars', v)} />
      </TweakSection>
    </TweaksPanel>
  );
}

/* ============ Loading Screen ============ */
function LoadingScreen() {
  const alreadyBooted = (() => {
    try { return sessionStorage.getItem('dk-booted') === '1'; }
    catch (e) { return false; }
  })();
  const [progress, setProgress] = useState(alreadyBooted ? 100 : 0);
  const [phase, setPhase] = useState(alreadyBooted ? 'hidden' : 'loading'); // loading | ready | done | hidden

  useEffect(() => {
    if (alreadyBooted) {
      document.body.classList.remove('booting');
      return;
    }
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
      setPhase('hidden');
      document.body.classList.remove('booting');
      try { sessionStorage.setItem('dk-booted', '1'); } catch (e) {}
      return;
    }
    document.body.classList.add('booting');

    const preload = [
      'assets/top.webp',
      'assets/logo.webp',
      'assets/screens/Title.webp',
      'assets/screens/StageSelect.webp',
      'assets/screens/StageCity1.webp',
      'assets/screens/Patcar.webp',
    ];
    let loaded = 0;
    const total = preload.length;
    preload.forEach((src) => {
      const img = new Image();
      const done = () => { loaded++; };
      img.onload = done;
      img.onerror = done;
      img.src = src;
    });

    const start = performance.now();
    const minDuration = 1400;
    let raf;
    let timers = [];
    const tick = () => {
      const elapsed = performance.now() - start;
      const timeFrac = Math.min(elapsed / minDuration, 1);
      const loadFrac = total > 0 ? loaded / total : 1;
      const target = Math.min(timeFrac, loadFrac);
      const eased = 1 - Math.pow(1 - target, 1.7);
      const pct = Math.floor(eased * 100);
      setProgress(pct);

      if (target < 1) {
        raf = requestAnimationFrame(tick);
      } else {
        setProgress(100);
        try { sessionStorage.setItem('dk-booted', '1'); } catch (e) {}
        timers.push(setTimeout(() => {
          setPhase('ready');
          timers.push(setTimeout(() => {
            setPhase('done');
            document.body.classList.remove('booting');
            timers.push(setTimeout(() => {
              setPhase('hidden');
            }, 700));
          }, 550));
        }, 150));
      }
    };
    raf = requestAnimationFrame(tick);
    return () => {
      if (raf) cancelAnimationFrame(raf);
      timers.forEach(clearTimeout);
      document.body.classList.remove('booting');
    };
  }, []);

  if (phase === 'hidden') return null;
  const pctStr = String(progress).padStart(3, '0');
  const isReady = phase === 'ready' || phase === 'done';

  return (
    <div className={`boot boot-${phase}`} aria-hidden="true">
      <div className="boot-noise"></div>
      <div className="boot-frame">
        <div className="boot-scanlines"></div>
        <div className="boot-content">
          <div className="boot-tag">MOTORFOODS · DELIVERY CREW</div>
          <div className="boot-brand">
            <span className="boot-brand-en">DELIVERY KID</span>
            <span className="boot-brand-jp">デリバリーキッド</span>
          </div>
          <div className="boot-version">VER 1.33 · BIKE ACTION</div>
          <div className="boot-bar">
            <div className="boot-bar-track">
              <div className="boot-bar-fill" style={{ width: progress + '%' }} />
              <div className="boot-bar-ticks" aria-hidden="true">
                {Array.from({ length: 20 }).map((_, i) => <i key={i} />)}
              </div>
            </div>
            <div className="boot-bar-meta">
              <span>LOADING ASSETS</span>
              <span className="boot-bar-pct">{pctStr}%</span>
            </div>
          </div>
          <div className="boot-status">
            {isReady ? (
              <span className="boot-go">▸ READY</span>
            ) : (
              <span className="boot-loading-text">
                NOW LOADING<i>.</i><i>.</i><i>.</i>
              </span>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

/* ============ Effects ============ */
function ScrollProgress() {
  const [pct, setPct] = useState(0);
  useEffect(() => {
    const onScroll = () => {
      const h = document.documentElement.scrollHeight - window.innerHeight;
      setPct(h > 0 ? (window.scrollY / h) * 100 : 0);
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll, { passive: true });
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, []);
  return (
    <div className="scroll-progress" aria-hidden="true">
      <div style={{ width: pct + '%' }} />
    </div>
  );
}

function useSiteEffects() {
  useEffect(() => {
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
      document.body.classList.add('reduce-motion');
      return;
    }

    // 1) stagger index for grid groups
    const groupSelectors = [
      '.modes-list', '.hazards-grid', '.features-grid', '.shots-grid',
      '.garage-list', '.credits-table', '.kbd-list', '.spec-list',
      '.hero-stats', '.hero-tagline',
    ];
    groupSelectors.forEach((sel) => {
      const c = document.querySelector(sel);
      if (!c) return;
      [...c.children].forEach((el, i) => el.style.setProperty('--reveal-i', i));
    });

    // 2) reveal on scroll
    const revealSel = [
      '.sec-eyebrow', '.sec-title', '.sec-sub', '.lead', '.about-meta',
      '.mode', '.hazard', '.feature', '.garage-row', '.shot', '.cr-row',
      '.hazards-banner', '.garage-feature', '.kbd-list li', '.spec-list li',
      '.hero-stats .row', '.hero-tagline > *', '.garage-note',
    ].join(', ');
    const revealEls = document.querySelectorAll(revealSel);
    revealEls.forEach((el) => el.classList.add('reveal'));
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            e.target.classList.add('in');
            io.unobserve(e.target);
          }
        });
      },
      { threshold: 0.06, rootMargin: '0px 0px -4% 0px' }
    );
    revealEls.forEach((el) => io.observe(el));

    // 3) spotlight cursor on cards (fine pointer only)
    let onMove = null;
    if (window.matchMedia('(pointer: fine)').matches) {
      const spotlightSel = '.feature, .hazard, .mode, .garage-feature, .shot, .foot-official-link, .release-cta';
      const sp = Array.from(document.querySelectorAll(spotlightSel));
      sp.forEach((el) => el.classList.add('spotlight'));
      const vh = () => window.innerHeight;
      onMove = (e) => {
        for (let i = 0; i < sp.length; i++) {
          const el = sp[i];
          const r = el.getBoundingClientRect();
          if (r.bottom < -40 || r.top > vh() + 40) continue;
          el.style.setProperty('--mx', `${e.clientX - r.left}px`);
          el.style.setProperty('--my', `${e.clientY - r.top}px`);
        }
      };
      document.addEventListener('mousemove', onMove, { passive: true });
    }

    // 4) nav scrolled state
    const nav = document.querySelector('.nav');
    const onNavScroll = () => {
      if (!nav) return;
      nav.classList.toggle('scrolled', window.scrollY > 24);
    };
    onNavScroll();
    window.addEventListener('scroll', onNavScroll, { passive: true });

    return () => {
      io.disconnect();
      if (onMove) document.removeEventListener('mousemove', onMove);
      window.removeEventListener('scroll', onNavScroll);
    };
  }, []);
}

/* ============ App ============ */
function App() {
  const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
    "palette": ["#060924", "#FF8A1A", "#FFD23F"],
    "stars": true
  }/*EDITMODE-END*/;

  const [tweaks, setTweaksState] = useState(TWEAK_DEFAULTS);
  const setTweak = (k, v) => {
    setTweaksState((p) => {
      const next = typeof k === 'object' ? { ...p, ...k } : { ...p, [k]: v };
      try { window.parent?.postMessage({ type: '__edit_mode_set_keys', edits: next }, '*'); } catch (e) {}
      return next;
    });
  };

  useEffect(() => {
    const [bg, orange, yellow] = tweaks.palette;
    const r = document.documentElement.style;
    r.setProperty('--bg-deep', bg);
    r.setProperty('--orange', orange);
    r.setProperty('--yellow', yellow);
    const bs = document.querySelector('.bg-stars');
    if (bs) bs.style.display = tweaks.stars ? '' : 'none';
  }, [tweaks]);

  useSiteEffects();

  return (
    <>
      <LoadingScreen />
      <ScrollProgress />
      <Nav />
      <Hero />
      <Story />
      <Modes />
      <Hazards />
      <Garage />
      <Features />
      <Screenshots />
      <Specs />
      <Credits />
      <Footer />
      <TweaksController tweaks={tweaks} setTweak={setTweak} />
    </>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
