Initial commit after re-install
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.idea
|
||||||
|
.idea/*
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*.tmp
|
||||||
|
|
||||||
35
api/list_images.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
// Returns a JSON list of images currently present in /img
|
||||||
|
require_once __DIR__ . '/../auth/auth.php';
|
||||||
|
dreamgirl_require_login_json();
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
$imgDir = realpath(__DIR__ . '/../img');
|
||||||
|
if ($imgDir === false || !is_dir($imgDir)) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'img directory not found']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedExt = ['jpg','jpeg','png','gif','webp'];
|
||||||
|
$images = [];
|
||||||
|
|
||||||
|
$files = scandir($imgDir);
|
||||||
|
if ($files === false) $files = [];
|
||||||
|
|
||||||
|
foreach ($files as $f) {
|
||||||
|
if ($f === '.' || $f === '..') continue;
|
||||||
|
$path = $imgDir . DIRECTORY_SEPARATOR . $f;
|
||||||
|
if (!is_file($path)) continue;
|
||||||
|
$ext = strtolower(pathinfo($f, PATHINFO_EXTENSION));
|
||||||
|
if (!in_array($ext, $allowedExt, true)) continue;
|
||||||
|
$images[] = $f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stable order
|
||||||
|
natcasesort($images);
|
||||||
|
$images = array_values($images);
|
||||||
|
|
||||||
|
echo json_encode(['ok' => true, 'images' => $images], JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
|
|
||||||
89
api/upload_image.php
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
// Upload handler: saves an uploaded image into /img and returns JSON.
|
||||||
|
require_once __DIR__ . '/../auth/auth.php';
|
||||||
|
dreamgirl_require_login_json();
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
// Limit: 10MB
|
||||||
|
$maxBytes = 10 * 1024 * 1024;
|
||||||
|
|
||||||
|
if (!isset($_FILES['image'])) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'No file uploaded']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $_FILES['image'];
|
||||||
|
if (!is_array($file) || !isset($file['error'])) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'Invalid upload']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($file['error'] !== UPLOAD_ERR_OK) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'Upload error: ' . $file['error']]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($file['size']) || $file['size'] <= 0 || $file['size'] > $maxBytes) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'File too large (max 10MB)']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate image content
|
||||||
|
$tmp = $file['tmp_name'];
|
||||||
|
$info = @getimagesize($tmp);
|
||||||
|
if ($info === false) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'Not a valid image']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mime = isset($info['mime']) ? strtolower($info['mime']) : '';
|
||||||
|
$allowedMimeToExt = [
|
||||||
|
'image/jpeg' => 'jpg',
|
||||||
|
'image/png' => 'png',
|
||||||
|
'image/gif' => 'gif',
|
||||||
|
'image/webp' => 'webp',
|
||||||
|
];
|
||||||
|
if (!isset($allowedMimeToExt[$mime])) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'Unsupported image type']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$imgDir = realpath(__DIR__ . '/../img');
|
||||||
|
if ($imgDir === false || !is_dir($imgDir)) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'img directory not found']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate safe unique filename
|
||||||
|
$ext = $allowedMimeToExt[$mime];
|
||||||
|
$base = 'upload_' . date('Ymd_His') . '_' . bin2hex(random_bytes(4));
|
||||||
|
$filename = $base . '.' . $ext;
|
||||||
|
$dest = $imgDir . DIRECTORY_SEPARATOR . $filename;
|
||||||
|
|
||||||
|
// Ensure we don't overwrite (extremely unlikely)
|
||||||
|
$tries = 0;
|
||||||
|
while (file_exists($dest) && $tries < 5) {
|
||||||
|
$filename = $base . '_' . bin2hex(random_bytes(2)) . '.' . $ext;
|
||||||
|
$dest = $imgDir . DIRECTORY_SEPARATOR . $filename;
|
||||||
|
$tries++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!move_uploaded_file($tmp, $dest)) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'Failed to save file (check permissions)']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conservative permissions
|
||||||
|
@chmod($dest, 0644);
|
||||||
|
|
||||||
|
echo json_encode(['ok' => true, 'filename' => $filename], JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
|
|
||||||
159
auth/auth.php
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session-based auth gate (no DB).
|
||||||
|
* Required credentials:
|
||||||
|
* - username: admin
|
||||||
|
* - password: admin5004!
|
||||||
|
*
|
||||||
|
* NOTE: Password is stored as SHA-256 hash here (not plaintext).
|
||||||
|
*/
|
||||||
|
|
||||||
|
function dreamgirl_session_start(): void {
|
||||||
|
if (session_status() === PHP_SESSION_ACTIVE) return;
|
||||||
|
|
||||||
|
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
|
||||||
|
|
||||||
|
// PHP 7.3+ supports samesite via array; older versions may ignore unknown keys.
|
||||||
|
session_set_cookie_params([
|
||||||
|
'lifetime' => 0,
|
||||||
|
'path' => '/',
|
||||||
|
'httponly' => true,
|
||||||
|
'samesite' => 'Lax',
|
||||||
|
'secure' => $secure,
|
||||||
|
]);
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
function dreamgirl_is_logged_in(): bool {
|
||||||
|
dreamgirl_session_start();
|
||||||
|
// Try NCue SSO once per session (if available)
|
||||||
|
if (!isset($_SESSION['dreamgirl_sso_checked'])) {
|
||||||
|
$_SESSION['dreamgirl_sso_checked'] = true;
|
||||||
|
dreamgirl_try_ncue_sso_login();
|
||||||
|
}
|
||||||
|
return isset($_SESSION['dreamgirl_user']) && $_SESSION['dreamgirl_user'] === 'admin';
|
||||||
|
}
|
||||||
|
|
||||||
|
function dreamgirl_check_credentials(string $username, string $password): bool {
|
||||||
|
if ($username !== 'admin') return false;
|
||||||
|
|
||||||
|
// sha256("admin5004!")
|
||||||
|
$expectedSha256 = 'adcda104b73b73f8cddf5c8047a6bc0e5e1388265ed4bf0f31f704c13cbc11b7';
|
||||||
|
$gotSha256 = hash('sha256', $password);
|
||||||
|
|
||||||
|
return hash_equals($expectedSha256, $gotSha256);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dreamgirl_base_path(): string {
|
||||||
|
// If deployed under /dreamgirl, SCRIPT_NAME is like /dreamgirl/index.php
|
||||||
|
// If at web root, SCRIPT_NAME is like /index.php
|
||||||
|
$script = isset($_SERVER['SCRIPT_NAME']) ? (string)$_SERVER['SCRIPT_NAME'] : '';
|
||||||
|
$dir = rtrim(str_replace('\\', '/', dirname($script)), '/');
|
||||||
|
return ($dir === '' || $dir === '.') ? '' : $dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dreamgirl_url(string $path): string {
|
||||||
|
$base = dreamgirl_base_path();
|
||||||
|
$p = ltrim($path, '/');
|
||||||
|
return $base . '/' . $p;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dreamgirl_try_ncue_sso_login(): bool {
|
||||||
|
// If already logged in, nothing to do.
|
||||||
|
if (isset($_SESSION['dreamgirl_user']) && $_SESSION['dreamgirl_user'] === 'admin') return true;
|
||||||
|
|
||||||
|
// Only accept SSO for the known NCue account (Google login).
|
||||||
|
$allowedEmails = ['dosangyoon2@gmail.com'];
|
||||||
|
|
||||||
|
// Endpoint commonly provided by NextAuth/Auth.js style setups.
|
||||||
|
$endpointPath = '/api/auth/session';
|
||||||
|
|
||||||
|
$host = isset($_SERVER['HTTP_HOST']) ? (string)$_SERVER['HTTP_HOST'] : '';
|
||||||
|
if ($host === '') return false;
|
||||||
|
|
||||||
|
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
|
||||||
|
$url = $scheme . '://' . $host . $endpointPath;
|
||||||
|
|
||||||
|
$cookieHeader = isset($_SERVER['HTTP_COOKIE']) ? (string)$_SERVER['HTTP_COOKIE'] : '';
|
||||||
|
if ($cookieHeader === '') return false;
|
||||||
|
|
||||||
|
$json = null;
|
||||||
|
|
||||||
|
// Prefer cURL if available
|
||||||
|
if (function_exists('curl_init')) {
|
||||||
|
$ch = curl_init();
|
||||||
|
if ($ch === false) return false;
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 2);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
'Accept: application/json',
|
||||||
|
'Cookie: ' . $cookieHeader,
|
||||||
|
]);
|
||||||
|
// Do not follow redirects to avoid loops
|
||||||
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
|
||||||
|
|
||||||
|
$resp = curl_exec($ch);
|
||||||
|
$code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($resp !== false && $code >= 200 && $code < 300) {
|
||||||
|
$json = $resp;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback using stream context
|
||||||
|
$ctx = stream_context_create([
|
||||||
|
'http' => [
|
||||||
|
'method' => 'GET',
|
||||||
|
'header' => "Accept: application/json\r\nCookie: {$cookieHeader}\r\n",
|
||||||
|
'timeout' => 2,
|
||||||
|
'ignore_errors' => true,
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
$resp = @file_get_contents($url, false, $ctx);
|
||||||
|
if ($resp !== false) $json = $resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($json === null) return false;
|
||||||
|
|
||||||
|
$data = json_decode($json, true);
|
||||||
|
if (!is_array($data)) return false;
|
||||||
|
|
||||||
|
// NextAuth shape: { user: { email: ... }, expires: ... }
|
||||||
|
$email = '';
|
||||||
|
if (isset($data['user']) && is_array($data['user']) && isset($data['user']['email'])) {
|
||||||
|
$email = (string)$data['user']['email'];
|
||||||
|
} elseif (isset($data['email'])) {
|
||||||
|
// Alternate shape
|
||||||
|
$email = (string)$data['email'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($email === '' || !in_array($email, $allowedEmails, true)) return false;
|
||||||
|
|
||||||
|
// SSO accepted: mark session as logged-in for DreamGirl.
|
||||||
|
$_SESSION['dreamgirl_user'] = 'admin';
|
||||||
|
$_SESSION['dreamgirl_sso_email'] = $email;
|
||||||
|
$_SESSION['dreamgirl_sso_at'] = time();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dreamgirl_require_login_page(): void {
|
||||||
|
if (dreamgirl_is_logged_in()) return;
|
||||||
|
header('Location: ' . dreamgirl_url('login.php'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dreamgirl_require_login_json(): void {
|
||||||
|
if (dreamgirl_is_logged_in()) return;
|
||||||
|
http_response_code(401);
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'Unauthorized'], JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
66
css/basic.css
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
html, body {
|
||||||
|
margin:0;
|
||||||
|
padding:0;
|
||||||
|
}
|
||||||
|
body{
|
||||||
|
text-align: center;
|
||||||
|
font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Helvetica, Arial, sans-serif;
|
||||||
|
background-color: #eee;
|
||||||
|
color: #444;
|
||||||
|
font-size: 75%;
|
||||||
|
}
|
||||||
|
a{
|
||||||
|
color: #27D;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a:focus, a:hover, a:active {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
p, li {
|
||||||
|
line-height: 1.8em;
|
||||||
|
}
|
||||||
|
h1, h2 {
|
||||||
|
font-family: "Trebuchet MS", Verdana, sans-serif;
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
letter-spacing:-1px;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 3em;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
font-size:2em;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
font-size: 1.2em;
|
||||||
|
line-height: 1.2em;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
div#page {
|
||||||
|
/* Full-width, responsive container */
|
||||||
|
width: 100%;
|
||||||
|
max-width: none;
|
||||||
|
background-color: #fff;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: left;
|
||||||
|
border-color: #ddd;
|
||||||
|
border-style: none solid solid;
|
||||||
|
border-width: medium 1px 1px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
div#container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
div#ads {
|
||||||
|
clear: both;
|
||||||
|
padding: 12px 0 12px 66px;
|
||||||
|
}
|
||||||
|
div#footer {
|
||||||
|
clear: both;
|
||||||
|
color: #777;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px 0 40px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
324
css/galleriffic-2.css
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
div.content {
|
||||||
|
/* The display of content is enabled using jQuery so that the slideshow content won't display unless javascript is enabled. */
|
||||||
|
display: none;
|
||||||
|
float: none;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
div.content a, div.navigation a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
div.content a:focus, div.content a:hover, div.content a:active {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
div.controls {
|
||||||
|
margin-top: 5px;
|
||||||
|
height: 23px;
|
||||||
|
}
|
||||||
|
div.controls a {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
div.ss-controls {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
div.nav-controls {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
div.slideshow-container {
|
||||||
|
position: relative;
|
||||||
|
clear: both;
|
||||||
|
/* Fill available screen height more aggressively on large displays */
|
||||||
|
height: clamp(700px, calc(100vh - 220px), 980px);
|
||||||
|
max-width: 1100px;
|
||||||
|
}
|
||||||
|
div.loader {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
background-image: url('loader.gif');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
div.slideshow {
|
||||||
|
|
||||||
|
}
|
||||||
|
div.slideshow span.image-wrapper {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
div.slideshow a.advance-link {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
div.slideshow a.advance-link:hover, div.slideshow a.advance-link:active, div.slideshow a.advance-link:visited {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
div.slideshow img {
|
||||||
|
vertical-align: middle;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
div.download {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
div.caption-container {
|
||||||
|
position: relative;
|
||||||
|
clear: left;
|
||||||
|
height: 75px;
|
||||||
|
}
|
||||||
|
span.image-caption {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
div.caption {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
div.image-title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
div.image-desc {
|
||||||
|
line-height: 1.3em;
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
div.navigation {
|
||||||
|
/* The navigation style is set using jQuery so that the javascript specific styles won't be applied unless javascript is enabled. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure thumbnail list area clears its floated children (thumbs + pagination) */
|
||||||
|
#thumbs-list::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Upload panel under thumbnails */
|
||||||
|
.upload-panel {
|
||||||
|
clear: both; /* never overlap floated thumbnails/pagination */
|
||||||
|
margin-top: 14px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid #e5e5e5;
|
||||||
|
}
|
||||||
|
.upload-title {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.dropzone {
|
||||||
|
position: relative;
|
||||||
|
border: 2px dashed #cfcfcf;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 14px 12px;
|
||||||
|
background: #fafafa;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.dropzone:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 3px rgba(46, 131, 255, 0.25);
|
||||||
|
border-color: #2e83ff;
|
||||||
|
}
|
||||||
|
.dropzone.dragover {
|
||||||
|
border-color: #2e83ff;
|
||||||
|
background: #f2f7ff;
|
||||||
|
}
|
||||||
|
.dropzone-text {
|
||||||
|
color: #666;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.file-input {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
opacity: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.upload-preview {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.upload-preview img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 160px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
background: #fff;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.preview-meta {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #666;
|
||||||
|
margin-top: 6px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.upload-actions {
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.upload-actions button {
|
||||||
|
padding: 7px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #cfcfcf;
|
||||||
|
background: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.upload-actions button:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.upload-status {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fullscreen lightbox */
|
||||||
|
.lightbox {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 99999;
|
||||||
|
}
|
||||||
|
.lightbox-backdrop {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.85);
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.lightbox-content {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.lightbox-content img {
|
||||||
|
/* Fill the dark area as much as possible while keeping aspect ratio */
|
||||||
|
width: 98vw;
|
||||||
|
height: 98vh;
|
||||||
|
object-fit: contain;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
.lightbox-close {
|
||||||
|
position: absolute;
|
||||||
|
top: 14px;
|
||||||
|
right: 14px;
|
||||||
|
z-index: 2; /* ensure clickable above .lightbox-content */
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||||
|
background: rgba(0, 0, 0, 0.35);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 28px;
|
||||||
|
line-height: 42px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.lightbox-close:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Layout: slideshow (left) + thumbs (right), full width ---- */
|
||||||
|
.gallery-wrap {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-wrap > #gallery.content {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-wrap > #thumbs.navigation {
|
||||||
|
flex: 0 0 380px;
|
||||||
|
max-width: 38vw;
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.gallery-wrap {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.gallery-wrap > #thumbs.navigation {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
max-width: none;
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ul.thumbs {
|
||||||
|
clear: both;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
ul.thumbs li {
|
||||||
|
float: left;
|
||||||
|
padding: 0;
|
||||||
|
margin: 5px 10px 5px 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
a.thumb {
|
||||||
|
padding: 2px;
|
||||||
|
display: block;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
ul.thumbs li.selected a.thumb {
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
a.thumb:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
ul.thumbs img {
|
||||||
|
border: none;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
div.pagination {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
div.navigation div.top {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
height: 11px;
|
||||||
|
}
|
||||||
|
div.navigation div.bottom {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
div.pagination a, div.pagination span.current, div.pagination span.ellipsis {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
margin-right: 2px;
|
||||||
|
padding: 4px 7px 2px 7px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
div.pagination a:hover {
|
||||||
|
background-color: #eee;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
div.pagination span.current {
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: #000;
|
||||||
|
border-color: #000;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
div.pagination span.ellipsis {
|
||||||
|
border: none;
|
||||||
|
padding: 5px 0 3px 2px;
|
||||||
|
}
|
||||||
BIN
css/loader.gif
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
favicon.ico
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
img/0153F35051A0C0211B8BD3.jpg
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
img/025B7F39519C5F6B044AF3.jpg
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
img/034C3235519C8CD00742EC.jpg
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
img/0c.jpg
Normal file
|
After Width: | Height: | Size: 157 KiB |
BIN
img/0ceb20aedffea866c20b5a49f299945b_JYkaEVOxYvJ4FzuyiMQdM.png
Normal file
|
After Width: | Height: | Size: 467 KiB |
BIN
img/12123.png
Normal file
|
After Width: | Height: | Size: 451 KiB |
BIN
img/14 (2).jpg
Normal file
|
After Width: | Height: | Size: 210 KiB |
BIN
img/146B934A4F5306BB0F1386.jpg
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
img/154CA4335051D4480CC608.jpg
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
img/15595D394F2D6E1108DE66.jpg
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
img/17030D4E5047116A279BEA.jpg
Normal file
|
After Width: | Height: | Size: 258 KiB |
|
After Width: | Height: | Size: 86 KiB |
BIN
img/1893082628_4qQJ81me_0022-16.jpg
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
img/1893432958_eef8ba17_1.jpg
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
img/19.PNG
Normal file
|
After Width: | Height: | Size: 966 KiB |
BIN
img/19087D434FB861602E1A0A.jpg
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
img/1994171862_t9boCwEX_image.jpg
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
img/20.jpg
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
img/2012-07-23_10%3B55%3B38.jpg
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
img/2012030900889_0.png
Normal file
|
After Width: | Height: | Size: 277 KiB |
BIN
img/20130724192905_1543.jpg
Normal file
|
After Width: | Height: | Size: 230 KiB |
BIN
img/20131120184957_8180.jpg
Normal file
|
After Width: | Height: | Size: 198 KiB |
BIN
img/206b6298c87612f682b5ef97772eb8a0_ET5lfVASQdpLm3lh4J9TDQd.jpg
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
img/206b6298c87612f682b5ef97772eb8a0_dwHUQsoPAfLrJPdnQQllJyn.jpg
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
img/212ACF4D53582D1716FD72.jpg
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
img/213D44455359A914075427.jpg
Normal file
|
After Width: | Height: | Size: 196 KiB |
BIN
img/2147B2455303422E2BFF66.jpg
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
img/216b0df4bffefb913a1bb76f7addd355_jKYSjW5li4iTsBG.jpg
Normal file
|
After Width: | Height: | Size: 203 KiB |
BIN
img/2205084253577CA410D389.jpg
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
img/222C1842532C05C4082DE6.jpg
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
img/22338F4A52F9C7B9024DFA.jpg
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
img/2314.png
Normal file
|
After Width: | Height: | Size: 618 KiB |
BIN
img/234234.jpg
Normal file
|
After Width: | Height: | Size: 173 KiB |
BIN
img/234234.png
Normal file
|
After Width: | Height: | Size: 586 KiB |
BIN
img/2353454545.jpg
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
img/24.jpg
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
img/244A134D53582DA01782A7.jpg
Normal file
|
After Width: | Height: | Size: 216 KiB |
BIN
img/247536425359AC0434BDE7.jpg
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
img/25.jpg
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
img/2558D23E53577D7A11F4BB.jpg
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
img/26.jpg
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
img/260DBD44535780E91FFCD9.jpg
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
img/261A7B4E53582CC515DA55.jpg
Normal file
|
After Width: | Height: | Size: 218 KiB |
BIN
img/26555839519C5F6A0ED774.jpg
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
img/267F854B527209130C24AC.jpg
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
img/27578E4551891D5228260D.jpg
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
img/28.jpg
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
img/2956682.jpg
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
img/3 (1).jpg
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
img/3034777999_c116a29b_L2SbhZV.jpg
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
img/3067451955_ObKEcQVZ_DkOcfiv25iBihDUYJnM.jpg
Normal file
|
After Width: | Height: | Size: 256 KiB |
BIN
img/32423.jpg
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
img/32434.jpg
Normal file
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 77 KiB |
BIN
img/342.png
Normal file
|
After Width: | Height: | Size: 465 KiB |
BIN
img/34234.png
Normal file
|
After Width: | Height: | Size: 305 KiB |
BIN
img/34234234234234.jpg
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
img/3434.png
Normal file
|
After Width: | Height: | Size: 925 KiB |
BIN
img/343415.jpg
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
img/3434234.png
Normal file
|
After Width: | Height: | Size: 761 KiB |
BIN
img/34342423.png
Normal file
|
After Width: | Height: | Size: 392 KiB |
BIN
img/3454546.jpg
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
img/3553406573_5RFBE8Oq_1.jpg
Normal file
|
After Width: | Height: | Size: 185 KiB |
BIN
img/36.jpg
Normal file
|
After Width: | Height: | Size: 231 KiB |
BIN
img/366554286.jpg
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
img/42e90b3a5a56ed41e8ac4eb64932a58d_1424309196.1739.jpg
Normal file
|
After Width: | Height: | Size: 217 KiB |
BIN
img/4354545.jpg
Normal file
|
After Width: | Height: | Size: 180 KiB |
BIN
img/4411.jpg
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
img/4545452.png
Normal file
|
After Width: | Height: | Size: 266 KiB |
BIN
img/480c0382e3b20b536b4de59878d72b8c_o2qdyx7gy28gqvtgck.jpg
Normal file
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 130 KiB |
BIN
img/5345.png
Normal file
|
After Width: | Height: | Size: 583 KiB |
BIN
img/54440DE7503BC90019.jpg
Normal file
|
After Width: | Height: | Size: 251 KiB |
BIN
img/598340912_e4782152_1.jpg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
img/598340923_665dda78_1.jpg
Normal file
|
After Width: | Height: | Size: 191 KiB |
BIN
img/66.jpg
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
img/7_1.jpg
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
img/8283.png
Normal file
|
After Width: | Height: | Size: 513 KiB |
BIN
img/831477005_71IGxrfy_EC82ACEBB3B8_-dp034_2_1100_08.jpg
Normal file
|
After Width: | Height: | Size: 399 KiB |
BIN
img/831477202_AumDRJtz_4.jpg
Normal file
|
After Width: | Height: | Size: 412 KiB |
BIN
img/831477202_KAneU1sl_EC82ACEBB3B8_-page1_1_21.jpg
Normal file
|
After Width: | Height: | Size: 341 KiB |
BIN
img/8346.png
Normal file
|
After Width: | Height: | Size: 298 KiB |
BIN
img/87347878.png
Normal file
|
After Width: | Height: | Size: 256 KiB |
BIN
img/96e0df2ab94ab2739e9ab2beee5a32e1_K3l2etyAjILwnN7.jpg
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
img/96e0df2ab94ab2739e9ab2beee5a32e1_ofGsnGhKoWPYk4DGlD.jpg
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
img/A1_840x1270.jpg
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
img/ARA54590812a33e1.jpg
Normal file
|
After Width: | Height: | Size: 208 KiB |
BIN
img/ASSA0130-2.jpg
Normal file
|
After Width: | Height: | Size: 276 KiB |