init
238
rse/index.html
Normal file
@@ -0,0 +1,238 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" href="/icon/ai.svg" type="image/svg+xml">
|
||||
<title>Airse | Atmosphere Open World</title>
|
||||
</head>
|
||||
<body style="opacity:0">
|
||||
|
||||
<svg style="position:absolute;width:0;height:0">
|
||||
<defs>
|
||||
<linearGradient id="nav-grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3a7ca5"/>
|
||||
<stop offset="100%" stop-color="#6a5acd"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<canvas id="space-canvas"></canvas>
|
||||
<div class="vignette"></div>
|
||||
|
||||
<!-- HEADER -->
|
||||
<header class="site-header" id="site-header">
|
||||
<a href="/" class="header-logo">
|
||||
<img src="/icon/ai.svg" alt="Airse" class="header-logo-icon">
|
||||
<span class="header-logo-text">Airse</span>
|
||||
</a>
|
||||
<div class="lang-selector" id="lang-selector">
|
||||
<button type="button" class="lang-btn" id="lang-tab">
|
||||
<img src="/icon/language.svg" alt="Lang" class="lang-icon">
|
||||
</button>
|
||||
<div class="lang-dropdown" id="lang-dropdown">
|
||||
<div class="lang-option selected" data-lang="en">
|
||||
<span class="lang-name">EN</span>
|
||||
<span class="lang-check">✓</span>
|
||||
</div>
|
||||
<div class="lang-option" data-lang="ja">
|
||||
<span class="lang-name">JA</span>
|
||||
<span class="lang-check">✓</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-selector" id="menu-selector">
|
||||
<button type="button" class="menu-btn" id="menu-btn">
|
||||
<img src="/icon/menu.svg" alt="Menu" class="menu-icon">
|
||||
</button>
|
||||
<div class="menu-dropdown" id="menu-dropdown">
|
||||
<a href="/" class="menu-option">
|
||||
<span class="menu-option-text">Airse</span>
|
||||
</a>
|
||||
<a href="/account" class="menu-option">
|
||||
<span class="menu-option-text" data-lang-en="Account" data-lang-ja="アカウント">Account</span>
|
||||
</a>
|
||||
<a href="/privacy" class="menu-option">
|
||||
<span class="menu-option-text" data-lang-en="Privacy" data-lang-ja="プライバシー">Privacy</span>
|
||||
</a>
|
||||
<a href="/terms" class="menu-option">
|
||||
<span class="menu-option-text" data-lang-en="Terms" data-lang-ja="利用規約">Terms</span>
|
||||
</a>
|
||||
<a href="/support" class="menu-option">
|
||||
<span class="menu-option-text" data-lang-en="Support" data-lang-ja="サポート">Support</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer class="site-footer" id="site-footer">
|
||||
<div class="footer-copy">©syui</div>
|
||||
</footer>
|
||||
|
||||
<!-- PAGE 1: Video -->
|
||||
<div id="page-video" class="page">
|
||||
<div class="hero-video">
|
||||
<video id="game-video" src="/video.mp4" autoplay muted playsinline loop></video>
|
||||
<div class="video-text">
|
||||
<div class="video-title-wrap">
|
||||
<img src="/icon/ai.svg" alt="" class="video-title-icon">
|
||||
<div class="video-label">Airse</div>
|
||||
</div>
|
||||
<div class="video-separator"></div>
|
||||
<div class="video-desc">Atmosphere Open World</div>
|
||||
</div>
|
||||
<div class="video-apps">
|
||||
<div class="video-apps-group">
|
||||
<span class="video-apps-label">Windows</span>
|
||||
<a href="https://store.epicgames.com/ja/p/airse-ef542f" class="video-app-link" target="_blank" rel="noopener">
|
||||
<img src="/service/ai.syui.rse.png" alt="Airse" class="video-app-icon">
|
||||
<span class="video-app-name">Airse</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="video-apps-group">
|
||||
<span class="video-apps-label">Mac</span>
|
||||
<a href="https://apps.apple.com/app/id6754494953" class="video-app-link" target="_blank" rel="noopener">
|
||||
<img src="/service/ai.syui.rse.png" alt="Airse" class="video-app-icon">
|
||||
<span class="video-app-name">Airse</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="video-apps-group">
|
||||
<span class="video-apps-label">iOS</span>
|
||||
<a href="https://apps.apple.com/app/id6754494806" class="video-app-link" target="_blank" rel="noopener">
|
||||
<img src="/service/ai.syui.card.png" alt="Aicard" class="video-app-icon">
|
||||
<span class="video-app-name">Aicard</span>
|
||||
</a>
|
||||
<a href="https://apps.apple.com/app/id6756220616" class="video-app-link" target="_blank" rel="noopener">
|
||||
<img src="/service/ai.syui.at.png" alt="Aiat" class="video-app-icon">
|
||||
<span class="video-app-name">Aiat</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="btn-next" class="nav-btn nav-btn-right" aria-label="Next">
|
||||
<svg viewBox="0 0 48 48" fill="none"><circle cx="24" cy="24" r="23" stroke="url(#nav-grad)" stroke-width="1" fill="none"/><polygon points="20,15 33,24 20,33" fill="url(#nav-grad)"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- PAGE: Logo -->
|
||||
<div id="page-logo" class="page page-hidden">
|
||||
<button id="btn-logo-back" class="nav-btn nav-btn-left" aria-label="Back">
|
||||
<svg viewBox="0 0 48 48" fill="none"><circle cx="24" cy="24" r="23" stroke="url(#nav-grad)" stroke-width="1" fill="none"/><polygon points="28,15 15,24 28,33" fill="url(#nav-grad)"/></svg>
|
||||
</button>
|
||||
<img src="/icon/ai.svg" alt="Airse" class="logo-icon">
|
||||
<button id="btn-logo-next" class="nav-btn nav-btn-right" aria-label="Next">
|
||||
<svg viewBox="0 0 48 48" fill="none"><circle cx="24" cy="24" r="23" stroke="url(#nav-grad)" stroke-width="1" fill="none"/><polygon points="20,15 33,24 20,33" fill="url(#nav-grad)"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- PAGE 2: Message -->
|
||||
<div id="page-message" class="page page-hidden">
|
||||
<button id="btn-msg-back" class="nav-btn nav-btn-left" aria-label="Back">
|
||||
<svg viewBox="0 0 48 48" fill="none"><circle cx="24" cy="24" r="23" stroke="url(#nav-grad)" stroke-width="1" fill="none"/><polygon points="28,15 15,24 28,33" fill="url(#nav-grad)"/></svg>
|
||||
</button>
|
||||
<div class="message-content">
|
||||
<h1 class="message-title" data-lang-en="GAME × SNS" data-lang-ja="ゲーム × SNS"></h1>
|
||||
<div class="message-separator"></div>
|
||||
<p class="message-desc" data-lang-en="A game built on social networks." data-lang-ja="ソーシャルネットワーク上に構築されたゲーム。">A game built on social networks.</p>
|
||||
</div>
|
||||
<button id="btn-msg-next" class="nav-btn nav-btn-right" aria-label="Next">
|
||||
<svg viewBox="0 0 48 48" fill="none"><circle cx="24" cy="24" r="23" stroke="url(#nav-grad)" stroke-width="1" fill="none"/><polygon points="20,15 33,24 20,33" fill="url(#nav-grad)"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- PAGE 3: Message 2 -->
|
||||
<div id="page-message2" class="page page-hidden">
|
||||
<button id="btn-msg2-back" class="nav-btn nav-btn-left" aria-label="Back">
|
||||
<svg viewBox="0 0 48 48" fill="none"><circle cx="24" cy="24" r="23" stroke="url(#nav-grad)" stroke-width="1" fill="none"/><polygon points="28,15 15,24 28,33" fill="url(#nav-grad)"/></svg>
|
||||
</button>
|
||||
<div class="message-content">
|
||||
<h1 class="message-title" data-lang-en="Ai × Universe" data-lang-ja="アイ × 宇宙">Ai × Universe</h1>
|
||||
<div class="message-separator"></div>
|
||||
<p class="message-desc" data-lang-en="The theme is the universe and ai." data-lang-ja="テーマは宇宙とアイ。">The theme is the universe and ai.</p>
|
||||
</div>
|
||||
<button id="btn-msg2-next" class="nav-btn nav-btn-right" aria-label="Next">
|
||||
<svg viewBox="0 0 48 48" fill="none"><circle cx="24" cy="24" r="23" stroke="url(#nav-grad)" stroke-width="1" fill="none"/><polygon points="20,15 33,24 20,33" fill="url(#nav-grad)"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- PAGE: About -->
|
||||
<div id="page-about" class="page page-hidden">
|
||||
<button id="btn-about-back" class="nav-btn nav-btn-left" aria-label="Back">
|
||||
<svg viewBox="0 0 48 48" fill="none"><circle cx="24" cy="24" r="23" stroke="url(#nav-grad)" stroke-width="1" fill="none"/><polygon points="28,15 15,24 28,33" fill="url(#nav-grad)"/></svg>
|
||||
</button>
|
||||
<div class="subpage-content">
|
||||
<h1 class="subpage-heading">Airse</h1>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="Atmosphere Open World." data-lang-ja="Atmosphere Open World.">Atmosphere Open World.</p>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="GAME × SNS" data-lang-ja="ゲーム × SNS">GAME × SNS</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="Your SNS account becomes your game account." data-lang-ja="SNSアカウントがゲームアカウントになります。">Your SNS account becomes your game account.</p>
|
||||
<p class="subpage-section-text" data-lang-en="Game data is stored on decentralized social networks like Bluesky — your world, your data." data-lang-ja="ゲームデータはBlueskyなどの分散型SNSに保存されます。あなたの世界、あなたのデータ。">Game data is stored on decentralized social networks like Bluesky — your world, your data.</p>
|
||||
</div>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="Ai × Universe" data-lang-ja="アイ × 宇宙">Ai × Universe</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="The theme is the universe." data-lang-ja="テーマは宇宙。">The theme is the universe.</p>
|
||||
<p class="subpage-section-text" data-lang-en=""Ai" refers to elemental attributes that exist in this world." data-lang-ja="「アイ」はこの世界に存在する属性のこと。">"Ai" refers to elemental attributes that exist in this world.</p>
|
||||
<p class="subpage-section-text" data-lang-en="A combat system built around elemental reactions." data-lang-ja="属性反応を軸にした戦闘システム。">A combat system built around elemental reactions.</p>
|
||||
</div>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="Elements" data-lang-ja="属性">Elements</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="Elements are named after real particles — atoms, neutrons, and more." data-lang-ja="属性は実在する粒子の名前 — 原子、中性子など。">Elements are named after real particles — atoms, neutrons, and more.</p>
|
||||
<p class="subpage-section-text" data-lang-en="Each character holds one element." data-lang-ja="各キャラクターは1つの属性を持つ。">Each character holds one element.</p>
|
||||
<p class="subpage-section-text" data-lang-en="Land an elemental attack to apply a status. Strike again with another element for massive damage." data-lang-ja="属性攻撃でステータスを付与し、別の属性で追撃すると大ダメージ。">Land an elemental attack to apply a status. Strike again with another element for massive damage.</p>
|
||||
</div>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="Growth" data-lang-ja="育成">Growth</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="Leveling up means ascending limits." data-lang-ja="レベルは上限解放。">Leveling up means ascending limits.</p>
|
||||
<p class="subpage-section-text" data-lang-en="Characters, items, and cards each serve different roles but share common stats." data-lang-ja="キャラクター、アイテム、カードはそれぞれ異なる役割を持ちつつ、それぞれ固有値、全体値という2つの値を持ちます。">Characters, items, and cards each serve different roles but share common stats.</p>
|
||||
<p class="subpage-section-text" data-lang-en="Items grant EXP. Cards unlock skills." data-lang-ja="アイテムは経験値、カードは技の解放です。">Items grant EXP. Cards unlock skills.</p>
|
||||
</div>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="Characters" data-lang-ja="キャラクター">Characters</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="Character acquisition is seasonal." data-lang-ja="キャラクターの獲得はシーズンになります。">Character acquisition is seasonal.</p>
|
||||
<p class="subpage-section-text" data-lang-en="When a new season begins, new characters become available." data-lang-ja="次のシーズンに移行すると、新しいキャラクターを獲得できます。">When a new season begins, new characters become available.</p>
|
||||
<p class="subpage-section-text" data-lang-en="The season you join determines your starting character." data-lang-ja="参加時のシーズンで最初に扱えるキャラクターが決まります。">The season you join determines your starting character.</p>
|
||||
<p class="subpage-section-text" data-lang-en="Each character has four elements: Attribute (Ai), Skill (Yui), Global Value, and Unique Value." data-lang-ja="キャラクターには、4つの要素があります。属性(アイ)、技(ユイ)、全体値、固有値です。">Each character has four elements: Attribute (Ai), Skill (Yui), Global Value, and Unique Value.</p>
|
||||
<p class="subpage-section-text" data-lang-en="Skills are called Yui in this world — equipping a card unlocks them." data-lang-ja="技はこの世界でユイと呼ばれ、カードを持っていると使えるようになります。">Skills are called Yui in this world — equipping a card unlocks them.</p>
|
||||
<p class="subpage-section-text" data-lang-en="Global Value is called CP, which is distributed across base stats." data-lang-ja="全体値は、CPと呼ばれ、その値が基礎ステータスに割り振られます。">Global Value is called CP, which is distributed across base stats.</p>
|
||||
</div>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="Cards" data-lang-ja="カード">Cards</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="You can draw cards in the iOS app Aicard." data-lang-ja="iOSアプリのAicardでカードを引くことができます。">You can draw cards in the iOS app Aicard.</p>
|
||||
<p class="subpage-section-text" data-lang-en="Owning a card lets you apply it in the game." data-lang-ja="持っていると、ゲームに反映することができます。">Owning a card lets you apply it in the game.</p>
|
||||
</div>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="Story" data-lang-ja="物語">Story</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="The story is a quest for the smallest matter." data-lang-ja="物語は、小さい物質の探求。">The story is a quest for the smallest matter.</p>
|
||||
</div>
|
||||
</div>
|
||||
<button id="btn-about-next" class="nav-btn nav-btn-right" aria-label="Next">
|
||||
<svg viewBox="0 0 48 48" fill="none"><circle cx="24" cy="24" r="23" stroke="url(#nav-grad)" stroke-width="1" fill="none"/><polygon points="20,15 33,24 20,33" fill="url(#nav-grad)"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- PAGE: Title -->
|
||||
<div id="page-title" class="page page-full page-hidden">
|
||||
<div class="hero-video">
|
||||
<video src="/video.mp4" autoplay muted playsinline loop></video>
|
||||
<div class="video-text">
|
||||
<div class="video-title-wrap">
|
||||
<img src="/icon/ai.svg" alt="" class="video-title-icon">
|
||||
<div class="video-label video-label-lg">Airse</div>
|
||||
</div>
|
||||
<div class="video-separator"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
19
rse/package.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "airse-web",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"three": "^0.172.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/three": "^0.172.0",
|
||||
"typescript": "^5.7.0",
|
||||
"vite": "^6.2.0"
|
||||
}
|
||||
}
|
||||
120
rse/public/account/index.html
Normal file
@@ -0,0 +1,120 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" href="/icon/ai.svg" type="image/svg+xml">
|
||||
<title>Account | Airse</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Space+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
:root{--c-bg:#f5f5f8;--c-text:#1a1a2e;--c-accent:#3a7ca5;--c-accent2:#6a5acd;--c-dim:#8888a0;--f-d:'Orbitron',sans-serif;--f-b:'Space Mono',monospace}
|
||||
html,body{width:100%;height:100%;background:var(--c-bg);color:var(--c-text);font-size:calc(1rem + 3px)}
|
||||
.site-header{position:fixed;top:0;left:0;width:100%;height:5rem;padding:0 2rem;display:flex;align-items:center;z-index:110;background:var(--c-bg)}
|
||||
.header-logo{display:inline-flex;align-items:center;gap:.5rem;text-decoration:none;color:var(--c-text);margin-right:auto}
|
||||
.header-logo-icon{width:24px;height:24px}
|
||||
.header-logo-text{font-family:var(--f-d);font-weight:700;font-size:.85rem;letter-spacing:.1em}
|
||||
.site-footer{position:fixed;bottom:0;left:0;width:100%;padding:1.5rem 2rem 2.5rem;z-index:110;display:flex;flex-direction:column;align-items:center;background:var(--c-bg)}
|
||||
.footer-copy{font-family:var(--f-b);font-size:.6rem;letter-spacing:.1em;color:var(--c-dim)}
|
||||
.lang-selector{position:relative}
|
||||
.lang-btn{display:flex;align-items:center;justify-content:center;background:0 0;border:none;border-radius:6px;cursor:pointer;padding:6px;opacity:.4;transition:opacity .3s,background .3s}
|
||||
.lang-btn:hover{opacity:.9;background:rgba(26,26,46,.06)}
|
||||
.lang-icon{width:20px;height:20px}
|
||||
.lang-dropdown{display:none;position:absolute;top:100%;right:0;margin-top:4px;background:#fff;border:1px solid #ddd;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.1);min-width:100px;overflow:hidden}
|
||||
.lang-dropdown.show{display:block}
|
||||
.lang-option{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;cursor:pointer;font-family:var(--f-b);font-size:.75rem;letter-spacing:.05em;transition:background .15s}
|
||||
.lang-option:hover{background:#f0f0f0}
|
||||
.lang-option.selected{background:linear-gradient(135deg,#f0f7ff,#e8f4ff)}
|
||||
.lang-check{width:18px;height:18px;border-radius:50%;border:2px solid #ccc;display:flex;align-items:center;justify-content:center;font-size:10px;transition:all .2s;color:transparent}
|
||||
.lang-option.selected .lang-check{background:var(--c-accent);border-color:var(--c-accent);color:#fff}
|
||||
.menu-selector{position:relative;margin-left:8px}
|
||||
.menu-btn{display:flex;align-items:center;justify-content:center;background:0 0;border:none;border-radius:6px;cursor:pointer;padding:6px;opacity:.4;transition:opacity .3s,background .3s}
|
||||
.menu-btn:hover{opacity:.9;background:rgba(26,26,46,.06)}
|
||||
.menu-icon{width:20px;height:20px}
|
||||
.menu-dropdown{display:none;position:absolute;top:100%;right:0;margin-top:4px;background:#fff;border:1px solid #ddd;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.1);min-width:180px;overflow:hidden}
|
||||
.menu-dropdown.show{display:block}
|
||||
.menu-option{display:flex;align-items:center;padding:10px 14px;cursor:pointer;font-family:var(--f-b);font-size:.75rem;letter-spacing:.05em;transition:background .15s}
|
||||
.menu-option:hover{background:#f0f0f0}
|
||||
a.menu-option{text-decoration:none;color:inherit}
|
||||
.menu-option-active{background:linear-gradient(135deg,#f0f7ff,#e8f4ff)}
|
||||
.page{position:fixed;top:5rem;left:0;width:100%;height:calc(100% - 5rem);z-index:10;display:flex;flex-direction:column;align-items:center;background:var(--c-bg);justify-content:flex-start;overflow-y:auto}
|
||||
.subpage-content{width:100%;max-width:640px;margin:0 auto;padding:4rem 2rem 6rem}
|
||||
.subpage-heading{font-family:var(--f-d);font-weight:700;font-size:clamp(1rem,2.5vw,1.4rem);letter-spacing:.1em;color:var(--c-text);margin-bottom:.8rem}
|
||||
.subpage-section{margin-top:2.5rem}
|
||||
.subpage-section-title{font-family:var(--f-d);font-weight:700;font-size:clamp(.85rem,1.8vw,1.1rem);letter-spacing:.1em;color:var(--c-text);margin-bottom:.8rem}
|
||||
.subpage-section-line{width:clamp(40px,8vw,80px);height:1px;background:linear-gradient(90deg,var(--c-accent),var(--c-accent2),transparent);margin-bottom:1rem}
|
||||
.subpage-section-text{font-family:var(--f-b);font-size:clamp(.65rem,1.1vw,.8rem);line-height:2;letter-spacing:.05em;color:rgba(26,26,46,.6)}
|
||||
.subpage-section-text a{color:var(--c-accent);text-decoration:none;word-break:break-all}
|
||||
.subpage-section-text a:hover{text-decoration:underline}
|
||||
.subpage-section-text code{background:rgba(26,26,46,.06);padding:.15em .4em;border-radius:3px;font-size:.9em}
|
||||
.subpage-img{max-width:100%;border-radius:4px;margin:.5rem 0}
|
||||
@media(max-width:768px){.site-header{padding:.8rem 1rem}.site-footer{padding:1rem 1rem 1.5rem}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="site-header">
|
||||
<a href="/" class="header-logo">
|
||||
<img src="/icon/ai.svg" alt="Airse" class="header-logo-icon">
|
||||
<span class="header-logo-text">Airse</span>
|
||||
</a>
|
||||
<div class="lang-selector">
|
||||
<button type="button" class="lang-btn" id="lang-tab">
|
||||
<img src="/icon/language.svg" alt="Lang" class="lang-icon">
|
||||
</button>
|
||||
<div class="lang-dropdown" id="lang-dropdown">
|
||||
<div class="lang-option selected" data-lang="en"><span class="lang-name">EN</span><span class="lang-check">✓</span></div>
|
||||
<div class="lang-option" data-lang="ja"><span class="lang-name">JA</span><span class="lang-check">✓</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-selector">
|
||||
<button type="button" class="menu-btn" id="menu-btn">
|
||||
<img src="/icon/menu.svg" alt="Menu" class="menu-icon">
|
||||
</button>
|
||||
<div class="menu-dropdown" id="menu-dropdown">
|
||||
<a href="/" class="menu-option"><span class="menu-option-text">Airse</span></a>
|
||||
<a href="/account" class="menu-option menu-option-active"><span class="menu-option-text" data-lang-en="Account" data-lang-ja="アカウント">Account</span></a>
|
||||
<a href="/privacy" class="menu-option"><span class="menu-option-text" data-lang-en="Privacy" data-lang-ja="プライバシー">Privacy</span></a>
|
||||
<a href="/terms" class="menu-option"><span class="menu-option-text" data-lang-en="Terms" data-lang-ja="利用規約">Terms</span></a>
|
||||
<a href="/support" class="menu-option"><span class="menu-option-text" data-lang-en="Support" data-lang-ja="サポート">Support</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<footer class="site-footer"><div class="footer-copy">©syui</div></footer>
|
||||
|
||||
<div class="page">
|
||||
<div class="subpage-content">
|
||||
<h1 class="subpage-heading" data-lang-en="Account" data-lang-ja="アカウント">Account</h1>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="Your SNS account is your game account." data-lang-ja="SNSアカウントがゲームアカウントになります。">Your SNS account is your game account.</p>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="Sign In" data-lang-ja="サインイン">Sign In</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="Sign in with your Bluesky account to access your game data." data-lang-ja="Blueskyアカウントでサインインしてゲームデータにアクセスできます。">Sign in with your Bluesky account to access your game data.</p>
|
||||
<p class="subpage-section-text" data-lang-en="Account data is managed through the AT Protocol." data-lang-ja="アカウントデータはAT Protocolを通じて管理されます。">Account data is managed through the AT Protocol.</p>
|
||||
</div>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="Check Game Data" data-lang-ja="ゲームデータの確認">Check Game Data</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="Access <a href='https://syui.ai/' target='_blank' rel='noopener'>https://syui.ai/</a> and search by your username (handle)." data-lang-ja="<a href='https://syui.ai/' target='_blank' rel='noopener'>https://syui.ai/</a> にアクセスして、自分のusername(handle)で検索します。">Access <a href="https://syui.ai/" target="_blank" rel="noopener">https://syui.ai/</a> and search by your username (handle).</p>
|
||||
<p class="subpage-section-text" data-lang-en="For example, if your name is <code>syui.ai</code>, the URL will be:" data-lang-ja="例えば、<code>syui.ai</code>という名前なら以下のURLになります。">For example, if your name is <code>syui.ai</code>, the URL will be:</p>
|
||||
<p class="subpage-section-text"><a href="https://syui.ai/@syui.ai/at/rse" target="_blank" rel="noopener">https://syui.ai/@syui.ai/at/rse</a></p>
|
||||
</div>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="Delete Game Data" data-lang-ja="ゲームデータの削除">Delete Game Data</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="Log in at <a href='https://syui.ai/' target='_blank' rel='noopener'>https://syui.ai/</a>, then delete the <code>ai.syui.rse.user</code> service." data-lang-ja="<a href='https://syui.ai/' target='_blank' rel='noopener'>https://syui.ai/</a> からログインします。そして、<code>ai.syui.rse.user</code>のサービスを削除します。">Log in at <a href="https://syui.ai/" target="_blank" rel="noopener">https://syui.ai/</a>, then delete the <code>ai.syui.rse.user</code> service.</p>
|
||||
<img src="/img/bluesky_oauth.png" alt="Bluesky OAuth" class="subpage-img">
|
||||
<p class="subpage-section-text" data-lang-en="For example, if your name is <code>syui.ai</code>, you can delete it from the following URL (login required):" data-lang-ja="例えば、<code>syui.ai</code>という名前なら以下のURLから削除することができます。この操作はログインしている場合に限ります。">For example, if your name is <code>syui.ai</code>, you can delete it from the following URL (login required):</p>
|
||||
<p class="subpage-section-text"><a href="https://syui.ai/@syui.ai/at/collection/ai.syui.rse.user/self" target="_blank" rel="noopener">https://syui.ai/@syui.ai/at/collection/ai.syui.rse.user/self</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
!function(){var l=localStorage.getItem('preferred-lang')||'en',d=document,lb=d.getElementById('lang-tab'),ld=d.getElementById('lang-dropdown'),mb=d.getElementById('menu-btn'),md=d.getElementById('menu-dropdown');function a(g){d.querySelectorAll('[data-lang-en]').forEach(function(e){var t=e.getAttribute('data-lang-'+g);if(t)e.innerHTML=t});ld.querySelectorAll('.lang-option').forEach(function(o){o.classList.toggle('selected',o.dataset.lang===g)})}lb.onclick=function(e){e.stopPropagation();ld.classList.toggle('show');md.classList.remove('show')};ld.querySelectorAll('.lang-option').forEach(function(o){o.onclick=function(e){e.stopPropagation();l=o.dataset.lang||'en';localStorage.setItem('preferred-lang',l);a(l);ld.classList.remove('show')}});mb.onclick=function(e){e.stopPropagation();md.classList.toggle('show');ld.classList.remove('show')};d.onclick=function(){ld.classList.remove('show');md.classList.remove('show')};a(l)}()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
19
rse/public/icon/ai.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="1024" height="1024">
|
||||
<path fill-rule="evenodd" fill="#F6E717" d="
|
||||
M 619,232
|
||||
L 512,7
|
||||
L 405,232
|
||||
A 300,300 0 0,0 216,559
|
||||
L 75,765
|
||||
L 323,745
|
||||
A 300,300 0 0,0 701,745
|
||||
L 949,765
|
||||
L 808,559
|
||||
A 300,300 0 0,0 619,232
|
||||
Z
|
||||
M 512,337
|
||||
A 175,175 0 0,0 512,687
|
||||
A 175,175 0 0,0 512,337
|
||||
Z
|
||||
"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 413 B |
1
rse/public/icon/language.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d="M192 64C209.7 64 224 78.3 224 96L224 128L352 128C369.7 128 384 142.3 384 160C384 177.7 369.7 192 352 192L342.4 192L334 215.1C317.6 260.3 292.9 301.6 261.8 337.1C276 345.9 290.8 353.7 306.2 360.6L356.6 383L418.8 243C423.9 231.4 435.4 224 448 224C460.6 224 472.1 231.4 477.2 243L605.2 531C612.4 547.2 605.1 566.1 589 573.2C572.9 580.3 553.9 573.1 546.8 557L526.8 512L369.3 512L349.3 557C342.1 573.2 323.2 580.4 307.1 573.2C291 566 283.7 547.1 290.9 531L330.7 441.5L280.3 419.1C257.3 408.9 235.3 396.7 214.5 382.7C193.2 399.9 169.9 414.9 145 427.4L110.3 444.6C94.5 452.5 75.3 446.1 67.4 430.3C59.5 414.5 65.9 395.3 81.7 387.4L116.2 370.1C132.5 361.9 148 352.4 162.6 341.8C148.8 329.1 135.8 315.4 123.7 300.9L113.6 288.7C102.3 275.1 104.1 254.9 117.7 243.6C131.3 232.3 151.5 234.1 162.8 247.7L173 259.9C184.5 273.8 197.1 286.7 210.4 298.6C237.9 268.2 259.6 232.5 273.9 193.2L274.4 192L64.1 192C46.3 192 32 177.7 32 160C32 142.3 46.3 128 64 128L160 128L160 96C160 78.3 174.3 64 192 64zM448 334.8L397.7 448L498.3 448L448 334.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
1
rse/public/icon/menu.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d="M96 160C96 142.3 110.3 128 128 128L512 128C529.7 128 544 142.3 544 160C544 177.7 529.7 192 512 192L128 192C110.3 192 96 177.7 96 160zM96 320C96 302.3 110.3 288 128 288L512 288C529.7 288 544 302.3 544 320C544 337.7 529.7 352 512 352L128 352C110.3 352 96 337.7 96 320zM544 480C544 497.7 529.7 512 512 512L128 512C110.3 512 96 497.7 96 480C96 462.3 110.3 448 128 448L512 448C529.7 448 544 462.3 544 480z"/></svg>
|
||||
|
After Width: | Height: | Size: 631 B |
BIN
rse/public/img/bluesky_oauth.png
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
rse/public/img/h.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
rse/public/img/hh.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
rse/public/img/logo.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
rse/public/img/w.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
rse/public/img/ww.png
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
142
rse/public/privacy/index.html
Normal file
@@ -0,0 +1,142 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" href="/icon/ai.svg" type="image/svg+xml">
|
||||
<title>Privacy Policy | Airse</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Space+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
:root{--c-bg:#f5f5f8;--c-text:#1a1a2e;--c-accent:#3a7ca5;--c-accent2:#6a5acd;--c-dim:#8888a0;--f-d:'Orbitron',sans-serif;--f-b:'Space Mono',monospace}
|
||||
html,body{width:100%;height:100%;background:var(--c-bg);color:var(--c-text);font-size:calc(1rem + 3px)}
|
||||
.site-header{position:fixed;top:0;left:0;width:100%;height:5rem;padding:0 2rem;display:flex;align-items:center;z-index:110;background:var(--c-bg)}
|
||||
.header-logo{display:inline-flex;align-items:center;gap:.5rem;text-decoration:none;color:var(--c-text);margin-right:auto}
|
||||
.header-logo-icon{width:24px;height:24px}
|
||||
.header-logo-text{font-family:var(--f-d);font-weight:700;font-size:.85rem;letter-spacing:.1em}
|
||||
.site-footer{position:fixed;bottom:0;left:0;width:100%;padding:1.5rem 2rem 2.5rem;z-index:110;display:flex;flex-direction:column;align-items:center;background:var(--c-bg)}
|
||||
.footer-copy{font-family:var(--f-b);font-size:.6rem;letter-spacing:.1em;color:var(--c-dim)}
|
||||
.lang-selector{position:relative}
|
||||
.lang-btn{display:flex;align-items:center;justify-content:center;background:0 0;border:none;border-radius:6px;cursor:pointer;padding:6px;opacity:.4;transition:opacity .3s,background .3s}
|
||||
.lang-btn:hover{opacity:.9;background:rgba(26,26,46,.06)}
|
||||
.lang-icon{width:20px;height:20px}
|
||||
.lang-dropdown{display:none;position:absolute;top:100%;right:0;margin-top:4px;background:#fff;border:1px solid #ddd;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.1);min-width:100px;overflow:hidden}
|
||||
.lang-dropdown.show{display:block}
|
||||
.lang-option{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;cursor:pointer;font-family:var(--f-b);font-size:.75rem;letter-spacing:.05em;transition:background .15s}
|
||||
.lang-option:hover{background:#f0f0f0}
|
||||
.lang-option.selected{background:linear-gradient(135deg,#f0f7ff,#e8f4ff)}
|
||||
.lang-check{width:18px;height:18px;border-radius:50%;border:2px solid #ccc;display:flex;align-items:center;justify-content:center;font-size:10px;transition:all .2s;color:transparent}
|
||||
.lang-option.selected .lang-check{background:var(--c-accent);border-color:var(--c-accent);color:#fff}
|
||||
.menu-selector{position:relative;margin-left:8px}
|
||||
.menu-btn{display:flex;align-items:center;justify-content:center;background:0 0;border:none;border-radius:6px;cursor:pointer;padding:6px;opacity:.4;transition:opacity .3s,background .3s}
|
||||
.menu-btn:hover{opacity:.9;background:rgba(26,26,46,.06)}
|
||||
.menu-icon{width:20px;height:20px}
|
||||
.menu-dropdown{display:none;position:absolute;top:100%;right:0;margin-top:4px;background:#fff;border:1px solid #ddd;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.1);min-width:180px;overflow:hidden}
|
||||
.menu-dropdown.show{display:block}
|
||||
.menu-option{display:flex;align-items:center;padding:10px 14px;cursor:pointer;font-family:var(--f-b);font-size:.75rem;letter-spacing:.05em;transition:background .15s}
|
||||
.menu-option:hover{background:#f0f0f0}
|
||||
a.menu-option{text-decoration:none;color:inherit}
|
||||
.menu-option-active{background:linear-gradient(135deg,#f0f7ff,#e8f4ff)}
|
||||
.page{position:fixed;top:5rem;left:0;width:100%;height:calc(100% - 5rem);z-index:10;display:flex;flex-direction:column;align-items:center;background:var(--c-bg);justify-content:flex-start;overflow-y:auto}
|
||||
.subpage-content{width:100%;max-width:640px;margin:0 auto;padding:4rem 2rem 6rem}
|
||||
.subpage-heading{font-family:var(--f-d);font-weight:700;font-size:clamp(1rem,2.5vw,1.4rem);letter-spacing:.1em;color:var(--c-text);margin-bottom:.8rem}
|
||||
.subpage-section{margin-top:2.5rem}
|
||||
.subpage-section-title{font-family:var(--f-d);font-weight:700;font-size:clamp(.85rem,1.8vw,1.1rem);letter-spacing:.1em;color:var(--c-text);margin-bottom:.8rem}
|
||||
.subpage-section-line{width:clamp(40px,8vw,80px);height:1px;background:linear-gradient(90deg,var(--c-accent),var(--c-accent2),transparent);margin-bottom:1rem}
|
||||
.subpage-section-text{font-family:var(--f-b);font-size:clamp(.65rem,1.1vw,.8rem);line-height:2;letter-spacing:.05em;color:rgba(26,26,46,.6)}
|
||||
.subpage-list{list-style:none;margin-top:1.5rem;display:flex;flex-direction:column;gap:1rem}
|
||||
.subpage-list-item{font-family:var(--f-b);font-size:clamp(.65rem,1.1vw,.8rem);line-height:2;letter-spacing:.05em;color:rgba(26,26,46,.6);padding-left:1.2em;position:relative}
|
||||
.subpage-list-item::before{content:'';position:absolute;left:0;top:.85em;width:6px;height:6px;border-radius:50%;background:linear-gradient(135deg,var(--c-accent),var(--c-accent2))}
|
||||
.subpage-section-text a{color:var(--c-accent);text-decoration:none;word-break:break-all}
|
||||
.subpage-section-text a:hover{text-decoration:underline}
|
||||
.subpage-section-text code{background:rgba(26,26,46,.06);padding:.15em .4em;border-radius:3px;font-size:.9em}
|
||||
@media(max-width:768px){.site-header{padding:.8rem 1rem}.site-footer{padding:1rem 1rem 1.5rem}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="site-header">
|
||||
<a href="/" class="header-logo">
|
||||
<img src="/icon/ai.svg" alt="Airse" class="header-logo-icon">
|
||||
<span class="header-logo-text">Airse</span>
|
||||
</a>
|
||||
<div class="lang-selector">
|
||||
<button type="button" class="lang-btn" id="lang-tab">
|
||||
<img src="/icon/language.svg" alt="Lang" class="lang-icon">
|
||||
</button>
|
||||
<div class="lang-dropdown" id="lang-dropdown">
|
||||
<div class="lang-option selected" data-lang="en"><span class="lang-name">EN</span><span class="lang-check">✓</span></div>
|
||||
<div class="lang-option" data-lang="ja"><span class="lang-name">JA</span><span class="lang-check">✓</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-selector">
|
||||
<button type="button" class="menu-btn" id="menu-btn">
|
||||
<img src="/icon/menu.svg" alt="Menu" class="menu-icon">
|
||||
</button>
|
||||
<div class="menu-dropdown" id="menu-dropdown">
|
||||
<a href="/" class="menu-option"><span class="menu-option-text">Airse</span></a>
|
||||
<a href="/account" class="menu-option"><span class="menu-option-text" data-lang-en="Account" data-lang-ja="アカウント">Account</span></a>
|
||||
<a href="/privacy" class="menu-option menu-option-active"><span class="menu-option-text" data-lang-en="Privacy" data-lang-ja="プライバシー">Privacy</span></a>
|
||||
<a href="/terms" class="menu-option"><span class="menu-option-text" data-lang-en="Terms" data-lang-ja="利用規約">Terms</span></a>
|
||||
<a href="/support" class="menu-option"><span class="menu-option-text" data-lang-en="Support" data-lang-ja="サポート">Support</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<footer class="site-footer"><div class="footer-copy">©syui</div></footer>
|
||||
|
||||
<div class="page">
|
||||
<div class="subpage-content">
|
||||
<h1 class="subpage-heading" data-lang-en="Privacy Policy" data-lang-ja="プライバシーポリシー">Privacy Policy</h1>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="Airse is an open-world action game that utilizes the AT Protocol for account authentication. This Privacy Policy explains how we handle your information when you use our game." data-lang-ja="Airseは、AT Protocolをアカウント認証に利用するオープンワールドアクションゲームです。本プライバシーポリシーは、ゲーム利用時における情報の取り扱いについて説明します。">Airse is an open-world action game that utilizes the AT Protocol for account authentication. This Privacy Policy explains how we handle your information when you use our game.</p>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="Information Collection" data-lang-ja="情報の収集">Information Collection</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="We do not collect any personal information." data-lang-ja="個人情報は一切収集しません。">We do not collect any personal information.</p>
|
||||
<p class="subpage-section-text" data-lang-en="Airse does not track, store, or analyze any user data, gameplay statistics, or personal information. Your gaming experience remains private." data-lang-ja="Airseはユーザーデータ、ゲームプレイ統計、個人情報の追跡・保存・分析を行いません。ゲーム体験はプライベートに保たれます。">Airse does not track, store, or analyze any user data, gameplay statistics, or personal information. Your gaming experience remains private.</p>
|
||||
</div>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="Account Authentication" data-lang-ja="アカウント認証">Account Authentication</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="Airse uses AT Protocol for account login functionality. When you log in:" data-lang-ja="AirseはAT Protocolをアカウントログインに使用します。ログイン時:">Airse uses AT Protocol for account login functionality. When you log in:</p>
|
||||
<ul class="subpage-list">
|
||||
<li class="subpage-list-item" data-lang-en="We only use your AT Protocol identity for authentication purposes." data-lang-ja="AT Protocolのアイデンティティは認証目的のみに使用します。">We only use your AT Protocol identity for authentication purposes.</li>
|
||||
<li class="subpage-list-item" data-lang-en="No additional data is collected or stored beyond what is necessary for login." data-lang-ja="ログインに必要な範囲を超えたデータの収集・保存は行いません。">No additional data is collected or stored beyond what is necessary for login.</li>
|
||||
<li class="subpage-list-item" data-lang-en="Your credentials are handled securely through the AT Protocol." data-lang-ja="認証情報はAT Protocolを通じて安全に処理されます。">Your credentials are handled securely through the AT Protocol.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="syu.is Service Users" data-lang-ja="syu.isサービス利用者">syu.is Service Users</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<ul class="subpage-list">
|
||||
<li class="subpage-list-item" data-lang-en="The service operator reserves the right to suspend or terminate (ban) accounts at their discretion." data-lang-ja="サービス運営者は、自身の裁量でアカウントの停止または終了(BAN)を行う権利を有します。">The service operator reserves the right to suspend or terminate (ban) accounts at their discretion.</li>
|
||||
<li class="subpage-list-item" data-lang-en="This may occur in cases of terms of service violations or inappropriate behavior." data-lang-ja="利用規約違反や不適切な行為があった場合に行われることがあります。">This may occur in cases of terms of service violations or inappropriate behavior.</li>
|
||||
<li class="subpage-list-item" data-lang-en="Account suspension decisions are final." data-lang-ja="アカウント停止の判断は最終的なものです。">Account suspension decisions are final.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="Third-Party Services" data-lang-ja="第三者サービス">Third-Party Services</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="Airse relies on AT Protocol infrastructure. Please refer to the privacy policies of your AT Protocol provider (PDS) for information about how they handle your data." data-lang-ja="AirseはAT Protocolのインフラに依存しています。データの取り扱いについては、ご利用のAT Protocolプロバイダー(PDS)のプライバシーポリシーをご参照ください。">Airse relies on AT Protocol infrastructure. Please refer to the privacy policies of your AT Protocol provider (PDS) for information about how they handle your data.</p>
|
||||
</div>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="Changes to This Policy" data-lang-ja="ポリシーの変更">Changes to This Policy</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="We may update this Privacy Policy from time to time. Any changes will be posted on this page with an updated revision date." data-lang-ja="本プライバシーポリシーは随時更新されることがあります。変更は本ページに更新日とともに掲載されます。">We may update this Privacy Policy from time to time. Any changes will be posted on this page with an updated revision date.</p>
|
||||
</div>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title" data-lang-en="Contact" data-lang-ja="お問い合わせ">Contact</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="If you have any questions about this Privacy Policy, please contact the developer." data-lang-ja="本プライバシーポリシーについてご質問がある場合は、開発者にお問い合わせください。">If you have any questions about this Privacy Policy, please contact the developer.</p>
|
||||
<p class="subpage-section-text"><a href="https://github.com/syui" target="_blank" rel="noopener">https://github.com/syui</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
!function(){var l=localStorage.getItem('preferred-lang')||'en',d=document,lb=d.getElementById('lang-tab'),ld=d.getElementById('lang-dropdown'),mb=d.getElementById('menu-btn'),md=d.getElementById('menu-dropdown');function a(g){d.querySelectorAll('[data-lang-en]').forEach(function(e){var t=e.getAttribute('data-lang-'+g);if(t)e.innerHTML=t});ld.querySelectorAll('.lang-option').forEach(function(o){o.classList.toggle('selected',o.dataset.lang===g)})}lb.onclick=function(e){e.stopPropagation();ld.classList.toggle('show');md.classList.remove('show')};ld.querySelectorAll('.lang-option').forEach(function(o){o.onclick=function(e){e.stopPropagation();l=o.dataset.lang||'en';localStorage.setItem('preferred-lang',l);a(l);ld.classList.remove('show')}});mb.onclick=function(e){e.stopPropagation();md.classList.toggle('show');ld.classList.remove('show')};d.onclick=function(){ld.classList.remove('show');md.classList.remove('show')};a(l)}()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
rse/public/service/ai.syui.at.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
rse/public/service/ai.syui.card.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
rse/public/service/ai.syui.rse.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
108
rse/public/support/index.html
Normal file
@@ -0,0 +1,108 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" href="/icon/ai.svg" type="image/svg+xml">
|
||||
<title>Support | Airse</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Space+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
:root{--c-bg:#f5f5f8;--c-text:#1a1a2e;--c-accent:#3a7ca5;--c-accent2:#6a5acd;--c-dim:#8888a0;--f-d:'Orbitron',sans-serif;--f-b:'Space Mono',monospace}
|
||||
html,body{width:100%;height:100%;background:var(--c-bg);color:var(--c-text);font-size:calc(1rem + 3px)}
|
||||
.site-header{position:fixed;top:0;left:0;width:100%;height:5rem;padding:0 2rem;display:flex;align-items:center;z-index:110;background:var(--c-bg)}
|
||||
.header-logo{display:inline-flex;align-items:center;gap:.5rem;text-decoration:none;color:var(--c-text);margin-right:auto}
|
||||
.header-logo-icon{width:24px;height:24px}
|
||||
.header-logo-text{font-family:var(--f-d);font-weight:700;font-size:.85rem;letter-spacing:.1em}
|
||||
.site-footer{position:fixed;bottom:0;left:0;width:100%;padding:1.5rem 2rem 2.5rem;z-index:110;display:flex;flex-direction:column;align-items:center;background:var(--c-bg)}
|
||||
.footer-copy{font-family:var(--f-b);font-size:.6rem;letter-spacing:.1em;color:var(--c-dim)}
|
||||
.lang-selector{position:relative}
|
||||
.lang-btn{display:flex;align-items:center;justify-content:center;background:0 0;border:none;border-radius:6px;cursor:pointer;padding:6px;opacity:.4;transition:opacity .3s,background .3s}
|
||||
.lang-btn:hover{opacity:.9;background:rgba(26,26,46,.06)}
|
||||
.lang-icon{width:20px;height:20px}
|
||||
.lang-dropdown{display:none;position:absolute;top:100%;right:0;margin-top:4px;background:#fff;border:1px solid #ddd;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.1);min-width:100px;overflow:hidden}
|
||||
.lang-dropdown.show{display:block}
|
||||
.lang-option{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;cursor:pointer;font-family:var(--f-b);font-size:.75rem;letter-spacing:.05em;transition:background .15s}
|
||||
.lang-option:hover{background:#f0f0f0}
|
||||
.lang-option.selected{background:linear-gradient(135deg,#f0f7ff,#e8f4ff)}
|
||||
.lang-check{width:18px;height:18px;border-radius:50%;border:2px solid #ccc;display:flex;align-items:center;justify-content:center;font-size:10px;transition:all .2s;color:transparent}
|
||||
.lang-option.selected .lang-check{background:var(--c-accent);border-color:var(--c-accent);color:#fff}
|
||||
.menu-selector{position:relative;margin-left:8px}
|
||||
.menu-btn{display:flex;align-items:center;justify-content:center;background:0 0;border:none;border-radius:6px;cursor:pointer;padding:6px;opacity:.4;transition:opacity .3s,background .3s}
|
||||
.menu-btn:hover{opacity:.9;background:rgba(26,26,46,.06)}
|
||||
.menu-icon{width:20px;height:20px}
|
||||
.menu-dropdown{display:none;position:absolute;top:100%;right:0;margin-top:4px;background:#fff;border:1px solid #ddd;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.1);min-width:180px;overflow:hidden}
|
||||
.menu-dropdown.show{display:block}
|
||||
.menu-option{display:flex;align-items:center;padding:10px 14px;cursor:pointer;font-family:var(--f-b);font-size:.75rem;letter-spacing:.05em;transition:background .15s}
|
||||
.menu-option:hover{background:#f0f0f0}
|
||||
a.menu-option{text-decoration:none;color:inherit}
|
||||
.menu-option-active{background:linear-gradient(135deg,#f0f7ff,#e8f4ff)}
|
||||
.page{position:fixed;top:5rem;left:0;width:100%;height:calc(100% - 5rem);z-index:10;display:flex;flex-direction:column;align-items:center;background:var(--c-bg);justify-content:flex-start;overflow-y:auto}
|
||||
.subpage-content{width:100%;max-width:640px;margin:0 auto;padding:4rem 2rem 6rem}
|
||||
.subpage-heading{font-family:var(--f-d);font-weight:700;font-size:clamp(1rem,2.5vw,1.4rem);letter-spacing:.1em;color:var(--c-text);margin-bottom:.8rem}
|
||||
.subpage-section{margin-top:2.5rem}
|
||||
.subpage-section-title{font-family:var(--f-d);font-weight:700;font-size:clamp(.85rem,1.8vw,1.1rem);letter-spacing:.1em;color:var(--c-text);margin-bottom:.8rem}
|
||||
.subpage-section-line{width:clamp(40px,8vw,80px);height:1px;background:linear-gradient(90deg,var(--c-accent),var(--c-accent2),transparent);margin-bottom:1rem}
|
||||
.subpage-section-text{font-family:var(--f-b);font-size:clamp(.65rem,1.1vw,.8rem);line-height:2;letter-spacing:.05em;color:rgba(26,26,46,.6)}
|
||||
.subpage-section-text a{color:var(--c-accent);text-decoration:none;word-break:break-all}
|
||||
.subpage-section-text a:hover{text-decoration:underline}
|
||||
@media(max-width:768px){.site-header{padding:.8rem 1rem}.site-footer{padding:1rem 1rem 1.5rem}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="site-header">
|
||||
<a href="/" class="header-logo">
|
||||
<img src="/icon/ai.svg" alt="Airse" class="header-logo-icon">
|
||||
<span class="header-logo-text">Airse</span>
|
||||
</a>
|
||||
<div class="lang-selector">
|
||||
<button type="button" class="lang-btn" id="lang-tab">
|
||||
<img src="/icon/language.svg" alt="Lang" class="lang-icon">
|
||||
</button>
|
||||
<div class="lang-dropdown" id="lang-dropdown">
|
||||
<div class="lang-option selected" data-lang="en"><span class="lang-name">EN</span><span class="lang-check">✓</span></div>
|
||||
<div class="lang-option" data-lang="ja"><span class="lang-name">JA</span><span class="lang-check">✓</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-selector">
|
||||
<button type="button" class="menu-btn" id="menu-btn">
|
||||
<img src="/icon/menu.svg" alt="Menu" class="menu-icon">
|
||||
</button>
|
||||
<div class="menu-dropdown" id="menu-dropdown">
|
||||
<a href="/" class="menu-option"><span class="menu-option-text">Airse</span></a>
|
||||
<a href="/account" class="menu-option"><span class="menu-option-text" data-lang-en="Account" data-lang-ja="アカウント">Account</span></a>
|
||||
<a href="/privacy" class="menu-option"><span class="menu-option-text" data-lang-en="Privacy" data-lang-ja="プライバシー">Privacy</span></a>
|
||||
<a href="/terms" class="menu-option"><span class="menu-option-text" data-lang-en="Terms" data-lang-ja="利用規約">Terms</span></a>
|
||||
<a href="/support" class="menu-option menu-option-active"><span class="menu-option-text" data-lang-en="Support" data-lang-ja="サポート">Support</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<footer class="site-footer"><div class="footer-copy">©syui</div></footer>
|
||||
|
||||
<div class="page">
|
||||
<div class="subpage-content">
|
||||
<h1 class="subpage-heading" data-lang-en="Support" data-lang-ja="サポート">Support</h1>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="If you have questions, feedback, or need help with Airse, please reach out through the following channels." data-lang-ja="Airseについてのご質問、フィードバック、サポートが必要な場合は、以下からご連絡ください。">If you have questions, feedback, or need help with Airse, please reach out through the following channels.</p>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title">GitHub</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text" data-lang-en="Source code and issue tracker." data-lang-ja="ソースコードとイシュートラッカー。">Source code and issue tracker.</p>
|
||||
<p class="subpage-section-text"><a href="https://github.com/syui" target="_blank" rel="noopener">https://github.com/syui</a></p>
|
||||
</div>
|
||||
<div class="subpage-section">
|
||||
<h2 class="subpage-section-title">Bluesky</h2>
|
||||
<div class="subpage-section-line"></div>
|
||||
<p class="subpage-section-text"><a href="https://bsky.app/profile/syui.ai" target="_blank" rel="noopener">@syui.ai</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
!function(){var l=localStorage.getItem('preferred-lang')||'en',d=document,lb=d.getElementById('lang-tab'),ld=d.getElementById('lang-dropdown'),mb=d.getElementById('menu-btn'),md=d.getElementById('menu-dropdown');function a(g){d.querySelectorAll('[data-lang-en]').forEach(function(e){var t=e.getAttribute('data-lang-'+g);if(t)e.innerHTML=t});ld.querySelectorAll('.lang-option').forEach(function(o){o.classList.toggle('selected',o.dataset.lang===g)})}lb.onclick=function(e){e.stopPropagation();ld.classList.toggle('show');md.classList.remove('show')};ld.querySelectorAll('.lang-option').forEach(function(o){o.onclick=function(e){e.stopPropagation();l=o.dataset.lang||'en';localStorage.setItem('preferred-lang',l);a(l);ld.classList.remove('show')}});mb.onclick=function(e){e.stopPropagation();md.classList.toggle('show');ld.classList.remove('show')};d.onclick=function(){ld.classList.remove('show');md.classList.remove('show')};a(l)}()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
99
rse/public/terms/index.html
Normal file
@@ -0,0 +1,99 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" href="/icon/ai.svg" type="image/svg+xml">
|
||||
<title>Terms | Airse</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Space+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
:root{--c-bg:#f5f5f8;--c-text:#1a1a2e;--c-accent:#3a7ca5;--c-accent2:#6a5acd;--c-dim:#8888a0;--f-d:'Orbitron',sans-serif;--f-b:'Space Mono',monospace}
|
||||
html,body{width:100%;height:100%;background:var(--c-bg);color:var(--c-text);font-size:calc(1rem + 3px)}
|
||||
.site-header{position:fixed;top:0;left:0;width:100%;height:5rem;padding:0 2rem;display:flex;align-items:center;z-index:110;background:var(--c-bg)}
|
||||
.header-logo{display:inline-flex;align-items:center;gap:.5rem;text-decoration:none;color:var(--c-text);margin-right:auto}
|
||||
.header-logo-icon{width:24px;height:24px}
|
||||
.header-logo-text{font-family:var(--f-d);font-weight:700;font-size:.85rem;letter-spacing:.1em}
|
||||
.site-footer{position:fixed;bottom:0;left:0;width:100%;padding:1.5rem 2rem 2.5rem;z-index:110;display:flex;flex-direction:column;align-items:center;background:var(--c-bg)}
|
||||
.footer-copy{font-family:var(--f-b);font-size:.6rem;letter-spacing:.1em;color:var(--c-dim)}
|
||||
.lang-selector{position:relative}
|
||||
.lang-btn{display:flex;align-items:center;justify-content:center;background:0 0;border:none;border-radius:6px;cursor:pointer;padding:6px;opacity:.4;transition:opacity .3s,background .3s}
|
||||
.lang-btn:hover{opacity:.9;background:rgba(26,26,46,.06)}
|
||||
.lang-icon{width:20px;height:20px}
|
||||
.lang-dropdown{display:none;position:absolute;top:100%;right:0;margin-top:4px;background:#fff;border:1px solid #ddd;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.1);min-width:100px;overflow:hidden}
|
||||
.lang-dropdown.show{display:block}
|
||||
.lang-option{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;cursor:pointer;font-family:var(--f-b);font-size:.75rem;letter-spacing:.05em;transition:background .15s}
|
||||
.lang-option:hover{background:#f0f0f0}
|
||||
.lang-option.selected{background:linear-gradient(135deg,#f0f7ff,#e8f4ff)}
|
||||
.lang-check{width:18px;height:18px;border-radius:50%;border:2px solid #ccc;display:flex;align-items:center;justify-content:center;font-size:10px;transition:all .2s;color:transparent}
|
||||
.lang-option.selected .lang-check{background:var(--c-accent);border-color:var(--c-accent);color:#fff}
|
||||
.menu-selector{position:relative;margin-left:8px}
|
||||
.menu-btn{display:flex;align-items:center;justify-content:center;background:0 0;border:none;border-radius:6px;cursor:pointer;padding:6px;opacity:.4;transition:opacity .3s,background .3s}
|
||||
.menu-btn:hover{opacity:.9;background:rgba(26,26,46,.06)}
|
||||
.menu-icon{width:20px;height:20px}
|
||||
.menu-dropdown{display:none;position:absolute;top:100%;right:0;margin-top:4px;background:#fff;border:1px solid #ddd;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.1);min-width:180px;overflow:hidden}
|
||||
.menu-dropdown.show{display:block}
|
||||
.menu-option{display:flex;align-items:center;padding:10px 14px;cursor:pointer;font-family:var(--f-b);font-size:.75rem;letter-spacing:.05em;transition:background .15s}
|
||||
.menu-option:hover{background:#f0f0f0}
|
||||
a.menu-option{text-decoration:none;color:inherit}
|
||||
.menu-option-active{background:linear-gradient(135deg,#f0f7ff,#e8f4ff)}
|
||||
.page{position:fixed;top:5rem;left:0;width:100%;height:calc(100% - 5rem);z-index:10;display:flex;flex-direction:column;align-items:center;background:var(--c-bg);justify-content:flex-start;overflow-y:auto}
|
||||
.subpage-content{width:100%;max-width:640px;margin:0 auto;padding:4rem 2rem 6rem}
|
||||
.subpage-heading{font-family:var(--f-d);font-weight:700;font-size:clamp(1rem,2.5vw,1.4rem);letter-spacing:.1em;color:var(--c-text);margin-bottom:.8rem}
|
||||
.subpage-section-line{width:clamp(40px,8vw,80px);height:1px;background:linear-gradient(90deg,var(--c-accent),var(--c-accent2),transparent);margin-bottom:1rem}
|
||||
.subpage-list{list-style:none;margin-top:1.5rem;display:flex;flex-direction:column;gap:1rem}
|
||||
.subpage-list-item{font-family:var(--f-b);font-size:clamp(.65rem,1.1vw,.8rem);line-height:2;letter-spacing:.05em;color:rgba(26,26,46,.6);padding-left:1.2em;position:relative}
|
||||
.subpage-list-item::before{content:'';position:absolute;left:0;top:.85em;width:6px;height:6px;border-radius:50%;background:linear-gradient(135deg,var(--c-accent),var(--c-accent2))}
|
||||
@media(max-width:768px){.site-header{padding:.8rem 1rem}.site-footer{padding:1rem 1rem 1.5rem}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="site-header">
|
||||
<a href="/" class="header-logo">
|
||||
<img src="/icon/ai.svg" alt="Airse" class="header-logo-icon">
|
||||
<span class="header-logo-text">Airse</span>
|
||||
</a>
|
||||
<div class="lang-selector">
|
||||
<button type="button" class="lang-btn" id="lang-tab">
|
||||
<img src="/icon/language.svg" alt="Lang" class="lang-icon">
|
||||
</button>
|
||||
<div class="lang-dropdown" id="lang-dropdown">
|
||||
<div class="lang-option selected" data-lang="en"><span class="lang-name">EN</span><span class="lang-check">✓</span></div>
|
||||
<div class="lang-option" data-lang="ja"><span class="lang-name">JA</span><span class="lang-check">✓</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-selector">
|
||||
<button type="button" class="menu-btn" id="menu-btn">
|
||||
<img src="/icon/menu.svg" alt="Menu" class="menu-icon">
|
||||
</button>
|
||||
<div class="menu-dropdown" id="menu-dropdown">
|
||||
<a href="/" class="menu-option"><span class="menu-option-text">Airse</span></a>
|
||||
<a href="/account" class="menu-option"><span class="menu-option-text" data-lang-en="Account" data-lang-ja="アカウント">Account</span></a>
|
||||
<a href="/privacy" class="menu-option"><span class="menu-option-text" data-lang-en="Privacy" data-lang-ja="プライバシー">Privacy</span></a>
|
||||
<a href="/terms" class="menu-option menu-option-active"><span class="menu-option-text" data-lang-en="Terms" data-lang-ja="利用規約">Terms</span></a>
|
||||
<a href="/support" class="menu-option"><span class="menu-option-text" data-lang-en="Support" data-lang-ja="サポート">Support</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<footer class="site-footer"><div class="footer-copy">©syui</div></footer>
|
||||
|
||||
<div class="page">
|
||||
<div class="subpage-content">
|
||||
<h1 class="subpage-heading" data-lang-en="Terms" data-lang-ja="利用規約">Terms</h1>
|
||||
<div class="subpage-section-line"></div>
|
||||
<ul class="subpage-list">
|
||||
<li class="subpage-list-item" data-lang-en="The service may be terminated at any time." data-lang-ja="サービスは終了することがあります。">The service may be terminated at any time.</li>
|
||||
<li class="subpage-list-item" data-lang-en="Game data may be lost." data-lang-ja="ゲームデータは失われることがあります。">Game data may be lost.</li>
|
||||
<li class="subpage-list-item" data-lang-en="Accounts created on syu.is may be lost." data-lang-ja="syu.isに作成されたアカウントは失われることがあります。">Accounts created on syu.is may be lost.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
!function(){var l=localStorage.getItem('preferred-lang')||'en',d=document,lb=d.getElementById('lang-tab'),ld=d.getElementById('lang-dropdown'),mb=d.getElementById('menu-btn'),md=d.getElementById('menu-dropdown');function a(g){d.querySelectorAll('[data-lang-en]').forEach(function(e){var t=e.getAttribute('data-lang-'+g);if(t)e.innerHTML=t});ld.querySelectorAll('.lang-option').forEach(function(o){o.classList.toggle('selected',o.dataset.lang===g)})}lb.onclick=function(e){e.stopPropagation();ld.classList.toggle('show');md.classList.remove('show')};ld.querySelectorAll('.lang-option').forEach(function(o){o.onclick=function(e){e.stopPropagation();l=o.dataset.lang||'en';localStorage.setItem('preferred-lang',l);a(l);ld.classList.remove('show')}});mb.onclick=function(e){e.stopPropagation();md.classList.toggle('show');ld.classList.remove('show')};d.onclick=function(){ld.classList.remove('show');md.classList.remove('show')};a(l)}()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
rse/public/video.mp4
Normal file
146
rse/src/main.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import './style.css'
|
||||
import * as THREE from 'three'
|
||||
import { createScene, updateScene } from './scene'
|
||||
|
||||
// Scene setup
|
||||
const canvas = document.getElementById('space-canvas') as HTMLCanvasElement
|
||||
const sceneObjs = createScene(canvas)
|
||||
|
||||
// Mouse / Touch
|
||||
let targetX = 0
|
||||
let targetY = 0
|
||||
let smoothX = 0
|
||||
let smoothY = 0
|
||||
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
targetX = (e.clientX / window.innerWidth - 0.5) * 2
|
||||
targetY = (e.clientY / window.innerHeight - 0.5) * 2
|
||||
})
|
||||
|
||||
document.addEventListener('touchmove', (e) => {
|
||||
const t = e.touches[0]
|
||||
targetX = (t.clientX / window.innerWidth - 0.5) * 2
|
||||
targetY = (t.clientY / window.innerHeight - 0.5) * 2
|
||||
}, { passive: true })
|
||||
|
||||
// Animation loop
|
||||
let time = 0
|
||||
const clock = new THREE.Clock()
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate)
|
||||
const dt = clock.getDelta()
|
||||
time += dt
|
||||
|
||||
smoothX += (targetX - smoothX) * 0.025
|
||||
smoothY += (targetY - smoothY) * 0.025
|
||||
|
||||
updateScene(sceneObjs, time, dt, smoothX, smoothY)
|
||||
sceneObjs.renderer.render(sceneObjs.scene, sceneObjs.camera)
|
||||
}
|
||||
|
||||
animate()
|
||||
|
||||
// Resize
|
||||
window.addEventListener('resize', () => {
|
||||
sceneObjs.camera.aspect = window.innerWidth / window.innerHeight
|
||||
sceneObjs.camera.updateProjectionMatrix()
|
||||
sceneObjs.renderer.setSize(window.innerWidth, window.innerHeight)
|
||||
})
|
||||
|
||||
// Page navigation
|
||||
const pageVideo = document.getElementById('page-video')
|
||||
const pageLogo = document.getElementById('page-logo')
|
||||
const pageMessage = document.getElementById('page-message')
|
||||
const pageMessage2 = document.getElementById('page-message2')
|
||||
const pageAbout = document.getElementById('page-about')
|
||||
const pageTitle = document.getElementById('page-title')
|
||||
const menuBtn = document.getElementById('menu-btn')
|
||||
const menuDropdown = document.getElementById('menu-dropdown')
|
||||
|
||||
let currentPage: HTMLElement | null = null
|
||||
|
||||
const siteHeader = document.getElementById('site-header')
|
||||
const siteFooter = document.getElementById('site-footer')
|
||||
const subpages = [pageAbout]
|
||||
const fullpages = [pageTitle]
|
||||
|
||||
function showPage(show: HTMLElement | null, hide: HTMLElement | null) {
|
||||
if (hide) {
|
||||
hide.classList.remove('visible')
|
||||
hide.classList.add('page-hidden')
|
||||
}
|
||||
if (show) {
|
||||
show.classList.remove('page-hidden')
|
||||
show.classList.add('visible')
|
||||
}
|
||||
currentPage = show
|
||||
const isFull = fullpages.includes(show)
|
||||
if (siteHeader) siteHeader.style.display = isFull ? 'none' : ''
|
||||
if (siteFooter) {
|
||||
siteFooter.style.display = isFull ? 'none' : ''
|
||||
siteFooter.classList.toggle('footer-solid', subpages.includes(show))
|
||||
}
|
||||
}
|
||||
|
||||
// Main page navigation
|
||||
document.getElementById('btn-next')?.addEventListener('click', () => showPage(pageLogo, pageVideo))
|
||||
document.getElementById('btn-logo-back')?.addEventListener('click', () => showPage(pageVideo, pageLogo))
|
||||
document.getElementById('btn-logo-next')?.addEventListener('click', () => showPage(pageMessage, pageLogo))
|
||||
document.getElementById('btn-msg-back')?.addEventListener('click', () => showPage(pageLogo, pageMessage))
|
||||
document.getElementById('btn-msg-next')?.addEventListener('click', () => showPage(pageMessage2, pageMessage))
|
||||
document.getElementById('btn-msg2-back')?.addEventListener('click', () => showPage(pageMessage, pageMessage2))
|
||||
document.getElementById('btn-msg2-next')?.addEventListener('click', () => showPage(pageAbout, pageMessage2))
|
||||
document.getElementById('btn-about-back')?.addEventListener('click', () => showPage(pageMessage2, pageAbout))
|
||||
document.getElementById('btn-about-next')?.addEventListener('click', () => showPage(pageTitle, pageAbout))
|
||||
pageTitle?.addEventListener('click', () => showPage(pageAbout, pageTitle))
|
||||
|
||||
// Menu dropdown
|
||||
menuBtn?.addEventListener('click', (e) => {
|
||||
e.stopPropagation()
|
||||
menuDropdown?.classList.toggle('show')
|
||||
langDropdown?.classList.remove('show')
|
||||
})
|
||||
|
||||
// Language selector
|
||||
let currentLang = localStorage.getItem('preferred-lang') || 'en'
|
||||
const langBtn = document.getElementById('lang-tab')
|
||||
const langDropdown = document.getElementById('lang-dropdown')
|
||||
|
||||
function applyLang(lang: string) {
|
||||
document.querySelectorAll<HTMLElement>('[data-lang-en]').forEach(el => {
|
||||
const text = el.getAttribute(`data-lang-${lang}`)
|
||||
if (text) el.innerHTML = text
|
||||
})
|
||||
langDropdown?.querySelectorAll('.lang-option').forEach(opt => {
|
||||
opt.classList.toggle('selected', (opt as HTMLElement).dataset.lang === lang)
|
||||
})
|
||||
}
|
||||
|
||||
langBtn?.addEventListener('click', (e) => {
|
||||
e.stopPropagation()
|
||||
langDropdown?.classList.toggle('show')
|
||||
menuDropdown?.classList.remove('show')
|
||||
})
|
||||
|
||||
langDropdown?.querySelectorAll('.lang-option').forEach(opt => {
|
||||
opt.addEventListener('click', (e) => {
|
||||
e.stopPropagation()
|
||||
currentLang = (opt as HTMLElement).dataset.lang || 'en'
|
||||
localStorage.setItem('preferred-lang', currentLang)
|
||||
applyLang(currentLang)
|
||||
langDropdown?.classList.remove('show')
|
||||
})
|
||||
})
|
||||
|
||||
document.addEventListener('click', () => {
|
||||
langDropdown?.classList.remove('show')
|
||||
menuDropdown?.classList.remove('show')
|
||||
})
|
||||
|
||||
applyLang(currentLang)
|
||||
|
||||
// Show first page immediately
|
||||
pageVideo?.classList.add('visible')
|
||||
currentPage = pageVideo
|
||||
document.body.style.opacity = '1'
|
||||
127
rse/src/scene.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
export interface SceneObjects {
|
||||
renderer: THREE.WebGLRenderer
|
||||
scene: THREE.Scene
|
||||
camera: THREE.PerspectiveCamera
|
||||
starsFar: THREE.Points
|
||||
starsMid: THREE.Points
|
||||
starsClose: THREE.Points
|
||||
dust: THREE.Points
|
||||
nebulaGroup: THREE.Group
|
||||
light1: THREE.PointLight
|
||||
light2: THREE.PointLight
|
||||
}
|
||||
|
||||
function createStarLayer(
|
||||
count: number, spread: number, size: number, opacity: number
|
||||
): THREE.Points {
|
||||
const geo = new THREE.BufferGeometry()
|
||||
const pos = new Float32Array(count * 3)
|
||||
const col = new Float32Array(count * 3)
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const i3 = i * 3
|
||||
pos[i3] = (Math.random() - 0.5) * spread
|
||||
pos[i3 + 1] = (Math.random() - 0.5) * spread
|
||||
pos[i3 + 2] = (Math.random() - 0.5) * spread
|
||||
|
||||
// Dark particles on white background
|
||||
const shade = Math.random() * 0.08
|
||||
col[i3] = shade; col[i3+1] = shade; col[i3+2] = shade + 0.02
|
||||
}
|
||||
|
||||
geo.setAttribute('position', new THREE.BufferAttribute(pos, 3))
|
||||
geo.setAttribute('color', new THREE.BufferAttribute(col, 3))
|
||||
|
||||
const mat = new THREE.PointsMaterial({
|
||||
size, vertexColors: true, transparent: true, opacity,
|
||||
sizeAttenuation: true, depthWrite: false,
|
||||
})
|
||||
|
||||
return new THREE.Points(geo, mat)
|
||||
}
|
||||
|
||||
export function createScene(canvas: HTMLCanvasElement): SceneObjects {
|
||||
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true })
|
||||
renderer.setSize(window.innerWidth, window.innerHeight)
|
||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
|
||||
renderer.setClearColor(0x000000, 0) // transparent, CSS handles bg
|
||||
|
||||
const scene = new THREE.Scene()
|
||||
scene.fog = new THREE.FogExp2(0xf5f5f8, 0.00025)
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 5000)
|
||||
camera.position.set(0, 0, 600)
|
||||
|
||||
// Stars (dark particles)
|
||||
const starsFar = createStarLayer(6000, 4000, 1.0, 0.3)
|
||||
const starsMid = createStarLayer(3000, 2500, 1.8, 0.45)
|
||||
const starsClose = createStarLayer(1000, 1500, 2.5, 0.6)
|
||||
scene.add(starsFar, starsMid, starsClose)
|
||||
|
||||
// Dust (dark)
|
||||
const dustGeo = new THREE.BufferGeometry()
|
||||
const dustCount = 2000
|
||||
const dustPos = new Float32Array(dustCount * 3)
|
||||
for (let i = 0; i < dustCount; i++) {
|
||||
dustPos[i*3] = (Math.random() - 0.5) * 3000
|
||||
dustPos[i*3+1] = (Math.random() - 0.5) * 3000
|
||||
dustPos[i*3+2] = (Math.random() - 0.5) * 3000
|
||||
}
|
||||
dustGeo.setAttribute('position', new THREE.BufferAttribute(dustPos, 3))
|
||||
const dust = new THREE.Points(dustGeo, new THREE.PointsMaterial({
|
||||
size: 0.5, color: 0x222233, transparent: true, opacity: 0.3,
|
||||
depthWrite: false, sizeAttenuation: true,
|
||||
}))
|
||||
scene.add(dust)
|
||||
|
||||
const nebulaGroup = new THREE.Group()
|
||||
|
||||
// Lights
|
||||
scene.add(new THREE.AmbientLight(0xffffff, 0.6))
|
||||
|
||||
const light1 = new THREE.PointLight(0xccddee, 1.5, 1200)
|
||||
light1.position.set(200, 200, 200)
|
||||
scene.add(light1)
|
||||
|
||||
const light2 = new THREE.PointLight(0xbbbbdd, 1.0, 1000)
|
||||
light2.position.set(-300, -100, 100)
|
||||
scene.add(light2)
|
||||
|
||||
return {
|
||||
renderer, scene, camera,
|
||||
starsFar, starsMid, starsClose, dust, nebulaGroup,
|
||||
light1, light2,
|
||||
}
|
||||
}
|
||||
|
||||
export function updateScene(objs: SceneObjects, time: number, _dt: number, smoothX: number, smoothY: number) {
|
||||
const {
|
||||
camera, starsFar, starsMid, starsClose, dust, nebulaGroup,
|
||||
light1, light2,
|
||||
} = objs
|
||||
|
||||
// Camera
|
||||
camera.position.x = smoothX * 150
|
||||
camera.position.y = -smoothY * 100
|
||||
camera.position.z = 600 + Math.sin(time * 0.08) * 30
|
||||
camera.lookAt(smoothX * 30, -smoothY * 20, -150)
|
||||
|
||||
// Star parallax
|
||||
starsFar.rotation.y = time * 0.008 + smoothX * 0.015
|
||||
starsFar.rotation.x = time * 0.004 + smoothY * 0.015
|
||||
starsMid.rotation.y = time * 0.015 + smoothX * 0.03
|
||||
starsMid.rotation.x = time * 0.008 + smoothY * 0.03
|
||||
starsClose.rotation.y = time * 0.025 + smoothX * 0.05
|
||||
starsClose.rotation.x = time * 0.012 + smoothY * 0.05
|
||||
|
||||
// Dust
|
||||
dust.rotation.y = time * 0.005 + smoothX * 0.01
|
||||
dust.rotation.x = time * 0.003 + smoothY * 0.01
|
||||
|
||||
|
||||
// Lights
|
||||
light1.position.x = 200 + Math.sin(time * 0.25) * 120
|
||||
light2.position.y = -100 + Math.cos(time * 0.3) * 100
|
||||
}
|
||||
677
rse/src/style.css
Normal file
@@ -0,0 +1,677 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;700;900&family=Space+Mono:wght@400;700&display=swap');
|
||||
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
:root {
|
||||
--color-bg: #f5f5f8;
|
||||
--color-text: #1a1a2e;
|
||||
--color-accent: #3a7ca5;
|
||||
--color-accent2: #6a5acd;
|
||||
--color-dim: #8888a0;
|
||||
--font-display: 'Orbitron', sans-serif;
|
||||
--font-body: 'Space Mono', monospace;
|
||||
}
|
||||
|
||||
html, body {
|
||||
width: 100%; height: 100%;
|
||||
overflow: hidden;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-size: calc(1rem + 3px);
|
||||
}
|
||||
|
||||
#space-canvas {
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* ===== HEADER ===== */
|
||||
.site-header {
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100%;
|
||||
height: 5rem;
|
||||
padding: 0 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 110;
|
||||
background: var(--color-bg);
|
||||
opacity: 1;
|
||||
transition: opacity 0.6s ease;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.header-logo {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
text-decoration: none;
|
||||
color: var(--color-text);
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.header-logo-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.header-logo-text {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: 0.85rem;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
|
||||
/* ===== FOOTER ===== */
|
||||
.site-footer {
|
||||
position: fixed;
|
||||
bottom: 0; left: 0;
|
||||
width: 100%;
|
||||
padding: 1.5rem 2rem 2.5rem;
|
||||
z-index: 110;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1.2rem;
|
||||
opacity: 1;
|
||||
transition: opacity 0.6s ease;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.site-footer.footer-solid {
|
||||
background: var(--color-bg);
|
||||
}
|
||||
|
||||
/* ===== VIDEO APPS (position 3: top-right) ===== */
|
||||
.video-apps {
|
||||
position: absolute;
|
||||
top: 15%;
|
||||
right: 18%;
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
|
||||
.video-apps-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
|
||||
.video-apps-label {
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.5rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.1em;
|
||||
color: #ffffff;
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
padding: 0.25rem 0.7rem;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.video-app-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
text-decoration: none;
|
||||
transition: opacity 0.3s ease;
|
||||
opacity: 0.7;
|
||||
padding: 0.3rem 0;
|
||||
}
|
||||
|
||||
.video-app-link:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.video-app-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.video-app-name {
|
||||
font-family: var(--font-body);
|
||||
font-size: 0.55rem;
|
||||
letter-spacing: 0.05em;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.footer-copy {
|
||||
font-family: var(--font-body);
|
||||
font-size: 0.6rem;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--color-dim);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
|
||||
/* ===== LOGO PAGE ===== */
|
||||
.logo-icon {
|
||||
width: clamp(80px, 15vw, 160px);
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* ===== PAGES ===== */
|
||||
.page {
|
||||
position: fixed;
|
||||
top: 5rem; left: 0;
|
||||
width: 100%; height: calc(100% - 5rem);
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: opacity 1s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.page.visible {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.page.page-hidden {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.page.page-full {
|
||||
top: 0;
|
||||
height: 100%;
|
||||
z-index: 120;
|
||||
background: var(--color-bg);
|
||||
}
|
||||
|
||||
.page-full .hero-video {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.page-full .hero-video::before,
|
||||
.page-full .hero-video::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ===== NAV BUTTONS ===== */
|
||||
.nav-btn {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 44px; height: 44px;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.nav-btn:hover {
|
||||
opacity: 1;
|
||||
transform: translateY(-50%) scale(1.15);
|
||||
}
|
||||
|
||||
.nav-btn-right {
|
||||
right: 2.5rem;
|
||||
}
|
||||
|
||||
.nav-btn-left {
|
||||
left: 2.5rem;
|
||||
}
|
||||
|
||||
/* ===== PAGE 1: VIDEO ===== */
|
||||
.hero-video {
|
||||
position: relative;
|
||||
width: calc(100vw - 8rem);
|
||||
height: calc(100vh - 5rem - 4rem);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
background: rgba(26, 26, 46, 0.05);
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
animation: fadeUp 1.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.hero-video::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -1px; left: -1px;
|
||||
right: -1px; bottom: -1px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid transparent;
|
||||
background: linear-gradient(135deg, var(--color-accent), transparent 40%, transparent 60%, var(--color-accent2)) border-box;
|
||||
-webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask-composite: exclude;
|
||||
z-index: 4;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero-video::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 0 30px rgba(0, 0, 0, 0.15);
|
||||
z-index: 3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.page.visible .hero-video {
|
||||
animation-play-state: running;
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.hero-video video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.video-text {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.15) 0%, rgba(0, 0, 0, 0.05) 60%, rgba(0, 0, 0, 0.2) 100%);
|
||||
}
|
||||
|
||||
.video-title-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.video-title-icon {
|
||||
width: clamp(2rem, 5vw, 3.5rem);
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.video-label {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(1.6rem, 5vw, 3.5rem);
|
||||
letter-spacing: 0.15em;
|
||||
color: #ffffff;
|
||||
text-shadow: 0 0 30px rgba(0, 0, 0, 0.4), 0 2px 15px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.video-label-lg {
|
||||
font-size: clamp(3rem, 10vw, 7rem);
|
||||
}
|
||||
|
||||
.video-separator {
|
||||
width: 0; height: 1px;
|
||||
background: linear-gradient(90deg, transparent, var(--color-accent), var(--color-accent2), transparent);
|
||||
margin: 1rem 0;
|
||||
animation: lineExpandWide 1.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.page.visible .video-separator {
|
||||
animation-play-state: running;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
.video-desc {
|
||||
font-family: var(--font-body);
|
||||
font-size: clamp(0.6rem, 1.2vw, 0.85rem);
|
||||
letter-spacing: 0.25em;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
text-shadow: 0 1px 8px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
/* ===== PAGE 2: MESSAGE ===== */
|
||||
.message-content {
|
||||
text-align: center;
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
animation: fadeUp 1.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.page.visible .message-content {
|
||||
animation-play-state: running;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.message-title {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 900;
|
||||
font-size: clamp(2rem, 7vw, 5rem);
|
||||
letter-spacing: 0.15em;
|
||||
text-transform: uppercase;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.message-separator {
|
||||
width: 0; height: 1px;
|
||||
margin: 1.5rem auto;
|
||||
background: linear-gradient(90deg, transparent, var(--color-accent), var(--color-accent2), transparent);
|
||||
animation: lineExpandWide 1.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.page.visible .message-separator {
|
||||
animation-play-state: running;
|
||||
animation-delay: 0.8s;
|
||||
}
|
||||
|
||||
.message-desc {
|
||||
font-family: var(--font-body);
|
||||
font-size: clamp(0.7rem, 1.3vw, 0.95rem);
|
||||
letter-spacing: 0.2em;
|
||||
color: var(--color-dim);
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
/* ===== LANG SELECTOR ===== */
|
||||
.lang-selector {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.lang-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
padding: 6px;
|
||||
opacity: 0.4;
|
||||
transition: opacity 0.3s ease, background 0.3s ease;
|
||||
}
|
||||
|
||||
.lang-btn:hover {
|
||||
opacity: 0.9;
|
||||
background: rgba(26, 26, 46, 0.06);
|
||||
}
|
||||
|
||||
.lang-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.lang-dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
margin-top: 4px;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
min-width: 100px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.lang-dropdown.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.lang-option {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 14px;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-body);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.05em;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.lang-option:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.lang-option.selected {
|
||||
background: linear-gradient(135deg, #f0f7ff 0%, #e8f4ff 100%);
|
||||
}
|
||||
|
||||
.lang-check {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #ccc;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 10px;
|
||||
transition: all 0.2s;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.lang-option.selected .lang-check {
|
||||
background: var(--color-accent);
|
||||
border-color: var(--color-accent);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* ===== MENU DROPDOWN ===== */
|
||||
.menu-selector {
|
||||
position: relative;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.menu-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
padding: 6px;
|
||||
opacity: 0.4;
|
||||
transition: opacity 0.3s ease, background 0.3s ease;
|
||||
}
|
||||
|
||||
.menu-btn:hover {
|
||||
opacity: 0.9;
|
||||
background: rgba(26, 26, 46, 0.06);
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.menu-dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
margin-top: 4px;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
min-width: 180px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.menu-dropdown.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.menu-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 14px;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-body);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.05em;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.menu-option:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
a.menu-option {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.menu-option-active {
|
||||
background: linear-gradient(135deg, #f0f7ff 0%, #e8f4ff 100%);
|
||||
}
|
||||
|
||||
/* ===== SUBPAGES: solid bg, no 3D ===== */
|
||||
#page-about,
|
||||
#page-privacy,
|
||||
#page-account,
|
||||
#page-terms {
|
||||
background: var(--color-bg);
|
||||
z-index: 15;
|
||||
justify-content: flex-start;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.subpage-content {
|
||||
width: 100%;
|
||||
max-width: 640px;
|
||||
margin: 0 auto;
|
||||
padding: 4rem 2rem 6rem;
|
||||
}
|
||||
|
||||
.subpage-heading {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(1rem, 2.5vw, 1.4rem);
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--color-text);
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.subpage-section {
|
||||
margin-top: 2.5rem;
|
||||
}
|
||||
|
||||
.subpage-section-title {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(0.85rem, 1.8vw, 1.1rem);
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--color-text);
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.subpage-section-line {
|
||||
width: clamp(40px, 8vw, 80px);
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, var(--color-accent), var(--color-accent2), transparent);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.subpage-section-text {
|
||||
font-family: var(--font-body);
|
||||
font-size: clamp(0.65rem, 1.1vw, 0.8rem);
|
||||
line-height: 2;
|
||||
letter-spacing: 0.05em;
|
||||
color: rgba(26, 26, 46, 0.6);
|
||||
}
|
||||
|
||||
.subpage-list {
|
||||
list-style: none;
|
||||
margin-top: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.subpage-list-item {
|
||||
font-family: var(--font-body);
|
||||
font-size: clamp(0.65rem, 1.1vw, 0.8rem);
|
||||
line-height: 2;
|
||||
letter-spacing: 0.05em;
|
||||
color: rgba(26, 26, 46, 0.6);
|
||||
padding-left: 1.2em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.subpage-list-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0.85em;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, var(--color-accent), var(--color-accent2));
|
||||
}
|
||||
|
||||
.subpage-section-text a {
|
||||
color: var(--color-accent);
|
||||
text-decoration: none;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.subpage-section-text a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.subpage-section-text code {
|
||||
background: rgba(26, 26, 46, 0.06);
|
||||
padding: 0.15em 0.4em;
|
||||
border-radius: 3px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.subpage-img {
|
||||
max-width: 100%;
|
||||
border-radius: 4px;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
/* Overlay */
|
||||
.vignette {
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
z-index: 4;
|
||||
pointer-events: none;
|
||||
background: radial-gradient(ellipse at center, transparent 40%, rgba(245, 245, 248, 0.7) 100%);
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes fadeUp {
|
||||
from { opacity: 0; transform: translateY(15px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
@keyframes lineExpandWide {
|
||||
to { width: clamp(80px, 15vw, 180px); }
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.hero-video {
|
||||
width: calc(100vw - 1rem);
|
||||
height: calc(100vh - 1rem);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.nav-btn-right { right: 1rem; }
|
||||
.nav-btn-left { left: 1rem; }
|
||||
.nav-btn { width: 36px; height: 36px; }
|
||||
|
||||
.site-header { padding: 0.8rem 1rem; }
|
||||
.site-footer { padding: 1rem 1rem 1.5rem; }
|
||||
}
|
||||
16
rse/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"]
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
21
rse/vite.config.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
export default defineConfig({
|
||||
publicDir: 'public',
|
||||
build: {
|
||||
assetsInlineLimit: 0,
|
||||
},
|
||||
appType: 'mpa',
|
||||
plugins: [{
|
||||
name: 'clean-urls',
|
||||
configureServer(server) {
|
||||
server.middlewares.use((req, _res, next) => {
|
||||
const url = req.url?.split('?')[0] ?? ''
|
||||
if (url.match(/^\/[a-z]+$/) && !url.includes('.')) {
|
||||
req.url = url + '/index.html'
|
||||
}
|
||||
next()
|
||||
})
|
||||
},
|
||||
}],
|
||||
})
|
||||