Init Git Prod Mars 2026
This commit is contained in:
commit
d81efe93b1
362 files changed
+86627
No files matched your search
@@ -0,0 +1,12 @@
|
||||
# Interdit l'accès HTTP aux fichiers sensibles
|
||||
<FilesMatch "\.(php|inc|phtml|phar|env|ini|json|ya?ml|sql|bak|zip)$">
|
||||
Require all denied
|
||||
</FilesMatch>
|
||||
|
||||
# Interdit l'accès aux fichiers cachés (.env, .git, etc.)
|
||||
<FilesMatch "^\.">
|
||||
Require all denied
|
||||
</FilesMatch>
|
||||
|
||||
# Pas de listing de répertoire
|
||||
Options -Indexes
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
// htdocs/includes/accepter_cookies.php — CookieConsent v3 (correctif)
|
||||
// Si tu as des traductions dans lang/fr.php / lang/ru.php, on les lit (optionnel)
|
||||
$lang = $_SESSION['lang'] ?? 'fr';
|
||||
$langFile = __DIR__ . "/../lang/{$lang}.php";
|
||||
if (is_file($langFile)) { include $langFile; }
|
||||
$t = isset($translations) && is_array($translations) ? $translations : [];
|
||||
function tt($k,$d=''){ global $t; return htmlspecialchars($t[$k]??$d, ENT_QUOTES, 'UTF-8'); }
|
||||
?>
|
||||
<!--<a id="cc-manage" href="#" style="display:none"><?= tt('cookies_manage_link','Paramètres cookies') ?></a>-->
|
||||
|
||||
<link id="cc-css" rel="stylesheet" href="/js/cookieconsent.css"
|
||||
onerror="this.href='https://cdn.jsdelivr.net/gh/orestbida/cookieconsent@3.1.0/dist/cookieconsent.css'">
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
// Textes (prend ceux de tes fichiers de langue si présents)
|
||||
const TXT = {
|
||||
title: <?= json_encode($t['cookies_title'] ?? 'Cookies & confidentialité') ?>,
|
||||
desc: <?= json_encode($t['cookies_desc'] ?? "Nous utilisons des cookies essentiels. Avec votre accord, nous activons aussi l'analyse et la personnalisation.") ?>,
|
||||
acceptAll: <?= json_encode($t['cookies_accept_all'] ?? 'Tout accepter') ?>,
|
||||
rejectAll: <?= json_encode($t['cookies_reject_all'] ?? 'Tout refuser') ?>,
|
||||
settings: <?= json_encode($t['cookies_settings'] ?? 'Personnaliser') ?>,
|
||||
necessary: <?= json_encode($t['cookies_necessary'] ?? 'Essentiels (toujours actifs)') ?>,
|
||||
analytics: <?= json_encode($t['cookies_analytics'] ?? 'Mesure d’audience') ?>,
|
||||
personalization: <?= json_encode($t['cookies_personalization'] ?? 'Personnalisation') ?>,
|
||||
save: <?= json_encode($t['cookies_save'] ?? 'Enregistrer') ?>,
|
||||
};
|
||||
|
||||
const SRCS = [
|
||||
'/js/cookieconsent.umd.js', // local d'abord
|
||||
'https://cdn.jsdelivr.net/gh/orestbida/cookieconsent@3.1.0/dist/cookieconsent.umd.js',
|
||||
'https://unpkg.com/vanilla-cookieconsent@3.1.0/dist/cookieconsent.umd.js'
|
||||
];
|
||||
|
||||
function loadScriptSequential(i){
|
||||
if(i >= SRCS.length){ console.error('[CC] Impossible de charger la lib'); return; }
|
||||
const s = document.createElement('script');
|
||||
s.src = SRCS[i];
|
||||
s.onload = function(){
|
||||
// v3 => CookieConsent.run(...)
|
||||
if (window.CookieConsent && typeof CookieConsent.run === 'function'){ initCC(); }
|
||||
else { console.warn('[CC] Script chargé mais API v3 absente → essai suivant'); loadScriptSequential(i+1); }
|
||||
};
|
||||
s.onerror = function(){ console.warn('[CC] Échec chargement', SRCS[i]); loadScriptSequential(i+1); };
|
||||
document.head.appendChild(s);
|
||||
}
|
||||
|
||||
function initCC(){
|
||||
// Sécurité
|
||||
if (!(window.CookieConsent && typeof CookieConsent.run === 'function')){
|
||||
console.error('[CC] CookieConsent.run indisponible'); return;
|
||||
}
|
||||
|
||||
CookieConsent.run({
|
||||
cookie: {
|
||||
name: 'cc_consent_v3',
|
||||
path: '/',
|
||||
sameSite: 'Lax',
|
||||
expiresAfterDays: 180
|
||||
},
|
||||
guiOptions: {
|
||||
consentModal: { layout: 'cloud', position: 'bottom center', equalWeightButtons: false, flipButtons: false },
|
||||
preferencesModal: { layout: 'box', equalWeightButtons: true, flipButtons: false }
|
||||
},
|
||||
// v3: définir "categories" + "language" (obligatoires)
|
||||
categories: {
|
||||
necessary: { enabled: true, readOnly: true },
|
||||
analytics: {},
|
||||
personalization: {}
|
||||
},
|
||||
language: {
|
||||
default: 'xx',
|
||||
translations: {
|
||||
xx: {
|
||||
consentModal: {
|
||||
title: TXT.title,
|
||||
description: TXT.desc,
|
||||
acceptAllBtn: TXT.acceptAll,
|
||||
acceptNecessaryBtn: TXT.rejectAll,
|
||||
showPreferencesBtn: TXT.settings
|
||||
},
|
||||
preferencesModal: {
|
||||
title: TXT.title,
|
||||
acceptAllBtn: TXT.acceptAll,
|
||||
acceptNecessaryBtn: TXT.rejectAll,
|
||||
savePreferencesBtn: TXT.save,
|
||||
sections: [
|
||||
{ title: TXT.necessary, description: '' },
|
||||
{ title: TXT.analytics, description: '', linkedCategory: 'analytics' },
|
||||
{ title: TXT.personalization, description: '', linkedCategory: 'personalization' }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Hooks
|
||||
onConsent: ({cookie}) => {
|
||||
if (CookieConsent.acceptedCategory('analytics')) enableAnalytics();
|
||||
},
|
||||
onChange: ({changedCategories}) => {
|
||||
if (changedCategories.includes('analytics')) {
|
||||
CookieConsent.acceptedCategory('analytics') ? enableAnalytics() : disableAnalytics();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Lien pour rouvrir
|
||||
const manage = document.getElementById('cc-manage');
|
||||
if (manage) {
|
||||
manage.style.display = 'inline';
|
||||
manage.addEventListener('click', (e)=>{ e.preventDefault(); CookieConsent.showPreferences(); });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Tes hooks perso (ex: activer GA seulement si analytics accepté)
|
||||
window.enableAnalytics = function(){
|
||||
if (window.__gaLoaded) return; window.__gaLoaded = true;
|
||||
// Exemple:
|
||||
CookieConsent.loadScript('https://www.googletagmanager.com/gtag/js?id=G-XXXX').then(()=>{
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments)}; gtag('js', new Date()); gtag('config','G-XXXX');
|
||||
});
|
||||
};
|
||||
window.disableAnalytics = function(){ /* éventuellement reset */ };
|
||||
|
||||
// Lance le chargement (local → CDN fallback)
|
||||
loadScriptSequential(0);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#cc--main .cm__btn, #cc--main .pm__btn { border-radius: 999px; }
|
||||
#cc-manage { position: fixed; right: 12px; bottom: 12px; font-size: 12px; opacity: .7; z-index: 99999; }
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,400 @@
|
||||
/* ==========================================================================
|
||||
admin_style.css — Mobile global (≤ 956px)
|
||||
Charge APRÈS Bootstrap
|
||||
========================================================================== */
|
||||
|
||||
/* --- Dropdown admin (utilisé aussi hors mobile) --- */
|
||||
:root {
|
||||
--header: #f1f1f1;
|
||||
}
|
||||
.menu-wrap {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.dropdown-menu-custom {
|
||||
position: absolute;
|
||||
top: calc(100% + 8px);
|
||||
right: 0;
|
||||
background: #fff;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 230px;
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
|
||||
display: none;
|
||||
z-index: 1100;
|
||||
}
|
||||
.dropdown-menu-custom li {
|
||||
padding: 12px 18px;
|
||||
border-bottom: 1px solid #f1f1f1;
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
}
|
||||
.dropdown-menu-custom li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.dropdown-menu-custom li:hover {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
/* ======================== Mobile-first (≤ 956px) ======================== */
|
||||
@media (max-width: 956px) {
|
||||
/* Garde-fous */
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow-x: hidden;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
img,
|
||||
video,
|
||||
iframe {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-left: env(safe-area-inset-left);
|
||||
padding-right: env(safe-area-inset-right);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
/* Grille & conteneurs */
|
||||
.row {
|
||||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
.container {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
padding-left: 16px !important;
|
||||
padding-right: 16px !important;
|
||||
}
|
||||
|
||||
/* Layout principal */
|
||||
main,
|
||||
.main,
|
||||
.main1,
|
||||
.main2 {
|
||||
display: block !important;
|
||||
align-items: initial !important;
|
||||
justify-content: initial !important;
|
||||
background: transparent !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
width: 100% !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
.btn-lg,
|
||||
.btn-sm {
|
||||
min-height: 38px !important;
|
||||
padding: 0.35rem 0.7rem !important;
|
||||
font-size: 0.92rem !important;
|
||||
border-radius: 10px !important;
|
||||
}
|
||||
.btn-outline-primary,
|
||||
.btn-outline-secondary,
|
||||
.btn-outline-danger,
|
||||
.btn-outline-dark {
|
||||
border-width: 1px !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* groupes d’actions dans les tableaux → même largeur */
|
||||
.d-flex.gap-2 .btn,
|
||||
.row .btn {
|
||||
flex: 1 1 0;
|
||||
text-align: center;
|
||||
}
|
||||
.text-end {
|
||||
text-align: initial !important;
|
||||
}
|
||||
.text-end .btn {
|
||||
width: 100%;
|
||||
}
|
||||
.btn-group {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.btn-group .btn {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
/* Cartes */
|
||||
.card,
|
||||
.card * {
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
}
|
||||
.card,
|
||||
.card.shadow-sm,
|
||||
.card.shadow-sm.p-3 {
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Formulaires */
|
||||
.form-control,
|
||||
.form-select,
|
||||
.form-check-input,
|
||||
.input-group-text,
|
||||
.input-group > .btn {
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
/* Tables: scroll horizontal “propre” */
|
||||
.table-responsive {
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
table.table {
|
||||
font-size: 14px;
|
||||
}
|
||||
table.table th,
|
||||
table.table td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Listes → actions tap-friendly */
|
||||
td.text-end .d-flex {
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
td.text-end .d-flex .btn {
|
||||
flex: 1 1 calc(50% - 4px);
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
/* Split list/detail */
|
||||
.row.g-3 > [class*="col-"] .card {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Autocomplete / suggestions */
|
||||
.suggest,
|
||||
#suggestions {
|
||||
position: static !important;
|
||||
max-height: none !important;
|
||||
overflow: visible !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.site-footer {
|
||||
font-size: 12px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
/* Menu hamburger */
|
||||
.header-actions .btn {
|
||||
min-width: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ====== Composants “pages admin” (tous écrans, pensés mobile) ====== */
|
||||
.table th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.table .minw-120 {
|
||||
min-width: 120px;
|
||||
}
|
||||
.photo {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
object-fit: cover;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 0.35rem 0.6rem;
|
||||
border-radius: 9999px;
|
||||
font-size: 0.85rem;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.badge-active {
|
||||
background: #e8f6ee;
|
||||
border-color: #27ae60;
|
||||
color: #2e07a3ff;
|
||||
}
|
||||
.badge-expiring {
|
||||
background: #fff8e6;
|
||||
border-color: #f39c12;
|
||||
color: #2e07a3ff;
|
||||
}
|
||||
.badge-expired {
|
||||
background: #fdecea;
|
||||
border-color: #c0392b;
|
||||
color: #2e07a3ff;
|
||||
}
|
||||
|
||||
.card pre {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.list-group-item > .badge {
|
||||
margin-left: auto;
|
||||
}
|
||||
:focus-visible {
|
||||
outline: 2px solid #6366f1;
|
||||
outline-offset: 2px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* ====== Table -> cartes (opt-in via .mobile-cards) ====== */
|
||||
@media (max-width: 768px) {
|
||||
table.mobile-cards {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
table.mobile-cards thead {
|
||||
display: none;
|
||||
}
|
||||
table.mobile-cards tbody {
|
||||
display: block;
|
||||
}
|
||||
table.mobile-cards tr {
|
||||
display: block;
|
||||
margin: 12px 0;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.08);
|
||||
overflow: hidden;
|
||||
}
|
||||
table.mobile-cards tr::before {
|
||||
content: "";
|
||||
display: block;
|
||||
height: 3px;
|
||||
background: #38378b;
|
||||
opacity: 0.9;
|
||||
}
|
||||
table.mobile-cards td {
|
||||
display: block;
|
||||
padding: 10px 14px !important;
|
||||
border: none !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
table.mobile-cards td + td {
|
||||
border-top: 1px dashed rgba(0, 0, 0, 0.08) !important;
|
||||
}
|
||||
table.mobile-cards td::before {
|
||||
content: attr(data-label);
|
||||
display: inline-block;
|
||||
min-width: 120px;
|
||||
margin-right: 8px;
|
||||
font-weight: 600;
|
||||
color: #666;
|
||||
}
|
||||
table.mobile-cards td::before:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 956px) {
|
||||
.d-flex.justify-content-end.gap-2 .btn {
|
||||
flex: 1 1 0; /* même largeur */
|
||||
min-width: 120px; /* évite les boutons trop petits */
|
||||
text-align: center; /* centre le texte */
|
||||
}
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.site-header {
|
||||
background: var(--header);
|
||||
border-bottom: 1px solid rgba(168, 39, 39, 0.06);
|
||||
}
|
||||
.site-header .container {
|
||||
margin: 0 auto;
|
||||
padding: 10px 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.site-header .btn-circle {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
.site-header .avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 999px;
|
||||
display: inline-grid;
|
||||
place-items: center;
|
||||
font-weight: 600;
|
||||
background: #eef2ff;
|
||||
color: #3730a3;
|
||||
}
|
||||
/* ==== LOGO: mobile vs desktop (béton) ==== */
|
||||
.brand {
|
||||
letter-spacing: 0.5px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 0; /* évite petits décalages baseline */
|
||||
}
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px; /* distance logo ↔ langue */
|
||||
}
|
||||
.brand .logo-mobile,
|
||||
.brand .logo-desktop {
|
||||
display: none !important; /* on cache tout par défaut */
|
||||
height: 40px; /* taille unique (tu ajustes) */
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* Mobile: logo_u */
|
||||
@media (max-width: 767.98px) {
|
||||
.brand .logo-mobile {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Desktop: logo.png */
|
||||
@media (min-width: 768px) {
|
||||
.brand .logo-desktop {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Langue choix*/
|
||||
/* Sélecteur de langue dans le header */
|
||||
.lang-switch {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
height: 42px;
|
||||
background: #fff;
|
||||
padding: 6px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.lang-btn {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
color: #38378b; /* bleu foncé */
|
||||
border-radius: 14px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.lang-btn:hover {
|
||||
background: rgba(56, 55, 139, 0.08);
|
||||
}
|
||||
|
||||
.lang-btn.active {
|
||||
background: #38378b; /* bleu foncé */
|
||||
color: #fff;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* Prépare le contexte attendu par header.php (pushCfg, alerte_abonnement, avatar/initiales)
|
||||
* SANS appeler init_praticien.php => pas de redirection abonnement.
|
||||
*/
|
||||
|
||||
/* =========================
|
||||
Helpers
|
||||
========================= */
|
||||
function is_logged_in(): bool {
|
||||
$role = (string)($_SESSION['role'] ?? ($_SESSION['user_type'] ?? ''));
|
||||
if (!empty($_SESSION['reference_id']) && $role !== '') return true;
|
||||
if (!empty($_SESSION['user_id'])) return true;
|
||||
if (!empty($_SESSION['client_id'])) return true;
|
||||
if (!empty($_SESSION['praticien_id'])) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inclut /public/includes/header.php sans DOCTYPE/HTML/HEAD/BODY et sans recharger bootstrap/css.
|
||||
*/
|
||||
function include_header_clean(string $absPath): void {
|
||||
global $translations, $lang;
|
||||
ob_start();
|
||||
include $absPath;
|
||||
$buf = ob_get_clean() ?: '';
|
||||
|
||||
$buf = preg_replace('~<!doctype\s+html[^>]*>~i', '', $buf);
|
||||
$buf = preg_replace('~</?html\b[^>]*>~i', '', $buf);
|
||||
$buf = preg_replace('~<head\b[^>]*>.*?</head>~is', '', $buf);
|
||||
$buf = preg_replace('~</?body\b[^>]*>~i', '', $buf);
|
||||
|
||||
// éviter d’écraser la home
|
||||
$buf = preg_replace('~<link[^>]+href="[^"]*(bootstrap[^"]*\.css|mobil_style\.css|site_style\.css|fonts\.googleapis)[^"]*"[^>]*>~i', '', $buf);
|
||||
$buf = preg_replace('~<script[^>]+src="[^"]*bootstrap[^"]*bundle[^"]*\.js[^"]*"[^>]*>\s*</script>~i', '', $buf);
|
||||
|
||||
echo $buf;
|
||||
}
|
||||
function prepare_secure_header_context(): void {
|
||||
$pdo = $GLOBALS['pdo'] ?? null;
|
||||
if (!($pdo instanceof PDO)) return;
|
||||
|
||||
$role = strtolower((string)($_SESSION['role'] ?? ''));
|
||||
$refId = (int)($_SESSION['reference_id'] ?? 0);
|
||||
|
||||
|
||||
|
||||
// Initiales client/praticien
|
||||
if ($refId > 0 && ($role === 'client' || $role === 'praticien')) {
|
||||
try {
|
||||
$table = ($role === 'client') ? 'client' : 'praticien';
|
||||
$st = $pdo->prepare("SELECT nom, prenom FROM {$table} WHERE id = ? LIMIT 1");
|
||||
$st->execute([$refId]);
|
||||
$u = $st->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||
$nom = trim((string)($u['nom'] ?? ''));
|
||||
$prenom = trim((string)($u['prenom'] ?? ''));
|
||||
$ini = mb_strtoupper(
|
||||
mb_substr($prenom, 0, 1, 'UTF-8') . mb_substr($nom, 0, 1, 'UTF-8'),
|
||||
'UTF-8'
|
||||
);
|
||||
$GLOBALS['initiales'] = $ini !== '' ? $ini : 'U';
|
||||
} catch (Throwable $e) {
|
||||
$GLOBALS['initiales'] = 'U';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Si connecté -> préparer le contexte header sécurisé (sans redirection)
|
||||
$logged = is_logged_in();
|
||||
if ($logged) prepare_secure_header_context();
|
||||
|
||||
@@ -0,0 +1,316 @@
|
||||
<?php
|
||||
/*
|
||||
* /city_aliases_ru.php
|
||||
*/
|
||||
// RU exonyms -> Latin/French city names (lowercase, sans accents si possible)
|
||||
return [
|
||||
/* ================= Île-de-France ================= */
|
||||
'париж' => 'paris',
|
||||
'булонь-бийанкур' => 'boulogne-billancourt',
|
||||
'сен-дени' => 'saint-denis',
|
||||
'аржантёй' => 'argenteuil',
|
||||
'монтрой' => 'montreuil',
|
||||
'нантер' => 'nanterre',
|
||||
'витри-сюр-сен' => 'vitry-sur-seine',
|
||||
'курбевуа' => 'courbevoie',
|
||||
'кольомб' => 'colombes',
|
||||
'аньер-сюр-сен' => 'asnieres-sur-seine',
|
||||
'олнэ-су-буа' => 'aulnay-sous-bois',
|
||||
'рюэй-мальмезон' => 'rueil-malmaison',
|
||||
'сен-мор-де-фоссе' => 'saint-maur-des-fosses',
|
||||
'шампиньи-сюр-марн' => 'champigny-sur-marne',
|
||||
'исси-ле-мулино' => 'issy-les-moulineaux',
|
||||
'клиши' => 'clichy',
|
||||
'нуази-ле-гран' => 'noisy-le-grand',
|
||||
'сарсель' => 'sarcelles',
|
||||
'леваллуа-перре' => 'levallois-perret',
|
||||
'нёйи-сюр-сен' => 'neuilly-sur-seine',
|
||||
'антони' => 'antony',
|
||||
'мэзон-алфор' => 'maisons-alfort',
|
||||
'кретей' => 'creteil',
|
||||
'версаль' => 'versailles',
|
||||
'иври-сюр-сен' => 'ivry-sur-seine',
|
||||
'пантен' => 'pantin',
|
||||
'севран' => 'sevran',
|
||||
'дранси' => 'drancy',
|
||||
'бобиньи' => 'bobigny',
|
||||
'бонди' => 'bondy',
|
||||
'обервилье' => 'aubervilliers',
|
||||
'сен-уэн' => 'saint-ouen',
|
||||
'масси' => 'massy',
|
||||
'мо' => 'meaux',
|
||||
'сен-жермен-ан-ле' => 'saint-germain-en-laye',
|
||||
'пюто' => 'puteaux',
|
||||
'мелён' => 'melun',
|
||||
'эври' => 'evry',
|
||||
'сержи' => 'cergy',
|
||||
'фонтенбло' => 'fontainebleau',
|
||||
'монруж' => 'montrouge',
|
||||
'нанси-сюр- марн' => 'nancy',
|
||||
'сарсель' => 'sarcelles',
|
||||
'монтеро-фо-ивонн' => 'montereau-fault-yonne',
|
||||
'сарсель' => 'sarcelles',
|
||||
'нантер' => 'nanterre',
|
||||
|
||||
/* ================= Auvergne–Rhône–Alpes ================= */
|
||||
'лион' => 'lyon',
|
||||
'виллербанн' => 'villeurbanne',
|
||||
'вок-ан-влен' => 'vaulx-en-velin',
|
||||
'венисьё' => 'venissieux',
|
||||
'кальюир-э-кюйр' => 'caluire-et-cuire',
|
||||
'сент-фуа-ле-льон' => 'sainte-foy-les-lyon',
|
||||
'гренобль' => 'grenoble',
|
||||
'сен-этьен' => 'saint-etienne',
|
||||
'клермон-ферран' => 'clermont-ferrand',
|
||||
'ансии' => 'annecy',
|
||||
'валанс' => 'valence',
|
||||
'шамбери' => 'chambery',
|
||||
'бур-ж-ан-бресс' => 'bourg-en-bresse',
|
||||
'роан' => 'roanne',
|
||||
'ойонна' => 'oyonnax',
|
||||
'анномас' => 'annemasse',
|
||||
'тнон-ле-бен' => 'thonon-les-bains',
|
||||
'эвиан-ле-бен' => 'evian-les-bains',
|
||||
'боннвиль' => 'bonneville',
|
||||
'вирьё-шательон' => 'villefontaine', /* approx area alias */
|
||||
'белле' => 'belley',
|
||||
|
||||
/* ================= Provence–Alpes–Côte d’Azur ================= */
|
||||
'марсель' => 'marseille',
|
||||
'ницца' => 'nice',
|
||||
'тулон' => 'toulon',
|
||||
'авиньон' => 'avignon',
|
||||
'канны' => 'cannes',
|
||||
'антиб' => 'antibes',
|
||||
'грас' => 'grasse',
|
||||
'экс-ан-прованс' => 'aix-en-provence',
|
||||
'арль' => 'arles',
|
||||
'фрежюс' => 'frejus',
|
||||
'драгиньян' => 'draguignan',
|
||||
'йер' => 'hyeres',
|
||||
'ла-сейн-сюр-мер' => 'la seyne-sur-mer',
|
||||
'ла-сьота' => 'la ciotat',
|
||||
'мартг' => 'martigues',
|
||||
'салон-де-прованс' => 'salon-de-provence',
|
||||
'оранж' => 'orange',
|
||||
'карпантра' => 'carpentras',
|
||||
'маноск' => 'manosque',
|
||||
'кавайон' => 'cavaillon',
|
||||
'мантон' => 'menton',
|
||||
'сен-рафаэль' => 'saint-raphael',
|
||||
'канн-сюр-мер' => 'cagnes-sur-mer',
|
||||
|
||||
/* ================= Occitanie ================= */
|
||||
'тулуза' => 'toulouse',
|
||||
'монпелье' => 'montpellier',
|
||||
'ним' => 'nimes',
|
||||
'перпиньян' => 'perpignan',
|
||||
'безье' => 'beziers',
|
||||
'нарбон' => 'narbonne',
|
||||
'каркасон' => 'carcassonne',
|
||||
'альби' => 'albi',
|
||||
'тарб' => 'tarbes',
|
||||
'ош' => 'auch',
|
||||
'каор' => 'cahors',
|
||||
'рудез' => 'rodez',
|
||||
'монто-бан' => 'montauban',
|
||||
'сет' => 'sete',
|
||||
'коломье' => 'colomiers',
|
||||
'бланьяк' => 'blagnac',
|
||||
'кастр' => 'castres',
|
||||
|
||||
/* ================= Nouvelle-Aquitaine ================= */
|
||||
'бордо' => 'bordeaux',
|
||||
'лимож' => 'limoges',
|
||||
'пуа' => 'poitiers',
|
||||
'пау' => 'pau',
|
||||
'байонна' => 'bayonne',
|
||||
'биарриц' => 'biarritz',
|
||||
'ла-рошель' => 'la rochelle',
|
||||
'ниор' => 'niort',
|
||||
'ангулем' => 'angouleme',
|
||||
'перигё' => 'perigueux',
|
||||
'мон-де-марсан' => 'mont-de-marsan',
|
||||
'брив-ла-гайард' => 'brive-la-gaillarde',
|
||||
'бержерак' => 'bergerac',
|
||||
'ажен' => 'agen',
|
||||
'анксьон' => 'anglet',
|
||||
|
||||
/* ================= Grand Est ================= */
|
||||
'страсбург' => 'strasbourg',
|
||||
'реймс' => 'reims',
|
||||
'мец' => 'metz',
|
||||
'нанси' => 'nancy',
|
||||
'мюлуз' => 'mulhouse',
|
||||
'труа' => 'troyes',
|
||||
'колмар' => 'colmar',
|
||||
'эпиналь' => 'epinal',
|
||||
'шалон-ан-шампань' => 'chalons-en-champagne',
|
||||
'шарлевиль-мезьер' => 'charleville-mezieres',
|
||||
'седан' => 'sedan',
|
||||
'сент-дизье' => 'saint-dizier',
|
||||
'саррегемин' => 'sarreguemines',
|
||||
'илкирш-графенштаден' => 'illkirch-graffenstaden',
|
||||
'сент-луи' => 'saint-louis',
|
||||
'агно' => 'haguenau',
|
||||
'томблен' => 'tomblaine',
|
||||
|
||||
/* ================= Hauts-de-France ================= */
|
||||
'лилль' => 'lille',
|
||||
'рубай' => 'roubaix',
|
||||
'туркуэн' => 'tourcoing',
|
||||
'дакс' => 'dax', /* ignore if not needed */
|
||||
'амиен' => 'amiens',
|
||||
'валансен' => 'valenciennes',
|
||||
'дуэ' => 'douai',
|
||||
'денкерк' => 'dunkerque',
|
||||
'кале' => 'calais',
|
||||
'компьень' => 'compiegne',
|
||||
'сен-кантен' => 'saint-quentin',
|
||||
'аррас' => 'arras',
|
||||
'булонь-сюр-мер' => 'boulogne-sur-mer',
|
||||
'абвиль' => 'abbeville',
|
||||
'ламберсар' => 'lambersart',
|
||||
'вилнев-д-аск' => 'villeneuve-d-ascq',
|
||||
'курриер' => 'courrieres',
|
||||
'ли-ленн' => 'lievin',
|
||||
|
||||
/* ================= Pays de la Loire ================= */
|
||||
'нант' => 'nantes',
|
||||
'анже' => 'angers',
|
||||
'ле-ман' => 'le mans',
|
||||
'сен-назер' => 'saint-nazaire',
|
||||
'шоле' => 'cholet',
|
||||
'ла-рош-сюр-йон' => 'la roche-sur-yon',
|
||||
'лаваль' => 'laval',
|
||||
|
||||
/* ================= Bretagne ================= */
|
||||
'ренн' => 'rennes',
|
||||
'брест' => 'brest',
|
||||
'квенпер' => 'quimper',
|
||||
'ван' => 'vannes',
|
||||
'сен-мало' => 'saint-malo',
|
||||
'лориан' => 'lorient',
|
||||
'сен-бриё' => 'saint-brieuc',
|
||||
|
||||
/* ================= Normandie ================= */
|
||||
'руан' => 'rouen',
|
||||
'ле-авр' => 'le havre',
|
||||
'ле-аврр' => 'le havre',
|
||||
'каен' => 'caen',
|
||||
'шербур' => 'cherbourg',
|
||||
'эвре' => 'evreux',
|
||||
'сен-ло' => 'saint-lo',
|
||||
'флер' => 'flers',
|
||||
'алансон' => 'alencon',
|
||||
|
||||
/* ================= Centre–Val de Loire ================= */
|
||||
'тур' => 'tours',
|
||||
'орлеан' => 'orleans',
|
||||
'шартр' => 'chartres',
|
||||
'бурж' => 'bourges',
|
||||
'блуа' => 'blois',
|
||||
'шатору' => 'chateauroux',
|
||||
|
||||
/* ================= Bourgogne–Franche-Comté ================= */
|
||||
'дижон' => 'dijon',
|
||||
'безансон' => 'besancon',
|
||||
'оссер' => 'auxerre',
|
||||
'макон' => 'macon',
|
||||
'невр' => 'nevers',
|
||||
'шалон-сюр-сон' => 'chalon-sur-saone',
|
||||
'дан-ле-золе' => 'dole',
|
||||
|
||||
/* ================= Grand Ouest divers ================= */
|
||||
'ла-манш' => 'saint-lo', /* dept seat hint */
|
||||
'пуатье' => 'poitiers',
|
||||
'сан-назар' => 'saint-nazaire',
|
||||
'сан-мало' => 'saint-malo',
|
||||
'конкарно' => 'concarneau',
|
||||
'морле' => 'morlaix',
|
||||
'сен-брюк' => 'saint-brieuc',
|
||||
'баянн' => 'bayonne', /* duplicate safe */
|
||||
|
||||
/* ================= Corse ================= */
|
||||
'аджакьо' => 'ajaccio',
|
||||
'бастия' => 'bastia',
|
||||
|
||||
/* ================= Outre-mer ================= */
|
||||
'фор-де-франс' => 'fort-de-france',
|
||||
'басс-тер' => 'basse-terre',
|
||||
'каенн' => 'cayenne',
|
||||
'сен-дени' => 'saint-denis',
|
||||
'мамудзу' => 'mamoudzou',
|
||||
'пуан-а-питр' => 'pointe-a-pitre',
|
||||
|
||||
/* ================= Autres communes fréquentes ================= */
|
||||
'нанси' => 'nancy',
|
||||
'реймс' => 'reims',
|
||||
'амьен' => 'amiens',
|
||||
'тур' => 'tours',
|
||||
'ниш' => 'nice',
|
||||
'монтабан' => 'montauban',
|
||||
'канн' => 'cannes',
|
||||
'анси' => 'annecy',
|
||||
'везул' => 'vesoul',
|
||||
'перига' => 'perigueux',
|
||||
'лейон' => 'lyon',
|
||||
|
||||
/* ===== Variantes pour Saint-/Sainte- (дублеты «сен-» / «сент-») ===== */
|
||||
'сент-этьен' => 'saint-etienne',
|
||||
'сент-мало' => 'saint-malo',
|
||||
'сент-назер' => 'saint-nazaire',
|
||||
'сент-дени' => 'saint-denis',
|
||||
'сент-ло' => 'saint-lo',
|
||||
'сент-омер' => 'saint-omer',
|
||||
'сент-рафаэль' => 'saint-raphael',
|
||||
'сент-квентин' => 'saint-quentin',
|
||||
'сент-жермен-ан-ле' => 'saint-germain-en-laye',
|
||||
'сент-мор-де-фоссе' => 'saint-maur-des-fosses',
|
||||
'сент-дизье' => 'saint-dizier',
|
||||
'сент-луи' => 'saint-louis',
|
||||
'сент-брие' => 'saint-brieuc',
|
||||
|
||||
/* ===== Variantes avec articles Le/La/Les ===== */
|
||||
'ле-авр' => 'le havre',
|
||||
'ле-ман' => 'le mans',
|
||||
'ла-рош-сюр-йон' => 'la roche-sur-yon',
|
||||
'ле-кане' => 'le cannet',
|
||||
'ле-мюро' => 'les mureaux',
|
||||
'ле-сабль-д-олон' => 'les sables-d-olonne',
|
||||
|
||||
/* ===== Agglo parisienne complémentaires ===== */
|
||||
'аржантей' => 'argenteuil',
|
||||
'монфермей' => 'montfermeil',
|
||||
'вильжюиф' => 'villejuif',
|
||||
'валь-де-репюблик' => 'val-de-reuil', /* approx */
|
||||
'нежен-сюр-сен' => 'gennevilliers',
|
||||
'оржи' => 'orges', /* ignore if not used */
|
||||
|
||||
/* ===== Sud-Ouest complémentaires ===== */
|
||||
'байон' => 'bayonne',
|
||||
'мон-де-марсан' => 'mont-de-marsan',
|
||||
'песак' => 'pesac', /* if stored */
|
||||
|
||||
/* ===== Côte d’Azur complémentaires ===== */
|
||||
'сан-лерен-дю-вар' => 'saint-laurent-du-var',
|
||||
'вилльфранш-сюр-мер' => 'villefranche-sur-mer',
|
||||
'ле-кане' => 'le cannet',
|
||||
|
||||
/* ===== Languedoc complémentaires ===== */
|
||||
'безье' => 'beziers',
|
||||
'альес' => 'ales',
|
||||
'агд' => 'agde',
|
||||
|
||||
/* ===== Savoie / Haute-Savoie ===== */
|
||||
'анси-ле-вье' => 'annecy',
|
||||
'кранс-же' => 'cran-gevrier',
|
||||
'анмас' => 'annemasse',
|
||||
|
||||
/* ===== Divers couverts par translit mais utiles en alias ===== */
|
||||
'луара' => 'loire',
|
||||
'исер' => 'isere',
|
||||
'вар' => 'var',
|
||||
'воклюз' => 'vaucluse',
|
||||
'кукурон' => 'cucuron',
|
||||
];
|
||||
@@ -0,0 +1,912 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@100..900&display=swap");
|
||||
|
||||
:root {
|
||||
--red: #e03c3f;
|
||||
--white: #ffffff;
|
||||
--blue: #38378b;
|
||||
--mint: #06ffa8;
|
||||
--hero: #8ca9ff; /* bleu clair fond hero (d’après maquette) */
|
||||
--header: #f1f1f1;
|
||||
--radius: 22px;
|
||||
|
||||
--c-bg: #8fa2ff; /* fond bleu/violet actuel de ton hero */
|
||||
--c-dark: #38378b; /* bleu foncé de ta charte */
|
||||
--c-white: #ffffff;
|
||||
--c-shadow: 0 10px 24px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
.only-mobile {
|
||||
display: none;
|
||||
}
|
||||
@media (max-width: 956px) {
|
||||
.only-desktop {
|
||||
display: none;
|
||||
}
|
||||
.only-mobile {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
/*Boutons page accueil du site*/
|
||||
.steps {
|
||||
width: min(800px, 92vw);
|
||||
margin: 40px auto 0;
|
||||
position: relative;
|
||||
display: grid;
|
||||
gap: 36px;
|
||||
}
|
||||
|
||||
.step {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
filter: drop-shadow(0 6px 10px rgba(0, 0, 0, 0.08));
|
||||
}
|
||||
|
||||
.step-badge {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
border-radius: 50%;
|
||||
background: var(--c-white);
|
||||
color: var(--c-dark);
|
||||
font-weight: 800;
|
||||
font-size: clamp(28px, 5vw, 56px);
|
||||
line-height: 1;
|
||||
box-shadow: var(--c-shadow);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.step-label {
|
||||
background: var(--c-white);
|
||||
color: var(--c-dark);
|
||||
font-weight: 700;
|
||||
font-size: clamp(16px, 2.6vw, 26px);
|
||||
padding: 14px 26px 14px 38px; /* pour chevauchement côté gauche */
|
||||
border-radius: 999px;
|
||||
white-space: nowrap;
|
||||
box-shadow: var(--c-shadow);
|
||||
margin-left: -30px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* Étape 3 inversée */
|
||||
.step--reverse {
|
||||
flex-direction: row-reverse; /* inverse ordre badge et label */
|
||||
}
|
||||
|
||||
.step--reverse .step-label {
|
||||
padding: 14px 38px 14px 26px; /* espace à droite */
|
||||
margin-left: 0;
|
||||
margin-right: -30px; /* chevauchement côté droit */
|
||||
}
|
||||
/* tailles différentes pour chaque étape */
|
||||
.step:nth-child(1) .step-badge {
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
font-size: clamp(28px, 5vw, 56px);
|
||||
}
|
||||
|
||||
.step:nth-child(2) .step-badge {
|
||||
width: 132px;
|
||||
height: 132px;
|
||||
font-size: clamp(32px, 5.5vw, 64px);
|
||||
}
|
||||
|
||||
.step:nth-child(3) .step-badge {
|
||||
width: 158px;
|
||||
height: 158px;
|
||||
font-size: clamp(36px, 6vw, 72px);
|
||||
}
|
||||
|
||||
/* placements (comme sur ta maquette) */
|
||||
.step--left {
|
||||
justify-self: start;
|
||||
transform: translateX(0);
|
||||
}
|
||||
.step--center {
|
||||
justify-self: center;
|
||||
transform: translateY(8px);
|
||||
padding-left: 230px;
|
||||
}
|
||||
.step--right {
|
||||
justify-self: end;
|
||||
transform: translateX(0);
|
||||
}
|
||||
/*Fin boutons rondes*/
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family:
|
||||
"Montserrat",
|
||||
system-ui,
|
||||
-apple-system,
|
||||
Segoe UI,
|
||||
Roboto,
|
||||
Arial,
|
||||
sans-serif;
|
||||
color: #111;
|
||||
/*background: var(--hero);*/
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto; /* header, contenu, footer */
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.site-header {
|
||||
background: var(--header);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
.site-header .container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 10px 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
/* ==== LOGO: mobile vs desktop (béton) ==== */
|
||||
.brand {
|
||||
letter-spacing: 0.5px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 0; /* évite petits décalages baseline */
|
||||
}
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px; /* distance logo ↔ langue */
|
||||
}
|
||||
.brand .logo-mobile,
|
||||
.brand .logo-desktop {
|
||||
display: none !important; /* on cache tout par défaut */
|
||||
height: 40px; /* taille unique (tu ajustes) */
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* Mobile: logo_u */
|
||||
@media (max-width: 767.98px) {
|
||||
.brand .logo-mobile {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Desktop: logo.png */
|
||||
@media (min-width: 768px) {
|
||||
.brand .logo-desktop {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Actions header */
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.icon-btn {
|
||||
border: none;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* HERO */
|
||||
.hero {
|
||||
padding: 0 0 56px;
|
||||
background: var(--hero);
|
||||
}
|
||||
.hero .container {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: 0 18px;
|
||||
}
|
||||
.hero-title {
|
||||
color: #38378b;
|
||||
text-align: center;
|
||||
font-size: 48px;
|
||||
line-height: 1.16;
|
||||
font-weight: 800;
|
||||
margin: 5px 0 26px;
|
||||
}
|
||||
|
||||
/* Bouton LOGIN */
|
||||
.btn-login {
|
||||
background: var(--red);
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
padding: 8px 14px;
|
||||
border-radius: 999px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.18);
|
||||
/*font-size: clamp(12px, 2vw, 16px);*/
|
||||
}
|
||||
|
||||
/* grille 3 au-dessus + 1 en dessous */
|
||||
|
||||
/* style des blocs ovales */
|
||||
|
||||
.hero .row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
margin: 15px 0;
|
||||
flex-wrap: wrap; /* au cas où ; reste cohérent */
|
||||
}
|
||||
|
||||
.chip {
|
||||
appearance: none;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
color: #000000;
|
||||
width: 230px; /* largeur fixe */
|
||||
text-align: center; /* texte centré */
|
||||
padding: 12px 0; /* hauteur uniforme */
|
||||
border-radius: 15px; /* ovale */
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.15);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* Footer plus large (haut) et gris, toujours en bas */
|
||||
footer {
|
||||
background: var(--header);
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.06);
|
||||
padding: 22px 20px; /* plus haut qu’avant */
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
color: #222;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/*===================================================================================================================*/
|
||||
|
||||
/*La partie affichage pour la page aprés la recherche*/
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 18px;
|
||||
}
|
||||
|
||||
/* Liste : espace entre blocs */
|
||||
.pro-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 40px; /* augmente l’écart vertical */
|
||||
padding: 20px 0 40px;
|
||||
}
|
||||
|
||||
/* Carte = 4 colonnes : photo | infos | vidéo | bouton */
|
||||
.pro-card {
|
||||
background: #fff;
|
||||
border-radius: 28px;
|
||||
box-shadow: 0 14px 30px rgba(0, 0, 0, 0.18);
|
||||
display: grid;
|
||||
/* NE PAS forcer start ici */
|
||||
align-items: stretch; /* <— important */
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
grid-template-columns: 260px 1.5fr 240px 180px;
|
||||
grid-template-areas: "photo info video cta";
|
||||
}
|
||||
|
||||
/* 1) Photo portrait, non déformée (recadrage OK) */
|
||||
.pro-photo {
|
||||
grid-area: photo;
|
||||
}
|
||||
.pro-photo img {
|
||||
width: 100%;
|
||||
aspect-ratio: 3/4; /* portrait */
|
||||
object-fit: cover; /* pas de déformation */
|
||||
display: block;
|
||||
border-radius: 22px;
|
||||
}
|
||||
|
||||
/* 2) Infos */
|
||||
.pro-info {
|
||||
grid-area: info;
|
||||
padding: 0 6px;
|
||||
color: #1b1b1b;
|
||||
}
|
||||
.pro-name {
|
||||
margin: 0 0 8px;
|
||||
font-size: 26px;
|
||||
font-weight: 800;
|
||||
}
|
||||
.pro-spec {
|
||||
color: #38378b;
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.pro-desc {
|
||||
color: #3f3f3f;
|
||||
line-height: 1.45;
|
||||
font-size: 1.05rem; /* augmente légèrement la taille */
|
||||
max-width: 95%; /* laisse plus d’espace dans la colonne */
|
||||
}
|
||||
.pro-info .favori {
|
||||
top: 10px;
|
||||
right: 10px; /* passe à left:10px si tu le veux à gauche */
|
||||
z-index: 2;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
filter: drop-shadow(0 2px 6px rgba(6, 6, 6, 0.25));
|
||||
transition:
|
||||
transform 0.15s ease,
|
||||
opacity 0.15s ease;
|
||||
}
|
||||
.pro-info .favori:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.pro-info .favori.ajoute {
|
||||
color: rgb(12, 12, 11);
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
/* 3) Vidéo (vignette 16:9) */
|
||||
/* même gabarit pour lien vidéo ET placeholder */
|
||||
.pro-video,
|
||||
.pro-video.placeholder {
|
||||
position: relative;
|
||||
display: block;
|
||||
aspect-ratio: 4 / 3; /* <- ton format souhaité */
|
||||
max-height: 180px;
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
background: #000; /* évite les bordures blanches */
|
||||
}
|
||||
|
||||
/* l'image occupe tout l'espace du cadre */
|
||||
.pro-video img,
|
||||
.pro-video.placeholder img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* optionnel: largeur de la colonne vidéo */
|
||||
.pro-video-col {
|
||||
display: flex;
|
||||
align-items: center; /* centre verticalement dans la colonne */
|
||||
justify-content: center;
|
||||
width: clamp(240px, 32vw, 200px);
|
||||
}
|
||||
|
||||
/* 4) Bouton dans sa colonne, centré verticalement et aligné à droite */
|
||||
.pro-cta-col {
|
||||
grid-area: cta;
|
||||
display: flex;
|
||||
align-items: center; /* centre verticalement */
|
||||
justify-content: center; /* centre horizontalement */
|
||||
}
|
||||
|
||||
.pro-cta {
|
||||
background: #e03c3f;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
padding: 10px 18px;
|
||||
border-radius: 999px;
|
||||
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.18);
|
||||
white-space: nowrap;
|
||||
/*font-size: 0.95rem;*/
|
||||
}
|
||||
|
||||
/* Langue choix*/
|
||||
/* Sélecteur de langue dans le header */
|
||||
.lang-switch {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
height: 42px;
|
||||
background: #fff;
|
||||
padding: 6px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.lang-btn {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
color: #38378b; /* bleu foncé */
|
||||
border-radius: 14px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.lang-btn:hover {
|
||||
background: rgba(56, 55, 139, 0.08);
|
||||
}
|
||||
|
||||
.lang-btn.active {
|
||||
background: #38378b; /* bleu foncé */
|
||||
color: #fff;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/* Compact */
|
||||
.pro-card--sm {
|
||||
--pro-card-pad: 12px;
|
||||
--pro-card-gap: 10px;
|
||||
--pro-avatar: 72px;
|
||||
--pro-title: 16px;
|
||||
--pro-lines: 2;
|
||||
}
|
||||
|
||||
/* Large */
|
||||
.pro-card--lg {
|
||||
--pro-card-pad: 22px;
|
||||
--pro-card-gap: 18px;
|
||||
--pro-avatar: 100px;
|
||||
--pro-title: 20px;
|
||||
--pro-lines: 4;
|
||||
}
|
||||
|
||||
.results-title,
|
||||
.results-meta,
|
||||
.result-count {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-logo {
|
||||
--bs-btn-bg: #38378b;
|
||||
--bs-btn-border-color: #38378b;
|
||||
--bs-btn-hover-bg: #2f2e77;
|
||||
--bs-btn-hover-border-color: #2f2e77;
|
||||
--bs-btn-active-bg: #24235e;
|
||||
--bs-btn-active-border-color: #24235e;
|
||||
--bs-btn-color: #fff; /* blanc par défaut */
|
||||
--bs-btn-focus-shadow-rgb: 56, 55, 139; /* halo focus */
|
||||
}
|
||||
|
||||
/* On force la couleur du texte à blanc dans tous les états */
|
||||
.btn-logo,
|
||||
.btn-logo:hover,
|
||||
.btn-logo:active,
|
||||
.btn-logo:focus,
|
||||
.btn-logo:focus-visible {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
/* Menu compact, largeur auto comme dans ta maquette */
|
||||
.help-menu {
|
||||
min-width: auto; /* override Bootstrap */
|
||||
width: max-content; /* s’adapte au contenu */
|
||||
max-width: 92vw; /* borne mobile */
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
}
|
||||
.help-menu .dropdown-item {
|
||||
padding: 10px 14px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.help-menu .dropdown-item + .dropdown-item {
|
||||
margin-top: 6px;
|
||||
} /* petits espacements */
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.help-menu {
|
||||
max-width: 360px;
|
||||
} /* borne desktop */
|
||||
}
|
||||
|
||||
/* Taille tactile du bouton “?” */
|
||||
.question-btn {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Menu plus étroit (recommandé) */
|
||||
.offcanvas-slim {
|
||||
--bs-offcanvas-width: clamp(240px, 80vw, 320px);
|
||||
}
|
||||
|
||||
/* Option: largeur calée au titre (plus “compacte”) */
|
||||
/* .offcanvas-slim{ --bs-offcanvas-width: fit-content; max-width: min(90vw, 360px); } */
|
||||
|
||||
/* Fallback minimal sans Bootstrap */
|
||||
.d-none {
|
||||
display: none !important;
|
||||
}
|
||||
.d-sm-inline {
|
||||
display: none !important;
|
||||
}
|
||||
.d-sm-none {
|
||||
display: inline !important;
|
||||
}
|
||||
.text-nowrap {
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.d-sm-inline {
|
||||
display: inline !important;
|
||||
}
|
||||
.d-sm-none {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ==== VISUEL COMMUN POUR TOUS LES MENUS ==== */
|
||||
.menu-panel {
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
|
||||
min-width: 230px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
/* Items menu principal (li custom OU .dropdown-item Bootstrap) */
|
||||
.menu-panel > li,
|
||||
.menu-panel .dropdown-item {
|
||||
padding: 12px 18px;
|
||||
font-size: 16px;
|
||||
line-height: 1.25;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.menu-panel > li + li,
|
||||
.menu-panel .dropdown-item + .dropdown-item {
|
||||
border-top: 1px solid #f1f1f1;
|
||||
}
|
||||
|
||||
.menu-panel > li:hover,
|
||||
.menu-panel .dropdown-item:hover {
|
||||
background: #f8f9fa; /* juste fond gris clair */
|
||||
}
|
||||
|
||||
.menu-panel a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* ==== MENU “BURGER” CUSTOM ==== */
|
||||
.menu-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-menu-custom {
|
||||
position: absolute;
|
||||
top: calc(100% + 8px);
|
||||
right: 0;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
display: none; /* fermé par défaut */
|
||||
z-index: 1100;
|
||||
}
|
||||
|
||||
.dropdown-menu-custom.is-open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Sous-menu */
|
||||
.has-submenu .submenu-toggle {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
background: none;
|
||||
border: 0;
|
||||
padding: 0.55rem 1rem;
|
||||
font-weight: 400;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.submenu {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
.submenu > li {
|
||||
padding: 0.4rem 1rem 0.4rem 1.5rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.submenu > li:hover {
|
||||
background: #fff; /* juste fond gris clair */
|
||||
}
|
||||
|
||||
.submenu[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ==== MENU “?” BOOTSTRAP ==== */
|
||||
.dropdown-menu.menu-panel {
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
|
||||
min-width: 230px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
/* Sécurité : que rien ne coupe les menus */
|
||||
.site-header,
|
||||
.header-actions,
|
||||
.menu-wrap,
|
||||
.dropdown {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* Bouton avatar (menu principal) */
|
||||
#btnMenu {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent; /* enlève flash bleu sur mobile */
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
/* Supprime le halo/cadre bleu par défaut */
|
||||
#btnMenu:focus,
|
||||
#btnMenu:active {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Si tu veux garder une indication focus clavier accessible */
|
||||
#btnMenu:focus-visible {
|
||||
outline: 2px solid var(--color-blue); /* ou une couleur douce */
|
||||
outline-offset: 3px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.footer-top-links {
|
||||
color: #38378b;
|
||||
font-weight: 600;
|
||||
}
|
||||
.footer-top-links a {
|
||||
color: inherit; /* hérite du bleu foncé */
|
||||
}
|
||||
|
||||
/* ===== Personnalisation bandeau cookies (CookieConsent v3) ===== */
|
||||
#cc-main {
|
||||
--cc-font-family: "Montserrat", sans-serif;
|
||||
/* Couleurs de texte */
|
||||
--cc-primary-color: #38378b; /* titres/links -> bleu foncé */
|
||||
--cc-secondary-color: #38378b; /* paragraphe du bandeau */
|
||||
|
||||
/* Bouton principal (Accepter tout) -> rouge arrondi */
|
||||
--cc-btn-border-radius: 22px;
|
||||
--cc-btn-primary-bg: #e03c3f;
|
||||
--cc-btn-primary-color: #ffffff;
|
||||
--cc-btn-primary-border-color: #e03c3f;
|
||||
--cc-btn-primary-hover-bg: #c92f33; /* rouge un peu plus foncé */
|
||||
--cc-btn-primary-hover-color: #ffffff;
|
||||
--cc-btn-primary-hover-border-color: #c92f33;
|
||||
|
||||
/* Boutons secondaires (Refuser / Paramétrer) -> fond blanc, bord gris, arrondi */
|
||||
--cc-btn-secondary-bg: #ffffff;
|
||||
--cc-btn-secondary-color: #38378b;
|
||||
--cc-btn-secondary-border-color: #d9dee3;
|
||||
--cc-btn-secondary-hover-bg: #f4f6f8;
|
||||
--cc-btn-secondary-hover-color: #38378b;
|
||||
--cc-btn-secondary-hover-border-color: #c9d0d6;
|
||||
|
||||
/* Séparateur & fond pieds/bords pour un rendu plus clean */
|
||||
--cc-separator-border-color: #eef2f5;
|
||||
--cc-footer-bg: #ffffff;
|
||||
--cc-footer-color: #38378b;
|
||||
|
||||
/* Optionnel: arrondir la carte elle-même */
|
||||
--cc-modal-border-radius: 22px;
|
||||
}
|
||||
|
||||
/* Si tu veux forcer l’arrondi aussi quand des classes sont injectées */
|
||||
#cc-main .cm__btn,
|
||||
#cc-main .pm__btn {
|
||||
border-radius: 22px;
|
||||
}
|
||||
|
||||
/*===================================================================================================================*/
|
||||
/* --- Container modal --- */
|
||||
.vmodal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: none;
|
||||
z-index: 99999;
|
||||
}
|
||||
.vmodal[aria-hidden="false"] {
|
||||
display: block;
|
||||
}
|
||||
.vmodal__backdrop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
/* --- Boîte --- */
|
||||
.vmodal__dialog {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
max-width: 1000px;
|
||||
width: 92%;
|
||||
margin: 40px auto;
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25);
|
||||
overflow: hidden; /* pour arrondis propres */
|
||||
}
|
||||
|
||||
/* --- Header “Bootstrap-like” --- */
|
||||
.vmodal__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.85rem 1rem;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
||||
background: #fff;
|
||||
}
|
||||
.vmodal__title {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
/* --- Close visible (rond blanc) --- */
|
||||
.vmodal__close {
|
||||
border: 0;
|
||||
background: #fff;
|
||||
color: #111;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 999px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
.vmodal__close:hover {
|
||||
filter: brightness(0.95);
|
||||
}
|
||||
|
||||
/* --- Body + ratio vidéo --- */
|
||||
.vmodal__body {
|
||||
padding: 1rem;
|
||||
}
|
||||
.vmodal__ratio {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 16/9;
|
||||
overflow: hidden;
|
||||
border-radius: 12px;
|
||||
background: #000;
|
||||
}
|
||||
.vmodal__ratio iframe {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* --- Footer --- */
|
||||
.vmodal__footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.5rem;
|
||||
padding: 0.85rem 1rem;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.08);
|
||||
background: #fff;
|
||||
}
|
||||
.btn.btn-secondary-like {
|
||||
appearance: none;
|
||||
border: 1px solid #ced4da;
|
||||
background: #f8f9fa;
|
||||
color: #212529;
|
||||
padding: 0.5rem 0.9rem;
|
||||
border-radius: 0.35rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn.btn-secondary-like:hover {
|
||||
background: #e9ecef;
|
||||
}
|
||||
.no-results {
|
||||
text-align: center; /* centre le texte */
|
||||
color: #fff; /* texte en blanc */
|
||||
}
|
||||
/* --- Mobile --- */
|
||||
@media (max-width: 575.98px) {
|
||||
.vmodal__dialog {
|
||||
margin: 16px auto;
|
||||
width: 96%;
|
||||
}
|
||||
.vmodal__title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.pager {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin: 22px 0;
|
||||
}
|
||||
.pager a {
|
||||
text-decoration: none;
|
||||
font-weight: 700;
|
||||
}
|
||||
.pager-arrow {
|
||||
padding: 10px 18px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid #e0e3eb;
|
||||
background: #fff;
|
||||
color: #38378b;
|
||||
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.06);
|
||||
transition:
|
||||
background 0.2s,
|
||||
transform 0.1s,
|
||||
opacity 0.2s;
|
||||
}
|
||||
.pager-arrow:hover {
|
||||
background: #f8f9ff;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.pager-arrow.is-disabled {
|
||||
opacity: 0.4;
|
||||
pointer-events: none;
|
||||
}
|
||||
.pager-page {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #e0e3eb;
|
||||
background: #fff;
|
||||
color: #38378b;
|
||||
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.06);
|
||||
transition:
|
||||
background 0.2s,
|
||||
transform 0.1s,
|
||||
color 0.2s,
|
||||
border-color 0.2s;
|
||||
}
|
||||
.pager-page:hover {
|
||||
background: #f8f9ff;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.pager-page.is-active {
|
||||
background: #e03c3f;
|
||||
color: #fff;
|
||||
border-color: #e03c3f;
|
||||
transform: none;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
// Si quelqu'un essaie d'appeler directement /includes/db.php dans le navigateur, on coupe
|
||||
if (isset($_SERVER['SCRIPT_FILENAME']) && realpath(__FILE__) === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||
http_response_code(403);
|
||||
exit('
|
||||
<!doctype html>
|
||||
<html lang="ru"><head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>403 — Accès refusé</title>
|
||||
<style>
|
||||
html,body{height:100%;margin:0}
|
||||
body{display:flex;align-items:center;justify-content:center;background:#f7f7f9;font-family:system-ui,Segoe UI,Roboto,Arial}
|
||||
.card{max-width:560px;width:92%;background:#fff;border:1px solid #e5e7eb;border-radius:14px;box-shadow:0 8px 24px rgba(0,0,0,.08);padding:28px;text-align:center}
|
||||
.emoji{font-size:48px;margin-bottom:8px}
|
||||
h1{margin:6px 0 10px;font-size:22px}
|
||||
p{margin:0 0 18px;color:#6b7280}
|
||||
.row{display:flex;gap:10px;justify-content:center;flex-wrap:wrap}
|
||||
.btn{padding:10px 14px;border-radius:999px;text-decoration:none;display:inline-block}
|
||||
.btn-primary{background:#E03C3F;color:#fff}
|
||||
.btn-outline{border:1px solid #d1d5db;color:#fff;background:#38378B}
|
||||
</style>
|
||||
</head><body>
|
||||
<div class="card">
|
||||
<div class="emoji"></div>
|
||||
<h1>Accès refusé</h1>
|
||||
<p>Vous n’êtes pas autorisé à consulter cette page.</p>
|
||||
<div class="row">
|
||||
<a class="btn btn-primary" href="/connexion.php">Se connecter</a>
|
||||
<a class="btn btn-outline" href="/">Retour à l’accueil</a>
|
||||
</div>
|
||||
</div>
|
||||
</body></html>');
|
||||
}
|
||||
$host = getenv('APP_DB_HOST') ?: 'localhost';
|
||||
$dbname = getenv('APP_DB_NAME') ?: '';
|
||||
$user = getenv('APP_DB_USER') ?: '';
|
||||
$password = getenv('APP_DB_PASS') ?: '';
|
||||
$charset = getenv('APP_DB_CHARSET') ?: 'utf8mb4';
|
||||
|
||||
|
||||
try {
|
||||
$pdo = new PDO(
|
||||
"mysql:host=$host;dbname=$dbname;charset=$charset",
|
||||
$user,
|
||||
$password,
|
||||
[
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
]
|
||||
);
|
||||
} catch (PDOException $e) {
|
||||
error_log('DB connect error: ' . $e->getMessage());
|
||||
http_response_code(500);
|
||||
exit('Erreur de connexion à la base de données. racine');
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,507 @@
|
||||
/* =========================================================
|
||||
1) Liens / Chips
|
||||
========================================================= */
|
||||
.chip-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
2) Grand panneau blanc
|
||||
========================================================= */
|
||||
.home-panel {
|
||||
max-width: 850px;
|
||||
margin: 22px auto 0;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
border: 1px solid rgba(255, 255, 255, 0.45);
|
||||
border-radius: 22px;
|
||||
/*box-shadow: 0 10px 26px rgba(0, 0, 0, 0.14);*/
|
||||
overflow: hidden;
|
||||
}
|
||||
.home-panel__inner {
|
||||
padding: 18px 16px;
|
||||
}
|
||||
.home-panel__divider {
|
||||
height: 1px;
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
margin: 14px 0;
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
3) Checklist
|
||||
========================================================= */
|
||||
.home-checklist {
|
||||
padding: 14px 14px 10px;
|
||||
}
|
||||
.home-checklist ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
.home-checklist li {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
color: #1b1b1b;
|
||||
}
|
||||
.home-check {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 999px;
|
||||
background: rgba(6, 255, 168, 0.18);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
.home-check svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
4) Titres
|
||||
========================================================= */
|
||||
.home-h2 {
|
||||
margin: 0 0 12px;
|
||||
font-size: 22px;
|
||||
font-weight: 800;
|
||||
color: #1b1b1b;
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
5) Liste praticiens
|
||||
========================================================= */
|
||||
.hp-list {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* --- Carte --- */
|
||||
.hp-card {
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.08);
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
/* --- Ligne 1 : avatar | texte | rating --- */
|
||||
.hp-row1 {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.hp-avatar {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 14px;
|
||||
object-fit: cover;
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.hp-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.hp-name {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
color: #111;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.hp-spec {
|
||||
margin-top: 2px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
/* --- Rating (étoiles base + fill) --- */
|
||||
.hp-rating {
|
||||
text-align: right;
|
||||
min-width: 110px;
|
||||
}
|
||||
|
||||
.hp-stars {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
font-size: 13px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.hp-stars .stars-base {
|
||||
color: #ddd;
|
||||
}
|
||||
.hp-stars .stars-fill {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
color: #f5b301;
|
||||
}
|
||||
|
||||
.hp-note {
|
||||
margin-top: 2px;
|
||||
font-size: 0.85rem;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
/* --- Ligne 2 : Online (gauche) | bouton (droite) --- */
|
||||
.hp-row2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
padding-left: 68px; /* aligné sous le texte (56 avatar + gap) */
|
||||
}
|
||||
|
||||
.hp-online {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #111;
|
||||
opacity: 0.9;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* --- Bouton --- */
|
||||
.hp-btn {
|
||||
background: var(--red, #e03c3f);
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
border-radius: 999px;
|
||||
padding: 10px 14px;
|
||||
min-width: 110px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.16);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
6) Mini steps
|
||||
========================================================= */
|
||||
.hp-steps {
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.08);
|
||||
padding: 12px 14px;
|
||||
}
|
||||
|
||||
/* --- Nouveau : entête (sobre) --- */
|
||||
.hp-steps__head {
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.hp-steps__title {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 900;
|
||||
color: #111;
|
||||
}
|
||||
.hp-steps__sub {
|
||||
margin: 6px 0 0;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
/* --- Nouveau : grille steps propre (sans <br>) --- */
|
||||
.hp-steps__grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.hp-steps__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.hp-steps__arr {
|
||||
color: #6b7280;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
7) Articles
|
||||
========================================================= */
|
||||
.hp-articles {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
.ha {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.08);
|
||||
overflow: hidden;
|
||||
color: #111;
|
||||
}
|
||||
.ha-img {
|
||||
height: 92px;
|
||||
background: #e9edf8;
|
||||
}
|
||||
.ha-body {
|
||||
padding: 10px 12px 12px;
|
||||
}
|
||||
.ha-title {
|
||||
margin: 0 0 6px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.ha-sub {
|
||||
margin: 0;
|
||||
font-size: 12.5px;
|
||||
font-weight: 400;
|
||||
color: #6b7280;
|
||||
}
|
||||
.hp-all {
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.hp-all a {
|
||||
font-weight: 600;
|
||||
color: #1f3bb3;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.spec-cta-wrap {
|
||||
margin-top: 24px;
|
||||
padding: 28px 22px;
|
||||
border-radius: 12px;
|
||||
|
||||
background: #8ca9ff;
|
||||
box-shadow: 0 20px 40px rgba(127, 91, 255, 0.2);
|
||||
|
||||
color: #fff;
|
||||
|
||||
/* ⬇️ IMPORTANT */
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto; /* gauche | milieu | droite */
|
||||
align-items: center;
|
||||
column-gap: 22px;
|
||||
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Titre */
|
||||
.spec-cta-wrap h3 {
|
||||
margin: 0;
|
||||
font-size: 34px; /* pour se rapprocher de la capture 2 */
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
white-space: nowrap; /* optionnel: évite retour ligne */
|
||||
}
|
||||
|
||||
.spec-cta-wrap p {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
opacity: 0.95;
|
||||
line-height: 1.35;
|
||||
max-width: 520px; /* pour un look “milieu” */
|
||||
}
|
||||
|
||||
/* Bouton blanc */
|
||||
.spec-cta {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
white-space: nowrap;
|
||||
|
||||
padding: 12px 24px;
|
||||
border-radius: 999px;
|
||||
|
||||
background: #fff;
|
||||
color: #8ca9ff;
|
||||
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
|
||||
text-decoration: none;
|
||||
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.12);
|
||||
|
||||
transition:
|
||||
transform 0.18s ease,
|
||||
box-shadow 0.18s ease;
|
||||
}
|
||||
|
||||
/* Hover */
|
||||
.spec-cta:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 12px 22px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
/* Active */
|
||||
.spec-cta:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
/* Barre style "carte" */
|
||||
.searchbar {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 10px 14px;
|
||||
max-width: 860px;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
margin-left: 6px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.input-wrap {
|
||||
position: relative;
|
||||
flex: 1 1 0;
|
||||
min-width: 0; /* IMPORTANT: évite débordements */
|
||||
}
|
||||
|
||||
/* Champs “plats” (comme sur ta capture) */
|
||||
.searchbar input {
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
font-size: 14px;
|
||||
padding: 10px 8px;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.searchbar input::placeholder {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
/* séparateur */
|
||||
.divider {
|
||||
width: 1px;
|
||||
height: 34px;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Bouton intégré à droite */
|
||||
.btn-find--inbar {
|
||||
margin: 0;
|
||||
border: none;
|
||||
background: var(--red);
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
padding: 10px 14px;
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.16);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn-find--inbar:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-find--inbar:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Suggestions dropdown */
|
||||
.suggest {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
list-style: none;
|
||||
margin: 6px 0 0;
|
||||
padding: 6px 0;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.14);
|
||||
max-height: 260px;
|
||||
overflow: auto;
|
||||
z-index: 20;
|
||||
}
|
||||
.suggest li {
|
||||
padding: 10px 12px;
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
}
|
||||
.suggest li:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
8) Mobile
|
||||
========================================================= */
|
||||
@media (max-width: 720px) {
|
||||
.hp-btn {
|
||||
min-width: 112px;
|
||||
padding: 9px 12px;
|
||||
}
|
||||
.hp-row2 {
|
||||
padding-left: 4px;
|
||||
}
|
||||
.ha-img {
|
||||
height: 90px;
|
||||
}
|
||||
.hp-articles {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.hp-steps__title {
|
||||
font-size: 17px;
|
||||
}
|
||||
.hp-steps__sub {
|
||||
font-size: 12.5px;
|
||||
}
|
||||
.hp-steps__item {
|
||||
font-size: 13.5px;
|
||||
}
|
||||
.spec-cta-wrap {
|
||||
grid-template-columns: 1fr;
|
||||
row-gap: 12px;
|
||||
text-align: center;
|
||||
justify-items: center;
|
||||
padding: 22px 18px;
|
||||
}
|
||||
|
||||
.spec-cta-wrap h3 {
|
||||
font-size: 26px;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.spec-cta-wrap p {
|
||||
font-size: 15px;
|
||||
max-width: 34ch;
|
||||
}
|
||||
|
||||
.spec-cta {
|
||||
width: 50%;
|
||||
max-width: 340px;
|
||||
}
|
||||
.hero {
|
||||
background: #f3f4f8;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
// NE PAS versionner ce fichier (ajoute-le au .gitignore)
|
||||
return [
|
||||
'smtp' => [
|
||||
'host' => 'smtp-relay.brevo.com',
|
||||
'port' => 587,
|
||||
'secure' => 'tls', // 'tls' (STARTTLS) ou 'ssl' si 465
|
||||
'auth' => true,
|
||||
'username' => getenv('BREVO_USERNAME') ?: '', // boîte SMTP dédiée
|
||||
'password' => getenv('BREVO_PASSWORD') ?: '', // ⚠️ remplace par le vrai mdp
|
||||
// 'password' => getenv('SMTP_PASS'), // (option) via variable d'env
|
||||
],
|
||||
'from' => ['no-reply@uslugi.fr', 'USLUGI.FR'], // From par défaut
|
||||
'reply_to' => ['contact@uslugi.fr', 'USLUGI.FR'], // Reply-To global
|
||||
'bounce' => 'bounce@uslugi.fr', // Return-Path
|
||||
'suppress_auto' => true, // Ajoute des entêtes pour réduire OOO/auto-replies
|
||||
];
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
$MAILCFG = require __DIR__.'/mail_config.php';
|
||||
|
||||
/**
|
||||
* Envoie un mail HTML via SMTP Gandi.
|
||||
*
|
||||
* @param string $to Adresse destinataire
|
||||
* @param string $subject Sujet (UTF-8)
|
||||
* @param string $html Corps HTML
|
||||
* @param string|null $text Alternative texte (sinon strip_tags)
|
||||
* @param array $opts ['to_name','cc'=>[],'bcc'=>[],'attachments'=>[],'headers'=>[],'from'=>[email,name],'reply_to'=>[email,name]]
|
||||
* @return array [bool success, ?string error]
|
||||
*/
|
||||
function uslugi_mail(string $to, string $subject, string $html, ?string $text = null, array $opts = []): array {
|
||||
global $MAILCFG;
|
||||
|
||||
$mail = new PHPMailer(true);
|
||||
try {
|
||||
// SMTP
|
||||
$mail->isSMTP();
|
||||
$mail->Host = $MAILCFG['smtp']['host'];
|
||||
$mail->Port = $MAILCFG['smtp']['port'];
|
||||
$mail->SMTPSecure = $MAILCFG['smtp']['secure'];
|
||||
$mail->SMTPAuth = $MAILCFG['smtp']['auth'];
|
||||
$mail->Username = $MAILCFG['smtp']['username'];
|
||||
$mail->Password = $MAILCFG['smtp']['password'];
|
||||
|
||||
// Base
|
||||
$mail->CharSet = 'UTF-8';
|
||||
$mail->Encoding = 'base64';
|
||||
$mail->XMailer = ''; // pas d'X-Mailer bavard
|
||||
|
||||
// Expéditeur / Reply-To
|
||||
$from = $opts['from'] ?? $MAILCFG['from'];
|
||||
$rpto = $opts['reply_to'] ?? $MAILCFG['reply_to'];
|
||||
$mail->setFrom($from[0], $from[1] ?? '');
|
||||
if (!empty($rpto[0])) $mail->addReplyTo($rpto[0], $rpto[1] ?? '');
|
||||
|
||||
// Return-Path (enveloppe) → SPF/DMARC alignés
|
||||
if (!empty($MAILCFG['bounce'])) $mail->Sender = $MAILCFG['bounce'];
|
||||
|
||||
// Destinataires
|
||||
$mail->addAddress($to, $opts['to_name'] ?? '');
|
||||
foreach (($opts['cc'] ?? []) as $cc) { $mail->addCC($cc); }
|
||||
foreach (($opts['bcc'] ?? []) as $bcc) { $mail->addBCC($bcc); }
|
||||
|
||||
// Pièces jointes
|
||||
foreach (($opts['attachments'] ?? []) as $k => $v) {
|
||||
if (is_int($k)) $mail->addAttachment($v);
|
||||
else $mail->addAttachment($k, $v);
|
||||
}
|
||||
|
||||
// Entêtes optionnelles
|
||||
foreach (($opts['headers'] ?? []) as $k => $v) {
|
||||
$mail->addCustomHeader($k, $v);
|
||||
}
|
||||
if (!empty($MAILCFG['suppress_auto'])) {
|
||||
$mail->addCustomHeader('Auto-Submitted', 'auto-generated');
|
||||
$mail->addCustomHeader('X-Auto-Response-Suppress', 'All');
|
||||
}
|
||||
|
||||
// Contenu
|
||||
$mail->isHTML(true);
|
||||
$mail->Subject = $subject; // PHPMailer encode proprement
|
||||
$mail->Body = $html;
|
||||
$mail->AltBody = $text ?? strip_tags($html);
|
||||
|
||||
$mail->send();
|
||||
return [true, null];
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('PHPMailer error: '.$mail->ErrorInfo);
|
||||
return [false, $mail->ErrorInfo];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700;800&display=swap" rel="stylesheet">
|
||||
<?php //Liste des langues disponibles pour traduction de site ?>
|
||||
<a href="/public/change_lang.php?lang=ru" class="lang-btn <?= ($_SESSION['lang'] ?? 'ru') === 'ru' ? 'active' : '' ?>">РУ</a>
|
||||
<a href="/public/change_lang.php?lang=fr" class="lang-btn <?= ($_SESSION['lang'] ?? 'ru') === 'fr' ? 'active' : '' ?>">FR</a>
|
||||
@@ -0,0 +1,937 @@
|
||||
/* ==========================================================================
|
||||
mobile.css — Overrides globaux mobile (≤ 956px)
|
||||
Charge CE FICHIER EN DERNIER
|
||||
========================================================================== */
|
||||
|
||||
@media (max-width: 956px) {
|
||||
/* ---------- Garde-fous généraux ---------- */
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow-x: hidden; /* coupe tout débordement horizontal */
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
img,
|
||||
video,
|
||||
iframe {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100svh; /* hauteur écran fiable iOS/Android */
|
||||
padding-left: env(safe-area-inset-left, 0);
|
||||
padding-right: env(safe-area-inset-right, 0);
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
padding-left: 16px !important;
|
||||
padding-right: 16px !important; /* ✅ symétrie gauche/droite */
|
||||
}
|
||||
|
||||
/* ==== VISUEL COMMUN POUR TOUS LES MENUS ==== */
|
||||
.menu-panel {
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
|
||||
min-width: 230px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
/* Items (li custom OU .dropdown-item Bootstrap) */
|
||||
.menu-panel > li,
|
||||
.menu-panel .dropdown-item {
|
||||
padding: 12px 18px;
|
||||
font-size: 16px;
|
||||
line-height: 1.25;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.menu-panel > li + li,
|
||||
.menu-panel .dropdown-item + .dropdown-item {
|
||||
border-top: 1px solid #f1f1f1;
|
||||
}
|
||||
|
||||
.menu-panel a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* ==== MENU “BURGER” CUSTOM ==== */
|
||||
.menu-wrap {
|
||||
position: relative;
|
||||
}
|
||||
.dropdown-menu-custom {
|
||||
position: absolute;
|
||||
top: calc(100% + 8px);
|
||||
right: 0; /* ← fini le -180px */
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
display: none; /* fermé par défaut */
|
||||
z-index: 1100;
|
||||
cursor: pointer;
|
||||
}
|
||||
.dropdown-menu-custom.is-open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Sous-menu admin (inchangé) */
|
||||
.has-submenu .submenu-toggle {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
background: none;
|
||||
border: 0;
|
||||
padding: 0.55rem 1rem;
|
||||
font-weight: 400;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
color: #000;
|
||||
}
|
||||
.submenu {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0 0 0.5rem 0;
|
||||
}
|
||||
.submenu > li {
|
||||
padding: 0.4rem 1rem 0.4rem 1.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.submenu > li:hover {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
.submenu[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ==== MENU “?” BOOTSTRAP ==== */
|
||||
.dropdown-menu.menu-panel {
|
||||
/* on garde le comportement Bootstrap */
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
|
||||
min-width: 230px;
|
||||
padding: 8px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Sécurité : que rien ne coupe les menus */
|
||||
.site-header,
|
||||
.header-actions,
|
||||
.menu-wrap,
|
||||
.dropdown {
|
||||
overflow: visible;
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* ---------- HERO ---------- */
|
||||
.hero-title {
|
||||
font-size: 40px;
|
||||
}
|
||||
.results-title,
|
||||
.results-meta,
|
||||
.result-count {
|
||||
margin-top: 12px;
|
||||
color: #38378b;
|
||||
text-align: center;
|
||||
}
|
||||
/* Chips : rangées centrées et espacées de façon homogène */
|
||||
.hero .row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center; /* ✅ centrage horizontal des chips */
|
||||
gap: 8px 10px; /* espace cohérent */
|
||||
}
|
||||
.chip {
|
||||
font-size: 12px;
|
||||
width: 110px; /* tu peux passer en auto + padding si tu veux */
|
||||
}
|
||||
|
||||
/* ---------- Steps (mobile) ---------- */
|
||||
.step-label {
|
||||
background: var(--c-white);
|
||||
color: var(--c-dark);
|
||||
font-weight: 700;
|
||||
font-size: clamp(13px, 2.6vw, 26px);
|
||||
padding: 14px 26px 14px 38px; /* pour chevauchement côté gauche */
|
||||
border-radius: 999px;
|
||||
white-space: nowrap;
|
||||
box-shadow: var(--c-shadow);
|
||||
margin-left: -30px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* tailles différentes pour chaque étape */
|
||||
.step:nth-child(1) .step-badge {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
font-size: clamp(41px, 5vw, 56px);
|
||||
}
|
||||
.step:nth-child(2) .step-badge {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
font-size: clamp(48px, 5.5vw, 64px);
|
||||
}
|
||||
.step:nth-child(3) .step-badge {
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
font-size: clamp(52px, 6vw, 72px);
|
||||
}
|
||||
|
||||
.step--left {
|
||||
justify-self: start;
|
||||
transform: translateX(0);
|
||||
}
|
||||
.step--right {
|
||||
justify-self: end;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
/* ❗️Supprime le décalage latéral au milieu (source d’asymétrie visuelle) */
|
||||
.step--center {
|
||||
justify-self: center;
|
||||
transform: translateY(12px);
|
||||
padding-left: 100px; /* ✅ avant : 100px → crée l’effet “plus d’espace à gauche” */
|
||||
}
|
||||
|
||||
/* ---------- Composants transverses ---------- */
|
||||
main,
|
||||
.main,
|
||||
.main1,
|
||||
.main2 {
|
||||
display: block !important;
|
||||
align-items: initial !important;
|
||||
justify-content: initial !important;
|
||||
background: transparent !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
width: 100% !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
.text-end {
|
||||
text-align: initial !important;
|
||||
}
|
||||
.text-end .btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card,
|
||||
.card * {
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.card,
|
||||
.card.shadow-sm.p-3,
|
||||
.card.shadow-sm.p-3.avis-card {
|
||||
min-width: 0 !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
overflow: hidden;
|
||||
border-radius: 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/*
|
||||
.profile-grid > * { min-width: 0; min-height: 0; }
|
||||
|
||||
.big-photo { width: 100% !important; max-height: 400px; overflow: hidden; border-radius: 16px; }
|
||||
.big-photo img { width: 100% !important; height: auto !important; object-fit: cover; display: block; }
|
||||
|
||||
.info-scroll, .vlist { max-height: none !important; overflow: visible !important; padding-right: 0 !important; }
|
||||
|
||||
.vlist { display: grid !important; grid-template-columns: 1fr !important; gap: 12px !important; }
|
||||
.vlist .vitem { width: 100%; border-radius: 16px; overflow: hidden; background: #000; aspect-ratio: 21 / 9; }
|
||||
.vlist .vitem img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
|
||||
.bottom-grid { display: grid !important; grid-template-columns: 1fr !important; gap: 12px !important; }
|
||||
.card .d-flex.align-items-center { flex-direction: column !important; align-items: flex-start !important; gap: 6px; }
|
||||
.avis-card .d-flex.justify-content-between { flex-direction: column !important; align-items: flex-start !important; gap: 4px; }
|
||||
.avis-card .meta + div { overflow: hidden; display: block; line-height: 1.5; max-height: calc(1.5em * 3); }
|
||||
|
||||
.big-photo, section.panel, aside.panel, .profile-grid, .bottom-grid, .card {
|
||||
position: static !important; float: none !important; transform: none !important;
|
||||
z-index: auto !important; margin-left: 0 !important; margin-right: 0 !important; clear: both !important;
|
||||
}*/
|
||||
|
||||
/* ---------- PROFIL ---------- */
|
||||
/* ---------- PROFIL (mobile) ---------- */
|
||||
.profile-grid {
|
||||
display: grid !important;
|
||||
grid-template-columns: 1fr !important; /* ← empile en 1 colonne */
|
||||
grid-auto-rows: auto !important;
|
||||
gap: 16px !important;
|
||||
}
|
||||
.profile-grid.profile-top {
|
||||
height: auto !important;
|
||||
} /* annule clamp() s'il existe */
|
||||
.profile-grid > * {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* annule les hauteurs "90%" du desktop */
|
||||
.profile-grid .panel {
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.big-photo {
|
||||
width: 100% !important;
|
||||
height: auto !important; /* ← annule le 90% */
|
||||
max-width: 100% !important;
|
||||
max-height: none !important; /* évite la “fenêtre” grise */
|
||||
overflow: hidden;
|
||||
border-radius: 16px;
|
||||
}
|
||||
.big-photo img {
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.info-scroll,
|
||||
.vlist {
|
||||
max-height: none !important;
|
||||
overflow: visible !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
.vlist {
|
||||
display: grid !important;
|
||||
grid-template-columns: 1fr !important;
|
||||
gap: 12px !important;
|
||||
}
|
||||
|
||||
.bottom-grid {
|
||||
display: grid !important;
|
||||
grid-template-columns: 1fr !important;
|
||||
gap: 12px !important;
|
||||
}
|
||||
|
||||
/* les cartes doivent pouvoir rétrécir */
|
||||
.card {
|
||||
min-width: 0 !important;
|
||||
}
|
||||
|
||||
/* ---------- LISTES / RÉSULTATS ---------- */
|
||||
.pro-list {
|
||||
gap: 16px !important;
|
||||
padding: 12px 0 24px !important;
|
||||
}
|
||||
.pro-card {
|
||||
display: grid !important;
|
||||
grid-template-columns: 1fr !important;
|
||||
grid-template-areas: "photo" "info" "video" "cta" !important;
|
||||
gap: 12px !important;
|
||||
padding: 16px !important;
|
||||
}
|
||||
.pro-photo img {
|
||||
width: 100% !important;
|
||||
aspect-ratio: 3 / 4;
|
||||
object-fit: cover;
|
||||
border-radius: 16px;
|
||||
}
|
||||
.pro-info {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.pro-video-col {
|
||||
width: 100% !important;
|
||||
display: block !important;
|
||||
}
|
||||
.pro-video,
|
||||
.pro-video.placeholder {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
max-height: none;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
background: #000;
|
||||
}
|
||||
.pro-cta-col {
|
||||
display: block !important;
|
||||
}
|
||||
.pro-cta {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ---------- FAVORIS ---------- */
|
||||
.praticien-card {
|
||||
display: block !important;
|
||||
}
|
||||
.praticien-card > div:first-child {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.praticien-card .card-img-top {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.praticien-card > .card-body.p-0 {
|
||||
margin-bottom: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
.praticien-card .d-flex {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.praticien-card .d-flex .btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* ---------- Suggestions (autocomplete) ---------- */
|
||||
.suggest,
|
||||
#suggestions {
|
||||
position: absolute !important;
|
||||
top: calc(100% + 8px);
|
||||
left: 0;
|
||||
right: 0; /* => même largeur que l'input */
|
||||
z-index: 2000;
|
||||
|
||||
background: #fff;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 14px;
|
||||
padding: 8px 0;
|
||||
margin: 0;
|
||||
|
||||
max-height: 50vh; /* scrollable si long */
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.12);
|
||||
width: auto !important; /* annule width:100% si besoin */
|
||||
}
|
||||
|
||||
.suggest li,
|
||||
#suggestions li {
|
||||
list-style: none;
|
||||
padding: 12px 16px;
|
||||
font-size: clamp(14px, 3.8vw, 18px);
|
||||
line-height: 1.25;
|
||||
cursor: pointer;
|
||||
}
|
||||
.suggest li + li,
|
||||
#suggestions li + li {
|
||||
border-top: 1px solid #f3f4f6;
|
||||
}
|
||||
.suggest li:hover,
|
||||
#suggestions li:hover {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
/* Largeurs figées à neutraliser en mobile */
|
||||
.pto-list {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.site-footer {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/*---------Transformation table en cards-----------------------*
|
||||
***************************************************************/
|
||||
/* ===== TABLE -> CARTES (mobile) ===== */
|
||||
@media (max-width: 768px) {
|
||||
/* ✅ N’affiche pas THEAD tant que data-label n’est pas prêt */
|
||||
#table_rdv thead {
|
||||
display: table-header-group;
|
||||
}
|
||||
.mc-ready #table_rdv thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* neutralise centrage hérité */
|
||||
#table_rdv,
|
||||
#table_rdv tr,
|
||||
#table_rdv td,
|
||||
#table_rdv td * {
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
/* wrapper bootstrap */
|
||||
.table-responsive {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
#table_rdv {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
#table_rdv tbody {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* chaque ligne -> carte */
|
||||
#table_rdv tr {
|
||||
display: block;
|
||||
margin: 12px 0;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.08);
|
||||
overflow: hidden;
|
||||
}
|
||||
#table_rdv tr::before {
|
||||
content: "";
|
||||
display: block;
|
||||
height: 3px;
|
||||
background: #38378b;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* cellules empilées */
|
||||
#table_rdv td {
|
||||
display: block;
|
||||
padding: 10px 14px !important;
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#table_rdv td + td {
|
||||
border-top: 1px dashed rgba(0, 0, 0, 0.08) !important;
|
||||
}
|
||||
|
||||
/* libellés auto (et on masque s’ils sont vides) */
|
||||
#table_rdv td::before {
|
||||
content: attr(data-label);
|
||||
display: inline-block;
|
||||
min-width: 120px;
|
||||
margin-right: 8px;
|
||||
font-weight: 600;
|
||||
color: #666;
|
||||
}
|
||||
#table_rdv td::before:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* hiérarchie visuelle (adapte si ordre diffère) */
|
||||
#table_rdv td:nth-child(1) {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
#table_rdv td:nth-child(2) {
|
||||
color: #6b7280;
|
||||
font-weight: 600;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
/* select pleine largeur + iOS align */
|
||||
#table_rdv select.form-select {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
font-size: 15px;
|
||||
border-radius: 10px;
|
||||
margin: 6px 0;
|
||||
text-align: left !important;
|
||||
text-align-last: left !important;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
/* actions */
|
||||
#table_rdv .row-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 6px;
|
||||
justify-content: flex-start !important;
|
||||
align-items: stretch !important;
|
||||
}
|
||||
#table_rdv .row-actions .btn {
|
||||
flex: 1 1 calc(50% - 4px);
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* neutralise utilitaires Bootstrap qui centrent */
|
||||
#table_rdv
|
||||
:is(.text-center, .text-sm-center, .text-md-center, .text-lg-center) {
|
||||
text-align: left !important;
|
||||
}
|
||||
#table_rdv :is(.mx-auto, .ms-auto, .me-auto) {
|
||||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
#table_rdv
|
||||
:is(
|
||||
.justify-content-center,
|
||||
.justify-content-sm-center,
|
||||
.justify-content-md-center,
|
||||
.justify-content-lg-center
|
||||
) {
|
||||
justify-content: flex-start !important;
|
||||
}
|
||||
#table_rdv :is(.align-items-center) {
|
||||
align-items: flex-start !important;
|
||||
}
|
||||
|
||||
/* retire les zébrures en mode carte */
|
||||
#table_rdv.table-striped > tbody > tr:nth-of-type(odd) > * {
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
/******************************FIN TABLES MOBILE********************************************/
|
||||
@media (max-width: 576px) {
|
||||
/* Barre de recherche */
|
||||
.searchbar {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 10px 14px;
|
||||
max-width: 760px;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
.search-icon {
|
||||
margin-left: 6px;
|
||||
}
|
||||
.input-wrap {
|
||||
position: relative;
|
||||
flex: 1.3;
|
||||
}
|
||||
.searchbar input {
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: clamp(10px, 3vw, 16px);
|
||||
padding: 10px 4px;
|
||||
}
|
||||
.divider {
|
||||
width: 1px;
|
||||
height: 28px;
|
||||
background: #e6e6e6;
|
||||
}
|
||||
}
|
||||
.footer-top-links {
|
||||
color: #38378b;
|
||||
font-weight: 600;
|
||||
}
|
||||
.footer-top-links a {
|
||||
color: inherit; /* hérite du bleu foncé */
|
||||
}
|
||||
|
||||
/* Fallback si le navigateur ne supporte pas gap */
|
||||
@supports not (gap: 1rem) {
|
||||
.d-flex.gap-2 > * {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
.d-flex.gap-3 > * {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
.d-flex.gap-4 > * {
|
||||
margin-right: 1.5rem;
|
||||
}
|
||||
.d-flex[class*="gap-"] > *:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.scrollable {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.fullheight {
|
||||
min-height: 100vh;
|
||||
} /* défaut */
|
||||
@supports (height: 100dvh) {
|
||||
.fullheight {
|
||||
min-height: 100dvh;
|
||||
} /* iOS 16+ */
|
||||
}
|
||||
@supports not (height: 100dvh) {
|
||||
.fullheight {
|
||||
min-height: -webkit-fill-available;
|
||||
} /* iOS anciens */
|
||||
}
|
||||
|
||||
/*---------Transformation table en cards-----------------------*
|
||||
***************************************************************/
|
||||
/* ===== TABLE -> CARTES (mobile) ===== */
|
||||
@media (max-width: 768px) {
|
||||
/* ✅ N’affiche pas THEAD tant que data-label n’est pas prêt */
|
||||
#table_rdv1 thead {
|
||||
display: table-header-group;
|
||||
}
|
||||
.mc-ready #table_rdv1 thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* neutralise centrage hérité */
|
||||
#table_rdv1,
|
||||
#table_rdv1 tr,
|
||||
#table_rdv1 td,
|
||||
#table_rdv1 td * {
|
||||
text-align: left !important;
|
||||
}
|
||||
/* wrapper bootstrap */
|
||||
.table-responsive {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
#table_rdv1 {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
#table_rdv1 tbody {
|
||||
display: block;
|
||||
}
|
||||
/* chaque ligne -> carte */
|
||||
#table_rdv1 tr {
|
||||
display: block;
|
||||
margin: 12px 0;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.08);
|
||||
overflow: hidden;
|
||||
}
|
||||
#table_rdv1 tr::before {
|
||||
content: "";
|
||||
display: block;
|
||||
height: 3px;
|
||||
background: #38378b;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* cellules empilées */
|
||||
#table_rdv1 td {
|
||||
display: block;
|
||||
padding: 10px 14px !important;
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#table_rdv1 td + td {
|
||||
border-top: 1px dashed rgba(0, 0, 0, 0.08) !important;
|
||||
}
|
||||
/* libellés auto (et on masque s’ils sont vides) */
|
||||
#table_rdv1 td::before {
|
||||
content: attr(data-label);
|
||||
display: inline-block;
|
||||
min-width: 120px;
|
||||
margin-right: 8px;
|
||||
font-weight: 600;
|
||||
color: #666;
|
||||
}
|
||||
#table_rdv1 td::before:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* hiérarchie visuelle (adapte si ordre diffère) */
|
||||
#table_rdv1 td:nth-child(1) {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
#table_rdv1 td:nth-child(2) {
|
||||
color: #6b7280;
|
||||
font-weight: 600;
|
||||
padding-top: 6px;
|
||||
}
|
||||
/* select pleine largeur + iOS align */
|
||||
#table_rdv1 select.form-select {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
font-size: 15px;
|
||||
border-radius: 10px;
|
||||
margin: 6px 0;
|
||||
text-align: left !important;
|
||||
text-align-last: left !important;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
/* actions */
|
||||
#table_rdv1 .row-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 6px;
|
||||
justify-content: flex-start !important;
|
||||
align-items: stretch !important;
|
||||
}
|
||||
#table_rdv1 .row-actions .btn {
|
||||
flex: 1 1 calc(50% - 4px);
|
||||
margin: 0 !important;
|
||||
}
|
||||
/* neutralise utilitaires Bootstrap qui centrent */
|
||||
#table_rdv1
|
||||
:is(.text-center, .text-sm-center, .text-md-center, .text-lg-center) {
|
||||
text-align: left !important;
|
||||
}
|
||||
#table_rdv1 :is(.mx-auto, .ms-auto, .me-auto) {
|
||||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
#table_rdv1
|
||||
:is(
|
||||
.justify-content-center,
|
||||
.justify-content-sm-center,
|
||||
.justify-content-md-center,
|
||||
.justify-content-lg-center
|
||||
) {
|
||||
justify-content: flex-start !important;
|
||||
}
|
||||
#table_rdv1 :is(.align-items-center) {
|
||||
align-items: flex-start !important;
|
||||
}
|
||||
|
||||
/* retire les zébrures en mode carte */
|
||||
#table_rdv1.table-striped > tbody > tr:nth-of-type(odd) > * {
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
/******************************FIN TABLES MOBILE********************************************/
|
||||
|
||||
/* Header fixe en haut */
|
||||
.site-header--fixed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1030;
|
||||
}
|
||||
|
||||
/* Safe-area iOS (notch) */
|
||||
@supports (padding: max(0px)) {
|
||||
.site-header--fixed {
|
||||
padding-top: max(env(safe-area-inset-top), 0px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Espace en haut pour ne pas cacher le contenu */
|
||||
body.has-fixed-header {
|
||||
padding-top: 64px; /* ajuste si besoin */
|
||||
}
|
||||
|
||||
@supports (padding: max(0px)) {
|
||||
body.has-fixed-header {
|
||||
padding-top: calc(64px + max(env(safe-area-inset-top), 0px));
|
||||
}
|
||||
}
|
||||
|
||||
.pwa-install {
|
||||
position: fixed;
|
||||
right: 14px;
|
||||
top: 100px;
|
||||
bottom: auto;
|
||||
transform: translateY(-50%);
|
||||
z-index: 9999;
|
||||
border: 0;
|
||||
border-radius: 9999px;
|
||||
padding: 10px 12px;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
background: #38378b;
|
||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.16);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.pwa-install img {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pwa-ios-hint {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 10000;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.pwa-ios-card {
|
||||
width: 100%;
|
||||
max-width: 520px;
|
||||
background: #fff;
|
||||
border-radius: 18px;
|
||||
padding: 14px 14px 12px;
|
||||
box-shadow: 0 18px 40px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.pwa-ios-title {
|
||||
font-weight: 800;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.pwa-ios-text {
|
||||
line-height: 1.25;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.pwa-ios-close {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
padding: 10px 12px;
|
||||
background: #e03c3f;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.pwa-install,
|
||||
.pwa-ios-hint {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*.d-none{ display:none !important; }*/
|
||||
|
||||
@media (max-width: 956px) {
|
||||
/* Layout page : header + main qui prend l'espace + footer en bas */
|
||||
body {
|
||||
min-height: 100dvh; /* mieux que svh */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1 0 auto; /* prend l’espace vide */
|
||||
}
|
||||
|
||||
.site-footer {
|
||||
margin-top: auto; /* pousse le footer tout en bas */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
function refreshOutlookToken(PDO $pdo, int $praticien_id): ?string {
|
||||
$stmt = $pdo->prepare("SELECT refresh_token FROM outlook_tokens WHERE praticien_id = ?");
|
||||
$stmt->execute([$praticien_id]);
|
||||
$refresh_token = $stmt->fetchColumn();
|
||||
|
||||
if (!$refresh_token) return null;
|
||||
|
||||
$client_id = '8328e538-3cee-4731-9122-8b7fd6f461a9';
|
||||
$client_secret = 'xKY8Q~dygFtWlM62--4t0RVkAlFI3f5uspwOedkL';
|
||||
$token_url = 'https://login.microsoftonline.com/consumers/oauth2/v2.0/token';
|
||||
|
||||
$data = [
|
||||
'client_id' => $client_id,
|
||||
'client_secret' => $client_secret,
|
||||
'refresh_token' => $refresh_token,
|
||||
'grant_type' => 'refresh_token',
|
||||
'scope' => 'offline_access User.Read Calendars.ReadWrite'
|
||||
];
|
||||
|
||||
$options = [
|
||||
'http' => [
|
||||
'method' => 'POST',
|
||||
'header' => "Content-type: application/x-www-form-urlencoded",
|
||||
'content' => http_build_query($data),
|
||||
],
|
||||
];
|
||||
|
||||
$response = file_get_contents($token_url, false, stream_context_create($options));
|
||||
$tokens = json_decode($response, true);
|
||||
|
||||
if (!isset($tokens['access_token'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$access_token = $tokens['access_token'];
|
||||
$refresh_token = $tokens['refresh_token'] ?? $refresh_token;
|
||||
$expires_at = time() + $tokens['expires_in'];
|
||||
|
||||
$pdo->prepare("UPDATE outlook_tokens SET access_token = ?, refresh_token = ?, expires_at = ? WHERE praticien_id = ?")
|
||||
->execute([$access_token, $refresh_token, $expires_at, $praticien_id]);
|
||||
|
||||
return $access_token;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
// --------------------------------------------------
|
||||
// BOOTSTRAP GLOBAL — exécuté UNE SEULE FOIS
|
||||
// --------------------------------------------------
|
||||
|
||||
if (!defined('APP_ROOT')) {
|
||||
define('APP_ROOT', realpath(__DIR__ . '/..')); // vhosts/staging.uslugi.fr
|
||||
}
|
||||
if (!defined('APP_HTDOCS')) {
|
||||
define('APP_HTDOCS', APP_ROOT . '/htdocs');
|
||||
}
|
||||
|
||||
// Charger env UNE FOIS
|
||||
if (empty($GLOBALS['ENV_LOADED'])) {
|
||||
require_once APP_ROOT . '/includes/env.php';
|
||||
app_load_env(APP_ROOT . '/.env');
|
||||
$GLOBALS['ENV_LOADED'] = true;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
// Évite la boucle quand router.php inclut la page
|
||||
if (defined('ROUTED_PRETTY')) return;
|
||||
|
||||
// Mapping vilain -> joli
|
||||
$UGLY_TO_PRETTY = [
|
||||
//racine
|
||||
'/index.php' => '/',
|
||||
'/result_search.php' => '/recherche',
|
||||
'/detail_public.php' => '/specialistes',
|
||||
'/connexion.php' => '/login',
|
||||
'/choix_inscr.php' => '/choix-inscription',
|
||||
'/inscription_client.php' => '/inscription-client',
|
||||
'/inscription_praticien.php' => '/inscription-praticien',
|
||||
'/mdp_oublie.php' => '/mot-de-passe-oublie',
|
||||
'/avis_public.php' => '/avis',
|
||||
//dossier client
|
||||
'/public/client/informations.php' => '/profil',
|
||||
'/public/client/modifier.php' => '/modifier-profil',
|
||||
'/public/client/compte.php' => '/mon-compte',
|
||||
'/public/client/list_praticien.php' => '/trouver-specialistes',
|
||||
'/public/client/mes_favori.php' => '/mes-preferences',
|
||||
'/public/client/detail_praticien.php' => '/specialiste',
|
||||
'/public/client/avis.php' => '/donner-avis',
|
||||
'/public/client/reserver_rdv.php' => '/reservation',
|
||||
'/public/client/mes_rendez_vous.php' => '/mes-rendez-vous',
|
||||
|
||||
//Dossier praticien
|
||||
'/public/praticien/informations.php' => '/profil-pro',
|
||||
'/public/praticien/modifier.php' => '/modifier-profil-pro',
|
||||
'/public/praticien/compte.php' => '/mon-compte-pro',
|
||||
'/public/praticien/disponibilites.php' => '/mes-creneaux',
|
||||
'/public/praticien/disponibilites_date.php' => '/mes-creneaux-date',
|
||||
'/public/praticien/rendez_vous.php' => '/mes-consultations',
|
||||
'/public/praticien/rendez_vous_du_jour.php' => '/consultations-du-jour',
|
||||
'/public/praticien/agenda.php' => '/agenda',
|
||||
'/public/praticien/synchronisation.php' => '/connecter-agenda',
|
||||
'/public/praticien/resiliation.php' => '/abonnement',
|
||||
'/public/praticien/factures.php' => '/mes-factures',
|
||||
|
||||
//Dossier messages
|
||||
|
||||
];
|
||||
|
||||
// Debug : ajouter ?_noredir=1 pour désactiver la redirection
|
||||
if (!empty($_GET['_noredir'])) return;
|
||||
|
||||
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) ?? '/';
|
||||
|
||||
if (isset($UGLY_TO_PRETTY[$path])) {
|
||||
$target = $UGLY_TO_PRETTY[$path];
|
||||
$qs = $_SERVER['QUERY_STRING'] ? ('?' . $_SERVER['QUERY_STRING']) : '';
|
||||
header('Location: ' . $target . $qs, true, 301);
|
||||
exit;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
// Ne PAS versionner ce fichier
|
||||
return [
|
||||
'site' => '6LcltswrAAAAAOQ3NwAgUbAu75WHQ9MsiFVd7ZVe', // publique
|
||||
'secret' => '6LcltswrAAAAABTicobq3NigOaO3hTFNZBieZyeO', // privée
|
||||
];
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
function has_cyrillic(string $s): bool {
|
||||
return (bool) preg_match('/\p{Cyrillic}/u', $s);
|
||||
}
|
||||
|
||||
function norm_key(string $s): string {
|
||||
$s = mb_strtolower(trim($s), 'UTF-8');
|
||||
$s = str_replace(['ё'], ['е'], $s); // homogénéiser RU
|
||||
$s = preg_replace('/[\'"’\-\s]+/u', '', $s); // enlève tirets, espaces, quotes
|
||||
return $s;
|
||||
}
|
||||
|
||||
// Translittération simple RU -> latin (fallback si intl n’est pas dispo)
|
||||
function translit_ru_to_lat(string $s): string {
|
||||
if (class_exists('Transliterator')) {
|
||||
// "Russian-Latin/BGN" produit Parizh (Париж) -> "parizh"
|
||||
$t = \Transliterator::create('Russian-Latin/BGN; Lower();');
|
||||
if ($t) return $t->transliterate($s);
|
||||
}
|
||||
$map = [
|
||||
'а'=>'a','б'=>'b','в'=>'v','г'=>'g','д'=>'d','е'=>'e','ё'=>'e','ж'=>'zh','з'=>'z','и'=>'i','й'=>'i','к'=>'k',
|
||||
'л'=>'l','м'=>'m','н'=>'n','о'=>'o','п'=>'p','р'=>'r','с'=>'s','т'=>'t','у'=>'u','ф'=>'f','х'=>'kh','ц'=>'ts',
|
||||
'ч'=>'ch','ш'=>'sh','щ'=>'shch','ъ'=>'','ы'=>'y','ь'=>'','э'=>'e','ю'=>'yu','я'=>'ya',
|
||||
];
|
||||
$s = mb_strtolower($s, 'UTF-8');
|
||||
return strtr($s, $map);
|
||||
}
|
||||
|
||||
// Retourne une liste de candidats latins (pour LIKE) à partir d’une saisie q
|
||||
function city_query_candidates(string $q): array {
|
||||
$q = trim($q);
|
||||
if ($q === '') return [];
|
||||
|
||||
$cands = [];
|
||||
$cands[] = mb_strtolower($q, 'UTF-8'); // saisie brute (si déjà latine)
|
||||
|
||||
// charger alias RU -> latin
|
||||
$aliases = @include __DIR__ . '/city_aliases_ru.php';
|
||||
if (!is_array($aliases)) $aliases = [];
|
||||
|
||||
if (has_cyrillic($q)) {
|
||||
// 1) correspondance par préfixe dans les alias (ПАР → Paris)
|
||||
$key = norm_key($q);
|
||||
foreach ($aliases as $ru => $lat) {
|
||||
if (strpos(norm_key($ru), $key) === 0) {
|
||||
$cands[] = $lat;
|
||||
}
|
||||
}
|
||||
// 2) translittération simple (Париж -> parizh)
|
||||
$cands[] = translit_ru_to_lat($q);
|
||||
}
|
||||
|
||||
// Normalisation légère des candidats (minuscules, accents retirés si possible)
|
||||
$res = [];
|
||||
foreach (array_unique($cands) as $c) {
|
||||
$lc = mb_strtolower($c, 'UTF-8');
|
||||
// enlever accents (si intl transliterator dispo)
|
||||
if (class_exists('Transliterator')) {
|
||||
$t = \Transliterator::create('Any-Latin; Latin-ASCII;');
|
||||
if ($t) $lc = $t->transliterate($lc);
|
||||
}
|
||||
$res[] = $lc;
|
||||
}
|
||||
return array_values(array_unique($res));
|
||||
}
|
||||
@@ -0,0 +1,916 @@
|
||||
/* ==========================================================================
|
||||
site_style.css — Refactor v2 (mobile‑first, compatible Bootstrap)
|
||||
- Tokens centralisés (couleurs, rayons, ombres, largeurs)
|
||||
- Nettoyage des imbrications, corrections de sélecteurs, fusion des doublons
|
||||
- Breakpoints cohérents, priorités claires
|
||||
========================================================================== */
|
||||
|
||||
/* 1) Design tokens */
|
||||
:root {
|
||||
--color-red: #e03c3f;
|
||||
--color-white: #ffffff;
|
||||
--color-blue: #38378b;
|
||||
--color-mint: #06ffa8;
|
||||
--color-hero: #8ca9ff;
|
||||
--color-header: #f1f1f1;
|
||||
|
||||
--radius: 22px;
|
||||
--radius-lg: 24px;
|
||||
--radius-pill: 999px;
|
||||
|
||||
--shadow-card: 0 12px 40px rgba(0, 0, 0, 0.08);
|
||||
--shadow-pop: 0 6px 15px rgba(0, 0, 0, 0.15);
|
||||
|
||||
--container: 1200px;
|
||||
}
|
||||
|
||||
/* 2) Base & layout */
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family:
|
||||
"Montserrat",
|
||||
system-ui,
|
||||
-apple-system,
|
||||
Segoe UI,
|
||||
Roboto,
|
||||
Arial,
|
||||
sans-serif;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
background: var(--color-hero);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
/* Compat anciens sélecteurs 'main1' / 'main2' (existaient comme balises) */
|
||||
.main1 {
|
||||
flex: 1;
|
||||
background: var(--color-hero);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.main2 {
|
||||
flex: 1;
|
||||
background: var(--color-white);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* HERO */
|
||||
.hero {
|
||||
padding: 40px 0 56px;
|
||||
/*background: var(--color-hero);*/
|
||||
}
|
||||
.hero .container {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: 0 18px;
|
||||
}
|
||||
.hero-title {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 48px;
|
||||
line-height: 1.16;
|
||||
font-weight: 800;
|
||||
margin: 10px 0 26px;
|
||||
}
|
||||
.container {
|
||||
width: min(var(--container), 100% - 36px);
|
||||
margin-inline: auto;
|
||||
padding-inline: 18px;
|
||||
}
|
||||
.pro-name {
|
||||
margin: 0 0 8px;
|
||||
font-size: 26px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
/* 3) Header & footer */
|
||||
.site-header {
|
||||
background: var(--color-header);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
.site-header .container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-block: 10px;
|
||||
}
|
||||
.brand {
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: var(--color-header);
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.06);
|
||||
padding: 22px 20px;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
color: #222;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* Barre style "carte" */
|
||||
.searchbar {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 10px 14px;
|
||||
max-width: 860px;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
margin-left: 6px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.input-wrap {
|
||||
position: relative;
|
||||
flex: 1 1 0;
|
||||
min-width: 0; /* IMPORTANT: évite débordements */
|
||||
}
|
||||
|
||||
/* Champs “plats” (comme sur ta capture) */
|
||||
.searchbar input {
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
font-size: 14px;
|
||||
padding: 10px 8px;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.searchbar input::placeholder {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
/* séparateur */
|
||||
.divider {
|
||||
width: 1px;
|
||||
height: 34px;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Bouton intégré à droite */
|
||||
.btn-find--inbar {
|
||||
margin: 0;
|
||||
border: none;
|
||||
background: var(--red);
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
padding: 10px 14px;
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.16);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 4) Boutons (override Bootstrap) */
|
||||
.btn-primary {
|
||||
background-color: var(--color-red) !important;
|
||||
border-color: var(--color-red) !important;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
filter: brightness(1.05);
|
||||
}
|
||||
.btn-primary a {
|
||||
color: #fff;
|
||||
} /* corrige l'ancien '.btn-primary :a' */
|
||||
|
||||
/* 5) Sélecteur de langue & menu utilisateur */
|
||||
.lang-switch {
|
||||
display: none;
|
||||
gap: 6px;
|
||||
background: #fff;
|
||||
padding: 4px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
.lang-btn {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--color-blue);
|
||||
border-radius: 14px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.lang-btn:hover {
|
||||
background: rgba(56, 55, 139, 0.08);
|
||||
}
|
||||
.lang-btn.active {
|
||||
background: var(--color-blue);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 6) Page Détails praticien */
|
||||
.profile-card {
|
||||
background: #fff;
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-card);
|
||||
padding: 28px;
|
||||
}
|
||||
|
||||
/* grille bas de page (fusion + align fix) */
|
||||
.bottom-grid {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
grid-template-columns: 0.8fr 1.6fr auto;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.bottom-grid .text-end {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* Rating (fusion doublons) */
|
||||
.rating {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
letter-spacing: 2px;
|
||||
line-height: 1;
|
||||
}
|
||||
.rating .stars-base {
|
||||
color: #e3e5ea;
|
||||
}
|
||||
.rating .stars-fill {
|
||||
color: #ffc107;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.avis-card .meta {
|
||||
font-size: 0.9rem;
|
||||
color: #667085;
|
||||
}
|
||||
|
||||
/* 8) Champs communs + force MDP (fusion doublons) */
|
||||
.field-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
.toggle-password {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
}
|
||||
.strength-bar {
|
||||
display: flex;
|
||||
height: 8px;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.strength-fill {
|
||||
flex: 1;
|
||||
background: #e0e0e0;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
/* 9) Listes & cartes (client) */
|
||||
.card {
|
||||
min-width: 380px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
.card h4 {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.btn-agenda {
|
||||
background-color: #4285f4;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.btn-agenda:hover {
|
||||
background-color: #2a69d1;
|
||||
}
|
||||
.card_sync {
|
||||
margin-bottom: 30px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.praticien-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
.card-img-top {
|
||||
max-width: 150px;
|
||||
aspect-ratio: 3/4;
|
||||
object-fit: cover;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.card-body {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.favori_lien {
|
||||
cursor: pointer;
|
||||
color: #055a92;
|
||||
transition:
|
||||
color 0.3s,
|
||||
transform 0.2s;
|
||||
}
|
||||
.favori_lien:hover {
|
||||
color: #d21427;
|
||||
}
|
||||
.favori_lien.ajoute {
|
||||
color: #d21427;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.card-img-top_liste {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
/* 10) Suggestions adresse */
|
||||
.input-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
#suggestions {
|
||||
position: absolute;
|
||||
background: #f1f1f1;
|
||||
border: 1px solid #ccc;
|
||||
width: 100%;
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
#suggestions li {
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#suggestions li:hover {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
/* 11) Partie avec scroll pour detail praticien pour scroller videos */
|
||||
.profile-grid.profile-top {
|
||||
/* hauteur comprise entre 420px et 780px, ~70% de la hauteur viewport */
|
||||
height: clamp(420px, 70vh, 780px);
|
||||
align-items: stretch; /* important pour que les colonnes prennent 100% de la hauteur */
|
||||
}
|
||||
|
||||
/* Permettre l’overflow interne (déjà presque bon chez toi) */
|
||||
.profile-grid > * {
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
.profile-grid {
|
||||
display: grid;
|
||||
gap: 24px;
|
||||
grid-template-columns: 1.15fr 1fr 0.9fr;
|
||||
}
|
||||
|
||||
.profile-grid .panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0; /* crucial */
|
||||
height: 90%; /* ← donner une hauteur de référence */
|
||||
}
|
||||
|
||||
/* La grande photo remplit la hauteur disponible */
|
||||
.big-photo img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.big-photo {
|
||||
height: 90%;
|
||||
max-width: 350px;
|
||||
border-radius: 22px;
|
||||
overflow: hidden;
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
/* contenu scrollable dans la colonne infos */
|
||||
.info-scroll {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
overflow: auto; /* scroll si ça dépasse */
|
||||
padding-right: 4px; /* petit espace pour la scrollbar */
|
||||
}
|
||||
|
||||
/* liste de vidéos scrollable (le <h5> reste fixe) */
|
||||
.vlist {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
overflow: auto; /* scroll si beaucoup de vignettes */
|
||||
}
|
||||
|
||||
/* Optionnel : taille des vignettes vidéo */
|
||||
.vlist .vitem {
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
background: #000;
|
||||
aspect-ratio: 21 / 9; /* 21:9 compact et propre */
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.vlist .vitem img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background-color: #38378b; /* bleu foncé */
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
a,
|
||||
a:link,
|
||||
a:visited {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Personnalisation des boutons*/
|
||||
|
||||
/* Même hauteur / alignement que les autres boutons */
|
||||
.btn-table {
|
||||
vertical-align: middle; /* alignement dans la cellule */
|
||||
margin: 0; /* pas de marge parasite */
|
||||
}
|
||||
|
||||
/* Bord gris par défaut */
|
||||
.btn-outline-secondary {
|
||||
border: 1px solid #6c757d;
|
||||
color: #6c757d;
|
||||
background-color: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Fond gris au survol */
|
||||
.btn-outline-secondary:hover {
|
||||
background-color: #6c757d !important;
|
||||
color: #fff; /* texte blanc pour contraste */
|
||||
}
|
||||
|
||||
/* Variante outline mais avec gris à la place du bleu */
|
||||
.btn-outline-primary.custom-gray {
|
||||
border-color: #6c757d; /* gris */
|
||||
color: #6c757d; /* texte gris */
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.btn-outline-primary.custom-gray:hover {
|
||||
background-color: #6c757d; /* fond gris au survol */
|
||||
color: #fff; /* texte blanc */
|
||||
border-color: #6c757d; /* garder la bordure grise */
|
||||
}
|
||||
|
||||
/*.swal2-actions .swal2-confirm.swal2-styled {
|
||||
box-shadow: 0 0 0 0.5px #6c757d;
|
||||
border: none;
|
||||
color: #6c757d;
|
||||
background-color: transparent;
|
||||
border-radius: 999px;
|
||||
}
|
||||
.swal2-actions .swal2-confirm.swal2-styled:hover {
|
||||
background-color: #6c757d;
|
||||
color: #fff;
|
||||
}
|
||||
.swal2-actions {
|
||||
margin-bottom: 12px;
|
||||
}*/
|
||||
|
||||
/* Dans la colonne actions du tableau */
|
||||
#table_rdv td {
|
||||
vertical-align: middle; /* centre verticalement la cellule */
|
||||
text-align: center; /* optionnel : centre horizontalement */
|
||||
}
|
||||
|
||||
/* Normaliser le <form> pour qu'il se comporte comme un bouton */
|
||||
#table_rdv td form {
|
||||
display: inline-block; /* au lieu de block */
|
||||
margin: 0; /* supprime la marge par défaut */
|
||||
}
|
||||
|
||||
/* Harmoniser <a> et <button> visuellement */
|
||||
#table_rdv td a.btn,
|
||||
#table_rdv td button.btn {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
line-height: 1.2; /* même hauteur de ligne */
|
||||
padding: 4px 10px; /* compacité identique */
|
||||
border-radius: 999px;
|
||||
box-shadow: none; /* même arrondi si tu l’utilises */
|
||||
}
|
||||
|
||||
/* Styles de tes variantes (si tu utilises ces classes) */
|
||||
.btn-table {
|
||||
border: 1px solid #6c757d;
|
||||
color: #6c757d;
|
||||
background: transparent;
|
||||
}
|
||||
.btn-table:hover {
|
||||
background: #6c757d;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-table_del {
|
||||
border: 1px solid #dc3545;
|
||||
color: #dc3545;
|
||||
background: transparent;
|
||||
}
|
||||
.btn-table_del:hover {
|
||||
background: #dc3545;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* === Global button polish (après Bootstrap) === */
|
||||
:root {
|
||||
/*Ajuste ici la rondeur et l’ombre */
|
||||
--btn-radius: 999px; /*passe à 999px pour style "pill" */
|
||||
--btn-shadow: 0 8px 22px rgba(0, 0, 0, 0.18);
|
||||
--btn-shadow-hover: 0 8px 22px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
.btn:not(.btn-link) {
|
||||
border-radius: var(--btn-radius);
|
||||
box-shadow: var(--btn-shadow);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.btn:not(.btn-link) {
|
||||
transition:
|
||||
box-shadow 0.2s ease,
|
||||
transform 0.08s ease,
|
||||
background-color 0.2s ease,
|
||||
border-color 0.2s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.btn:hover:not(.btn-link),
|
||||
.btn:focus-visible:not(.btn-link) {
|
||||
box-shadow: var(--btn-shadow-hover);
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.btn:active:not(.btn-link) {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 8px px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
.btn:disabled,
|
||||
.btn.disabled {
|
||||
box-shadow: none;
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
/*Exceptions / utilitaires */
|
||||
.btn.btn-link {
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
} /* les liens ne prennent pas d’ombre*/
|
||||
.btn--no-shadow {
|
||||
box-shadow: none !important;
|
||||
} /*opt-out par bouton si besoin */
|
||||
|
||||
/*********************PAGE PRISE RENDEZ-VOUS**************************/
|
||||
|
||||
.rdv-card {
|
||||
background: #fff;
|
||||
border-radius: 22px;
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.08);
|
||||
padding: 28px;
|
||||
}
|
||||
|
||||
/* Titres */
|
||||
.rdv-title {
|
||||
margin: 0 0 6px;
|
||||
text-align: center;
|
||||
font-weight: 800;
|
||||
color: #6c757d;
|
||||
}
|
||||
.rdv-subtitle {
|
||||
margin: 0 0 22px;
|
||||
text-align: center;
|
||||
font-weight: 800;
|
||||
color: #000;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
/* Pagination (boutons discrets “pill”) */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin: 6px 0 18px;
|
||||
}
|
||||
.pagination a {
|
||||
text-decoration: none;
|
||||
padding: 8px 14px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
background: #fff;
|
||||
color: #38378b;
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.06);
|
||||
transition:
|
||||
transform 0.06s ease,
|
||||
box-shadow 0.2s ease,
|
||||
background-color 0.2s ease;
|
||||
}
|
||||
.pagination a:hover {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 10px 22px rgba(0, 0, 0, 0.1);
|
||||
background: #f8f9ff;
|
||||
}
|
||||
|
||||
/* Grille des jours (responsive) */
|
||||
.grid-container {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
}
|
||||
/******************Gestion des Slots affichage par 3 par ligne***************************/
|
||||
@media (min-width: 1200px) {
|
||||
.grid-container {
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
/* Colonne “jour” (carte) */
|
||||
.day-column {
|
||||
background: #ffffff;
|
||||
border-radius: 18px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.06);
|
||||
padding: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* En-tête jour */
|
||||
.day-column h4 {
|
||||
margin: 0 0 8px;
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
font-weight: 800;
|
||||
color: #fff;
|
||||
background: #38378b;
|
||||
border-radius: 14px;
|
||||
padding: 8px 10px;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
/* Slot (heure) */
|
||||
.slot {
|
||||
background: #f1f1f1;
|
||||
border: 1px solid #e6e9ff;
|
||||
border-radius: 14px;
|
||||
padding: 10px 12px;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
color: #38378b;
|
||||
line-height: 1.25;
|
||||
box-shadow: inset 0 0 0 1px rgba(56, 55, 139, 0.05);
|
||||
}
|
||||
|
||||
/* Bouton “Réserver” (pill) */
|
||||
.slot .reserver-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
padding: 8px 14px;
|
||||
border-radius: 999px;
|
||||
text-decoration: none;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
background: #e03c3f;
|
||||
border: 1px solid transparent;
|
||||
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.12);
|
||||
transition:
|
||||
transform 0.06s ease,
|
||||
box-shadow 0.2s ease,
|
||||
background-color 0.2s ease;
|
||||
}
|
||||
.slot .reserver-btn:hover {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 12px 26px rgba(0, 0, 0, 0.16);
|
||||
background: #cc3538; /* léger foncé */
|
||||
}
|
||||
.slot .reserver-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* États “désactivé” si besoin (au cas où) */
|
||||
.slot.is-disabled {
|
||||
opacity: 0.55;
|
||||
filter: grayscale(20%);
|
||||
}
|
||||
.slot.is-disabled .reserver-btn {
|
||||
background: #d4d7e5;
|
||||
color: #fff;
|
||||
box-shadow: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Message “aucun créneau” */
|
||||
.rdv-empty {
|
||||
color: rgb(33, 37, 41);
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
background: #fff;
|
||||
border-radius: 18px;
|
||||
padding: 18px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
/* SweetAlert – harmonisation rapide avec tes boutons pills gris (optionnel) */
|
||||
.swal2-actions .swal2-confirm.swal2-styled {
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
background: #e03c3f;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
.swal2-actions .swal2-confirm.swal2-styled:hover {
|
||||
filter: brightness(1.05);
|
||||
}
|
||||
.swal2-actions .swal2-cancel.swal2-styled {
|
||||
border-radius: 999px;
|
||||
background: #fff;
|
||||
color: #38378b;
|
||||
border: 1px solid #e0e3eb;
|
||||
}
|
||||
|
||||
/* Mobile: aération + swipe confortable si trop de colonnes */
|
||||
@media (max-width: 640px) {
|
||||
.rdv-card {
|
||||
padding: 18px;
|
||||
}
|
||||
.grid-container {
|
||||
grid-auto-flow: row;
|
||||
grid-template-columns: 1fr; /* empile les jours */
|
||||
}
|
||||
/* Variante swipe (si tu préfères conserver les colonnes) :
|
||||
dé-commente ce bloc et entoure .grid-container d’un wrapper .rdv-scroll
|
||||
.rdv-scroll{ overflow-x:auto; -webkit-overflow-scrolling:touch; }
|
||||
.grid-container{ grid-auto-flow: column; grid-auto-columns: 88%; }
|
||||
*/
|
||||
}
|
||||
|
||||
/**********************Paginantion pour le site ****************************/
|
||||
.pager {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin: 22px 0;
|
||||
}
|
||||
.pager a {
|
||||
text-decoration: none;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.pager-arrow {
|
||||
padding: 10px 18px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid #e0e3eb;
|
||||
background: #fff;
|
||||
color: #38378b;
|
||||
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.06);
|
||||
transition:
|
||||
background 0.2s,
|
||||
transform 0.1s,
|
||||
opacity 0.2s;
|
||||
}
|
||||
.pager-arrow:hover {
|
||||
background: #f8f9ff;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.pager-arrow.is-disabled {
|
||||
opacity: 0.4;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.pager-page {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #e0e3eb;
|
||||
background: #fff;
|
||||
color: #38378b;
|
||||
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.06);
|
||||
transition:
|
||||
background 0.2s,
|
||||
transform 0.1s,
|
||||
color 0.2s,
|
||||
border-color 0.2s;
|
||||
}
|
||||
.pager-page:hover {
|
||||
background: #f8f9ff;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.pager-page.is-active {
|
||||
background: #e03c3f;
|
||||
color: #fff;
|
||||
border-color: #e03c3f;
|
||||
transform: none;
|
||||
}
|
||||
/* =========================================
|
||||
Header FIXE seulement en mobile + PWA
|
||||
========================================= */
|
||||
|
||||
/* Desktop par défaut */
|
||||
.site-header {
|
||||
position: static;
|
||||
}
|
||||
|
||||
/* Mobile navigateur */
|
||||
@media (max-width: 899px) {
|
||||
.site-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
z-index: 1030;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: 64px; /* hauteur header mobile */
|
||||
}
|
||||
}
|
||||
|
||||
/* PWA (même si largeur > 899px) */
|
||||
@media (display-mode: standalone) {
|
||||
.site-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
z-index: 1030;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: calc(64px + env(safe-area-inset-top));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
// Map des clés de traduction -> valeur canonique (singulier) pour la BDD
|
||||
return [
|
||||
'ru' => [
|
||||
'avocats' => 'адвокат',
|
||||
'consultants' => 'консультант',
|
||||
'vrachi' => 'врач',
|
||||
'psychologues' => 'психолог',
|
||||
'repetitori' => 'преподаватель',
|
||||
'finance' => 'бухгалтер', // ajuste si besoin
|
||||
'perevod' => 'переводчик',
|
||||
'notarius' => 'нотариус',
|
||||
'gid' => 'гид',
|
||||
'rieltori' => 'риелтор',
|
||||
|
||||
|
||||
],
|
||||
'fr' => [
|
||||
'avocats' => 'адвокат',
|
||||
'consultants' => 'консультант',
|
||||
'vrachi' => 'врач', // clé existante "vrachi" côté traductions
|
||||
'psychologues' => 'психолог',
|
||||
'repetitori' => 'преподаватель', // ou "Répetiteur" selon ta BDD
|
||||
'finance' => 'бухгалтер', // ajuste si besoin
|
||||
'perevod' => 'переводчик',
|
||||
'notarius' => 'нотариус',
|
||||
'gid' => 'гид',
|
||||
'rieltori' => 'риелтор',
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
// Charge les scripts de tracking uniquement pour des pages HTML vues en GET.
|
||||
// → Pas d’injection sur API/AJAX/assets/callbacks.
|
||||
if (PHP_SAPI === 'cli') return;
|
||||
|
||||
$uri = $_SERVER['REQUEST_URI'] ?? '';
|
||||
$accept = $_SERVER['HTTP_ACCEPT'] ?? '';
|
||||
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
||||
$xrw = $_SERVER['HTTP_X_REQUESTED_WITH'] ?? '';
|
||||
|
||||
$isGet = strcasecmp($method, 'GET') === 0;
|
||||
$wantsHtml = (stripos($accept, 'text/html') !== false) || ($accept === '');
|
||||
$wantsJson = stripos($accept, 'application/json') !== false;
|
||||
|
||||
$isAjax = strcasecmp($xrw, 'XMLHttpRequest') === 0;
|
||||
$isApi = preg_match('~/(api/|messages/api/|webhook|callback|notify)~i', $uri);
|
||||
$isAsset = preg_match('~\.(css|js|map|png|jpe?g|gif|svg|webp|ico|json|xml|txt|pdf|woff2?|ttf|eot|mp4|webm)(\?|$)~i', $uri);
|
||||
$isMatomo = stripos($uri, '/matomo/') !== false;
|
||||
$isRdv = preg_match('~/(valider_rdv\.php|confirmer_rdv\.php|rdv/)~i', $uri);
|
||||
|
||||
if ($isGet && $wantsHtml && !$wantsJson && !$isAjax && !$isApi && !$isAsset && !$isMatomo && !$isRdv) {
|
||||
// 100% externe (pas d’inline) → compatible CSP 'self' + domaines autorisés
|
||||
echo '<script src="/js/tracking.js" defer></script>';
|
||||
// Pixel noscript Matomo (ok même sans JS)
|
||||
echo '<noscript><p><img src="https://staging.uslugi.fr/matomo/matomo.php?idsite=1&rec=1" style="border:0" alt=""></p></noscript>';
|
||||
}
|
||||
Reference in new issue
Block a user