@php // Sprache: ein englischer Bereichs-Host (knowledge/tools/beta…) ist IMMER Englisch // (dort kein /en-Segment); sonst trägt das erste URL-Segment die Sprache. Standard = de. $routeLocale = (new \App\Support\SubdomainRegistry())->currentLocale(); $localizedPathSegments = request()->segments(); if (in_array($localizedPathSegments[0] ?? null, ['de', 'en'], true)) { array_shift($localizedPathSegments); } $localizedPath = implode('/', $localizedPathSegments); // Canonical/hreflang an den BEREICH binden: jede Subdomain ist ihr eigener // Canonical-Host (z. B. https://android.kotsch.tech), NICHT www.kotsch.tech/. // Dadurch „verweist" die Subdomain nicht mehr auf den Apex-Pfad. Der alte Pfad // (kotsch.tech/android) bleibt voll erreichbar, kanonisiert aber auf die Subdomain. $canonicalRegistry = new \App\Support\SubdomainRegistry(); $canonicalArea = $canonicalRegistry->currentArea(); $canonicalAreaIsHub = ($canonicalArea['key'] ?? 'hub') === 'hub'; if (! $canonicalAreaIsHub) { // Beim Zugriff über den Apex-Pfad (kotsch.tech/android/...) steckt der Basis- // Prefix noch im Pfad — entfernen, damit er nicht doppelt erscheint. Auf der // Subdomain ist der Pfad bereits bereichsrelativ → No-op. $canonicalAreaBase = trim($canonicalArea['base'] ?? '', '/'); if ($canonicalAreaBase !== '') { if ($localizedPath === $canonicalAreaBase) { $localizedPath = ''; } elseif (str_starts_with($localizedPath, $canonicalAreaBase . '/')) { $localizedPath = substr($localizedPath, strlen($canonicalAreaBase) + 1); } } } $localizedSuffix = $localizedPath === '' ? '' : '/' . $localizedPath; $publicBaseUrl = $canonicalAreaIsHub ? 'https://www.kotsch.tech' : 'https://' . ($canonicalArea['host'] ?? 'www.kotsch.tech'); // hreflang/Canonical konsistent halten: Die deutsche (Standard-)Version nutzt die // URL OHNE Locale-Prefix, Englisch nutzt /en. Jede Sprachversion ist SELF-CANONICAL // und die hreflang-de-Alternative == die deutsche Canonical-URL. Sonst kanonisierte // die EN-Seite auf die deutsche, während hreflang sie als eigene Alternative bewarb // → Google wertete sie als "Duplikat ohne gewählten Canonical" (GSC-Befund). // Deutsch lebt jetzt unter /de (explizites Prefix), Englisch unter /en. Beide Versionen // sind self-canonical; hreflang-de == deutsche Canonical-URL (= /de). So zeigt jede Seite // konsistent auf ihre eigene /de- bzw. /en-URL (passt zur Server-Umleitung auf /de). // Hat der Bereich einen eigenen englischen Host (z. B. knowledge.kotsch.tech), ist // die EN-Alternative die saubere EN-Host-URL OHNE /en; sonst /en auf demselben Host. $areaHostEn = $canonicalAreaIsHub ? null : ($canonicalArea['host_en'] ?? null); $alternateDeUrl = $publicBaseUrl . '/de' . $localizedSuffix; $alternateEnUrl = $areaHostEn ? 'https://' . $areaHostEn . ($localizedSuffix === '' ? '/' : $localizedSuffix) : $publicBaseUrl . '/en' . $localizedSuffix; $canonicalUrl = $routeLocale === 'en' ? $alternateEnUrl : $alternateDeUrl; $defaultTitle = 'KOTSCH.TECH | Arthur Kotsch - Software, Apps, Webdesign & IT'; $defaultDescription = 'KOTSCH.TECH ist die persönliche Tech-Marke von Arthur Kotsch für Softwareentwicklung, Apps, Webdesign, Webtools und IT-Hilfe.'; // Social-Image IMMER als eine kanonische https://www-URL ausgeben — unabhängig // vom Request-Host/-Schema. asset() leitete Host+Schema aus dem Request ab und // erzeugte so http:///… (falsches Schema + falscher Host) (M41/M5/M2). $defaultOgImage = 'https://www.kotsch.tech/' . ltrim(parse_url(asset('img/logo-wordmark-solid-cropped.png'), PHP_URL_PATH) ?: 'img/logo-wordmark-solid-cropped.png', '/'); // og:/twitter: leiteten Titel/Description aus dem Section-Inhalt ab und @yield // escapte den Default ein ZWEITES Mal → "&". Lösung: Rohtext vollständig // dekodieren und unten per {{ }} genau EINMAL escapen (P1-4). Kind-Views können // og_title/twitter_title weiterhin überschreiben (yieldContent-Fallback-Kette). $fullDecode = function (string $s): string { $prev = null; while ($prev !== $s) { $prev = $s; $s = html_entity_decode($s, ENT_QUOTES | ENT_HTML5); } return trim($s); }; $ogTitleOut = $fullDecode($__env->yieldContent('og_title', $__env->yieldContent('title', $defaultTitle))); $ogDescOut = $fullDecode($__env->yieldContent('og_description', $__env->yieldContent('meta_description', $defaultDescription))); $twTitleOut = $fullDecode($__env->yieldContent('twitter_title', $__env->yieldContent('title', $defaultTitle))); $twDescOut = $fullDecode($__env->yieldContent('twitter_description', $__env->yieldContent('meta_description', $defaultDescription))); // Exakte DE/EN-Auswahl für Layout-Chrome (Footer/Nav). request() bleibt der // äußere Request → segment(1) ist auch beim /en-Dispatch 'en' (P1-7). $tr = fn ($de, $enText) => $routeLocale === 'en' ? $enText : $de; @endphp @yield('title', $defaultTitle) @yield('meta_extra') @yield('structured_data') @vite(['resources/css/app.css', 'resources/js/app.js']) @php // ───────── Werbung & Consent-Wall ───────── // Anzeigen laden NUR nach Einwilligung. Aktive Netzwerke: Google AdSense (sofern // freigeschaltet) und PropellerAds In-Page Push. "Alle akzeptieren" UND "Nur // notwendige" aktivieren beide (Anzeigen = notwendig — Wunsch Betreiber). Die Wahl // wird DOMAINWEIT (.kotsch.tech) gemerkt, erscheint also nur EINMAL über alle // Subdomains. Keine Werbung/keine Wall auf Datei-/QR-/Download-Seiten (geteilte // PDFs /p/{token}, Dokumente, Downloads, /go) sowie im Shop und auf Demo-Seiten. $adsensePubId = (string) config('adsense.publisher_id'); $adsenseEnabled = $adsensePubId !== '' && ! str_contains($adsensePubId, 'XXXXXXXX'); // Monetag/PropellerAds komplett entfernt (2026-07-21): Werbung kommt jetzt aus der // eigenen KotschAds-Engine (house_ads, /admin/werbung) — first-party, kein Consent // nötig. Die Consent-Wall bleibt NUR für AdSense (aktuell aus, reaktivierbar). // Roh aus $_COOKIE (clientseitig als Klartext gesetzt; EncryptCookies würde es zu null). $cookieConsent = $_COOKIE['cookie_consent'] ?? null; $cookieConsent = in_array($cookieConsent, ['all', 'necessary'], true) ? $cookieConsent : null; $consentExempt = request()->routeIs('pdf.*', 'documents.*', 'download.*', 'go'); // Shop (Kauf-/Checkout-Flow) und Demos (Kundenbeispiele) bleiben werbefrei. $adsExempt = $consentExempt || request()->routeIs('shop.*', 'demo', 'demo.*'); // Wall nur, wo wirklich Tracking-Werbung lädt: ausschließlich AdSense (falls an). $adsAnyEnabled = $adsenseEnabled && ! $adsExempt; $showConsentWall = $adsAnyEnabled && $cookieConsent === null; @endphp @if($adsenseEnabled && ! $adsExempt && $cookieConsent !== null) @endif @stack('head') @php $registry = new \App\Support\SubdomainRegistry(); $area = $registry->currentArea(); $allAreas = $registry->subdomains(); $isHub = ($area['key'] ?? 'hub') === 'hub'; $accent = $area['accent'] ?? '#2563eb'; $accent2 = $area['accent_2'] ?? $accent; // routeIs('home') ist beim lokalisierten /de- bzw. /en-Dispatch false (äußere Route // ist LocalizedPageController), wodurch die Startseite ihr Glas-Header-Layout // (body[data-home] + Scroll-Skript) verlöre. Daher zusätzlich aus dem locale- // bereinigten Pfad ableiten: leerer Pfad auf dem Hub (Apex) == Startseite. Die // Beschränkung auf $isHub verhindert, dass auch Subdomain-Wurzeln (z. B. // shop.kotsch.tech/, deren äußerer Pfad ebenfalls "/" ist) data-home erhalten (M20). $isHome = request()->routeIs('home') || ($isHub && $localizedPath === ''); @endphp
@if (session('success'))
{{ session('success') }}
@endif @if (session('error'))
{{ session('error') }}
@endif @yield('content')
@if($isHome) @endif @if($showConsentWall) {{-- Blockierende Consent-Wall: Seite ist erst nutzbar nach einer Wahl. Beide Optionen laden AdSense (Anzeigen = notwendig). Wahl wird domainweit (.kotsch.tech) gemerkt. --}} @endif