                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        'presets' => [
            'hero' => [
                'width' => 1920,
                'height' => 1080,
                'quality' => 88,
                'format' => 'webp',
                'sharpen' => 15
            ],
            'card' => [
                'width' => 800,
                'height' => 1000,
                'quality' => 82,
                'format' => 'webp',
                'sharpen' => 10
            ],
            'thumb' => [
                'width' => 400,
                'height' => 400,
                'quality' => 75,
                'format' => 'webp'
            ]
        ]
    ],

    // ===================================
    // CACHE STATIQUE ULTRARAPIDE
    // ===================================
    'cache' => [
        'pages' => [
            'active' => true,
            'type' => 'static',
            // Stocke aussi le status + les headers (CSP, sécurité…) dans les
            // fichiers de cache ; ils sont rejoués par le loader staticache
            // en tête d'index.php. Après activation, vider site/cache/*/pages
            // (les anciens fichiers ne contiennent pas de bloc headers).
            'headers' => true,
            'ignore' => function ($page) {
                return in_array($page->template()->name(), [
                    'contact',
                    'search',
                    'devis',
                    'map-reports'
                ]);
            }
        ],
        'media' => [
            'active' => true,
            'type' => 'file',
            'expires' => 60 * 24 * 365
        ]
    ],

    // ===================================
    // OPTIMISATIONS DIVERSES
    // ===================================
    'smartypants' => false,

    'markdown' => [
        'extra' => false,
        'breaks' => true
    ],

    'panel' => [
        'css' => 'panel.css',
        'js' => 'panel.js'
    ],

    // Routes avec headers de sécurité et performance
    'routes' => [
        // API JSON pour la soumission du formulaire de devis (consommée en AJAX)
        [
            'pattern' => ['devis-submit', '(:any)/devis-submit'],
            'method'  => 'POST',
            'action'  => function () {
                require_once __DIR__ . '/../snippets/forms/devis-handler.php';
                $result = handleDevisForm(kirby());
                return new Kirby\Http\Response(
                    json_encode([
                        'success' => $result['success'],
                        'errors'  => $result['errors'],
                    ], JSON_UNESCAPED_UNICODE),
                    'application/json',
                    $result['success'] ? 200 : 400
                );
            }
        ],
        // Jeton CSRF pour le formulaire de devis (récupéré en AJAX par
        // devis-wizard.js à la soumission). Ne PAS le générer au rendu des
        // pages : cela créerait une session et rendrait les pages non
        // cachables par le cache statique.
        [
            'pattern' => ['devis-csrf', '(:any)/devis-csrf'],
            'method'  => 'GET',
            'action'  => function () {
                return new Kirby\Http\Response(
                    json_encode(['csrf' => csrf()]),
                    'application/json',
                    200,
                    ['Cache-Control' => 'no-store, private']
                );
            }
        ],
        // Carte de l'enseignement : proposition d'un lieu (AJAX). Crée une fiche
        // en brouillon à modérer dans le Panel.
        [
            'pattern' => ['carte/proposer', '(:any)/carte/proposer'],
            'method'  => 'POST',
            'action'  => function () {
                require_once __DIR__ . '/../snippets/forms/map-propose-handler.php';
                $result = handleMapProposeForm(kirby());
                return new Kirby\Http\Response(
                    json_encode(['success' => $result['success'], 'errors' => $result['errors']], JSON_UNESCAPED_UNICODE),
                    'application/json',
                    $result['success'] ? 200 : 400
                );
            }
        ],
        // Carte de l'enseignement : signalement d'erreur / demande de suppression (AJAX).
        [
            'pattern' => ['carte/signaler', '(:any)/carte/signaler'],
            'method'  => 'POST',
            'action'  => function () {
                require_once __DIR__ . '/../snippets/forms/map-report-handler.php';
                $result = handleMapReportForm(kirby());
                return new Kirby\Http\Response(
                    json_encode(['success' => $result['success'], 'errors' => $result['errors']], JSON_UNESCAPED_UNICODE),
                    'application/json',
                    $result['success'] ? 200 : 400
                );
            }
        ],
        // Jeton CSRF pour les formulaires de la carte (récupéré en AJAX). Ne PAS
        // le générer au rendu des pages (créerait une session, casserait le cache).
        [
            'pattern' => ['carte/form-csrf', '(:any)/carte/form-csrf'],
            'method'  => 'GET',
            'action'  => function () {
                return new Kirby\Http\Response(
                    json_encode(['csrf' => csrf()]),
                    'application/json',
                    200,
                    ['Cache-Control' => 'no-store, private']
                );
            }
        ],
        // Flux produits Google Shopping / Merchant Center
        [
            'pattern' => 'feeds/products.xml',
            'method'  => 'GET',
            'action'  => function () {
                $xml = snippet('seo/google-shopping-feed', [], true);
                return new Kirby\Http\Response(
                    trim($xml),
                    'application/xml',
                    200,
                    ['Cache-Control' => 'public, max-age=3600']
                );
            }
        ],
        // API JSON pour la carte des enseignants (consommé par teaching-map.js et nearby-teachers-map.js)
        [
            'pattern' => 'data/teaching-map.json',
            'action' => function () {
                if (!kirby()->site()->feature_teaching_map()->toBool()) {
                    return new Kirby\Http\Response(
                        json_encode(['center' => null, 'points' => []]),
                        'application/json',
                        404
                    );
                }
                $mapPage = kirby()->site()->find('carte-enseignement-accordeon');
                if (!$mapPage) {
                    return new Kirby\Http\Response(
                        json_encode(['center' => null, 'points' => []]),
                        'application/json',
                        404
                    );
                }

                // Centre de la carte
                $defaultLocation = $mapPage->defaultlocation()->toStructure()->first();
                $center = [
                    'latitude'  => $defaultLocation ? (float) $defaultLocation->latitude()->value() : 46.603354,
                    'longitude' => $defaultLocation ? (float) $defaultLocation->longitude()->value() : 1.888334,
                    'zoom'      => $defaultLocation ? (int) $defaultLocation->zoom()->value() : 6,
                ];

                // Points (tous les enfants publiés — unlisted inclus).
                // Modèle : chaque STAGE est un point indépendant (à son propre lieu).
                // Le point de la STRUCTURE n'est émis que si elle n'est pas « stagesOnly ».
                // On retire les champs vides (null / '' / []) de chaque point pour
                // alléger le JSON (~16 % des valeurs). Le front lit déjà tout de
                // façon défensive (point.city || '' …), donc l'absence est sûre.
                $clean = fn (array $p) => array_filter(
                    $p,
                    fn ($v) => !($v === null || $v === '' || (is_array($v) && count($v) === 0))
                );
                // Les champs « lourds » (surtout la description) ne servent qu'au
                // panneau d'une fiche ouverte. On ne les envoie qu'à l'admin (qui
                // édite en place et a besoin des valeurs). Le public reçoit un
                // payload allégé (~40 % plus léger) et récupère le détail d'une
                // fiche à son ouverture via la route data/teaching-map-point.
                $isAdmin = kirby()->user() !== null;
                $HEAVY = ['description', 'website', 'email', 'phone', 'levels'];
                $emit = function (array $p) use ($clean, $isAdmin, $HEAVY) {
                    if (!$isAdmin) {
                        foreach ($HEAVY as $h) {
                            unset($p[$h]);
                        }
                    }
                    return $clean($p);
                };
                $points = [];
                foreach ($mapPage->children() as $child) {
                    $loc       = $child->location()->toStructure()->first();
                    $structLat = $loc ? $loc->latitude()->value() : null;
                    $structLng = $loc ? $loc->longitude()->value() : null;
                    $stagesOnly = $child->stagesonly()->toBool();

                    // 1) Point de la structure (école/prof/conservatoire/asso)
                    if (!$stagesOnly && $structLat && $structLng) {
                        $points[] = $emit([
                            'id'               => $child->slug(),
                            'title'            => $child->title()->value(),
                            'type'             => $child->pointtype()->value(),
                            'offersStages'     => $child->offersstages()->toBool(),
                            'city'             => $child->city()->value(),
                            'department'       => $child->department()->value(),
                            'departmentNumber' => $child->departmentnumber()->value(),
                            'description'      => $child->description()->value(),
                            'website'          => $child->website()->value(),
                            'email'            => $child->email()->value(),
                            'phone'            => $child->phone()->value(),
                            'instruments'      => $child->instruments()->split(','),
                            'levels'           => $child->levels()->split(','),
                            'location'         => [
                                'latitude'  => (float) $structLat,
                                'longitude' => (float) $structLng,
                            ],
                        ]);
                    }

                    // 2) Un point par STAGE (à son lieu propre ; défaut = lieu de la structure)
                    if ($child->offersstages()->toBool() && $child->stages()->isNotEmpty()) {
                        $i = 0;
                        foreach ($child->stages()->toStructure() as $stage) {
                            $sLat = $stage->latitude()->value() ?: $structLat;
                            $sLng = $stage->longitude()->value() ?: $structLng;
                            if (!$sLat || !$sLng) continue; // pas de lieu → pas de point
                            $i++;
                            $points[] = $emit([
                                'id'               => $child->slug() . '-stage-' . $i,
                                'title'            => $stage->title()->value() ?: $child->title()->value(),
                                'type'             => 'stage',
                                'organizer'        => $child->title()->value(),
                                'city'             => $child->city()->value(),
                                'department'       => $child->department()->value(),
                                'departmentNumber' => $child->departmentnumber()->value(),
                                'description'      => $stage->note()->value() ?: $child->description()->value(),
                                'website'          => $child->website()->value(),
                                'email'            => $child->email()->value(),
                                'phone'            => $child->phone()->value(),
                                'instruments'      => $child->instr<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/sitemap.xsl"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" seo-version="1.0.0-rc.1">
  <url>
    <lastmod>2025-12-10T19:33:39+01:00</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
    <loc>https://paulpichot-accordeons.fr/accueil</loc>
    <xhtml:link hreflang="de" href="https://paulpichot-accordeons.fr/de/accueil"/>
    <xhtml:link hreflang="en" href="https://paulpichot-accordeons.fr/en/accueil"/>
    <xhtml:link hreflang="es" href="https://paulpichot-accordeons.fr/es/accueil"/>
    <xhtml:link hreflang="x-default" href="https://paulpichot-accordeons.fr/accueil"/>
    <xhtml:link hreflang="it" href="https://paulpichot-accordeons.fr/it/accueil"/>
  </url>
  <url>
    <lastmod>2025-12-10T19:33:36+01:00</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
    <loc>https://paulpichot-accordeons.fr/carte</loc>
    <xhtml:link hreflang="de" href="https://paulpichot-accordeons.fr/de/carte"/>
    <xhtml:link hreflang="en" href="https://paulpichot-accordeons.fr/en/carte"/>
    <xhtml:link hreflang="es" href="https://paulpichot-accordeons.fr/es/carte"/>
    <xhtml:link hreflang="x-default" href="https://paulpichot-accordeons.fr/carte"/>
    <xhtml:link hreflang="it" href="https://paulpichot-accordeons.fr/it/carte"/>
  </url>
  <url>
    <lastmod>2025-12-10T19:33:40+01:00</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
    <loc>https://paulpichot-accordeons.fr/accordeons-occasion-correze-limousin</loc>
    <xhtml:link hreflang="de" href="https://paulpichot-accordeons.fr/de/accordeons-occasion-correze-limousin"/>
    <xhtml:link hreflang="en" href="https://paulpichot-accordeons.fr/en/accordeons-occasion-correze-limousin"/>
    <xhtml:link hreflang="es" href="https://paulpichot-accordeons.fr/es/accordeons-occasion-correze-limousin"/>
    <xhtml:link hreflang="x-default" href="https://paulpichot-accordeons.fr/accordeons-occasion-correze-limousin"/>
    <xhtml:link hreflang="it" href="https://paulpichot-accordeons.fr/it/accordeons-occasion-correze-limousin"/>
  </url>
  <url>
    <lastmod>2026-08-03T19:27:01+02:00</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
    <loc>https://paulpichot-accordeons.fr/proposer-un-lieu</loc>
    <xhtml:link hreflang="de" href="https://paulpichot-accordeons.fr/de/proposer-un-lieu"/>
    <xhtml:link hreflang="en" href="https://paulpichot-accordeons.fr/en/proposer-un-lieu"/>
    <xhtml:link hreflang="es" href="https://paulpichot-accordeons.fr/es/proposer-un-lieu"/>
    <xhtml:link hreflang="x-default" href="https://paulpichot-accordeons.fr/proposer-un-lieu"/>
    <xhtml:link hreflang="it" href="https://paulpichot-accordeons.fr/it/proposer-un-lieu"/>
  </url>
  <url>
    <lastmod>2025-12-10T19:33:36+01:00</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
    <loc>https://paulpichot-accordeons.fr/success</loc>
    <xhtml:link hreflang="de" href="https://paulpichot-accordeons.fr/de/success"/>
    <xhtml:link hreflang="en" href="https://paulpichot-accordeons.fr/en/success"/>
    <xhtml:link hreflang="es" href="https://paulpichot-accordeons.fr/es/success"/>
    <xhtml:link hreflang="x-default" href="https://paulpichot-accordeons.fr/success"/>
    <xhtml:link hreflang="it" href="https://paulpichot-accordeons.fr/it/success"/>
  </url>
</urlset>
