refonte: UI/UX complet — landing page éditoriale + design system Tailwind
- BaseLayout: bandeau tricolore, nav sticky mobile (hamburger), footer 4col - index.astro: landing éditoriale (hero + stats live + méthode + thèmes + scénarios) Données Eurostat live depuis indicators.json (dette, déficit, chômage, croissance) Thèmes à venir en cards dashed — séparation claire publiés / en préparation - themes/index.astro: nettoyage inline styles → Tailwind classes - themes/[slug].astro: header hero refait, nav interne, sidebar sticky, prose améliorée - donnees/index.astro: grille indicateurs responsive, tableau comparatif - global.css: design tokens unifiés (nuit/republic/accent/rouge), badges, cards, prose Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ffb49c3495
commit
b687720cce
6 changed files with 706 additions and 632 deletions
|
|
@ -14,104 +14,142 @@ const {
|
||||||
} = Astro.props;
|
} = Astro.props;
|
||||||
|
|
||||||
const canonical = new URL(Astro.url.pathname, Astro.site);
|
const canonical = new URL(Astro.url.pathname, Astro.site);
|
||||||
|
const currentPath = Astro.url.pathname;
|
||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<meta name="description" content={description} />
|
<meta name="description" content={description} />
|
||||||
<link rel="canonical" href={canonical} />
|
<link rel="canonical" href={canonical} />
|
||||||
|
<meta property="og:title" content={title} />
|
||||||
|
<meta property="og:description" content={description} />
|
||||||
|
<meta property="og:image" content={og_image} />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Playfair+Display:wght@700;900&display=swap" rel="stylesheet" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
|
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||||
|
</head>
|
||||||
|
<body class="min-h-screen flex flex-col">
|
||||||
|
|
||||||
<!-- OG -->
|
<!-- Bandeau tricolore 1px -->
|
||||||
<meta property="og:title" content={title} />
|
<div class="h-1 w-full flex">
|
||||||
<meta property="og:description" content={description} />
|
<div class="flex-1 bg-[#002395]"></div>
|
||||||
<meta property="og:image" content={og_image} />
|
<div class="flex-1 bg-white border-y border-gray-100"></div>
|
||||||
<meta property="og:type" content="website" />
|
<div class="flex-1 bg-[#ED2939]"></div>
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
</div>
|
||||||
|
|
||||||
<!-- Fonts -->
|
<!-- Header -->
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<header class="bg-white border-b border-gray-200 sticky top-0 z-50 shadow-sm">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 flex items-center justify-between h-14">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Playfair+Display:wght@700&display=swap" rel="stylesheet" />
|
|
||||||
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<!-- Logo -->
|
||||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
<a href="/" class="flex items-center gap-3 no-underline">
|
||||||
</head>
|
<div class="bg-[#1e3a6e] text-white font-black text-sm px-2.5 py-1 rounded tracking-wider leading-none">IRPF</div>
|
||||||
<body class="min-h-screen flex flex-col">
|
<span class="hidden sm:block text-xs text-gray-500 font-medium leading-tight">
|
||||||
|
Institut de Recherche<br/>Politique pour la France
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
<!-- Barre institutionnelle -->
|
<!-- Nav desktop -->
|
||||||
<div style="background: #0f172a; color: white; font-size: 0.75rem; padding: 0.375rem 0; text-align: center; letter-spacing: 0.05em;">
|
<nav class="hidden md:flex items-center gap-0.5">
|
||||||
INSTITUT DE RECHERCHE POLITIQUE POUR LA FRANCE · Analyse indépendante · Données publiques
|
{[
|
||||||
|
{ href: '/themes/', label: 'Thèmes' },
|
||||||
|
{ href: '/donnees/', label: 'Données' },
|
||||||
|
{ href: '/scenarios/', label: 'Scénarios' },
|
||||||
|
{ href: '/rapports/', label: 'Publications' },
|
||||||
|
{ href: '/a-propos/', label: 'À propos' },
|
||||||
|
].map(({ href, label }) => (
|
||||||
|
<a
|
||||||
|
href={href}
|
||||||
|
class={`px-3 py-1.5 rounded text-sm font-medium no-underline transition-colors ${currentPath.startsWith(href) ? 'text-[#1e3a6e] bg-blue-50' : 'text-gray-500 hover:text-[#1e3a6e] hover:bg-gray-50'}`}
|
||||||
|
>{label}</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Burger mobile -->
|
||||||
|
<button
|
||||||
|
id="burger"
|
||||||
|
class="md:hidden p-2 rounded text-gray-500 hover:bg-gray-100 transition-colors"
|
||||||
|
aria-label="Menu"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Header -->
|
<!-- Mobile menu -->
|
||||||
<header style="background: white; border-bottom: 2px solid #1e3a6e; position: sticky; top: 0; z-index: 50;">
|
<div id="mobile-menu" class="md:hidden flex-col border-t border-gray-100 bg-white pb-2">
|
||||||
<div style="max-width: 1200px; margin: 0 auto; padding: 0 1.5rem; display: flex; align-items: center; justify-content: space-between; height: 4rem;">
|
{[
|
||||||
|
{ href: '/themes/', label: 'Thèmes' },
|
||||||
|
{ href: '/donnees/', label: 'Données' },
|
||||||
|
{ href: '/scenarios/', label: 'Scénarios' },
|
||||||
|
{ href: '/rapports/', label: 'Publications' },
|
||||||
|
{ href: '/a-propos/', label: 'À propos' },
|
||||||
|
].map(({ href, label }) => (
|
||||||
|
<a href={href} class="block px-5 py-3 text-sm text-gray-600 hover:bg-gray-50 hover:text-[#1e3a6e] border-b border-gray-50 no-underline last:border-0">{label}</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
<a href="/" style="display: flex; align-items: center; gap: 0.75rem; text-decoration: none;">
|
<!-- Contenu -->
|
||||||
<div style="background: #1e3a6e; color: white; font-weight: 700; font-size: 1.125rem; padding: 0.25rem 0.625rem; border-radius: 0.25rem; letter-spacing: 0.05em;">IRPF</div>
|
<main class="flex-1">
|
||||||
<span style="font-size: 0.8rem; color: #374151; font-weight: 500; display: none;" class="sm-show">Institut de Recherche Politique pour la France</span>
|
<slot />
|
||||||
</a>
|
</main>
|
||||||
|
|
||||||
<nav style="display: flex; gap: 1.5rem; align-items: center;">
|
<!-- Footer -->
|
||||||
<a href="/themes/" style="font-size: 0.875rem; color: #374151; text-decoration: none; font-weight: 500;" class="nav-link">Thèmes</a>
|
<footer class="bg-[#0f172a] text-gray-400 mt-20">
|
||||||
<a href="/donnees/" style="font-size: 0.875rem; color: #374151; text-decoration: none; font-weight: 500;" class="nav-link">Données</a>
|
<div class="max-w-7xl mx-auto px-6 py-12 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||||
<a href="/propositions/" style="font-size: 0.875rem; color: #374151; text-decoration: none; font-weight: 500;" class="nav-link">Propositions</a>
|
<div class="lg:col-span-2">
|
||||||
<a href="/scenarios/" style="font-size: 0.875rem; color: #374151; text-decoration: none; font-weight: 500;" class="nav-link">Scénarios</a>
|
<div class="flex items-center gap-2.5 mb-3">
|
||||||
<a href="/rapports/" style="font-size: 0.875rem; color: #374151; text-decoration: none; font-weight: 500;" class="nav-link">Rapports</a>
|
<div class="bg-white text-[#0f172a] font-black text-sm px-2.5 py-1 rounded tracking-wider">IRPF</div>
|
||||||
<a href="/a-propos/" style="font-size: 0.875rem; color: #6b7280; text-decoration: none;" class="nav-link">À propos</a>
|
<span class="text-white font-semibold text-sm">Institut de Recherche Politique pour la France</span>
|
||||||
</nav>
|
</div>
|
||||||
|
<p class="text-sm text-gray-500 leading-relaxed max-w-sm mb-3">
|
||||||
|
Analyse indépendante, non-partisane, fondée sur les données publiques de l'INSEE, Eurostat, l'OCDE et des institutions parlementaires.
|
||||||
|
</p>
|
||||||
|
<p class="text-xs text-gray-600">Un projet <a href="https://reverdin.eu" class="text-gray-500 hover:text-gray-300 no-underline">Reverdin</a></p>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
<div>
|
||||||
|
<div class="text-gray-300 font-semibold text-xs uppercase tracking-wider mb-3">Analyses</div>
|
||||||
<!-- Contenu -->
|
<ul class="space-y-2 text-sm text-gray-500">
|
||||||
<main class="flex-1">
|
<li><a href="/themes/" class="hover:text-white no-underline transition-colors">Thèmes</a></li>
|
||||||
<slot />
|
<li><a href="/donnees/" class="hover:text-white no-underline transition-colors">Tableau de bord</a></li>
|
||||||
</main>
|
<li><a href="/scenarios/" class="hover:text-white no-underline transition-colors">Scénarios 2032</a></li>
|
||||||
|
<li><a href="/rapports/" class="hover:text-white no-underline transition-colors">Publications</a></li>
|
||||||
<!-- Footer -->
|
</ul>
|
||||||
<footer style="background: #0f172a; color: #9ca3af; margin-top: 4rem;">
|
|
||||||
<div style="max-width: 1200px; margin: 0 auto; padding: 3rem 1.5rem; display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 2rem;">
|
|
||||||
<div>
|
|
||||||
<div style="color: white; font-weight: 700; font-size: 1.125rem; margin-bottom: 0.75rem;">IRPF</div>
|
|
||||||
<p style="font-size: 0.875rem; line-height: 1.6;">Institut de Recherche Politique pour la France. Analyse indépendante, non-partisane, fondée sur les données publiques.</p>
|
|
||||||
<p style="font-size: 0.75rem; margin-top: 1rem; color: #6b7280;">Sources : INSEE, OCDE, Eurostat, Banque de France, data.gouv.fr et institutions publiques.</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div style="color: white; font-weight: 600; font-size: 0.875rem; margin-bottom: 0.75rem;">Thèmes</div>
|
|
||||||
<ul style="space-y: 0.5rem; font-size: 0.8rem;">
|
|
||||||
<li><a href="/themes/economie-finances/" style="color: #9ca3af; text-decoration: none; hover: white;">Économie</a></li>
|
|
||||||
<li><a href="/themes/sante/" style="color: #9ca3af; text-decoration: none;">Santé</a></li>
|
|
||||||
<li><a href="/themes/education/" style="color: #9ca3af; text-decoration: none;">Éducation</a></li>
|
|
||||||
<li><a href="/themes/" style="color: #6b7280; text-decoration: none;">Voir tous →</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div style="color: white; font-weight: 600; font-size: 0.875rem; margin-bottom: 0.75rem;">Analyses</div>
|
|
||||||
<ul style="font-size: 0.8rem;">
|
|
||||||
<li><a href="/donnees/" style="color: #9ca3af; text-decoration: none;">Tableau de bord</a></li>
|
|
||||||
<li><a href="/propositions/" style="color: #9ca3af; text-decoration: none;">Propositions</a></li>
|
|
||||||
<li><a href="/scenarios/" style="color: #9ca3af; text-decoration: none;">Scénarios 2032</a></li>
|
|
||||||
<li><a href="/rapports/" style="color: #9ca3af; text-decoration: none;">Publications</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div style="color: white; font-weight: 600; font-size: 0.875rem; margin-bottom: 0.75rem;">Institut</div>
|
|
||||||
<ul style="font-size: 0.8rem;">
|
|
||||||
<li><a href="/a-propos/" style="color: #9ca3af; text-decoration: none;">Mission</a></li>
|
|
||||||
<li><a href="/acteurs/" style="color: #9ca3af; text-decoration: none;">Cartographie</a></li>
|
|
||||||
<li><a href="/bibliotheque/" style="color: #9ca3af; text-decoration: none;">Bibliothèque</a></li>
|
|
||||||
<li><a href="/veille/" style="color: #9ca3af; text-decoration: none;">Veille</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div style="border-top: 1px solid #1f2937; text-align: center; padding: 1.5rem; font-size: 0.75rem; color: #4b5563;">
|
<div>
|
||||||
© {new Date().getFullYear()} Institut de Recherche Politique pour la France · Données publiques · Analyse libre
|
<div class="text-gray-300 font-semibold text-xs uppercase tracking-wider mb-3">Institut</div>
|
||||||
|
<ul class="space-y-2 text-sm text-gray-500">
|
||||||
|
<li><a href="/a-propos/" class="hover:text-white no-underline transition-colors">Mission</a></li>
|
||||||
|
<li><a href="/methode/" class="hover:text-white no-underline transition-colors">Méthode</a></li>
|
||||||
|
<li><a href="/sources/" class="hover:text-white no-underline transition-colors">Sources</a></li>
|
||||||
|
<li><a href="/contribuer/" class="hover:text-white no-underline transition-colors">Contribuer</a></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</div>
|
||||||
|
<div class="border-t border-gray-800 py-4 text-center text-xs text-gray-600">
|
||||||
|
© {new Date().getFullYear()} Institut de Recherche Politique pour la France · Données publiques · Analyse libre
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const burger = document.getElementById('burger');
|
||||||
|
const menu = document.getElementById('mobile-menu');
|
||||||
|
burger?.addEventListener('click', () => {
|
||||||
|
const open = menu?.classList.toggle('open');
|
||||||
|
burger?.setAttribute('aria-expanded', String(!!open));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,15 @@
|
||||||
---
|
---
|
||||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||||
import KpiLive from '../../components/KpiLive.astro';
|
import KpiLive from '../../components/KpiLive.astro';
|
||||||
import { INDICATEURS_BY_THEME, PAYS_LABEL } from '../../lib/indicators-config.ts';
|
|
||||||
import { existsSync, readFileSync } from 'node:fs';
|
import { existsSync, readFileSync } from 'node:fs';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
|
|
||||||
// Charger le JSON des indicateurs (généré en pré-build)
|
let indicateursData: any = { indicators: [], generated_at: null };
|
||||||
// Utilise readFileSync + existsSync pour éviter l'échec de résolution statique Vite
|
|
||||||
let indicateursData: any = { indicators: [], insee: {}, generated_at: null };
|
|
||||||
const jsonPath = join(process.cwd(), 'src/data/live/indicators.json');
|
const jsonPath = join(process.cwd(), 'src/data/live/indicators.json');
|
||||||
if (existsSync(jsonPath)) {
|
if (existsSync(jsonPath)) {
|
||||||
try {
|
try { indicateursData = JSON.parse(readFileSync(jsonPath, 'utf-8')); } catch {}
|
||||||
indicateursData = JSON.parse(readFileSync(jsonPath, 'utf-8'));
|
|
||||||
} catch {
|
|
||||||
// fichier corrompu, on utilise les défauts
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indexer par ID pour accès rapide
|
|
||||||
const byId = Object.fromEntries(
|
const byId = Object.fromEntries(
|
||||||
(indicateursData.indicators || []).map((i: any) => [i.id, i])
|
(indicateursData.indicators || []).map((i: any) => [i.id, i])
|
||||||
);
|
);
|
||||||
|
|
@ -26,54 +18,18 @@ const generatedAt = indicateursData.generated_at
|
||||||
? new Date(indicateursData.generated_at).toLocaleDateString('fr-FR', { day: 'numeric', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit' })
|
? new Date(indicateursData.generated_at).toLocaleDateString('fr-FR', { day: 'numeric', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit' })
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
// Groupes d'indicateurs pour le dashboard
|
|
||||||
const GROUPES = [
|
const GROUPES = [
|
||||||
{
|
{ titre: 'Finances publiques', couleur: '#1e3a6e', ids: ['deficit_pib','dette_pib','depenses_publiques_pib','prelevements_obligatoires','croissance_pib','depenses_sociales_pib'] },
|
||||||
titre: 'Finances publiques',
|
{ titre: 'Emploi', couleur: '#7c3aed', ids: ['taux_emploi','taux_chomage','taux_chomage_jeunes','taux_emploi_seniors'] },
|
||||||
couleur: '#1e3a6e',
|
{ titre: 'Santé', couleur: '#dc2626', ids: ['esperance_vie','depenses_sante_pib'] },
|
||||||
bg: '#eff6ff',
|
{ titre: 'Éducation', couleur: '#0891b2', ids: ['decrochage_scolaire','diplomes_superieurs','depenses_education_pib'] },
|
||||||
ids: ['deficit_pib', 'dette_pib', 'depenses_publiques_pib', 'prelevements_obligatoires', 'croissance_pib', 'depenses_sociales_pib'],
|
{ titre: 'Cohésion sociale', couleur: '#d97706', ids: ['gini','taux_pauvrete','taux_pauvrete_enfants'] },
|
||||||
},
|
{ titre: 'Logement', couleur: '#059669', ids: ['indice_prix_immobilier','surcharge_logement','taux_proprietaires'] },
|
||||||
{
|
{ titre: 'Environnement & Énergie', couleur: '#16a34a', ids: ['emissions_ghg_per_capita','part_energies_renouvelables','intensite_energetique'] },
|
||||||
titre: 'Emploi & Marché du travail',
|
|
||||||
couleur: '#7c3aed',
|
|
||||||
bg: '#f5f3ff',
|
|
||||||
ids: ['taux_emploi', 'taux_chomage', 'taux_chomage_jeunes', 'taux_emploi_seniors'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titre: 'Santé',
|
|
||||||
couleur: '#dc2626',
|
|
||||||
bg: '#fef2f2',
|
|
||||||
ids: ['esperance_vie', 'depenses_sante_pib'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titre: 'Éducation',
|
|
||||||
couleur: '#0891b2',
|
|
||||||
bg: '#ecfeff',
|
|
||||||
ids: ['decrochage_scolaire', 'diplomes_superieurs', 'depenses_education_pib'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titre: 'Cohésion sociale',
|
|
||||||
couleur: '#d97706',
|
|
||||||
bg: '#fffbeb',
|
|
||||||
ids: ['gini', 'taux_pauvrete', 'taux_pauvrete_enfants'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titre: 'Logement',
|
|
||||||
couleur: '#059669',
|
|
||||||
bg: '#f0fdf4',
|
|
||||||
ids: ['indice_prix_immobilier', 'surcharge_logement', 'taux_proprietaires'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titre: 'Environnement & Énergie',
|
|
||||||
couleur: '#16a34a',
|
|
||||||
bg: '#f0fdf4',
|
|
||||||
ids: ['emissions_ghg_per_capita', 'part_energies_renouvelables', 'intensite_energetique'],
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const PAYS_ORDRE = ['FR', 'DE', 'ES', 'IT', 'SE', 'NL', 'EU27_2020'];
|
const PAYS_ORDRE = ['FR', 'DE', 'ES', 'IT', 'SE', 'NL', 'EU27_2020'];
|
||||||
const INDICATEURS_TABLE = ['deficit_pib', 'dette_pib', 'depenses_publiques_pib', 'taux_chomage', 'taux_emploi', 'esperance_vie', 'gini', 'emissions_ghg_per_capita'];
|
const INDICATEURS_TABLE = ['deficit_pib','dette_pib','depenses_publiques_pib','taux_chomage','taux_emploi','esperance_vie','gini','emissions_ghg_per_capita'];
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout
|
<BaseLayout
|
||||||
|
|
@ -82,54 +38,62 @@ const INDICATEURS_TABLE = ['deficit_pib', 'dette_pib', 'depenses_publiques_pib',
|
||||||
>
|
>
|
||||||
|
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<section style="background: linear-gradient(135deg, #0f172a 0%, #1e3a6e 100%); color: white; padding: 3rem 1.5rem 2rem;">
|
<section class="bg-gradient-to-br from-[#0f172a] to-[#1e3a6e] text-white px-6 py-14">
|
||||||
<div style="max-width: 1200px; margin: 0 auto;">
|
<div class="max-w-7xl mx-auto">
|
||||||
<div style="font-size: 0.75rem; letter-spacing: 0.1em; color: #93c5fd; margin-bottom: 0.75rem;">IRPF — DONNÉES OUVERTES</div>
|
<nav class="text-xs text-gray-500 mb-6 flex items-center gap-1.5">
|
||||||
<h1 style="font-family: 'Playfair Display', serif; font-size: 2.25rem; margin-bottom: 1rem;">Tableau de bord France</h1>
|
<a href="/" class="hover:text-gray-300 no-underline">Accueil</a>
|
||||||
<p style="color: #cbd5e1; max-width: 650px; line-height: 1.7; margin-bottom: 1.5rem;">
|
<span>›</span>
|
||||||
Indicateurs clés de la France comparés à l'Allemagne, l'Espagne, l'Italie, la Suède, les Pays-Bas et la moyenne UE27.
|
<span class="text-gray-400">Données</span>
|
||||||
Sources : Eurostat API (données ouvertes) + INSEE BDM.
|
</nav>
|
||||||
|
<div class="text-xs text-blue-400 font-semibold uppercase tracking-wider mb-3">IRPF — Données ouvertes</div>
|
||||||
|
<h1 class="font-serif text-4xl font-black mb-3">Tableau de bord France</h1>
|
||||||
|
<p class="text-blue-200 max-w-2xl leading-relaxed text-sm mb-5">
|
||||||
|
Indicateurs clés de la France comparés à l'Allemagne, l'Espagne, l'Italie, la Suède, les Pays-Bas et la moyenne UE27. Sources : Eurostat API + INSEE BDM.
|
||||||
</p>
|
</p>
|
||||||
{generatedAt && (
|
{generatedAt && (
|
||||||
<div style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.75rem; color: #94a3b8;">
|
<div class="flex items-center gap-2 text-xs text-gray-400">
|
||||||
<span style="width: 8px; height: 8px; background: #22c55e; border-radius: 50;"></span>
|
<span class="w-2 h-2 rounded-full bg-green-400 inline-block"></span>
|
||||||
Mis à jour le {generatedAt} — {indicateursData.indicators?.length || 0} indicateurs collectés
|
Mis à jour le {generatedAt} — {indicateursData.indicators?.length ?? 0} indicateurs
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Navigation indicateurs -->
|
<!-- Nav groupes -->
|
||||||
<div style="background: white; border-bottom: 1px solid #e5e7eb; position: sticky; top: 0; z-index: 10;">
|
<div class="bg-white border-b border-gray-200 sticky top-[57px] z-30 overflow-x-auto">
|
||||||
<div style="max-width: 1200px; margin: 0 auto; padding: 0 1.5rem; overflow-x: auto;">
|
<div class="max-w-7xl mx-auto px-6 flex gap-0 scrollbar-hide">
|
||||||
<nav style="display: flex; gap: 0; white-space: nowrap;">
|
{GROUPES.map(g => (
|
||||||
{GROUPES.map(g => (
|
<a
|
||||||
<a href={`#${g.titre.toLowerCase().replace(/[^a-z0-9]+/g, '-')}`}
|
href={`#${g.titre.toLowerCase().replace(/[^a-z0-9]+/g, '-')}`}
|
||||||
style="padding: 0.875rem 1rem; font-size: 0.8rem; font-weight: 500; color: #6b7280; text-decoration: none; border-bottom: 2px solid transparent; display: inline-block;">
|
class="px-3 py-3.5 text-xs font-medium text-gray-500 hover:text-[#1e3a6e] whitespace-nowrap border-b-2 border-transparent hover:border-[#1e3a6e] -mb-px transition-colors no-underline"
|
||||||
{g.titre}
|
>
|
||||||
</a>
|
{g.titre}
|
||||||
))}
|
|
||||||
<a href="#comparaison" style="padding: 0.875rem 1rem; font-size: 0.8rem; font-weight: 500; color: #1e3a6e; text-decoration: none; border-bottom: 2px solid #1e3a6e;">
|
|
||||||
Comparatif EU →
|
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
))}
|
||||||
|
<a
|
||||||
|
href="#comparaison"
|
||||||
|
class="px-3 py-3.5 text-xs font-semibold text-[#1e3a6e] whitespace-nowrap border-b-2 border-[#1e3a6e] -mb-px no-underline ml-auto"
|
||||||
|
>
|
||||||
|
Comparatif EU →
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="max-width: 1200px; margin: 0 auto; padding: 3rem 1.5rem;">
|
<div class="max-w-7xl mx-auto px-6 py-10 space-y-12">
|
||||||
|
|
||||||
<!-- Groupes d'indicateurs -->
|
<!-- Groupes d'indicateurs -->
|
||||||
{GROUPES.map(groupe => {
|
{GROUPES.map(groupe => {
|
||||||
const inds = groupe.ids.map(id => byId[id]).filter(Boolean);
|
const inds = groupe.ids.map((id: string) => byId[id]).filter(Boolean);
|
||||||
if (inds.length === 0) return null;
|
if (!inds.length) return null;
|
||||||
|
const ancre = groupe.titre.toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
||||||
return (
|
return (
|
||||||
<section id={groupe.titre.toLowerCase().replace(/[^a-z0-9]+/g, '-')} style="margin-bottom: 3rem;">
|
<section id={ancre}>
|
||||||
<div style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1.25rem;">
|
<div class="flex items-center gap-3 mb-5">
|
||||||
<div style={`width: 3px; height: 1.5rem; background: ${groupe.couleur}; border-radius: 2px;`}></div>
|
<div class="w-1 h-6 rounded-full shrink-0" style={`background:${groupe.couleur}`}></div>
|
||||||
<h2 style={`font-size: 1.1rem; font-weight: 700; color: ${groupe.couleur}; margin: 0;`}>{groupe.titre}</h2>
|
<h2 class="font-semibold text-base" style={`color:${groupe.couleur}`}>{groupe.titre}</h2>
|
||||||
<div style="flex: 1; height: 1px; background: #e5e7eb;"></div>
|
<div class="flex-1 h-px bg-gray-200"></div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 0.75rem;">
|
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 gap-3">
|
||||||
{inds.map((ind: any) => (
|
{inds.map((ind: any) => (
|
||||||
<KpiLive
|
<KpiLive
|
||||||
label={ind.label}
|
label={ind.label}
|
||||||
|
|
@ -147,45 +111,44 @@ const INDICATEURS_TABLE = ['deficit_pib', 'dette_pib', 'depenses_publiques_pib',
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<!-- Tableau comparatif -->
|
<!-- Tableau comparatif -->
|
||||||
<section id="comparaison" style="margin-top: 2rem;">
|
<section id="comparaison">
|
||||||
<div style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1.5rem;">
|
<div class="flex items-center gap-3 mb-6">
|
||||||
<h2 style="font-family: 'Playfair Display', serif; font-size: 1.5rem; color: #0f172a; margin: 0;">Comparatif France — Europe</h2>
|
<h2 class="font-serif text-2xl text-[#0f172a]">Comparatif France — Europe</h2>
|
||||||
<div style="flex: 1; height: 1px; background: #e5e7eb;"></div>
|
<div class="flex-1 h-px bg-gray-200"></div>
|
||||||
<span style="font-size: 0.75rem; color: #9ca3af;">Source : Eurostat API</span>
|
<span class="text-xs text-gray-400">Eurostat API</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="overflow-x: auto; border-radius: 0.5rem; border: 1px solid #e5e7eb;">
|
<div class="overflow-x-auto rounded-xl border border-gray-200 shadow-sm">
|
||||||
<table style="width: 100%; border-collapse: collapse; font-size: 0.82rem;">
|
<table class="w-full text-sm border-collapse">
|
||||||
<thead>
|
<thead>
|
||||||
<tr style="background: #f8fafc; border-bottom: 2px solid #e5e7eb;">
|
<tr class="bg-[#0f172a] text-white text-xs">
|
||||||
<th style="text-align: left; padding: 0.75rem 1rem; font-weight: 600; color: #374151; min-width: 200px;">Indicateur</th>
|
<th class="text-left px-4 py-3 font-semibold min-w-[180px]">Indicateur</th>
|
||||||
{PAYS_ORDRE.map(code => (
|
{PAYS_ORDRE.map(code => (
|
||||||
<th style="text-align: right; padding: 0.75rem 0.75rem; font-weight: 600; color: code === 'FR' ? '#1e3a6e' : '#374151; min-width: 80px;">
|
<th class={`text-right px-3 py-3 font-semibold min-w-[70px] ${code === 'FR' ? 'text-blue-300' : ''}`}>
|
||||||
{code === 'EU27_2020' ? 'UE27' : code}
|
{code === 'EU27_2020' ? 'UE27' : code}
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{INDICATEURS_TABLE.map((id, idx) => {
|
{INDICATEURS_TABLE.map((id: string, idx: number) => {
|
||||||
const ind = byId[id];
|
const ind = byId[id];
|
||||||
if (!ind) return null;
|
if (!ind) return null;
|
||||||
return (
|
return (
|
||||||
<tr style={`border-bottom: 1px solid #e5e7eb; background: ${idx % 2 === 0 ? 'white' : '#f8fafc'};`}>
|
<tr class={idx % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
|
||||||
<td style="padding: 0.625rem 1rem; font-weight: 500; color: #374151;">
|
<td class="px-4 py-3 font-medium text-gray-700">
|
||||||
{ind.label}
|
{ind.label}
|
||||||
<div style="font-size: 0.7rem; color: #9ca3af; font-weight: 400;">{ind.unite}</div>
|
<div class="text-xs text-gray-400 font-normal">{ind.unite}</div>
|
||||||
</td>
|
</td>
|
||||||
{PAYS_ORDRE.map(code => {
|
{PAYS_ORDRE.map((code: string) => {
|
||||||
const pv = code === 'FR' ? ind.france : ind.pays?.[code];
|
const pv = code === 'FR' ? ind.france : ind.pays?.[code];
|
||||||
const val = pv?.value;
|
const val = pv?.value;
|
||||||
const formatted = val !== null && val !== undefined
|
const txt = val != null
|
||||||
? (Math.abs(val) >= 100 ? Math.round(val).toLocaleString('fr-FR') : val.toFixed(1))
|
? (Math.abs(val) >= 100 ? Math.round(val).toLocaleString('fr-FR') : Number(val).toFixed(1))
|
||||||
: '—';
|
: '—';
|
||||||
const isFr = code === 'FR';
|
|
||||||
return (
|
return (
|
||||||
<td style={`text-align: right; padding: 0.625rem 0.75rem; font-weight: ${isFr ? '700' : '400'}; color: ${isFr ? '#1e3a6e' : '#374151'};`}>
|
<td class={`text-right px-3 py-3 font-mono text-xs ${code === 'FR' ? 'font-bold text-[#1e3a6e]' : 'text-gray-600'}`}>
|
||||||
{formatted}
|
{txt}
|
||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
@ -195,33 +158,27 @@ const INDICATEURS_TABLE = ['deficit_pib', 'dette_pib', 'depenses_publiques_pib',
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<p style="font-size: 0.72rem; color: #9ca3af; margin-top: 0.75rem;">
|
<p class="text-xs text-gray-400 mt-2">
|
||||||
Dernière mise à jour : {generatedAt || 'N/A'} — Eurostat API JSON-stat 2.0
|
Mis à jour le {generatedAt ?? 'N/A'} — Eurostat API JSON-stat 2.0
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Note méthodologique -->
|
<!-- Note méthodologique -->
|
||||||
<section style="background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 0.5rem; padding: 1.5rem; margin-top: 2rem;">
|
<div class="bg-[#f8fafc] border border-gray-200 rounded-xl p-6">
|
||||||
<h3 style="font-size: 0.9rem; font-weight: 700; color: #374151; margin-bottom: 0.75rem;">Sources et méthodologie</h3>
|
<h3 class="text-sm font-bold text-gray-700 mb-3">Sources et méthodologie</h3>
|
||||||
<div style="font-size: 0.8rem; color: #6b7280; line-height: 1.7; display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;">
|
<div class="grid sm:grid-cols-2 gap-5 text-xs text-gray-500 leading-relaxed">
|
||||||
<div>
|
<p>
|
||||||
<strong>Eurostat</strong> — API JSON-stat 2.0, données officielles de l'Office statistique de l'Union européenne.
|
<strong class="text-gray-700">Eurostat</strong> — API JSON-stat 2.0, données officielles de l'Office statistique de l'Union européenne. Mise à jour automatique à chaque rebuild du site (max 6h de délai). CORS ouvert, pas d'authentification requise.
|
||||||
Mise à jour automatique à chaque rebuild du site (max 6h de délai).
|
</p>
|
||||||
CORS ouvert, pas d'authentification requise.
|
<p>
|
||||||
</div>
|
<strong class="text-gray-700">Comparaison</strong> — France vs Allemagne, Espagne, Italie, Suède, Pays-Bas et moyenne UE27. Données non disponibles (N/D) = non publiées par Eurostat pour cette période. INSEE OAuth2 en cours de configuration.
|
||||||
<div>
|
</p>
|
||||||
<strong>Comparaison</strong> — France vs Allemagne, Espagne, Italie, Suède, Pays-Bas et moyenne UE27.
|
|
||||||
Les valeurs <span style="color: #dc2626;">en rouge</span> indiquent un retard par rapport à la moyenne UE ;
|
|
||||||
les valeurs <span style="color: #16a34a;">en vert</span> indiquent une avance.
|
|
||||||
Données non disponibles (N/D) = non publiées par Eurostat pour cette période.
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
th { white-space: nowrap; }
|
[id] { scroll-margin-top: 110px; }
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,148 +1,275 @@
|
||||||
---
|
---
|
||||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
import { getCollection } from 'astro:content';
|
import { getCollection } from 'astro:content';
|
||||||
|
import { existsSync, readFileSync } from 'node:fs';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
|
||||||
const themes = (await getCollection('themes')).sort((a, b) => a.data.ordre - b.data.ordre);
|
const themes = (await getCollection('themes')).sort((a, b) => a.data.ordre - b.data.ordre);
|
||||||
const rapports = (await getCollection('rapports')).slice(0, 3);
|
|
||||||
|
// Indicateurs live
|
||||||
|
let byId: Record<string, any> = {};
|
||||||
|
let generatedAt = '';
|
||||||
|
const jsonPath = join(process.cwd(), 'src/data/live/indicators.json');
|
||||||
|
if (existsSync(jsonPath)) {
|
||||||
|
try {
|
||||||
|
const d = JSON.parse(readFileSync(jsonPath, 'utf-8'));
|
||||||
|
byId = Object.fromEntries((d.indicators ?? []).map((i: any) => [i.id, i]));
|
||||||
|
generatedAt = d.generated_at ?? '';
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fmt(id: string, digits = 1): string {
|
||||||
|
const v = byId[id]?.france?.value;
|
||||||
|
if (v == null) return 'N/D';
|
||||||
|
return Number(v).toLocaleString('fr-FR', { minimumFractionDigits: digits, maximumFractionDigits: digits });
|
||||||
|
}
|
||||||
|
|
||||||
|
const THEMES_A_VENIR = [
|
||||||
|
'Éducation', 'Emploi & Travail', 'Retraites', 'Logement',
|
||||||
|
'Justice & Sécurité', 'Énergie & Environnement', 'Agriculture',
|
||||||
|
'Numérique', 'Cohésion sociale', 'Démocratie', 'Europe', 'Immigration',
|
||||||
|
];
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout
|
<BaseLayout
|
||||||
title="IRPF — Institut de Recherche Politique pour la France"
|
title="IRPF — Institut de Recherche Politique pour la France"
|
||||||
description="Analyse indépendante des grandes problématiques françaises. Données, diagnostics, propositions pour 2027 et au-delà."
|
description="Analyse non-partisane des politiques publiques françaises fondée sur les données de l'INSEE, l'OCDE et Eurostat. Diagnostics, propositions, scénarios pour 2027."
|
||||||
>
|
>
|
||||||
|
|
||||||
<!-- Hero -->
|
<!-- ── HERO ──────────────────────────────────────────── -->
|
||||||
<section style="background: linear-gradient(135deg, #0f172a 0%, #1e3a6e 100%); color: white; padding: 5rem 1.5rem 4rem;">
|
<section class="relative bg-white overflow-hidden">
|
||||||
<div style="max-width: 900px; margin: 0 auto; text-align: center;">
|
<!-- Fond graphique subtil -->
|
||||||
<div style="font-size: 0.8rem; letter-spacing: 0.15em; color: #93c5fd; margin-bottom: 1.5rem; font-weight: 500;">
|
<div class="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,_#eff6ff_0%,_transparent_60%)]"></div>
|
||||||
INSTITUT DE RECHERCHE POLITIQUE POUR LA FRANCE
|
<div class="relative max-w-7xl mx-auto px-6 pt-20 pb-16 lg:pt-28 lg:pb-24 grid lg:grid-cols-2 gap-12 items-center">
|
||||||
|
<div>
|
||||||
|
<div class="section-label mb-4">Institut de Recherche Politique pour la France</div>
|
||||||
|
<h1 class="font-serif text-5xl lg:text-6xl font-black text-[#0f172a] leading-tight mb-6">
|
||||||
|
La France<br/>sous les données.
|
||||||
|
</h1>
|
||||||
|
<p class="text-lg text-gray-500 leading-relaxed mb-8 max-w-xl">
|
||||||
|
Des diagnostics rigoureux sur les 14 grandes problématiques françaises. Des données, pas des slogans. Des propositions chiffrées, pas des promesses.
|
||||||
|
</p>
|
||||||
|
<div class="flex flex-wrap gap-3">
|
||||||
|
<a href="/themes/" class="inline-flex items-center gap-2 bg-[#1e3a6e] text-white px-5 py-2.5 rounded-lg font-semibold text-sm hover:bg-[#162d56] transition-colors no-underline">
|
||||||
|
Explorer les analyses →
|
||||||
|
</a>
|
||||||
|
<a href="/donnees/" class="inline-flex items-center gap-2 border border-gray-300 text-gray-600 px-5 py-2.5 rounded-lg font-medium text-sm hover:border-[#1e3a6e] hover:text-[#1e3a6e] transition-colors no-underline">
|
||||||
|
Tableau de bord France
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<p class="mt-5 text-xs text-gray-400">
|
||||||
|
Non-partisan · Données publiques · Accès libre
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<h1 style="font-family: 'Playfair Display', serif; font-size: 3rem; font-weight: 700; line-height: 1.2; margin-bottom: 1.5rem;">
|
|
||||||
Comprendre la France.<br/>
|
<!-- Carte stat mise en avant -->
|
||||||
<span style="color: #93c5fd;">Proposer des solutions.</span>
|
<div class="hidden lg:block">
|
||||||
</h1>
|
<div class="bg-[#0f172a] rounded-2xl p-8 text-white shadow-2xl">
|
||||||
<p style="font-size: 1.125rem; color: #cbd5e1; max-width: 680px; margin: 0 auto 2.5rem; line-height: 1.7;">
|
<div class="text-xs text-blue-400 font-semibold uppercase tracking-wider mb-6">France — chiffres clés 2025</div>
|
||||||
Analyse non-partisane des politiques publiques françaises, fondée sur les données de l'INSEE, de l'OCDE et des institutions internationales. Pour alimenter le débat démocratique en 2027 et au-delà.
|
<div class="space-y-5">
|
||||||
|
{[
|
||||||
|
{ id: 'dette_pib', label: 'Dette publique / PIB', unit: '%', alert: true },
|
||||||
|
{ id: 'deficit_pib', label: 'Déficit public / PIB', unit: '%', alert: true },
|
||||||
|
{ id: 'taux_chomage_jeunes',label: 'Chômage des jeunes', unit: '%', alert: false },
|
||||||
|
{ id: 'croissance_pib', label: 'Croissance du PIB', unit: '%', alert: false },
|
||||||
|
{ id: 'esperance_vie', label: 'Espérance de vie', unit: ' ans', alert: false },
|
||||||
|
].map(({ id, label, unit, alert }) => (
|
||||||
|
<div class="flex items-baseline justify-between border-b border-white/10 pb-4 last:border-0 last:pb-0">
|
||||||
|
<span class="text-sm text-gray-400">{label}</span>
|
||||||
|
<span class={`font-mono font-bold text-lg ${alert ? 'text-red-400' : 'text-green-400'}`}>
|
||||||
|
{fmt(id)}{unit}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div class="mt-5 text-xs text-gray-600 text-right">
|
||||||
|
Source : Eurostat · {generatedAt ? new Date(generatedAt).toLocaleDateString('fr-FR', { month: 'long', year: 'numeric' }) : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ── CHIFFRES CLÉS (mobile / complément) ─────────── -->
|
||||||
|
<section class="bg-[#0f172a] border-t border-gray-800">
|
||||||
|
<div class="max-w-7xl mx-auto px-6 py-8 grid grid-cols-2 sm:grid-cols-4 gap-px bg-gray-800">
|
||||||
|
{[
|
||||||
|
{ id: 'dette_pib', label: 'Dette / PIB', unit: '%' },
|
||||||
|
{ id: 'deficit_pib', label: 'Déficit / PIB', unit: '%' },
|
||||||
|
{ id: 'taux_chomage', label: 'Chômage', unit: '%' },
|
||||||
|
{ id: 'prelevements_obligatoires', label: 'Prélèvements oblig.', unit: '%' },
|
||||||
|
].map(({ id, label, unit }) => (
|
||||||
|
<div class="bg-[#0f172a] px-6 py-5 text-center">
|
||||||
|
<div class="font-mono text-2xl font-bold text-white">{fmt(id)}<span class="text-sm">{unit}</span></div>
|
||||||
|
<div class="text-xs text-gray-500 mt-1">{label}</div>
|
||||||
|
<div class="text-xs text-gray-700 mt-0.5">{byId[id]?.france?.year ?? ''}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ── MÉTHODE ─────────────────────────────────────── -->
|
||||||
|
<section class="py-20 px-6 max-w-7xl mx-auto">
|
||||||
|
<div class="text-center mb-12">
|
||||||
|
<div class="section-label mb-3">Notre démarche</div>
|
||||||
|
<h2 class="font-serif text-3xl text-[#0f172a] mb-4">Le diagnostic avant le programme</h2>
|
||||||
|
<p class="text-gray-500 max-w-2xl mx-auto leading-relaxed">
|
||||||
|
Le débat politique français manque de données. L'IRPF fait le chemin inverse : partir des faits pour cartographier chaque problématique, recenser les propositions, et évaluer leur impact.
|
||||||
</p>
|
</p>
|
||||||
<div style="display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap;">
|
</div>
|
||||||
<a href="/themes/" style="background: white; color: #1e3a6e; padding: 0.75rem 2rem; border-radius: 0.375rem; font-weight: 600; text-decoration: none; font-size: 0.9rem;">
|
|
||||||
Explorer les thèmes →
|
<div class="grid md:grid-cols-3 gap-8">
|
||||||
</a>
|
{[
|
||||||
<a href="/donnees/" style="border: 1px solid rgba(255,255,255,0.3); color: white; padding: 0.75rem 2rem; border-radius: 0.375rem; font-weight: 500; text-decoration: none; font-size: 0.9rem;">
|
{
|
||||||
Tableau de bord France
|
num: '01',
|
||||||
</a>
|
titre: 'Données publiques',
|
||||||
</div>
|
desc: 'INSEE, OCDE, Eurostat, Banque de France, Cour des comptes, data.gouv.fr. Chaque chiffre est sourcé, daté, contextualisé.',
|
||||||
|
color: '#1e3a6e',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: '02',
|
||||||
|
titre: 'Diagnostic rigoureux',
|
||||||
|
desc: 'Cartographie des dynamiques longues, comparaisons européennes, identification des nœuds structurels — sans idéologie.',
|
||||||
|
color: '#2563eb',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: '03',
|
||||||
|
titre: 'Propositions chiffrées',
|
||||||
|
desc: 'Recensement de toutes les propositions des acteurs politiques, économiques et de la société civile. Évaluation de leur faisabilité.',
|
||||||
|
color: '#0891b2',
|
||||||
|
},
|
||||||
|
].map(({ num, titre, desc, color }) => (
|
||||||
|
<div class="relative p-8 rounded-xl border border-gray-200 bg-white hover:shadow-md transition-shadow">
|
||||||
|
<div class="text-5xl font-black mb-4 leading-none" style={`color: ${color}; opacity: 0.12;`}>{num}</div>
|
||||||
|
<h3 class="font-semibold text-[#0f172a] text-lg mb-3">{titre}</h3>
|
||||||
|
<p class="text-sm text-gray-500 leading-relaxed">{desc}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- KPIs -->
|
<!-- ── THÈMES ─────────────────────────────────────── -->
|
||||||
<section style="background: #f8fafc; border-bottom: 1px solid #e5e7eb; padding: 2rem 1.5rem;">
|
<section class="bg-[#f8fafc] border-y border-gray-200 py-20 px-6">
|
||||||
<div style="max-width: 1200px; margin: 0 auto;">
|
<div class="max-w-7xl mx-auto">
|
||||||
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem;">
|
<div class="flex items-baseline justify-between mb-10">
|
||||||
<div class="kpi-card" style="text-align: center;">
|
|
||||||
<div class="kpi-value">-5,5%</div>
|
|
||||||
<div class="kpi-label">Déficit public / PIB 2024</div>
|
|
||||||
<div class="kpi-source">Source : INSEE</div>
|
|
||||||
</div>
|
|
||||||
<div class="kpi-card" style="text-align: center;">
|
|
||||||
<div class="kpi-value">113%</div>
|
|
||||||
<div class="kpi-label">Dette publique / PIB</div>
|
|
||||||
<div class="kpi-source">Source : Eurostat</div>
|
|
||||||
</div>
|
|
||||||
<div class="kpi-card" style="text-align: center;">
|
|
||||||
<div class="kpi-value">7,3%</div>
|
|
||||||
<div class="kpi-label">Taux de chômage T1 2025</div>
|
|
||||||
<div class="kpi-source">Source : DARES</div>
|
|
||||||
</div>
|
|
||||||
<div class="kpi-card" style="text-align: center;">
|
|
||||||
<div class="kpi-value">28e</div>
|
|
||||||
<div class="kpi-label">Rang PISA 2022 (sciences)</div>
|
|
||||||
<div class="kpi-source">Source : OCDE</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Manifeste -->
|
|
||||||
<section style="padding: 4rem 1.5rem; max-width: 780px; margin: 0 auto;">
|
|
||||||
<div style="font-size: 0.8rem; letter-spacing: 0.1em; color: #1e3a6e; font-weight: 600; margin-bottom: 1rem;">NOTRE DÉMARCHE</div>
|
|
||||||
<h2 style="font-family: 'Playfair Display', serif; font-size: 2rem; color: #0f172a; margin-bottom: 1.5rem;">
|
|
||||||
La France a besoin d'un diagnostic rigoureux avant un programme
|
|
||||||
</h2>
|
|
||||||
<div style="color: #374151; line-height: 1.8; font-size: 1.05rem;">
|
|
||||||
<p style="margin-bottom: 1rem;">Le débat politique français souffre d'un paradoxe : beaucoup de propositions, peu de données ; beaucoup de slogans, peu de diagnostic. L'IRPF fait le chemin inverse.</p>
|
|
||||||
<p style="margin-bottom: 1rem;">Nous partons des faits — ceux de l'INSEE, de l'OCDE, de la Cour des comptes, des rapports parlementaires — pour cartographier chaque grande problématique française, recenser toutes les propositions des acteurs du débat, et proposer des analyses originales.</p>
|
|
||||||
<p>Ni de gauche ni de droite. Exigeant sur les chiffres. Libre sur les idées.</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Thèmes -->
|
|
||||||
<section style="background: #f8fafc; padding: 4rem 1.5rem; border-top: 1px solid #e5e7eb;">
|
|
||||||
<div style="max-width: 1200px; margin: 0 auto;">
|
|
||||||
<div style="display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 2rem;">
|
|
||||||
<div>
|
<div>
|
||||||
<div style="font-size: 0.8rem; letter-spacing: 0.1em; color: #1e3a6e; font-weight: 600; margin-bottom: 0.5rem;">14 GRANDES THÉMATIQUES</div>
|
<div class="section-label mb-2">14 grandes thématiques</div>
|
||||||
<h2 style="font-family: 'Playfair Display', serif; font-size: 1.875rem; color: #0f172a;">Les chantiers de la France</h2>
|
<h2 class="font-serif text-3xl text-[#0f172a]">Les chantiers de la France</h2>
|
||||||
</div>
|
</div>
|
||||||
<a href="/themes/" style="color: #1e3a6e; font-weight: 500; text-decoration: none; font-size: 0.9rem;">Voir tout →</a>
|
<a href="/themes/" class="text-sm text-[#1e3a6e] font-medium hover:underline no-underline hidden sm:block">Voir tout →</a>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;">
|
|
||||||
{themes.slice(0, 6).map((theme) => (
|
<!-- Fiches publiées -->
|
||||||
<a href={`/themes/${theme.data.slug}/`} style="background: white; border: 1px solid #e5e7eb; border-radius: 0.5rem; padding: 1.5rem; text-decoration: none; display: block;" class="theme-card">
|
{themes.length > 0 && (
|
||||||
<div style="font-weight: 600; color: #0f172a; margin-bottom: 0.5rem;">{theme.data.title}</div>
|
<div class="mb-6">
|
||||||
<p style="font-size: 0.85rem; color: #6b7280; line-height: 1.5; margin-bottom: 1rem;">{theme.data.description}</p>
|
<div class="flex items-center gap-3 mb-5">
|
||||||
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
|
<span class="badge badge-dark">EN LIGNE</span>
|
||||||
{theme.data.priorite === 'critique' && (
|
<div class="flex-1 h-px bg-gray-300"></div>
|
||||||
<span style="background: #fee2e2; color: #991b1b; font-size: 0.7rem; font-weight: 600; padding: 0.2rem 0.5rem; border-radius: 0.25rem;">CRITIQUE</span>
|
</div>
|
||||||
)}
|
<div class="grid sm:grid-cols-2 gap-5">
|
||||||
{theme.data.budget_etat_milliards && (
|
{themes.map(theme => (
|
||||||
<span style="background: #eff6ff; color: #1d4ed8; font-size: 0.7rem; padding: 0.2rem 0.5rem; border-radius: 0.25rem;">{theme.data.budget_etat_milliards} Md€</span>
|
<a href={`/themes/${theme.data.slug}/`} class="card p-6 no-underline block group">
|
||||||
)}
|
<div class="flex items-start justify-between gap-3 mb-3">
|
||||||
</div>
|
<h3 class="font-semibold text-[#0f172a] text-base group-hover:text-[#1e3a6e] transition-colors leading-snug">
|
||||||
</a>
|
{theme.data.title}
|
||||||
))}
|
</h3>
|
||||||
</div>
|
{theme.data.priorite === 'critique' && <span class="badge badge-critique shrink-0">CRITIQUE</span>}
|
||||||
{themes.length > 6 && (
|
{theme.data.priorite === 'haute' && <span class="badge badge-haute shrink-0">PRIORITÉ</span>}
|
||||||
<div style="text-align: center; margin-top: 2rem;">
|
</div>
|
||||||
<a href="/themes/" style="display: inline-block; border: 1px solid #1e3a6e; color: #1e3a6e; padding: 0.625rem 1.5rem; border-radius: 0.375rem; font-weight: 500; text-decoration: none; font-size: 0.9rem;">
|
<p class="text-sm text-gray-500 leading-relaxed mb-4">{theme.data.description}</p>
|
||||||
Voir les {themes.length - 6} autres thèmes
|
<div class="flex items-center justify-between">
|
||||||
</a>
|
<div class="flex gap-2 flex-wrap">
|
||||||
|
{theme.data.budget_etat_milliards && (
|
||||||
|
<span class="badge badge-blue">{theme.data.budget_etat_milliards} Md€/an</span>
|
||||||
|
)}
|
||||||
|
{theme.data.tags.slice(0, 2).map((tag: string) => (
|
||||||
|
<span class="badge badge-muted">#{tag}</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<span class="text-xs text-[#1e3a6e] font-medium opacity-0 group-hover:opacity-100 transition-opacity">Lire →</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<!-- Thèmes à venir -->
|
||||||
|
<div>
|
||||||
|
<div class="flex items-center gap-3 mb-5">
|
||||||
|
<span class="badge badge-muted">À VENIR</span>
|
||||||
|
<div class="flex-1 h-px bg-gray-200"></div>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3">
|
||||||
|
{THEMES_A_VENIR.map(t => (
|
||||||
|
<div class="rounded-lg border border-dashed border-gray-300 px-4 py-3 text-sm text-gray-400 bg-white/60 select-none">
|
||||||
|
{t}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-8 text-center sm:hidden">
|
||||||
|
<a href="/themes/" class="text-sm text-[#1e3a6e] font-medium hover:underline no-underline">Voir tous les thèmes →</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Scénarios -->
|
<!-- ── SCÉNARIOS ──────────────────────────────────── -->
|
||||||
<section style="padding: 4rem 1.5rem; max-width: 1200px; margin: 0 auto;">
|
<section class="py-20 px-6 max-w-7xl mx-auto">
|
||||||
<div style="background: linear-gradient(135deg, #0f172a, #1e3a6e); border-radius: 0.75rem; padding: 3rem; color: white; display: grid; grid-template-columns: 1fr 1fr; gap: 3rem; align-items: center;">
|
<div class="bg-[#0f172a] rounded-2xl p-10 lg:p-14 grid lg:grid-cols-5 gap-10 items-center">
|
||||||
<div>
|
<div class="lg:col-span-3">
|
||||||
<div style="font-size: 0.8rem; letter-spacing: 0.1em; color: #93c5fd; font-weight: 600; margin-bottom: 1rem;">SCÉNARIOS MACRO 2027–2032</div>
|
<div class="text-xs text-blue-400 font-semibold uppercase tracking-wider mb-4">Projection macro 2027–2032</div>
|
||||||
<h2 style="font-family: 'Playfair Display', serif; font-size: 1.875rem; margin-bottom: 1rem;">Trois avenirs possibles pour la France</h2>
|
<h2 class="font-serif text-3xl text-white mb-4 leading-tight">
|
||||||
<p style="color: #cbd5e1; line-height: 1.7; margin-bottom: 1.5rem;">De la dérive budgétaire au choc républicain : nous chiffrons les conséquences de chaque trajectoire sur la dette, la croissance et les services publics.</p>
|
Trois trajectoires possibles pour la France
|
||||||
<a href="/scenarios/" style="background: white; color: #1e3a6e; padding: 0.625rem 1.5rem; border-radius: 0.375rem; font-weight: 600; text-decoration: none; font-size: 0.9rem;">Lire les scénarios →</a>
|
</h2>
|
||||||
|
<p class="text-gray-400 leading-relaxed mb-6 text-sm">
|
||||||
|
Déficit, dette, croissance, services publics — nous chiffrons les conséquences de chaque trajectoire politique sur un horizon de 5 ans.
|
||||||
|
</p>
|
||||||
|
<a href="/scenarios/" class="inline-flex items-center gap-2 bg-white text-[#1e3a6e] px-5 py-2.5 rounded-lg font-semibold text-sm hover:bg-blue-50 transition-colors no-underline">
|
||||||
|
Lire les scénarios →
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex; flex-direction: column; gap: 1rem;">
|
<div class="lg:col-span-2 space-y-3">
|
||||||
<div style="background: rgba(255,255,255,0.07); border-left: 3px solid #ef4444; padding: 1rem 1.25rem; border-radius: 0.25rem;">
|
{[
|
||||||
<div style="font-weight: 600; color: white;">Dérive</div>
|
{ label: 'Dérive', kpi: '-5,5% PIB · +0,8%/an', color: '#ef4444' },
|
||||||
<div style="font-size: 0.85rem; color: #ef4444; font-weight: 500;">-5,5% PIB en 2032 · +0,8%/an</div>
|
{ label: 'Réforme graduelle',kpi: '-3% PIB · +1,4%/an', color: '#f59e0b' },
|
||||||
</div>
|
{ label: 'Choc républicain', kpi: '-1,5% PIB · +2%/an', color: '#22c55e' },
|
||||||
<div style="background: rgba(255,255,255,0.07); border-left: 3px solid #f59e0b; padding: 1rem 1.25rem; border-radius: 0.25rem;">
|
].map(({ label, kpi, color }) => (
|
||||||
<div style="font-weight: 600; color: white;">Réforme graduelle</div>
|
<div class="bg-white/5 border border-white/10 rounded-xl px-5 py-4 flex items-center justify-between">
|
||||||
<div style="font-size: 0.85rem; color: #f59e0b; font-weight: 500;">-3% PIB en 2030 · +1,4%/an</div>
|
<div class="flex items-center gap-3">
|
||||||
</div>
|
<div class="w-2.5 h-2.5 rounded-full shrink-0" style={`background: ${color};`}></div>
|
||||||
<div style="background: rgba(255,255,255,0.07); border-left: 3px solid #22c55e; padding: 1rem 1.25rem; border-radius: 0.25rem;">
|
<span class="font-medium text-white text-sm">{label}</span>
|
||||||
<div style="font-weight: 600; color: white;">Choc républicain</div>
|
</div>
|
||||||
<div style="font-size: 0.85rem; color: #22c55e; font-weight: 500;">-1,5% PIB en 2032 · +2%/an</div>
|
<span class="font-mono text-xs font-semibold" style={`color: ${color};`}>{kpi}</span>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ── À PROPOS ────────────────────────────────────── -->
|
||||||
|
<section class="bg-[#f8fafc] border-t border-gray-200 py-20 px-6">
|
||||||
|
<div class="max-w-3xl mx-auto text-center">
|
||||||
|
<div class="section-label mb-4">Pourquoi l'IRPF</div>
|
||||||
|
<h2 class="font-serif text-3xl text-[#0f172a] mb-6">Ni de gauche ni de droite. Exigeant sur les chiffres.</h2>
|
||||||
|
<div class="text-gray-500 leading-relaxed space-y-4 text-base">
|
||||||
|
<p>
|
||||||
|
Le débat politique français souffre d'un paradoxe : beaucoup de propositions, peu de données ; beaucoup de slogans, peu de diagnostic. Chaque camp dispose de ses instituts, de ses économistes, de ses chiffres. L'IRPF fait le chemin inverse.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Nous partons des faits — ceux de l'INSEE, de l'OCDE, de la Cour des comptes, des rapports parlementaires — pour cartographier chaque grande problématique française, recenser toutes les propositions des acteurs du débat, et proposer des analyses originales.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="mt-8 flex flex-wrap gap-4 justify-center">
|
||||||
|
<a href="/donnees/" class="inline-flex items-center gap-2 bg-[#1e3a6e] text-white px-5 py-2.5 rounded-lg font-semibold text-sm hover:bg-[#162d56] transition-colors no-underline">
|
||||||
|
Tableau de bord France
|
||||||
|
</a>
|
||||||
|
<a href="/methode/" class="inline-flex items-center gap-2 border border-gray-300 text-gray-600 px-5 py-2.5 rounded-lg font-medium text-sm hover:border-[#1e3a6e] hover:text-[#1e3a6e] transition-colors no-underline">
|
||||||
|
Notre méthode
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
<style>
|
|
||||||
.theme-card:hover {
|
|
||||||
border-color: #1e3a6e !important;
|
|
||||||
box-shadow: 0 2px 8px rgba(30,58,110,0.1);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ const { Content } = await render(theme);
|
||||||
const allPropositions = await getCollection('propositions');
|
const allPropositions = await getCollection('propositions');
|
||||||
const propositions = allPropositions.filter(p => p.data.theme === theme.data.slug);
|
const propositions = allPropositions.filter(p => p.data.theme === theme.data.slug);
|
||||||
|
|
||||||
// Sections configurées dans le frontmatter → nav dynamique
|
|
||||||
const SECTION_LABELS: Record<string, string> = {
|
const SECTION_LABELS: Record<string, string> = {
|
||||||
diagnostic: 'Diagnostic',
|
diagnostic: 'Diagnostic',
|
||||||
historique: 'Historique',
|
historique: 'Historique',
|
||||||
|
|
@ -29,115 +28,115 @@ const SECTION_LABELS: Record<string, string> = {
|
||||||
sources: 'Sources',
|
sources: 'Sources',
|
||||||
};
|
};
|
||||||
|
|
||||||
const navSections = theme.data.sections.map(s => ({
|
const navSections = theme.data.sections.map((s: string) => ({
|
||||||
id: s,
|
id: s,
|
||||||
label: SECTION_LABELS[s] ?? s,
|
label: SECTION_LABELS[s] ?? s,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function orientationBadge(o?: string) {
|
const TENDANCE_COLOR: Record<string, string> = {
|
||||||
const map: Record<string, string> = {
|
hausse: '#16a34a', baisse: '#dc2626', stable: '#6b7280', critique: '#dc2626', alerte: '#d97706'
|
||||||
'gauche': 'background:#fee2e2;color:#991b1b',
|
};
|
||||||
'centre-gauche': 'background:#fce7f3;color:#9d174d',
|
const TENDANCE_ICON: Record<string, string> = {
|
||||||
'centre': 'background:#dbeafe;color:#1e40af',
|
hausse: '↑', baisse: '↓', stable: '→', critique: '⚠', alerte: '!'
|
||||||
'centre-droit': 'background:#ede9fe;color:#5b21b6',
|
};
|
||||||
'droite': 'background:#f3e8ff;color:#6b21a8',
|
|
||||||
'neutre': 'background:#f3f4f6;color:#374151',
|
|
||||||
'irpf': 'background:#0f172a;color:white',
|
|
||||||
};
|
|
||||||
return map[o ?? 'neutre'] ?? map['neutre'];
|
|
||||||
}
|
|
||||||
|
|
||||||
function tendanceCouleur(t?: string) {
|
const ORIENTATION_CLASS: Record<string, string> = {
|
||||||
return { hausse: '#16a34a', baisse: '#dc2626', stable: '#6b7280', critique: '#dc2626', alerte: '#d97706' }[t ?? 'stable'] ?? '#6b7280';
|
'gauche': 'bg-red-100 text-red-800',
|
||||||
}
|
'centre-gauche': 'bg-pink-100 text-pink-800',
|
||||||
|
'centre': 'bg-blue-100 text-blue-800',
|
||||||
function tendanceIcon(t?: string) {
|
'centre-droit': 'bg-violet-100 text-violet-800',
|
||||||
return { hausse: '↑', baisse: '↓', stable: '→', critique: '⚠', alerte: '!' }[t ?? 'stable'] ?? '→';
|
'droite': 'bg-purple-100 text-purple-800',
|
||||||
}
|
'neutre': 'bg-gray-100 text-gray-700',
|
||||||
|
'irpf': 'bg-[#0f172a] text-white',
|
||||||
|
};
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout
|
<BaseLayout title={`${theme.data.title} — IRPF`} description={theme.data.description}>
|
||||||
title={`${theme.data.title} — IRPF`}
|
|
||||||
description={theme.data.description}
|
|
||||||
>
|
|
||||||
|
|
||||||
<!-- Breadcrumb -->
|
<!-- Breadcrumb -->
|
||||||
<div style="background: #f8fafc; border-bottom: 1px solid #e5e7eb; padding: 0.75rem 1.5rem;">
|
<div class="bg-[#f8fafc] border-b border-gray-200 px-6 py-3">
|
||||||
<div style="max-width: 1200px; margin: 0 auto; font-size: 0.8rem; color: #6b7280;">
|
<nav class="max-w-7xl mx-auto text-xs text-gray-500 flex items-center gap-1.5">
|
||||||
<a href="/" style="color: #6b7280; text-decoration: none;">Accueil</a>
|
<a href="/" class="hover:text-gray-700 no-underline">Accueil</a>
|
||||||
<span style="margin: 0 0.5rem;">›</span>
|
<span>›</span>
|
||||||
<a href="/themes/" style="color: #6b7280; text-decoration: none;">Thèmes</a>
|
<a href="/themes/" class="hover:text-gray-700 no-underline">Thèmes</a>
|
||||||
<span style="margin: 0 0.5rem;">›</span>
|
<span>›</span>
|
||||||
<span style="color: #374151;">{theme.data.title}</span>
|
<span class="text-gray-700">{theme.data.title}</span>
|
||||||
</div>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Header enrichi -->
|
<!-- Hero -->
|
||||||
<section style="background: linear-gradient(135deg, #0f172a 0%, #1e3a6e 100%); color: white; padding: 3rem 1.5rem 2.5rem;">
|
<section class="bg-gradient-to-br from-[#0f172a] to-[#1e3a6e] text-white px-6 py-12">
|
||||||
<div style="max-width: 1200px; margin: 0 auto;">
|
<div class="max-w-7xl mx-auto">
|
||||||
<div style="display: grid; grid-template-columns: 1fr auto; gap: 2rem; align-items: start;">
|
<div class="flex flex-col lg:flex-row gap-8 lg:items-start">
|
||||||
<div>
|
|
||||||
|
<div class="flex-1">
|
||||||
{theme.data.priorite === 'critique' && (
|
{theme.data.priorite === 'critique' && (
|
||||||
<span style="background: #ef4444; color: white; font-size: 0.65rem; font-weight: 700; padding: 0.2rem 0.625rem; border-radius: 0.25rem; letter-spacing: 0.06em; margin-bottom: 0.875rem; display: inline-block;">PRIORITÉ CRITIQUE</span>
|
<span class="inline-block badge bg-red-500 text-white mb-4">PRIORITÉ CRITIQUE</span>
|
||||||
)}
|
)}
|
||||||
{theme.data.priorite === 'haute' && (
|
{theme.data.priorite === 'haute' && (
|
||||||
<span style="background: #d97706; color: white; font-size: 0.65rem; font-weight: 700; padding: 0.2rem 0.625rem; border-radius: 0.25rem; letter-spacing: 0.06em; margin-bottom: 0.875rem; display: inline-block;">PRIORITÉ HAUTE</span>
|
<span class="inline-block badge bg-amber-500 text-white mb-4">PRIORITÉ HAUTE</span>
|
||||||
)}
|
)}
|
||||||
<h1 style="font-family: 'Playfair Display', serif; font-size: 2.5rem; font-weight: 700; margin: 0 0 0.875rem; line-height: 1.15;">{theme.data.title}</h1>
|
<h1 class="font-serif text-4xl font-black mb-3 leading-tight">{theme.data.title}</h1>
|
||||||
<p style="color: #cbd5e1; font-size: 1.05rem; line-height: 1.7; max-width: 680px; margin: 0 0 1.75rem;">{theme.data.description}</p>
|
<p class="text-blue-200 text-base leading-relaxed max-w-2xl mb-6">{theme.data.description}</p>
|
||||||
|
|
||||||
<!-- Métriques header -->
|
<!-- KPIs header -->
|
||||||
<div style="display: flex; gap: 0.875rem; flex-wrap: wrap;">
|
<div class="flex flex-wrap gap-3">
|
||||||
{theme.data.kpi_valeur && (
|
{theme.data.kpi_valeur && (
|
||||||
<div style="background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.15); padding: 0.75rem 1.25rem; border-radius: 0.5rem; min-width: 130px;">
|
<div class="bg-white/10 border border-white/15 rounded-xl px-4 py-3 min-w-[130px]">
|
||||||
<div style={`font-size: 1.5rem; font-weight: 700; display: flex; align-items: baseline; gap: 0.3rem; color: white;`}>
|
<div class="text-2xl font-bold font-mono flex items-baseline gap-1">
|
||||||
{theme.data.kpi_valeur}
|
{theme.data.kpi_valeur}
|
||||||
<span style={`font-size: 0.9rem; color: ${tendanceCouleur(theme.data.kpi_tendance)};`}>{tendanceIcon(theme.data.kpi_tendance)}</span>
|
<span class="text-sm" style={`color: ${TENDANCE_COLOR[theme.data.kpi_tendance ?? 'stable']}`}>
|
||||||
|
{TENDANCE_ICON[theme.data.kpi_tendance ?? 'stable']}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size: 0.72rem; color: #93c5fd; margin-top: 0.2rem;">{theme.data.kpi_label}</div>
|
<div class="text-xs text-blue-300 mt-0.5">{theme.data.kpi_label}</div>
|
||||||
{theme.data.kpi_contexte && <div style="font-size: 0.65rem; color: #64748b; margin-top: 0.15rem;">{theme.data.kpi_contexte}</div>}
|
{theme.data.kpi_contexte && <div class="text-xs text-gray-500 mt-0.5">{theme.data.kpi_contexte}</div>}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{theme.data.budget_etat_milliards && (
|
{theme.data.budget_etat_milliards && (
|
||||||
<div style="background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.15); padding: 0.75rem 1.25rem; border-radius: 0.5rem;">
|
<div class="bg-white/10 border border-white/15 rounded-xl px-4 py-3">
|
||||||
<div style="font-size: 1.5rem; font-weight: 700; color: white;">{theme.data.budget_etat_milliards} Md€</div>
|
<div class="text-2xl font-bold">{theme.data.budget_etat_milliards} Md€</div>
|
||||||
<div style="font-size: 0.72rem; color: #93c5fd; margin-top: 0.2rem;">Budget État 2025</div>
|
<div class="text-xs text-blue-300 mt-0.5">Budget État 2025</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{theme.data.opinion_pct && (
|
{theme.data.opinion_pct && (
|
||||||
<div style="background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.15); padding: 0.75rem 1.25rem; border-radius: 0.5rem;">
|
<div class="bg-white/10 border border-white/15 rounded-xl px-4 py-3">
|
||||||
<div style="font-size: 1.5rem; font-weight: 700; color: white;">{theme.data.opinion_pct}%</div>
|
<div class="text-2xl font-bold">{theme.data.opinion_pct}%</div>
|
||||||
<div style="font-size: 0.72rem; color: #93c5fd; margin-top: 0.2rem; max-width: 140px; line-height: 1.3;">{theme.data.opinion_question ?? 'Satisfaction'}</div>
|
<div class="text-xs text-blue-300 mt-0.5 max-w-[140px] leading-snug">
|
||||||
{theme.data.opinion_source && <div style="font-size: 0.65rem; color: #64748b; margin-top: 0.15rem;">{theme.data.opinion_source} · {theme.data.opinion_annee}</div>}
|
{theme.data.opinion_question ?? 'Satisfaction'}
|
||||||
|
</div>
|
||||||
|
{theme.data.opinion_source && (
|
||||||
|
<div class="text-xs text-gray-500 mt-0.5">{theme.data.opinion_source} · {theme.data.opinion_annee}</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div style="background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.15); padding: 0.75rem 1.25rem; border-radius: 0.5rem;">
|
<div class="bg-white/10 border border-white/15 rounded-xl px-4 py-3">
|
||||||
<div style="font-size: 1.5rem; font-weight: 700; color: white;">{propositions.length}</div>
|
<div class="text-2xl font-bold">{propositions.length}</div>
|
||||||
<div style="font-size: 0.72rem; color: #93c5fd; margin-top: 0.2rem;">Propositions</div>
|
<div class="text-xs text-blue-300 mt-0.5">Propositions</div>
|
||||||
</div>
|
</div>
|
||||||
{theme.data.score_sources && (
|
{theme.data.score_sources && (
|
||||||
<div style="background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.15); padding: 0.75rem 1.25rem; border-radius: 0.5rem;">
|
<div class="bg-white/10 border border-white/15 rounded-xl px-4 py-3">
|
||||||
<div style="font-size: 1.5rem; font-weight: 700; color: white;">{theme.data.score_sources}</div>
|
<div class="text-2xl font-bold">{theme.data.score_sources}</div>
|
||||||
<div style="font-size: 0.72rem; color: #93c5fd; margin-top: 0.2rem;">Sources vérifiées</div>
|
<div class="text-xs text-blue-300 mt-0.5">Sources vérifiées</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Meta droite -->
|
<!-- Meta droite -->
|
||||||
<div style="text-align: right; flex-shrink: 0;">
|
<div class="lg:text-right shrink-0 text-sm">
|
||||||
<div style="font-size: 0.7rem; color: #64748b;">Mis à jour</div>
|
<div class="text-gray-500 text-xs">Mis à jour</div>
|
||||||
<div style="font-size: 0.875rem; color: #93c5fd; margin-bottom: 1rem;">{theme.data.derniere_maj}</div>
|
<div class="text-blue-300 mb-4">{theme.data.derniere_maj}</div>
|
||||||
{theme.data.rang_eu && (
|
{theme.data.rang_eu && (
|
||||||
<div style="margin-bottom: 1rem;">
|
<div class="mb-4">
|
||||||
<div style="font-size: 0.7rem; color: #64748b;">Rang UE</div>
|
<div class="text-gray-500 text-xs">Rang UE</div>
|
||||||
<div style="font-size: 1.25rem; font-weight: 700; color: white;">{theme.data.rang_eu}<span style="font-size: 0.75rem; color: #64748b;">/{theme.data.nb_pays_compares ?? 27}</span></div>
|
<div class="text-xl font-bold">{theme.data.rang_eu}<span class="text-xs text-gray-500">/{theme.data.nb_pays_compares ?? 27}</span></div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
<div style="font-size: 0.7rem; color: #64748b; margin-bottom: 0.4rem;">Sources data</div>
|
<div class="text-gray-500 text-xs mb-1">Sources data</div>
|
||||||
{theme.data.sources_data.slice(0, 6).map(s => (
|
{theme.data.sources_data.slice(0, 5).map((s: string) => (
|
||||||
<div style="font-size: 0.72rem; color: #93c5fd; line-height: 1.6;">{s}</div>
|
<div class="text-xs text-blue-300 leading-relaxed">{s}</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -145,14 +144,13 @@ function tendanceIcon(t?: string) {
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Nav interne dynamique -->
|
<!-- Nav interne -->
|
||||||
<nav style="background: white; border-bottom: 2px solid #e5e7eb; padding: 0 1.5rem; position: sticky; top: 0; z-index: 40; box-shadow: 0 1px 3px rgba(0,0,0,0.06);">
|
<nav class="bg-white border-b-2 border-gray-200 sticky top-[57px] z-40 shadow-sm overflow-x-auto">
|
||||||
<div style="max-width: 1200px; margin: 0 auto; display: flex; gap: 0; overflow-x: auto; scrollbar-width: none;">
|
<div class="max-w-7xl mx-auto px-6 flex gap-0 scrollbar-hide">
|
||||||
{navSections.map(({ id, label }) => (
|
{navSections.map(({ id, label }: { id: string; label: string }) => (
|
||||||
<a
|
<a
|
||||||
href={`#${id}`}
|
href={`#${id}`}
|
||||||
style="padding: 0.875rem 1rem; font-size: 0.82rem; font-weight: 500; color: #4b5563; text-decoration: none; white-space: nowrap; border-bottom: 2px solid transparent; margin-bottom: -2px; transition: all 0.15s;"
|
class="px-4 py-3.5 text-xs font-semibold text-gray-500 hover:text-[#1e3a6e] whitespace-nowrap border-b-2 border-transparent hover:border-[#1e3a6e] -mb-0.5 transition-colors no-underline uppercase tracking-wide"
|
||||||
class="nav-anchor"
|
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -161,62 +159,60 @@ function tendanceIcon(t?: string) {
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Corps : article + sidebar -->
|
<!-- Corps : article + sidebar -->
|
||||||
<div style="max-width: 1200px; margin: 0 auto; padding: 2.5rem 1.5rem 4rem; display: grid; grid-template-columns: 1fr 300px; gap: 3rem; align-items: start;">
|
<div class="max-w-7xl mx-auto px-6 py-10 grid grid-cols-1 lg:grid-cols-[1fr_280px] gap-10 items-start">
|
||||||
|
|
||||||
<article>
|
<article class="prose-irpf min-w-0">
|
||||||
<div class="prose-irpf">
|
<Content />
|
||||||
<Content />
|
|
||||||
</div>
|
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<!-- Sidebar enrichie -->
|
<!-- Sidebar -->
|
||||||
<aside style="position: sticky; top: 60px; display: flex; flex-direction: column; gap: 1.25rem;">
|
<aside class="hidden lg:flex flex-col gap-5 sticky top-[108px]">
|
||||||
|
|
||||||
|
<!-- Table des matières -->
|
||||||
|
<div class="bg-[#f8fafc] border border-gray-200 rounded-xl p-5">
|
||||||
|
<div class="text-xs font-bold text-gray-400 uppercase tracking-wider mb-3">Contenu</div>
|
||||||
|
<nav class="flex flex-col gap-0.5">
|
||||||
|
{navSections.map(({ id, label }: { id: string; label: string }) => (
|
||||||
|
<a href={`#${id}`} class="text-sm text-gray-600 hover:text-[#1e3a6e] hover:bg-blue-50 px-2 py-1.5 rounded no-underline transition-colors">
|
||||||
|
{label}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Propositions -->
|
<!-- Propositions -->
|
||||||
{propositions.length > 0 && (
|
{propositions.length > 0 && (
|
||||||
<div style="background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 0.5rem; padding: 1.25rem;">
|
<div class="bg-[#f8fafc] border border-gray-200 rounded-xl p-5">
|
||||||
<div style="font-weight: 600; font-size: 0.82rem; color: #0f172a; margin-bottom: 0.875rem; text-transform: uppercase; letter-spacing: 0.04em;">
|
<div class="text-xs font-bold text-gray-400 uppercase tracking-wider mb-3">
|
||||||
Propositions · {propositions.length}
|
Propositions ({propositions.length})
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex; flex-direction: column; gap: 0.625rem;">
|
<div class="flex flex-col gap-2.5">
|
||||||
{propositions.slice(0, 6).map(p => (
|
{propositions.slice(0, 5).map(p => (
|
||||||
<div style="background: white; border: 1px solid #e5e7eb; border-radius: 0.375rem; padding: 0.75rem;">
|
<div class="bg-white border border-gray-200 rounded-lg p-3">
|
||||||
<div style="font-size: 0.78rem; font-weight: 500; color: #0f172a; margin-bottom: 0.4rem; line-height: 1.45;">{p.data.titre}</div>
|
<div class="text-xs font-medium text-[#0f172a] mb-2 leading-snug">{p.data.titre}</div>
|
||||||
<div style="display: flex; gap: 0.35rem; flex-wrap: wrap; align-items: center;">
|
<div class="flex gap-1.5 flex-wrap items-center">
|
||||||
<span style={`font-size: 0.62rem; padding: 0.15rem 0.4rem; border-radius: 0.2rem; font-weight: 600; ${orientationBadge(p.data.orientation)}`}>
|
<span class={`badge text-[10px] ${ORIENTATION_CLASS[p.data.orientation ?? 'neutre'] ?? ORIENTATION_CLASS.neutre}`}>
|
||||||
{p.data.source}
|
{p.data.source}
|
||||||
</span>
|
</span>
|
||||||
<span style="font-size: 0.62rem; color: #9ca3af;">{p.data.horizon} · {p.data.annee}</span>
|
<span class="text-[10px] text-gray-400">{p.data.horizon}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{propositions.length > 6 && (
|
{propositions.length > 5 && (
|
||||||
<a href="/propositions/" style="display: block; text-align: center; margin-top: 0.75rem; font-size: 0.78rem; color: #1e3a6e; text-decoration: none; font-weight: 500;">
|
<a href="/propositions/" class="block text-center mt-3 text-xs text-[#1e3a6e] font-medium hover:underline no-underline">
|
||||||
+ {propositions.length - 6} autres propositions →
|
+{propositions.length - 5} autres →
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<!-- Sections -->
|
|
||||||
<div style="background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 0.5rem; padding: 1.25rem;">
|
|
||||||
<div style="font-weight: 600; font-size: 0.82rem; color: #0f172a; margin-bottom: 0.875rem; text-transform: uppercase; letter-spacing: 0.04em;">Contenu</div>
|
|
||||||
<div style="display: flex; flex-direction: column; gap: 0.25rem;">
|
|
||||||
{navSections.map(({ id, label }) => (
|
|
||||||
<a href={`#${id}`} style="font-size: 0.8rem; color: #374151; text-decoration: none; padding: 0.3rem 0.5rem; border-radius: 0.25rem; display: block;" class="toc-link">
|
|
||||||
{label}
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Tags -->
|
<!-- Tags -->
|
||||||
<div style="background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 0.5rem; padding: 1.25rem;">
|
<div class="bg-[#f8fafc] border border-gray-200 rounded-xl p-5">
|
||||||
<div style="font-weight: 600; font-size: 0.82rem; color: #0f172a; margin-bottom: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em;">Mots-clés</div>
|
<div class="text-xs font-bold text-gray-400 uppercase tracking-wider mb-3">Mots-clés</div>
|
||||||
<div style="display: flex; flex-wrap: wrap; gap: 0.4rem;">
|
<div class="flex flex-wrap gap-1.5">
|
||||||
{theme.data.tags.map(tag => (
|
{theme.data.tags.map((tag: string) => (
|
||||||
<span style="background: white; border: 1px solid #e5e7eb; color: #374151; font-size: 0.72rem; padding: 0.2rem 0.5rem; border-radius: 0.2rem;">#{tag}</span>
|
<span class="badge badge-muted text-xs">#{tag}</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -227,115 +223,5 @@ function tendanceIcon(t?: string) {
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.nav-anchor:hover,
|
[id] { scroll-margin-top: 115px; }
|
||||||
.nav-anchor:focus { color: #1e3a6e !important; border-bottom-color: #1e3a6e !important; }
|
|
||||||
|
|
||||||
.toc-link:hover { background: #e5e7eb; color: #0f172a !important; }
|
|
||||||
|
|
||||||
/* Typographie du corps de fiche */
|
|
||||||
.prose-irpf h2 {
|
|
||||||
font-family: 'Playfair Display', serif;
|
|
||||||
font-size: 1.6rem;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #0f172a;
|
|
||||||
margin: 3rem 0 1rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
border-bottom: 2px solid #e5e7eb;
|
|
||||||
scroll-margin-top: 80px;
|
|
||||||
}
|
|
||||||
.prose-irpf h3 {
|
|
||||||
font-size: 1.1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #1e3a6e;
|
|
||||||
margin: 1.75rem 0 0.75rem;
|
|
||||||
scroll-margin-top: 80px;
|
|
||||||
}
|
|
||||||
.prose-irpf h4 {
|
|
||||||
font-size: 0.95rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #374151;
|
|
||||||
margin: 1.25rem 0 0.5rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.04em;
|
|
||||||
}
|
|
||||||
.prose-irpf p {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
line-height: 1.8;
|
|
||||||
color: #374151;
|
|
||||||
margin: 0.75rem 0;
|
|
||||||
}
|
|
||||||
.prose-irpf strong { color: #0f172a; }
|
|
||||||
.prose-irpf em { color: #4b5563; }
|
|
||||||
|
|
||||||
.prose-irpf table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin: 1.25rem 0;
|
|
||||||
font-size: 0.82rem;
|
|
||||||
}
|
|
||||||
.prose-irpf th {
|
|
||||||
background: #0f172a;
|
|
||||||
color: white;
|
|
||||||
padding: 0.6rem 0.875rem;
|
|
||||||
text-align: left;
|
|
||||||
font-size: 0.72rem;
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.04em;
|
|
||||||
}
|
|
||||||
.prose-irpf td {
|
|
||||||
padding: 0.6rem 0.875rem;
|
|
||||||
border-bottom: 1px solid #e5e7eb;
|
|
||||||
color: #374151;
|
|
||||||
vertical-align: top;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
.prose-irpf tr:nth-child(even) td { background: #f8fafc; }
|
|
||||||
.prose-irpf tr:hover td { background: #f0f4ff; }
|
|
||||||
|
|
||||||
.prose-irpf ul, .prose-irpf ol {
|
|
||||||
padding-left: 1.5rem;
|
|
||||||
margin: 0.75rem 0;
|
|
||||||
}
|
|
||||||
.prose-irpf li {
|
|
||||||
font-size: 0.88rem;
|
|
||||||
line-height: 1.75;
|
|
||||||
color: #374151;
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prose-irpf blockquote {
|
|
||||||
border-left: 4px solid #1e3a6e;
|
|
||||||
background: #f0f4ff;
|
|
||||||
padding: 1rem 1.25rem;
|
|
||||||
margin: 1.5rem 0;
|
|
||||||
border-radius: 0 0.375rem 0.375rem 0;
|
|
||||||
font-style: italic;
|
|
||||||
color: #374151;
|
|
||||||
}
|
|
||||||
.prose-irpf blockquote p { margin: 0; }
|
|
||||||
|
|
||||||
.prose-irpf a {
|
|
||||||
color: #1e3a6e;
|
|
||||||
text-decoration: underline;
|
|
||||||
text-decoration-color: #bfdbfe;
|
|
||||||
}
|
|
||||||
.prose-irpf a:hover { text-decoration-color: #1e3a6e; }
|
|
||||||
|
|
||||||
.prose-irpf code {
|
|
||||||
background: #f1f5f9;
|
|
||||||
padding: 0.1rem 0.35rem;
|
|
||||||
border-radius: 0.2rem;
|
|
||||||
font-size: 0.82rem;
|
|
||||||
color: #0f172a;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Couches visuelles : héritage du slot */
|
|
||||||
.layer-content p { font-size: 0.88rem; }
|
|
||||||
.layer-content ul { padding-left: 1.25rem; }
|
|
||||||
.layer-content li { font-size: 0.85rem; }
|
|
||||||
.layer-content table { font-size: 0.8rem; }
|
|
||||||
|
|
||||||
/* Scroll offset pour nav sticky */
|
|
||||||
[id] { scroll-margin-top: 80px; }
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -4,38 +4,49 @@ import { getCollection } from 'astro:content';
|
||||||
|
|
||||||
const themes = (await getCollection('themes')).sort((a, b) => a.data.ordre - b.data.ordre);
|
const themes = (await getCollection('themes')).sort((a, b) => a.data.ordre - b.data.ordre);
|
||||||
const critiques = themes.filter(t => t.data.priorite === 'critique');
|
const critiques = themes.filter(t => t.data.priorite === 'critique');
|
||||||
const autres = themes.filter(t => t.data.priorite !== 'critique');
|
const autres = themes.filter(t => t.data.priorite !== 'critique');
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title="Thèmes — IRPF" description="Les 14 grandes thématiques des politiques publiques françaises analysées par l'IRPF.">
|
<BaseLayout title="Thèmes — IRPF" description="Les 14 grandes thématiques des politiques publiques françaises analysées par l'IRPF.">
|
||||||
|
|
||||||
<section style="background: #0f172a; color: white; padding: 3rem 1.5rem;">
|
<!-- Header -->
|
||||||
<div style="max-width: 1200px; margin: 0 auto;">
|
<section class="bg-[#0f172a] text-white px-6 py-14">
|
||||||
<div style="font-size: 0.75rem; letter-spacing: 0.1em; color: #93c5fd; margin-bottom: 0.75rem;">IRPF</div>
|
<div class="max-w-7xl mx-auto">
|
||||||
<h1 style="font-family: 'Playfair Display', serif; font-size: 2.5rem; margin-bottom: 1rem;">Les 14 thèmes</h1>
|
<nav class="text-xs text-gray-500 mb-6 flex items-center gap-1.5">
|
||||||
<p style="color: #cbd5e1; max-width: 600px; line-height: 1.7;">Pour chaque thème : diagnostic sourcé, cartographie des propositions par acteur, scénarios chiffrés, analyse originale.</p>
|
<a href="/" class="hover:text-gray-300 no-underline">Accueil</a>
|
||||||
|
<span>›</span>
|
||||||
|
<span class="text-gray-400">Thèmes</span>
|
||||||
|
</nav>
|
||||||
|
<div class="text-xs text-blue-400 font-semibold uppercase tracking-wider mb-3">IRPF</div>
|
||||||
|
<h1 class="font-serif text-4xl font-black mb-3">Les 14 thèmes</h1>
|
||||||
|
<p class="text-gray-400 max-w-2xl leading-relaxed text-sm">
|
||||||
|
Pour chaque thème : diagnostic sourcé, cartographie des propositions par acteur, scénarios chiffrés, analyse originale.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section style="padding: 3rem 1.5rem; max-width: 1200px; margin: 0 auto;">
|
<div class="max-w-7xl mx-auto px-6 py-12">
|
||||||
|
|
||||||
{critiques.length > 0 && (
|
{critiques.length > 0 && (
|
||||||
<div style="margin-bottom: 3rem;">
|
<div class="mb-12">
|
||||||
<div style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1.5rem;">
|
<div class="flex items-center gap-3 mb-6">
|
||||||
<span style="background: #fee2e2; color: #991b1b; font-size: 0.7rem; font-weight: 700; padding: 0.25rem 0.75rem; border-radius: 0.25rem; letter-spacing: 0.05em;">PRIORITÉ CRITIQUE</span>
|
<span class="badge badge-critique">PRIORITÉ CRITIQUE</span>
|
||||||
<div style="flex: 1; height: 1px; background: #fca5a5;"></div>
|
<div class="flex-1 h-px bg-red-100"></div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;">
|
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||||
{critiques.map(theme => (
|
{critiques.map(theme => (
|
||||||
<a href={`/themes/${theme.data.slug}/`} style="background: white; border: 2px solid #fca5a5; border-radius: 0.5rem; padding: 1.5rem; text-decoration: none; display: block;" class="theme-card">
|
<a href={`/themes/${theme.data.slug}/`} class="card p-6 no-underline block group border-red-100 hover:border-red-300">
|
||||||
<div style="font-weight: 700; color: #0f172a; font-size: 1.05rem; margin-bottom: 0.5rem;">{theme.data.title}</div>
|
<div class="flex justify-between items-start gap-2 mb-3">
|
||||||
<p style="font-size: 0.85rem; color: #6b7280; line-height: 1.5; margin-bottom: 1rem;">{theme.data.description}</p>
|
<h2 class="font-semibold text-[#0f172a] leading-snug group-hover:text-[#1e3a6e] transition-colors">{theme.data.title}</h2>
|
||||||
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center;">
|
<span class="badge badge-critique shrink-0">CRITIQUE</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm text-gray-500 leading-relaxed mb-4">{theme.data.description}</p>
|
||||||
|
<div class="flex gap-2 flex-wrap">
|
||||||
{theme.data.budget_etat_milliards && (
|
{theme.data.budget_etat_milliards && (
|
||||||
<span style="background: #eff6ff; color: #1d4ed8; font-size: 0.7rem; padding: 0.2rem 0.5rem; border-radius: 0.25rem; font-weight: 500;">{theme.data.budget_etat_milliards} Md€/an</span>
|
<span class="badge badge-blue">{theme.data.budget_etat_milliards} Md€/an</span>
|
||||||
)}
|
)}
|
||||||
{theme.data.tags.slice(0, 2).map(tag => (
|
{theme.data.tags.slice(0, 2).map((tag: string) => (
|
||||||
<span style="background: #f3f4f6; color: #6b7280; font-size: 0.7rem; padding: 0.2rem 0.5rem; border-radius: 0.25rem;">#{tag}</span>
|
<span class="badge badge-muted">#{tag}</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -46,19 +57,28 @@ const autres = themes.filter(t => t.data.priorite !== 'critique');
|
||||||
|
|
||||||
{autres.length > 0 && (
|
{autres.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<div style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1.5rem;">
|
<div class="flex items-center gap-3 mb-6">
|
||||||
<span style="color: #374151; font-size: 0.8rem; font-weight: 600; letter-spacing: 0.05em;">TOUS LES THÈMES</span>
|
<span class="text-xs font-semibold text-gray-500 uppercase tracking-wider">Tous les thèmes</span>
|
||||||
<div style="flex: 1; height: 1px; background: #e5e7eb;"></div>
|
<div class="flex-1 h-px bg-gray-200"></div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;">
|
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||||
{autres.map(theme => (
|
{autres.map(theme => (
|
||||||
<a href={`/themes/${theme.data.slug}/`} style="background: white; border: 1px solid #e5e7eb; border-radius: 0.5rem; padding: 1.5rem; text-decoration: none; display: block;" class="theme-card">
|
<a href={`/themes/${theme.data.slug}/`} class="card p-6 no-underline block group">
|
||||||
<div style="font-weight: 600; color: #0f172a; margin-bottom: 0.5rem;">{theme.data.title}</div>
|
<div class="flex justify-between items-start gap-2 mb-2">
|
||||||
<p style="font-size: 0.85rem; color: #6b7280; line-height: 1.5; margin-bottom: 1rem;">{theme.data.description}</p>
|
<h2 class="font-semibold text-[#0f172a] leading-snug group-hover:text-[#1e3a6e] transition-colors">{theme.data.title}</h2>
|
||||||
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
|
{theme.data.priorite === 'haute' && <span class="badge badge-haute shrink-0">PRIORITÉ</span>}
|
||||||
{theme.data.tags.slice(0, 2).map(tag => (
|
</div>
|
||||||
<span style="background: #f3f4f6; color: #6b7280; font-size: 0.7rem; padding: 0.2rem 0.5rem; border-radius: 0.25rem;">#{tag}</span>
|
<p class="text-sm text-gray-500 leading-relaxed mb-4">{theme.data.description}</p>
|
||||||
))}
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex gap-2 flex-wrap">
|
||||||
|
{theme.data.budget_etat_milliards && (
|
||||||
|
<span class="badge badge-blue">{theme.data.budget_etat_milliards} Md€/an</span>
|
||||||
|
)}
|
||||||
|
{theme.data.tags.slice(0, 2).map((tag: string) => (
|
||||||
|
<span class="badge badge-muted">#{tag}</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<span class="text-xs text-[#1e3a6e] font-medium opacity-0 group-hover:opacity-100 transition-opacity">Lire →</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
))}
|
))}
|
||||||
|
|
@ -66,13 +86,22 @@ const autres = themes.filter(t => t.data.priorite !== 'critique');
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</section>
|
<!-- À venir -->
|
||||||
|
<div class="mt-12">
|
||||||
|
<div class="flex items-center gap-3 mb-6">
|
||||||
|
<span class="badge badge-muted">EN PRÉPARATION</span>
|
||||||
|
<div class="flex-1 h-px bg-gray-200"></div>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3">
|
||||||
|
{['Emploi & Travail', 'Retraites', 'Logement', 'Éducation', 'Justice & Sécurité',
|
||||||
|
'Énergie & Environnement', 'Agriculture', 'Numérique', 'Cohésion sociale',
|
||||||
|
'Démocratie', 'Europe', 'Immigration'].map(t => (
|
||||||
|
<div class="rounded-lg border border-dashed border-gray-300 px-4 py-3 text-sm text-gray-400 bg-white select-none">
|
||||||
|
{t}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
<style>
|
|
||||||
.theme-card:hover {
|
|
||||||
border-color: #1e3a6e !important;
|
|
||||||
box-shadow: 0 2px 8px rgba(30,58,110,0.08);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -1,72 +1,109 @@
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--color-bleu-nuit: #0f172a;
|
--color-nuit: #0f172a;
|
||||||
--color-bleu-republic: #1e3a6e;
|
--color-republic: #1e3a6e;
|
||||||
--color-bleu-accent: #2563eb;
|
--color-accent: #2563eb;
|
||||||
--color-gris-ardoise: #374151;
|
--color-rouge: #c0392b;
|
||||||
--color-gris-clair: #f8fafc;
|
--color-surface: #f8fafc;
|
||||||
--color-rouge-republique: #c0392b;
|
--color-border: #e2e8f0;
|
||||||
|
--color-muted: #64748b;
|
||||||
|
|
||||||
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
||||||
--font-serif: "Playfair Display", ui-serif, Georgia, serif;
|
--font-serif: "Playfair Display", ui-serif, Georgia, serif;
|
||||||
--font-mono: ui-monospace, monospace;
|
--font-mono: ui-monospace, "Fira Code", monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
html { scroll-behavior: smooth; }
|
html { scroll-behavior: smooth; }
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: white;
|
|
||||||
color: #111827;
|
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
|
color: #1e293b;
|
||||||
|
background: white;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Typographie éditoriale */
|
/* ── Prose ─────────────────────────────────────────────── */
|
||||||
.prose-irpf h2 {
|
.prose-irpf h2 {
|
||||||
font-size: 1.5rem;
|
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
|
font-size: 1.5rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--color-bleu-nuit);
|
color: var(--color-nuit);
|
||||||
border-bottom: 1px solid #e5e7eb;
|
border-bottom: 1px solid var(--color-border);
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
margin-top: 2.5rem;
|
margin-top: 2.5rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose-irpf h3 {
|
.prose-irpf h3 {
|
||||||
font-size: 1.25rem;
|
font-size: 1.125rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--color-bleu-republic);
|
color: var(--color-republic);
|
||||||
margin-top: 2rem;
|
margin-top: 1.75rem;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.625rem;
|
||||||
}
|
}
|
||||||
|
.prose-irpf p { color: #374151; line-height: 1.8; margin-bottom: 1rem; }
|
||||||
.prose-irpf p {
|
.prose-irpf a { color: var(--color-accent); text-decoration: underline; }
|
||||||
color: #374151;
|
.prose-irpf strong { font-weight: 600; color: #111827; }
|
||||||
line-height: 1.75;
|
.prose-irpf ul { list-style: disc; padding-left: 1.5rem; color: #374151; margin-bottom: 1rem; }
|
||||||
margin-bottom: 1rem;
|
.prose-irpf ul li { margin-bottom: 0.375rem; line-height: 1.7; }
|
||||||
}
|
.prose-irpf blockquote {
|
||||||
|
border-left: 3px solid var(--color-accent);
|
||||||
.prose-irpf a { color: var(--color-bleu-accent); text-decoration: underline; }
|
padding: 0.75rem 1rem;
|
||||||
.prose-irpf blockquote {
|
|
||||||
border-left: 4px solid var(--color-bleu-accent);
|
|
||||||
padding-left: 1rem;
|
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
color: #6b7280;
|
color: var(--color-muted);
|
||||||
|
background: var(--color-surface);
|
||||||
margin: 1.5rem 0;
|
margin: 1.5rem 0;
|
||||||
|
border-radius: 0 0.25rem 0.25rem 0;
|
||||||
}
|
}
|
||||||
.prose-irpf ul { list-style: disc inside; color: #374151; margin-bottom: 1rem; }
|
.prose-irpf table { width: 100%; border-collapse: collapse; font-size: 0.875rem; margin: 1.5rem 0; }
|
||||||
.prose-irpf strong { font-weight: 600; color: #111827; }
|
.prose-irpf th { background: var(--color-nuit); color: white; padding: 0.625rem 0.875rem; text-align: left; font-weight: 600; font-size: 0.8rem; letter-spacing: 0.03em; }
|
||||||
|
.prose-irpf td { padding: 0.625rem 0.875rem; border-bottom: 1px solid var(--color-border); color: #374151; }
|
||||||
|
.prose-irpf tr:last-child td { border-bottom: none; }
|
||||||
|
.prose-irpf tr:nth-child(even) td { background: var(--color-surface); }
|
||||||
|
|
||||||
/* KPI cards */
|
/* ── KPI cards ─────────────────────────────────────────── */
|
||||||
.kpi-card {
|
.kpi-card {
|
||||||
background: white;
|
background: white;
|
||||||
border: 1px solid #e5e7eb;
|
border: 1px solid var(--color-border);
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.75rem;
|
||||||
padding: 1rem;
|
padding: 1.25rem 1.5rem;
|
||||||
box-shadow: 0 1px 3px rgba(0,0,0,0.08);
|
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
|
||||||
|
transition: box-shadow 0.15s;
|
||||||
}
|
}
|
||||||
.kpi-value { font-size: 1.875rem; font-weight: 700; color: var(--color-bleu-nuit); font-family: var(--font-mono); }
|
.kpi-card:hover { box-shadow: 0 4px 16px rgba(30,58,110,0.1); }
|
||||||
.kpi-label { font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem; }
|
.kpi-value { font-size: 1.875rem; font-weight: 700; color: var(--color-nuit); font-family: var(--font-mono); line-height: 1; }
|
||||||
.kpi-source { font-size: 0.75rem; color: #9ca3af; margin-top: 0.5rem; }
|
.kpi-label { font-size: 0.8rem; color: var(--color-muted); margin-top: 0.375rem; line-height: 1.4; }
|
||||||
|
.kpi-source { font-size: 0.7rem; color: #94a3b8; margin-top: 0.5rem; }
|
||||||
|
|
||||||
|
/* ── Badges ────────────────────────────────────────────── */
|
||||||
|
.badge { display: inline-flex; align-items: center; font-size: 0.7rem; font-weight: 600; padding: 0.2rem 0.6rem; border-radius: 0.25rem; letter-spacing: 0.04em; }
|
||||||
|
.badge-critique { background: #fee2e2; color: #991b1b; }
|
||||||
|
.badge-haute { background: #fef3c7; color: #92400e; }
|
||||||
|
.badge-blue { background: #eff6ff; color: #1d4ed8; }
|
||||||
|
.badge-dark { background: var(--color-nuit); color: white; }
|
||||||
|
.badge-muted { background: #f1f5f9; color: #475569; }
|
||||||
|
|
||||||
|
/* ── Cards ─────────────────────────────────────────────── */
|
||||||
|
.card {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
transition: border-color 0.15s, box-shadow 0.15s;
|
||||||
|
}
|
||||||
|
.card:hover {
|
||||||
|
border-color: #93c5fd;
|
||||||
|
box-shadow: 0 4px 20px rgba(30,58,110,0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Section label ─────────────────────────────────────── */
|
||||||
|
.section-label {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-republic);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Mobile menu ───────────────────────────────────────── */
|
||||||
|
#mobile-menu { display: none; }
|
||||||
|
#mobile-menu.open { display: flex; }
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue