Files
archicratie-edition/src/components/GlossaryPortalStickySync.astro
Archicratia 622963e8e9
All checks were successful
SMOKE / smoke (push) Successful in 3s
CI / build-and-anchors (push) Successful in 38s
CI / build-and-anchors (pull_request) Successful in 37s
Polish glossary portal compact sticky follow behavior
2026-05-07 22:56:53 +02:00

1182 lines
42 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
interface Props {
heroMoreId: string;
heroToggleId: string;
sectionHeadSelector?: string;
mobileBreakpoint?: number;
autoCollapseDelta?: number;
compactSticky?: boolean;
}
const {
heroMoreId,
heroToggleId,
sectionHeadSelector = ".glossary-portal-section__head",
mobileBreakpoint = 860,
autoCollapseDelta = 160,
compactSticky = false,
} = Astro.props;
---
<script
is:inline
define:vars={{ heroMoreId, heroToggleId, sectionHeadSelector, mobileBreakpoint, autoCollapseDelta, compactSticky }}
>
(() => {
const boot = () => {
const body = document.body;
const root = document.documentElement;
const hero = document.querySelector("[data-glossary-portal-hero]");
const follow = document.getElementById("reading-follow");
const heroMore = document.getElementById(heroMoreId);
const heroToggle = document.getElementById(heroToggleId);
if (!body || !root || !hero || !follow) return;
const BODY_CLASS = "is-glossary-portal-page";
const COMPACT_STICKY_CLASS = "glossary-portal-compact-sticky";
const FOLLOW_ON_CLASS = "glossary-portal-follow-on";
const EXPANDED_CLASS = "glossary-portal-hero-expanded";
const CONDENSED_CLASS = "glossary-portal-hero-condensed";
const mqMobile = window.matchMedia(`(max-width: ${mobileBreakpoint}px)`);
const mqSmallLandscape = window.matchMedia(
"(orientation: landscape) and (max-width: 920px) and (max-height: 520px)"
);
let expandedAtY = null;
let lastScrollY = window.scrollY || 0;
let raf = 0;
let lastFollowOn = null;
let lastCondensed = null;
let lastHeroHeight = -1;
let heroPinStartY = null;
let lastCompactFollowTitle = null;
let lastCompactFollowOn = null;
body.classList.add(BODY_CLASS);
body.classList.toggle(COMPACT_STICKY_CLASS, Boolean(compactSticky));
const isCompactViewport = () =>
mqMobile.matches || mqSmallLandscape.matches;
const keepPortalPremiumOnCompact = () =>
Boolean(compactSticky) || body.classList.contains("is-index-complet-page");
const compactShouldDisablePortal = () =>
isCompactViewport() && !keepPortalPremiumOnCompact();
const compactStickyEnabled = () => Boolean(compactSticky);
const stripLocalSticky = () => {
document.querySelectorAll(sectionHeadSelector).forEach((el) => {
el.classList.remove("is-sticky");
el.removeAttribute("data-sticky-active");
});
};
const readStickyTop = () => {
const raw = getComputedStyle(document.documentElement)
.getPropertyValue("--glossary-sticky-top")
.trim();
const n = Number.parseFloat(raw);
return Number.isFinite(n) ? n : 64;
};
const captureHeroPinStart = (force = false) => {
if (!force && heroPinStartY != null) return heroPinStartY;
const stickyTop = readStickyTop();
const rect = hero.getBoundingClientRect();
const y = window.scrollY || window.pageYOffset || 0;
/*
Seuil documentaire réel : moment où le haut naturel du hero
rejoint le rail sticky. Ce seuil empêche la condensation au haut
de page lorsque le hero n'est pas encore embarqué dans le scroll.
*/
heroPinStartY = Math.max(0, Math.round(y + rect.top - stickyTop));
return heroPinStartY;
};
const hasReachedHeroPinStart = () => {
const y = window.scrollY || window.pageYOffset || 0;
const start = captureHeroPinStart(false);
const rect = hero.getBoundingClientRect();
const stickyTop = readStickyTop();
/*
Condensation immédiate au moment réel d'accrochage :
- soit le scroll a atteint le seuil documentaire ;
- soit le hero est déjà visuellement posé sur son rail sticky.
Aucun délai artificiel ne doit retarder la troncature.
*/
return y >= start - 1 || Math.abs(rect.top - stickyTop) <= 2;
};
const computeFollowOn = () =>
body.classList.contains(CONDENSED_CLASS) &&
(!isCompactViewport() || compactStickyEnabled()) &&
follow.classList.contains("is-on") &&
follow.style.display !== "none" &&
follow.getAttribute("aria-hidden") !== "true";
const computeCondensed = () => {
if (isCompactViewport() && !compactStickyEnabled()) return false;
return hasReachedHeroPinStart();
};
const measureHeroHeight = () =>
Math.max(0, Math.round(hero.getBoundingClientRect().height || 0));
const PIN_EPS = 3;
const isHeroPinned = () => {
if (isCompactViewport() && !compactStickyEnabled()) return false;
const rect = hero.getBoundingClientRect();
const stickyTop = readStickyTop();
const cs = getComputedStyle(hero);
if (cs.position !== "sticky") return false;
const stillVisible = rect.bottom > stickyTop + 24;
return hasReachedHeroPinStart() && stillVisible;
};
const applyCompactHeroRail = () => {
/*
Les portails compactSticky doivent coller sous ledition-bar.
Certaines couches CSS historiques peuvent encore forcer top:0.
On pose donc le rail en inline !important, uniquement pour les
portails génériques compacts, jamais pour lindex complet.
*/
if (
!isCompactViewport() ||
!compactStickyEnabled() ||
body.classList.contains("is-index-complet-page")
) {
hero.style.removeProperty("top");
return;
}
const stickyTop = readStickyTop();
hero.style.setProperty("top", `${Math.round(stickyTop)}px`, "important");
};
const applyCompactFollowRail = () => {
/*
Le reading-follow compact doit être collé sous le hero.
Certaines règles historiques de #reading-follow imposent top:-1px
avec une priorité supérieure au style simple. On pose donc le rail
en inline !important, uniquement pour les portails compacts génériques.
*/
if (
!isCompactViewport() ||
!compactStickyEnabled() ||
body.classList.contains("is-index-complet-page")
) {
follow.style.removeProperty("top");
follow.style.removeProperty("left");
follow.style.removeProperty("width");
return;
}
const stickyTop = readStickyTop();
const heroH = body.classList.contains(CONDENSED_CLASS)
? measureHeroHeight()
: 0;
const top = Math.max(0, Math.round(stickyTop + heroH - 1));
follow.style.setProperty("position", "fixed", "important");
follow.style.setProperty("top", `${top}px`, "important");
follow.style.setProperty("left", "var(--reading-left)", "important");
follow.style.setProperty("width", "var(--reading-width)", "important");
follow.style.setProperty("z-index", "71", "important");
};
const applyLocalStickyHeight = () => {
/*
Stabilisation premium :
une fois le hero condensé, on réserve sa hauteur réelle.
Cela évite les oscillations dun frame où isHeroPinned()
peut repasser brièvement à false pendant le scroll.
*/
const shouldReserveHero =
body.classList.contains(CONDENSED_CLASS) || isHeroPinned();
const h = shouldReserveHero ? measureHeroHeight() : 0;
if (h === lastHeroHeight) return;
lastHeroHeight = h;
if (typeof window.__archiSetLocalStickyHeight === "function") {
window.__archiSetLocalStickyHeight(h);
} else {
root.style.setProperty("--glossary-local-sticky-h", `${h}px`);
}
};
const syncFollowState = () => {
const on = computeFollowOn();
if (on !== lastFollowOn) {
lastFollowOn = on;
body.classList.toggle(FOLLOW_ON_CLASS, on);
}
return on;
};
const syncCondensedState = () => {
const condensed = computeCondensed();
if (condensed !== lastCondensed) {
lastCondensed = condensed;
body.classList.toggle(CONDENSED_CLASS, condensed);
}
return condensed;
};
const collapseHero = () => {
if (!body.classList.contains(EXPANDED_CLASS)) return;
body.classList.remove(EXPANDED_CLASS);
expandedAtY = null;
if (heroMore) {
heroMore.setAttribute("aria-hidden", "true");
}
if (heroToggle) {
heroToggle.hidden = false;
heroToggle.setAttribute("aria-expanded", "false");
}
try {
window.__archiUpdateFollow?.();
} catch {}
schedule();
};
const expandHero = () => {
body.classList.add(EXPANDED_CLASS);
expandedAtY = window.scrollY || 0;
if (heroMore) {
heroMore.setAttribute("aria-hidden", "false");
}
if (heroToggle) {
heroToggle.hidden = true;
heroToggle.setAttribute("aria-expanded", "true");
}
try {
window.__archiUpdateFollow?.();
} catch {}
schedule();
};
const syncHeroState = (condensed) => {
const expanded = body.classList.contains(EXPANDED_CLASS);
const collapsed = condensed && !expanded;
if ((isCompactViewport() && !compactStickyEnabled()) || !condensed) {
body.classList.remove(EXPANDED_CLASS);
expandedAtY = null;
if (heroMore) {
heroMore.setAttribute("aria-hidden", "false");
}
if (heroToggle) {
heroToggle.hidden = true;
heroToggle.setAttribute("aria-expanded", "false");
}
return;
}
if (heroMore) {
heroMore.setAttribute("aria-hidden", collapsed ? "true" : "false");
}
if (heroToggle) {
heroToggle.hidden = !collapsed;
heroToggle.setAttribute("aria-expanded", expanded ? "true" : "false");
}
};
/* GENERIC_COMPACT_FOLLOW_CLEAN_START
Portails génériques compactSticky :
- ligne 1 : section macro active ;
- ligne 2 : sous-tête interne active ;
- un titre ne devient actif qu'une fois complètement dépassé
par la bande réelle du reading-follow__inner.
*/
const isGenericCompactPortal = () =>
isCompactViewport() &&
compactStickyEnabled() &&
!body.classList.contains("is-index-complet-page");
const compactFollowBand = () => {
const inner = follow.querySelector(".reading-follow__inner");
const innerRect = inner?.getBoundingClientRect();
const heroRect = hero.getBoundingClientRect();
if (innerRect && innerRect.height > 0) {
return {
top: innerRect.top,
bottom: innerRect.bottom,
height: innerRect.height,
};
}
const top = Math.max(readStickyTop(), heroRect.bottom || readStickyTop());
const height = 30;
return {
top,
bottom: top + height,
height,
};
};
const cleanFollowTitle = (el) => {
if (!el) return "";
const clone = el.cloneNode(true);
clone
.querySelectorAll(
[
".glossary-portal-section__count",
"[class*='count']",
"[class*='index']",
"[class*='meta']",
"[class*='eyebrow']",
"[aria-hidden='true']"
].join(",")
)
.forEach((node) => node.remove());
return clone.textContent?.replace(/\s+/g, " ").trim() ?? "";
};
const titleNodeFor = (node) =>
node?.matches?.("h2,h3,h4,[data-reading-title]")
? node
: node?.querySelector?.("h2,h3,h4,[data-reading-title]") ?? node;
const passedTitleStateFrom = (nodes) => {
const band = compactFollowBand();
const bandCenter = band.top + band.height / 2;
let active = null;
for (const node of nodes) {
const titleNode = titleNodeFor(node);
const title = cleanFollowTitle(titleNode);
if (!title) continue;
const rect = titleNode.getBoundingClientRect();
if (rect.height <= 0) continue;
/*
Règle perceptive stabilisée :
la traversée commence quand le bas du follow rencontre le haut
du titre réel, et s'achève quand le haut du follow rencontre
le bas du titre réel.
Comme le follow n'anime pas deux titres concurrents, le basculement
se fait au milieu de cette traversée : quand le centre du titre
est passé au-dessus du centre de la bande follow.
*/
const titleCenter = rect.top + rect.height / 2;
if (titleCenter <= bandCenter + 1) {
active = {
node,
titleNode,
title,
};
continue;
}
break;
}
return active;
};
const detailCandidatesFor = (sectionNode) => {
const section = sectionNode?.closest("section");
if (!section) return [];
const selector = [
".glossary-portal-panel__head",
"[class*='__head']",
"[class*='__title']",
"h3",
"h4"
].join(",");
return Array.from(section.querySelectorAll(selector)).filter((node) => {
if (node.closest(".glossary-portal-section__head")) return false;
const titleNode = titleNodeFor(node);
const title = cleanFollowTitle(titleNode);
if (!title) return false;
const rect = titleNode.getBoundingClientRect();
if (rect.height <= 0) return false;
return true;
});
};
const renderFollowTextLine = (inner, className, title) => {
if (!title) return;
let line = inner.querySelector(`.${className}`);
if (!line) {
line = document.createElement("div");
line.className = `rf-line ${className}`;
inner.appendChild(line);
}
line.textContent = title;
};
const setCompactFollowOff = () => {
lastCompactFollowTitle = null;
if (lastCompactFollowOn !== false) {
lastCompactFollowOn = false;
body.classList.remove(FOLLOW_ON_CLASS);
follow.classList.remove("is-on");
follow.setAttribute("aria-hidden", "true");
}
follow.style.display = "block";
follow.style.setProperty("opacity", "0", "important");
follow.style.setProperty("visibility", "hidden", "important");
follow.style.setProperty("pointer-events", "none", "important");
root.style.setProperty("--followbar-h", "0px");
};
const syncGenericCompactFollow = (condensed) => {
if (!isGenericCompactPortal()) return false;
if (!condensed) {
setCompactFollowOff();
return true;
}
const sectionHeads = Array.from(
document.querySelectorAll(".glossary-portal-section__head")
);
const sectionState = passedTitleStateFrom(sectionHeads);
if (!sectionState?.title) {
setCompactFollowOff();
return true;
}
const detailState = passedTitleStateFrom(detailCandidatesFor(sectionState.node));
follow.innerHTML = "";
const inner = document.createElement("div");
inner.className = "reading-follow__inner";
renderFollowTextLine(inner, "rf-h2", sectionState.title);
if (detailState?.title && detailState.title !== sectionState.title) {
renderFollowTextLine(inner, "rf-h3", detailState.title);
}
follow.appendChild(inner);
if (lastCompactFollowOn !== true) {
lastCompactFollowOn = true;
body.classList.add(FOLLOW_ON_CLASS);
follow.classList.add("is-on");
follow.setAttribute("aria-hidden", "false");
}
follow.style.display = "block";
follow.style.setProperty("opacity", "1", "important");
follow.style.setProperty("visibility", "visible", "important");
follow.style.setProperty("pointer-events", "auto", "important");
applyCompactFollowRail();
inner.style.setProperty("opacity", "1", "important");
inner.style.setProperty("visibility", "visible", "important");
inner.style.setProperty("display", "block", "important");
requestAnimationFrame(() => {
applyCompactFollowRail();
const h = Math.max(0, Math.round(follow.getBoundingClientRect().height || 0));
root.style.setProperty("--followbar-h", `${h}px`);
});
return true;
};
/* GENERIC_COMPACT_FOLLOW_CLEAN_END */
const maybeAutoCollapseOnScroll = () => {
if (isCompactViewport() && !compactStickyEnabled()) {
lastScrollY = window.scrollY || 0;
return;
}
if (!body.classList.contains(EXPANDED_CLASS)) {
lastScrollY = window.scrollY || 0;
return;
}
if (expandedAtY == null) {
lastScrollY = window.scrollY || 0;
return;
}
const currentY = window.scrollY || 0;
const scrollingDown = currentY > lastScrollY;
const delta = currentY - expandedAtY;
if (scrollingDown && delta >= autoCollapseDelta) {
collapseHero();
}
lastScrollY = currentY;
};
const syncAll = () => {
stripLocalSticky();
applyCompactHeroRail();
if (isCompactViewport() && !compactStickyEnabled()) {
body.classList.remove(FOLLOW_ON_CLASS);
body.classList.remove(CONDENSED_CLASS);
body.classList.remove(EXPANDED_CLASS);
lastFollowOn = false;
lastCondensed = false;
expandedAtY = null;
if (heroMore) {
heroMore.setAttribute("aria-hidden", "false");
}
if (heroToggle) {
heroToggle.hidden = true;
heroToggle.setAttribute("aria-expanded", "false");
}
requestAnimationFrame(() => {
applyLocalStickyHeight();
try {
window.__archiUpdateFollow?.();
} catch {}
});
return;
}
const condensed = syncCondensedState();
syncHeroState(condensed);
requestAnimationFrame(() => {
applyLocalStickyHeight();
applyCompactFollowRail();
if (syncGenericCompactFollow(condensed)) {
return;
}
try {
window.__archiUpdateFollow?.();
} catch {}
syncFollowState();
});
requestAnimationFrame(() => {
applyLocalStickyHeight();
applyCompactFollowRail();
if (syncGenericCompactFollow(condensed)) {
return;
}
try {
window.__archiUpdateFollow?.();
} catch {}
});
};
const schedule = () => {
if (raf) return;
raf = requestAnimationFrame(() => {
raf = 0;
syncAll();
});
};
heroToggle?.addEventListener("click", expandHero);
const onScroll = () => {
maybeAutoCollapseOnScroll();
schedule();
};
const followObserver = new MutationObserver(schedule);
followObserver.observe(follow, {
attributes: true,
attributeFilter: ["class", "style", "aria-hidden"],
subtree: false,
});
const heroResizeObserver =
typeof ResizeObserver !== "undefined"
? new ResizeObserver(schedule)
: null;
heroResizeObserver?.observe(hero);
window.addEventListener("scroll", onScroll, { passive: true });
window.addEventListener("resize", () => {
heroPinStartY = null;
applyCompactHeroRail();
applyCompactFollowRail();
schedule();
});
window.addEventListener("pageshow", () => {
heroPinStartY = null;
applyCompactHeroRail();
applyCompactFollowRail();
captureHeroPinStart(true);
schedule();
});
if (document.fonts?.ready) {
document.fonts.ready.then(schedule).catch(() => {});
}
if (mqMobile.addEventListener) {
mqMobile.addEventListener("change", schedule);
} else if (mqMobile.addListener) {
mqMobile.addListener(schedule);
}
if (mqSmallLandscape.addEventListener) {
mqSmallLandscape.addEventListener("change", schedule);
} else if (mqSmallLandscape.addListener) {
mqSmallLandscape.addListener(schedule);
}
applyCompactHeroRail();
applyCompactFollowRail();
captureHeroPinStart(true);
schedule();
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot, { once: true });
} else {
boot();
}
})();
</script>
<style>
:global(body.is-glossary-portal-page #reading-follow){
z-index: 10;
}
/* Le hero se condense dès quil devient sticky */
:global(body.is-glossary-portal-page.glossary-portal-hero-condensed .glossary-portal-hero){
padding:
var(--portal-hero-pad-top-condensed, 14px)
var(--portal-hero-pad-x-condensed, 16px)
var(--portal-hero-pad-bottom-condensed, 16px);
row-gap: var(--portal-hero-gap-condensed, 10px);
box-shadow:
inset 0 1px 0 rgba(255,255,255,0.02),
0 8px 20px rgba(0,0,0,0.12);
}
:global(body.is-glossary-portal-page.glossary-portal-hero-condensed .glossary-portal-hero h1){
font-size: var(--portal-hero-h1-size-condensed, clamp(2.05rem, 3.15vw, 2.7rem));
line-height: var(--portal-hero-h1-lh-condensed, 1);
letter-spacing: var(--portal-hero-h1-spacing-condensed, -.04em);
}
:global(body.is-glossary-portal-page.glossary-portal-hero-condensed .glossary-portal-hero__intro){
max-width: var(--portal-hero-follow-intro-max-w, 62ch);
font-size: var(--portal-hero-intro-size-condensed, .98rem);
line-height: var(--portal-hero-intro-lh-condensed, 1.46);
}
:global(body.is-glossary-portal-page.glossary-portal-hero-condensed .glossary-portal-hero__kicker){
opacity: .68;
}
/* Le more se replie dès létat condensé */
:global(body.is-glossary-portal-page.glossary-portal-hero-condensed:not(.glossary-portal-hero-expanded) .glossary-portal-hero__more){
max-height: 0;
opacity: 0;
overflow: hidden;
pointer-events: none;
}
:global(body.is-glossary-portal-page.glossary-portal-hero-condensed:not(.glossary-portal-hero-expanded) .glossary-portal-hero__toggle){
display: inline-flex;
}
/* Laccolage hero + follow narrive que quand le follow est actif */
:global(body.is-glossary-portal-page.glossary-portal-hero-condensed.glossary-portal-follow-on .glossary-portal-hero){
margin-bottom: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
:global(body.is-glossary-portal-page.glossary-portal-follow-on #reading-follow .reading-follow__inner){
margin-top: -1px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
:global(body.is-glossary-portal-page.glossary-portal-follow-on #reading-follow .rf-h2){
letter-spacing: -.02em;
}
:global(body.is-glossary-portal-page .glossary-portal-section__head.is-sticky),
:global(body.is-glossary-portal-page .glossary-portal-section__head[data-sticky-active="true"]){
position: static !important;
top: auto !important;
z-index: auto !important;
padding: 0 !important;
border: 0 !important;
background: transparent !important;
box-shadow: none !important;
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
}
@media (max-width: 860px){
:global(body.is-glossary-portal-page:not(.glossary-portal-compact-sticky):not(.is-index-complet-page) #reading-follow),
:global(body.is-glossary-portal-page:not(.glossary-portal-compact-sticky):not(.is-index-complet-page) #reading-follow .reading-follow__inner){
display: none !important;
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
:global(body.is-glossary-portal-page:not(.glossary-portal-compact-sticky):not(.is-index-complet-page)){
--followbar-h: 0px !important;
--sticky-offset-px: calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px)) !important;
}
:global(body.is-glossary-portal-page:not(.glossary-portal-compact-sticky):not(.is-index-complet-page) .glossary-portal-hero){
margin-bottom: var(--portal-hero-margin-bottom, 18px);
border-radius: 20px !important;
box-shadow: none !important;
}
:global(body.is-glossary-portal-page:not(.glossary-portal-compact-sticky):not(.is-index-complet-page) .glossary-portal-hero__more){
max-height: none !important;
opacity: 1 !important;
overflow: visible !important;
pointer-events: auto !important;
}
:global(body.is-glossary-portal-page:not(.glossary-portal-compact-sticky):not(.is-index-complet-page) .glossary-portal-hero__toggle){
display: none !important;
}
}
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
:global(body.is-glossary-portal-page:not(.glossary-portal-compact-sticky):not(.is-index-complet-page) #reading-follow),
:global(body.is-glossary-portal-page:not(.glossary-portal-compact-sticky):not(.is-index-complet-page) #reading-follow .reading-follow__inner){
display: none !important;
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
:global(body.is-glossary-portal-page:not(.glossary-portal-compact-sticky):not(.is-index-complet-page)){
--followbar-h: 0px !important;
--sticky-offset-px: calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px)) !important;
}
:global(body.is-glossary-portal-page:not(.glossary-portal-compact-sticky):not(.is-index-complet-page) .glossary-portal-hero){
margin-bottom: var(--portal-hero-margin-bottom, 12px);
border-radius: 16px !important;
box-shadow: none !important;
}
:global(body.is-glossary-portal-page:not(.glossary-portal-compact-sticky):not(.is-index-complet-page) .glossary-portal-hero__more){
max-height: none !important;
opacity: 1 !important;
overflow: visible !important;
pointer-events: auto !important;
}
:global(body.is-glossary-portal-page:not(.glossary-portal-compact-sticky):not(.is-index-complet-page) .glossary-portal-hero__toggle){
display: none !important;
}
}
/* =========================================================
Index complet — exception premium compacte portal
---------------------------------------------------------
Les portails ordinaires restent neutralisés en compact.
Lindex complet, lui, conserve la pile :
hero sticky → reading-follow → lettres flottantes.
========================================================= */
@media (max-width: 980px){
:global(body.is-index-complet-page[data-edition-key="glossaire"][data-sticky-mode="glossary-portal"]) .glossary-portal-hero,
:global(body.is-index-complet-page[data-edition-key="glossaire"][data-sticky-mode="glossary-portal"]) .glossary-page-hero{
position: sticky !important;
top: var(--glossary-sticky-top, calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px))) !important;
z-index: 72 !important;
margin-bottom: 0 !important;
overflow: hidden !important;
transform: translateZ(0) !important;
backface-visibility: hidden !important;
border-bottom-left-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
:global(body.is-index-complet-page #reading-follow){
display: block !important;
position: fixed !important;
left: var(--reading-left) !important;
width: var(--reading-width) !important;
top: calc(var(--glossary-sticky-top, var(--sticky-header-h, 0px)) + var(--glossary-local-sticky-h, 0px) - var(--followbar-h, 0px) - 1px) !important;
z-index: 71 !important;
visibility: hidden !important;
opacity: 0 !important;
pointer-events: none !important;
}
:global(body.is-index-complet-page.glossary-portal-follow-on #reading-follow){
visibility: visible !important;
opacity: 1 !important;
}
:global(body.is-index-complet-page #reading-follow .reading-follow__inner){
display: block !important;
visibility: visible !important;
opacity: 1 !important;
max-height: none !important;
margin-top: -1px !important;
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
}
:global(body.is-index-complet-page #gic-follow-letters){
position: fixed !important;
left: var(--reading-left) !important;
width: var(--reading-width) !important;
top: calc(var(--glossary-sticky-top, var(--sticky-header-h, 0px)) + var(--gic-premium-hero-h, 0px) + var(--gic-premium-follow-h, 0px) - 1px) !important;
z-index: 70 !important;
}
:global(body.is-index-complet-page.gic-letters-docked #gic-follow-letters){
display: flex !important;
visibility: visible !important;
opacity: 1 !important;
pointer-events: auto !important;
}
}
/* PORTAL_COMPACT_STICKY_CLEAN_START
Portails glossaire — comportement compact générique final.
- Aucune condensation en haut de page.
- Condensation seulement après seuil documentaire réel.
- Hero sticky sous ledition-bar.
- reading-follow collé sous le hero condensé.
- index-complet exclu.
*/
@media (min-width: 981px){
:global(body.is-glossary-portal-page:not(.glossary-portal-hero-condensed) #reading-follow),
:global(body.is-glossary-portal-page:not(.glossary-portal-hero-condensed) #reading-follow .reading-follow__inner){
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
}
@media (max-width: 980px){
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) .glossary-portal-hero),
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) .glossary-page-hero){
position: sticky !important;
top: var(--glossary-sticky-top, calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px))) !important;
z-index: 72 !important;
display: grid !important;
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
overflow: hidden !important;
transform: translateZ(0) !important;
backface-visibility: hidden !important;
background:
linear-gradient(rgba(0,0,0,.72), rgba(0,0,0,.96)),
radial-gradient(780px 220px at 18% 0%, rgba(0,217,255,.08), transparent 60%) !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page):not(.glossary-portal-hero-condensed) #reading-follow),
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page):not(.glossary-portal-hero-condensed) #reading-follow .reading-follow__inner),
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page):not(.glossary-portal-follow-on) #reading-follow),
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page):not(.glossary-portal-follow-on) #reading-follow .reading-follow__inner){
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-hero-condensed .glossary-portal-hero),
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-hero-condensed .glossary-page-hero){
padding: 8px 10px 9px !important;
row-gap: 4px !important;
border-radius: 16px 16px 0 0 !important;
margin-bottom: 0 !important;
box-shadow: 0 12px 30px rgba(0,0,0,.24) !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-hero-condensed .glossary-portal-hero__kicker){
font-size: 9px !important;
line-height: 1.05 !important;
letter-spacing: .11em !important;
opacity: .72 !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-hero-condensed .glossary-portal-hero h1){
font-size: clamp(1.35rem, 5.1vw, 1.72rem) !important;
line-height: 1.02 !important;
letter-spacing: -.03em !important;
margin: 0 !important;
max-width: 100% !important;
white-space: normal !important;
overflow: visible !important;
text-overflow: clip !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-hero-condensed:not(.glossary-portal-hero-expanded) .glossary-portal-hero__intro--lead){
display: -webkit-box !important;
-webkit-box-orient: vertical !important;
-webkit-line-clamp: 2 !important;
line-clamp: 2 !important;
max-height: calc(2 * 1.12em) !important;
overflow: hidden !important;
font-size: .72rem !important;
line-height: 1.12 !important;
opacity: .78 !important;
margin: 0 !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-hero-condensed:not(.glossary-portal-hero-expanded) .glossary-portal-hero__more){
max-height: 0 !important;
opacity: 0 !important;
overflow: hidden !important;
pointer-events: none !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-hero-condensed .glossary-portal-hero__toggle){
min-height: 22px !important;
padding: 2px 0 !important;
font-size: 11px !important;
line-height: 1.05 !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) #reading-follow){
display: block !important;
position: fixed !important;
left: var(--reading-left) !important;
width: var(--reading-width) !important;
top: calc(
var(--glossary-sticky-top, calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px)))
+ var(--glossary-local-sticky-h, 0px)
- 1px
) !important;
z-index: 71 !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-follow-on #reading-follow),
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-follow-on #reading-follow .reading-follow__inner){
opacity: 1 !important;
visibility: visible !important;
pointer-events: auto !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) #reading-follow .reading-follow__inner){
min-height: 26px !important;
padding: 5px 10px 6px !important;
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
border-bottom-left-radius: 13px !important;
border-bottom-right-radius: 13px !important;
border-top: 1px solid rgba(127,127,127,.16) !important;
background:
linear-gradient(180deg, rgba(0,0,0,.80), rgba(0,0,0,.94)),
radial-gradient(620px 120px at 18% 0%, rgba(0,217,255,.055), transparent 60%) !important;
box-shadow: 0 10px 22px rgba(0,0,0,.18) !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) #reading-follow .rf-h1),
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) #reading-follow .rf-h3),
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) #reading-follow .rf-actions){
display: none !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) #reading-follow .rf-h2){
display: block !important;
font-size: .86rem !important;
line-height: 1.08 !important;
font-weight: 800 !important;
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
}
}
@media (min-width: 761px) and (max-width: 980px){
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-hero-condensed .glossary-portal-hero),
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-hero-condensed .glossary-page-hero){
padding: 8px 12px 8px !important;
border-radius: 16px 16px 0 0 !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-hero-condensed .glossary-portal-hero h1){
font-size: clamp(1.48rem, 3vw, 1.82rem) !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-hero-condensed:not(.glossary-portal-hero-expanded) .glossary-portal-hero__intro--lead){
font-size: .76rem !important;
line-height: 1.14 !important;
max-height: calc(2 * 1.14em) !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) #reading-follow .rf-h2){
font-size: .9rem !important;
}
}
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-hero-condensed .glossary-portal-hero),
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-hero-condensed .glossary-page-hero){
padding: 5px 8px 5px !important;
row-gap: 2px !important;
border-radius: 12px 12px 0 0 !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-hero-condensed .glossary-portal-hero h1){
font-size: clamp(1rem, 3vw, 1.22rem) !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page).glossary-portal-hero-condensed:not(.glossary-portal-hero-expanded) .glossary-portal-hero__intro--lead){
display: none !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) #reading-follow .reading-follow__inner){
min-height: 22px !important;
padding: 4px 8px 5px !important;
border-bottom-left-radius: 11px !important;
border-bottom-right-radius: 11px !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) #reading-follow .rf-h2){
font-size: .78rem !important;
line-height: 1.04 !important;
}
}
/* PORTAL_COMPACT_STICKY_CLEAN_END */
/* PORTAL_COMPACT_STICKY_OVERFLOW_FIX_START
Libération du contexte sticky sur les portails glossaire compacts.
Le hero ne peut pas coller sous ledition-bar si un ancêtre le piège
dans un overflow/contain compact.
*/
@media (max-width: 980px){
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) .page),
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) .page-shell),
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) .reading),
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) article.reading),
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) main){
overflow: visible !important;
overflow-x: visible !important;
overflow-y: visible !important;
contain: none !important;
transform: none !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) .glossary-portal-hero){
align-self: start !important;
justify-self: stretch !important;
position: sticky !important;
top: var(--glossary-sticky-top, calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px))) !important;
z-index: 72 !important;
}
}
/* PORTAL_COMPACT_STICKY_OVERFLOW_FIX_END */
/* PORTAL_COMPACT_FOLLOW_TWO_LINES_START
Le follow compact des portails génériques affiche deux niveaux :
section active + sous-tête interne active.
*/
@media (max-width: 980px){
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) #reading-follow .rf-h2){
display: block !important;
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) #reading-follow .rf-h3){
display: block !important;
margin-top: 1px !important;
font-size: .68rem !important;
line-height: 1.05 !important;
letter-spacing: .01em !important;
opacity: .72 !important;
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
}
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) #reading-follow .reading-follow__inner){
padding-top: 5px !important;
padding-bottom: 6px !important;
}
}
@media (max-width: 520px){
:global(body.is-glossary-portal-page.glossary-portal-compact-sticky:not(.is-index-complet-page) #reading-follow .rf-h3){
font-size: .64rem !important;
line-height: 1.02 !important;
}
}
/* PORTAL_COMPACT_FOLLOW_TWO_LINES_END */
</style>