This commit is contained in:
2026-01-26 01:29:14 +03:00
commit 6c145b1a61
991 changed files with 166848 additions and 0 deletions

1393
public/game.js Normal file

File diff suppressed because it is too large Load Diff

289
public/index.html Normal file
View File

@ -0,0 +1,289 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Star Wars Hearthstone — PvP по сети</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;800&family=Rajdhani:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="starwars-glyphicons/css/starwars-glyphicons.css">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="app">
<div id="stars"></div>
<div id="stars2"></div>
<div id="stars3"></div>
<!-- Lobby -->
<section id="lobby" class="screen">
<div class="lobby-card">
<h1 class="logo">
<i class="swg swg-2x swg-starwars logo-icon" aria-hidden="true"></i>
<span class="logo-sw">STAR WARS</span>
<span class="logo-hs">HEARTHSTONE</span>
</h1>
<p class="subtitle">PvP до 4 игроков · Работает через Radmin VPN</p>
<button type="button" id="btn-instructions-lobby" class="btn-instructions-lobby" title="Как играть">?</button>
<button type="button" id="btn-cards-gallery" class="btn-instructions-lobby" title="Галерея карт" style="right: 3.5rem;">📚</button>
<button type="button" id="btn-settings-lobby" class="btn-settings-lobby" title="Настройки"></button>
<div class="lobby-tabs">
<button type="button" class="tab active" data-tab="host">Создать игру</button>
<button type="button" class="tab" data-tab="join">Подключиться</button>
</div>
<div id="host-panel" class="panel active">
<label>Ваше имя</label>
<input type="text" id="host-name" placeholder="Игрок 1" maxlength="20" />
<button type="button" id="btn-host" class="btn btn-primary">Создать комнату</button>
</div>
<div id="join-panel" class="panel">
<label>Ваше имя</label>
<input type="text" id="join-name" placeholder="Игрок 2" maxlength="20" />
<button type="button" id="btn-join" class="btn btn-primary">Подключиться к серверу</button>
<p class="hint" style="font-size: 0.75rem; margin-top: 0.5rem; opacity: 0.7;">Автоматическое подключение к серверу</p>
<details style="margin-top: 0.5rem; font-size: 0.85rem;">
<summary style="cursor: pointer; color: #94a3b8; user-select: none;">Расширенные настройки</summary>
<label style="margin-top: 0.5rem; display: block;">Адрес сервера (IP:порт)</label>
<input type="text" id="join-addr" placeholder="Оставьте пустым для авто" style="margin-top: 0.3rem;" />
</details>
</div>
<div id="room-panel" class="panel hidden">
<h3>Комната</h3>
<p class="hint">Код комнаты: <strong id="room-code-display" style="font-size: 1.2rem; color: var(--cyan); letter-spacing: 0.1em; cursor: pointer; user-select: all;" title="Клик для копирования">-----</strong></p>
<p class="hint" style="font-size: 0.85rem; margin-top: 0.5rem;">Адрес для подключения: <strong id="room-connect-info" style="color: var(--amber); cursor: pointer; user-select: all;" title="Клик для копирования">-----</strong></p>
<p class="hint" style="font-size: 0.75rem; margin-top: 0.3rem; opacity: 0.8;">Отправьте адрес другим игрокам. Они введут код на странице подключения.</p>
<label>Фракция колоды</label>
<select id="room-faction">
<option value="">Случайная</option>
<option value="rebellion">Сопротивление</option>
<option value="empire">Империя</option>
<option value="pirates">Пираты</option>
<option value="mandalorians">Мандалорцы</option>
</select>
<ul id="player-list"></ul>
<button type="button" id="btn-start" class="btn btn-primary">Начать игру</button>
<button type="button" id="btn-leave" class="btn btn-ghost">Выйти</button>
</div>
<div id="connect-panel" class="panel hidden">
<label>Код комнаты</label>
<input type="text" id="join-code" placeholder="ABCDE" maxlength="5" style="text-transform: uppercase; font-size: 1.1rem; letter-spacing: 0.1em; text-align: center; width: 100%; padding: 0.6rem; background: rgba(0,0,0,0.3); border: 2px solid rgba(0,180,255,0.35); border-radius: 8px; color: #f8fafc; font-weight: 700;" />
<p class="hint" style="font-size: 0.85rem; margin-top: 0.5rem;">Введите код комнаты, который дал хост</p>
<button type="button" id="btn-join-code" class="btn btn-primary">Подключиться к комнате</button>
<p id="connect-status" style="margin-top: 1rem; color: #94a3b8;">Ожидание начала игры...</p>
<ul id="connect-player-list"></ul>
<button type="button" id="btn-leave-join" class="btn btn-ghost">Выйти</button>
</div>
<p id="lobby-error" class="error hidden"></p>
</div>
</section>
<div id="cards-gallery-overlay" class="modal-overlay hidden">
<div class="modal cards-gallery-modal">
<h2>📚 Галерея карт</h2>
<div class="gallery-controls">
<select id="gallery-faction-filter" class="gallery-filter">
<option value="">Все фракции</option>
<option value="rebellion">Сопротивление</option>
<option value="empire">Империя</option>
<option value="pirates">Пираты</option>
<option value="mandalorians">Мандалорцы</option>
<option value="neutral">Нейтральные</option>
</select>
<select id="gallery-type-filter" class="gallery-filter">
<option value="">Все типы</option>
<option value="minion">Миньоны</option>
<option value="spell">Заклинания</option>
</select>
<input type="text" id="gallery-search" placeholder="Поиск по имени..." class="gallery-search" />
</div>
<div id="cards-gallery-grid" class="cards-gallery-grid"></div>
<button type="button" id="btn-gallery-close" class="btn btn-primary">Закрыть</button>
</div>
</div>
<div id="instructions-overlay" class="modal-overlay hidden">
<div class="modal instructions-modal">
<h2>Как играть</h2>
<div class="instructions-body">
<p><strong>Цель игры:</strong> свести здоровье героев всех противников до 0. Последний выживший побеждает!</p>
<h3>Основные механики</h3>
<p><strong>Мана</strong> <i class="swg swg-credits instructions-icon"></i>ресурс для разыгрывания карт. Растёт каждый ход (максимум 10). Восстанавливается полностью в начале вашего хода.</p>
<p><strong>Здоровье героя</strong> <i class="swg swg-deathstar instructions-icon"></i> — начинаете с 30. При достижении 0 — поражение.</p>
<p><strong>Колода</strong> <i class="swg swg-galrep instructions-icon"></i> — 20 карт. При пустой колоде получаете урон усталости (увеличивается каждый раз).</p>
<p><strong>Рука</strong> — максимум 10 карт. При переполнении карты сжигаются.</p>
<h3>Ход игры</h3>
<ol>
<li><strong>Розыгрыш карт</strong> — разыграйте миньонов (до 7 на столе) или заклинания, тратя ману.</li>
<li><strong>Атака</strong> — атакуйте вражескими миньонами или используйте способности.</li>
<li><strong>Добор карты</strong> — клик по колоде (1 раз за ход, если не использовали).</li>
<li><strong>Завершить ход</strong> — кнопка «Завершить ход».</li>
</ol>
<h3>Управление</h3>
<ul>
<li><strong>Розыгрыш карты</strong> — клик по карте в руке (если хватает маны). Для миньонов — клик или перетаскивание на поле.</li>
<li><strong>Атака миньоном</strong> — клик по своему миньону → клик по цели. Или <strong>перетащите</strong> миньона на врага/героя.</li>
<li><strong>Заклинания</strong> — клик по заклинанию в руке. Если требуется цель — выберите её.</li>
<li><strong>Геройская способность</strong> — кнопка «Герой (2)» → выберите цель (1 урон, стоит 2 маны).</li>
<li><strong>Звёздная кузница</strong> — кнопка «⚒ Кузница» → выберите 2-3 карты из колоды → создайте улучшенную карту (-1 стоимость, +1/+1 статы).</li>
<li><strong>Информация о карте</strong> — кнопка «i» на карте для просмотра описания и биографии.</li>
</ul>
<h3>Типы карт</h3>
<p><strong>Миньоны</strong> — существа с <span class="atk">атакой</span> и <span class="hp">здоровьем</span>. Могут атаковать со следующего хода после розыгрыша. При атаке оба наносят урон друг другу.</p>
<p><strong>Заклинания</strong> — одноразовые эффекты. Некоторые требуют выбора цели.</p>
<p><strong>Структуры</strong> — здания и сооружения с особыми эффектами.</p>
<h3>Способности</h3>
<p><strong>Battlecry</strong> — срабатывает при розыгрыше карты.</p>
<p><strong>Deathrattle</strong> — срабатывает при смерти миньона.</p>
<p><strong>Заморозка</strong> ❄ — миньон не может атаковать следующий ход.</p>
<h3>Фракции</h3>
<p><span class="faction-r">Сопротивление</span> <i class="swg swg-galrep instructions-icon"></i> — джедаи, повстанцы, защитники свободы.</p>
<p><span class="faction-e">Империя</span> <i class="swg swg-galemp instructions-icon"></i> — ситхи, штурмовики, тёмная сторона.</p>
<p><span class="faction-n">Пираты</span> — охотники за головами, контрабандисты.</p>
<p><span class="faction-n">Мандалорцы</span> — воины-мандалорцы, охотники.</p>
<p><span class="faction-n">Нейтральные</span> — доступны всем фракциям.</p>
<h3>Советы</h3>
<ul>
<li>Управляйте маной — не тратьте всё в начале хода.</li>
<li>Используйте способности карт эффективно.</li>
<li>Контролируйте поле боя — не давайте противнику накапливать миньонов.</li>
<li>Следите за здоровьем — не забывайте про урон по герою.</li>
<li>Используйте кузницу для улучшения ключевых карт.</li>
</ul>
</div>
<button type="button" id="btn-instructions-close" class="btn btn-primary">Понятно</button>
</div>
</div>
<!-- Game -->
<section id="game" class="screen hidden">
<header class="game-header">
<div class="header-left">
<span class="mana-display">
<i class="swg swg-credits mana-icon" aria-hidden="true"></i>
<span id="your-mana">0</span>/<span id="your-max-mana">0</span>
</span>
<span class="health-display">
<i class="swg swg-deathstar health-icon" aria-hidden="true"></i>
<span id="your-health">30</span>
</span>
<span class="deck-count"><i class="swg swg-galrep deck-icon" aria-hidden="true"></i><span id="your-deck">0</span> в колоде</span>
</div>
<div class="header-center">
<span class="turn-badge" id="turn-badge">Ход 1</span>
<span class="turn-timer" id="turn-timer" title="Время хода">1:30</span>
<button type="button" id="btn-hero-ability" class="btn btn-ghost" title="Геройская способность (2 маны): 1 урон цели">Герой (2)</button>
<button type="button" id="btn-forge" class="btn btn-ghost" title="Звёздная кузница: улучшить карту из колоды">⚒ Кузница</button>
<button type="button" id="btn-end-turn" class="btn btn-end-turn">Завершить ход</button>
<button type="button" id="btn-reset-lobby" class="btn btn-ghost hidden" title="Сбросить игру и вернуться в лобби">Вернуться в лобби</button>
</div>
<div class="header-right">
<div class="game-log-wrap">
<div id="game-log" class="game-log"></div>
</div>
<button type="button" id="btn-cards-gallery-game" class="btn-instructions" title="Галерея карт" style="right: 3.5rem;">📚</button>
<button type="button" id="btn-instructions" class="btn-instructions" title="Инструкция">?</button>
<button type="button" id="btn-settings" class="btn-settings" title="Настройки"></button>
</div>
</header>
<div class="board">
<div class="opponents" id="opponents-area">
<!-- Opponent boards filled by JS -->
</div>
<div class="battlefield">
<div class="your-board" id="your-board"></div>
</div>
</div>
<div class="hand-container">
<div class="deck-fan" id="deck-fan" title="Колода (клик — вытянуть карту)">
<i class="swg swg-galrep deck-fan-icon" aria-hidden="true"></i>
<div class="deck-stack" id="deck-stack"></div>
<span class="deck-count-badge" id="deck-count-badge">0</span>
</div>
<div class="hand" id="your-hand"></div>
</div>
<div id="modal-overlay" class="modal-overlay hidden">
<div class="modal">
<h2 id="modal-title">Результат</h2>
<p id="modal-body"></p>
<button type="button" id="btn-modal-close" class="btn btn-primary">OK</button>
</div>
</div>
<div id="attack-mode" class="attack-mode hidden">
<p>Выберите цель для атаки (враг или его существо)</p>
<button type="button" id="btn-cancel-attack" class="btn btn-ghost">Отмена</button>
</div>
<div id="spell-mode" class="attack-mode spell-mode hidden">
<p>Выберите цель для заклинания</p>
<button type="button" id="btn-cancel-spell" class="btn btn-ghost">Отмена</button>
</div>
<div id="hero-ability-mode" class="attack-mode spell-mode hidden">
<p>Выберите цель для геройской способности (1 урон)</p>
<button type="button" id="btn-cancel-hero" class="btn btn-ghost">Отмена</button>
</div>
<div id="forge-overlay" class="modal-overlay hidden">
<div class="modal forge-modal">
<h2>⚒ Звёздная кузница</h2>
<p class="hint" style="font-size: 0.85rem; margin-bottom: 1rem;">Выберите 2-3 карты из колоды для улучшения</p>
<div id="forge-deck-list" class="forge-deck-list"></div>
<div id="forge-selected" class="forge-selected"></div>
<div class="forge-actions">
<button type="button" id="btn-forge-craft" class="btn btn-primary" disabled>Создать улучшенную карту</button>
<button type="button" id="btn-forge-close" class="btn btn-ghost">Закрыть</button>
</div>
</div>
</div>
<div id="card-info-overlay" class="modal-overlay hidden">
<div class="modal card-info-modal">
<h2 id="card-info-name"></h2>
<p id="card-info-meta" class="card-info-meta"></p>
<p id="card-info-text" class="card-info-text"></p>
<div id="card-info-abilities" class="card-info-abilities"></div>
<p id="card-info-bio" class="card-info-bio"></p>
<button type="button" id="btn-card-info-close" class="btn btn-primary">Закрыть</button>
</div>
</div>
<div id="settings-overlay" class="modal-overlay hidden">
<div class="modal settings-modal">
<h2>Настройки</h2>
<div class="settings-content">
<div class="setting-group">
<label for="music-toggle">Музыка</label>
<button type="button" id="music-toggle" class="btn btn-ghost">Включить</button>
</div>
<div class="setting-group">
<label for="music-volume-slider">Громкость музыки: <span id="music-volume-value">50%</span></label>
<input type="range" id="music-volume-slider" min="0" max="100" value="50" step="1" />
</div>
</div>
<button type="button" id="btn-settings-close" class="btn btn-primary">Закрыть</button>
</div>
</div>
<div id="effects-layer"></div>
<div id="attack-announcement" class="attack-announcement hidden"></div>
</section>
</div>
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
<script src="sounds.js"></script>
<script src="music.js"></script>
<script src="game.js"></script>
</body>
</html>

160
public/music.js Normal file
View File

@ -0,0 +1,160 @@
// Music playlist manager
(function() {
'use strict';
const MUSIC_FOLDER = 'music/';
const PLAYLIST = [
'Knights Of The Old Republic II - The Sith Lords — The Temple of Freedon Nadd (www.lightaudio.ru).mp3',
'Soundtrack — StarWars (www.lightaudio.ru) (1).mp3',
'Soundtrack — Starwars (www.lightaudio.ru).mp3',
'StarWars — Across The Stars Love Theme (www.lightaudio.ru).mp3'
];
let currentAudio = null;
let currentTrackIndex = -1;
let playedTracks = [];
let volume = 0.5;
let isPlaying = false;
let isEnabled = true;
function getRandomTrack() {
if (playedTracks.length >= PLAYLIST.length) {
playedTracks = [];
}
let available = PLAYLIST.filter((_, i) => !playedTracks.includes(i));
if (available.length === 0) {
playedTracks = [];
available = PLAYLIST;
}
const randomIndex = Math.floor(Math.random() * available.length);
const trackName = available[randomIndex];
const trackIndex = PLAYLIST.indexOf(trackName);
playedTracks.push(trackIndex);
return { trackName, trackIndex };
}
function loadTrack(trackName) {
return new Promise((resolve, reject) => {
const audio = new Audio(MUSIC_FOLDER + trackName);
audio.volume = volume;
audio.addEventListener('loadeddata', () => resolve(audio));
audio.addEventListener('error', (e) => reject(e));
audio.load();
});
}
function playNext() {
if (!isEnabled) return;
if (currentAudio) {
currentAudio.pause();
currentAudio = null;
}
const { trackName, trackIndex } = getRandomTrack();
currentTrackIndex = trackIndex;
loadTrack(trackName)
.then((audio) => {
currentAudio = audio;
currentAudio.volume = volume;
currentAudio.addEventListener('ended', () => {
playNext();
});
currentAudio.addEventListener('error', () => {
console.warn('Failed to load track:', trackName);
playNext();
});
currentAudio.play().catch((e) => {
console.warn('Failed to play track:', e);
});
isPlaying = true;
updateUI();
})
.catch((e) => {
console.warn('Failed to load track:', trackName, e);
playNext();
});
}
function stop() {
if (currentAudio) {
currentAudio.pause();
currentAudio = null;
}
isPlaying = false;
updateUI();
}
function setVolume(newVolume) {
volume = Math.max(0, Math.min(1, newVolume));
if (currentAudio) {
currentAudio.volume = volume;
}
localStorage.setItem('musicVolume', volume.toString());
updateUI();
}
function toggle() {
isEnabled = !isEnabled;
localStorage.setItem('musicEnabled', isEnabled.toString());
if (isEnabled && !isPlaying) {
playNext();
} else if (!isEnabled) {
stop();
}
updateUI();
}
function updateUI() {
const volumeSlider = document.getElementById('music-volume-slider');
const volumeValue = document.getElementById('music-volume-value');
const musicToggle = document.getElementById('music-toggle');
if (volumeSlider) {
volumeSlider.value = volume;
}
if (volumeValue) {
volumeValue.textContent = Math.round(volume * 100) + '%';
}
if (musicToggle) {
musicToggle.textContent = isEnabled ? 'Выключить' : 'Включить';
musicToggle.classList.toggle('active', isEnabled);
}
}
function init() {
const savedVolume = localStorage.getItem('musicVolume');
if (savedVolume !== null) {
volume = parseFloat(savedVolume);
} else {
volume = 0.5;
}
const savedEnabled = localStorage.getItem('musicEnabled');
if (savedEnabled !== null) {
isEnabled = savedEnabled === 'true';
} else {
isEnabled = true;
}
updateUI();
if (isEnabled) {
setTimeout(() => {
playNext();
}, 500);
}
}
window.Music = {
play: playNext,
stop: stop,
setVolume: setVolume,
toggle: toggle,
getVolume: () => volume,
isEnabled: () => isEnabled,
isPlaying: () => isPlaying,
init: init
};
})();

130
public/sounds.js Normal file
View File

@ -0,0 +1,130 @@
/**
* Star Wars Hearthstone - Web Audio sound effects
*/
(function (global) {
'use strict';
let ctx = null;
function init() {
if (ctx) {
if (ctx.state === 'suspended') ctx.resume().catch(function () {});
return ctx;
}
try {
ctx = new (global.AudioContext || global.webkitAudioContext)();
} catch (e) {
return null;
}
return ctx;
}
function beep(freq, duration, type, vol) {
const c = init();
if (!c) return;
const osc = c.createOscillator();
const gain = c.createGain();
osc.connect(gain);
gain.connect(c.destination);
osc.frequency.value = freq;
osc.type = type || 'sine';
gain.gain.setValueAtTime(vol ?? 0.15, c.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, c.currentTime + duration);
osc.start(c.currentTime);
osc.stop(c.currentTime + duration);
}
function playCard() {
const c = init();
if (!c) return;
const osc = c.createOscillator();
const gain = c.createGain();
osc.connect(gain);
gain.connect(c.destination);
osc.type = 'sine';
osc.frequency.setValueAtTime(523, c.currentTime);
osc.frequency.exponentialRampToValueAtTime(784, c.currentTime + 0.08);
gain.gain.setValueAtTime(0.12, c.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, c.currentTime + 0.15);
osc.start(c.currentTime);
osc.stop(c.currentTime + 0.15);
}
function attack() {
const c = init();
if (!c) return;
const osc = c.createOscillator();
const gain = c.createGain();
osc.connect(gain);
gain.connect(c.destination);
osc.type = 'square';
osc.frequency.value = 180;
gain.gain.setValueAtTime(0.08, c.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, c.currentTime + 0.12);
osc.start(c.currentTime);
osc.stop(c.currentTime + 0.12);
}
function endTurn() {
beep(392, 0.06, 'sine', 0.1);
setTimeout(() => beep(523, 0.08, 'sine', 0.08), 80);
}
function victory() {
const notes = [523, 659, 784, 1047];
notes.forEach((f, i) => {
setTimeout(() => beep(f, 0.25, 'sine', 0.12), i * 120);
});
}
function defeat() {
const notes = [392, 349, 294];
notes.forEach((f, i) => {
setTimeout(() => beep(f, 0.2, 'sine', 0.1), i * 150);
});
}
function drawCard() {
beep(440, 0.06, 'sine', 0.08);
}
function hoverCard() {
beep(600, 0.04, 'sine', 0.05);
}
function timerWarning() {
const c = init();
if (!c) return;
const osc = c.createOscillator();
const gain = c.createGain();
osc.connect(gain);
gain.connect(c.destination);
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(220, c.currentTime);
osc.frequency.exponentialRampToValueAtTime(440, c.currentTime + 0.15);
gain.gain.setValueAtTime(0.08, c.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, c.currentTime + 0.2);
osc.start(c.currentTime);
osc.stop(c.currentTime + 0.2);
}
function timerEnd() {
const notes = [262, 330, 392];
notes.forEach((f, i) => {
setTimeout(() => beep(f, 0.1, 'square', 0.08), i * 80);
});
}
global.Sounds = {
init,
playCard,
attack,
endTurn,
victory,
defeat,
drawCard,
hoverCard,
timerWarning,
timerEnd,
};
})(typeof window !== 'undefined' ? window : globalThis);

View File

@ -0,0 +1,686 @@
/*!
* Star Wars GlyphIcons 1.4 by @maxtusken - http://starwarsglyphicons.com - @starwarsglyphicons
* License - http://starwarsglyphicons.com/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
/* FONT PATH
* -------------------------- */
@font-face {
font-family: 'StarWarsGlyphicons';
1src: url('../fonts/fontawesome-webfont.eot?v=4.7.0');
1src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
src: url('../fonts/starwars-glyphicons-webfont.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
.swg {
display: inline-block;
font: normal normal normal 14px/1 StarWarsGlyphicons;
font-size: inherit;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* makes the font 33% larger relative to the icon container */
.swg-lg {
font-size: 1.33333333em;
line-height: 0.75em;
vertical-align: -15%;
}
.swg-2x {
font-size: 2em;
}
.swg-3x {
font-size: 3em;
}
.swg-4x {
font-size: 4em;
}
.swg-5x {
font-size: 5em;
}
.swg-6x {
font-size: 6em;
}
.swg-fw {
width: 1.28571429em;
text-align: center;
}
.swg-ul {
padding-left: 0;
margin-left: 2.14285714em;
list-style-type: none;
}
.swg-ul > li {
position: relative;
}
.swg-li {
position: absolute;
left: -2.14285714em;
width: 2.14285714em;
top: 0.14285714em;
text-align: center;
}
.swg-li.swg-lg {
left: -1.85714286em;
}
.swg-border {
padding: .2em .25em .15em;
border: solid 0.08em #eeeeee;
border-radius: .1em;
}
.swg-pull-left {
float: left;
}
.swg-pull-right {
float: right;
}
.fa.swg-pull-left {
margin-right: .3em;
}
.fa.swg-pull-right {
margin-left: .3em;
}
/* Deprecated as of 4.4.0 */
.pull-right {
float: right;
}
.pull-left {
float: left;
}
.fa.pull-left {
margin-right: .3em;
}
.fa.pull-right {
margin-left: .3em;
}
.swg-spin {
-webkit-animation: fa-spin 2s infinite linear;
animation: fa-spin 2s infinite linear;
}
.swg-pulse {
-webkit-animation: fa-spin 1s infinite steps(8);
animation: fa-spin 1s infinite steps(8);
}
@-webkit-keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
.swg-rotate-90 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
-webkit-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
}
.swg-rotate-180 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
-webkit-transform: rotate(180deg);
-ms-transform: rotate(180deg);
transform: rotate(180deg);
}
.swg-rotate-270 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
-webkit-transform: rotate(270deg);
-ms-transform: rotate(270deg);
transform: rotate(270deg);
}
.swg-flip-horizontal {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
-webkit-transform: scale(-1, 1);
-ms-transform: scale(-1, 1);
transform: scale(-1, 1);
}
.swg-flip-vertical {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-webkit-transform: scale(1, -1);
-ms-transform: scale(1, -1);
transform: scale(1, -1);
}
:root .swg-rotate-90,
:root .swg-rotate-180,
:root .swg-rotate-270,
:root .swg-flip-horizontal,
:root .swg-flip-vertical {
filter: none;
}
.swg-stack {
position: relative;
display: inline-block;
width: 2em;
height: 2em;
line-height: 2em;
vertical-align: middle;
}
.swg-stack-1x,
.swg-stack-2x {
position: absolute;
left: 0;
width: 100%;
text-align: center;
}
.swg-stack-1x {
line-height: inherit;
}
.swg-stack-2x {
font-size: 2em;
}
.swg-inverse {
color: #ffffff;
}
/* Star Wars GlyphIcons uses the Unicode Private Use Area (PUA) to ensure screen
readers do not read off random characters that represent icons */
.swg-starwars:before {
content: "\f000";
}
.swg-sw-alt:before {
content: "\f001";
}
.swg-sw-alt-2:before {
content: "\f002";
}
.swg-title4:before {
content: "\f003";
}
.swg-title5:before {
content: "\f004";
}
.swg-title6:before {
content: "\f005";
}
.swg-reball:before {
content: "\f006";
}
.swg-newrep:before {
content: "\f007";
}
.swg-galemp:before {
content: "\f008";
}
.swg-mandalorcrest:before {
content: "\f009";
}
.swg-mandalorian:before {
content: "\f00A";
}
.swg-blacksun:before {
content: "\f00B";
}
.swg-sith:before {
content: "\f00C";
}
.swg-galsenate:before {
content: "\f00D";
}
.swg-newjediorder:before {
content: "\f00E";
}
.swg-separ:before {
content: "\f00F";
}
.swg-jediorder:before {
content: "\f010";
}
.swg-galrep:before {
content: "\f011";
}
.swg-sithemp:before {
content: "\f012";
}
.swg-kirkanos:before {
content: "\f013";
}
.swg-credits:before {
content: "\f014";
}
.swg-ep1:before {
content: "\f015";
}
.swg-ep2:before {
content: "\f016";
}
.swg-ep3:before {
content: "\f017";
}
.swg-ep4:before {
content: "\f018";
}
.swg-ep5:before {
content: "\f019";
}
.swg-ep6:before {
content: "\f01A";
}
.swg-deathstar:before {
content: "\f01B";
}
.swg-deathstar-3:before {
content: "\f01C";
}
.swg-deathstar-4:before {
content: "\f01D";
}
.swg-r2d2:before {
content: "\f01E";
}
.swg-darthvader-3:before {
content: "\f01F";
}
.swg-yoda-2:before {
content: "\f020";
}
.swg-darthvader:before {
content: "\f021";
}
.swg-stormtrooper:before {
content: "\f022";
}
.swg-c3po:before {
content: "\f023";
}
.swg-yoda:before {
content: "\f024";
}
.swg-bobbafett:before {
content: "\f025";
}
.swg-saberjedi:before {
content: "\f026";
}
.swg-sabersith:before {
content: "\f027";
}
.swg-atat:before {
content: "\f028";
}
.swg-xwing:before {
content: "\f029";
}
.swg-wookie:before {
content: "\f02A";
}
.swg-akbar:before {
content: "\f02B";
}
.swg-falcon:before {
content: "\f02C";
}
.swg-leia:before {
content: "\f02D";
}
.swg-tie:before {
content: "\f02E";
}
.swg-lightsabers:before {
content: "\f02F";
}
.swg-kylo:before {
content: "\f030";
}
.swg-bb8:before {
content: "\f031";
}
.swg-trooper-ep7:before {
content: "\f032";
}
.swg-phasma:before {
content: "\f033";
}
.swg-trooper-o:before {
content: "\f034";
}
.swg-darthvader-o:before {
content: "\f035";
}
.swg-deathstar-o:before {
content: "\f036";
}
.swg-falcon-o:before {
content: "\f037";
}
.swg-r2d2-o:before {
content: "\f038";
}
.swg-tfdroid-o:before {
content: "\f039";
}
.swg-c3po-o:before {
content: "\f03A";
}
.swg-bobbafett-o:before {
content: "\f03B";
}
.swg-rogueone-title:before {
content: "\f03C";
}
.swg-rogueone-sybm:before {
content: "\f03D";
}
.swg-rogueone-2:before {
content: "\f03E";
}
.swg-k2s0:before {
content: "\f03F";
}
.swg-jynerso:before {
content: "\f040";
}
.swg-cassianandor:before {
content: "\f041";
}
.swg-mazkanata:before {
content: "\f042";
}
.swg-deathtrooper:before {
content: "\f043";
}
.swg-sawgerrera:before {
content: "\f044";
}
.swg-tie-2:before {
content: "\f045";
}
.swg-xwing-2:before {
content: "\f046";
}
.swg-falcon-2:before {
content: "\f047";
}
.swg-bobbafett-2:before {
content: "\f048";
}
.swg-darthvader-2:before {
content: "\f049";
}
.swg-bb8-2:before {
content: "\f04A";
}
.swg-cantina:before {
content: "\f04B";
}
.swg-carbonite:before {
content: "\f04C";
}
.swg-hologram:before {
content: "\f04D";
}
.swg-deathstar-2:before {
content: "\f04E";
}
.swg-combatdrone:before {
content: "\f04F";
}
.swg-falcon-3:before {
content: "\f050";
}
.swg-landspeeder:before {
content: "\f051";
}
.swg-speederbike:before {
content: "\f052";
}
.swg-stardestroyer:before {
content: "\f053";
}
.swg-xwing-3:before {
content: "\f054";
}
.swg-tie-3:before {
content: "\f055";
}
.swg-ywing:before {
content: "\f056";
}
.swg-jedistarfight:before {
content: "\f057";
}
.swg-sandcrawler:before {
content: "\f058";
}
.swg-atat-2:before {
content: "\f059";
}
.swg-atst:before {
content: "\f05A";
}
.swg-hanblaster:before {
content: "\f05B";
}
.swg-blasterrifle:before {
content: "\f05C";
}
.swg-lightsabers-2:before {
content: "\f05D";
}
.swg-lukelightsaber:before {
content: "\f05E";
}
.swg-kylolightsaber:before {
content: "\f05F";
}
.swg-darthvader-4:before {
content: "\f060";
}
.swg-anakin:before {
content: "\f061";
}
.swg-anakin-young:before {
content: "\f062";
}
.swg-anakin-kid:before {
content: "\f063";
}
.swg-hansolo:before {
content: "\f064";
}
.swg-leia-2:before {
content: "\f065";
}
.swg-lukeskywalker:before {
content: "\f066";
}
.swg-c3po-2:before {
content: "\f067";
}
.swg-r2d2-2:before {
content: "\f068";
}
.swg-bb8-3:before {
content: "\f069";
}
.swg-padme:before {
content: "\f06A";
}
.swg-yoda-3:before {
content: "\f06B";
}
.swg-chewbacca:before {
content: "\f06C";
}
.swg-emperor:before {
content: "\f06D";
}
.swg-dooku:before {
content: "\f06E";
}
.swg-darthmaul:before {
content: "\f06F";
}
.swg-stormtrooper-2:before {
content: "\f070";
}
.swg-snowtrooper:before {
content: "\f071";
}
.swg-clonetrooper:before {
content: "\f072";
}
.swg-bobafett:before {
content: "\f073";
}
.swg-jabba:before {
content: "\f074";
}
.swg-jabba:before {
content: "\f074";
}
.swg-imperialguard:before {
content: "\f075";
}
.swg-quigonjinn:before {
content: "\f076";
}
.swg-obiwankenobi:before {
content: "\f077";
}
.swg-macewindu:before {
content: "\f078";
}
.swg-starwarsrebels:before {
content: "\f079";
}
.swg-clonewars:before {
content: "\f07A";
}
.swg-swtrilogy:before {
content: "\f07B";
}
.swg-100clones:before {
content: "\f07C";
}
.swg-lastjedi:before {
content: "\f07D";
}
.swg-lastjedi-2:before {
content: "\f07E";
}
.swg-40starwars:before {
content: "\f07F";
}
.swg-darthvader-5:before {
content: "\f080";
}
.swg-stormtrooper-3:before {
content: "\f081";
}
.swg-clonetrooper-2:before {
content: "\f082";
}
.swg-bobafett-2:before {
content: "\f083";
}
.swg-kylo-2:before {
content: "\f084";
}
.swg-wookie-2:before {
content: "\f085";
}
.swg-r2d2-3:before {
content: "\f086";
}
.swg-c3po-3:before {
content: "\f087";
}
.swg-bb8-4:before {
content: "\f088";
}
.swg-xwingpilot:before {
content: "\f089";
}
.swg-firstorder:before {
content: "\f08A";
}
.swg-phoenix:before {
content: "\f08B";
}
.swg-porg-1:before {
content: "\f08C";
}
.swg-porg-2:before {
content: "\f08D";
}
.swg-porg-3:before {
content: "\f08E";
}
.swg-porg-4:before {
content: "\f08F";
}
.swg-porg-5:before {
content: "\f090";
}
.swg-porg-6:before {
content: "\f091";
}
.swg-porg-7:before {
content: "\f092";
}
.swg-porg-8:before {
content: "\f093";
}
.swg-porg-9:before {
content: "\f094";
}
.swg-porg-10:before {
content: "\f095";
}
.swg-decals-1:before {
content: "\f096";
}
.swg-decals-2:before {
content: "\f097";
}
.swg-decals-3:before {
content: "\f098";
}
.swg-decals-4:before {
content: "\f099";
}
.swg-decals-5:before {
content: "\f09A";
}
.swg-holocron-1:before {
content: "\f09B";
}
.swg-holocron-2:before {
content: "\f09C";
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
.sr-only-focusable:active,
.sr-only-focusable:focus {
position: static;
width: auto;
height: auto;
margin: 0;
overflow: visible;
clip: auto;
}

1566
public/styles.css Normal file

File diff suppressed because it is too large Load Diff