39514-vm/index-en.php
Flatlogic Bot 83915bc446 V1.3.9
2026-04-19 09:50:50 +00:00

2660 lines
89 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
require_once __DIR__ . '/db/auth.php';
require_once __DIR__ . '/db/config.php';
function index_scan_normalize_rarity(?string $rarity): string
{
return strtoupper(trim((string) $rarity));
}
function index_scan_rarity_class(?string $rarity): string
{
return match (index_scan_normalize_rarity($rarity)) {
'L' => 'scan-rarity-L',
'E' => 'scan-rarity-E',
'R' => 'scan-rarity-R',
'U' => 'scan-rarity-U',
'C' => 'scan-rarity-C',
default => 'scan-rarity-none',
};
}
function index_scan_rarity_label(?string $rarity): string
{
return match (index_scan_normalize_rarity($rarity)) {
'L' => 'Légendaire',
'E' => 'Épique',
'R' => 'Rare',
'U' => 'Peu commun',
'C' => 'Commun',
default => 'Non définie',
};
}
function index_itemcustom_display_value($value): string
{
if ($value === null || $value === '') {
return '0';
}
if (is_numeric($value)) {
$formatted = number_format((float) $value, 2, '.', '');
$formatted = rtrim(rtrim($formatted, '0'), '.');
return $formatted === '' ? '0' : $formatted;
}
return trim((string) $value) !== '' ? trim((string) $value) : '0';
}
function index_itemcustom_stat_preview(?string $sign, $value, ?string $unit): string
{
$prefix = $sign === '-' ? '-' : ($sign === '+' ? '+' : '');
$displayValue = index_itemcustom_display_value($value);
$displayUnit = trim((string) ($unit ?? ''));
return trim($prefix . $displayValue . ($displayUnit !== '' ? ' ' . $displayUnit : ''));
}
function index_vanilla_description_html(?string $description): string
{
$description = trim((string) $description);
if ($description === '') {
return '<span class="vanilla-db-muted">Aucune description.</span>';
}
if ($description !== strip_tags($description)) {
return $description;
}
return nl2br(htmlspecialchars($description, ENT_QUOTES, 'UTF-8'));
}
auth_start_session();
auth_bootstrap();
$session_cl_auth_user = isset($_SESSION['user']) ? (string) $_SESSION['user'] : '';
$session_cl_auth_right = isset($_SESSION['role']) ? (string) $_SESSION['role'] : '';
$is_authenticated = $session_cl_auth_user !== '';
$has_member_access = $is_authenticated && in_array($session_cl_auth_right, ['member', 'admin'], true);
$has_vanilla_db_access = $is_authenticated && in_array($session_cl_auth_right, ['member', 'moderator', 'admin'], true);
$scan_reference_rows = [];
$scan_reference_max_occurrence = 0;
$scan_reference_error = null;
$ship_preset_rows = [];
$ship_preset_manufacturers = [];
$ship_preset_error = null;
$item_custom_public_rows = [];
$item_custom_public_types = [];
$item_custom_public_error = null;
$vanilla_db_rows = [];
$vanilla_db_error = null;
$vanilla_db_search = '';
$vanilla_db_per_page = 10;
$vanilla_db_total_rows = 0;
$vanilla_db_total_pages = 1;
$vanilla_db_current_page = 1;
$vanilla_db_result_start = 0;
$vanilla_db_result_end = 0;
$vanilla_db_base_query = ['open_modal' => 'vanilla-db'];
$vanilla_db_reset_url = 'index.php?open_modal=vanilla-db';
$should_open_vanilla_db_modal = false;
try {
$db = db();
$stmt_scan_reference = $db->query(
"SELECT o.cl_scobjs_id, o.cl_scobjs_name, o.cl_scobjs_rarity, m.cl_scmining_scan_value, m.cl_scmining_max_occurrence,
m.cl_scmining_can_manual, m.cl_scmining_can_land, m.cl_scmining_can_space
FROM tbl_scmining m
INNER JOIN tbl_scobjs o ON o.cl_scobjs_id = m.cl_scmining_obj_id
ORDER BY
CASE UPPER(COALESCE(o.cl_scobjs_rarity, ''))
WHEN 'L' THEN 1
WHEN 'E' THEN 2
WHEN 'R' THEN 3
WHEN 'U' THEN 4
WHEN 'C' THEN 5
ELSE 6
END,
o.cl_scobjs_name ASC"
);
foreach ($stmt_scan_reference->fetchAll() as $row) {
$occurrence_count = max(1, (int) ($row['cl_scmining_max_occurrence'] ?? 1));
$scan_value = (int) ($row['cl_scmining_scan_value'] ?? 0);
$recovery_modes = [];
if (!empty($row['cl_scmining_can_manual'])) {
$recovery_modes[] = 'Manuel';
}
if (!empty($row['cl_scmining_can_land'])) {
$recovery_modes[] = 'Terrestre';
}
if (!empty($row['cl_scmining_can_space'])) {
$recovery_modes[] = 'Spatial';
}
$scan_steps = [];
for ($i = 1; $i <= $occurrence_count; $i++) {
$scan_steps[] = $scan_value * $i;
}
$scan_reference_rows[] = [
'id' => (string) ($row['cl_scobjs_id'] ?? ''),
'name' => (string) ($row['cl_scobjs_name'] ?? ''),
'rarity' => index_scan_normalize_rarity($row['cl_scobjs_rarity'] ?? ''),
'rarity_label' => index_scan_rarity_label($row['cl_scobjs_rarity'] ?? ''),
'rarity_class' => index_scan_rarity_class($row['cl_scobjs_rarity'] ?? ''),
'recovery_modes' => $recovery_modes,
'scan_steps' => $scan_steps,
'max_occurrence' => $occurrence_count,
];
$scan_reference_max_occurrence = max($scan_reference_max_occurrence, $occurrence_count);
}
} catch (Throwable $e) {
$scan_reference_error = 'Impossible de charger le tableau des signatures de scan pour le moment.';
}
if ($has_member_access) {
try {
if (!isset($db) || !($db instanceof PDO)) {
$db = db();
}
$stmt_ship_presets = $db->query(
"SELECT cl_scpreset_id, cl_scpreset_name, cl_scpreset_manufacturer, cl_scpreset_description, cl_scpreset_link, cl_scpreset_creator
FROM tbl_scpreset
ORDER BY cl_scpreset_manufacturer ASC, cl_scpreset_name ASC"
);
foreach ($stmt_ship_presets->fetchAll() as $row) {
$ship_name = trim((string) ($row['cl_scpreset_name'] ?? '')) ?: 'Vaisseau inconnu';
$manufacturer = trim((string) ($row['cl_scpreset_manufacturer'] ?? '')) ?: 'Manufacture inconnue';
$manufacturer_key = function_exists('mb_strtolower')
? mb_strtolower($manufacturer, 'UTF-8')
: strtolower($manufacturer);
$ship_preset_manufacturers[$manufacturer_key] = $manufacturer;
$ship_preset_rows[] = [
'id' => (string) ($row['cl_scpreset_id'] ?? ''),
'name' => $ship_name,
'manufacturer' => $manufacturer,
'creator' => trim((string) ($row['cl_scpreset_creator'] ?? '')) ?: 'Inconnu',
'description' => trim((string) ($row['cl_scpreset_description'] ?? '')) ?: 'Aucune description disponible pour ce preset.',
'link' => trim((string) ($row['cl_scpreset_link'] ?? '')),
];
}
natcasesort($ship_preset_manufacturers);
$ship_preset_manufacturers = array_values($ship_preset_manufacturers);
} catch (Throwable $e) {
$ship_preset_error = 'Impossible de charger les presets de vaisseaux pour le moment.';
}
}
if ($has_member_access) {
try {
if (!isset($db) || !($db instanceof PDO)) {
$db = db();
}
$stmt_item_custom_public = $db->query(
"SELECT
c.cl_scitemcustom_id,
o.cl_scobjs_name,
o.cl_scobjs_uuid,
o.cl_scobjs_type,
o.cl_scobjs_subtype,
COALESCE(NULLIF(TRIM(a.cl_auth_user), ''), 'Inconnu') AS creator_name,
cs.cl_scitemcustomstat_id,
cs.cl_scitemcustomstat_sign,
cs.cl_scitemcustomstat_value,
st.cl_scstatsitem_name,
st.cl_scstatsitem_unit
FROM tbl_scitemcustom c
INNER JOIN tbl_scobjs o ON o.cl_scobjs_id = c.cl_scitemcustom_obj_id
LEFT JOIN tbl_auth a ON a.cl_auth_id = c.cl_scitemcustom_owner_auth_id
LEFT JOIN tbl_scitemcustomstat cs ON cs.cl_scitemcustomstat_itemcustom_id = c.cl_scitemcustom_id
LEFT JOIN tbl_scstatsitem st ON st.cl_scstatsitem_id = cs.cl_scitemcustomstat_stat_id
WHERE c.cl_scitemcustom_owner_auth_id IS NOT NULL
ORDER BY o.cl_scobjs_name ASC, creator_name ASC, c.cl_scitemcustom_id ASC, st.cl_scstatsitem_name ASC, cs.cl_scitemcustomstat_id ASC"
);
$grouped_item_custom_public = [];
foreach ($stmt_item_custom_public->fetchAll() as $row) {
$item_id = (int) ($row['cl_scitemcustom_id'] ?? 0);
if ($item_id <= 0) {
continue;
}
if (!isset($grouped_item_custom_public[$item_id])) {
$name = trim((string) ($row['cl_scobjs_name'] ?? '')) ?: 'Objet inconnu';
$creator = trim((string) ($row['creator_name'] ?? '')) ?: 'Inconnu';
$type = trim((string) ($row['cl_scobjs_type'] ?? ''));
$subtype = trim((string) ($row['cl_scobjs_subtype'] ?? ''));
$uuid = trim((string) ($row['cl_scobjs_uuid'] ?? ''));
$grouped_item_custom_public[$item_id] = [
'id' => $item_id,
'name' => $name,
'creator' => $creator,
'type' => $type,
'subtype' => $subtype,
'uuid' => $uuid,
'stats' => [],
'search_parts' => [$name, $creator, $type, $subtype, $uuid],
];
}
if (!empty($row['cl_scitemcustomstat_id'])) {
$stat_name = trim((string) ($row['cl_scstatsitem_name'] ?? '')) ?: 'Stat inconnue';
$stat_sign = trim((string) ($row['cl_scitemcustomstat_sign'] ?? ''));
$stat_value_raw = $row['cl_scitemcustomstat_value'] ?? 0;
$stat_unit = trim((string) ($row['cl_scstatsitem_unit'] ?? ''));
$stat_preview = index_itemcustom_stat_preview($stat_sign, $stat_value_raw, $stat_unit);
$grouped_item_custom_public[$item_id]['stats'][] = [
'name' => $stat_name,
'preview' => $stat_preview,
];
$grouped_item_custom_public[$item_id]['search_parts'][] = $stat_name;
$grouped_item_custom_public[$item_id]['search_parts'][] = $stat_preview;
}
}
$item_custom_public_types_lookup = [];
foreach ($grouped_item_custom_public as $item_custom_public_row) {
$item_custom_public_row['search'] = trim(implode(' ', array_filter($item_custom_public_row['search_parts'], static function ($value) {
return trim((string) $value) !== '';
})));
unset($item_custom_public_row['search_parts']);
$item_custom_public_rows[] = $item_custom_public_row;
$item_custom_public_type_label = $item_custom_public_row['type'] !== '' ? $item_custom_public_row['type'] : 'Type inconnu';
$item_custom_public_type_key = strtolower($item_custom_public_type_label);
if (!isset($item_custom_public_types_lookup[$item_custom_public_type_key])) {
$item_custom_public_types_lookup[$item_custom_public_type_key] = $item_custom_public_type_label;
}
}
natcasesort($item_custom_public_types_lookup);
$item_custom_public_types = array_values($item_custom_public_types_lookup);
} catch (Throwable $e) {
$item_custom_public_error = 'Impossible de charger les objets Item Custom pour le moment.';
}
}
if ($has_vanilla_db_access) {
$vanilla_db_search = trim((string) ($_GET['vanilla_search'] ?? ''));
$vanilla_db_current_page = max(1, (int) ($_GET['vanilla_page'] ?? 1));
$should_open_vanilla_db_modal = (
(isset($_GET['open_modal']) && (string) $_GET['open_modal'] === 'vanilla-db')
|| $vanilla_db_search !== ''
|| isset($_GET['vanilla_page'])
);
$vanilla_db_base_query = ['open_modal' => 'vanilla-db'];
if ($vanilla_db_search !== '') {
$vanilla_db_base_query['vanilla_search'] = $vanilla_db_search;
}
$vanilla_db_reset_url = 'index.php?' . http_build_query(['open_modal' => 'vanilla-db']);
try {
if (!isset($db) || !($db instanceof PDO)) {
$db = db();
}
$vanilla_db_where_sql = '';
$vanilla_db_bindings = [];
if ($vanilla_db_search !== '') {
$vanilla_db_where_sql = "
WHERE (
cl_scobjs_name LIKE :vanilla_search
OR cl_scobjs_type LIKE :vanilla_search
OR cl_scobjs_subtype LIKE :vanilla_search
OR cl_scobjs_uuid LIKE :vanilla_search
OR cl_scobjs_rarity LIKE :vanilla_search
OR cl_scobjs_description LIKE :vanilla_search
)";
$vanilla_db_bindings[':vanilla_search'] = '%' . $vanilla_db_search . '%';
}
$stmt_vanilla_db_count = $db->prepare(
"SELECT COUNT(*)
FROM tbl_scobjs" . $vanilla_db_where_sql
);
foreach ($vanilla_db_bindings as $bindingName => $bindingValue) {
$stmt_vanilla_db_count->bindValue($bindingName, $bindingValue, PDO::PARAM_STR);
}
$stmt_vanilla_db_count->execute();
$vanilla_db_total_rows = (int) $stmt_vanilla_db_count->fetchColumn();
$vanilla_db_total_pages = max(1, (int) ceil($vanilla_db_total_rows / $vanilla_db_per_page));
$vanilla_db_current_page = min($vanilla_db_current_page, $vanilla_db_total_pages);
$vanilla_db_offset = ($vanilla_db_current_page - 1) * $vanilla_db_per_page;
$stmt_vanilla_db = $db->prepare(
"SELECT cl_scobjs_id, cl_scobjs_name, cl_scobjs_type, cl_scobjs_subtype, cl_scobjs_uuid, cl_scobjs_rarity, cl_scobjs_description
FROM tbl_scobjs" . $vanilla_db_where_sql . "
ORDER BY cl_scobjs_id DESC
LIMIT :vanilla_limit OFFSET :vanilla_offset"
);
foreach ($vanilla_db_bindings as $bindingName => $bindingValue) {
$stmt_vanilla_db->bindValue($bindingName, $bindingValue, PDO::PARAM_STR);
}
$stmt_vanilla_db->bindValue(':vanilla_limit', $vanilla_db_per_page, PDO::PARAM_INT);
$stmt_vanilla_db->bindValue(':vanilla_offset', $vanilla_db_offset, PDO::PARAM_INT);
$stmt_vanilla_db->execute();
foreach ($stmt_vanilla_db->fetchAll() as $row) {
$name = trim((string) ($row['cl_scobjs_name'] ?? '')) ?: 'Objet inconnu';
$type = trim((string) ($row['cl_scobjs_type'] ?? '')) ?: '—';
$subtype = trim((string) ($row['cl_scobjs_subtype'] ?? '')) ?: '—';
$uuid = trim((string) ($row['cl_scobjs_uuid'] ?? '')) ?: '—';
$rarity = index_scan_normalize_rarity($row['cl_scobjs_rarity'] ?? '');
$description = trim((string) ($row['cl_scobjs_description'] ?? ''));
$vanilla_db_rows[] = [
'id' => (int) ($row['cl_scobjs_id'] ?? 0),
'name' => $name,
'type' => $type,
'subtype' => $subtype,
'uuid' => $uuid,
'image_url' => $uuid !== '—' ? 'https://cstone.space/uifimages/' . rawurlencode($uuid) . '.png' : null,
'rarity' => $rarity !== '' ? $rarity : '—',
'rarity_label' => $rarity !== '' ? index_scan_rarity_label($rarity) : 'Non définie',
'rarity_class' => index_scan_rarity_class($rarity),
'description_html' => index_vanilla_description_html($description),
];
}
if ($vanilla_db_total_rows > 0) {
$vanilla_db_result_start = $vanilla_db_offset + 1;
$vanilla_db_result_end = $vanilla_db_offset + count($vanilla_db_rows);
}
} catch (Throwable $e) {
$vanilla_db_error = 'Impossible de charger la base des objets pour le moment.';
}
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="BlackOps Agency, Star Citizen, Advocacy, UEE, Sécurité, Exploration, Renseignement, Patrouilles, Chasses de primes, Investigation, Neutralisation de menaces, Assistance humanitaire, Abordages, Réseaux anti-toxicité, Formation tactique, Entraînements, Coopération, Zones UEE, Systèmes inexplorés, Lore Star Citizen, Missions multi-joueurs, Protocole Arkange, Protocole Vigilance, Respect des lois impériales, Justice interstellaire, Criminalité galactique, Piraterie, XenoThreat, Vanduul, Sécurité spatiale, Leadership BlackOps, Collaboration stratégique, Combat spatial, Star Citizen gameplay, Exploration stratégique, Missions BlackOps Agency, Réputation UEE, Gestion des conflits, BlackOps, Agency, Recrutement Star Citizen, Zones de guerre, Star Citizen immersion.">
<title>Rapid Emergency & Action Combat Team / Star Citizen</title>
<link rel="stylesheet" type="text/css" href="css/styles.css">
<link rel="stylesheet" type="text/css" href="css/default.css" />
<link rel="stylesheet" type="text/css" href="css/component.css" />
<link rel="stylesheet" type="text/css" href="css/switch.css" />
<link rel="stylesheet" type="text/css" href="css/styles-modal.css" />
<script src="js/modernizr.custom.js"></script>
<style>
.modal-scan-reference {
width: 92%;
max-width: 1400px;
min-width: 960px;
}
.scan-reference-dialog {
background: linear-gradient(180deg, rgba(24, 28, 37, 0.98), rgba(11, 14, 20, 0.98));
border: 1px solid rgba(162, 155, 120, 0.35);
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.45);
}
.scan-reference-dialog h3 {
font-size: 1.8em;
letter-spacing: 0.08em;
}
.scan-reference-dialog > div {
padding: 22px 26px 26px;
}
.scan-reference-intro,
.scan-reference-empty,
.scan-reference-error {
margin: 0 0 18px;
padding: 12px 14px !important;
border-radius: 10px;
font-size: 0.95em;
text-align: left !important;
}
.scan-reference-intro {
background: rgba(162, 155, 120, 0.12);
border: 1px solid rgba(162, 155, 120, 0.18);
}
.scan-reference-empty {
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.12);
}
.scan-reference-error {
background: rgba(140, 32, 32, 0.18);
border: 1px solid rgba(255, 88, 88, 0.25);
color: #ffbdbd;
}
.scan-reference-filter-bar {
display: flex;
justify-content: space-between;
align-items: center;
gap: 14px;
margin: 0 0 14px;
padding: 10px 12px;
border: 1px solid rgba(162, 155, 120, 0.18);
border-radius: 10px;
background: rgba(162, 155, 120, 0.07);
}
.scan-reference-filter-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.scan-reference-filter-btn {
padding: 7px 12px;
border: 1px solid rgba(162, 155, 120, 0.35);
border-radius: 999px;
background: rgba(162, 155, 120, 0.14);
color: #f2eed9;
font-size: 0.60em;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
cursor: pointer;
text-decoration: none;
text-align: center;
}
.vanilla-db-search-btn {
background: rgba(162, 155, 120, 0.2);
}
.scan-reference-filter-btn:hover,
.scan-reference-filter-btn:focus-visible {
background: rgba(162, 155, 120, 0.22);
outline: none;
}
.scan-reference-filter-btn.is-disabled {
opacity: 0.45;
cursor: not-allowed;
}
.scan-reference-filter-status {
margin: 0;
font-size: 0.74em;
color: rgba(255, 255, 255, 0.72);
text-align: right;
}
.scan-reference-table-wrapper {
max-height: 65vh;
overflow: auto;
border: 1px solid rgba(162, 155, 120, 0.22);
border-radius: 12px;
background: rgba(6, 9, 14, 0.65);
}
.scan-reference-table {
width: 100%;
border-collapse: collapse;
min-width: 880px;
font-size: 0.84em;
}
.scan-reference-table th,
.scan-reference-table td {
padding: 9px 11px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
vertical-align: middle;
text-align: left;
}
.scan-reference-table th {
position: sticky;
top: 0;
z-index: 2;
background: rgba(18, 22, 30, 0.96);
text-transform: uppercase;
font-size: 0.74em;
letter-spacing: 0.08em;
color: #d7cfac;
}
.scan-reference-table tbody tr {
cursor: pointer;
transition: background 0.18s ease, box-shadow 0.18s ease, opacity 0.18s ease;
}
.scan-reference-table tbody tr:hover {
background: rgba(162, 155, 120, 0.08);
}
.scan-reference-table tbody tr.is-selected {
background: rgba(162, 155, 120, 0.16);
box-shadow: inset 0 0 0 1px rgba(162, 155, 120, 0.35);
}
.scan-reference-table tbody tr.is-hidden-by-filter {
display: none;
}
.scan-reference-name span {
display: block;
font-weight: 700;
font-size: 0.94em;
}
.scan-reference-recovery {
min-width: 180px;
}
.scan-reference-pill {
display: inline-block;
margin: 0 5px 5px 0;
padding: 4px 8px;
border-radius: 999px;
background: rgba(162, 155, 120, 0.16);
border: 1px solid rgba(162, 155, 120, 0.24);
font-size: 0.72em;
white-space: nowrap;
}
.scan-reference-value {
text-align: center;
font-variant-numeric: tabular-nums;
font-weight: 700;
font-size: 0.92em;
white-space: nowrap;
}
.scan-reference-muted {
color: rgba(255, 255, 255, 0.35);
}
.scan-rarity-L .scan-reference-name span,
.scan-rarity-L .scan-reference-value {
color: #ff8000;
}
.scan-rarity-E .scan-reference-name span,
.scan-rarity-E .scan-reference-value {
color: #a335ee;
}
.scan-rarity-R .scan-reference-name span,
.scan-rarity-R .scan-reference-value {
color: #4db2ff;
}
.scan-rarity-U .scan-reference-name span,
.scan-rarity-U .scan-reference-value {
color: #1eff00;
}
.scan-rarity-C .scan-reference-name span,
.scan-rarity-C .scan-reference-value {
color: #ffffff;
}
.scan-rarity-none .scan-reference-name span,
.scan-rarity-none .scan-reference-value {
color: #b5bcc8;
}
.modal-ship-presets {
width: 92%;
max-width: 1320px;
min-width: 940px;
}
.modal-item-custom {
width: 94%;
max-width: 1500px;
min-width: 1040px;
}
.modal-vanilla-db {
width: 92%;
max-width: 1440px;
min-width: 980px;
}
.item-custom-dialog {
display: flex;
flex-direction: column;
background: linear-gradient(180deg, rgba(15, 20, 29, 0.98), rgba(7, 10, 16, 0.98));
border: 1px solid rgba(162, 155, 120, 0.35);
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.45);
max-height: min(88vh, 980px);
}
.item-custom-dialog h3 {
font-size: 1.75em;
letter-spacing: 0.08em;
}
.item-custom-dialog > div {
display: flex;
flex-direction: column;
flex: 1 1 auto;
min-height: 0;
overflow: hidden;
padding: 22px 26px 26px;
}
.item-custom-empty,
.item-custom-error {
margin: 0 0 18px;
padding: 12px 14px !important;
border-radius: 10px;
font-size: 0.95em;
text-align: left !important;
}
.item-custom-empty {
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.12);
}
.item-custom-error {
background: rgba(140, 32, 32, 0.18);
border: 1px solid rgba(255, 88, 88, 0.25);
color: #ffbdbd;
}
.item-custom-filter-bar {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 10px;
margin: 0 0 18px;
padding: 14px 16px;
border-radius: 14px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(162, 155, 120, 0.18);
}
.item-custom-filter-top {
display: flex;
align-items: center;
gap: 14px;
width: 100%;
}
.item-custom-search-wrap {
display: flex;
align-items: center;
gap: 12px;
flex: 1 1 auto;
min-width: 0;
}
.item-custom-type-filters {
display: flex;
align-items: flex-start;
gap: 8px;
width: 100%;
}
.item-custom-type-label {
font-size: 0.64em;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.62);
}
.item-custom-type-options {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.item-custom-type-chip {
position: relative;
display: inline-flex;
align-items: center;
cursor: pointer;
}
.item-custom-type-chip input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.item-custom-type-chip span {
display: inline-flex;
align-items: center;
padding: 4px 9px;
min-height: 24px;
border-radius: 999px;
border: 1px solid rgba(162, 155, 120, 0.22);
background: rgba(255, 255, 255, 0.028);
color: rgba(255, 255, 255, 0.76);
font-size: 0.62em;
font-weight: 400;
letter-spacing: 0.03em;
line-height: 1;
transition: border-color 0.18s ease, background 0.18s ease, color 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease;
}
.item-custom-type-chip input:checked + span {
border-color: rgba(162, 155, 120, 0.72);
background: rgba(162, 155, 120, 0.18);
color: #fff7dd;
box-shadow: inset 0 0 0 1px rgba(162, 155, 120, 0.18);
}
.item-custom-type-chip input:focus-visible + span {
outline: 2px solid rgba(162, 155, 120, 0.45);
outline-offset: 2px;
}
.item-custom-search-input {
width: 100%;
padding: 10px 14px;
border-radius: 10px;
border: 1px solid rgba(162, 155, 120, 0.3);
background: rgba(10, 14, 21, 0.95);
color: #ffffff;
font-size: 0.92em;
}
.item-custom-search-input:focus-visible {
outline: 2px solid rgba(162, 155, 120, 0.45);
outline-offset: 2px;
}
.item-custom-reset-btn {
padding: 10px 14px;
border-radius: 999px;
border: 1px solid rgba(162, 155, 120, 0.34);
background: rgba(162, 155, 120, 0.1);
color: #f6f1dc;
font-size: 0.76em;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
cursor: pointer;
}
.item-custom-filter-status {
margin: 0;
flex: 0 0 auto;
font-size: 0.85em;
color: rgba(255, 255, 255, 0.7);
text-align: right;
white-space: nowrap;
}
.item-custom-grid {
display: flex;
flex-direction: column;
flex: 1 1 auto;
gap: 8px;
min-height: clamp(320px, 52vh, 620px);
max-height: none;
overflow-y: auto;
padding-right: 4px;
}
.item-custom-card {
display: grid;
grid-template-columns: 56px minmax(0, 430px) minmax(320px, 1.65fr);
gap: 14px;
align-items: center;
padding: 7px 14px 8px;
border-radius: 12px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.022));
border: 1px solid rgba(162, 155, 120, 0.2);
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.18);
}
.item-custom-card-media {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 52px !important;
height: 52px !important;
min-width: 52px;
min-height: 52px;
aspect-ratio: 1 / 1;
overflow: hidden;
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.12);
background: rgba(255, 255, 255, 0.04);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
cursor: zoom-in;
flex-shrink: 0;
}
.item-custom-card-media.is-image-missing {
cursor: default;
}
.item-custom-card-media.is-image-missing::after {
content: 'N/A';
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.62em;
font-weight: 700;
letter-spacing: 0.12em;
color: rgba(255, 255, 255, 0.45);
background: linear-gradient(180deg, rgba(255, 255, 255, 0.04), rgba(255, 255, 255, 0.01));
}
.item-custom-card-main {
display: flex;
flex-direction: column;
justify-content: center;
gap: 1px;
min-width: 0;
padding-top: 0;
}
.item-custom-card-stats {
display: flex;
flex-direction: column;
gap: 4px;
min-width: 0;
}
.item-custom-card-stats-header {
display: flex;
justify-content: flex-end;
align-items: center;
}
.item-custom-card-thumb {
width: 100% !important;
height: 100% !important;
min-width: 100%;
max-width: none;
border-radius: 0;
object-fit: cover;
background: transparent;
border: 0;
box-shadow: none;
display: block;
flex: 1 0 auto;
}
.item-custom-card-media.is-image-missing .item-custom-card-thumb,
.item-custom-card-media.is-image-missing .item-custom-card-preview {
display: none !important;
}
.item-custom-card-preview {
visibility: hidden;
opacity: 0;
position: absolute;
top: -10px;
left: 68px;
z-index: 1000;
padding: 5px;
background: rgba(11, 15, 22, 0.97);
border: 1px solid rgba(162, 155, 120, 0.5);
border-radius: 10px;
box-shadow: 0 14px 40px rgba(0, 0, 0, 0.55), 0 0 20px rgba(162, 155, 120, 0.2);
backdrop-filter: blur(12px);
transition: opacity 0.18s ease, visibility 0.18s ease;
pointer-events: none;
overflow: hidden;
}
.item-custom-card-preview img {
width: 320px;
height: 320px;
object-fit: contain;
display: block;
border-radius: 6px;
background: rgba(255, 255, 255, 0.02);
}
.item-custom-card-media:hover .item-custom-card-preview,
.item-custom-card-media:focus-within .item-custom-card-preview {
visibility: visible;
opacity: 1;
}
.item-custom-card-type {
margin: 0;
font-size: 0.65em;
text-transform: uppercase;
letter-spacing: 0.115em;
line-height: 1.08;
color: rgba(162, 155, 120, 0.95);
}
.item-custom-card-name {
margin: 0;
font-size: 0.97em;
line-height: 1.08;
color: #ffffff;
word-break: normal;
overflow-wrap: normal;
hyphens: none;
}
.item-custom-card-meta {
margin: 0;
font-size: 0.73em;
line-height: 1.08;
color: rgba(255, 255, 255, 0.68);
}
.item-custom-card-count {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 3px 9px;
border-radius: 999px;
background: rgba(162, 155, 120, 0.12);
border: 1px solid rgba(162, 155, 120, 0.22);
color: #f6f1dc;
font-size: 0.6em;
font-weight: 700;
letter-spacing: 0.075em;
text-transform: uppercase;
white-space: nowrap;
}
.item-custom-stats-list {
display: flex;
flex-wrap: wrap;
gap: 5px 6px;
margin: 0;
padding: 0;
list-style: none;
}
.item-custom-stat-chip {
display: inline-flex;
align-items: center;
flex-wrap: nowrap;
gap: 10px;
padding: 6px 18px 7px !important;
padding-left: 18px !important;
padding-right: 18px !important;
border-radius: 11px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
min-height: 30px;
max-width: 100%;
box-sizing: border-box;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
}
.item-custom-stat-name {
font-size: 0.72em;
color: rgba(255, 255, 255, 0.76);
white-space: nowrap;
line-height: 1.05;
}
.item-custom-stat-value {
font-size: 0.79em;
font-weight: 700;
color: #ffffff;
word-break: keep-all;
white-space: nowrap;
padding: 0 2px;
}
.item-custom-stat-chip.sign-plus {
border-color: rgba(127, 255, 185, 0.24);
background: rgba(0, 255, 136, 0.1);
}
.item-custom-stat-chip.sign-plus .item-custom-stat-value {
color: #7fffb9;
}
.item-custom-stat-chip.sign-minus {
border-color: rgba(255, 120, 120, 0.22);
background: rgba(255, 77, 77, 0.1);
}
.item-custom-stat-chip.sign-minus .item-custom-stat-value {
color: #ffb0b0;
}
.item-custom-stat-chip.sign-neutral {
background: rgba(255, 255, 255, 0.045);
}
.item-custom-stat-chip.sign-neutral .item-custom-stat-value {
color: #f1f1f1;
}
.item-custom-card-empty {
margin: 0;
padding: 12px 14px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
color: rgba(255, 255, 255, 0.72);
font-size: 0.88em;
}
.item-custom-card.is-hidden-by-search,
.item-custom-card.is-hidden-by-type {
display: none;
}
.ship-presets-dialog {
background: linear-gradient(180deg, rgba(17, 22, 31, 0.98), rgba(8, 11, 18, 0.98));
border: 1px solid rgba(162, 155, 120, 0.35);
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.45);
}
.ship-presets-dialog h3 {
font-size: 1.75em;
letter-spacing: 0.08em;
}
.ship-presets-dialog > div {
padding: 22px 26px 26px;
}
.ship-presets-empty,
.ship-presets-error {
margin: 0 0 18px;
padding: 12px 14px !important;
border-radius: 10px;
font-size: 0.95em;
text-align: left !important;
}
.ship-presets-empty {
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.12);
}
.ship-presets-error {
background: rgba(140, 32, 32, 0.18);
border: 1px solid rgba(255, 88, 88, 0.25);
color: #ffbdbd;
}
.ship-presets-filter-bar {
display: flex;
align-items: flex-end;
gap: 16px;
margin: 0 0 18px;
padding: 14px 16px;
border-radius: 14px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(162, 155, 120, 0.18);
flex-wrap: wrap;
}
.ship-presets-filter-group {
display: flex;
flex-direction: column;
gap: 8px;
min-width: 240px;
flex: 1 1 240px;
}
.ship-presets-filter-label {
margin: 0;
font-size: 0.8em;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.88);
}
.ship-presets-filter-select {
width: 100%;
padding: 10px 14px;
border-radius: 10px;
border: 1px solid rgba(162, 155, 120, 0.3);
background: rgba(10, 14, 21, 0.95);
color: #ffffff;
font-size: 0.92em;
}
.ship-presets-filter-select:disabled {
opacity: 0.55;
cursor: not-allowed;
}
.ship-presets-filter-select:focus-visible {
outline: 2px solid rgba(162, 155, 120, 0.45);
outline-offset: 2px;
}
.ship-presets-filter-status {
margin: 0 0 0 auto;
font-size: 0.85em;
color: rgba(255, 255, 255, 0.7);
flex: 1 1 220px;
text-align: right;
align-self: center;
}
.ship-presets-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 18px;
max-height: 68vh;
overflow-y: auto;
padding-right: 4px;
}
.ship-preset-card {
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 290px;
padding: 18px;
border-radius: 16px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.025));
border: 1px solid rgba(162, 155, 120, 0.22);
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.22);
gap: 14px;
}
.ship-preset-card-header {
display: flex;
flex-direction: column;
gap: 8px;
}
.ship-preset-card-manufacturer {
margin: 0;
font-size: 0.74em;
text-transform: uppercase;
letter-spacing: 0.14em;
color: rgba(162, 155, 120, 0.95);
}
.ship-preset-card-name {
margin: 0;
font-size: 1.35em;
line-height: 1.2;
color: #ffffff;
text-transform: uppercase;
}
.ship-preset-card-meta {
margin: 0;
font-size: 0.78em;
color: rgba(255, 255, 255, 0.68);
}
.ship-preset-card-description {
margin: 0;
font-size: 0.88em;
line-height: 1.6;
color: rgba(255, 255, 255, 0.82);
flex: 1;
}
.ship-preset-card-link {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 14px;
border-radius: 999px;
border: 1px solid rgba(162, 155, 120, 0.4);
background: rgba(162, 155, 120, 0.12);
color: #f6f1dc;
font-size: 0.76em;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
text-decoration: none;
transition: background 0.18s ease, transform 0.18s ease;
}
.ship-preset-card-link:hover,
.ship-preset-card-link:focus-visible {
background: rgba(162, 155, 120, 0.22);
transform: translateY(-1px);
outline: none;
}
.ship-preset-card.is-hidden-by-ship-filter {
display: none;
}
.vanilla-db-dialog {
display: flex;
flex-direction: column;
background: linear-gradient(180deg, rgba(18, 22, 31, 0.98), rgba(8, 11, 18, 0.98));
border: 1px solid rgba(162, 155, 120, 0.35);
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.45);
max-height: min(88vh, 980px);
}
.vanilla-db-dialog h3 {
font-size: 1.75em;
letter-spacing: 0.08em;
}
.vanilla-db-dialog > div {
display: flex;
flex-direction: column;
flex: 1 1 auto;
min-height: 0;
overflow: hidden;
padding: 22px 26px 26px;
}
.vanilla-db-empty,
.vanilla-db-error {
margin: 0 0 18px;
padding: 12px 14px !important;
border-radius: 10px;
font-size: 0.95em;
text-align: left !important;
}
.vanilla-db-empty {
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.12);
}
.vanilla-db-error {
background: rgba(140, 32, 32, 0.18);
border: 1px solid rgba(255, 88, 88, 0.25);
color: #ffbdbd;
}
.vanilla-db-filter-bar {
display: flex;
align-items: center;
gap: 14px;
margin: 0 0 18px;
padding: 14px 16px;
border-radius: 14px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(162, 155, 120, 0.18);
flex-wrap: wrap;
}
.vanilla-db-search-wrap {
display: flex;
align-items: center;
gap: 12px;
flex: 1 1 420px;
min-width: 260px;
}
.vanilla-db-search-input {
width: 100%;
padding: 10px 14px;
border-radius: 10px;
border: 1px solid rgba(162, 155, 120, 0.3);
background: rgba(10, 14, 21, 0.95);
color: #ffffff;
font-size: 0.92em;
}
.vanilla-db-search-input:focus-visible {
outline: 2px solid rgba(162, 155, 120, 0.45);
outline-offset: 2px;
}
.vanilla-db-search-btn,
.vanilla-db-reset-btn {
padding: 10px 14px;
border-radius: 999px;
border: 1px solid rgba(162, 155, 120, 0.34);
background: rgba(162, 155, 120, 0.1);
color: #f6f1dc;
font-size: 0.76em;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
cursor: pointer;
text-decoration: none;
text-align: center;
}
.vanilla-db-search-btn {
background: rgba(162, 155, 120, 0.2);
}
.vanilla-db-status {
margin: 0 0 0 auto;
font-size: 0.85em;
color: rgba(255, 255, 255, 0.7);
text-align: right;
flex: 1 1 220px;
}
.vanilla-db-grid {
display: flex;
flex-direction: column;
flex: 1 1 auto;
gap: 8px;
min-height: clamp(320px, 54vh, 700px);
overflow-y: auto;
padding-right: 4px;
}
.vanilla-db-card {
grid-template-columns: 56px minmax(260px, 380px) minmax(320px, 1fr);
align-items: start;
}
.vanilla-db-card-main {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-self: start;
gap: 6px;
min-width: 0;
padding-top: 0;
}
.vanilla-db-card-topline {
display: flex;
align-items: center;
justify-content: flex-start;
flex-wrap: wrap;
gap: 8px;
margin: 0;
}
.vanilla-db-card-type-badge {
display: inline-flex;
align-items: center;
padding: 4px 9px;
border-radius: 999px;
border: 1px solid rgba(162, 155, 120, 0.28);
background: rgba(162, 155, 120, 0.12);
color: rgba(246, 241, 220, 0.96);
font-size: 0.7em;
font-weight: 500;
letter-spacing: 0.08em;
line-height: 1;
text-transform: uppercase;
}
.vanilla-db-card-subtype {
color: rgba(255, 255, 255, 0.72);
font-size: 0.8em;
font-weight: 400;
letter-spacing: 0.04em;
line-height: 1.2;
text-transform: uppercase;
}
.vanilla-db-card-meta {
margin: 0;
color: rgba(255, 255, 255, 0.68);
font-size: 0.8em;
line-height: 1.35;
}
.vanilla-db-card-meta span {
word-break: break-all;
}
.vanilla-db-card-uuid {
display: inline-block;
font-size: 0.76em;
color: rgba(255, 255, 255, 0.58);
letter-spacing: 0.02em;
}
.vanilla-db-card-details {
display: flex;
align-items: flex-start;
justify-content: flex-start;
min-width: 0;
}
.vanilla-db-card-description {
width: 100%;
margin: 0;
font-size: 0.8em;
line-height: 1.45;
color: rgba(255, 255, 255, 0.8);
word-break: break-word;
overflow-wrap: anywhere;
}
.vanilla-db-card-description p {
margin: 0 0 0.45em;
}
.vanilla-db-card-description p:last-child {
margin-bottom: 0;
}
.vanilla-db-muted {
color: rgba(255, 255, 255, 0.45);
}
.item-custom-card-name.vanilla-db-item-name {
text-shadow: 0 0 10px rgba(255, 255, 255, 0.08);
}
.item-custom-card-name.vanilla-db-item-name.scan-rarity-L { color: #ff8000; }
.item-custom-card-name.vanilla-db-item-name.scan-rarity-E { color: #a335ee; }
.item-custom-card-name.vanilla-db-item-name.scan-rarity-R { color: #4db2ff; }
.item-custom-card-name.vanilla-db-item-name.scan-rarity-U { color: #1eff00; }
.item-custom-card-name.vanilla-db-item-name.scan-rarity-C { color: #ffffff; }
.item-custom-card-name.vanilla-db-item-name.scan-rarity-none { color: #b5bcc8; }
.vanilla-db-hover-preview {
position: fixed;
top: 0;
left: 0;
z-index: 10000;
display: none;
width: min(360px, calc(100vw - 32px));
padding: 6px;
border-radius: 12px;
border: 1px solid rgba(162, 155, 120, 0.42);
background: rgba(9, 13, 19, 0.98);
box-shadow: 0 18px 44px rgba(0, 0, 0, 0.58), 0 0 18px rgba(162, 155, 120, 0.18);
backdrop-filter: blur(14px);
pointer-events: none;
}
.vanilla-db-hover-preview.is-visible {
display: block;
}
.vanilla-db-hover-preview img {
display: block;
width: 100%;
height: auto;
max-height: min(360px, calc(100vh - 32px));
object-fit: contain;
border-radius: 8px;
background: rgba(255, 255, 255, 0.02);
}
.vanilla-db-pagination {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-top: 16px;
padding: 14px 16px;
border-radius: 14px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(162, 155, 120, 0.18);
flex-wrap: wrap;
}
.vanilla-db-pagination-pages {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.vanilla-db-pagination-summary {
margin: 0;
font-size: 0.84em;
color: rgba(255, 255, 255, 0.68);
}
.vanilla-db-page-link,
.vanilla-db-page-current,
.vanilla-db-page-ellipsis {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 40px;
height: 40px;
padding: 0 12px;
border-radius: 999px;
font-size: 0.82em;
font-weight: 700;
letter-spacing: 0.04em;
}
.vanilla-db-page-link {
border: 1px solid rgba(162, 155, 120, 0.24);
background: rgba(162, 155, 120, 0.08);
color: #f6f1dc;
text-decoration: none;
}
.vanilla-db-page-link:hover,
.vanilla-db-page-link:focus-visible {
background: rgba(162, 155, 120, 0.18);
outline: none;
}
.vanilla-db-page-current {
border: 1px solid rgba(255, 255, 255, 0.18);
background: rgba(255, 255, 255, 0.12);
color: #ffffff;
}
.vanilla-db-page-ellipsis {
min-width: auto;
height: auto;
padding: 0 2px;
color: rgba(255, 255, 255, 0.45);
}
.vanilla-db-card.is-hidden-by-search {
display: none;
}
@media (max-width: 1100px) {
.modal-scan-reference,
.modal-ship-presets,
.modal-item-custom,
.modal-vanilla-db {
width: 96%;
min-width: 0;
}
.scan-reference-dialog h3,
.ship-presets-dialog h3,
.item-custom-dialog h3,
.vanilla-db-dialog h3 {
font-size: 1.2em;
}
.scan-reference-filter-bar {
flex-direction: column;
align-items: stretch;
}
.scan-reference-filter-status {
text-align: left;
}
.ship-presets-filter-bar {
align-items: stretch;
}
.vanilla-db-filter-bar {
align-items: stretch;
}
.vanilla-db-status {
margin-left: 0;
text-align: left;
}
.ship-presets-filter-status {
margin-left: 0;
text-align: left;
}
.ship-presets-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.item-custom-filter-top {
align-items: stretch;
}
.item-custom-filter-status {
text-align: left;
white-space: normal;
}
.item-custom-card {
grid-template-columns: 52px minmax(280px, 1.9fr) minmax(220px, 1.3fr);
gap: 10px;
padding: 7px 10px;
}
.vanilla-db-card {
grid-template-columns: 52px minmax(220px, 1fr) minmax(140px, auto);
}
.item-custom-card-preview {
left: 62px;
}
}
@media (max-width: 720px) {
.item-custom-dialog {
max-height: 92vh;
}
.item-custom-grid {
min-height: min(42vh, 340px);
}
.ship-presets-grid {
grid-template-columns: 1fr;
}
.ship-preset-card {
min-height: 0;
}
.item-custom-filter-top,
.item-custom-search-wrap,
.item-custom-type-filters,
.vanilla-db-search-wrap {
flex-direction: column;
align-items: stretch;
}
.item-custom-filter-status {
text-align: left;
}
.item-custom-card {
grid-template-columns: 52px minmax(0, 1fr);
padding: 8px;
gap: 8px;
}
.vanilla-db-card-main,
.vanilla-db-card-details {
grid-column: 1 / -1;
}
.item-custom-card-media {
width: 48px !important;
height: 48px !important;
min-width: 48px;
min-height: 48px;
}
.item-custom-card-stats {
grid-column: 1 / -1;
}
.item-custom-card-stats-header {
justify-content: flex-start;
}
.item-custom-card-preview {
display: none;
}
.item-custom-stat-chip {
width: 100%;
justify-content: space-between;
padding: 6px 14px !important;
padding-left: 14px !important;
padding-right: 14px !important;
}
.item-custom-stat-name {
white-space: normal;
}
}
</style>
</head>
<body>
<?php if ($has_member_access || $has_vanilla_db_access): ?>
<div class="assets-div-menu">
<?php if ($has_member_access): ?>
<a href="#" class="md-trigger" data-modal="modal-ScanReference">Signatures de minage</a>
<a href="#" class="md-trigger" data-modal="modal-ShipPresets">Presets de vaisseaux</a>
<?php endif; ?>
<?php if ($has_vanilla_db_access): ?>
<a href="#" class="md-trigger" data-modal="modal-VanillaDb">Objets Vanilla DB</a>
<?php endif; ?>
<?php if ($has_member_access): ?>
<a href="#" class="md-trigger" data-modal="modal-ItemCustom">Objet(s) personnalisé(s)</a>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="connexion-div-menu <?php echo $is_authenticated ? 'is-authenticated' : 'md-trigger'; ?>" data-login-label="Connexion" <?php echo $is_authenticated ? '' : 'data-modal="modal-Login"'; ?> id="accountPanel">
<span id="accountLabel"><?php echo htmlspecialchars($is_authenticated ? ('Bonjour, ' . $session_cl_auth_user) : 'Connexion', ENT_QUOTES, 'UTF-8'); ?></span>
<span class="connexion-actions" id="accountActions" <?php echo $is_authenticated ? '' : 'hidden'; ?>>
<a id="adminLink" class="connexion-action-btn connexion-action-admin" href="admin.php" title="Zone d'administration" aria-label="Zone d'administration" <?php echo $has_member_access ? '' : 'hidden'; ?>>
<span class="connexion-action-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" role="img" focusable="false">
<path d="M10.33 2.04c1-.2 2.03-.2 3.03 0l.4 2.04c.45.12.89.3 1.29.54l1.78-1.07c.84.56 1.56 1.29 2.12 2.12l-1.07 1.78c.24.4.42.84.54 1.29l2.04.4c.2 1 .2 2.03 0 3.03l-2.04.4a5.98 5.98 0 0 1-.54 1.29l1.07 1.78a8.04 8.04 0 0 1-2.12 2.12l-1.78-1.07c-.4.24-.84.42-1.29.54l-.4 2.04c-1 .2-2.03.2-3.03 0l-.4-2.04a5.98 5.98 0 0 1-1.29-.54l-1.78 1.07a8.04 8.04 0 0 1-2.12-2.12l1.07-1.78a5.98 5.98 0 0 1-.54-1.29l-2.04-.4a7.64 7.64 0 0 1 0-3.03l2.04-.4c.12-.45.3-.89.54-1.29L3.91 5.72A8.04 8.04 0 0 1 6.03 3.6l1.78 1.07c.4-.24.84-.42 1.29-.54l.4-2.04ZM12 8.25A3.75 3.75 0 1 0 12 15.75 3.75 3.75 0 0 0 12 8.25Z" fill="currentColor"/>
</svg>
</span>
<span class="sr-only">Administration</span>
</a>
<a id="logoutLink" class="connexion-action-btn connexion-action-logout" href="logout.php" title="Déconnexion" aria-label="Déconnexion">
<span class="connexion-action-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" role="img" focusable="false">
<path d="M10 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h4v-2H6V5h4V3Zm5.59 4.59L14.17 9l2.58 2.59H9v2h7.75l-2.58 2.59 1.42 1.41L20.59 12l-5-4.41Z" fill="currentColor"/>
</svg>
</span>
<span class="sr-only">Déconnexion</span>
</a>
</span>
</div>
<div class="se-switch switch-center" role="group" aria-label="Choix entre FR et EN">
<a class="se-switch__btn" href="index.php" data-site="se1">🇫🇷</a>
<a class="se-switch__btn is-active" href="index-en.php" data-site="se2" aria-current="page">🇬🇧</a>
</div>
<div class="md-modal md-effect-1" id="modal-Login">
<div class="md-content">
<h3>
<img class="float-left" src="img/icon_bops.png" width="48" height="48" alt="" />
LOGIN INTERFACE
<a class="frame-icon-close md-close float-right" href="#"><img src="img/icon_close.png" width="48" height="48" alt="" /></a>
</h3>
<div>
<p class="txt-center">Enter your credentials to access the secure area.*</p>
<form class="js-login-form" method="post" action="login.php">
<div>
<p class="txt-center"><input class="connexion-champ" id="idr" type="text" name="cl_auth_user" placeholder="RID" required /></p>
<p class="txt-center"><input class="connexion-champ" id="pwr" type="password" name="cl_auth_pass" placeholder="MOT DE PASSE" required /></p>
</div>
<div>
<p class="txt-center"><input class="connexion-bouton" type="submit" value="S'authentifier" /></p>
<p class="txt-center login-status" id="loginStatus" aria-live="polite"></p>
</div>
</form>
<p class="txt-center txt-s10">* Access under high surveillance. By confirming your credentials, you agree to the R.E.A.C.T. Initiative security protocols. Any breach of protocol is subject to sanctions.</p>
</div>
</div>
</div>
<div class="md-modal md-effect-1" id="modal-About">
<div class="md-content">
<h3>
<img class="float-left" src="img/icon_bops.png" width="48" height="48" alt="" />
MORE INFORMATIONS
<a class="frame-icon-close md-close float-right" href="#"><img src="img/icon_close.png" width="48" height="48" alt="" /></a>
</h3>
<div>
<img class="float-left" src="img/fl_security.png" width="300" height="460" alt="" />
<p>La sécurité et la protection des intérêts impériaux sont des priorités absolues pour lagence.</p>
<p>En tant que garante de lordre dans les zones sous juridiction de lUEE, lagence BOPS s'engage à prévenir, neutraliser et répondre efficacement à toutes les formes de menaces, qu'elles soient internes ou externes aux territoires de l'Empire.</p>
<p>Cette mission repose sur une approche combinant vigilance, réactivité et expertise tactique, afin dassurer la stabilité, la sécurité des citoyens et le respect des lois impériales.</p>
<p>Chaque opération est conçue pour inspirer confiance aux populations locales tout en projetant une image de force et de discipline au sein de lunivers connu.</p>
<p class="ul-style">
— Surveillance active des secteurs critiques pour dissuader les menaces potentielles.</br>
— Protection rapprochée de convois, personnalités importantes ou cargaisons sensibles.</br>
— Neutralisation de vaisseaux hostiles ou suspects, avec récupération dactifs stratégiques.</br>
— Ciblage et capture ou élimination de fugitifs dangereux, conformément aux lois impériales.</br>
— Protection des civils en détresse et aide aux infrastructures endommagées.</br>
— Collecte et analyse dinformations sur des activités suspectes, incidents ou organisations hostiles.
</p>
</div>
</div>
</div>
<?php if ($has_member_access): ?>
<div class="md-modal md-effect-1 modal-ship-presets" id="modal-ShipPresets">
<div class="md-content ship-presets-dialog">
<h3>
<img class="float-left" src="img/icon_bops.png" width="48" height="48" alt="" />
PRESETS DE VAISSEAUX
<a class="frame-icon-close md-close float-right" href="#"><img src="img/icon_close.png" width="48" height="48" alt="" /></a>
</h3>
<div>
<?php if ($ship_preset_error !== null): ?>
<p class="ship-presets-error"><?php echo htmlspecialchars($ship_preset_error, ENT_QUOTES, 'UTF-8'); ?></p>
<?php elseif ($ship_preset_rows === []): ?>
<p class="ship-presets-empty">Aucun preset de vaisseau n'est enregistré pour le moment.</p>
<?php else: ?>
<div class="ship-presets-filter-bar">
<div class="ship-presets-filter-group">
<label class="ship-presets-filter-label" for="shipPresetManufacturerFilter">Filtrer par manufacture</label>
<select id="shipPresetManufacturerFilter" class="ship-presets-filter-select">
<option value="">Toutes les manufactures</option>
<?php foreach ($ship_preset_manufacturers as $ship_preset_manufacturer): ?>
<option value="<?php echo htmlspecialchars($ship_preset_manufacturer, ENT_QUOTES, 'UTF-8'); ?>"><?php echo htmlspecialchars($ship_preset_manufacturer, ENT_QUOTES, 'UTF-8'); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="ship-presets-filter-group">
<label class="ship-presets-filter-label" for="shipPresetShipFilter">Filtrer par vaisseau</label>
<select id="shipPresetShipFilter" class="ship-presets-filter-select" disabled>
<option value="">Choisis d'abord une manufacture</option>
</select>
</div>
<p class="ship-presets-filter-status" id="shipPresetFilterStatus"></p>
</div>
<div class="ship-presets-grid" id="shipPresetsGrid">
<?php foreach ($ship_preset_rows as $ship_preset_row): ?>
<article class="ship-preset-card" data-manufacturer="<?php echo htmlspecialchars($ship_preset_row['manufacturer'], ENT_QUOTES, 'UTF-8'); ?>" data-ship-name="<?php echo htmlspecialchars($ship_preset_row['name'], ENT_QUOTES, 'UTF-8'); ?>">
<div class="ship-preset-card-header">
<p class="ship-preset-card-manufacturer"><?php echo htmlspecialchars($ship_preset_row['manufacturer'], ENT_QUOTES, 'UTF-8'); ?></p>
<h4 class="ship-preset-card-name"><?php echo htmlspecialchars($ship_preset_row['name'], ENT_QUOTES, 'UTF-8'); ?></h4>
<p class="ship-preset-card-meta">Créé par <?php echo htmlspecialchars($ship_preset_row['creator'], ENT_QUOTES, 'UTF-8'); ?></p>
</div>
<p class="ship-preset-card-description"><?php echo htmlspecialchars($ship_preset_row['description'], ENT_QUOTES, 'UTF-8'); ?></p>
<a href="<?php echo htmlspecialchars($ship_preset_row['link'], ENT_QUOTES, 'UTF-8'); ?>" target="_blank" rel="noopener noreferrer" class="ship-preset-card-link">Voir la configuration</a>
</article>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php endif; ?>
<?php if ($has_member_access): ?>
<div class="md-modal md-effect-1 modal-item-custom" id="modal-ItemCustom">
<div class="md-content item-custom-dialog">
<h3>
<img class="float-left" src="img/icon_bops.png" width="48" height="48" alt="" />
OBJET(S) PERSONNALISÉ(S)
<a class="frame-icon-close md-close float-right" href="#"><img src="img/icon_close.png" width="48" height="48" alt="" /></a>
</h3>
<div>
<?php if ($item_custom_public_error !== null): ?>
<p class="item-custom-error"><?php echo htmlspecialchars($item_custom_public_error, ENT_QUOTES, 'UTF-8'); ?></p>
<?php elseif ($item_custom_public_rows === []): ?>
<p class="item-custom-empty">Aucun objet Item Custom n'est disponible pour le moment.</p>
<?php else: ?>
<div class="item-custom-filter-bar">
<div class="item-custom-filter-top">
<div class="item-custom-search-wrap">
<input type="search" id="itemCustomSearch" class="item-custom-search-input" placeholder="Rechercher un objet, un créateur ou une statistique..." autocomplete="off">
<button type="button" id="itemCustomSearchReset" class="item-custom-reset-btn">Réinitialiser</button>
</div>
<p class="item-custom-filter-status" id="itemCustomSearchStatus"></p>
</div>
<?php if ($item_custom_public_types !== []): ?>
<div class="item-custom-type-filters" id="itemCustomTypeFilters">
<span class="item-custom-type-label">Filtrer par type</span>
<div class="item-custom-type-options">
<?php foreach ($item_custom_public_types as $item_custom_public_type): ?>
<label class="item-custom-type-chip">
<input type="checkbox" class="item-custom-type-checkbox" value="<?php echo htmlspecialchars($item_custom_public_type, ENT_QUOTES, 'UTF-8'); ?>">
<span><?php echo htmlspecialchars($item_custom_public_type, ENT_QUOTES, 'UTF-8'); ?></span>
</label>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
<div class="item-custom-grid" id="itemCustomGrid">
<?php foreach ($item_custom_public_rows as $item_custom_public_row): ?>
<?php $item_custom_stat_count = count($item_custom_public_row['stats']); ?>
<article class="item-custom-card" data-item-search="<?php echo htmlspecialchars($item_custom_public_row['search'], ENT_QUOTES, 'UTF-8'); ?>" data-item-type="<?php echo htmlspecialchars($item_custom_public_row['type'] !== '' ? $item_custom_public_row['type'] : 'Type inconnu', ENT_QUOTES, 'UTF-8'); ?>">
<div class="item-custom-card-media">
<img src="https://cstone.space/uifimages/<?php echo htmlspecialchars($item_custom_public_row['uuid'], ENT_QUOTES, 'UTF-8'); ?>.png" class="item-custom-card-thumb" alt="Aperçu de <?php echo htmlspecialchars($item_custom_public_row['name'], ENT_QUOTES, 'UTF-8'); ?>" loading="lazy" onerror="this.closest('.item-custom-card-media').classList.add('is-image-missing'); this.remove();">
<div class="item-custom-card-preview" aria-hidden="true">
<img src="https://cstone.space/uifimages/<?php echo htmlspecialchars($item_custom_public_row['uuid'], ENT_QUOTES, 'UTF-8'); ?>.png" alt="" onerror="this.closest('.item-custom-card-media').classList.add('is-image-missing'); this.closest('.item-custom-card-preview').remove();">
</div>
</div>
<div class="item-custom-card-main">
<p class="item-custom-card-type"><?php echo htmlspecialchars(trim(($item_custom_public_row['type'] !== '' ? $item_custom_public_row['type'] : 'Type inconnu') . ($item_custom_public_row['subtype'] !== '' ? ' / ' . $item_custom_public_row['subtype'] : '')), ENT_QUOTES, 'UTF-8'); ?></p>
<h4 class="item-custom-card-name"><?php echo htmlspecialchars($item_custom_public_row['name'], ENT_QUOTES, 'UTF-8'); ?></h4>
<p class="item-custom-card-meta">Créé par <?php echo htmlspecialchars($item_custom_public_row['creator'], ENT_QUOTES, 'UTF-8'); ?></p>
</div>
<div class="item-custom-card-stats">
<div class="item-custom-card-stats-header">
<span class="item-custom-card-count"><?php echo $item_custom_stat_count; ?> stat<?php echo $item_custom_stat_count > 1 ? 's' : ''; ?></span>
</div>
<?php if ($item_custom_stat_count === 0): ?>
<p class="item-custom-card-empty">Aucune statistique personnalisée n'a encore été enregistrée pour cet objet.</p>
<?php else: ?>
<ul class="item-custom-stats-list">
<?php foreach ($item_custom_public_row['stats'] as $item_custom_stat): ?>
<?php
$item_custom_stat_preview = trim((string) ($item_custom_stat['preview'] ?? ''));
$item_custom_stat_sign_class = 'sign-neutral';
if ($item_custom_stat_preview !== '') {
if (strpos($item_custom_stat_preview, '+') === 0) {
$item_custom_stat_sign_class = 'sign-plus';
} elseif (strpos($item_custom_stat_preview, '-') === 0) {
$item_custom_stat_sign_class = 'sign-minus';
}
}
?>
<li class="item-custom-stat-chip <?php echo $item_custom_stat_sign_class; ?>">
<span class="item-custom-stat-name"><?php echo htmlspecialchars($item_custom_stat['name'], ENT_QUOTES, 'UTF-8'); ?></span>
<span class="item-custom-stat-value"><?php echo htmlspecialchars($item_custom_stat['preview'], ENT_QUOTES, 'UTF-8'); ?></span>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
</article>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php endif; ?>
<?php if ($has_vanilla_db_access): ?>
<div class="md-modal md-effect-1 modal-vanilla-db" id="modal-VanillaDb">
<div class="md-content vanilla-db-dialog">
<h3>
<img class="float-left" src="img/icon_bops.png" width="48" height="48" alt="" />
BASE DE DONNEES VANILLA
<a class="frame-icon-close md-close float-right" href="#"><img src="img/icon_close.png" width="48" height="48" alt="" /></a>
</h3>
<div>
<?php if ($vanilla_db_error !== null): ?>
<p class="vanilla-db-error"><?php echo htmlspecialchars($vanilla_db_error, ENT_QUOTES, 'UTF-8'); ?></p>
<?php else: ?>
<form class="vanilla-db-filter-bar" method="get" action="index.php">
<input type="hidden" name="open_modal" value="vanilla-db" />
<input type="hidden" name="vanilla_page" value="1" />
<div class="vanilla-db-search-wrap">
<input type="search" id="vanillaDbSearch" name="vanilla_search" class="vanilla-db-search-input" placeholder="Rechercher un objet, un type, un sous-type, une rareté ou un UUID..." autocomplete="off" value="<?php echo htmlspecialchars($vanilla_db_search, ENT_QUOTES, 'UTF-8'); ?>" />
<button type="submit" class="vanilla-db-search-btn">Rechercher</button>
<a href="<?php echo htmlspecialchars($vanilla_db_reset_url, ENT_QUOTES, 'UTF-8'); ?>" class="vanilla-db-reset-btn">Réinitialiser</a>
</div>
<p class="vanilla-db-status">
<?php if ($vanilla_db_total_rows > 0): ?>
Affichage de <strong><?php echo $vanilla_db_result_start; ?></strong> à <strong><?php echo $vanilla_db_result_end; ?></strong> sur <strong><?php echo $vanilla_db_total_rows; ?></strong> objet(s)<?php echo $vanilla_db_search !== '' ? ' pour « ' . htmlspecialchars($vanilla_db_search, ENT_QUOTES, 'UTF-8') . ' »' : ''; ?>.
<?php else: ?>
Aucun objet trouvé<?php echo $vanilla_db_search !== '' ? ' pour « ' . htmlspecialchars($vanilla_db_search, ENT_QUOTES, 'UTF-8') . ' »' : ''; ?>.
<?php endif; ?>
</p>
</form>
<?php if ($vanilla_db_rows === []): ?>
<p class="vanilla-db-empty">Aucun objet n'est actuellement enregistré dans la BASE D'OBJETS.</p>
<?php else: ?>
<div class="vanilla-db-grid" id="vanillaDbGrid">
<?php foreach ($vanilla_db_rows as $vanilla_db_row): ?>
<article class="item-custom-card vanilla-db-card">
<div class="item-custom-card-media<?php echo $vanilla_db_row['image_url'] === null ? ' is-image-missing' : ''; ?>"<?php if ($vanilla_db_row['image_url'] !== null): ?> data-preview-src="<?php echo htmlspecialchars($vanilla_db_row['image_url'], ENT_QUOTES, 'UTF-8'); ?>" data-preview-label="<?php echo htmlspecialchars($vanilla_db_row['name'], ENT_QUOTES, 'UTF-8'); ?>"<?php endif; ?>>
<?php if ($vanilla_db_row['image_url'] !== null): ?>
<img src="<?php echo htmlspecialchars($vanilla_db_row['image_url'], ENT_QUOTES, 'UTF-8'); ?>" class="item-custom-card-thumb" alt="Aperçu de <?php echo htmlspecialchars($vanilla_db_row['name'], ENT_QUOTES, 'UTF-8'); ?>" loading="lazy" onerror="this.closest('.item-custom-card-media').classList.add('is-image-missing'); this.remove();">
<?php endif; ?>
</div>
<div class="item-custom-card-main vanilla-db-card-main">
<p class="vanilla-db-card-topline">
<span class="vanilla-db-card-type-badge"><?php echo htmlspecialchars($vanilla_db_row['type'], ENT_QUOTES, 'UTF-8'); ?></span>
<?php if ($vanilla_db_row['subtype'] !== '—'): ?><span class="vanilla-db-card-subtype"><?php echo htmlspecialchars($vanilla_db_row['subtype'], ENT_QUOTES, 'UTF-8'); ?></span><?php endif; ?>
</p>
<h4 class="item-custom-card-name vanilla-db-item-name <?php echo htmlspecialchars($vanilla_db_row['rarity_class'], ENT_QUOTES, 'UTF-8'); ?>" title="<?php echo htmlspecialchars($vanilla_db_row['rarity_label'], ENT_QUOTES, 'UTF-8'); ?>"><?php echo htmlspecialchars($vanilla_db_row['name'], ENT_QUOTES, 'UTF-8'); ?></h4>
<p class="vanilla-db-card-meta"><?php if ($vanilla_db_row['uuid'] !== '—'): ?><span class="vanilla-db-card-uuid">UUID : <?php echo htmlspecialchars($vanilla_db_row['uuid'], ENT_QUOTES, 'UTF-8'); ?></span><?php else: ?><span class="vanilla-db-muted vanilla-db-card-uuid">UUID : —</span><?php endif; ?></p>
</div>
<div class="vanilla-db-card-details">
<div class="vanilla-db-card-description"><?php echo $vanilla_db_row['description_html']; ?></div>
</div>
</article>
<?php endforeach; ?>
</div>
<?php if ($vanilla_db_total_pages > 1): ?>
<?php
$vanilla_db_page_window_start = max(1, $vanilla_db_current_page - 2);
$vanilla_db_page_window_end = min($vanilla_db_total_pages, $vanilla_db_current_page + 2);
if (($vanilla_db_page_window_end - $vanilla_db_page_window_start) < 4) {
if ($vanilla_db_page_window_start === 1) {
$vanilla_db_page_window_end = min($vanilla_db_total_pages, 5);
} elseif ($vanilla_db_page_window_end === $vanilla_db_total_pages) {
$vanilla_db_page_window_start = max(1, $vanilla_db_total_pages - 4);
}
}
?>
<nav class="vanilla-db-pagination" aria-label="Pagination des objets vanilla">
<p class="vanilla-db-pagination-summary">Page <strong><?php echo $vanilla_db_current_page; ?></strong> sur <strong><?php echo $vanilla_db_total_pages; ?></strong>.</p>
<div class="vanilla-db-pagination-pages">
<?php if ($vanilla_db_current_page > 1): ?>
<?php $vanilla_db_page_query = $vanilla_db_base_query; $vanilla_db_page_query['vanilla_page'] = 1; ?>
<a class="vanilla-db-page-link" href="index.php?<?php echo htmlspecialchars(http_build_query($vanilla_db_page_query), ENT_QUOTES, 'UTF-8'); ?>">«</a>
<?php $vanilla_db_page_query = $vanilla_db_base_query; $vanilla_db_page_query['vanilla_page'] = $vanilla_db_current_page - 1; ?>
<a class="vanilla-db-page-link" href="index.php?<?php echo htmlspecialchars(http_build_query($vanilla_db_page_query), ENT_QUOTES, 'UTF-8'); ?>"></a>
<?php endif; ?>
<?php if ($vanilla_db_page_window_start > 1): ?>
<?php $vanilla_db_page_query = $vanilla_db_base_query; $vanilla_db_page_query['vanilla_page'] = 1; ?>
<a class="vanilla-db-page-link" href="index.php?<?php echo htmlspecialchars(http_build_query($vanilla_db_page_query), ENT_QUOTES, 'UTF-8'); ?>">1</a>
<?php if ($vanilla_db_page_window_start > 2): ?>
<span class="vanilla-db-page-ellipsis">…</span>
<?php endif; ?>
<?php endif; ?>
<?php for ($vanilla_db_page_number = $vanilla_db_page_window_start; $vanilla_db_page_number <= $vanilla_db_page_window_end; $vanilla_db_page_number++): ?>
<?php if ($vanilla_db_page_number === $vanilla_db_current_page): ?>
<span class="vanilla-db-page-current"><?php echo $vanilla_db_page_number; ?></span>
<?php else: ?>
<?php $vanilla_db_page_query = $vanilla_db_base_query; $vanilla_db_page_query['vanilla_page'] = $vanilla_db_page_number; ?>
<a class="vanilla-db-page-link" href="index.php?<?php echo htmlspecialchars(http_build_query($vanilla_db_page_query), ENT_QUOTES, 'UTF-8'); ?>"><?php echo $vanilla_db_page_number; ?></a>
<?php endif; ?>
<?php endfor; ?>
<?php if ($vanilla_db_page_window_end < $vanilla_db_total_pages): ?>
<?php if ($vanilla_db_page_window_end < ($vanilla_db_total_pages - 1)): ?>
<span class="vanilla-db-page-ellipsis">…</span>
<?php endif; ?>
<?php $vanilla_db_page_query = $vanilla_db_base_query; $vanilla_db_page_query['vanilla_page'] = $vanilla_db_total_pages; ?>
<a class="vanilla-db-page-link" href="index.php?<?php echo htmlspecialchars(http_build_query($vanilla_db_page_query), ENT_QUOTES, 'UTF-8'); ?>"><?php echo $vanilla_db_total_pages; ?></a>
<?php endif; ?>
<?php if ($vanilla_db_current_page < $vanilla_db_total_pages): ?>
<?php $vanilla_db_page_query = $vanilla_db_base_query; $vanilla_db_page_query['vanilla_page'] = $vanilla_db_current_page + 1; ?>
<a class="vanilla-db-page-link" href="index.php?<?php echo htmlspecialchars(http_build_query($vanilla_db_page_query), ENT_QUOTES, 'UTF-8'); ?>"></a>
<?php $vanilla_db_page_query = $vanilla_db_base_query; $vanilla_db_page_query['vanilla_page'] = $vanilla_db_total_pages; ?>
<a class="vanilla-db-page-link" href="index.php?<?php echo htmlspecialchars(http_build_query($vanilla_db_page_query), ENT_QUOTES, 'UTF-8'); ?>">»</a>
<?php endif; ?>
</div>
</nav>
<?php endif; ?>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
</div>
<?php endif; ?>
<?php if ($has_member_access): ?>
<div class="md-modal md-effect-1 modal-scan-reference" id="modal-ScanReference">
<div class="md-content scan-reference-dialog">
<h3>
<img class="float-left" src="img/icon_bops.png" width="48" height="48" alt="" />
TABLEAU DES SIGNATURES DE SCAN
<a class="frame-icon-close md-close float-right" href="#"><img src="img/icon_close.png" width="48" height="48" alt="" /></a>
</h3>
<div>
<?php if ($scan_reference_error !== null): ?>
<p class="scan-reference-error"><?php echo htmlspecialchars($scan_reference_error, ENT_QUOTES, 'UTF-8'); ?></p>
<?php elseif ($scan_reference_rows === []): ?>
<p class="scan-reference-empty">Aucune donnée de scan n'est configurée pour le moment.</p>
<?php else: ?>
<div class="scan-reference-filter-bar">
<div class="scan-reference-filter-actions">
<button type="button" class="scan-reference-filter-btn" id="scanReferenceIsolateBtn">Isoler</button>
<button type="button" class="scan-reference-filter-btn" id="scanReferenceClearBtn">Supprimer le filtrage</button>
</div>
<p class="scan-reference-filter-status" id="scanReferenceFilterStatus">Clique sur les lignes à conserver puis sur « Isoler ».</p>
</div>
<div class="scan-reference-table-wrapper">
<table class="scan-reference-table" id="scanReferenceTable">
<thead>
<tr>
<th>Ressource</th>
<th>Récupérations</th>
<?php for ($occurrence = 1; $occurrence <= $scan_reference_max_occurrence; $occurrence++): ?>
<th>x <?php echo $occurrence; ?></th>
<?php endfor; ?>
</tr>
</thead>
<tbody>
<?php foreach ($scan_reference_rows as $scan_reference_row): ?>
<tr class="scan-reference-row <?php echo htmlspecialchars($scan_reference_row['rarity_class'], ENT_QUOTES, 'UTF-8'); ?>" data-resource-id="<?php echo htmlspecialchars($scan_reference_row['id'], ENT_QUOTES, 'UTF-8'); ?>" data-resource-name="<?php echo htmlspecialchars($scan_reference_row['name'], ENT_QUOTES, 'UTF-8'); ?>">
<td class="scan-reference-name">
<span><?php echo htmlspecialchars($scan_reference_row['name'], ENT_QUOTES, 'UTF-8'); ?></span>
</td>
<td class="scan-reference-recovery">
<?php if ($scan_reference_row['recovery_modes'] !== []): ?>
<?php foreach ($scan_reference_row['recovery_modes'] as $recovery_mode): ?>
<span class="scan-reference-pill"><?php echo htmlspecialchars($recovery_mode, ENT_QUOTES, 'UTF-8'); ?></span>
<?php endforeach; ?>
<?php else: ?>
<span class="scan-reference-muted">—</span>
<?php endif; ?>
</td>
<?php for ($occurrence = 1; $occurrence <= $scan_reference_max_occurrence; $occurrence++): ?>
<td class="scan-reference-value">
<?php echo array_key_exists($occurrence - 1, $scan_reference_row['scan_steps']) ? number_format((int) $scan_reference_row['scan_steps'][$occurrence - 1], 0, ',', ' ') : '<span class="scan-reference-muted">—</span>'; ?>
</td>
<?php endfor; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php endif; ?>
<div class="center-page-bops"></div>
<div class="center-div-menu">
<div class="padding50">
<p class="txt-center txt-bold txt-s40 small-caps padding25 txt-or">Rapid Emergency & Action Combat Team</p>
<!-- <p class="txt-center txt-bold txt-italic txt-s20 padding15 txt-or"><span class="padding5">SÉCURITÉ</span> —— <span class="padding5">INTELLIGENCE</span> —— <span class="padding5">SAUVETAGE</span></p> -->
<p class="txt-justify padding5">Founded to combat extreme criminality across the Verse. R.E.A.C.T. is an independent operational label, uniting dedicated pilots from all horizons to neutralize threats where the law is failing.</p>
<p class="txt-center txt-bold txt-s22 padding25 txt-or">—————— R.E.A.C.T. is not an organization, but a tactical standard ——————</p>
<p class="txt-justify padding5">It is a cross-org initiative designed for players who share the same vision: protecting citizens and NPCs from hostile entities. Wearing the R.E.A.C.T. badge doesnt mean leaving your organization; it means joining a rapid response network.</p>
</div>
<div class="center-div-menu-flex txt-bold">
<div class="menu-item md-trigger" data-modal="modal-About"><a href="#">> MORE INFORMATIONS <</a></div>
</div>
</div>
<div class="black-filter"></div>
<!-- Vidéo de fond -->
<div class="video-container" aria-hidden="true">
<iframe
id="react-video"
src="https://www.youtube-nocookie.com/embed/tFDqWOqm1G8?controls=0&autoplay=1&mute=1&playsinline=1&loop=1&playlist=tFDqWOqm1G8&cc_load_policy=0&iv_load_policy=3&modestbranding=1&rel=0&enablejsapi=1"
title="Présentation R.E.A.C.T — YouTube"
loading="lazy"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; fullscreen"
allowfullscreen
referrerpolicy="strict-origin-when-cross-origin">
</iframe>
</div>
<div class="md-overlay"></div><!-- the overlay element -->
<!-- classie.js by @desandro: https://github.com/desandro/classie -->
<script src="js/classie.js"></script>
<script src="js/modalEffects.js"></script>
<script src="js/auth.js"></script>
<!-- for the blur effect -->
<!-- by @derSchepp https://github.com/Schepp/CSS-Filters-Polyfill -->
<script>
// this is important for IEs
var polyfilter_scriptpath = '/js/';
</script>
<script src="js/cssParser.js"></script>
<script src="js/css-filters-polyfill.js"></script>
<script>
(function initScanReferenceFilter() {
const storageKey = 'scanReferenceIsolatedResources.v1';
const table = document.getElementById('scanReferenceTable');
const status = document.getElementById('scanReferenceFilterStatus');
const isolateButton = document.getElementById('scanReferenceIsolateBtn');
const clearButton = document.getElementById('scanReferenceClearBtn');
if (!table || !status || !isolateButton || !clearButton) {
return;
}
const rows = Array.from(table.querySelectorAll('.scan-reference-row'));
const selectedIds = new Set();
let activeFilterIds = [];
function saveFilter(ids) {
if (!Array.isArray(ids) || ids.length === 0) {
window.localStorage.removeItem(storageKey);
return;
}
window.localStorage.setItem(storageKey, JSON.stringify(ids));
}
function loadFilter() {
try {
const raw = window.localStorage.getItem(storageKey);
if (!raw) {
return [];
}
const parsed = JSON.parse(raw);
return Array.isArray(parsed) ? parsed.map(String).filter(Boolean) : [];
} catch (error) {
window.localStorage.removeItem(storageKey);
return [];
}
}
function getVisibleCount() {
return rows.filter((row) => !row.classList.contains('is-hidden-by-filter')).length;
}
function refreshStatus() {
const selectedCount = selectedIds.size;
const visibleCount = getVisibleCount();
const totalCount = rows.length;
const filterIsActive = activeFilterIds.length > 0;
isolateButton.classList.toggle('is-disabled', selectedCount === 0);
isolateButton.disabled = selectedCount === 0;
clearButton.classList.toggle('is-disabled', !filterIsActive);
clearButton.disabled = !filterIsActive;
if (filterIsActive) {
status.textContent = 'Filtre actif : ' + visibleCount + ' ressource(s) affichée(s) sur ' + totalCount + '.';
return;
}
if (selectedCount > 0) {
status.textContent = selectedCount + ' ressource(s) sélectionnée(s). Clique sur « Isoler » pour nafficher que celles-ci.';
return;
}
status.textContent = 'Clique sur les lignes à conserver puis sur « Isoler ».';
}
function applyFilter(ids) {
activeFilterIds = Array.isArray(ids) ? ids.map(String).filter(Boolean) : [];
const filterSet = new Set(activeFilterIds);
rows.forEach((row) => {
const rowId = String(row.dataset.resourceId || '');
const shouldHide = filterSet.size > 0 && !filterSet.has(rowId);
row.classList.toggle('is-hidden-by-filter', shouldHide);
});
refreshStatus();
}
function clearSelection() {
selectedIds.clear();
rows.forEach((row) => row.classList.remove('is-selected'));
refreshStatus();
}
rows.forEach((row) => {
row.addEventListener('click', function () {
const rowId = String(row.dataset.resourceId || '');
if (!rowId) {
return;
}
if (selectedIds.has(rowId)) {
selectedIds.delete(rowId);
row.classList.remove('is-selected');
} else {
selectedIds.add(rowId);
row.classList.add('is-selected');
}
refreshStatus();
});
});
isolateButton.addEventListener('click', function () {
if (selectedIds.size === 0) {
refreshStatus();
return;
}
const ids = Array.from(selectedIds);
saveFilter(ids);
applyFilter(ids);
clearSelection();
});
clearButton.addEventListener('click', function () {
saveFilter([]);
applyFilter([]);
});
applyFilter(loadFilter());
})();
</script>
<script>
(function initVanillaDbModalState() {
const shouldOpen = <?php echo $should_open_vanilla_db_modal ? 'true' : 'false'; ?>;
const modal = document.getElementById('modal-VanillaDb');
const searchInput = document.getElementById('vanillaDbSearch');
if (!shouldOpen || !modal || typeof classie === 'undefined') {
return;
}
classie.add(modal, 'md-show');
if (searchInput) {
window.requestAnimationFrame(function () {
searchInput.focus();
searchInput.setSelectionRange(searchInput.value.length, searchInput.value.length);
});
}
})();
</script>
<script>
(function initVanillaDbImagePreview() {
const modal = document.getElementById('modal-VanillaDb');
if (!modal) {
return;
}
const mediaNodes = Array.from(modal.querySelectorAll('.item-custom-card-media[data-preview-src]'));
if (mediaNodes.length === 0) {
return;
}
let preview = document.getElementById('vanillaDbImagePreview');
if (!preview) {
preview = document.createElement('div');
preview.id = 'vanillaDbImagePreview';
preview.className = 'vanilla-db-hover-preview';
preview.setAttribute('aria-hidden', 'true');
preview.innerHTML = '<img alt="">';
document.body.appendChild(preview);
}
const previewImage = preview.querySelector('img');
let activeNode = null;
function movePreview(event) {
if (!activeNode || !preview.classList.contains('is-visible')) {
return;
}
const offset = 22;
const bounds = preview.getBoundingClientRect();
let left = event.clientX + offset;
let top = event.clientY + offset;
if ((left + bounds.width) > (window.innerWidth - 16)) {
left = event.clientX - bounds.width - offset;
}
if ((top + bounds.height) > (window.innerHeight - 16)) {
top = event.clientY - bounds.height - offset;
}
left = Math.max(8, left);
top = Math.max(8, top);
preview.style.transform = 'translate(' + left + 'px, ' + top + 'px)';
}
function hidePreview() {
activeNode = null;
preview.classList.remove('is-visible');
preview.style.transform = 'translate(-9999px, -9999px)';
previewImage.removeAttribute('src');
previewImage.alt = '';
}
function showPreview(node, event) {
const src = node.dataset.previewSrc || '';
if (src === '') {
return;
}
activeNode = node;
previewImage.src = src;
previewImage.alt = node.dataset.previewLabel || '';
preview.classList.add('is-visible');
movePreview(event);
}
previewImage.addEventListener('error', hidePreview);
window.addEventListener('scroll', hidePreview, { passive: true });
window.addEventListener('resize', hidePreview);
mediaNodes.forEach(function (node) {
node.addEventListener('mouseenter', function (event) {
showPreview(node, event);
});
node.addEventListener('mousemove', movePreview);
node.addEventListener('mouseleave', hidePreview);
node.addEventListener('focusin', function () {
const rect = node.getBoundingClientRect();
showPreview(node, {
clientX: rect.right,
clientY: rect.top
});
});
node.addEventListener('focusout', hidePreview);
});
})();
</script>
<script>
(function initItemCustomSearch() {
const searchInput = document.getElementById('itemCustomSearch');
const resetButton = document.getElementById('itemCustomSearchReset');
const status = document.getElementById('itemCustomSearchStatus');
const grid = document.getElementById('itemCustomGrid');
const typeCheckboxes = Array.from(document.querySelectorAll('.item-custom-type-checkbox'));
if (!searchInput || !resetButton || !status || !grid) {
return;
}
const cards = Array.from(grid.querySelectorAll('.item-custom-card'));
function normalizeValue(value) {
return String(value || '')
.toLowerCase()
.normalize('NFD')
.replace(/[̀-ͯ]/g, '')
.trim();
}
function getSelectedTypes() {
return typeCheckboxes
.filter((checkbox) => checkbox.checked)
.map((checkbox) => ({
label: checkbox.value,
value: normalizeValue(checkbox.value)
}));
}
function applyFilter() {
const query = normalizeValue(searchInput.value);
const selectedTypes = getSelectedTypes();
const selectedTypeSet = new Set(selectedTypes.map((type) => type.value));
let visibleCount = 0;
cards.forEach((card) => {
const haystack = normalizeValue(card.dataset.itemSearch || '');
const itemType = normalizeValue(card.dataset.itemType || '');
const matchesQuery = query === '' || haystack.includes(query);
const matchesType = selectedTypeSet.size === 0 || selectedTypeSet.has(itemType);
const isVisible = matchesQuery && matchesType;
card.classList.toggle('is-hidden-by-search', !matchesQuery);
card.classList.toggle('is-hidden-by-type', matchesQuery && !matchesType);
if (isVisible) {
visibleCount += 1;
}
});
if (query === '' && selectedTypeSet.size === 0) {
status.textContent = visibleCount + ' objet(s) affiché(s) sur ' + cards.length + '.';
return;
}
const activeFilters = [];
if (query !== '') {
activeFilters.push('la recherche « ' + searchInput.value.trim() + ' »');
}
if (selectedTypes.length > 0) {
activeFilters.push((selectedTypes.length > 1 ? 'les types ' : 'le type ') + selectedTypes.map((type) => '« ' + type.label + ' »').join(', '));
}
status.textContent = visibleCount + ' objet(s) correspondent à ' + activeFilters.join(' + ') + '.';
}
searchInput.addEventListener('input', applyFilter);
typeCheckboxes.forEach((checkbox) => {
checkbox.addEventListener('change', applyFilter);
});
resetButton.addEventListener('click', function () {
searchInput.value = '';
typeCheckboxes.forEach((checkbox) => {
checkbox.checked = false;
});
applyFilter();
searchInput.focus();
});
applyFilter();
})();
</script>
<script>
(function initShipPresetFilter() {
const manufacturerSelect = document.getElementById('shipPresetManufacturerFilter');
const shipSelect = document.getElementById('shipPresetShipFilter');
const status = document.getElementById('shipPresetFilterStatus');
const grid = document.getElementById('shipPresetsGrid');
if (!manufacturerSelect || !shipSelect || !status || !grid) {
return;
}
const cards = Array.from(grid.querySelectorAll('.ship-preset-card'));
const manufacturerToShips = new Map();
cards.forEach((card) => {
const manufacturer = String(card.dataset.manufacturer || '').trim();
const shipName = String(card.dataset.shipName || '').trim();
if (!manufacturerToShips.has(manufacturer)) {
manufacturerToShips.set(manufacturer, new Set());
}
if (shipName !== '') {
manufacturerToShips.get(manufacturer).add(shipName);
}
});
function setShipOptions(manufacturer) {
const previousValue = shipSelect.value;
shipSelect.innerHTML = '';
if (manufacturer === '') {
shipSelect.disabled = true;
shipSelect.append(new Option("Choisis d'abord une manufacture", ''));
shipSelect.value = '';
return;
}
shipSelect.disabled = false;
shipSelect.append(new Option('Tous les vaisseaux de cette manufacture', ''));
const ships = Array.from(manufacturerToShips.get(manufacturer) || []);
ships.sort((a, b) => a.localeCompare(b, 'fr', { sensitivity: 'base' }));
ships.forEach((shipName) => {
shipSelect.append(new Option(shipName, shipName));
});
shipSelect.value = ships.includes(previousValue) ? previousValue : '';
}
function refreshStatus() {
const visibleCards = cards.filter((card) => !card.classList.contains('is-hidden-by-ship-filter'));
const visibleShips = new Set(
visibleCards
.map((card) => String(card.dataset.shipName || '').trim())
.filter(Boolean)
);
const selectedManufacturer = manufacturerSelect.value;
const selectedShip = shipSelect.value;
if (selectedManufacturer !== '' && selectedShip !== '') {
status.textContent = visibleCards.length + ' preset(s) affiché(s) pour ' + selectedShip + ' chez ' + selectedManufacturer + '.';
return;
}
if (selectedManufacturer !== '') {
status.textContent = visibleCards.length + ' preset(s) affiché(s) pour ' + visibleShips.size + ' vaisseau(x) chez ' + selectedManufacturer + '.';
return;
}
status.textContent = visibleCards.length + ' preset(s) affiché(s) sur ' + cards.length + ' pour ' + visibleShips.size + ' vaisseau(x).';
}
function applyFilter() {
const selectedManufacturer = manufacturerSelect.value;
const selectedShip = shipSelect.value;
cards.forEach((card) => {
const manufacturer = String(card.dataset.manufacturer || '').trim();
const shipName = String(card.dataset.shipName || '').trim();
const hideByManufacturer = selectedManufacturer !== '' && manufacturer !== selectedManufacturer;
const hideByShip = selectedShip !== '' && shipName !== selectedShip;
card.classList.toggle('is-hidden-by-ship-filter', hideByManufacturer || hideByShip);
});
refreshStatus();
}
manufacturerSelect.addEventListener('change', function () {
setShipOptions(manufacturerSelect.value);
applyFilter();
});
shipSelect.addEventListener('change', applyFilter);
setShipOptions(manufacturerSelect.value);
applyFilter();
})();
</script>
<script>
// On ajoute dynamiquement l'origine à l'URL de l'iframe (recommandé par l'API YouTube)
(function attachOrigin(){
const iframe = document.getElementById('react-video');
const url = new URL(iframe.src);
if (!url.searchParams.has('origin')) {
url.searchParams.set('origin', window.location.origin);
iframe.src = url.toString();
}
})();
// Utilitaires pour parler au lecteur via postMessage (API YouTube)
function ytCommand(func, args = []) {
const iframe = document.getElementById('react-video');
if (!iframe || !iframe.contentWindow) return;
iframe.contentWindow.postMessage(JSON.stringify({ event: 'command', func, args }), '*');
}
// Au premier geste utilisateur : on remet le son et on met le volume à 50 %
function enableSoundOnce() {
ytCommand('unMute');
ytCommand('setVolume', [50]);
// Masquer le bouton
const btn = document.getElementById('unmute-btn');
if (btn) btn.style.display = 'none';
// Retirer les écouteurs pour ne pas répéter laction
window.removeEventListener('pointerdown', enableSoundOnce);
window.removeEventListener('keydown', enableSoundOnce);
window.removeEventListener('touchstart', enableSoundOnce, { passive: true });
}
// Bouton dédié
// document.getElementById('unmute-btn').addEventListener('click', enableSoundOnce);
// …ou nimporte quel premier geste sur la page
//window.addEventListener('pointerdown', enableSoundOnce, { once: true });
//window.addEventListener('keydown', enableSoundOnce, { once: true });
//window.addEventListener('touchstart', enableSoundOnce, { once: true, passive: true });
</script>
<div class="footer txt-s12 txt-center">
<p>
<a href="https://robertsspaceindustries.com/en/orgs/REACT" target="_blank" title="Notre page RSI"><img src="img/icon09b.png" width="48" height="48" alt="" /></a>
<a href="https://discord.gg/dAvST8E7Mq" target="_blank" title="Notre Discord"><img src="img/icon08b.png" width="48" height="48" alt="" /></a>
<a href="https://robertsspaceindustries.com" target="_blank" title="RSI"><img src="img/icon10.png" width="48" height="48" alt="" /></a>
</p>
<p>Copyright © 2024-<?php echo date('Y'); ?> | www.react-sc.fr | All rights reserved.</p>
</div>
</body>
</html>