2354
@@ -0,0 +1,109 @@
|
|||||||
|
# Ocultar headers de servidor
|
||||||
|
<IfModule mod_headers.c>
|
||||||
|
Header unset X-Powered-By
|
||||||
|
Header always unset X-Powered-By
|
||||||
|
Header unset Server
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# Bloquear acceso a archivos sensibles
|
||||||
|
<FilesMatch "\.(env|log|git|gitignore|htpasswd|DS_Store)$">
|
||||||
|
Order allow,deny
|
||||||
|
Deny from all
|
||||||
|
</FilesMatch>
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Compresion (Brotli si esta, Gzip como fallback)
|
||||||
|
# ============================================================
|
||||||
|
<IfModule mod_brotli.c>
|
||||||
|
AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/css text/xml text/javascript application/javascript application/x-javascript application/json application/xml application/rss+xml application/atom+xml image/svg+xml font/ttf font/otf application/vnd.ms-fontobject
|
||||||
|
</IfModule>
|
||||||
|
<IfModule mod_deflate.c>
|
||||||
|
AddOutputFilterByType DEFLATE text/html text/plain text/css text/xml text/javascript application/javascript application/x-javascript application/json application/xml application/rss+xml application/atom+xml image/svg+xml font/ttf font/otf application/vnd.ms-fontobject
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Cache-Control para estaticos
|
||||||
|
# ============================================================
|
||||||
|
<IfModule mod_expires.c>
|
||||||
|
ExpiresActive On
|
||||||
|
ExpiresDefault "access plus 1 month"
|
||||||
|
|
||||||
|
# HTML y JSON: no cachear (siempre frescos)
|
||||||
|
ExpiresByType text/html "access plus 0 seconds"
|
||||||
|
ExpiresByType application/json "access plus 0 seconds"
|
||||||
|
ExpiresByType application/manifest+json "access plus 1 day"
|
||||||
|
|
||||||
|
# CSS y JS: 1 anio (con cache-busting via ?v= o hash de Vite)
|
||||||
|
ExpiresByType text/css "access plus 1 year"
|
||||||
|
ExpiresByType application/javascript "access plus 1 year"
|
||||||
|
ExpiresByType text/javascript "access plus 1 year"
|
||||||
|
|
||||||
|
# Imagenes: 1 anio
|
||||||
|
ExpiresByType image/png "access plus 1 year"
|
||||||
|
ExpiresByType image/jpeg "access plus 1 year"
|
||||||
|
ExpiresByType image/webp "access plus 1 year"
|
||||||
|
ExpiresByType image/avif "access plus 1 year"
|
||||||
|
ExpiresByType image/svg+xml "access plus 1 year"
|
||||||
|
ExpiresByType image/x-icon "access plus 1 year"
|
||||||
|
ExpiresByType image/vnd.microsoft.icon "access plus 1 year"
|
||||||
|
|
||||||
|
# Fuentes: 1 anio
|
||||||
|
ExpiresByType font/woff "access plus 1 year"
|
||||||
|
ExpiresByType font/woff2 "access plus 1 year"
|
||||||
|
ExpiresByType application/font-woff "access plus 1 year"
|
||||||
|
ExpiresByType application/font-woff2 "access plus 1 year"
|
||||||
|
ExpiresByType application/vnd.ms-fontobject "access plus 1 year"
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
<IfModule mod_headers.c>
|
||||||
|
# Cache largo + immutable para estaticos con hash o version
|
||||||
|
<FilesMatch "\.(css|js|woff2?|ttf|eot|otf|svg|png|jpe?g|webp|avif|ico)$">
|
||||||
|
Header set Cache-Control "public, max-age=31536000, immutable"
|
||||||
|
</FilesMatch>
|
||||||
|
|
||||||
|
# HTML siempre fresco
|
||||||
|
<FilesMatch "\.(html|htm)$">
|
||||||
|
Header set Cache-Control "no-cache, must-revalidate"
|
||||||
|
</FilesMatch>
|
||||||
|
|
||||||
|
# Vary: Accept-Encoding (correcto para CDN/proxies)
|
||||||
|
<FilesMatch "\.(css|js|html|svg|json)$">
|
||||||
|
Header append Vary Accept-Encoding
|
||||||
|
</FilesMatch>
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Routing Laravel + seguridad
|
||||||
|
# ============================================================
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
<IfModule mod_negotiation.c>
|
||||||
|
Options -MultiViews -Indexes
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
# Forzar HTTPS
|
||||||
|
RewriteCond %{HTTPS} off
|
||||||
|
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
|
||||||
|
|
||||||
|
# Bloquear acceso directo a /storage/app
|
||||||
|
RewriteRule ^storage/app/(.*)$ - [F,L]
|
||||||
|
|
||||||
|
# Handle Authorization Header
|
||||||
|
RewriteCond %{HTTP:Authorization} .
|
||||||
|
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||||
|
|
||||||
|
# Handle X-XSRF-Token Header
|
||||||
|
RewriteCond %{HTTP:x-xsrf-token} .
|
||||||
|
RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}]
|
||||||
|
|
||||||
|
# Redirect Trailing Slashes If Not A Folder...
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteCond %{REQUEST_URI} (.+)/$
|
||||||
|
RewriteRule ^ %1 [L,R=301]
|
||||||
|
|
||||||
|
# Send Requests To Front Controller...
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteRule ^ index.php [L]
|
||||||
|
</IfModule>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
Contact: mailto:admin@onapb.com
|
||||||
|
Expires: 2027-04-01T00:00:00Z
|
||||||
|
Preferred-Languages: es, en
|
||||||
|
Canonical: https://onapb.com/.well-known/security.txt
|
||||||
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 378 B |
|
After Width: | Height: | Size: 861 B |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Application;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
define('LARAVEL_START', microtime(true));
|
||||||
|
|
||||||
|
// Determine if the application is in maintenance mode...
|
||||||
|
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
|
||||||
|
require $maintenance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the Composer autoloader...
|
||||||
|
require __DIR__.'/../vendor/autoload.php';
|
||||||
|
|
||||||
|
// Bootstrap Laravel and handle the request...
|
||||||
|
/** @var Application $app */
|
||||||
|
$app = require_once __DIR__.'/../bootstrap/app.php';
|
||||||
|
|
||||||
|
$app->handleRequest(Request::capture());
|
||||||
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
"name": "OnAPB — Asociación de Básquet Paraná",
|
||||||
|
"short_name": "OnAPB",
|
||||||
|
"description": "Seguí el básquet de Paraná: partidos, equipos, entradas y resultados.",
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#0a0a0a",
|
||||||
|
"theme_color": "#b00000",
|
||||||
|
"orientation": "portrait-primary",
|
||||||
|
"lang": "es-AR",
|
||||||
|
"scope": "/",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-72.png?v=4",
|
||||||
|
"sizes": "72x72",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-96.png?v=4",
|
||||||
|
"sizes": "96x96",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-128.png?v=4",
|
||||||
|
"sizes": "128x128",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-144.png?v=4",
|
||||||
|
"sizes": "144x144",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-152.png?v=4",
|
||||||
|
"sizes": "152x152",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-192.png?v=4",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-384.png?v=4",
|
||||||
|
"sizes": "384x384",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-512.png?v=4",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"shortcuts": [
|
||||||
|
{
|
||||||
|
"name": "Próximos Partidos",
|
||||||
|
"short_name": "Partidos",
|
||||||
|
"description": "Ver los próximos partidos",
|
||||||
|
"url": "/eventos",
|
||||||
|
"icons": [{ "src": "/icons/icon-96.png?v=4", "sizes": "96x96" }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mi Panel",
|
||||||
|
"short_name": "Mi Panel",
|
||||||
|
"description": "Acceder a mi perfil y entradas",
|
||||||
|
"url": "/panel-usuario",
|
||||||
|
"icons": [{ "src": "/icons/icon-96.png?v=4", "sizes": "96x96" }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Torneos",
|
||||||
|
"short_name": "Torneos",
|
||||||
|
"description": "Ver torneos activos",
|
||||||
|
"url": "/torneos",
|
||||||
|
"icons": [{ "src": "/icons/icon-96.png?v=4", "sizes": "96x96" }]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"screenshots": [],
|
||||||
|
"categories": ["sports", "entertainment"],
|
||||||
|
"prefer_related_applications": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
User-agent: *
|
||||||
|
Disallow: /admin
|
||||||
|
Disallow: /recuperar
|
||||||
|
Disallow: /api/
|
||||||
|
Disallow: /storage/
|
||||||
|
Disallow: /vendor/
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
Sitemap: https://onapb.com/sitemap.xml
|
||||||
@@ -0,0 +1,249 @@
|
|||||||
|
/*
|
||||||
|
OnAPB Admin - Kinetic Arena Design System
|
||||||
|
"The Editorial Athlete" - Admin Extension
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--admin-sidebar-bg: #0A0A0A;
|
||||||
|
--admin-sidebar-active: #B00000;
|
||||||
|
--admin-content-bg: #F8F9FF;
|
||||||
|
--admin-surface: #FFFFFF;
|
||||||
|
--admin-primary: #B00000;
|
||||||
|
--admin-text-main: #1A1A1A;
|
||||||
|
--admin-text-muted: #666666;
|
||||||
|
--admin-outline: #E0E0E0;
|
||||||
|
|
||||||
|
/* Typography */
|
||||||
|
--admin-font-header: 'Space Grotesk', sans-serif;
|
||||||
|
--admin-font-body: 'Plus Jakarta Sans', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--admin-content-bg);
|
||||||
|
font-family: var(--admin-font-body);
|
||||||
|
color: var(--admin-text-main);
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar Redesign */
|
||||||
|
.admin-sidebar {
|
||||||
|
background: var(--admin-sidebar-bg) !important;
|
||||||
|
width: 280px;
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
border-right: none !important;
|
||||||
|
overflow-y: auto;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: var(--admin-primary) #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-sidebar::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-sidebar::-webkit-scrollbar-track {
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-sidebar::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--admin-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-header {
|
||||||
|
padding: 2rem;
|
||||||
|
background: #000;
|
||||||
|
border-bottom: 4px solid var(--admin-sidebar-active);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-logo {
|
||||||
|
height: 40px;
|
||||||
|
filter: brightness(0) invert(1);
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-title {
|
||||||
|
font-family: var(--admin-font-header);
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: #fff;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-nav {
|
||||||
|
padding: 1.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.85rem 2rem;
|
||||||
|
color: rgba(255,255,255,0.6);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link i {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link:hover {
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(255,255,255,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link.active {
|
||||||
|
background: var(--admin-sidebar-active);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main Content Area */
|
||||||
|
.admin-main {
|
||||||
|
margin-left: 280px;
|
||||||
|
min-height: 100vh;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-topbar {
|
||||||
|
background: #fff;
|
||||||
|
height: 70px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 2rem;
|
||||||
|
border-bottom: 1px solid var(--admin-outline);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-content {
|
||||||
|
padding: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kinetic Admin Cards */
|
||||||
|
.admin-card {
|
||||||
|
background: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.03);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-card-header {
|
||||||
|
border-bottom: 2px solid var(--admin-content-bg);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-card-header h3 {
|
||||||
|
font-family: var(--admin-font-header);
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stats Cards */
|
||||||
|
.stat-card {
|
||||||
|
background: #000;
|
||||||
|
color: #fff;
|
||||||
|
padding: 2rem;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card .stat-value {
|
||||||
|
font-family: var(--admin-font-header);
|
||||||
|
font-size: 3rem;
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card .stat-label {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
color: rgba(255,255,255,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* High Contrast Table */
|
||||||
|
.kinetic-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kinetic-table th {
|
||||||
|
background: var(--admin-content-bg);
|
||||||
|
padding: 1rem;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
color: var(--admin-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kinetic-table td {
|
||||||
|
padding: 1.2rem 1rem;
|
||||||
|
border-bottom: 1px solid var(--admin-outline);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kinetic-table tr:hover td {
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Admin Buttons */
|
||||||
|
.btn-admin-primary {
|
||||||
|
background: var(--admin-primary);
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-admin-primary:hover {
|
||||||
|
background: #900000;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-admin-action {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 0;
|
||||||
|
margin-right: 5px;
|
||||||
|
transition: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media (max-width: 991.98px) {
|
||||||
|
.admin-sidebar {
|
||||||
|
left: -280px;
|
||||||
|
}
|
||||||
|
.admin-sidebar.show {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.admin-main {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,621 @@
|
|||||||
|
/* ══════════════════════════════════
|
||||||
|
ADMIN PANEL STYLES - ONAPB
|
||||||
|
══════════════════════════════════ */
|
||||||
|
|
||||||
|
/* ── Variables ── */
|
||||||
|
:root {
|
||||||
|
--admin-sidebar-w: 260px;
|
||||||
|
--admin-primary: #b00000;
|
||||||
|
--admin-primary-dark: #8b0000;
|
||||||
|
--admin-bg: #f0f2f5;
|
||||||
|
--admin-sidebar-bg: #1a1a2e;
|
||||||
|
--admin-sidebar-hover: #16213e;
|
||||||
|
--admin-sidebar-active: #b00000;
|
||||||
|
--admin-card-bg: #ffffff;
|
||||||
|
--admin-topbar-h: 60px;
|
||||||
|
--admin-text: #333;
|
||||||
|
--admin-muted: #6c757d;
|
||||||
|
--admin-border: #e0e0e0;
|
||||||
|
--admin-shadow: 0 2px 12px rgba(0,0,0,0.08);
|
||||||
|
--admin-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Typography ── */
|
||||||
|
body {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
background: var(--admin-bg);
|
||||||
|
color: var(--admin-text);
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4 {
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Layout ── */
|
||||||
|
.admin-wrapper {
|
||||||
|
display: flex;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Sidebar ── */
|
||||||
|
.admin-sidebar {
|
||||||
|
width: var(--admin-sidebar-w);
|
||||||
|
background: var(--admin-sidebar-bg);
|
||||||
|
color: #ccc;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1050;
|
||||||
|
overflow-y: auto;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-header {
|
||||||
|
padding: 20px 16px 10px;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-logo {
|
||||||
|
max-height: 80px;
|
||||||
|
margin: 0 auto 8px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-title {
|
||||||
|
display: block;
|
||||||
|
font-family: 'Bebas Neue', sans-serif;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
color: var(--admin-primary);
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-nav {
|
||||||
|
padding: 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 12px 20px;
|
||||||
|
color: #b0b0c0;
|
||||||
|
text-decoration: none;
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
border-left: 3px solid transparent;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link:hover {
|
||||||
|
background: var(--admin-sidebar-hover);
|
||||||
|
color: #fff;
|
||||||
|
border-left-color: var(--admin-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link.active {
|
||||||
|
background: rgba(176, 0, 0, 0.15);
|
||||||
|
color: #fff;
|
||||||
|
border-left-color: var(--admin-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link i {
|
||||||
|
font-size: 1.15rem;
|
||||||
|
width: 22px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-divider {
|
||||||
|
border-color: rgba(255,255,255,0.08);
|
||||||
|
margin: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Main ── */
|
||||||
|
.admin-main {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: var(--admin-sidebar-w);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Topbar ── */
|
||||||
|
.admin-topbar {
|
||||||
|
height: var(--admin-topbar-h);
|
||||||
|
background: var(--admin-card-bg);
|
||||||
|
border-bottom: 1px solid var(--admin-border);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 24px;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1020;
|
||||||
|
box-shadow: 0 1px 4px rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-toggle {
|
||||||
|
color: var(--admin-text);
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topbar-greeting {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: var(--admin-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Content ── */
|
||||||
|
.admin-content {
|
||||||
|
padding: 24px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Cards ── */
|
||||||
|
.admin-card {
|
||||||
|
background: var(--admin-card-bg);
|
||||||
|
border-radius: var(--admin-radius);
|
||||||
|
box-shadow: var(--admin-shadow);
|
||||||
|
border: none;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-card .card-header {
|
||||||
|
background: var(--admin-card-bg);
|
||||||
|
border-bottom: 2px solid var(--admin-border);
|
||||||
|
padding: 16px 20px;
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-card .card-body {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Stat Cards ── */
|
||||||
|
.stat-card {
|
||||||
|
background: var(--admin-card-bg);
|
||||||
|
border-radius: var(--admin-radius);
|
||||||
|
box-shadow: var(--admin-shadow);
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
border-left: 4px solid var(--admin-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 6px 20px rgba(0,0,0,0.12);
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: #fff;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon.clubes { background: linear-gradient(135deg, #b00000, #d32f2f); }
|
||||||
|
.stat-icon.equipos { background: linear-gradient(135deg, #1565c0, #42a5f5); }
|
||||||
|
.stat-icon.jugadores{ background: linear-gradient(135deg, #2e7d32, #66bb6a); }
|
||||||
|
.stat-icon.eventos { background: linear-gradient(135deg, #e65100, #ff9800); }
|
||||||
|
.stat-icon.promos { background: linear-gradient(135deg, #6a1b9a, #ab47bc); }
|
||||||
|
.stat-icon.noticias { background: linear-gradient(135deg, #00838f, #26c6da); }
|
||||||
|
|
||||||
|
.stat-info h3 {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--admin-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-info p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--admin-muted);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Tables ── */
|
||||||
|
.admin-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-table thead th {
|
||||||
|
background: #f8f9fa;
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
color: var(--admin-muted);
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-bottom: 2px solid var(--admin-border);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-table tbody td {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-table tbody tr:hover {
|
||||||
|
background: #fafbfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-table tbody tr:last-child td {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Forms ── */
|
||||||
|
.admin-form .form-label {
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
color: var(--admin-muted);
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-form .form-control,
|
||||||
|
.admin-form .form-select {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1.5px solid var(--admin-border);
|
||||||
|
padding: 10px 14px;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-form .form-control:focus,
|
||||||
|
.admin-form .form-select:focus {
|
||||||
|
border-color: var(--admin-primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(176, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Buttons ── */
|
||||||
|
.btn-admin {
|
||||||
|
background: var(--admin-primary);
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-admin:hover {
|
||||||
|
background: var(--admin-primary-dark);
|
||||||
|
color: #fff;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-admin-outline {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--admin-primary);
|
||||||
|
border: 2px solid var(--admin-primary);
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-admin-outline:hover {
|
||||||
|
background: var(--admin-primary);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Action buttons in table */
|
||||||
|
.btn-action {
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
border: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-action.edit {
|
||||||
|
background: #e3f2fd;
|
||||||
|
color: #1565c0;
|
||||||
|
}
|
||||||
|
.btn-action.edit:hover {
|
||||||
|
background: #1565c0;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-action.delete {
|
||||||
|
background: #ffebee;
|
||||||
|
color: #c62828;
|
||||||
|
}
|
||||||
|
.btn-action.delete:hover {
|
||||||
|
background: #c62828;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Page Header ── */
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: var(--admin-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Badge ── */
|
||||||
|
.badge-admin {
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Empty State ── */
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 48px 20px;
|
||||||
|
color: var(--admin-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state i {
|
||||||
|
font-size: 3rem;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state p {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Search ── */
|
||||||
|
.search-box {
|
||||||
|
position: relative;
|
||||||
|
max-width: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box input {
|
||||||
|
padding-left: 40px;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 1.5px solid var(--admin-border);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box i {
|
||||||
|
position: absolute;
|
||||||
|
left: 14px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
color: var(--admin-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── QR Scanner ── */
|
||||||
|
.qr-scanner-area {
|
||||||
|
max-width: 500px;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-result {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: var(--admin-radius);
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-result.success {
|
||||||
|
background: #e8f5e9;
|
||||||
|
border: 2px solid #4caf50;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-result.error {
|
||||||
|
background: #ffebee;
|
||||||
|
border: 2px solid #f44336;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Overlay ── */
|
||||||
|
.sidebar-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0,0,0,0.5);
|
||||||
|
z-index: 1040;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-overlay.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Responsive ── */
|
||||||
|
@media (max-width: 991px) {
|
||||||
|
.admin-sidebar {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-sidebar.show {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-main {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.admin-content {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-info h3 {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-table thead {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-table tbody tr {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
border: 1px solid var(--admin-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-table tbody td {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 6px 0;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-table tbody td::before {
|
||||||
|
content: attr(data-label);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--admin-muted);
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Pagination ── */
|
||||||
|
.pagination {
|
||||||
|
margin-bottom: 0;
|
||||||
|
gap: 4px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination .page-link {
|
||||||
|
color: var(--admin-text);
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid var(--admin-border);
|
||||||
|
border-radius: 6px !important;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 6px 12px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination .page-link:hover {
|
||||||
|
background-color: var(--admin-bg);
|
||||||
|
border-color: var(--admin-primary);
|
||||||
|
color: var(--admin-primary);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination .page-item.active .page-link {
|
||||||
|
background-color: var(--admin-primary);
|
||||||
|
border-color: var(--admin-primary);
|
||||||
|
color: #fff;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination .page-item.disabled .page-link {
|
||||||
|
color: #bdbdbd;
|
||||||
|
background-color: #fcfcfc;
|
||||||
|
border-color: #eee;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix huge arrows in some Laravel pagination templates */
|
||||||
|
.pagination svg,
|
||||||
|
nav svg,
|
||||||
|
[role="navigation"] svg {
|
||||||
|
width: 20px !important;
|
||||||
|
height: 20px !important;
|
||||||
|
max-width: 20px !important;
|
||||||
|
max-height: 20px !important;
|
||||||
|
display: inline-block !important;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide the redundant text and large flex containers from Tailwind template if it leaks */
|
||||||
|
nav[role="navigation"] > div:first-child {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination .flex.justify-between.flex-1 {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.pagination .page-link {
|
||||||
|
padding: 4px 10px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
min-width: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Delete form inline ── */
|
||||||
|
.delete-form {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
.asociate-container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 40px auto;
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Título */
|
||||||
|
.asociate-container .page-title {
|
||||||
|
font-size: 2.4rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--primary);
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cards */
|
||||||
|
.asociate-container .card {
|
||||||
|
border-radius: var(--radius-sm, 4px);
|
||||||
|
border: 1px solid var(--outline-variant, #e0ddd6);
|
||||||
|
background: var(--surface-container-lowest, #fff);
|
||||||
|
box-shadow: var(--shadow-sm, 0 3px 10px rgba(0,0,0,0.05));
|
||||||
|
}
|
||||||
|
|
||||||
|
.asociate-container h4 {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--on-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
.asociate-container p {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--on-surface-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Formularios */
|
||||||
|
.asociate-container .form-label {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--on-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
.asociate-container .form-control {
|
||||||
|
border-radius: var(--radius-sm, 4px);
|
||||||
|
border: 1px solid var(--outline-variant, #ccc);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
padding: 10px;
|
||||||
|
background: var(--surface-container-lowest, #fff);
|
||||||
|
color: var(--on-surface);
|
||||||
|
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asociate-container .form-control:focus {
|
||||||
|
border-color: var(--primary);
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(194, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Botones */
|
||||||
|
.asociate-container .btn {
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 10px 18px;
|
||||||
|
border-radius: var(--radius-sm, 4px);
|
||||||
|
transition: transform 0.1s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asociate-container .btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.asociate-container .btn-primary {
|
||||||
|
background: var(--primary);
|
||||||
|
border-color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.asociate-container .btn-primary:hover {
|
||||||
|
background: var(--primary-deep, #8b0000);
|
||||||
|
border-color: var(--primary-deep, #8b0000);
|
||||||
|
}
|
||||||
|
|
||||||
|
.asociate-container .btn-success {
|
||||||
|
background: #28a745;
|
||||||
|
border-color: #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asociate-container .btn-success:hover {
|
||||||
|
background: #218838;
|
||||||
|
border-color: #218838;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alertas */
|
||||||
|
.asociate-container .alert {
|
||||||
|
border-radius: var(--radius-sm, 4px);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.asociate-container .page-title {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
.asociate-container { padding-left: 16px; padding-right: 16px; }
|
||||||
|
.asociate-container .form-control { min-height: 44px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.asociate-container .page-title {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asociate-container .card {
|
||||||
|
padding: 15px !important;
|
||||||
|
}
|
||||||
|
.asociate-container .btn { min-height: 44px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.sugerencia-email {
|
||||||
|
background: var(--surface-container-low, #f8f9fa);
|
||||||
|
border-left: 3px solid var(--primary);
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: var(--radius-sm, 4px);
|
||||||
|
margin-top: 4px;
|
||||||
|
color: var(--on-surface);
|
||||||
|
}
|
||||||
|
.sugerencia-email a {
|
||||||
|
color: var(--primary);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.sugerencia-email a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
@@ -0,0 +1,447 @@
|
|||||||
|
/* ====== GLOBAL ====== */
|
||||||
|
:root {
|
||||||
|
--primary-color: #b00000;
|
||||||
|
--primary-dark: #8b0000;
|
||||||
|
--bg-color: #f4f7f6;
|
||||||
|
--card-bg: #ffffff;
|
||||||
|
--text-dark: #2c3e50;
|
||||||
|
--text-muted: #6c757d;
|
||||||
|
--shadow-sm: 0 4px 15px rgba(0,0,0,0.05);
|
||||||
|
--shadow-lg: 0 10px 30px rgba(0,0,0,0.12);
|
||||||
|
--radius: 4px;
|
||||||
|
--gradient-primary: linear-gradient(135deg, #b00000, #d32f2f);
|
||||||
|
--gradient-success: linear-gradient(135deg, #2e7d32, #66bb6a);
|
||||||
|
--gradient-info: linear-gradient(135deg, #0277bd, #29b6f6);
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
min-height: 100vh;
|
||||||
|
font-size: 16px;
|
||||||
|
background: var(--bg-color);
|
||||||
|
color: var(--text-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
*, *::before, *::after { box-sizing: border-box; }
|
||||||
|
img, video, canvas, svg { max-width: 100%; height: auto; display: block; }
|
||||||
|
|
||||||
|
/* ===== TIPOGRAFÍA — delegada a kinetic-arena.css ====== */
|
||||||
|
/* Las familias tipográficas las define el sistema de diseño principal */
|
||||||
|
|
||||||
|
/* ====== NAVBAR PREMIUN ====== */
|
||||||
|
.custom-navbar {
|
||||||
|
background-color: rgba(255, 255, 255, 0.95) !important;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
border-bottom: 1px solid rgba(0,0,0,0.05);
|
||||||
|
padding: 15px 20px;
|
||||||
|
box-shadow: 0 4px 20px rgba(0,0,0,0.03);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1030;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-navbar .navbar-brand {
|
||||||
|
color: var(--primary-color);
|
||||||
|
font-size: 1.8rem;
|
||||||
|
text-shadow: 1px 1px 2px rgba(0,0,0,0.1);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
.custom-navbar .navbar-brand:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-navbar .nav-link {
|
||||||
|
color: var(--text-dark) !important;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin: 0 5px;
|
||||||
|
padding: 8px 15px !important;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-navbar .nav-link::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 15px;
|
||||||
|
right: 15px;
|
||||||
|
height: 3px;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
border-radius: 3px;
|
||||||
|
transform: scaleX(0);
|
||||||
|
transform-origin: bottom right;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-navbar .nav-link:hover::after,
|
||||||
|
.custom-navbar .nav-link.active::after {
|
||||||
|
transform: scaleX(1);
|
||||||
|
transform-origin: bottom left;
|
||||||
|
}
|
||||||
|
.custom-navbar .nav-link:hover, .custom-navbar .nav-link.active {
|
||||||
|
color: var(--primary-color) !important;
|
||||||
|
background: rgba(176, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
border-radius: var(--radius);
|
||||||
|
border: none;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
padding: 10px;
|
||||||
|
animation: fadeInDown 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInDown {
|
||||||
|
from { opacity: 0; transform: translateY(-10px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item {
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px 15px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
.dropdown-item:hover {
|
||||||
|
background-color: rgba(176, 0, 0, 0.05);
|
||||||
|
color: var(--primary-color);
|
||||||
|
transform: translateX(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====== HERO ====== */
|
||||||
|
.hero {
|
||||||
|
height: 70vh;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-img {
|
||||||
|
object-fit: cover;
|
||||||
|
width: 100%;
|
||||||
|
height: 75vh;
|
||||||
|
max-height: 800px;
|
||||||
|
filter: brightness(80%);
|
||||||
|
transition: transform 6s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-item.active .hero-img {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-caption {
|
||||||
|
bottom: 50%;
|
||||||
|
transform: translateY(50%);
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 40px 30px;
|
||||||
|
max-width: 700px;
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
|
||||||
|
animation: fadeInUp 1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
from { opacity: 0; transform: translate(-50%, 60%); }
|
||||||
|
to { opacity: 1; transform: translate(-50%, 50%); left: 50%; }
|
||||||
|
}
|
||||||
|
.hero-caption { left: 50%; transform: translate(-50%, 50%); position: absolute; }
|
||||||
|
|
||||||
|
|
||||||
|
.hero-caption h1 {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 4.5rem;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-shadow: 2px 4px 10px rgba(0,0,0,0.5);
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-caption p {
|
||||||
|
color: #f0f0f0;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====== CARDS PREMIUN ====== */
|
||||||
|
.card {
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
background: var(--card-bg);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||||
|
overflow: hidden;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover {
|
||||||
|
transform: translateY(-8px);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-img-top {
|
||||||
|
transition: transform 0.5s ease;
|
||||||
|
}
|
||||||
|
.card:hover .card-img-top {
|
||||||
|
transform: scale(1.08);
|
||||||
|
}
|
||||||
|
.card > div:first-child:not(.card-body) {
|
||||||
|
overflow: hidden; /* For zooming images */
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
color: var(--text-dark);
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====== BADGES ====== */
|
||||||
|
.badge {
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.badge.bg-success { background: var(--gradient-success) !important; }
|
||||||
|
.badge.bg-primary { background: var(--gradient-info) !important; }
|
||||||
|
.badge.bg-danger { background: var(--gradient-primary) !important; }
|
||||||
|
|
||||||
|
|
||||||
|
/* ====== BOTONES PREMIUN ====== */
|
||||||
|
.btn {
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px 24px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
border: none;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:active {
|
||||||
|
transform: scale(0.96);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary, .btn-danger, .btn-hero {
|
||||||
|
background: var(--gradient-primary) !important;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.btn-primary:hover, .btn-danger:hover, .btn-hero:hover {
|
||||||
|
box-shadow: 0 8px 20px rgba(176, 0, 0, 0.3);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success {
|
||||||
|
background: var(--gradient-success) !important;
|
||||||
|
}
|
||||||
|
.btn-success:hover {
|
||||||
|
box-shadow: 0 8px 20px rgba(46, 125, 50, 0.3);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-info {
|
||||||
|
background: var(--gradient-info) !important;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-primary, .btn-outline-danger {
|
||||||
|
background: transparent !important;
|
||||||
|
border: 2px solid var(--primary-color) !important;
|
||||||
|
color: var(--primary-color) !important;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.btn-outline-primary:hover, .btn-outline-danger:hover {
|
||||||
|
background: var(--gradient-primary) !important;
|
||||||
|
color: white !important;
|
||||||
|
box-shadow: 0 8px 20px rgba(176, 0, 0, 0.2);
|
||||||
|
border-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====== SECTIONS & CONTAINERS ====== */
|
||||||
|
.content {
|
||||||
|
animation: fadeIn 0.6s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====== FOOTER ====== */
|
||||||
|
.footer {
|
||||||
|
background: #1a1a2e; /* Admin sidebar color */
|
||||||
|
color: #a0aab5;
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px 0;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====== SPONSOR CAROUSEL ====== */
|
||||||
|
.sponsor-carousel-container {
|
||||||
|
background: rgba(255, 255, 255, 1);
|
||||||
|
padding: 20px 0;
|
||||||
|
border-bottom: 1px solid rgba(0,0,0,0.08);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sponsor-carousel-track {
|
||||||
|
display: flex;
|
||||||
|
width: max-content;
|
||||||
|
animation: scrollSponsors 40s linear infinite;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When only 1 or 2 items (no sponsors), center instead of animate */
|
||||||
|
.sponsor-carousel-track:has(.sponsor-item:only-of-type) {
|
||||||
|
width: 100%;
|
||||||
|
animation: none;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sponsor-item {
|
||||||
|
width: 200px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin: 0 30px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sponsor-item img {
|
||||||
|
max-height: 55px;
|
||||||
|
max-width: 150px;
|
||||||
|
filter: grayscale(50%);
|
||||||
|
opacity: 0.65;
|
||||||
|
transition: all 0.4s ease;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sponsor-item:hover img {
|
||||||
|
filter: grayscale(0%);
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes scrollSponsors {
|
||||||
|
0% { transform: translateX(0); }
|
||||||
|
100% { transform: translateX(-50%); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pause on hover */
|
||||||
|
.sponsor-carousel-container:hover .sponsor-carousel-track {
|
||||||
|
animation-play-state: paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====== MODAL PREMIUN ====== */
|
||||||
|
.modal-content {
|
||||||
|
border-radius: 16px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 25px 50px rgba(0,0,0,0.2);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.modal-header {
|
||||||
|
border-bottom: 1px solid rgba(0,0,0,0.05);
|
||||||
|
background: #f8f9fa;
|
||||||
|
}
|
||||||
|
.nav-tabs .nav-link {
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--text-muted);
|
||||||
|
border: none;
|
||||||
|
border-bottom: 3px solid transparent;
|
||||||
|
}
|
||||||
|
.nav-tabs .nav-link.active {
|
||||||
|
color: var(--primary-color);
|
||||||
|
border-bottom: 3px solid var(--primary-color);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====== RESPONSIVE ====== */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.navbar-brand img { height: 60px; }
|
||||||
|
.custom-navbar { padding: 10px; }
|
||||||
|
.hero-caption { width: 90%; padding: 25px 20px; }
|
||||||
|
.hero-caption h1 { font-size: 3rem; }
|
||||||
|
.card:hover { transform: none; } /* Disable heavy animations on mobile */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====== LOGO ====== */
|
||||||
|
.navbar-logo {
|
||||||
|
height: auto;
|
||||||
|
max-height: 80px;
|
||||||
|
transition: max-height 0.3s ease, filter 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======= MOBILE TABLE STACK ======= */
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.table-mobile-stack:not(.no-responsive) { display: block; width: 100%; overflow-x: hidden; }
|
||||||
|
.table-mobile-stack thead { display: none; }
|
||||||
|
.table-mobile-stack tr {
|
||||||
|
display: block; margin-bottom: 15px; background: #fff;
|
||||||
|
border-radius: var(--radius); padding: 15px; box-shadow: var(--shadow-sm);
|
||||||
|
}
|
||||||
|
.table-mobile-stack td {
|
||||||
|
display: flex; justify-content: space-between; text-align: right !important;
|
||||||
|
padding: 8px 0; border: none; align-items: center; border-bottom: 1px dashed #eee;
|
||||||
|
}
|
||||||
|
.table-mobile-stack td:last-child { border-bottom: none; }
|
||||||
|
.table-mobile-stack td::before {
|
||||||
|
content: attr(data-label); font-weight: 600; color: var(--text-muted); font-size: 0.85rem; text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======= SPLASH SCREEN (X STYLE) ======= */
|
||||||
|
#splash-screen {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #ffffff;
|
||||||
|
z-index: 99999;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
visibility 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#splash-logo {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
object-fit: contain;
|
||||||
|
animation: logo-entrance 1.2s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards,
|
||||||
|
logo-pulse 2s ease-in-out infinite 1.2s;
|
||||||
|
transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estado de salida */
|
||||||
|
.splash-hidden {
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.splash-hidden #splash-logo {
|
||||||
|
transform: scale(10); /* El logo se agranda para "perforar" la pantalla estilo X */
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes logo-entrance {
|
||||||
|
0% { opacity: 0; transform: scale(0.5); }
|
||||||
|
100% { opacity: 1; transform: scale(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes logo-pulse {
|
||||||
|
0%, 100% { transform: scale(1); }
|
||||||
|
50% { transform: scale(1.08); }
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/* OnAPB — Eventos
|
||||||
|
La vista usa kinetic-card + Bootstrap.
|
||||||
|
Este archivo conserva solo estilos de soporte. */
|
||||||
|
|
||||||
|
/* Barra de fechas horizontal — scroll sin scrollbar visible */
|
||||||
|
.no-scrollbar::-webkit-scrollbar { display: none; }
|
||||||
|
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
||||||
|
|
||||||
|
/* Pulse para badge "EN VIVO" */
|
||||||
|
.pulse { animation: pulse-badge 2s ease-in-out infinite; }
|
||||||
|
@keyframes pulse-badge {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
|
50% { opacity: 0.5; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Score display */
|
||||||
|
.match-score-display {
|
||||||
|
font-family: var(--font-display, 'Antonio', sans-serif);
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
color: var(--on-surface);
|
||||||
|
}
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
/* ════════════════════════════════
|
||||||
|
HOME / WELCOME — OnAPB
|
||||||
|
Construido sobre kinetic-arena.css
|
||||||
|
════════════════════════════════ */
|
||||||
|
|
||||||
|
/* ── Contenedor base ── */
|
||||||
|
.index-container {
|
||||||
|
max-width: 1100px;
|
||||||
|
margin: 40px auto;
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-container h2 {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
color: var(--primary);
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Hero / Carousel principal ── */
|
||||||
|
.hero-img {
|
||||||
|
object-fit: cover;
|
||||||
|
height: 70vh;
|
||||||
|
filter: brightness(70%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-caption h1 {
|
||||||
|
font-size: clamp(2.5rem, 7vw, 5rem);
|
||||||
|
text-shadow: 0 2px 12px rgba(0,0,0,0.5);
|
||||||
|
font-family: var(--font-display) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-caption p {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
text-shadow: 0 2px 8px rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-hero {
|
||||||
|
background: var(--primary);
|
||||||
|
color: #fff;
|
||||||
|
padding: 12px 28px;
|
||||||
|
border-radius: 0;
|
||||||
|
text-decoration: none;
|
||||||
|
font-family: var(--font-display) !important;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: background-color 0.25s, transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-hero:hover {
|
||||||
|
background: var(--primary-deep);
|
||||||
|
color: white;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Cards de eventos / promos ── */
|
||||||
|
.index-container .card {
|
||||||
|
border-radius: var(--radius);
|
||||||
|
border: 1px solid var(--outline-variant);
|
||||||
|
overflow: hidden;
|
||||||
|
transition: transform 0.22s ease, box-shadow 0.22s ease;
|
||||||
|
background: var(--surface-container-lowest);
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-container .card img {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-container .card:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-container .card-body {
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-container .card-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--on-surface);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-family: var(--font-display) !important;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-container .card-text {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--on-surface-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-container .badge {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-text i.bi {
|
||||||
|
color: var(--primary);
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Carousel de eventos (controles) ── */
|
||||||
|
#eventosCarousel {
|
||||||
|
position: relative;
|
||||||
|
padding: 0 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#eventosCarousel .carousel-item {
|
||||||
|
transition: transform 0.8s ease, opacity 0.5s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#eventosCarousel .carousel-control-prev,
|
||||||
|
#eventosCarousel .carousel-control-next {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background: rgba(26, 18, 8, 0.25);
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: background 0.25s;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
#eventosCarousel .carousel-control-prev:hover,
|
||||||
|
#eventosCarousel .carousel-control-next:hover {
|
||||||
|
background: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#eventosCarousel .carousel-control-prev { left: -52px; }
|
||||||
|
#eventosCarousel .carousel-control-next { right: -52px; }
|
||||||
|
|
||||||
|
/* ── Standings Widget ── */
|
||||||
|
#standingsCarousel {
|
||||||
|
min-height: 380px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.standings-header {
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#standingsCarousel .table td {
|
||||||
|
border-color: rgba(255,255,255,0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
#standingsCarousel .btn-outline-light:hover {
|
||||||
|
background: var(--primary);
|
||||||
|
border-color: var(--primary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Responsive ── */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
#eventosCarousel { padding: 0 16px; overflow: hidden; }
|
||||||
|
#eventosCarousel .carousel-control-prev { left: 6px; }
|
||||||
|
#eventosCarousel .carousel-control-next { right: 6px; }
|
||||||
|
|
||||||
|
.index-container .card img { height: 160px; }
|
||||||
|
.hero-caption h1 { font-size: 2rem; }
|
||||||
|
.hero-caption p { font-size: 0.95rem; }
|
||||||
|
.index-container h2 { font-size: 1.6rem; }
|
||||||
|
|
||||||
|
#standingsCarousel { min-height: auto; }
|
||||||
|
|
||||||
|
#promosCarousel .col-md-4,
|
||||||
|
#eventosCarousel .col-md-4 {
|
||||||
|
flex: 0 0 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.hero-img { height: 45vh; }
|
||||||
|
.index-container .card img { height: 140px; }
|
||||||
|
.index-container h2 { font-size: 1.4rem; }
|
||||||
|
.index-container .card-body { padding: 10px; }
|
||||||
|
|
||||||
|
#eventosCarousel .carousel-control-prev,
|
||||||
|
#eventosCarousel .carousel-control-next { display: none; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,569 @@
|
|||||||
|
/*
|
||||||
|
ONAPB — "Constructivismo de Cancha"
|
||||||
|
Design System v2.0 — 2026
|
||||||
|
─────────────────────────────────────
|
||||||
|
Display: Antonio (condensed, impactful)
|
||||||
|
Body: DM Sans (warm, clean, modern)
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Antonio:wght@400;500;600;700&family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&display=swap');
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
TOKENS
|
||||||
|
══════════════════════════════ */
|
||||||
|
:root {
|
||||||
|
/* Superficies — base cálida */
|
||||||
|
--surface: #f5f3ef;
|
||||||
|
--surface-bright: #faf9f6;
|
||||||
|
--surface-container-low: #ede9e2;
|
||||||
|
--surface-container: #e5e1d9;
|
||||||
|
--surface-container-high: #dbd5cc;
|
||||||
|
--surface-container-lowest: #ffffff;
|
||||||
|
--surface-dim: #c9c3b9;
|
||||||
|
|
||||||
|
/* Oscuro */
|
||||||
|
--arena-dark: #111111;
|
||||||
|
--arena-dark-surface: #1a1a18;
|
||||||
|
--arena-mid: #2c2c2a;
|
||||||
|
|
||||||
|
/* Primario — rojo carmesí */
|
||||||
|
--primary: #c20000;
|
||||||
|
--primary-deep: #8a0000;
|
||||||
|
--primary-container: #c20000;
|
||||||
|
--on-primary: #ffffff;
|
||||||
|
--on-primary-container: #ffe4e1;
|
||||||
|
|
||||||
|
/* Alias legacy (vistas antiguas) */
|
||||||
|
--primary-color: #c20000;
|
||||||
|
--primary-dark: #8a0000;
|
||||||
|
--bg-color: #f5f3ef;
|
||||||
|
--card-bg: #ffffff;
|
||||||
|
|
||||||
|
/* Texto */
|
||||||
|
--on-surface: #1a1208;
|
||||||
|
--on-surface-variant: #524840;
|
||||||
|
--on-surface-muted: #7a6e65;
|
||||||
|
--text-dark: #1a1208;
|
||||||
|
--text-muted: #7a6e65;
|
||||||
|
|
||||||
|
/* Bordes */
|
||||||
|
--outline: #9e8f85;
|
||||||
|
--outline-variant: rgba(158, 143, 133, 0.18);
|
||||||
|
|
||||||
|
/* Tipografía */
|
||||||
|
--font-display: 'Antonio', sans-serif;
|
||||||
|
--font-body: 'DM Sans', sans-serif;
|
||||||
|
|
||||||
|
/* Espaciado */
|
||||||
|
--spacing-1: 0.25rem;
|
||||||
|
--spacing-2: 0.5rem;
|
||||||
|
--spacing-3: 0.75rem;
|
||||||
|
--spacing-4: 1rem;
|
||||||
|
--spacing-6: 1.5rem;
|
||||||
|
--spacing-8: 2rem;
|
||||||
|
--spacing-12: 3rem;
|
||||||
|
--spacing-16: 4rem;
|
||||||
|
|
||||||
|
/* Radio — editorial, cuadrado */
|
||||||
|
--radius-sm: 2px;
|
||||||
|
--radius: 4px;
|
||||||
|
--radius-lg: 8px;
|
||||||
|
|
||||||
|
/* Sombras */
|
||||||
|
--shadow-sm: 0 2px 8px rgba(26, 18, 8, 0.07);
|
||||||
|
--shadow-lg: 0 8px 32px rgba(26, 18, 8, 0.12);
|
||||||
|
|
||||||
|
/* Gradients */
|
||||||
|
--gradient-primary: linear-gradient(135deg, #c20000, #8a0000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
BASE
|
||||||
|
══════════════════════════════ */
|
||||||
|
*, *::before, *::after { box-sizing: border-box; }
|
||||||
|
|
||||||
|
html { scroll-behavior: smooth; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--surface) !important;
|
||||||
|
color: var(--on-surface) !important;
|
||||||
|
font-family: var(--font-body) !important;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.65;
|
||||||
|
margin: 0;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
img, video, canvas, svg { max-width: 100%; height: auto; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
GRANO DE TEXTURA
|
||||||
|
══════════════════════════════ */
|
||||||
|
body::after {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='n' x='0' y='0'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='300' height='300' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E");
|
||||||
|
background-size: 250px 250px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 9998;
|
||||||
|
mix-blend-mode: multiply;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
TIPOGRAFÍA
|
||||||
|
══════════════════════════════ */
|
||||||
|
h1, h2, h3, h4, h5, h6,
|
||||||
|
.display-1, .display-2, .display-3,
|
||||||
|
.display-4, .display-5, .display-6,
|
||||||
|
.navbar-brand, .hero-title {
|
||||||
|
font-family: var(--font-display) !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
letter-spacing: 0.03em !important;
|
||||||
|
text-transform: uppercase;
|
||||||
|
line-height: 0.93 !important;
|
||||||
|
color: var(--on-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
p, td, li, .form-control, .form-label,
|
||||||
|
.nav-link, .dropdown-item, .btn,
|
||||||
|
body, small {
|
||||||
|
font-family: var(--font-body) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-family: var(--font-display) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
NAVBAR
|
||||||
|
══════════════════════════════ */
|
||||||
|
.custom-navbar {
|
||||||
|
background-color: rgba(245, 243, 239, 0.93) !important;
|
||||||
|
backdrop-filter: blur(20px) saturate(180%);
|
||||||
|
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
||||||
|
border-bottom: 1px solid var(--outline-variant) !important;
|
||||||
|
padding: 10px 0;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1030;
|
||||||
|
box-shadow: none !important;
|
||||||
|
transition: background 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-navbar .navbar-logo {
|
||||||
|
height: 36px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-navbar .navbar-brand {
|
||||||
|
font-family: var(--font-display) !important;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
color: var(--primary) !important;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
.custom-navbar .navbar-brand:hover { opacity: 0.8; }
|
||||||
|
|
||||||
|
/* Remove old underline effect */
|
||||||
|
.custom-navbar .nav-link::after { display: none !important; }
|
||||||
|
|
||||||
|
.custom-navbar .nav-link {
|
||||||
|
font-family: var(--font-body) !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
font-size: 0.8rem !important;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
color: var(--on-surface) !important;
|
||||||
|
padding: 6px 14px !important;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
transition: color 0.2s, background 0.2s;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-navbar .nav-link:hover,
|
||||||
|
.custom-navbar .nav-link.active {
|
||||||
|
color: var(--primary) !important;
|
||||||
|
background: rgba(194, 0, 0, 0.06) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-toggler {
|
||||||
|
border: 1px solid var(--outline-variant);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
padding: 6px 10px;
|
||||||
|
}
|
||||||
|
.navbar-toggler:focus { box-shadow: none; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
DROPDOWN
|
||||||
|
══════════════════════════════ */
|
||||||
|
.dropdown-menu {
|
||||||
|
background: var(--surface-container-lowest);
|
||||||
|
border: 1px solid var(--outline-variant);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
padding: 6px;
|
||||||
|
animation: menuIn 0.18s ease;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes menuIn {
|
||||||
|
from { opacity: 0; transform: translateY(-6px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item {
|
||||||
|
font-family: var(--font-body) !important;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
padding: 8px 14px;
|
||||||
|
color: var(--on-surface);
|
||||||
|
transition: background 0.15s, color 0.15s;
|
||||||
|
}
|
||||||
|
.dropdown-item:hover,
|
||||||
|
.dropdown-item:focus {
|
||||||
|
background: var(--surface-container);
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-divider { border-color: var(--outline-variant); }
|
||||||
|
.dropdown-header {
|
||||||
|
font-family: var(--font-body) !important;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
color: var(--on-surface-muted);
|
||||||
|
padding: 6px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
CARDS
|
||||||
|
══════════════════════════════ */
|
||||||
|
.kinetic-card {
|
||||||
|
background-color: var(--surface-container-lowest);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
border: 1px solid var(--outline-variant);
|
||||||
|
transition: transform 0.22s ease, box-shadow 0.22s ease, border-color 0.22s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kinetic-card:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
border-color: rgba(194, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
BOTONES
|
||||||
|
══════════════════════════════ */
|
||||||
|
.btn-kinetic-primary {
|
||||||
|
background-color: var(--primary) !important;
|
||||||
|
color: white !important;
|
||||||
|
border: none !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
padding: var(--spacing-3) var(--spacing-8) !important;
|
||||||
|
font-family: var(--font-display) !important;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1rem;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s, transform 0.18s;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-kinetic-primary:hover {
|
||||||
|
background-color: var(--primary-deep) !important;
|
||||||
|
color: white !important;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-kinetic-primary:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
HERO
|
||||||
|
══════════════════════════════ */
|
||||||
|
.hero-title {
|
||||||
|
font-family: var(--font-display) !important;
|
||||||
|
font-size: clamp(3.5rem, 11vw, 8rem);
|
||||||
|
line-height: 0.88 !important;
|
||||||
|
letter-spacing: 0.02em !important;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
margin-bottom: var(--spacing-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-caption {
|
||||||
|
background: transparent !important;
|
||||||
|
backdrop-filter: none !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clip-path diagonal en imagen hero */
|
||||||
|
.hero-clip {
|
||||||
|
clip-path: polygon(8% 0, 100% 0, 100% 100%, 0% 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
BADGES / TAGS
|
||||||
|
══════════════════════════════ */
|
||||||
|
.kinetic-tag-live {
|
||||||
|
background-color: var(--primary);
|
||||||
|
color: white;
|
||||||
|
padding: 3px 10px;
|
||||||
|
border-radius: 0;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
font-family: var(--font-body) !important;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kinetic-tag-live::before {
|
||||||
|
content: '';
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
animation: livePulse 1.3s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes livePulse {
|
||||||
|
0%, 100% { opacity: 1; transform: scale(1); }
|
||||||
|
50% { opacity: 0.3; transform: scale(0.65); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bootstrap badge override */
|
||||||
|
.badge {
|
||||||
|
border-radius: var(--radius-sm) !important;
|
||||||
|
font-family: var(--font-body) !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
ETIQUETAS DE SECCIÓN
|
||||||
|
══════════════════════════════ */
|
||||||
|
.section-label {
|
||||||
|
font-family: var(--font-body) !important;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.2em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--primary);
|
||||||
|
display: block;
|
||||||
|
margin-bottom: var(--spacing-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Acento izquierdo para secciones */
|
||||||
|
.accent-left {
|
||||||
|
border-left: 5px solid var(--primary);
|
||||||
|
padding-left: var(--spacing-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
LÍNEA DIVISORIA CON ACENTO
|
||||||
|
══════════════════════════════ */
|
||||||
|
.kinetic-divider {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-4);
|
||||||
|
margin: var(--spacing-8) 0;
|
||||||
|
}
|
||||||
|
.kinetic-divider::before,
|
||||||
|
.kinetic-divider::after {
|
||||||
|
content: '';
|
||||||
|
flex: 1;
|
||||||
|
height: 1px;
|
||||||
|
background: var(--outline-variant);
|
||||||
|
}
|
||||||
|
.kinetic-divider::before { background: var(--primary); max-width: 40px; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
ANIMACIONES AL SCROLL
|
||||||
|
══════════════════════════════ */
|
||||||
|
.animate-on-scroll {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
transition: opacity 0.55s cubic-bezier(0.25, 0.46, 0.45, 0.94),
|
||||||
|
transform 0.55s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-on-scroll.visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-on-scroll[data-delay="1"] { transition-delay: 0.08s; }
|
||||||
|
.animate-on-scroll[data-delay="2"] { transition-delay: 0.16s; }
|
||||||
|
.animate-on-scroll[data-delay="3"] { transition-delay: 0.24s; }
|
||||||
|
.animate-on-scroll[data-delay="4"] { transition-delay: 0.32s; }
|
||||||
|
.animate-on-scroll[data-delay="5"] { transition-delay: 0.40s; }
|
||||||
|
|
||||||
|
/* Variante: slide desde izquierda */
|
||||||
|
.animate-slide-left {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-20px);
|
||||||
|
transition: opacity 0.5s ease, transform 0.5s ease;
|
||||||
|
}
|
||||||
|
.animate-slide-left.visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
FORMULARIOS
|
||||||
|
══════════════════════════════ */
|
||||||
|
.form-control {
|
||||||
|
border: 1px solid var(--outline-variant);
|
||||||
|
border-radius: var(--radius-sm) !important;
|
||||||
|
background: var(--surface-container-lowest);
|
||||||
|
color: var(--on-surface);
|
||||||
|
font-family: var(--font-body) !important;
|
||||||
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus {
|
||||||
|
border-color: var(--primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(194, 0, 0, 0.1);
|
||||||
|
background: var(--surface-container-lowest);
|
||||||
|
color: var(--on-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
TABLAS
|
||||||
|
══════════════════════════════ */
|
||||||
|
.table {
|
||||||
|
font-family: var(--font-body) !important;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th {
|
||||||
|
font-family: var(--font-body) !important;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: var(--on-surface-muted);
|
||||||
|
border-bottom: 2px solid var(--outline-variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
NOTICIAS — ITEM
|
||||||
|
══════════════════════════════ */
|
||||||
|
.kinetic-news-item {
|
||||||
|
transition: background 0.2s;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
padding: var(--spacing-2) 0;
|
||||||
|
}
|
||||||
|
.kinetic-news-item:hover { background: var(--surface-container-low); }
|
||||||
|
|
||||||
|
.hover-primary:hover { color: var(--primary) !important; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
SPLASH SCREEN
|
||||||
|
══════════════════════════════ */
|
||||||
|
#splash-screen {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: var(--arena-dark);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 99999;
|
||||||
|
transition: opacity 0.4s ease, visibility 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#splash-screen.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#splash-logo {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
animation: splashPulse 0.8s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes splashPulse {
|
||||||
|
from { transform: scale(0.9); opacity: 0.7; }
|
||||||
|
to { transform: scale(1.05); opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
UTILIDADES
|
||||||
|
══════════════════════════════ */
|
||||||
|
.no-line { border: none !important; }
|
||||||
|
.no-scrollbar::-webkit-scrollbar { display: none; }
|
||||||
|
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
||||||
|
.text-kinetic-muted { color: var(--on-surface-muted) !important; }
|
||||||
|
.bg-primary-container { background-color: rgba(194, 0, 0, 0.1) !important; }
|
||||||
|
.text-primary { color: var(--primary) !important; }
|
||||||
|
|
||||||
|
.tracking-widest { letter-spacing: 0.2em; }
|
||||||
|
.line-clamp-2 {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
PULSE (badges en vivo)
|
||||||
|
══════════════════════════════ */
|
||||||
|
.pulse {
|
||||||
|
animation: badgePulse 2s infinite;
|
||||||
|
}
|
||||||
|
@keyframes badgePulse {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
|
50% { opacity: 0.5; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
FOOTER
|
||||||
|
══════════════════════════════ */
|
||||||
|
footer, .footer-section {
|
||||||
|
font-family: var(--font-body) !important;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
background: var(--arena-dark) !important;
|
||||||
|
color: rgba(255,255,255,0.6) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════
|
||||||
|
RESPONSIVE
|
||||||
|
══════════════════════════════ */
|
||||||
|
@media (max-width: 991px) {
|
||||||
|
.custom-navbar .navbar-collapse {
|
||||||
|
background: var(--surface-container-lowest);
|
||||||
|
border: 1px solid var(--outline-variant);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: var(--spacing-2);
|
||||||
|
margin-top: var(--spacing-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-navbar .nav-link {
|
||||||
|
padding: 10px 16px !important;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.hero-title {
|
||||||
|
font-size: clamp(3rem, 15vw, 5rem);
|
||||||
|
line-height: 0.9 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kinetic-card:hover {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,480 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
ONAPB — Kinetic FX
|
||||||
|
Capa de interacciones del frontend.
|
||||||
|
Vanilla JS, sin dependencias. Cargar al final de <body> con defer.
|
||||||
|
===================================================================== */
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
var hasFinePointer = window.matchMedia('(hover: hover) and (pointer: fine)').matches;
|
||||||
|
var isTouch = !hasFinePointer || ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
|
||||||
|
|
||||||
|
function ready(fn) {
|
||||||
|
if (document.readyState !== 'loading') fn();
|
||||||
|
else document.addEventListener('DOMContentLoaded', fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
1. Reveal on scroll (.kfx-reveal, [data-stagger], .kfx-section-tag)
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
function initReveal() {
|
||||||
|
if (!('IntersectionObserver' in window)) {
|
||||||
|
document.querySelectorAll('.kfx-reveal, [data-stagger], .kfx-section-tag, .hero-title, .kinetic-reveal-text, .kinetic-highlight')
|
||||||
|
.forEach(function (el) { el.classList.add('is-in'); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var io = new IntersectionObserver(function (entries) {
|
||||||
|
entries.forEach(function (e) {
|
||||||
|
if (e.isIntersecting) {
|
||||||
|
e.target.classList.add('is-in');
|
||||||
|
io.unobserve(e.target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, { threshold: 0.12, rootMargin: '0px 0px -60px 0px' });
|
||||||
|
|
||||||
|
document.querySelectorAll('.kfx-reveal, [data-stagger], .kfx-section-tag, .hero-title, .kinetic-reveal-text, .kinetic-highlight')
|
||||||
|
.forEach(function (el) { io.observe(el); });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
2. Word splitter para reveal de títulos (hero-title, kinetic-reveal-text)
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
function splitWords() {
|
||||||
|
document.querySelectorAll('.hero-title:not([data-split]), .kinetic-reveal-text:not([data-split])').forEach(function (el) {
|
||||||
|
if (el.children.length > 0 && el.querySelector('.word')) return;
|
||||||
|
var text = el.textContent.trim();
|
||||||
|
if (!text) return;
|
||||||
|
var words = text.split(/\s+/);
|
||||||
|
el.innerHTML = words.map(function (w) {
|
||||||
|
return '<span class="word"><span>' + w + '</span></span>';
|
||||||
|
}).join(' ');
|
||||||
|
el.setAttribute('data-split', 'true');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
3. Reading progress bar (top)
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
function initReadingBar() {
|
||||||
|
if (prefersReduced) return;
|
||||||
|
var bar = document.getElementById('kinetic-reading-bar');
|
||||||
|
if (!bar) {
|
||||||
|
bar = document.createElement('div');
|
||||||
|
bar.id = 'kinetic-reading-bar';
|
||||||
|
document.body.appendChild(bar);
|
||||||
|
}
|
||||||
|
var ticking = false;
|
||||||
|
function update() {
|
||||||
|
var scrollTop = window.scrollY || document.documentElement.scrollTop;
|
||||||
|
var docHeight = document.documentElement.scrollHeight - window.innerHeight;
|
||||||
|
var pct = docHeight > 0 ? Math.min(100, (scrollTop / docHeight) * 100) : 0;
|
||||||
|
bar.style.width = pct + '%';
|
||||||
|
ticking = false;
|
||||||
|
}
|
||||||
|
window.addEventListener('scroll', function () {
|
||||||
|
if (!ticking) {
|
||||||
|
window.requestAnimationFrame(update);
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
4. Navbar scroll state
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
function initNavbarScroll() {
|
||||||
|
var nav = document.querySelector('.custom-navbar');
|
||||||
|
if (!nav) return;
|
||||||
|
function update() {
|
||||||
|
if (window.scrollY > 30) nav.classList.add('is-scrolled');
|
||||||
|
else nav.classList.remove('is-scrolled');
|
||||||
|
}
|
||||||
|
window.addEventListener('scroll', update, { passive: true });
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
5. Spotlight para .kinetic-card y .kfx-img-spotlight
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
function initSpotlight() {
|
||||||
|
if (isTouch) return;
|
||||||
|
var els = document.querySelectorAll('.kinetic-card, .kfx-img-spotlight');
|
||||||
|
els.forEach(function (el) {
|
||||||
|
el.addEventListener('mousemove', function (e) {
|
||||||
|
var rect = el.getBoundingClientRect();
|
||||||
|
var x = ((e.clientX - rect.left) / rect.width) * 100;
|
||||||
|
var y = ((e.clientY - rect.top) / rect.height) * 100;
|
||||||
|
el.style.setProperty('--mx', x + '%');
|
||||||
|
el.style.setProperty('--my', y + '%');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
6. Tilt 3D (data-tilt)
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
function initTilt() {
|
||||||
|
if (isTouch || prefersReduced) return;
|
||||||
|
document.querySelectorAll('[data-tilt]').forEach(function (el) {
|
||||||
|
var max = parseFloat(el.getAttribute('data-tilt-max') || '8');
|
||||||
|
el.addEventListener('mousemove', function (e) {
|
||||||
|
var rect = el.getBoundingClientRect();
|
||||||
|
var x = (e.clientX - rect.left) / rect.width - 0.5;
|
||||||
|
var y = (e.clientY - rect.top) / rect.height - 0.5;
|
||||||
|
el.classList.add('is-tilting');
|
||||||
|
el.style.setProperty('--ry', (x * max) + 'deg');
|
||||||
|
el.style.setProperty('--rx', (-y * max) + 'deg');
|
||||||
|
});
|
||||||
|
el.addEventListener('mouseleave', function () {
|
||||||
|
el.classList.remove('is-tilting');
|
||||||
|
el.style.setProperty('--rx', '0deg');
|
||||||
|
el.style.setProperty('--ry', '0deg');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
7. Magnetic buttons (data-magnetic)
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
function initMagnetic() {
|
||||||
|
if (isTouch || prefersReduced) return;
|
||||||
|
document.querySelectorAll('[data-magnetic]').forEach(function (el) {
|
||||||
|
var strength = parseFloat(el.getAttribute('data-magnetic-strength') || '0.25');
|
||||||
|
el.addEventListener('mousemove', function (e) {
|
||||||
|
var rect = el.getBoundingClientRect();
|
||||||
|
var x = e.clientX - rect.left - rect.width / 2;
|
||||||
|
var y = e.clientY - rect.top - rect.height / 2;
|
||||||
|
el.style.transform = 'translate(' + (x * strength) + 'px, ' + (y * strength) + 'px)';
|
||||||
|
});
|
||||||
|
el.addEventListener('mouseleave', function () {
|
||||||
|
el.style.transform = '';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
8. Ripple en clicks (cualquier .btn, .btn-kinetic-primary)
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
function initRipple() {
|
||||||
|
document.addEventListener('click', function (e) {
|
||||||
|
var btn = e.target.closest('.btn, .btn-kinetic-primary, .btn-admin-primary, .sidebar-link');
|
||||||
|
if (!btn || btn.classList.contains('no-ripple')) return;
|
||||||
|
var rect = btn.getBoundingClientRect();
|
||||||
|
var size = Math.max(rect.width, rect.height);
|
||||||
|
var wave = document.createElement('span');
|
||||||
|
wave.className = 'kinetic-ripple-wave';
|
||||||
|
wave.style.width = wave.style.height = size + 'px';
|
||||||
|
wave.style.left = (e.clientX - rect.left) + 'px';
|
||||||
|
wave.style.top = (e.clientY - rect.top) + 'px';
|
||||||
|
// Posición relativa para contener el ripple
|
||||||
|
var pos = window.getComputedStyle(btn).position;
|
||||||
|
if (pos === 'static') btn.style.position = 'relative';
|
||||||
|
btn.style.overflow = 'hidden';
|
||||||
|
btn.appendChild(wave);
|
||||||
|
setTimeout(function () { wave.remove(); }, 650);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
9. Counter animado (.kfx-counter con data-target)
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
function initCounters() {
|
||||||
|
if (!('IntersectionObserver' in window)) return;
|
||||||
|
var counters = document.querySelectorAll('.kfx-counter[data-target]');
|
||||||
|
if (!counters.length) return;
|
||||||
|
var io = new IntersectionObserver(function (entries) {
|
||||||
|
entries.forEach(function (entry) {
|
||||||
|
if (!entry.isIntersecting) return;
|
||||||
|
var el = entry.target;
|
||||||
|
var target = parseFloat(el.getAttribute('data-target')) || 0;
|
||||||
|
var duration = parseInt(el.getAttribute('data-duration') || '1400', 10);
|
||||||
|
var decimals = parseInt(el.getAttribute('data-decimals') || '0', 10);
|
||||||
|
var prefix = el.getAttribute('data-prefix') || '';
|
||||||
|
var suffix = el.getAttribute('data-suffix') || '';
|
||||||
|
var start = 0;
|
||||||
|
var startTime = null;
|
||||||
|
if (prefersReduced) {
|
||||||
|
el.textContent = prefix + target.toFixed(decimals) + suffix;
|
||||||
|
io.unobserve(el);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
function tick(ts) {
|
||||||
|
if (!startTime) startTime = ts;
|
||||||
|
var progress = Math.min(1, (ts - startTime) / duration);
|
||||||
|
var eased = 1 - Math.pow(1 - progress, 3); // easeOutCubic
|
||||||
|
var current = start + (target - start) * eased;
|
||||||
|
el.textContent = prefix + current.toFixed(decimals) + suffix;
|
||||||
|
if (progress < 1) requestAnimationFrame(tick);
|
||||||
|
}
|
||||||
|
requestAnimationFrame(tick);
|
||||||
|
io.unobserve(el);
|
||||||
|
});
|
||||||
|
}, { threshold: 0.4 });
|
||||||
|
counters.forEach(function (c) { io.observe(c); });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
10. Parallax suave (data-parallax="speed")
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
function initParallax() {
|
||||||
|
if (prefersReduced) return;
|
||||||
|
var els = Array.prototype.slice.call(document.querySelectorAll('[data-parallax]'));
|
||||||
|
if (!els.length) return;
|
||||||
|
var ticking = false;
|
||||||
|
function update() {
|
||||||
|
var vh = window.innerHeight;
|
||||||
|
els.forEach(function (el) {
|
||||||
|
var rect = el.getBoundingClientRect();
|
||||||
|
if (rect.bottom < 0 || rect.top > vh) return;
|
||||||
|
var speed = parseFloat(el.getAttribute('data-parallax')) || 0.2;
|
||||||
|
var offset = (rect.top - vh / 2) * speed * -1;
|
||||||
|
el.style.transform = 'translate3d(0, ' + offset.toFixed(1) + 'px, 0)';
|
||||||
|
});
|
||||||
|
ticking = false;
|
||||||
|
}
|
||||||
|
window.addEventListener('scroll', function () {
|
||||||
|
if (!ticking) {
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
11. Marquee duplicador (.kfx-marquee con un solo track)
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
function initMarquee() {
|
||||||
|
document.querySelectorAll('.kfx-marquee').forEach(function (el) {
|
||||||
|
var track = el.querySelector('.kfx-marquee__track');
|
||||||
|
if (!track || track.dataset.cloned === '1') return;
|
||||||
|
track.dataset.cloned = '1';
|
||||||
|
track.innerHTML += track.innerHTML; // duplicar para loop seamless
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
12. Cursor personalizado (sólo desktop hover-fino)
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
function initCustomCursor() {
|
||||||
|
if (!hasFinePointer || isTouch || prefersReduced) return;
|
||||||
|
if (document.querySelector('.kinetic-cursor-dot')) return;
|
||||||
|
|
||||||
|
var dot = document.createElement('div');
|
||||||
|
var ring = document.createElement('div');
|
||||||
|
dot.className = 'kinetic-cursor-dot';
|
||||||
|
ring.className = 'kinetic-cursor-ring';
|
||||||
|
document.body.appendChild(dot);
|
||||||
|
document.body.appendChild(ring);
|
||||||
|
document.body.classList.add('kinetic-cursor-on');
|
||||||
|
|
||||||
|
var mx = window.innerWidth / 2, my = window.innerHeight / 2;
|
||||||
|
var rx = mx, ry = my;
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', function (e) {
|
||||||
|
mx = e.clientX; my = e.clientY;
|
||||||
|
dot.style.transform = 'translate(' + mx + 'px, ' + my + 'px) translate(-50%, -50%)';
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
function loop() {
|
||||||
|
rx += (mx - rx) * 0.18;
|
||||||
|
ry += (my - ry) * 0.18;
|
||||||
|
ring.style.transform = 'translate(' + rx + 'px, ' + ry + 'px) translate(-50%, -50%)';
|
||||||
|
requestAnimationFrame(loop);
|
||||||
|
}
|
||||||
|
loop();
|
||||||
|
|
||||||
|
// Hover en interactivos
|
||||||
|
var hoverSel = 'a, button, .btn, .kinetic-card, [role="button"], [data-tilt], [data-magnetic], .nav-link, input[type="submit"]';
|
||||||
|
document.addEventListener('mouseover', function (e) {
|
||||||
|
if (e.target.closest(hoverSel)) {
|
||||||
|
ring.classList.add('is-hover');
|
||||||
|
dot.classList.add('is-hover');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.addEventListener('mouseout', function (e) {
|
||||||
|
if (e.target.closest(hoverSel)) {
|
||||||
|
ring.classList.remove('is-hover');
|
||||||
|
dot.classList.remove('is-hover');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.addEventListener('mouseleave', function () {
|
||||||
|
ring.style.opacity = '0';
|
||||||
|
dot.style.opacity = '0';
|
||||||
|
});
|
||||||
|
document.addEventListener('mouseenter', function () {
|
||||||
|
ring.style.opacity = '1';
|
||||||
|
dot.style.opacity = '1';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
13. Auto-aplicar reveal a tarjetas / encabezados (best-effort)
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
function autoTagReveal() {
|
||||||
|
// Tarjetas comunes en home/eventos
|
||||||
|
document.querySelectorAll('.kinetic-card:not(.kfx-reveal)').forEach(function (el) {
|
||||||
|
el.classList.add('kfx-reveal');
|
||||||
|
if (!el.hasAttribute('data-fx')) el.setAttribute('data-fx', 'up');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Display headings (h1.display-* / h2.display-*)
|
||||||
|
document.querySelectorAll('h1.display-1, h1.display-2, h1.display-3, h1.display-4, h2.display-3, h2.display-4, h2.display-5')
|
||||||
|
.forEach(function (el) {
|
||||||
|
if (!el.classList.contains('kfx-reveal') && !el.classList.contains('hero-title')) {
|
||||||
|
el.classList.add('kfx-reveal');
|
||||||
|
el.setAttribute('data-fx', 'up');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
14. Smooth anchor scroll
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
function initAnchorSmooth() {
|
||||||
|
document.addEventListener('click', function (e) {
|
||||||
|
var a = e.target.closest('a[href^="#"]');
|
||||||
|
if (!a) return;
|
||||||
|
var href = a.getAttribute('href');
|
||||||
|
if (!href || href === '#' || href.length < 2) return;
|
||||||
|
var target = document.querySelector(href);
|
||||||
|
if (!target) return;
|
||||||
|
e.preventDefault();
|
||||||
|
target.scrollIntoView({ behavior: prefersReduced ? 'auto' : 'smooth', block: 'start' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
15. SweetAlert2 patcher (a prueba de cache)
|
||||||
|
Detecta cuando aparece un .swal2-popup y fuerza inline styles en
|
||||||
|
los botones para garantizar fondo rojo en confirm y gris en cancel,
|
||||||
|
sin importar el orden/cache del CSS o el confirmButtonColor del JS.
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
function initSwalPatcher() {
|
||||||
|
if (!('MutationObserver' in window)) return;
|
||||||
|
|
||||||
|
var BRAND_RED = '#c20000';
|
||||||
|
var BRAND_RED_DEEP = '#8a0000';
|
||||||
|
var BRAND_GRAY = '#6c757d';
|
||||||
|
|
||||||
|
// Inyectar reglas críticas como <style> en runtime para evitar
|
||||||
|
// problemas de caché del archivo .css (LiteSpeed/Cloudflare).
|
||||||
|
if (!document.getElementById('kfx-swal-runtime-style')) {
|
||||||
|
var s = document.createElement('style');
|
||||||
|
s.id = 'kfx-swal-runtime-style';
|
||||||
|
s.textContent = [
|
||||||
|
/* Selección visible dentro del modal SweetAlert (rojo + texto blanco). */
|
||||||
|
'.swal2-popup ::selection,.swal2-popup *::selection{background-color:' + BRAND_RED + ' !important;color:#fff !important;}',
|
||||||
|
'.swal2-popup ::-moz-selection,.swal2-popup *::-moz-selection{background-color:' + BRAND_RED + ' !important;color:#fff !important;}',
|
||||||
|
/* Selección global (failsafe por si el CSS principal cachea viejo). */
|
||||||
|
'::selection{background-color:' + BRAND_RED + ' !important;color:#fff !important;}',
|
||||||
|
'::-moz-selection{background-color:' + BRAND_RED + ' !important;color:#fff !important;}',
|
||||||
|
/* Botones de SweetAlert (failsafe). */
|
||||||
|
'body .swal2-popup .swal2-actions .swal2-styled.swal2-confirm{' +
|
||||||
|
'background-color:' + BRAND_RED + ' !important;' +
|
||||||
|
'background-image:linear-gradient(135deg,' + BRAND_RED + ',' + BRAND_RED_DEEP + ') !important;' +
|
||||||
|
'color:#fff !important;border:none !important;' +
|
||||||
|
'}',
|
||||||
|
'body .swal2-popup .swal2-actions .swal2-styled.swal2-cancel{' +
|
||||||
|
'background-color:' + BRAND_GRAY + ' !important;' +
|
||||||
|
'background-image:none !important;' +
|
||||||
|
'color:#fff !important;border:none !important;' +
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
document.head.appendChild(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
function styleConfirm(btn) {
|
||||||
|
if (!btn) return;
|
||||||
|
btn.style.setProperty('background-color', BRAND_RED, 'important');
|
||||||
|
btn.style.setProperty('background-image',
|
||||||
|
'linear-gradient(135deg, ' + BRAND_RED + ', ' + BRAND_RED_DEEP + ')', 'important');
|
||||||
|
btn.style.setProperty('color', '#ffffff', 'important');
|
||||||
|
btn.style.setProperty('border', 'none', 'important');
|
||||||
|
btn.style.setProperty('text-shadow', 'none', 'important');
|
||||||
|
btn.style.setProperty('font-weight', '700', 'important');
|
||||||
|
btn.style.setProperty('text-transform', 'uppercase', 'important');
|
||||||
|
btn.style.setProperty('letter-spacing', '0.08em', 'important');
|
||||||
|
}
|
||||||
|
function styleCancel(btn) {
|
||||||
|
if (!btn) return;
|
||||||
|
btn.style.setProperty('background-color', BRAND_GRAY, 'important');
|
||||||
|
btn.style.setProperty('background-image', 'none', 'important');
|
||||||
|
btn.style.setProperty('color', '#ffffff', 'important');
|
||||||
|
btn.style.setProperty('border', 'none', 'important');
|
||||||
|
btn.style.setProperty('font-weight', '700', 'important');
|
||||||
|
btn.style.setProperty('text-transform', 'uppercase', 'important');
|
||||||
|
btn.style.setProperty('letter-spacing', '0.08em', 'important');
|
||||||
|
}
|
||||||
|
function styleDeny(btn) {
|
||||||
|
if (!btn) return;
|
||||||
|
btn.style.setProperty('background-color', '#b91c1c', 'important');
|
||||||
|
btn.style.setProperty('color', '#ffffff', 'important');
|
||||||
|
}
|
||||||
|
|
||||||
|
function patch(popup) {
|
||||||
|
if (!popup || popup.dataset.kfxPatched === '1') return;
|
||||||
|
popup.dataset.kfxPatched = '1';
|
||||||
|
styleConfirm(popup.querySelector('.swal2-confirm'));
|
||||||
|
styleCancel(popup.querySelector('.swal2-cancel'));
|
||||||
|
styleDeny(popup.querySelector('.swal2-deny'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch popups que ya estén en el DOM al cargar
|
||||||
|
document.querySelectorAll('.swal2-popup').forEach(patch);
|
||||||
|
|
||||||
|
// Observa nuevos popups (SWAL los agrega/quita dinámicamente)
|
||||||
|
var obs = new MutationObserver(function (mutations) {
|
||||||
|
mutations.forEach(function (m) {
|
||||||
|
m.addedNodes && m.addedNodes.forEach(function (n) {
|
||||||
|
if (!(n instanceof HTMLElement)) return;
|
||||||
|
if (n.classList && n.classList.contains('swal2-popup')) {
|
||||||
|
patch(n);
|
||||||
|
} else {
|
||||||
|
// Por si el contenedor agrega un wrapper antes del popup
|
||||||
|
var inner = n.querySelector && n.querySelector('.swal2-popup');
|
||||||
|
if (inner) patch(inner);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
obs.observe(document.body, { childList: true, subtree: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────
|
||||||
|
Init
|
||||||
|
───────────────────────────────────────────────────────────────── */
|
||||||
|
ready(function () {
|
||||||
|
try { splitWords(); } catch (e) { console.warn('kinetic-fx splitWords:', e); }
|
||||||
|
try { autoTagReveal(); } catch (e) { console.warn('kinetic-fx autoTagReveal:', e); }
|
||||||
|
try { initReveal(); } catch (e) { console.warn('kinetic-fx reveal:', e); }
|
||||||
|
try { initReadingBar(); } catch (e) { console.warn('kinetic-fx readingBar:', e); }
|
||||||
|
try { initNavbarScroll(); } catch (e) { console.warn('kinetic-fx navbarScroll:', e); }
|
||||||
|
try { initSpotlight(); } catch (e) { console.warn('kinetic-fx spotlight:', e); }
|
||||||
|
try { initTilt(); } catch (e) { console.warn('kinetic-fx tilt:', e); }
|
||||||
|
try { initMagnetic(); } catch (e) { console.warn('kinetic-fx magnetic:', e); }
|
||||||
|
try { initRipple(); } catch (e) { console.warn('kinetic-fx ripple:', e); }
|
||||||
|
try { initCounters(); } catch (e) { console.warn('kinetic-fx counters:', e); }
|
||||||
|
try { initParallax(); } catch (e) { console.warn('kinetic-fx parallax:', e); }
|
||||||
|
try { initMarquee(); } catch (e) { console.warn('kinetic-fx marquee:', e); }
|
||||||
|
try { initCustomCursor(); } catch (e) { console.warn('kinetic-fx cursor:', e); }
|
||||||
|
try { initAnchorSmooth(); } catch (e) { console.warn('kinetic-fx anchor:', e); }
|
||||||
|
try { initSwalPatcher(); } catch (e) { console.warn('kinetic-fx swalPatcher:', e); }
|
||||||
|
});
|
||||||
|
|
||||||
|
// API pública mínima (por si una vista necesita re-inicializar tras AJAX)
|
||||||
|
window.KineticFX = {
|
||||||
|
rescan: function () {
|
||||||
|
splitWords();
|
||||||
|
autoTagReveal();
|
||||||
|
initReveal();
|
||||||
|
initSpotlight();
|
||||||
|
initTilt();
|
||||||
|
initMagnetic();
|
||||||
|
initCounters();
|
||||||
|
initMarquee();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
/* OnAPB — Lugares / Promociones
|
||||||
|
Actualizado para usar el sistema de diseño kinetic-arena.css */
|
||||||
|
|
||||||
|
.lugares-container {
|
||||||
|
max-width: 1100px;
|
||||||
|
margin: 40px auto;
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lugares-container h1 {
|
||||||
|
font-size: 2.4rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--primary);
|
||||||
|
margin-bottom: 15px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lugares-container p {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--on-surface-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Barra de filtro */
|
||||||
|
.filter-bar {
|
||||||
|
background: var(--surface-container-low);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
border: 1px solid var(--outline-variant);
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar label {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar select {
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Container de promo (solo para filtrado, no visual) */
|
||||||
|
.promo-card {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Detalle */
|
||||||
|
#promo-detail {
|
||||||
|
border-radius: var(--radius);
|
||||||
|
background: var(--surface-container-lowest);
|
||||||
|
border: 1px solid var(--outline-variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
#promo-detail h3 {
|
||||||
|
color: var(--primary);
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#promo-detail p {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--on-surface-muted);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#promo-detail a {
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 8px 14px;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
border: 1px solid var(--primary);
|
||||||
|
color: var(--primary);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#promo-detail a:hover {
|
||||||
|
background: var(--primary);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mapa */
|
||||||
|
.map-container {
|
||||||
|
height: 450px;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
margin-top: 30px;
|
||||||
|
border: 1px solid var(--outline-variant);
|
||||||
|
box-shadow: var(--shadow-sm, 0 2px 8px rgba(0,0,0,0.08));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mapa fluido sin desbordes */
|
||||||
|
.map-container, #map { width: 100%; max-width: 100%; }
|
||||||
|
.map-container { overflow: hidden; }
|
||||||
|
#map, .leaflet-container { width: 100% !important; }
|
||||||
|
|
||||||
|
/* Pines marcador */
|
||||||
|
.custom-pin .pin {
|
||||||
|
position: relative;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
background: gray;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
|
||||||
|
--pin-color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-pin .pin i {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
font-size: 16px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-pin .pin::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: -7px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-left: 6px solid transparent;
|
||||||
|
border-right: 6px solid transparent;
|
||||||
|
border-top: 10px solid;
|
||||||
|
border-top-color: var(--pin-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.lugares-container h1 { font-size: 2rem; }
|
||||||
|
.map-container { height: 350px; }
|
||||||
|
.lugares-container { padding-left: 16px; padding-right: 16px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.lugares-container h1 { font-size: 1.6rem; }
|
||||||
|
.map-container { height: 300px; }
|
||||||
|
.map-container, #map { width: 100%; }
|
||||||
|
|
||||||
|
.custom-pin .pin {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
}
|
||||||
|
.custom-pin .pin i { font-size: 14px; }
|
||||||
|
.custom-pin .pin::after {
|
||||||
|
bottom: -6px;
|
||||||
|
border-left: 5px solid transparent;
|
||||||
|
border-right: 5px solid transparent;
|
||||||
|
border-top: 8px solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
const CACHE_NAME = 'onapb-v4';
|
||||||
|
|
||||||
|
// Recursos estáticos que se cachean en la instalación
|
||||||
|
const STATIC_ASSETS = [
|
||||||
|
'/',
|
||||||
|
'/eventos',
|
||||||
|
'/torneos',
|
||||||
|
'/offline',
|
||||||
|
'/manifest.json',
|
||||||
|
];
|
||||||
|
|
||||||
|
// ── Install: pre-cachear recursos estáticos ──
|
||||||
|
self.addEventListener('install', (event) => {
|
||||||
|
self.skipWaiting();
|
||||||
|
event.waitUntil(
|
||||||
|
caches.open(CACHE_NAME).then((cache) => {
|
||||||
|
return cache.addAll(STATIC_ASSETS).catch(() => {
|
||||||
|
// Si algún recurso falla, no bloquear la instalación
|
||||||
|
console.warn('[SW] Algún recurso estático no pudo cachearse.');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Activate: limpiar caches viejas ──
|
||||||
|
self.addEventListener('activate', (event) => {
|
||||||
|
event.waitUntil(
|
||||||
|
caches.keys().then((keys) =>
|
||||||
|
Promise.all(
|
||||||
|
keys
|
||||||
|
.filter((key) => key !== CACHE_NAME)
|
||||||
|
.map((key) => caches.delete(key))
|
||||||
|
)
|
||||||
|
).then(() => self.clients.claim())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Fetch: Network-first para páginas, Cache-first para assets ──
|
||||||
|
self.addEventListener('fetch', (event) => {
|
||||||
|
const { request } = event;
|
||||||
|
const url = new URL(request.url);
|
||||||
|
|
||||||
|
// Solo manejar peticiones del mismo origen
|
||||||
|
if (url.origin !== location.origin) return;
|
||||||
|
|
||||||
|
// Ignorar peticiones POST, PUT, DELETE, admin, notificaciones en tiempo real
|
||||||
|
if (request.method !== 'GET') return;
|
||||||
|
if (url.pathname.startsWith('/admin')) return;
|
||||||
|
if (url.pathname.startsWith('/notificaciones/count')) return;
|
||||||
|
|
||||||
|
// Assets estáticos (JS, CSS, imágenes, fuentes): Cache-first
|
||||||
|
if (
|
||||||
|
url.pathname.match(/\.(js|css|png|jpg|jpeg|gif|svg|webp|woff2?|ico)$/)
|
||||||
|
) {
|
||||||
|
event.respondWith(
|
||||||
|
caches.match(request).then((cached) => {
|
||||||
|
if (cached) return cached;
|
||||||
|
return fetch(request).then((response) => {
|
||||||
|
if (response && response.status === 200) {
|
||||||
|
const clone = response.clone();
|
||||||
|
caches.open(CACHE_NAME).then((cache) => cache.put(request, clone));
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Páginas HTML: Network-first, fallback a cache, luego /offline
|
||||||
|
event.respondWith(
|
||||||
|
fetch(request)
|
||||||
|
.then((response) => {
|
||||||
|
if (response && response.status === 200) {
|
||||||
|
const clone = response.clone();
|
||||||
|
caches.open(CACHE_NAME).then((cache) => cache.put(request, clone));
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.catch(() =>
|
||||||
|
caches.match(request).then(
|
||||||
|
(cached) => cached || caches.match('/offline')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Web Push Notifications ──
|
||||||
|
self.addEventListener('push', (event) => {
|
||||||
|
let data = { title: 'OnAPB', body: 'Tenés una nueva notificación' };
|
||||||
|
|
||||||
|
if (event.data) {
|
||||||
|
try {
|
||||||
|
data = event.data.json();
|
||||||
|
} catch (e) {
|
||||||
|
data.body = event.data.text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
body: data.body,
|
||||||
|
icon: '/icons/icon-192.png?v=4',
|
||||||
|
badge: '/icons/icon-72.png?v=4',
|
||||||
|
data: {
|
||||||
|
url: data.url || '/'
|
||||||
|
},
|
||||||
|
vibrate: [100, 50, 100]
|
||||||
|
};
|
||||||
|
|
||||||
|
event.waitUntil(
|
||||||
|
self.registration.showNotification(data.title, options)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener('notificationclick', (event) => {
|
||||||
|
event.notification.close();
|
||||||
|
const targetUrl = event.notification.data.url || '/';
|
||||||
|
|
||||||
|
event.waitUntil(
|
||||||
|
clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {
|
||||||
|
// Si ya hay una ventana abierta con la misma URL, enfocarla
|
||||||
|
for (const client of clientList) {
|
||||||
|
if (client.url === targetUrl && 'focus' in client) {
|
||||||
|
return client.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Si no, abrir una nueva
|
||||||
|
if (clients.openWindow) {
|
||||||
|
return clients.openWindow(targetUrl);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||