oglasi in CMP nazaj na stanje pred 28. 8. 2026 (Discover padec)

- index.html: Googlovo CMP sporocilo spet zaduseno (controlledMessagingFunction + skrivanje fc-*), kot do 28. 8.
- adsense.tsx in mobile-sticky-ad.tsx: stara verzija (stare enote 4154017639, 7672178246, 4593001335, 3854634730, 2398082838; in-article okvir se izrise sele ob polnem oglasu; sticky 320x50)
- clanek: vrhnji pas nad naslovnico, brez display oglasa pred besedilom, najvec dva in-article oglasa
- naslovnica: postavitev ostane, GridBannerAd na stari enoti 4154017639

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViFFkiQmVM2yhrd6pU1UBQ
This commit is contained in:
Claude
2026-09-04 07:10:37 +02:00
co-authored by Claude Fable 5.1
parent a31797a807
commit bc275e272f
4 changed files with 140 additions and 264 deletions
+26
View File
@@ -35,6 +35,32 @@
<link rel="apple-touch-icon" href="/favicon.png" />
<link rel="shortcut icon" href="/favicon.png" />
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4465464714854276" crossorigin="anonymous"></script>
<script>
window.googlefc = window.googlefc || {};
window.googlefc.ccpa = window.googlefc.ccpa || {};
window.googlefc.callbackQueue = window.googlefc.callbackQueue || [];
window.googlefc.controlledMessagingFunction = function(message) { message.proceed(false); };
(function hideGoogleCMP() {
var style = document.createElement('style');
style.textContent = '.fc-consent-root,.fc-dialog-container,.fc-dialog-overlay,div[id^="fc-"],div[class^="fc-"]{display:none!important;visibility:hidden!important}';
document.head.appendChild(style);
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(m) {
m.addedNodes.forEach(function(node) {
if (node.nodeType === 1) {
var id = node.id || '';
var cls = node.className || '';
if (id.indexOf('fc-') === 0 || (typeof cls === 'string' && cls.indexOf('fc-') >= 0)) {
node.style.display = 'none';
node.remove();
}
}
});
});
});
observer.observe(document.documentElement, { childList: true, subtree: true });
})();
</script>
<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=Architects+Daughter&family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Fira+Code:wght@300..700&family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&family=Lora:ital,wght@0,400..700;1,400..700&family=Merriweather:ital,opsz,wght@0,18..144,300..900;1,18..144,300..900&family=Montserrat:ital,wght@0,100..900;1,100..900&family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Outfit:wght@100..900&family=Oxanium:wght@200..800&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto:ital,wght@0,100..900;1,100..900&family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&family=Source+Serif+4:ital,opsz,wght@0,8..60,200..900;1,8..60,200..900&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
+96 -221
View File
@@ -11,7 +11,6 @@ interface AdSenseProps {
style?: Record<string, string>;
layout?: string;
layoutKey?: string;
eager?: boolean;
onAdStatus?: (filled: boolean) => void;
}
@@ -23,11 +22,6 @@ export default function AdSense({
style,
layout,
layoutKey,
// Privzeto zahtevamo takoj. Odlozena zahteva dvigne vidnost, a znizza stevilo
// prikazov pri enotah globlje na strani - in prav te (in-article, multiplex)
// nosijo prihodek. Lazy pot ostaja na voljo prek eager={false}, a je nikjer
// ne uporabljamo, dokler ucinka ne moremo izmeriti v AdSense.
eager = true,
onAdStatus,
}: AdSenseProps) {
const adRef = useRef<HTMLModElement>(null);
@@ -35,100 +29,44 @@ export default function AdSense({
useEffect(() => {
if (pushed.current) return;
const ins = adRef.current;
try {
const adsbygoogle = (window as any).adsbygoogle || [];
adsbygoogle.push({});
pushed.current = true;
} catch (e) {
}
let io: IntersectionObserver | null = null;
let obs: MutationObserver | null = null;
let timer: ReturnType<typeof setTimeout> | null = null;
let decided = false;
if (!onAdStatus) return;
const ins = adRef.current;
if (!ins) return;
// Google postavi data-ad-status na "filled", "unfilled" ali "unfill-optimized".
// Ce statusa ni, se zanesemo na dejansko visino. Odlocimo se enkrat.
// AdSense odzivni enoti vnaprej doloci visino po sirini zaslona. Ce je
// postrezena kreativa nizja, ostane pod njo prazen rezerviran pas znotraj
// okvirja. Visino <ins> zato poravnamo na dejansko visino oglasnega okvirja.
// Nic se ne odreze - visina je natanko taksna kot oglas.
let fitTimers: ReturnType<typeof setTimeout>[] = [];
const fitToCreative = () => {
// Enota s trdno visino v style (fiksne Display enote) se ne prilagaja
// kreativi, sicer nam Google 300x400 razpotegne na 300x600.
if (style && typeof (style as any).height === "string") return;
const f = ins?.querySelector("iframe") as HTMLIFrameElement | null;
if (!ins || !f) return;
const h = f.getBoundingClientRect().height;
if (h > 10 && Math.abs(ins.getBoundingClientRect().height - h) > 2) {
ins.style.height = `${Math.ceil(h)}px`;
}
};
const scheduleFit = () => {
fitTimers.forEach(clearTimeout);
fitTimers = [0, 300, 1000, 2500].map((d) => setTimeout(fitToCreative, d));
};
let decided = false;
const decide = (filled: boolean) => {
if (decided) return;
decided = true;
obs?.disconnect();
if (timer) clearTimeout(timer);
if (filled) scheduleFit();
onAdStatus?.(filled);
obs.disconnect();
clearTimeout(timer);
onAdStatus(filled);
};
// Visina samega <ins> ni dokaz - nekatere enote imajo rezerviran prostor.
// Napolnjen oglas ima notri okvir (iframe) z dejansko visino.
const hasFrame = () => {
const f = ins?.querySelector("iframe");
const f = ins.querySelector("iframe");
return !!f && (f as HTMLIFrameElement).clientHeight > 10;
};
const check = () => {
const st = ins?.getAttribute("data-ad-status") || "";
const st = ins.getAttribute("data-ad-status") || "";
if (st === "filled") return decide(true);
if (st.startsWith("unfill")) return decide(false);
if (hasFrame()) return decide(true);
};
// Oglas zahtevamo sele, ko se enota priblizuje vidnemu polju (200 px prej).
// Enote globoko pod pregibom so se prej zahtevale takoj ob nalaganju strani in
// vecine uporabnik ni nikoli videl - nizka vidnost pomeni nizjo ceno.
// Odlocitev o praznosti steje sele od zahteve naprej, ne od mounta.
const push = () => {
if (pushed.current) return;
try {
const adsbygoogle = (window as any).adsbygoogle || [];
adsbygoogle.push({});
pushed.current = true;
} catch (e) {
}
if (!ins) return;
// Opazujemo tudi pri enotah brez onAdStatus - potrebujemo jih za poravnavo
// visine na kreativo.
obs = new MutationObserver(() => { check(); fitToCreative(); });
obs.observe(ins, { attributes: true, childList: true, subtree: true, attributeFilter: ["data-ad-status", "style"] });
timer = setTimeout(() => decide(hasFrame()), 3000);
window.addEventListener("resize", scheduleFit);
check();
scheduleFit();
};
if (eager || !ins || typeof IntersectionObserver === "undefined") {
push();
} else {
io = new IntersectionObserver((entries) => {
if (entries.some((e) => e.isIntersecting)) {
io?.disconnect();
io = null;
push();
}
}, { rootMargin: "200px 0px" });
io.observe(ins);
}
return () => {
io?.disconnect();
obs?.disconnect();
if (timer) clearTimeout(timer);
fitTimers.forEach(clearTimeout);
window.removeEventListener("resize", scheduleFit);
};
const obs = new MutationObserver(check);
obs.observe(ins, { attributes: true, childList: true, subtree: true, attributeFilter: ["data-ad-status", "style"] });
const timer = setTimeout(() => decide(hasFrame()), 3000);
check();
return () => { obs.disconnect(); clearTimeout(timer); };
}, []);
return (
@@ -139,8 +77,8 @@ export default function AdSense({
style={style || { display: "block" }}
data-ad-client="ca-pub-4465464714854276"
data-ad-slot={slot}
{...(format ? { "data-ad-format": format } : {})}
data-full-width-responsive={responsive ? "true" : "false"}
data-ad-format={format}
data-full-width-responsive={responsive ? "true" : undefined}
{...(layout ? { "data-ad-layout": layout } : {})}
{...(layoutKey ? { "data-ad-layout-key": layoutKey } : {})}
/>
@@ -150,54 +88,20 @@ export default function AdSense({
let adSeedCounter = 1;
// Vodoravni pas med vrsticami mreze. Oglas v celici je vrstico raztegnil na
// svojo visino (546 px proti 399 px kartice) in pod izvlecki je ostalo prazno.
// Pas cez vso sirino na visino kartic ne vpliva in ima boljso zapolnjenost.
export function GridBannerAd() {
export function ArticleCardAd() {
const seed = useMemo(() => adSeedCounter++, []);
const [filled, setFilled] = useState<boolean | null>(null);
if (filled === false) return null;
return (
// Pas cez vso sirino je bil visok 248 do 280 px in je bralca prekinil bolj,
// kot je prinesel. Zozimo ga na 728 px: pri tej sirini Google servira
// leaderboard 728x90, na telefonu pa 320x100. Rezanja ne uporabljamo,
// ker je obrezan oglas krsitev pravil.
<div className="my-2 mx-auto w-full max-w-[728px] min-h-[110px]" data-testid="ad-grid-banner">
<div className="text-[9px] text-muted-foreground/40 text-center pb-1 uppercase tracking-widest">
Anzeige
</div>
{/* FOLX 2026 - Leaderboard */}
<ArtistPatternBg className="rounded-md border border-card-border h-full bg-card" seed={seed}>
<AdSense
slot="2757497569"
format="auto"
responsive={true}
slot="3854634730"
format="fluid"
layoutKey="-6r+cy-10+8a-3"
style={{ display: "block" }}
onAdStatus={setFilled}
/>
</div>
);
}
export function ArticleCardAd() {
const [filled, setFilled] = useState<boolean | null>(null);
if (filled === false) return null;
// 31. 8. 2026: oglas mora biti enak kartici, torej cela celica in ne nizji
// blok z luknjo pod njim. Okvir je zato h-full in prevzame visino vrstice,
// oglas pa je v njem centriran. Enota 9916590834 je fiksna 300x400: fiksne
// mere Googlova skripta ne prevaja, zato se ne razteguje kot responsive.
return (
<div
className="h-full w-full flex items-center justify-center rounded-md border border-card-border bg-card overflow-hidden"
data-testid="ad-card"
>
{/* FOLX 2026 - Grid, fiksna Display enota 300x600. Fiksna mera pomeni,
da Googlova skripta visine ne prevaja; stara 9916590834 je bila
Responsiv in se je razteznila na 548 px. */}
<AdSense
slot="4126524750"
responsive={false}
style={{ display: "inline-block", width: "300px", height: "250px" }}
onAdStatus={setFilled}
/>
</div>
</ArtistPatternBg>
);
}
@@ -215,71 +119,36 @@ export function TopBannerAd() {
{shown && (
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest w-full">Anzeige</div>
)}
{/* format="auto" + full-width-responsive: Google dobi celo sirino in
izbere kreativo, ki jo zapolni. Pri "horizontal" s trdo visino je
vracal 320x50 in okrog je ostal prazen rob. */}
{/* FOLX 2026 - Leaderboard */}
<AdSense
slot="2757497569"
format="auto"
slot="4154017639"
format="horizontal"
className="w-full"
style={{ display: "block" }}
style={{ display: "block", width: "100%", height: "90px", textAlign: "center", margin: "0 auto" }}
responsive={false}
eager
onAdStatus={setFilled}
/>
</div>
);
}
// Navaden odzivni display v toku besedila. Za razliko od fluid in-article enote
// dobi Google celotno sirino in lahko postrezhe visoko kreativo (336x280, 300x600),
// torej en cel oglas, mimo katerega bralec odskrola - namesto vec drobnih.
export function InContentDisplayAd() {
const [filled, setFilled] = useState<boolean | null>(null);
if (filled === false) return null;
const shown = filled === true;
return (
<div
className={shown ? "my-6 rounded-md overflow-hidden bg-card border border-card-border" : ""}
data-testid="ad-in-content-display"
>
{shown && (
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
)}
{/* Sirina 728 px: pri njej Google servira leaderboard 728x90, na telefonu
320x100. Pri polni sirini clanka je servirala velik nativni oglas
visok 250 px in bralcu je zakrila zacetek besedila. */}
<div className="mx-auto w-full max-w-[728px]">
<AdSense
slot="2757497569"
format="auto"
responsive={true}
style={{ display: "block" }}
onAdStatus={setFilled}
/>
</div>
</div>
);
}
export function InArticleAd() {
// Prava AdSense In-article enota (fluid + data-ad-layout="in-article").
// Kadar oglasa ni, se okvir in rezerviran prostor ne izriseta.
const [filled, setFilled] = useState<boolean | null>(null);
// 1. 9. 2026: prej se je okvir dodal sele po nalozitvi oglasa in stran je
// poskocila - meritev je pokazala CLS 0,10 na clanku in 0,15 na naslovnici,
// Search Console pa 225 naslovov cez prag. Zdaj je prostor rezerviran
// vnaprej in se po nalozitvi nic ne premakne.
if (filled === false) return null;
const shown = filled === true;
return (
<div className="my-6 rounded-md overflow-hidden bg-card border border-card-border min-h-[280px]">
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
{/* FOLX 2026 - In-Article */}
<div
className={shown ? "my-6 rounded-md overflow-hidden bg-card border border-card-border" : ""}
style={shown ? { minHeight: "260px" } : undefined}
>
{shown && (
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
)}
<AdSense
slot="6313599193"
slot="7672178246"
format="fluid"
layout="in-article"
responsive={false}
style={{ display: "block", textAlign: "center" }}
onAdStatus={setFilled}
/>
@@ -294,60 +163,39 @@ export function MultiplexAd() {
return (
<div className={shown ? "bg-card rounded-md border border-card-border overflow-hidden" : ""}>
<AdSense
slot="8995602853"
slot="4593001335"
format="autorelaxed"
responsive={false}
style={{ display: "block" }}
className="w-full"
className={shown ? "w-full min-h-[250px]" : "w-full"}
onAdStatus={setFilled}
/>
</div>
);
}
// Oba stolpca ostaneta (nic se ne odvzame), a se prilagodita sirini zaslona:
// 160x600 od 1536 px, 300x600 od 1700 px naprej, kjer je prostora dovolj.
// Sirino dolocz CSS, AdSense jo prebere ob zahtevi. Dodan onAdStatus, da prazna
// enota ne pusti luknje - prej je ni imela.
function SideRail({ side, offset }: { side: "left" | "right"; offset: number }) {
const [filled, setFilled] = useState<boolean | null>(null);
if (filled === false) return null;
return (
<div
className="hidden 2xl:flex fixed top-0 bottom-0 w-[160px] z-10 items-center justify-center pointer-events-none"
style={side === "left"
? { left: `calc(50% - ${offset}px - var(--rail-w))` }
: { right: `calc(50% - ${offset}px - var(--rail-w))` }}
data-testid={`ad-${side}-sidebar`}
>
<div className="pointer-events-auto w-full">
{/* Trdna mera 160x600 (skyscraper). Prej je bil pas na sirokih zaslonih
300 px in z responsive vertical si je Google visino racunal sam,
zato se je raztegoval cez pol zaslona. */}
{/* FOLX 2026 - Skyscraper, fiksna Display enota 160x600. */}
<AdSense
slot="4349780836"
format=""
responsive={false}
style={{ display: "inline-block", width: "160px", height: "600px" }}
onAdStatus={setFilled}
/>
</div>
</div>
);
}
export function PageSideAds({ contentHalfWidth = 640 }: { contentHalfWidth?: number }) {
// 31. 8. 2026: pasova sta bila prilepljena na rob zaslona, ker je bila
// spremenljivka --rail-w nad 1700 px se vedno 300, enota pa je zdaj fiksna
// 160 px - odmik se je racunal s 140 px prevelike sirine. Zdaj je povsod
// 160, razmik do vsebine pa majhen, da sta pasova tik ob clanku.
const gap = 8;
const gap = 10;
const offset = contentHalfWidth + gap;
return (
<div className="relative page-side-rails">
<SideRail side="left" offset={offset} />
<SideRail side="right" offset={offset} />
<div className="relative">
<div className="hidden 2xl:flex fixed top-0 bottom-0 w-[160px] z-10 items-center justify-center pointer-events-none" style={{ left: `calc(50% - ${offset}px - 160px)` }} data-testid="ad-left-sidebar">
<div className="pointer-events-auto">
<AdSense
slot="2398082838"
format="vertical"
style={{ display: "block", width: "160px", height: "600px" }}
/>
</div>
</div>
<div className="hidden 2xl:flex fixed top-0 bottom-0 w-[160px] z-10 items-center justify-center pointer-events-none" style={{ right: `calc(50% - ${offset}px - 160px)` }} data-testid="ad-right-sidebar">
<div className="pointer-events-auto">
<AdSense
slot="2398082838"
format="vertical"
style={{ display: "block", width: "160px", height: "600px" }}
/>
</div>
</div>
</div>
);
}
@@ -358,14 +206,41 @@ export function SidebarAd() {
return (
<div className={`sticky top-4 ${visible ? "" : "hidden"}`}>
<div className="bg-card rounded-md border border-card-border p-4 mt-4">
{/* FOLX 2026 - Sidebar, fiksna Display enota 300x600. */}
<AdSense
slot="9953566507"
responsive={false}
style={{ display: "inline-block", width: "300px", height: "600px" }}
slot="2398082838"
format="auto"
onAdStatus={(filled) => setVisible(filled)}
/>
</div>
</div>
);
}
// Vodoravni pas med vrsticami mreze na naslovnici. 4. 9. 2026: nazaj na staro
// enoto 4154017639 (horizontal), enako kot vrhnji pas pred 28. 8.
export function GridBannerAd() {
const [filled, setFilled] = useState<boolean | null>(null);
if (filled === false) return null;
const shown = filled === true;
return (
<div className="my-2 mx-auto w-full max-w-[728px]" data-testid="ad-grid-banner">
<div
className={shown
? "rounded-md overflow-hidden bg-card border border-card-border flex flex-col items-center"
: "flex flex-col items-center"}
>
{shown && (
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest w-full">Anzeige</div>
)}
<AdSense
slot="4154017639"
format="horizontal"
className="w-full"
style={{ display: "block", width: "100%", height: "90px", textAlign: "center", margin: "0 auto" }}
responsive={false}
onAdStatus={setFilled}
/>
</div>
</div>
);
}
+13 -25
View File
@@ -8,7 +8,6 @@ const DISMISS_KEY = "folx-sticky-ad-dismissed";
export default function MobileStickyAd() {
const isMobile = useIsMobile();
const [visible, setVisible] = useState(false);
const [filled, setFilled] = useState<boolean | null>(null);
useEffect(() => {
if (!isMobile) return;
@@ -24,37 +23,26 @@ export default function MobileStickyAd() {
sessionStorage.setItem(DISMISS_KEY, "1");
};
if (!isMobile || !visible || filled === false) return null;
const shown = filled === true;
if (!isMobile || !visible) return null;
return (
<div className="fixed bottom-0 left-0 right-0 z-[100] animate-in slide-in-from-bottom duration-300" data-testid="mobile-sticky-ad">
<div className={shown ? "relative bg-card border-t border-card-border shadow-2xl" : "relative"}>
{shown && (
<button
onClick={close}
className="absolute -top-8 right-2 w-7 h-7 flex items-center justify-center rounded-full bg-card border border-card-border shadow-lg text-muted-foreground hover:text-foreground transition-colors"
data-testid="button-sticky-ad-close"
aria-label="Schließen"
>
<X className="w-4 h-4" />
</button>
)}
{shown && (
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
)}
{/* Vodoravni format brez full-width-responsive: enota se drzi sirine
svojega vsebnika in da 320x50 ali 320x100. Z vklopljenim
full-width-responsive je Google vracal 390x390 kvadrat. */}
<div className="w-full pb-1">
<div className="relative bg-card border-t border-card-border shadow-2xl">
<button
onClick={close}
className="absolute -top-8 right-2 w-7 h-7 flex items-center justify-center rounded-full bg-card border border-card-border shadow-lg text-muted-foreground hover:text-foreground transition-colors"
data-testid="button-sticky-ad-close"
aria-label="Schließen"
>
<X className="w-4 h-4" />
</button>
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
<div className="flex justify-center pb-1">
<AdSense
slot="4154017639"
format="horizontal"
className="w-full"
style={{ display: "block" }}
style={{ display: "inline-block", width: "320px", height: "50px" }}
responsive={false}
eager
onAdStatus={setFilled}
/>
</div>
</div>
+5 -18
View File
@@ -9,7 +9,7 @@ import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import Header from "@/components/header";
import Footer from "@/components/footer";
import { InArticleAd, InContentDisplayAd, PageSideAds, MultiplexAd } from "@/components/adsense";
import { InArticleAd, PageSideAds, MultiplexAd, TopBannerAd } from "@/components/adsense";
import { useIsMobile } from "@/hooks/use-mobile";
import DOMPurify from "dompurify";
import ShareButtons from "@/components/share-buttons";
@@ -344,8 +344,7 @@ export default function ArticlePage() {
<Header />
<PageSideAds contentHalfWidth={480} />
<main className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
{/* Zgornjega pasu na clanku namenoma ni: potiskal je naslovnico in naslov
pod pregib, placan pa je najslabse. Prvi oglas pride sele v besedilu. */}
<TopBannerAd />
<Link href="/">
<Button variant="ghost" size="sm" className="mb-3 gap-2" data-testid="button-back">
<ArrowLeft className="w-4 h-4" />
@@ -407,11 +406,7 @@ export default function ArticlePage() {
</div>
</div>
{/* Enota "Folx News - In-Article" (4154017639) je bila prej vrhnji pas nad
naslovnico. Ostane na clanku, a pod naslovom in tik pred besedilom:
isti prikazi, boljse mesto, naslov ne pade pod pregib. */}
<InContentDisplayAd />
{(() => {
const sanitized = sanitizeContent(articleBody || article.content);
const blocks = sanitized.split(/(?=<(?:p|h[2-4]|div)[\s>])/i).filter(Boolean);
@@ -435,13 +430,8 @@ export default function ArticlePage() {
// Vsi so fluid in-article enota 7672178246 (najvecji zasluzkar).
const last = segments.length - 1;
const extra = Math.min(3, Math.max(0, Math.floor((blocks.length - 10) / 5)));
const adCount = last <= 0 ? 0 : (last === 1 ? 1 : Math.min(2 + extra, last));
const AD_AFTER: number[] = [];
for (let i = 0; i < adCount; i++) {
const pos = i === 0 ? 0 : Math.round((i * last) / adCount);
if (pos < last && !AD_AFTER.includes(pos)) AD_AFTER.push(pos);
}
// Najvec dva oglasa v besedilu: po prvem odstavku in na sredini (stanje pred 28. 8. 2026).
const AD_AFTER = last <= 0 ? [] : (last === 1 ? [0] : [0, Math.floor(last / 2)]);
return (
<div ref={(el) => {
if (!el) return;
@@ -462,9 +452,6 @@ export default function ArticlePage() {
dangerouslySetInnerHTML={{ __html: html }}
data-testid={idx === 0 ? "article-content" : `article-content-${idx}`}
/>
{/* VSA mesta v besedilu so 7672178246 - to je enota z najvecjim
prihodkom (98,93 EUR v 7 dneh). Prej sta bili tam dve, zdaj
dve do pet. Enota 4154017639 ima svoje mesto pred besedilom. */}
{AD_AFTER.includes(idx) && <InArticleAd />}
</div>
))}