🚀 Patch auto

This commit is contained in:
karim hassan
2025-08-24 23:00:40 +00:00
parent 92e6afff00
commit f1103d67a0
1314 changed files with 2511 additions and 562 deletions

View File

@@ -0,0 +1,160 @@
import {
bindLoginForm, isAuthenticated, logout,
createTournament, addParticipant, scoreMatch,
listTournaments, listMatches
} from '/assets/api.js?v=10';
const toast = document.getElementById('toast');
const adminSection = document.getElementById('adminSection');
const loginSection = document.getElementById('loginSection');
function showToast(msg, ok=true){
toast.textContent = msg;
toast.style.borderColor = ok ? 'rgba(143,237,255,.45)' : 'rgba(255,120,120,.45)';
toast.classList.add('show');
setTimeout(()=>toast.classList.remove('show'), 2200);
}
function toggleAdminUI(){
const logged = isAuthenticated();
adminSection.classList.toggle('hide', !logged);
loginSection.classList.toggle('hide', logged);
}
function disable(btn, spin, yes){
btn.disabled = !!yes;
spin.classList.toggle('hide', !yes);
}
async function renderTournaments(){
const wrap = document.getElementById('listTournaments');
const p_tid = document.getElementById('p_tid');
const s_tid = document.getElementById('s_tid');
try{
const ts = await listTournaments();
wrap.innerHTML = ts.map(t => `<div class="card"><strong>#${t.id}</strong> — ${t.name} <span class="muted">(${[t.location||'—', t.start_date].filter(Boolean).join(' • ')})</span></div>`).join('') || '<div class="empty">Aucun tournoi.</div>';
const opts = ['<option value="">— choisir un tournoi —</option>'].concat(
ts.map(t => `<option value="${t.id}">#${t.id}${t.name}</option>`)
).join('');
p_tid.innerHTML = opts;
s_tid.innerHTML = opts;
}catch(e){
wrap.innerHTML = `<div class="empty">Erreur chargement: ${e.message}</div>`;
}
}
async function renderMatchesForSelectedTournament(){
const tid = Number(document.getElementById('s_tid').value || '0');
const s_mid = document.getElementById('s_mid');
if(!tid){ s_mid.innerHTML = '<option value="">— choisir un match —</option>'; return; }
try{
const ms = await listMatches(tid);
s_mid.innerHTML = ['<option value="">— choisir un match —</option>'].concat(
ms.map(m => `<option value="${m.id}">#${m.id}${m.team_a} vs ${m.team_b} (${m.court||'—'})</option>`)
).join('');
}catch(e){
s_mid.innerHTML = '<option value="">Erreur chargement matches</option>';
}
}
function bindAuth(){
bindLoginForm({});
const logoutBtn = document.getElementById('logoutBtn');
logoutBtn?.addEventListener('click', ()=>{
logout(); toggleAdminUI(); showToast('Déconnecté.');
});
}
function bindAdminActions(){
document.getElementById('createTournamentBtn')?.addEventListener('click', async (e)=>{
const btn = e.currentTarget;
const spin = document.getElementById('createTournamentSpin');
const err = document.getElementById('createTournamentErr');
err.textContent='';
const name = document.getElementById('t_name').value.trim();
if(!name){ err.textContent='Le nom est requis.'; return; }
const payload = {
name,
location: document.getElementById('t_location').value.trim(),
start_date: document.getElementById('t_start').value || null,
end_date: document.getElementById('t_end').value || null
};
try{
disable(btn, spin, true);
await createTournament(payload);
showToast('Tournoi créé ✔');
document.getElementById('t_name').value='';
document.getElementById('t_location').value='';
document.getElementById('t_start').value='';
document.getElementById('t_end').value='';
renderTournaments();
}catch(e){
err.textContent = e?.payload?.error || e.message;
showToast('Erreur création tournoi', false);
}finally{
disable(btn, spin, false);
}
});
document.getElementById('addParticipantBtn')?.addEventListener('click', async (e)=>{
const btn = e.currentTarget;
const spin = document.getElementById('addParticipantSpin');
const err = document.getElementById('addParticipantErr');
err.textContent='';
const tid = Number(document.getElementById('p_tid').value || '0');
const full_name = document.getElementById('p_fullname').value.trim();
if(!tid){ err.textContent='Choisis un tournoi.'; return; }
if(!full_name){ err.textContent='Nom du joueur requis.'; return; }
try{
disable(btn, spin, true);
await addParticipant(tid, { full_name });
showToast('Joueur ajouté ✔');
document.getElementById('p_fullname').value='';
}catch(e){
err.textContent = e?.payload?.error || e.message;
showToast('Erreur ajout joueur', false);
}finally{
disable(btn, spin, false);
}
});
document.getElementById('s_tid')?.addEventListener('change', renderMatchesForSelectedTournament);
document.getElementById('scoreBtn')?.addEventListener('click', async (e)=>{
const btn = e.currentTarget;
const spin = document.getElementById('scoreSpin');
const err = document.getElementById('scoreErr');
err.textContent='';
const mid = Number(document.getElementById('s_mid').value || '0');
if(!mid){ err.textContent='Choisis un match.'; return; }
const payload = {
score_a: Number(document.getElementById('m_a').value||'0'),
score_b: Number(document.getElementById('m_b').value||'0'),
done: document.getElementById('m_done').checked
};
try{
disable(btn, spin, true);
await scoreMatch(mid, payload);
showToast('Score enregistré ✔');
document.getElementById('m_a').value='';
document.getElementById('m_b').value='';
document.getElementById('m_done').checked=false;
}catch(e){
err.textContent = e?.payload?.error || e.message;
showToast('Erreur scoring', false);
}finally{
disable(btn, spin, false);
}
});
document.getElementById('refreshTournaments')?.addEventListener('click', renderTournaments);
}
function init(){
toggleAdminUI();
bindAuth();
bindAdminActions();
if(isAuthenticated()) renderTournaments();
}
document.addEventListener('DOMContentLoaded', init);

View File

@@ -1,12 +1,80 @@
const api = {
base: '',
token: null,
setToken(t){ this.token = t; localStorage.setItem('ss_token', t); },
getToken(){ return this.token || localStorage.getItem('ss_token'); },
headers(){ const h = { 'Content-Type':'application/json' }; const t=this.getToken(); if(t) h['Authorization']='Bearer '+t; return h; },
async get(path){ const r = await fetch('/api'+path, { headers: this.headers() }); if(!r.ok) throw new Error(await r.text()); return r.json(); },
async post(path, body){ const r = await fetch('/api'+path, { method:'POST', headers:this.headers(), body: JSON.stringify(body) }); if(!r.ok) throw new Error(await r.text()); return r.json(); },
async put(path, body){ const r = await fetch('/api'+path, { method:'PUT', headers:this.headers(), body: JSON.stringify(body) }); if(!r.ok) throw new Error(await r.text()); return r.json(); },
async del(path, body){ const r = await fetch('/api'+path, { method:'DELETE', headers:this.headers(), body: JSON.stringify(body) }); if(!r.ok) throw new Error(await r.text()); return r.json(); }
};
window.SSAPI = api;
/* ============================================================================
Super Sunday — Frontend API client (ESM)
============================================================================ */
const BASE = '/api';
const TOKEN_KEY = 'ss_token';
function getToken() { return localStorage.getItem(TOKEN_KEY) || ''; }
function setToken(t) { if (t) localStorage.setItem(TOKEN_KEY, t); else localStorage.removeItem(TOKEN_KEY); }
async function fetchJSON(url, opts = {}) {
const res = await fetch(url, opts);
const ct = res.headers.get('content-type') || '';
const isJSON = ct.includes('application/json');
const data = isJSON ? await res.json().catch(() => ({})) : await res.text();
if (!res.ok) {
const err = new Error((data && data.error) || res.statusText || 'HTTP Error');
err.status = res.status; err.payload = data;
throw err;
}
return data;
}
async function authFetch(path, options = {}) {
const token = getToken();
const headers = new Headers(options.headers || {});
headers.set('Content-Type','application/json');
if(token) headers.set('Authorization',`Bearer ${token}`);
return fetchJSON(`${BASE}${path}`,{...options,headers});
}
/* Public endpoints */
export async function listTournaments(){ return fetchJSON(`${BASE}/tournaments`); }
export async function getTournament(id){ return fetchJSON(`${BASE}/tournaments/${id}`); }
export async function listParticipants(id){ return fetchJSON(`${BASE}/tournaments/${id}/participants`); }
export async function listMatches(id){ return fetchJSON(`${BASE}/tournaments/${id}/matches`); }
/* Admin endpoints */
export async function login(email,password){
const data = await fetchJSON(`${BASE}/auth/login`,{
method:'POST',headers:{'Content-Type':'application/json'},
body: JSON.stringify({email,password})
});
if(data && data.token) setToken(data.token);
return data;
}
export function logout(){ setToken(''); }
export function isAuthenticated(){ return !!getToken(); }
export async function createTournament(payload){
return authFetch(`/tournaments`,{method:'POST',body:JSON.stringify(payload)});
}
export async function addParticipant(tid,payload){
return authFetch(`/tournaments/${tid}/participants`,{method:'POST',body:JSON.stringify(payload)});
}
export async function generateAmericano(tid,payload){
return authFetch(`/tournaments/${tid}/generate-americano`,{method:'POST',body:JSON.stringify(payload)});
}
export async function scoreMatch(mid,payload){
return authFetch(`/matches/${mid}/score`,{method:'POST',body:JSON.stringify(payload)});
}
/* Helpers */
export function bindLoginForm({formSel='#loginSection',statusSel='#loginStatus'}={}){
const form=document.querySelector(formSel);
const status=document.querySelector(statusSel);
if(!form) return;
const email=form.querySelector('#email');
const password=form.querySelector('#password');
const btn=form.querySelector('#loginBtn');
btn?.addEventListener('click',async ()=>{
status.textContent='Connexion…';
try{
await login(email.value.trim(),password.value);
status.textContent='Connecté ✔';
const admin=document.querySelector('#adminSection');
if(admin) admin.style.display='grid';
form.style.display='none';
}catch(e){ status.textContent=`Erreur: ${e.payload?.error||e.message}`; }
});
}

View File

@@ -1,371 +1,247 @@
/* === Super Sunday — Global sporty theme + hero background + subtle bubbles === */
/* ==========================================================================
Super Sunday — Frontend Theme (Clean, Consolidated)
- Dark, Sirion-like base
- Hero gradient headings (n8n vibe)
- Background image + dark overlays + colored bubbles
- Glass-dark cards, flat rounded buttons, dark inputs
- Simple, deterministic z-index stack
-------------------------------------------------------------------------- */
/* Palette */
/* ========== 0) Design tokens ==========
*/
:root {
--bg: #f4f7fb;
--surface: #ffffff;
--surface-2: #f2f5fa;
--ink: #101827;
--muted: #6b7280;
--border: #e5e9f2;
/* Colors */
--ink: #eaf1ff;
--ink-strong: #ffffff;
--muted: #9fb0c7;
--panel: rgba(12,16,28,.75);
--panel-border: rgba(255,255,255,.12);
--cta: #1890ff;
--cta-hover: #3aa0ff;
--accent-start: #1ea7ff;
--accent-end: #27d980;
--accent: #1890ff;
/* Accents for gradients */
--grad-rose: #ff7ad9;
--grad-violet: #7c4dff;
--grad-cyan: #00e5ff;
/* Layout */
--radius: 16px;
--shadow: 0 6px 18px rgba(0,0,0,.06);
--shadow-hover: 0 10px 28px rgba(0,0,0,.12);
--radius-xl: 20px;
--shadow: 0 10px 30px rgba(0,0,0,.35);
--shadow-hover: 0 16px 36px rgba(0,0,0,.45);
--trans: 180ms ease;
}
*{box-sizing:border-box}
html,body{height:100%}/* === Super Sunday — Dark "Sirion-like" theme overrides === */
* { box-sizing: border-box; }
html, body { height: 100%; }
/* 0) Couleurs globales + typo */
:root{
--ink: #e8eef6; /* texte global clair */
--ink-strong: #ffffff; /* titres */
--muted: #b7c3d6; /* textes secondaires */
--border-dark: rgba(255,255,255,.08);
--surface-dark: rgba(15,23,42,.60); /* slate-900 translucide */
--surface-dark-2: rgba(15,23,42,.72);
}
/* 1) Fond sombre bleuté + image adoucie */
body{
/* ========== 1) Base & background ==========
*/
body {
margin: 0;
font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji","Segoe UI Emoji", sans-serif;
color: var(--ink);
line-height: 1.5;
}
body::before{
content:"";
position:fixed; inset:0; z-index:-2;
/* Background image + dark overlays */
body::before {
content: "";
position: fixed; inset: 0;
z-index: -3; /* far background */
background:
radial-gradient(120% 85% at 20% -10%, rgba(3,7,18,.9) 0%, rgba(3,7,18,.6) 40%, rgba(3,7,18,.3) 60%, rgba(3,7,18,.2) 100%),
linear-gradient(180deg, rgba(9,14,30,.75), rgba(9,14,30,.55) 40%, rgba(9,14,30,.75)),
/* colored ambience blobs */
radial-gradient(40% 35% at 15% 10%, rgba(124,77,255,.23), rgba(124,77,255,0) 60%),
radial-gradient(45% 40% at 85% 20%, rgba(0,229,255,.20), rgba(0,229,255,0) 60%),
radial-gradient(55% 50% at 50% 85%, rgba(255,122,217,.16), rgba(255,122,217,0) 60%),
/* dark veil */
linear-gradient(180deg, rgba(0,0,0,.72), rgba(0,0,0,.58) 35%, rgba(0,0,0,.70)),
/* photo */
url("/assets/image/backgroundp24p.jpg") center top / cover no-repeat;
filter: saturate(.9) contrast(.95) brightness(.9) blur(0.5px);
filter: saturate(.96) contrast(.98) brightness(.9) blur(.4px);
}
body::after{
/* vignette douce pour focus centre */
content:"";
position:fixed; inset:0; z-index:-1; pointer-events:none;
body::after {
/* vignette for edge darkening */
content: "";
position: fixed; inset: 0;
z-index: -2; /* above background */
pointer-events: none;
background:
radial-gradient(140% 100% at 50% 0%, rgba(0,0,0,0) 55%, rgba(0,0,0,.26) 100%),
radial-gradient(120% 90% at 50% 100%, rgba(0,0,0,0) 60%, rgba(0,0,0,.22) 100%);
radial-gradient(145% 100% at 50% 0%, rgba(0,0,0,0) 52%, rgba(0,0,0,.30) 100%),
radial-gradient(125% 90% at 50% 100%, rgba(0,0,0,0) 58%, rgba(0,0,0,.24) 100%);
}
/* 2) Titres & textes */
h1,h2,h3{ color: var(--ink-strong); }
.hero-title{
color:#fff !important;
text-shadow: 0 8px 30px rgba(30,167,255,.25), 0 2px 12px rgba(0,0,0,.35);
/* ========== 2) Colored bubbles (between overlays and UI) ==========
*/
@keyframes float {
0% { transform: translateY(0) scale(1); opacity: .75; }
50% { transform: translateY(-36px) scale(1.06); opacity: .45; }
100% { transform: translateY(0) scale(1); opacity: .75; }
}
.section-title{
color:#f1f5ff;
}
.section-title::before{
background: linear-gradient(180deg, #8ee7ff, #9fffcf);
box-shadow: 0 6px 16px rgba(143, 237, 255, .35);
}
.muted{ color: var(--muted); }
/* 3) Header sombre translucide */
header{
background: rgba(10,14,26,.78);
color:#fff;
border-bottom: 1px solid var(--border-dark);
.bg-bubble {
position: fixed;
z-index: -1; /* between overlays and UI */
pointer-events: none;
border-radius: 50%;
opacity: .9;
/* blue ↔ violet glow */
background:
radial-gradient(60% 60% at 30% 30%, rgba(124,77,255,.55), rgba(124,77,255,0) 70%),
radial-gradient(55% 55% at 70% 70%, rgba(0,229,255,.45), rgba(0,229,255,0) 75%);
animation: float 10s ease-in-out infinite;
filter: blur(.3px);
}
.bg-bubble.b1 { width:120px; height:120px; left:12%; top:68%; animation-duration: 9s; }
.bg-bubble.b2 { width:90px; height:90px; left:70%; top:58%; animation-duration:12s; }
.bg-bubble.b3 { width:150px; height:150px; left:46%; top:76%; animation-duration:14s; }
.bg-bubble.b4 { width:80px; height:80px; left:84%; top:22%; animation-duration:11s; }
.bg-bubble.b5 { width:110px; height:110px; left:6%; top:18%; animation-duration:10s; }
@media (prefers-reduced-motion: reduce) { .bg-bubble { animation: none; } }
/* Ensure content sits above bubbles */
header, main, nav, .card { position: relative; z-index: 0; }
/* ========== 3) Header & navigation ==========
*/
header {
position: sticky; top: 0; z-index: 40;
display: flex; align-items: center; justify-content: space-between;
padding: 14px 24px;
background: linear-gradient(180deg, rgba(5,8,14,.96), rgba(5,8,14,.88));
color: #fff;
border-bottom: 1px solid rgba(255,255,255,.10);
backdrop-filter: blur(10px) saturate(130%);
-webkit-backdrop-filter: blur(10px) saturate(130%);
}
nav a{ color:#e9f2ff; }
nav a:hover{ background: rgba(255,255,255,.06); }
header h1 { margin: 0; font-size: 20px; font-weight: 700; color: #fff; }
/* 4) Cartes dark glass */
.card{
background: var(--surface-dark);
border: 1px solid var(--border-dark);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
box-shadow: 0 10px 30px rgba(0,0,0,.35);
nav a {
margin-left: 16px; padding: 8px 14px;
color: #f2f6ff; text-decoration: none;
border-radius: 12px; font-weight: 600;
transition: background var(--trans), transform var(--trans), border-color var(--trans);
}
.card.accent{
background:
linear-gradient(135deg, rgba(30,167,255,.12), rgba(39,217,128,.12)),
var(--surface-dark-2);
border-color: rgba(255,255,255,.12);
nav a:hover { background: rgba(255,255,255,.08); }
nav a.active {
background: rgba(255,255,255,.10);
border: 1px solid rgba(255,255,255,.22);
box-shadow: inset 0 0 0 1px rgba(255,255,255,.08);
}
/* 5) Inputs / selects en sombre lisible */
input, select{
color:#e8eef6;
background: rgba(8,12,24,.6);
border: 1px solid rgba(255,255,255,.10);
}
input::placeholder, select::placeholder{ color:#9fb0c7; }
input:focus, select:focus{
border-color: rgba(143, 237, 255, .55);
box-shadow: 0 0 0 3px rgba(143, 237, 255, .18);
}
/* ========== 4) Layout & containers ==========
*/
.container { max-width: 1160px; margin: 24px auto; padding: 0 16px; }
.grid { display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
/* 6) Boutons */
.btn{
box-shadow: 0 12px 28px rgba(30,167,255,.3);
}
.btn-outline{
color:#eaf3ff;
background: rgba(255,255,255,.06);
border:1px solid rgba(255,255,255,.25);
}
.btn-outline:hover{
background: rgba(255,255,255,.12);
border-color: rgba(255,255,255,.45);
}
/* 7) État vide & préformatés */
.empty{
background: rgba(255,255,255,.06);
border: 1px dashed rgba(255,255,255,.18);
color: #c8d5e8;
}
pre{
color:#e7efff;
background: rgba(8,12,24,.55);
border: 1px solid rgba(255,255,255,.08);
padding: 12px; border-radius: 12px; overflow:auto;
}
/* 8) Légère retouche des bulles pour le mode sombre */
.bg-bubble{
background: radial-gradient(circle at 30% 30%, rgba(255,255,255,.22), rgba(255,255,255,0));
mix-blend-mode: screen;
opacity:.7;
}
body{
margin:0;
font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
color:var(--ink);
line-height:1.5;
/* Hero background image */
background-image: url("/assets/image/backgroundp24p.jpg");
background-size: cover;
background-position: center top;
background-attachment: fixed;
}
/* dark overlay to ensure readability */
body::before{
content:"";
position:fixed; inset:0;
background: linear-gradient(180deg, rgba(255,255,255,.30), rgba(255,255,255,.60));
mix-blend-mode: normal;
z-index:-1;
}
/* ===== Header / Nav ===== */
header{
position: sticky; top:0; z-index:40;
display:flex; align-items:center; justify-content:space-between;
padding: 14px 24px;
background: rgba(255,255,255,0.86);
border-bottom:1px solid var(--border);
backdrop-filter: blur(8px) saturate(120%);
}
header h1{margin:0; font-size:20px; font-weight:700; color:var(--ink)}
nav a{
margin-left:16px; padding:8px 14px;
color:var(--ink); text-decoration:none; border-radius:12px;
transition: background var(--trans), transform var(--trans);
}
nav a:hover{ background: linear-gradient(90deg, rgba(30,167,255,.15), rgba(39,217,128,.15)); transform:translateY(-1px) }
/* ===== Layout ===== */
.container{ max-width:1100px; margin:24px auto; padding:0 16px }
.grid{ display:grid; gap:20px; grid-template-columns: repeat(auto-fit,minmax(280px,1fr)) }
/* ===== Cards ===== */
.card{
background: var(--surface);
border:1px solid var(--border);
border-radius: var(--radius);
/* ========== 5) Cards ==========
*/
.card {
background: var(--panel);
border: 1px solid var(--panel-border);
border-radius: var(--radius-xl);
padding: 18px;
box-shadow: var(--shadow);
transition: transform var(--trans), box-shadow var(--trans), border-color var(--trans);
}
.card:hover{ transform: translateY(-2px); box-shadow: var(--shadow-hover); border-color:#dbe5f2 }
a.card{ display:block; color:inherit; text-decoration:none }
.card.accent{
background: linear-gradient(135deg, rgba(30,167,255,.08), rgba(39,217,128,.08)), var(--surface);
border-color: rgba(30,167,255,.25);
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow-hover); border-color: rgba(143,237,255,.35); }
.card.accent {
background:
linear-gradient(180deg, rgba(124,77,255,.10), rgba(0,229,255,.10)),
var(--panel);
border-color: rgba(255,255,255,.14);
}
#tournaments .card, #matches .card { margin-bottom: 12px; }
/* ========== 6) Typography ==========
*/
h1, h2, h3 { color: var(--ink-strong); line-height: 1.2; }
.muted { color: var(--muted); }
/* Hero & section titles (n8n-like gradients) */
.hero-title {
font-size: clamp(42px, 6.8vw, 78px);
font-weight: 900;
letter-spacing: -.02em;
line-height: 1.04;
background: linear-gradient(90deg, var(--grad-rose) 0%, var(--grad-violet) 45%, var(--grad-cyan) 95%);
-webkit-background-clip: text; background-clip: text; color: transparent;
text-shadow: 0 10px 30px rgba(124,77,255,.22), 0 2px 12px rgba(0,0,0,.35);
margin-bottom: .25rem;
}
.section-title {
font-size: clamp(26px, 3.8vw, 42px);
font-weight: 800; letter-spacing: -.01em;
background: linear-gradient(90deg, var(--grad-violet), var(--grad-cyan));
-webkit-background-clip: text; background-clip: text; color: transparent;
}
/* Titles / text */
h1,h2,h3{ line-height:1.2 }
h2{ margin: 20px 0 12px; font-size:22px; font-weight:600 }
.muted{ color: var(--muted) }
.mt{ margin-top:24px }
/* Buttons & inputs */
.btn{
display:inline-block; padding:10px 18px; border-radius:12px; border:none;
background: linear-gradient(90deg, var(--accent-start), var(--accent-end));
color:#fff; font-weight:600; text-decoration:none;
box-shadow: 0 8px 20px rgba(24,144,255,.25);
transition: transform var(--trans), box-shadow var(--trans);
}
.btn:hover{ transform: translateY(-1px); box-shadow: 0 12px 28px rgba(24,144,255,.35) }
input,select{
width:100%; padding:10px 12px; border-radius:12px; border:1px solid var(--border);
background: var(--surface-2); color: var(--ink); outline:none;
/* ========== 7) Forms & buttons ==========
*/
input, select, textarea {
width: 100%;
padding: 12px 14px;
min-height: 44px;
border-radius: 14px;
color: #eaf3ff;
background: #0f1b33;
border: 1px solid rgba(255,255,255,.15);
outline: none;
transition: border-color var(--trans), box-shadow var(--trans);
margin: 10px 0 16px;
}
input:focus,select:focus{ border-color:#b8d8ff; box-shadow: 0 0 0 3px rgba(24,144,255,.20) }
/* Empty state */
.empty{
padding: 20px; text-align:center; color:var(--muted);
background: var(--surface-2); border:1px dashed var(--border);
border-radius: var(--radius);
input::placeholder, textarea::placeholder { color: #93a4c0; }
input:focus, select:focus, textarea:focus {
border-color: rgba(143,237,255,.55);
box-shadow: 0 0 0 3px rgba(143, 237, 255, .18);
}
/* Specific existing sections */
#tournaments .card, #matches .card{ margin-bottom:12px }
/* ===== Animated bubbles (tennis/padel ball ghosts) ===== */
@keyframes float {
0% { transform: translateY(0) scale(1); opacity:.7; }
50% { transform: translateY(-36px) scale(1.05); opacity:.4; }
100% { transform: translateY(0) scale(1); opacity:.7; }
}
.bg-bubble{
position: fixed; z-index: -1; pointer-events: none; border-radius: 50%;
background: radial-gradient(circle at 30% 30%, rgba(255,255,255,.75), rgba(255,255,255,0));
filter: blur(0.2px);
animation: float 9s ease-in-out infinite;
mix-blend-mode: screen;
}
.bg-bubble.b1{ width:90px; height:90px; left:15%; top:72%; animation-duration:8s }
.bg-bubble.b2{ width:70px; height:70px; left:68%; top:64%; animation-duration:10s }
.bg-bubble.b3{ width:115px; height:115px; left:48%; top:78%; animation-duration:12s }
.bg-bubble.b4{ width:60px; height:60px; left:82%; top:20%; animation-duration:11s }
.bg-bubble.b5{ width:80px; height:80px; left:6%; top:18%; animation-duration:9s }
@media (prefers-reduced-motion: reduce){
.bg-bubble{ animation: none; }
}
/* Footer (if needed) */
footer{ margin:40px 0 0; padding:24px 16px; text-align:center; color:var(--muted);
border-top:1px solid var(--border); background: rgba(255,255,255,.75); backdrop-filter: blur(6px) }
/* === Retouches: overlay noir + spacing formulaires === */
/* 1) Overlay plus noir, façon Sirion */
body::before{
/* on garde l'image + on fonce nettement avec un voile noir bleuté */
background:
linear-gradient(180deg, rgba(0,0,0,.70), rgba(0,0,0,.55) 35%, rgba(0,0,0,.68)),
radial-gradient(120% 85% at 20% -10%, rgba(0,10,20,.75) 0%, rgba(0,10,20,.35) 60%, rgba(0,10,20,.20) 100%),
url("/assets/image/backgroundp24p.jpg") center top / cover no-repeat !important;
filter: saturate(.95) contrast(.98) brightness(.88) blur(0.6px);
}
/* vignette un peu plus marquée sur les bords */
body::after{
background:
radial-gradient(145% 100% at 50% 0%, rgba(0,0,0,0) 52%, rgba(0,0,0,.30) 100%),
radial-gradient(125% 90% at 50% 100%, rgba(0,0,0,0) 58%, rgba(0,0,0,.24) 100%) !important;
}
/* 2) Spacing des formulaires (inputs & dropdowns) */
.card input,
.card select,
.card textarea {
margin-top: 8px !important;
margin-bottom: 14px !important; /* +respiration */
min-height: 42px; /* dropdowns plus grands */
padding: 10px 12px !important;
border-radius: 12px !important;
}
/* espacement entre groupes de champs (si <div> direct dans .card) */
.card > div + div { margin-top: 16px; }
/* si tu utilises des <label>, on les rapproche proprement du champ */
.card label {
display: block;
font-weight: 600;
color: #e9f1ff;
margin-top: 12px;
margin-bottom: 6px;
}
/* 3) Lisibilité du header encore meilleure sur overlay noir */
header{
background: linear-gradient(180deg, rgba(5,8,14,.96), rgba(5,8,14,.88)) !important;
border-bottom: 1px solid rgba(255,255,255,.10) !important;
}
nav a{ color:#f2f6ff !important; }
nav a:hover{ background: rgba(255,255,255,.08) !important; }
/* 4) Cartes & blocs "empty" légèrement plus sombres pour contraster */
.card{
background: rgba(12,18,32,.72) !important;
border: 1px solid rgba(255,255,255,.12) !important;
}
.empty{
background: rgba(255,255,255,.07) !important;
border-color: rgba(255,255,255,.20) !important;
}
/* 5) Sections et titres : contraste blanc franc */
.section-title{ color:#ffffff !important; }
.hero-title{ color:#ffffff !important; }
/* === Retouches inputs + boutons === */
/* 1) Inputs & selects : fond bleu foncé homogène */
.card input,
.card select,
.card textarea {
background: #0f1b33 !important; /* bleu nuit uniforme */
color: #eaf3ff !important;
border: 1px solid rgba(255,255,255,.15) !important;
}
.card input::placeholder,
.card select::placeholder,
.card textarea::placeholder {
color: #93a4c0 !important;
}
/* 2) Boutons flat arrondis */
/* Buttons (flat, rounded) */
.btn {
display:inline-block;
padding:10px 18px;
border-radius: 999px; /* bien arrondi */
border:none;
background: #1890ff; /* bleu flat */
color:#fff;
font-weight:600;
box-shadow: none; /* pas dombre lourde */
transition: background 0.2s ease, transform 0.2s ease;
display: inline-block;
padding: 12px 20px;
border-radius: 999px;
border: none;
background: var(--cta);
color: #fff;
font-weight: 700;
box-shadow: none;
transition: background .2s ease, transform .2s ease;
}
.btn:hover {
background: #3aa0ff; /* bleu un peu plus clair au hover */
transform: translateY(-1px);
}
/* Variante secondaire : outline */
.btn:hover { background: var(--cta-hover); transform: translateY(-1px); }
.btn-outline {
border-radius: 999px;
padding:10px 16px;
border:1px solid rgba(255,255,255,.25);
padding: 10px 16px;
border: 1px solid rgba(255,255,255,.28);
background: transparent;
color:#eaf3ff;
transition: background 0.2s ease, border-color 0.2s ease;
color: #eaf3ff;
transition: background .2s ease, border-color .2s ease;
}
.btn-outline:hover {
background: rgba(255,255,255,.08);
border-color: rgba(255,255,255,.45);
.btn-outline:hover { background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.45); }
/* ========== 8) Utility blocks ==========
*/
.empty {
padding: 20px; text-align: center; color: #cfe1ff;
background: rgba(255,255,255,.05);
border: 1px dashed rgba(255,255,255,.18);
border-radius: var(--radius);
}
pre {
color: #e7efff;
background: rgba(10,14,26,.55);
border: 1px solid rgba(255,255,255,.10);
padding: 12px; border-radius: 16px; overflow: auto;
}
/* ========== 9) Footer (optional) ==========
*/
footer {
margin: 40px 0 0; padding: 24px 16px; text-align: center; color: var(--muted);
border-top: 1px solid rgba(255,255,255,.10);
background: rgba(5,8,14,.82);
backdrop-filter: blur(6px);
}