Compare commits

...

9 Commits

Author SHA1 Message Date
e6c18d6b16 refactor(glossaire): componentize glossary entry page
All checks were successful
SMOKE / smoke (push) Successful in 5s
CI / build-and-anchors (push) Successful in 45s
CI / build-and-anchors (pull_request) Successful in 44s
2026-03-25 19:18:58 +01:00
a3092f5d5b Merge pull request 'refactor(glossaire): componentize glossary home sections' (#295) from refactor/glossaire-home-componentization into main
All checks were successful
Proposer Apply (Queue) / apply-proposer (push) Successful in 32s
Deploy staging+live (annotations) / deploy (push) Successful in 11m41s
SMOKE / smoke (push) Successful in 5s
CI / build-and-anchors (push) Successful in 46s
Reviewed-on: #295
2026-03-25 18:30:14 +01:00
7187b69935 refactor(glossaire): componentize glossary home sections
All checks were successful
SMOKE / smoke (push) Successful in 4s
CI / build-and-anchors (push) Successful in 45s
CI / build-and-anchors (pull_request) Successful in 48s
2026-03-25 18:26:43 +01:00
4ba4453661 Merge pull request 'refactor(glossaire): centralize aside and home data' (#294) from feat/glossaire-relational-asides-and-home into main
All checks were successful
Proposer Apply (Queue) / apply-proposer (push) Successful in 21s
CI / build-and-anchors (push) Successful in 42s
SMOKE / smoke (push) Successful in 7s
Deploy staging+live (annotations) / deploy (push) Successful in 9m5s
Reviewed-on: #294
2026-03-25 16:50:31 +01:00
ee42e391e3 refactor(glossaire): centralize aside and home data
All checks were successful
SMOKE / smoke (push) Successful in 5s
CI / build-and-anchors (push) Successful in 48s
CI / build-and-anchors (pull_request) Successful in 43s
2026-03-25 16:48:43 +01:00
f7756be59e Merge pull request 'feat/glossaire-entry-relations-rendering' (#293) from feat/glossaire-entry-relations-rendering into main
All checks were successful
Proposer Apply (Queue) / apply-proposer (push) Successful in 27s
CI / build-and-anchors (push) Successful in 42s
SMOKE / smoke (push) Successful in 6s
Deploy staging+live (annotations) / deploy (push) Successful in 9m40s
Reviewed-on: #293
2026-03-25 15:39:41 +01:00
4abe70e10e refactor(glossaire): extract entry relations rendering
All checks were successful
SMOKE / smoke (push) Successful in 4s
CI / build-and-anchors (push) Successful in 42s
CI / build-and-anchors (pull_request) Successful in 41s
2026-03-25 15:30:51 +01:00
b2b4ec35c0 refactor(glossaire): preserve editorial order for entry relations 2026-03-25 15:20:39 +01:00
b255436958 Merge pull request 'refactor(glossaire): centralize glossary relation helpers' (#292) from feat/glossaire-ui-relations-foundation into main
All checks were successful
Proposer Apply (Queue) / apply-proposer (push) Successful in 24s
Deploy staging+live (annotations) / deploy (push) Successful in 9m2s
SMOKE / smoke (push) Successful in 5s
CI / build-and-anchors (push) Successful in 43s
Reviewed-on: #292
2026-03-25 14:17:11 +01:00
14 changed files with 1389 additions and 998 deletions

View File

@@ -1,14 +1,7 @@
---
import {
familyOf,
getContextualTheory,
getDisplayDomain,
getDisplayFamily,
getDisplayLevel,
getEntriesOfSameFamily,
getFondamentaux,
getRelationSections,
getSameFamilyTitle,
getGlossaryEntryAsideData,
getGlossaryPortalLinks,
hrefOfGlossaryEntry,
slugOfGlossaryEntry,
} from "../lib/glossary";
@@ -19,28 +12,21 @@ const {
} = Astro.props;
const currentSlug = slugOfGlossaryEntry(currentEntry);
const currentFamily = familyOf(currentEntry);
const fondamentaux = getFondamentaux(allEntries);
const {
displayFamily,
displayDomain,
displayLevel,
showNoyau,
showSameFamily,
fondamentaux,
sameFamilyTitle,
sameFamilyEntries,
relationSections,
contextualTheory,
} = getGlossaryEntryAsideData(currentEntry, allEntries);
const displayFamily = getDisplayFamily(currentEntry);
const displayDomain = getDisplayDomain(currentEntry);
const displayLevel = getDisplayLevel(currentEntry);
const sameFamilyEntries = getEntriesOfSameFamily(currentEntry, allEntries);
const sameFamilyTitle = getSameFamilyTitle(currentEntry);
const contextualTheory = getContextualTheory(currentEntry, allEntries);
const showNoyau =
currentFamily !== "concept-fondamental" &&
fondamentaux.length > 0;
const showSameFamily =
sameFamilyEntries.length > 0 &&
currentFamily !== "concept-fondamental";
const relationSections = getRelationSections(currentEntry, allEntries);
const portalLinks = getGlossaryPortalLinks();
---
<nav class="glossary-aside" aria-label="Navigation du glossaire">
@@ -52,20 +38,23 @@ const relationSections = getRelationSections(currentEntry, allEntries);
<span class="glossary-aside__pill glossary-aside__pill--family">
{displayFamily}
</span>
<span class="glossary-aside__pill">{displayDomain}</span>
<span class="glossary-aside__pill">{displayLevel}</span>
{displayDomain && (
<span class="glossary-aside__pill">{displayDomain}</span>
)}
{displayLevel && (
<span class="glossary-aside__pill">{displayLevel}</span>
)}
</div>
</div>
<section class="glossary-aside__block">
<h2 class="glossary-aside__heading">Portails</h2>
<ul class="glossary-aside__list">
<li><a href="/glossaire/">Accueil du glossaire</a></li>
<li><a href="/glossaire/index-complet/">Index complet</a></li>
<li><a href="/glossaire/paradigme-archicratique/">Paradigme archicratique</a></li>
<li><a href="/glossaire/archicrations/">Archicrations</a></li>
<li><a href="/glossaire/paradigmes/">Paradigmes et doctrines</a></li>
<li><a href="/glossaire/tensions-irreductibles/">Tensions irréductibles</a></li>
{portalLinks.map((item) => (
<li><a href={item.href}>{item.label}</a></li>
))}
</ul>
</section>

View File

@@ -0,0 +1,83 @@
---
import { hrefOfGlossaryEntry, type GlossaryEntry } from "../lib/glossary";
export interface Props {
entries?: GlossaryEntry[];
wide?: boolean;
}
const {
entries = [],
wide = false,
} = Astro.props;
---
<div class="glossary-cards">
{entries.map((entry) => (
<a
class:list={[
"glossary-card",
wide && "glossary-card--wide",
]}
href={hrefOfGlossaryEntry(entry)}
>
<strong>{entry.data.term}</strong>
<span>{entry.data.definitionShort}</span>
</a>
))}
</div>
<style>
.glossary-cards{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 12px;
margin-top: 14px;
}
.glossary-card{
display: flex;
flex-direction: column;
gap: 8px;
padding: 14px 16px;
border: 1px solid var(--glossary-border);
border-radius: 18px;
background: var(--glossary-bg-soft);
text-decoration: none;
transition: transform 120ms ease, background 120ms ease, border-color 120ms ease;
}
.glossary-card:hover{
transform: translateY(-1px);
background: var(--glossary-bg-soft-strong);
border-color: rgba(0,217,255,0.16);
text-decoration: none;
}
.glossary-card--wide{
grid-column: 1 / -1;
}
.glossary-card strong{
color: var(--glossary-accent);
font-size: 1.04rem;
line-height: 1.28;
}
.glossary-card span{
color: inherit;
font-size: 1rem;
line-height: 1.5;
opacity: .94;
}
@media (prefers-color-scheme: dark){
.glossary-card{
background: rgba(255,255,255,0.04);
}
.glossary-card:hover{
background: rgba(255,255,255,0.07);
}
}
</style>

View File

@@ -0,0 +1,16 @@
<div class="glossary-entry-body">
<slot />
</div>
<style>
.glossary-entry-body{
margin-bottom: 28px;
}
:global(.glossary-entry-body h2),
:global(.glossary-entry-body h3),
:global(.glossary-relations h2),
:global(.glossary-relations h3){
scroll-margin-top: calc(var(--sticky-offset-px, 96px) + 18px);
}
</style>

View File

@@ -0,0 +1,203 @@
---
interface Props {
term: string;
definitionShort: string;
displayFamily: string;
displayDomain?: string;
displayLevel?: string;
mobilizedAuthors?: string[];
comparisonTraditions?: string[];
}
const {
term,
definitionShort,
displayFamily,
displayDomain = "",
displayLevel = "",
mobilizedAuthors = [],
comparisonTraditions = [],
} = Astro.props;
const hasScholarlyMeta =
mobilizedAuthors.length > 0 ||
comparisonTraditions.length > 0;
---
<header class="glossary-entry-head" data-ge-hero>
<div class="glossary-entry-head__title">
<h1>{term}</h1>
</div>
<div class="glossary-entry-summary">
<p class="glossary-entry-dek">
<em>{definitionShort}</em>
</p>
<div class="glossary-entry-signals" aria-label="Repères de lecture">
<span class="glossary-pill glossary-pill--family">
<strong>Famille :</strong> {displayFamily}
</span>
{displayDomain && (
<span class="glossary-pill">
<strong>Domaine :</strong> {displayDomain}
</span>
)}
{displayLevel && (
<span class="glossary-pill">
<strong>Niveau :</strong> {displayLevel}
</span>
)}
</div>
{hasScholarlyMeta && (
<div class="glossary-entry-meta">
{mobilizedAuthors.length > 0 && (
<p>
<strong>Auteurs mobilisés :</strong> {mobilizedAuthors.join(" / ")}
</p>
)}
{comparisonTraditions.length > 0 && (
<p>
<strong>Traditions de comparaison :</strong> {comparisonTraditions.join(" / ")}
</p>
)}
</div>
)}
</div>
</header>
<style>
.glossary-entry-head{
position: sticky;
top: calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px));
z-index: 11;
margin: 0 0 24px;
border: 1px solid rgba(127,127,127,0.18);
border-radius: 28px;
background:
linear-gradient(180deg, rgba(0,0,0,0.60), rgba(0,0,0,0.92)),
radial-gradient(900px 240px at 20% 0%, rgba(0,217,255,0.08), transparent 60%);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
overflow: hidden;
transition:
border-radius 180ms ease,
box-shadow 180ms ease,
border-color 180ms ease;
}
.glossary-entry-head__title{
padding: 18px 18px 16px;
}
.glossary-entry-head h1{
margin: 0;
font-size: clamp(2.2rem, 4vw, 3.15rem);
line-height: 1.02;
letter-spacing: -.04em;
font-weight: 850;
}
.glossary-entry-summary{
display: grid;
gap: 14px;
padding: 16px 18px 18px;
border-top: 1px solid rgba(127,127,127,0.14);
background: rgba(255,255,255,0.02);
}
.glossary-entry-dek{
margin: 0;
max-width: 76ch;
font-size: 1.04rem;
line-height: 1.55;
opacity: .94;
}
.glossary-entry-signals{
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 0;
}
.glossary-pill{
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 10px;
border: 1px solid rgba(127,127,127,0.24);
border-radius: 999px;
background: rgba(127,127,127,0.05);
font-size: 13px;
line-height: 1.35;
}
.glossary-pill--family{
border-color: rgba(127,127,127,0.36);
font-weight: 700;
}
.glossary-entry-meta{
margin: 0;
padding: 10px 12px;
border: 1px solid rgba(127,127,127,0.18);
border-radius: 12px;
background: rgba(127,127,127,0.04);
}
.glossary-entry-meta p{
margin: 0;
font-size: 14px;
line-height: 1.5;
}
.glossary-entry-meta p + p{
margin-top: 6px;
}
@media (max-width: 720px){
.glossary-entry-signals{
gap: 6px;
}
.glossary-pill{
font-size: 12px;
}
}
@media (max-width: 860px){
.glossary-entry-head{
position: static;
border-radius: 22px;
margin-bottom: 20px;
}
.glossary-entry-head__title{
padding: 14px 14px 12px;
}
.glossary-entry-summary{
gap: 12px;
padding: 14px;
}
.glossary-entry-dek{
max-width: none;
}
}
@media (prefers-color-scheme: dark){
.glossary-entry-meta{
background: rgba(255,255,255,0.03);
}
.glossary-pill{
background: rgba(255,255,255,0.04);
}
}
</style>

View File

@@ -0,0 +1,31 @@
---
interface Props {
canonicalHref: string;
term: string;
}
const { canonicalHref, term } = Astro.props;
---
<p class="glossary-legacy-note">
Cette entrée a été renommée. Lintitulé canonique est :
<a href={canonicalHref}>{term}</a>.
</p>
<style>
.glossary-legacy-note{
padding: 10px 12px;
border: 1px solid rgba(127,127,127,0.22);
border-radius: 12px;
background: rgba(127,127,127,0.05);
font-size: 14px;
line-height: 1.45;
margin-bottom: 18px;
}
@media (prefers-color-scheme: dark){
.glossary-legacy-note{
background: rgba(255,255,255,0.04);
}
}
</style>

View File

@@ -0,0 +1,172 @@
<script is:inline>
(() => {
const boot = () => {
const body = document.body;
const root = document.documentElement;
const hero = document.querySelector("[data-ge-hero]");
const follow = document.getElementById("reading-follow");
const mqMobile = window.matchMedia("(max-width: 860px)");
if (!body || !root || !hero || !follow) return;
const BODY_CLASS = "is-glossary-entry-page";
const FOLLOW_ON_CLASS = "glossary-entry-follow-on";
let lastHeight = -1;
let lastFollowOn = null;
let raf = 0;
body.classList.add(BODY_CLASS);
const heroHeight = () =>
Math.max(0, Math.round(hero.getBoundingClientRect().height || 0));
const computeFollowOn = () =>
!mqMobile.matches &&
follow.classList.contains("is-on") &&
follow.style.display !== "none" &&
follow.getAttribute("aria-hidden") !== "true";
const stripLocalSticky = () => {
document
.querySelectorAll(
".glossary-entry-body h2, .glossary-entry-body h3, .glossary-relations h2, .glossary-relations h3"
)
.forEach((el) => {
el.classList.remove("is-sticky");
el.removeAttribute("data-sticky-active");
});
};
const applyLocalStickyHeight = () => {
const h = mqMobile.matches ? 0 : heroHeight();
if (h === lastHeight) return;
lastHeight = 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) return;
lastFollowOn = on;
body.classList.toggle(FOLLOW_ON_CLASS, on);
};
const syncAll = () => {
stripLocalSticky();
syncFollowState();
applyLocalStickyHeight();
};
const schedule = () => {
if (raf) return;
raf = requestAnimationFrame(() => {
raf = 0;
syncAll();
});
};
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("resize", schedule);
window.addEventListener("pageshow", 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);
}
schedule();
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot, { once: true });
} else {
boot();
}
})();
</script>
<style>
:global(body.is-glossary-entry-page #reading-follow){
z-index: 10;
}
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-head){
margin-bottom: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-summary){
gap: 10px;
padding-top: 12px;
padding-bottom: 10px;
}
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-signals){
gap: 6px;
}
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-meta){
padding: 8px 10px;
}
:global(body.is-glossary-entry-page.glossary-entry-follow-on #reading-follow){
transform: translateY(-1px);
}
:global(body.is-glossary-entry-page.glossary-entry-follow-on #reading-follow .reading-follow__inner){
margin-top: -1px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
:global(body.is-glossary-entry-page .glossary-entry-body h2.is-sticky),
:global(body.is-glossary-entry-page .glossary-entry-body h2[data-sticky-active="true"]),
:global(body.is-glossary-entry-page .glossary-entry-body h3.is-sticky),
:global(body.is-glossary-entry-page .glossary-entry-body h3[data-sticky-active="true"]),
:global(body.is-glossary-entry-page .glossary-relations h2.is-sticky),
:global(body.is-glossary-entry-page .glossary-relations h2[data-sticky-active="true"]),
:global(body.is-glossary-entry-page .glossary-relations h3.is-sticky),
:global(body.is-glossary-entry-page .glossary-relations h3[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-entry-page.glossary-entry-follow-on .glossary-entry-head){
margin-bottom: 20px;
border-radius: 22px;
}
}
</style>

View File

@@ -1,8 +1,8 @@
---
import {
countGlossaryEntriesByFamily,
countGlossaryEntriesByKind,
getFondamentaux,
getGlossaryHomeStats,
getGlossaryPortalLinks,
hrefOfGlossaryEntry,
} from "../lib/glossary";
@@ -11,23 +11,14 @@ const {
} = Astro.props;
const fondamentaux = getFondamentaux(allEntries);
const portalLinks = getGlossaryPortalLinks();
const totalEntries = allEntries.length;
const paradigmesCount = countGlossaryEntriesByKind(allEntries, "paradigme");
const doctrinesCount = countGlossaryEntriesByKind(allEntries, "doctrine");
const metaRegimesCount = countGlossaryEntriesByFamily(allEntries, "meta-regime");
const portalLinks = [
{ href: "/glossaire/concepts-fondamentaux/", label: "Concepts fondamentaux" },
{ href: "/glossaire/index-complet/", label: "Index complet" },
{ href: "/glossaire/paradigme-archicratique/", label: "Paradigme archicratique" },
{ href: "/glossaire/scenes-archicratiques/", label: "Scènes archicratiques" },
{ href: "/glossaire/dynamiques-archicratiques/", label: "Dynamiques archicratiques" },
{ href: "/glossaire/tensions-irreductibles/", label: "Tensions irréductibles" },
{ href: "/glossaire/archicrations/", label: "Méta-régimes archicratiques" },
{ href: "/glossaire/paradigmes/", label: "Paradigmes et doctrines" },
{ href: "/glossaire/verbes-de-la-scene/", label: "Verbes de la scène" },
];
const {
totalEntries,
paradigmesCount,
doctrinesCount,
metaRegimesCount,
} = getGlossaryHomeStats(allEntries);
---
<nav class="glossary-home-aside" aria-label="Navigation du portail du glossaire">

View File

@@ -0,0 +1,103 @@
---
export interface Props {
kicker?: string;
title?: string;
intro?: string;
}
const {
kicker = "Référentiel terminologique",
title = "Glossaire archicratique",
intro = "Ce glossaire nest pas seulement un index de définitions. Il constitue une porte dentrée dans la pensée archicratique : une cartographie raisonnée des concepts fondamentaux, des scènes, des dynamiques et des méta-régimes à partir desquels une société peut être décrite comme organisation de tensions et recherche de co-viabilité.",
} = Astro.props;
---
<header class="glossary-hero" id="glossary-hero">
<p class="glossary-kicker">{kicker}</p>
<h1>{title}</h1>
<p class="glossary-intro">{intro}</p>
<h2
class="glossary-hero-follow"
id="glossary-hero-follow"
aria-hidden="true"
></h2>
</header>
<style>
.glossary-hero{
position: sticky;
top: var(--glossary-sticky-top);
z-index: 12;
margin-bottom: 28px;
padding: 14px 16px 18px;
border: 1px solid rgba(127,127,127,0.18);
border-radius: 28px;
background:
linear-gradient(180deg, rgba(0,0,0,0.60), rgba(0,0,0,0.90)),
radial-gradient(900px 240px at 20% 0%, rgba(0,217,255,0.08), transparent 60%);
transition:
background 300ms cubic-bezier(.22,.8,.22,1),
border-color 300ms cubic-bezier(.22,.8,.22,1),
box-shadow 300ms cubic-bezier(.22,.8,.22,1);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
display: grid;
row-gap: 12px;
}
.glossary-kicker{
margin: 0;
font-size: 12px;
letter-spacing: .12em;
text-transform: uppercase;
opacity: .72;
}
.glossary-hero h1{
margin: 0;
font-size: clamp(2.2rem, 4vw, 3.15rem);
line-height: 1.02;
letter-spacing: -.04em;
font-weight: 850;
}
.glossary-intro{
margin: 0;
max-width: 72ch;
font-size: 1.05rem;
line-height: 1.55;
opacity: .94;
}
.glossary-hero-follow{
margin: 2px 0 0;
min-height: var(--glossary-follow-height);
display: flex;
align-items: flex-end;
opacity: 0;
transform: translateY(10px) scale(.985);
filter: blur(6px);
transition:
opacity 220ms cubic-bezier(.22,1,.36,1),
transform 320ms cubic-bezier(.22,1,.36,1),
filter 320ms cubic-bezier(.22,1,.36,1);
pointer-events: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
will-change: opacity, transform, filter;
}
.glossary-hero-follow.is-visible{
opacity: 1;
transform: translateY(0) scale(1);
filter: blur(0);
}
@media (max-width: 760px){
.glossary-hero{
top: calc(var(--glossary-sticky-top) - 2px);
padding: 12px 14px 16px;
}
}
</style>

View File

@@ -0,0 +1,109 @@
---
export interface Props {
id?: string;
title: string;
intro?: string;
followSection?: string;
ctaHref?: string;
ctaLabel?: string;
}
const {
id,
title,
intro,
followSection,
ctaHref,
ctaLabel,
} = Astro.props;
const resolvedFollowSection = (followSection || title || "").trim();
const showCta = Boolean(ctaHref && ctaLabel);
---
<section id={id} class="glossary-section">
<div class="glossary-section__head">
<div>
<h2 data-follow-section={resolvedFollowSection}>{title}</h2>
{intro && (
<p class="glossary-intro">{intro}</p>
)}
</div>
{showCta && (
<a class="glossary-cta" href={ctaHref}>
{ctaLabel}
</a>
)}
</div>
<slot />
</section>
<style>
.glossary-section{
margin-top: 42px;
scroll-margin-top: calc(var(--glossary-sticky-top) + 190px);
}
.glossary-section__head{
display: flex;
justify-content: space-between;
align-items: start;
gap: 16px;
flex-wrap: wrap;
margin-bottom: 14px;
}
.glossary-section h2{
margin: 0;
font-size: clamp(2rem, 3vw, 2.55rem);
line-height: 1.06;
letter-spacing: -.03em;
font-weight: 800;
}
.glossary-intro{
margin: 0;
max-width: 72ch;
font-size: 1.05rem;
line-height: 1.55;
opacity: .94;
}
.glossary-section__head .glossary-intro{
margin-top: 10px;
}
.glossary-cta{
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 40px;
border: 1px solid var(--glossary-border-strong);
border-radius: 999px;
padding: 7px 14px;
color: var(--glossary-accent);
text-decoration: none;
white-space: nowrap;
transition: transform 120ms ease, background 120ms ease;
}
.glossary-cta:hover{
background: var(--glossary-bg-soft-strong);
text-decoration: none;
transform: translateY(-1px);
}
@media (max-width: 760px){
.glossary-section__head{
flex-direction: column;
align-items: stretch;
}
.glossary-cta{
width: fit-content;
}
}
</style>

View File

@@ -0,0 +1,91 @@
---
export type GlossaryPortalGridItem = {
href: string;
title: string;
description: string;
meta: string;
};
export interface Props {
items?: GlossaryPortalGridItem[];
secondary?: boolean;
}
const {
items = [],
secondary = false,
} = Astro.props;
---
<div
class:list={[
"glossary-portals",
secondary && "glossary-portals--secondary",
]}
>
{items.map((item) => (
<a class="glossary-portal-card" href={item.href}>
<strong>{item.title}</strong>
<span>{item.description}</span>
<small>{item.meta}</small>
</a>
))}
</div>
<style>
.glossary-portals{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 14px;
margin-top: 14px;
}
.glossary-portal-card{
display: flex;
flex-direction: column;
gap: 8px;
padding: 16px 18px;
border: 1px solid var(--glossary-border);
border-radius: 18px;
background: var(--glossary-bg-soft);
text-decoration: none;
transition: transform 120ms ease, background 120ms ease, border-color 120ms ease;
}
.glossary-portal-card:hover{
transform: translateY(-1px);
background: var(--glossary-bg-soft-strong);
border-color: rgba(0,217,255,0.16);
text-decoration: none;
}
.glossary-portal-card strong{
color: var(--glossary-accent);
font-size: 1.08rem;
line-height: 1.28;
}
.glossary-portal-card span{
color: inherit;
font-size: 1rem;
line-height: 1.5;
opacity: .94;
}
.glossary-portal-card small{
color: var(--glossary-accent);
font-size: .94rem;
line-height: 1.35;
opacity: .9;
}
@media (prefers-color-scheme: dark){
.glossary-portal-card{
background: rgba(255,255,255,0.04);
}
.glossary-portal-card:hover{
background: rgba(255,255,255,0.07);
}
}
</style>

View File

@@ -0,0 +1,89 @@
---
import type { GlossaryRelationBlock } from "../lib/glossary";
import { hrefOfGlossaryEntry } from "../lib/glossary";
interface Props {
relationBlocks: GlossaryRelationBlock[];
}
const { relationBlocks = [] } = Astro.props;
---
{relationBlocks.length > 0 && (
<section class="glossary-relations" aria-label="Relations conceptuelles">
<h2>Relations conceptuelles</h2>
<div class="glossary-relations-grid">
{relationBlocks.map((block) => (
<section class={`glossary-relations-card ${block.className}`}>
<h3>{block.title}</h3>
<ul>
{block.items.map((item) => (
<li>
<a href={hrefOfGlossaryEntry(item)}>{item.data.term}</a>
<span> — {item.data.definitionShort}</span>
</li>
))}
</ul>
</section>
))}
</div>
</section>
)}
<style>
.glossary-relations{
margin-top: 26px;
padding-top: 18px;
border-top: 1px solid rgba(127,127,127,0.18);
}
.glossary-relations h2{
margin-bottom: 14px;
}
.glossary-relations-grid{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 12px;
}
.glossary-relations-card{
border: 1px solid rgba(127,127,127,0.22);
border-radius: 16px;
padding: 14px 16px;
background: rgba(127,127,127,0.05);
}
.glossary-relations-card h3{
margin-top: 0;
margin-bottom: 10px;
font-size: 15px;
line-height: 1.35;
}
.glossary-relations-card ul{
margin: 0;
padding-left: 18px;
}
.glossary-relations-card li{
margin-bottom: 8px;
font-size: 14px;
line-height: 1.45;
}
.glossary-relations-card li:last-child{
margin-bottom: 0;
}
.glossary-relations-card span{
opacity: .9;
}
@media (prefers-color-scheme: dark){
.glossary-relations-card{
background: rgba(255,255,255,0.04);
}
}
</style>

View File

@@ -2,6 +2,53 @@ import type { CollectionEntry } from "astro:content";
export type GlossaryEntry = CollectionEntry<"glossaire">;
export type GlossaryPortalLink = {
href: string;
label: string;
};
export type GlossaryRelationSection = {
title: string;
items: GlossaryEntry[];
};
export type GlossaryRelationBlock = GlossaryRelationSection & {
className: string;
};
export type GlossaryHomeStats = {
totalEntries: number;
paradigmesCount: number;
doctrinesCount: number;
metaRegimesCount: number;
};
export type GlossaryEntryAsideData = {
displayFamily: string;
displayDomain: string;
displayLevel: string;
showNoyau: boolean;
showSameFamily: boolean;
fondamentaux: GlossaryEntry[];
sameFamilyTitle: string;
sameFamilyEntries: GlossaryEntry[];
relationSections: GlossaryRelationSection[];
contextualTheory: GlossaryEntry[];
};
export type GlossaryHomeData = {
fondamentaux: GlossaryEntry[];
scenes: GlossaryEntry[];
dynamiques: GlossaryEntry[];
metaRegimes: GlossaryEntry[];
metaRegimesPreview: GlossaryEntry[];
arcalite?: GlossaryEntry;
cratialite?: GlossaryEntry;
tension?: GlossaryEntry;
sceneDepreuve?: GlossaryEntry;
archicration?: GlossaryEntry;
};
export const GLOSSARY_COLLATOR = new Intl.Collator("fr", {
sensitivity: "base",
numeric: true,
@@ -75,6 +122,34 @@ export const FAMILY_SECTION_TITLES: Record<string, string> = {
epistemologie: "Outillage épistémologique",
};
const PREFERRED_PARADIGME_SLUGS = [
"gouvernementalite",
"gouvernementalite-algorithmique",
"cybernetique",
"biopolitique",
"domination-legale-rationnelle",
"democratie-deliberative",
"gouvernance-des-communs",
"agencement-machinique",
"pharmacologie-technique",
"preemption-algorithmique",
"dissensus-politique",
"lieu-vide-du-pouvoir",
"habitus-et-violence-symbolique",
"theorie-de-la-resonance",
"conatus-et-multitude",
"configuration-et-interdependance",
"technodiversite-et-cosmotechnie",
"grammatisation-et-proletarisation-cognitive",
] as const;
const PREFERRED_DOCTRINE_SLUGS = [
"contractualisme-hobbesien",
"droit-naturel-et-propriete",
"volonte-generale",
"decisionnisme-souverain",
] as const;
export function normalizeGlossarySlug(value: unknown): string {
return String(value ?? "")
.trim()
@@ -99,9 +174,7 @@ export function hrefOfGlossaryEntry(
export function buildGlossaryBySlug(
entries: GlossaryEntry[] = [],
): Map<string, GlossaryEntry> {
return new Map(
entries.map((entry) => [slugOfGlossaryEntry(entry), entry]),
);
return new Map(entries.map((entry) => [slugOfGlossaryEntry(entry), entry]));
}
export function sortGlossaryEntries(
@@ -128,17 +201,35 @@ export function uniqueGlossaryEntries(
return out;
}
export function resolveGlossaryEntries(
export function resolveGlossaryEntriesInSourceOrder(
slugs: string[] = [],
allEntries: GlossaryEntry[] = [],
): GlossaryEntry[] {
const bySlug = buildGlossaryBySlug(allEntries);
const seen = new Set<string>();
const resolved: GlossaryEntry[] = [];
const resolved = slugs
.map((slug) => bySlug.get(normalizeGlossarySlug(slug)))
.filter(Boolean) as GlossaryEntry[];
for (const rawSlug of slugs) {
const slug = normalizeGlossarySlug(rawSlug);
if (!slug || seen.has(slug)) continue;
return sortGlossaryEntries(uniqueGlossaryEntries(resolved));
const entry = bySlug.get(slug);
if (!entry) continue;
seen.add(slug);
resolved.push(entry);
}
return resolved;
}
export function resolveGlossaryEntries(
slugs: string[] = [],
allEntries: GlossaryEntry[] = [],
): GlossaryEntry[] {
return sortGlossaryEntries(
resolveGlossaryEntriesInSourceOrder(slugs, allEntries),
);
}
export function rawFamilyOf(
@@ -170,7 +261,11 @@ export function familyOf(
if (slug === "autarchicratie") return "pathologie";
if (slug === "obliteration-archicratique") return "dynamique";
if (FONDAMENTAUX_WANTED.includes(slug as (typeof FONDAMENTAUX_WANTED)[number])) {
if (
FONDAMENTAUX_WANTED.includes(
slug as (typeof FONDAMENTAUX_WANTED)[number],
)
) {
return "concept-fondamental";
}
@@ -241,6 +336,47 @@ export function countGlossaryEntriesByFamily(
return entries.filter((entry) => familyOf(entry) === familyKey).length;
}
export function getGlossaryPortalLinks(): GlossaryPortalLink[] {
return [
{ href: "/glossaire/", label: "Accueil du glossaire" },
{ href: "/glossaire/concepts-fondamentaux/", label: "Concepts fondamentaux" },
{ href: "/glossaire/index-complet/", label: "Index complet" },
{
href: "/glossaire/paradigme-archicratique/",
label: "Paradigme archicratique",
},
{
href: "/glossaire/scenes-archicratiques/",
label: "Scènes archicratiques",
},
{
href: "/glossaire/dynamiques-archicratiques/",
label: "Dynamiques archicratiques",
},
{
href: "/glossaire/tensions-irreductibles/",
label: "Tensions irréductibles",
},
{
href: "/glossaire/archicrations/",
label: "Méta-régimes archicratiques",
},
{ href: "/glossaire/paradigmes/", label: "Paradigmes et doctrines" },
{ href: "/glossaire/verbes-de-la-scene/", label: "Verbes de la scène" },
];
}
export function getGlossaryHomeStats(
allEntries: GlossaryEntry[] = [],
): GlossaryHomeStats {
return {
totalEntries: allEntries.length,
paradigmesCount: countGlossaryEntriesByKind(allEntries, "paradigme"),
doctrinesCount: countGlossaryEntriesByKind(allEntries, "doctrine"),
metaRegimesCount: countGlossaryEntriesByFamily(allEntries, "meta-regime"),
};
}
export function getEntriesOfSameFamily(
entry: GlossaryEntry,
allEntries: GlossaryEntry[] = [],
@@ -261,19 +397,26 @@ export function getSameFamilyTitle(
return FAMILY_SECTION_TITLES[familyOf(entry)] ?? "Même famille";
}
export type GlossaryRelationBlock = {
title: string;
items: GlossaryEntry[];
className: string;
};
export function getRelationBlocks(
entry: GlossaryEntry,
allEntries: GlossaryEntry[] = [],
): GlossaryRelationBlock[] {
const relatedEntries = resolveGlossaryEntries(entry.data.related ?? [], allEntries);
const opposedEntries = resolveGlossaryEntries(entry.data.opposedTo ?? [], allEntries);
const seeAlsoEntries = resolveGlossaryEntries(entry.data.seeAlso ?? [], allEntries);
const currentSlug = slugOfGlossaryEntry(entry);
const relatedEntries = resolveGlossaryEntriesInSourceOrder(
entry.data.related ?? [],
allEntries,
).filter((item) => slugOfGlossaryEntry(item) !== currentSlug);
const opposedEntries = resolveGlossaryEntriesInSourceOrder(
entry.data.opposedTo ?? [],
allEntries,
).filter((item) => slugOfGlossaryEntry(item) !== currentSlug);
const seeAlsoEntries = resolveGlossaryEntriesInSourceOrder(
entry.data.seeAlso ?? [],
allEntries,
).filter((item) => slugOfGlossaryEntry(item) !== currentSlug);
return [
{
@@ -297,7 +440,7 @@ export function getRelationBlocks(
export function getRelationSections(
entry: GlossaryEntry,
allEntries: GlossaryEntry[] = [],
): Array<{ title: string; items: GlossaryEntry[] }> {
): GlossaryRelationSection[] {
return getRelationBlocks(entry, allEntries).map(({ title, items }) => ({
title,
items,
@@ -316,34 +459,6 @@ function isTheoryEntry(entry: GlossaryEntry): boolean {
);
}
const PREFERRED_PARADIGME_SLUGS = [
"gouvernementalite",
"gouvernementalite-algorithmique",
"cybernetique",
"biopolitique",
"domination-legale-rationnelle",
"democratie-deliberative",
"gouvernance-des-communs",
"agencement-machinique",
"pharmacologie-technique",
"preemption-algorithmique",
"dissensus-politique",
"lieu-vide-du-pouvoir",
"habitus-et-violence-symbolique",
"theorie-de-la-resonance",
"conatus-et-multitude",
"configuration-et-interdependance",
"technodiversite-et-cosmotechnie",
"grammatisation-et-proletarisation-cognitive",
] as const;
const PREFERRED_DOCTRINE_SLUGS = [
"contractualisme-hobbesien",
"droit-naturel-et-propriete",
"volonte-generale",
"decisionnisme-souverain",
] as const;
export function getContextualTheory(
entry: GlossaryEntry,
allEntries: GlossaryEntry[] = [],
@@ -352,15 +467,15 @@ export function getContextualTheory(
const bySlug = buildGlossaryBySlug(allEntries);
const fromRelations = uniqueGlossaryEntries([
...resolveGlossaryEntries(entry.data.related ?? [], allEntries),
...resolveGlossaryEntries(entry.data.seeAlso ?? [], allEntries),
...resolveGlossaryEntries(entry.data.opposedTo ?? [], allEntries),
...resolveGlossaryEntriesInSourceOrder(entry.data.related ?? [], allEntries),
...resolveGlossaryEntriesInSourceOrder(entry.data.seeAlso ?? [], allEntries),
...resolveGlossaryEntriesInSourceOrder(entry.data.opposedTo ?? [], allEntries),
])
.filter((item) => slugOfGlossaryEntry(item) !== currentSlug)
.filter((item) => isTheoryEntry(item));
if (fromRelations.length > 0) {
return sortGlossaryEntries(fromRelations).slice(0, 6);
return fromRelations.slice(0, 6);
}
if (familyOf(entry) === "paradigme") {
@@ -382,4 +497,65 @@ export function getContextualTheory(
}
return [];
}
}
export function getGlossaryEntryAsideData(
currentEntry: GlossaryEntry,
allEntries: GlossaryEntry[] = [],
): GlossaryEntryAsideData {
const currentFamily = familyOf(currentEntry);
const fondamentaux = getFondamentaux(allEntries);
const sameFamilyEntries = getEntriesOfSameFamily(currentEntry, allEntries);
const sameFamilyTitle = getSameFamilyTitle(currentEntry);
const relationSections = getRelationSections(currentEntry, allEntries);
const contextualTheory = getContextualTheory(currentEntry, allEntries);
const showNoyau =
currentFamily !== "concept-fondamental" &&
fondamentaux.length > 0;
const showSameFamily =
currentFamily !== "concept-fondamental" &&
sameFamilyEntries.length > 0;
return {
displayFamily: getDisplayFamily(currentEntry),
displayDomain: getDisplayDomain(currentEntry),
displayLevel: getDisplayLevel(currentEntry),
showNoyau,
showSameFamily,
fondamentaux,
sameFamilyTitle,
sameFamilyEntries: showSameFamily ? sameFamilyEntries : [],
relationSections,
contextualTheory,
};
}
export function getGlossaryHomeData(
entries: GlossaryEntry[] = [],
): GlossaryHomeData {
const bySlug = buildGlossaryBySlug(entries);
const fondamentaux = getGlossaryEntriesByFamily(entries, "concept-fondamental");
const scenes = getGlossaryEntriesByFamily(entries, "scene");
const dynamiques = sortGlossaryEntries(
entries.filter((entry) =>
["dynamique", "pathologie"].includes(familyOf(entry)),
),
);
const metaRegimes = getGlossaryEntriesByFamily(entries, "meta-regime");
return {
fondamentaux,
scenes,
dynamiques,
metaRegimes,
metaRegimesPreview: metaRegimes.slice(0, 6),
arcalite: bySlug.get("arcalite"),
cratialite: bySlug.get("cratialite"),
tension: bySlug.get("tension"),
sceneDepreuve: bySlug.get("scene-depreuve"),
archicration: bySlug.get("archicration"),
};
}

View File

@@ -1,13 +1,17 @@
---
import GlossaryLayout from "../../layouts/GlossaryLayout.astro";
import GlossaryAside from "../../components/GlossaryAside.astro";
import GlossaryRelationCards from "../../components/GlossaryRelationCards.astro";
import GlossaryEntryLegacyNote from "../../components/GlossaryEntryLegacyNote.astro";
import GlossaryEntryHero from "../../components/GlossaryEntryHero.astro";
import GlossaryEntryBody from "../../components/GlossaryEntryBody.astro";
import GlossaryEntryStickySync from "../../components/GlossaryEntryStickySync.astro";
import { getCollection, render } from "astro:content";
import {
getDisplayDomain,
getDisplayFamily,
getDisplayLevel,
getRelationBlocks,
hrefOfGlossaryEntry,
normalizeGlossarySlug,
} from "../../lib/glossary";
@@ -61,10 +65,6 @@ const relationBlocks = getRelationBlocks(entry, allEntries);
const displayFamily = getDisplayFamily(entry);
const displayDomain = getDisplayDomain(entry);
const displayLevel = getDisplayLevel(entry);
const hasScholarlyMeta =
(entry.data.mobilizedAuthors?.length ?? 0) > 0 ||
(entry.data.comparisonTraditions?.length ?? 0) > 0;
---
<GlossaryLayout
@@ -77,454 +77,27 @@ const hasScholarlyMeta =
</Fragment>
{isAliasRoute && (
<p class="glossary-legacy-note">
Cette entrée a été renommée. Lintitulé canonique est :
<a href={canonicalHref}>{entry.data.term}</a>.
</p>
<GlossaryEntryLegacyNote
canonicalHref={canonicalHref}
term={entry.data.term}
/>
)}
<header class="glossary-entry-head" data-ge-hero>
<div class="glossary-entry-head__title">
<h1>{entry.data.term}</h1>
</div>
<GlossaryEntryHero
term={entry.data.term}
definitionShort={entry.data.definitionShort}
displayFamily={displayFamily}
displayDomain={displayDomain}
displayLevel={displayLevel}
mobilizedAuthors={entry.data.mobilizedAuthors ?? []}
comparisonTraditions={entry.data.comparisonTraditions ?? []}
/>
<div class="glossary-entry-summary">
<p class="glossary-entry-dek">
<em>{entry.data.definitionShort}</em>
</p>
<div class="glossary-entry-signals" aria-label="Repères de lecture">
<span class="glossary-pill glossary-pill--family">
<strong>Famille :</strong> {displayFamily}
</span>
{displayDomain && (
<span class="glossary-pill">
<strong>Domaine :</strong> {displayDomain}
</span>
)}
{displayLevel && (
<span class="glossary-pill">
<strong>Niveau :</strong> {displayLevel}
</span>
)}
</div>
{hasScholarlyMeta && (
<div class="glossary-entry-meta">
{(entry.data.mobilizedAuthors?.length ?? 0) > 0 && (
<p>
<strong>Auteurs mobilisés :</strong> {entry.data.mobilizedAuthors.join(" / ")}
</p>
)}
{(entry.data.comparisonTraditions?.length ?? 0) > 0 && (
<p>
<strong>Traditions de comparaison :</strong> {entry.data.comparisonTraditions.join(" / ")}
</p>
)}
</div>
)}
</div>
</header>
<div class="glossary-entry-body">
<GlossaryEntryBody>
<Content />
</div>
</GlossaryEntryBody>
{relationBlocks.length > 0 && (
<section class="glossary-relations" aria-label="Relations conceptuelles">
<h2>Relations conceptuelles</h2>
<GlossaryRelationCards relationBlocks={relationBlocks} />
<div class="glossary-relations-grid">
{relationBlocks.map((block) => (
<section class={`glossary-relations-card ${block.className}`}>
<h3>{block.title}</h3>
<ul>
{block.items.map((item) => (
<li>
<a href={hrefOfGlossaryEntry(item)}>{item.data.term}</a>
<span> — {item.data.definitionShort}</span>
</li>
))}
</ul>
</section>
))}
</div>
</section>
)}
</GlossaryLayout>
<script is:inline>
(() => {
const boot = () => {
const body = document.body;
const root = document.documentElement;
const hero = document.querySelector("[data-ge-hero]");
const follow = document.getElementById("reading-follow");
const mqMobile = window.matchMedia("(max-width: 860px)");
if (!body || !root || !hero || !follow) return;
const BODY_CLASS = "is-glossary-entry-page";
const FOLLOW_ON_CLASS = "glossary-entry-follow-on";
let lastHeight = -1;
let lastFollowOn = null;
let raf = 0;
body.classList.add(BODY_CLASS);
const heroHeight = () =>
Math.max(0, Math.round(hero.getBoundingClientRect().height || 0));
const computeFollowOn = () =>
!mqMobile.matches &&
follow.classList.contains("is-on") &&
follow.style.display !== "none" &&
follow.getAttribute("aria-hidden") !== "true";
const stripLocalSticky = () => {
document
.querySelectorAll(
".glossary-entry-body h2, .glossary-entry-body h3, .glossary-relations h2, .glossary-relations h3"
)
.forEach((el) => {
el.classList.remove("is-sticky");
el.removeAttribute("data-sticky-active");
});
};
const applyLocalStickyHeight = () => {
const h = mqMobile.matches ? 0 : heroHeight();
if (h === lastHeight) return;
lastHeight = 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) return;
lastFollowOn = on;
body.classList.toggle(FOLLOW_ON_CLASS, on);
};
const syncAll = () => {
stripLocalSticky();
syncFollowState();
applyLocalStickyHeight();
};
const schedule = () => {
if (raf) return;
raf = requestAnimationFrame(() => {
raf = 0;
syncAll();
});
};
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("resize", schedule);
window.addEventListener("pageshow", 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);
}
schedule();
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot, { once: true });
} else {
boot();
}
})();
</script>
<style>
.glossary-legacy-note{
padding: 10px 12px;
border: 1px solid rgba(127,127,127,0.22);
border-radius: 12px;
background: rgba(127,127,127,0.05);
font-size: 14px;
line-height: 1.45;
margin-bottom: 18px;
}
.glossary-entry-head{
position: sticky;
top: calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px));
z-index: 11;
margin: 0 0 24px;
border: 1px solid rgba(127,127,127,0.18);
border-radius: 28px;
background:
linear-gradient(180deg, rgba(0,0,0,0.60), rgba(0,0,0,0.92)),
radial-gradient(900px 240px at 20% 0%, rgba(0,217,255,0.08), transparent 60%);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
overflow: hidden;
transition:
border-radius 180ms ease,
box-shadow 180ms ease,
border-color 180ms ease;
}
.glossary-entry-head__title{
padding: 18px 18px 16px;
}
.glossary-entry-head h1{
margin: 0;
font-size: clamp(2.2rem, 4vw, 3.15rem);
line-height: 1.02;
letter-spacing: -.04em;
font-weight: 850;
}
.glossary-entry-summary{
display: grid;
gap: 14px;
padding: 16px 18px 18px;
border-top: 1px solid rgba(127,127,127,0.14);
background: rgba(255,255,255,0.02);
}
.glossary-entry-dek{
margin: 0;
max-width: 76ch;
font-size: 1.04rem;
line-height: 1.55;
opacity: .94;
}
.glossary-entry-signals{
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 0;
}
.glossary-pill{
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 10px;
border: 1px solid rgba(127,127,127,0.24);
border-radius: 999px;
background: rgba(127,127,127,0.05);
font-size: 13px;
line-height: 1.35;
}
.glossary-pill--family{
border-color: rgba(127,127,127,0.36);
font-weight: 700;
}
.glossary-entry-meta{
margin: 0;
padding: 10px 12px;
border: 1px solid rgba(127,127,127,0.18);
border-radius: 12px;
background: rgba(127,127,127,0.04);
}
.glossary-entry-meta p{
margin: 0;
font-size: 14px;
line-height: 1.5;
}
.glossary-entry-meta p + p{
margin-top: 6px;
}
.glossary-entry-body{
margin-bottom: 28px;
}
.glossary-entry-body h2,
.glossary-entry-body h3,
.glossary-relations h2,
.glossary-relations h3{
scroll-margin-top: calc(var(--sticky-offset-px, 96px) + 18px);
}
.glossary-relations{
margin-top: 26px;
padding-top: 18px;
border-top: 1px solid rgba(127,127,127,0.18);
}
.glossary-relations h2{
margin-bottom: 14px;
}
.glossary-relations-grid{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 12px;
}
.glossary-relations-card{
border: 1px solid rgba(127,127,127,0.22);
border-radius: 16px;
padding: 14px 16px;
background: rgba(127,127,127,0.05);
}
.glossary-relations-card h3{
margin-top: 0;
margin-bottom: 10px;
font-size: 15px;
line-height: 1.35;
}
.glossary-relations-card ul{
margin: 0;
padding-left: 18px;
}
.glossary-relations-card li{
margin-bottom: 8px;
font-size: 14px;
line-height: 1.45;
}
.glossary-relations-card li:last-child{
margin-bottom: 0;
}
.glossary-relations-card span{
opacity: .9;
}
:global(body.is-glossary-entry-page #reading-follow){
z-index: 10;
}
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-head){
margin-bottom: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-summary){
gap: 10px;
padding-top: 12px;
padding-bottom: 10px;
}
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-signals){
gap: 6px;
}
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-meta){
padding: 8px 10px;
}
:global(body.is-glossary-entry-page.glossary-entry-follow-on #reading-follow){
transform: translateY(-1px);
}
:global(body.is-glossary-entry-page.glossary-entry-follow-on #reading-follow .reading-follow__inner){
margin-top: -1px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
:global(body.is-glossary-entry-page .glossary-entry-body h2.is-sticky),
:global(body.is-glossary-entry-page .glossary-entry-body h2[data-sticky-active="true"]),
:global(body.is-glossary-entry-page .glossary-entry-body h3.is-sticky),
:global(body.is-glossary-entry-page .glossary-entry-body h3[data-sticky-active="true"]),
:global(body.is-glossary-entry-page .glossary-relations h2.is-sticky),
:global(body.is-glossary-entry-page .glossary-relations h2[data-sticky-active="true"]),
:global(body.is-glossary-entry-page .glossary-relations h3.is-sticky),
:global(body.is-glossary-entry-page .glossary-relations h3[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: 720px){
.glossary-entry-signals{
gap: 6px;
}
.glossary-pill{
font-size: 12px;
}
}
@media (max-width: 860px){
.glossary-entry-head{
position: static;
border-radius: 22px;
margin-bottom: 20px;
}
.glossary-entry-head__title{
padding: 14px 14px 12px;
}
.glossary-entry-summary{
gap: 12px;
padding: 14px;
}
.glossary-entry-dek{
max-width: none;
}
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-head){
margin-bottom: 20px;
border-radius: 22px;
}
}
@media (prefers-color-scheme: dark){
.glossary-entry-meta{
background: rgba(255,255,255,0.03);
}
.glossary-legacy-note,
.glossary-pill,
.glossary-relations-card{
background: rgba(255,255,255,0.04);
}
}
</style>
<GlossaryEntryStickySync />
</GlossaryLayout>

View File

@@ -1,6 +1,10 @@
---
import GlossaryLayout from "../../layouts/GlossaryLayout.astro";
import GlossaryHomeAside from "../../components/GlossaryHomeAside.astro";
import GlossaryHomeHero from "../../components/GlossaryHomeHero.astro";
import GlossaryHomeSection from "../../components/GlossaryHomeSection.astro";
import GlossaryCardGrid from "../../components/GlossaryCardGrid.astro";
import GlossaryPortalGrid from "../../components/GlossaryPortalGrid.astro";
import { getCollection } from "astro:content";
import {
buildGlossaryBySlug,
@@ -41,6 +45,47 @@ const tensionsPageHref = "/glossaire/tensions-irreductibles/";
const verbesPageHref = "/glossaire/verbes-de-la-scene/";
const paradigmesPageHref = "/glossaire/paradigmes/";
const indexCompletPageHref = "/glossaire/index-complet/";
const paradigmePortalItems = [
{
href: paradigmeArchicratiquePageHref,
title: "Paradigme archicratique",
description:
"Saisir la logique densemble du système archicratique : de larcalité et de la cratialité jusquaux scènes, aux tensions, aux formes de co-viabilité et aux traditions de comparaison mobilisées.",
meta: "Portail de synthèse générale",
},
];
const approfondirPortalItems = [
{
href: tensionsPageHref,
title: "Tensions irréductibles",
description:
"Parcourir les dix tensions ontologiquement irréductibles et politiquement fondatrices à partir desquelles la co-viabilité doit être pensée.",
meta: "10 tensions structurantes",
},
{
href: verbesPageHref,
title: "Verbes de la scène",
description:
"Disposer dun mini-glossaire opératoire pour décrire louverture, lentrave, la capture, la fermeture ou la remise en scène des architectures régulatrices.",
meta: "19 verbes danalyse",
},
{
href: paradigmesPageHref,
title: "Cartographie théorique",
description:
"Situer larchicratie dans son paysage de doctrines fondatrices et de paradigmes régulateurs.",
meta: `${doctrinesCount} doctrine${doctrinesCount > 1 ? "s" : ""} · ${paradigmesCount} paradigme${paradigmesCount > 1 ? "s" : ""}`,
},
{
href: indexCompletPageHref,
title: "Index complet",
description:
"Retrouver lensemble des entrées du glossaire dans une navigation alphabétique intégrale.",
meta: `${entries.length} entrée${entries.length > 1 ? "s" : ""}`,
},
];
---
<GlossaryLayout
@@ -53,289 +98,182 @@ const indexCompletPageHref = "/glossaire/index-complet/";
</Fragment>
<section class="glossary-home">
<header class="glossary-hero" id="glossary-hero">
<p class="glossary-kicker">Référentiel terminologique</p>
<h1>Glossaire archicratique</h1>
<p class="glossary-intro">
Ce glossaire nest pas seulement un index de définitions. Il constitue
une porte dentrée dans la pensée archicratique : une cartographie
raisonnée des concepts fondamentaux, des scènes, des dynamiques et des
méta-régimes à partir desquels une société peut être décrite comme
organisation de tensions et recherche de co-viabilité.
</p>
<h2
class="glossary-hero-follow"
id="glossary-hero-follow"
aria-hidden="true"
></h2>
</header>
<GlossaryHomeHero />
<section class="glossary-map-block glossary-section" aria-labelledby="glossary-map-title">
<div class="glossary-map-block__head">
<h2 id="glossary-map-title" data-follow-section="Cartographie du système archicratique">
Cartographie du système archicratique
</h2>
<p>
La lecture la plus simple du système part de deux vecteurs premiers,
larcalité et la cratialité, dont la rencontre produit des tensions.
Ces tensions doivent être mises en scène, traitées par larchicration,
puis stabilisées dans des méta-régimes de co-viabilité.
</p>
</div>
<section
class="glossary-map-block glossary-section"
aria-labelledby="glossary-map-title"
>
<div class="glossary-map-block__head">
<h2
id="glossary-map-title"
data-follow-section="Cartographie du système archicratique"
>
Cartographie du système archicratique
</h2>
<p>
La lecture la plus simple du système part de deux vecteurs premiers,
larcalité et la cratialité, dont la rencontre produit des tensions.
Ces tensions doivent être mises en scène, traitées par larchicration,
puis stabilisées dans des méta-régimes de co-viabilité.
</p>
</div>
<div class="glossary-map" aria-label="Carte conceptuelle du glossaire">
<div class="glossary-map__stage">
<div class="glossary-map__title">Forces en composition</div>
<div class="glossary-map__roots">
{arcalite ? (
<a class="glossary-map__node" href={hrefOfGlossaryEntry(arcalite)}>ARCALITÉ</a>
) : (
<span class="glossary-map__node">ARCALITÉ</span>
)}
{cratialite ? (
<a class="glossary-map__node" href={hrefOfGlossaryEntry(cratialite)}>CRATIALITÉ</a>
) : (
<span class="glossary-map__node">CRATIALITÉ</span>
)}
</div>
</div>
<div class="glossary-map__arrow" aria-hidden="true">↓</div>
<div class="glossary-map__stage">
<div class="glossary-map__title">Phénomène transversal</div>
{tension ? (
<a class="glossary-map__node glossary-map__node--wide" href={hrefOfGlossaryEntry(tension)}>
TENSION
</a>
) : (
<span class="glossary-map__node glossary-map__node--wide">TENSION</span>
)}
</div>
<div class="glossary-map__arrow" aria-hidden="true">↓</div>
<div class="glossary-map__stage">
<div class="glossary-map__title">Comparution</div>
{sceneDepreuve ? (
<a class="glossary-map__node glossary-map__node--wide" href={hrefOfGlossaryEntry(sceneDepreuve)}>
MISE EN SCÈNE
</a>
) : (
<span class="glossary-map__node glossary-map__node--wide">MISE EN SCÈNE</span>
)}
</div>
<div class="glossary-map__arrow" aria-hidden="true">↓</div>
<div class="glossary-map__stage">
<div class="glossary-map__title">Opérateur régulateur</div>
{archicration ? (
<a class="glossary-map__node glossary-map__node--wide" href={hrefOfGlossaryEntry(archicration)}>
ARCHICRATION
</a>
) : (
<span class="glossary-map__node glossary-map__node--wide">ARCHICRATION</span>
)}
</div>
<div class="glossary-map__arrow" aria-hidden="true">↓</div>
<div class="glossary-map__stage">
<div class="glossary-map__title">Formes de stabilisation</div>
<a class="glossary-map__node glossary-map__node--wide" href={metaRegimesPageHref}>
MÉTA-RÉGIMES
<div class="glossary-map" aria-label="Carte conceptuelle du glossaire">
<div class="glossary-map__stage">
<div class="glossary-map__title">Forces en composition</div>
<div class="glossary-map__roots">
{arcalite ? (
<a class="glossary-map__node" href={hrefOfGlossaryEntry(arcalite)}>
ARCALITÉ
</a>
</div>
) : (
<span class="glossary-map__node">ARCALITÉ</span>
)}
{cratialite ? (
<a class="glossary-map__node" href={hrefOfGlossaryEntry(cratialite)}>
CRATIALITÉ
</a>
) : (
<span class="glossary-map__node">CRATIALITÉ</span>
)}
</div>
</section>
</div>
{fondamentaux.length > 0 && (
<section id="concepts-fondamentaux" class="glossary-section">
<div class="glossary-section__head">
<div>
<h2 data-follow-section="Concepts fondamentaux">Concepts fondamentaux</h2>
<p class="glossary-intro">
Ces notions forment la grammaire minimale de larchicratie.
Elles donnent accès à la structure générale du système.
</p>
</div>
</div>
<div class="glossary-map__arrow" aria-hidden="true">↓</div>
<div class="glossary-cards">
{fondamentaux.map((entry) => (
<a class="glossary-card" href={hrefOfGlossaryEntry(entry)}>
<strong>{entry.data.term}</strong>
<span>{entry.data.definitionShort}</span>
</a>
))}
</div>
</section>
)}
<section id="paradigme-archicratique" class="glossary-section">
<div class="glossary-section__head">
<div>
<h2 data-follow-section="Paradigme archicratique">Paradigme archicratique</h2>
<p class="glossary-intro">
Cette page de synthèse offre une vue densemble du système
archicratique. Elle articule le noyau conceptuel, les scènes de
comparution, les dynamiques, les tensions irréductibles, les
méta-régimes de co-viabilité et le paysage théorique dans lequel
larchicratie se situe et se distingue.
</p>
</div>
<a class="glossary-cta" href={paradigmeArchicratiquePageHref}>
Ouvrir la synthèse
<div class="glossary-map__stage">
<div class="glossary-map__title">Phénomène transversal</div>
{tension ? (
<a
class="glossary-map__node glossary-map__node--wide"
href={hrefOfGlossaryEntry(tension)}
>
TENSION
</a>
</div>
<div class="glossary-portals">
<a class="glossary-portal-card" href={paradigmeArchicratiquePageHref}>
<strong>Paradigme archicratique</strong>
<span>
Saisir la logique densemble du système archicratique : de
larcalité et de la cratialité jusquaux scènes, aux tensions, aux
formes de co-viabilité et aux traditions de comparaison
mobilisées.
</span>
<small>Portail de synthèse générale</small>
</a>
</div>
</section>
{scenes.length > 0 && (
<section id="scenes-archicratiques" class="glossary-section">
<div class="glossary-section__head">
<div>
<h2 data-follow-section="Scènes archicratiques">Scènes archicratiques</h2>
<p class="glossary-intro">
Les scènes archicratiques rendent possible la comparution des
architectures de régulation. Elles sont le lieu où lordre peut
être exposé, discuté et révisé.
</p>
</div>
</div>
<div class="glossary-cards">
{scenes.map((entry) => (
<a class="glossary-card glossary-card--wide" href={hrefOfGlossaryEntry(entry)}>
<strong>{entry.data.term}</strong>
<span>{entry.data.definitionShort}</span>
</a>
))}
</div>
</section>
)}
{dynamiques.length > 0 && (
<section id="dynamiques-archicratiques" class="glossary-section">
<div class="glossary-section__head">
<div>
<h2 data-follow-section="Dynamiques archicratiques">Dynamiques archicratiques</h2>
<p class="glossary-intro">
Cette famille rassemble les processus de déplacement, les dérives
et les formes de pathologisation de la régulation archicratique.
</p>
</div>
</div>
<div class="glossary-cards">
{dynamiques.map((entry) => (
<a class="glossary-card" href={hrefOfGlossaryEntry(entry)}>
<strong>{entry.data.term}</strong>
<span>{entry.data.definitionShort}</span>
</a>
))}
</div>
</section>
)}
<section id="meta-regimes-archicratiques" class="glossary-section">
<div class="glossary-section__head">
<div>
<h2 data-follow-section="Méta-régimes archicratiques">Méta-régimes archicratiques</h2>
<p class="glossary-intro">
Les méta-régimes décrivent les grandes formes historiques et
topologiques par lesquelles une société organise durablement ses
tensions et sa co-viabilité.
</p>
</div>
<a class="glossary-cta" href={metaRegimesPageHref}>
Explorer la cartographie complète
</a>
</div>
{metaRegimesPreview.length > 0 && (
<div class="glossary-cards">
{metaRegimesPreview.map((entry) => (
<a class="glossary-card" href={hrefOfGlossaryEntry(entry)}>
<strong>{entry.data.term}</strong>
<span>{entry.data.definitionShort}</span>
</a>
))}
</div>
) : (
<span class="glossary-map__node glossary-map__node--wide">
TENSION
</span>
)}
</section>
</div>
<section id="approfondir" class="glossary-section">
<div class="glossary-section__head">
<div>
<h2 data-follow-section="Prolonger la lecture">Prolonger la lecture</h2>
<p class="glossary-intro">
Quatre parcours complémentaires permettent délargir la lecture :
lun vers les tensions irréductibles, lun vers les verbes de la scène
archicratique, lun vers le paysage théorique dans lequel larchicratie
se situe et se distingue, lautre vers lensemble alphabétique complet
des entrées du glossaire.
</p>
</div>
</div>
<div class="glossary-map__arrow" aria-hidden="true">↓</div>
<div class="glossary-portals glossary-portals--secondary">
<a class="glossary-portal-card" href={tensionsPageHref}>
<strong>Tensions irréductibles</strong>
<span>
Parcourir les dix tensions ontologiquement irréductibles et
politiquement fondatrices à partir desquelles la co-viabilité doit
être pensée.
</span>
<small>10 tensions structurantes</small>
<div class="glossary-map__stage">
<div class="glossary-map__title">Comparution</div>
{sceneDepreuve ? (
<a
class="glossary-map__node glossary-map__node--wide"
href={hrefOfGlossaryEntry(sceneDepreuve)}
>
MISE EN SCÈNE
</a>
) : (
<span class="glossary-map__node glossary-map__node--wide">
MISE EN SCÈNE
</span>
)}
</div>
<a class="glossary-portal-card" href={verbesPageHref}>
<strong>Verbes de la scène</strong>
<span>
Disposer dun mini-glossaire opératoire pour décrire louverture,
lentrave, la capture, la fermeture ou la remise en scène des
architectures régulatrices.
</span>
<small>19 verbes danalyse</small>
</a>
<div class="glossary-map__arrow" aria-hidden="true">↓</div>
<a class="glossary-portal-card" href={paradigmesPageHref}>
<strong>Cartographie théorique</strong>
<span>
Situer larchicratie dans son paysage de doctrines fondatrices et de
paradigmes régulateurs.
</span>
<small>{doctrinesCount} doctrine{doctrinesCount > 1 ? "s" : ""} · {paradigmesCount} paradigme{paradigmesCount > 1 ? "s" : ""}</small>
<div class="glossary-map__stage">
<div class="glossary-map__title">Opérateur régulateur</div>
{archicration ? (
<a
class="glossary-map__node glossary-map__node--wide"
href={hrefOfGlossaryEntry(archicration)}
>
ARCHICRATION
</a>
) : (
<span class="glossary-map__node glossary-map__node--wide">
ARCHICRATION
</span>
)}
</div>
<a class="glossary-portal-card" href={indexCompletPageHref}>
<strong>Index complet</strong>
<span>
Retrouver lensemble des entrées du glossaire dans une navigation
alphabétique intégrale.
</span>
<small>{entries.length} entrée{entries.length > 1 ? "s" : ""}</small>
</a>
</div>
</section>
</section>
<div class="glossary-map__arrow" aria-hidden="true">↓</div>
<div class="glossary-map__stage">
<div class="glossary-map__title">Formes de stabilisation</div>
<a
class="glossary-map__node glossary-map__node--wide"
href={metaRegimesPageHref}
>
MÉTA-RÉGIMES
</a>
</div>
</div>
</section>
{fondamentaux.length > 0 && (
<GlossaryHomeSection
id="concepts-fondamentaux"
title="Concepts fondamentaux"
followSection="Concepts fondamentaux"
intro="Ces notions forment la grammaire minimale de larchicratie. Elles donnent accès à la structure générale du système."
>
<GlossaryCardGrid entries={fondamentaux} />
</GlossaryHomeSection>
)}
<GlossaryHomeSection
id="paradigme-archicratique"
title="Paradigme archicratique"
followSection="Paradigme archicratique"
intro="Cette page de synthèse offre une vue densemble du système archicratique. Elle articule le noyau conceptuel, les scènes de comparution, les dynamiques, les tensions irréductibles, les méta-régimes de co-viabilité et le paysage théorique dans lequel larchicratie se situe et se distingue."
ctaHref={paradigmeArchicratiquePageHref}
ctaLabel="Ouvrir la synthèse"
>
<GlossaryPortalGrid items={paradigmePortalItems} />
</GlossaryHomeSection>
{scenes.length > 0 && (
<GlossaryHomeSection
id="scenes-archicratiques"
title="Scènes archicratiques"
followSection="Scènes archicratiques"
intro="Les scènes archicratiques rendent possible la comparution des architectures de régulation. Elles sont le lieu où lordre peut être exposé, discuté et révisé."
>
<GlossaryCardGrid entries={scenes} wide={true} />
</GlossaryHomeSection>
)}
{dynamiques.length > 0 && (
<GlossaryHomeSection
id="dynamiques-archicratiques"
title="Dynamiques archicratiques"
followSection="Dynamiques archicratiques"
intro="Cette famille rassemble les processus de déplacement, les dérives et les formes de pathologisation de la régulation archicratique."
>
<GlossaryCardGrid entries={dynamiques} />
</GlossaryHomeSection>
)}
<GlossaryHomeSection
id="meta-regimes-archicratiques"
title="Méta-régimes archicratiques"
followSection="Méta-régimes archicratiques"
intro="Les méta-régimes décrivent les grandes formes historiques et topologiques par lesquelles une société organise durablement ses tensions et sa co-viabilité."
ctaHref={metaRegimesPageHref}
ctaLabel="Explorer la cartographie complète"
>
{metaRegimesPreview.length > 0 && (
<GlossaryCardGrid entries={metaRegimesPreview} />
)}
</GlossaryHomeSection>
<GlossaryHomeSection
id="approfondir"
title="Prolonger la lecture"
followSection="Prolonger la lecture"
intro="Quatre parcours complémentaires permettent délargir la lecture : lun vers les tensions irréductibles, lun vers les verbes de la scène archicratique, lun vers le paysage théorique dans lequel larchicratie se situe et se distingue, lautre vers lensemble alphabétique complet des entrées du glossaire."
>
<GlossaryPortalGrid items={approfondirPortalItems} secondary={true} />
</GlossaryHomeSection>
<script is:inline>
(() => {
@@ -502,76 +440,6 @@ const indexCompletPageHref = "/glossaire/index-complet/";
position: static;
}
.glossary-hero{
position: sticky;
top: var(--glossary-sticky-top);
z-index: 12;
margin-bottom: 28px;
padding: 14px 16px 18px;
border: 1px solid rgba(127,127,127,0.18);
border-radius: 28px;
background:
linear-gradient(180deg, rgba(0,0,0,0.60), rgba(0,0,0,0.90)),
radial-gradient(900px 240px at 20% 0%, rgba(0,217,255,0.08), transparent 60%);
transition:
background 300ms cubic-bezier(.22,.8,.22,1),
border-color 300ms cubic-bezier(.22,.8,.22,1),
box-shadow 300ms cubic-bezier(.22,.8,.22,1);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
display: grid;
row-gap: 12px;
}
.glossary-kicker{
margin: 0;
font-size: 12px;
letter-spacing: .12em;
text-transform: uppercase;
opacity: .72;
}
.glossary-hero h1{
margin: 0;
font-size: clamp(2.2rem, 4vw, 3.15rem);
line-height: 1.02;
letter-spacing: -.04em;
font-weight: 850;
}
.glossary-intro{
margin: 0;
max-width: 72ch;
font-size: 1.05rem;
line-height: 1.55;
opacity: .94;
}
.glossary-hero-follow{
margin: 2px 0 0;
min-height: var(--glossary-follow-height);
display: flex;
align-items: flex-end;
opacity: 0;
transform: translateY(10px) scale(.985);
filter: blur(6px);
transition:
opacity 220ms cubic-bezier(.22,1,.36,1),
transform 320ms cubic-bezier(.22,1,.36,1),
filter 320ms cubic-bezier(.22,1,.36,1);
pointer-events: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
will-change: opacity, transform, filter;
}
.glossary-hero-follow.is-visible{
opacity: 1;
transform: translateY(0) scale(1);
filter: blur(0);
}
.glossary-map-block__head h2,
.glossary-section h2{
margin: 0;
@@ -581,20 +449,6 @@ const indexCompletPageHref = "/glossaire/index-complet/";
font-weight: 800;
}
.glossary-section{
margin-top: 42px;
scroll-margin-top: calc(var(--glossary-sticky-top) + 190px);
}
.glossary-section__head{
display: flex;
justify-content: space-between;
align-items: start;
gap: 16px;
flex-wrap: wrap;
margin-bottom: 14px;
}
.glossary-map-block{
padding: 18px 18px 20px;
border: 1px solid var(--glossary-border);
@@ -677,95 +531,6 @@ const indexCompletPageHref = "/glossaire/index-complet/";
opacity: .72;
}
.glossary-cta{
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 40px;
border: 1px solid var(--glossary-border-strong);
border-radius: 999px;
padding: 7px 14px;
color: var(--glossary-accent);
text-decoration: none;
white-space: nowrap;
transition: transform 120ms ease, background 120ms ease;
}
.glossary-cta:hover{
background: var(--glossary-bg-soft-strong);
text-decoration: none;
transform: translateY(-1px);
}
.glossary-cards{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 12px;
margin-top: 14px;
}
.glossary-card{
display: flex;
flex-direction: column;
gap: 8px;
padding: 14px 16px;
border: 1px solid var(--glossary-border);
border-radius: 18px;
background: var(--glossary-bg-soft);
text-decoration: none;
transition: transform 120ms ease, background 120ms ease, border-color 120ms ease;
}
.glossary-card:hover{
transform: translateY(-1px);
background: var(--glossary-bg-soft-strong);
border-color: rgba(0,217,255,0.16);
text-decoration: none;
}
.glossary-card--wide{
grid-column: 1 / -1;
}
.glossary-card strong{
color: var(--glossary-accent);
font-size: 1.04rem;
line-height: 1.28;
}
.glossary-card span{
color: inherit;
font-size: 1rem;
line-height: 1.5;
opacity: .94;
}
.glossary-portals{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 14px;
margin-top: 14px;
}
.glossary-portal-card{
display: flex;
flex-direction: column;
gap: 8px;
padding: 16px 18px;
border: 1px solid var(--glossary-border);
border-radius: 18px;
background: var(--glossary-bg-soft);
text-decoration: none;
transition: transform 120ms ease, background 120ms ease, border-color 120ms ease;
}
.glossary-portal-card:hover{
transform: translateY(-1px);
background: var(--glossary-bg-soft-strong);
border-color: rgba(0,217,255,0.16);
text-decoration: none;
}
.glossary-portal-card strong{
color: var(--glossary-accent);
font-size: 1.08rem;