   FOOTER MANIFESTO — déclaration plein écran avant les liens
   - Tendance Awwwards 2026 : le footer porte une affirmation
   - Type display Bricolage Grotesque XL + accents italique serif
   - Eyebrow live status, CTA mailto en double-ligne
   ========================================================= */
.footer { padding: 0 0 36px; }   /* override : on ne padde plus en haut, le manifesto le porte */

.footer-manifesto {
  background: var(--c-encre);
  color: var(--c-papier-warm);
  padding: clamp(120px, 18vh, 180px) 0 clamp(80px, 12vh, 140px);
  border-bottom: 1px solid rgba(232, 221, 200, 0.10);
  position: relative;
  overflow: hidden;
}
.footer-manifesto::before {
  /* halo or en bas — résonance du logo, signe de fin */
  content: '';
  position: absolute;
  left: -10%; right: -10%;
  bottom: -50%;
  height: 80%;
  background: radial-gradient(ellipse 50% 40% at 50% 50%, color-mix(in srgb, var(--c-or) 18%, transparent), transparent 70%);
  pointer-events: none;
}

.footer-manifesto-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--c-or);
  margin-bottom: 36px;
  position: relative;
  z-index: 2;
}
.footer-manifesto-eyebrow .dot {
  width: 6px; height: 6px;
  background: var(--c-or);
  border-radius: 50%;
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--c-or) 70%, transparent);
  animation: pulse 2.4s var(--ease-soft) infinite;
}

.footer-manifesto-text {
  font-family: var(--display);
  font-stretch: 90%;
  font-weight: 540;
  font-size: clamp(48px, 9vw, 140px);
  line-height: 0.92;
  letter-spacing: -0.034em;
  color: var(--c-papier-warm);
  display: flex;
  flex-direction: column;
  gap: 0;
  margin-bottom: 56px;
  position: relative;
  z-index: 2;
  text-wrap: balance;
}
.footer-manifesto-text span {
  display: block;
  /* chaque ligne a un retrait progressif pour casser l'alignement IA */
}
.footer-manifesto-text span:nth-child(1) { padding-left: 0; }
.footer-manifesto-text span:nth-child(2) { padding-left: 8%; }
.footer-manifesto-text span:nth-child(3) { padding-left: 16%; }
.footer-manifesto-text em {
  font-family: var(--serif-display);
  font-style: italic;
  font-weight: 400;
  color: var(--c-or);
  font-stretch: 100%;
  letter-spacing: -0.012em;
}

.footer-manifesto-cta {
  display: inline-flex;
  align-items: baseline;
  gap: 24px;
  padding: 18px 0;
  border-top: 1px solid rgba(232, 221, 200, 0.18);
  border-bottom: 1px solid rgba(232, 221, 200, 0.18);
  width: 100%;
  max-width: 720px;
  transition: border-color 0.3s var(--ease-out), color 0.3s var(--ease-out);
  position: relative;
  z-index: 2;
}
.footer-manifesto-cta:hover {
  border-color: var(--c-or);
}
.footer-manifesto-cta-mono {
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--c-or);
  white-space: nowrap;
}
.footer-manifesto-cta-mail {
  font-family: var(--display);
  font-size: clamp(20px, 2.6vw, 32px);
  font-weight: 480;
  font-stretch: 92%;
  letter-spacing: -0.014em;
  color: var(--c-papier-warm);
  transition: color 0.3s var(--ease-out);
}
.footer-manifesto-cta:hover .footer-manifesto-cta-mail {
  color: var(--c-or);
}

@media (max-width: 720px) {
  .footer-manifesto { padding: clamp(72px, 12vh, 120px) 0 clamp(48px, 8vh, 80px); }
  .footer-manifesto-text span:nth-child(2) { padding-left: 6%; }
  .footer-manifesto-text span:nth-child(3) { padding-left: 12%; }
}

/* Le footer-grid existant : on l'aère du manifesto au-dessus */
.footer-grid { padding-top: 64px; }

/* =========================================================
   STOP ITALIQUE DANS LES TITRES — override radical anti-Claude
   ----------------------------------------------------------
   L'italique serif au milieu d'un titre grotesque (Fraunces ou
   Instrument Serif) = signature Anthropic 2025-2026. On la
   retire de TOUS les titres et on signale les mots-clés
   uniquement par la COULEUR. Les chiffres romains et glyphes
   (α β γ, V IV III II I, ∑ § ⌘) gardent l'italique serif —
   ce ne sont pas des mots, ce sont des signes.
   ========================================================= */
.h2,
.h2 .accent,
.h3 .accent,
.display .accent,
.h1 .accent,
.mod-title .accent,
.principle .title .accent,
.split-name .accent,
.cta-final h2 .accent,
.brick-label em,
.hg-title em,
.founder-name em,
.footer-manifesto-text em,
.polaroid-caption em {
  font-family: var(--display);
  font-style: normal !important;
  font-weight: inherit;
  font-stretch: inherit;
  letter-spacing: inherit;
}

/* h2 entier — on neutralise l'italique de la charte (sortie du tic Claude) */
.h2 {
  font-style: normal !important;
  font-weight: 540;
  font-stretch: 88%;
  letter-spacing: -0.02em;
  line-height: 1.0;
}

/* Mots accentués : signal couleur uniquement, héritent de la chasse parent */
.display .accent,
.h1 .accent,
.h3 .accent {
  color: var(--display-acc);
  font-weight: inherit;
}
.h2 .accent {
  color: var(--accent);
}
.mod-title .accent,
.principle .title .accent,
.split-name .accent,
.cta-final h2 .accent {
  color: var(--accent);
}
.brick-label em { color: var(--h2-italic); }
.hg-title em { color: var(--h2-italic); }
.founder-name em { color: var(--brand); }
.footer-manifesto-text em { color: var(--c-or); }
.polaroid-caption em { color: var(--c-bordeaux); }

/* Le glyphe d'ouverture du h2 (rond bullet "·" en or) reste un signal
   quand le h2 est en upright — décoratif minimal */
.section h2.h1::before,
.section .h1::before { content: none; }   /* clean si jamais hérité */

/* =========================================================
   HERO MODES — 3 compositions distinctes (anti-template Claude)
   - LinkZen home   : grotesque XL pleine largeur, italique fragile
                      en contrepoint, asymétrie horizontale
   - OpenFrame      : condensed narrow column, presse imprimée
                      (75% wdth, max 14ch, gravité gauche)
   - CaseOne        : asymétrie tampon-driven, décalage à 12%,
                      titre presque sortant du cadre
   ========================================================= */

/* === LinkZen home === */
body[data-page="home"] .hero-display {
  font-size: clamp(64px, 12vw, 200px);
  font-stretch: 90%;
  line-height: 0.82;
  letter-spacing: -0.04em;
  margin: 28px 0 56px;
  max-width: none;
}
body[data-page="home"] .hero-display .accent {
  font-size: 0.78em;            /* l'italique serif est plus petit, contrepoint */
  letter-spacing: -0.012em;
  display: inline-block;
  transform: translateY(-0.04em);
}

/* === OpenFrame — colonne presse, condensed, gravité gauche === */
body[data-page="openframe"] .hero-display {
  font-size: clamp(72px, 14vw, 240px);
  font-stretch: 78%;
  font-weight: 580;
  line-height: 0.78;
  letter-spacing: -0.05em;
  max-width: 14ch;
  margin: 32px 0 48px;
  /* jeté légèrement vers la gauche — le titre tient la fold mais
     laisse de l'espace pour le sablier scroll qui suit */
}
body[data-page="openframe"] .hero-display .accent {
  font-size: 1em;
  font-stretch: 100%;
  letter-spacing: -0.02em;
  display: inline-block;
}
body[data-page="openframe"] .hero-display .alt {
  /* Le "01 /" devient un compteur qui flotte plus discret */
  display: block;
  margin-bottom: 12px;
  font-size: 14px;
  vertical-align: 0;
}

/* === CaseOne — composition asymétrique tampon-driven === */
body[data-page="caseone"] .hero-display {
  font-size: clamp(64px, 13vw, 220px);
  font-stretch: 100%;       /* Special Elite a sa chasse propre */
  line-height: 0.92;
  letter-spacing: -0.018em;
  margin-left: 8%;
  max-width: 80%;
  position: relative;
  z-index: 3;               /* au-dessus des tampons absolus */
}
@media (max-width: 720px) {
  body[data-page="home"] .hero-display { font-size: clamp(48px, 16vw, 96px); }
  body[data-page="openframe"] .hero-display { font-size: clamp(56px, 18vw, 110px); max-width: 100%; }
  body[data-page="caseone"] .hero-display { margin-left: 0; max-width: 100%; }
}

/* =========================================================
   SYSTÈME GRAPHIQUE « OPEN FRAME »
   ---------------------------------------------------------
   Reproduction stricte de la logique du logo OpenFrame :
   - DEUX RAILS HORIZONTAUX continus (haut + bas)
   - QUATRE PATTES VERTICALES aux coins (en L), courtes
   - OUVERTURES CENTRALES sur les côtés gauche et droit
   - Le contenu est CADRÉ MAIS JAMAIS ENFERMÉ

   Anti-Claude : ce ne sont PAS des "brackets" / coins ouverts
   type Anthropic. Ce sont des barres horizontales pleines avec
   ouvertures latérales — la métaphore d'une fenêtre, d'un seuil.

   ---------------------------------------------------------
   Variables locales :
   - --frame-color    : couleur des rails (vert sauge charte par défaut)
   - --frame-thickness: épaisseur des rails (3-6px selon scale)
   - --frame-leg      : hauteur des pattes verticales d'angle (12-32px)
   - --frame-pad-x    : padding horizontal interne
   - --frame-pad-y    : padding vertical interne
   ========================================================= */

.of-frame {
  --frame-color:     var(--c-vert-sauge);
  --frame-thickness: 3px;
  --frame-leg-long:  56.25%;  /* TL + BR : pattes longues (logo asymétrique) */
  --frame-leg-short: 18.75%;  /* TR + BL : pattes courtes */
  --frame-leg:       18px;     /* fallback px pour petits cadres (btn/card) */
  --frame-pad-x:     clamp(20px, 3vw, 36px);
  --frame-pad-y:     clamp(28px, 3.5vw, 44px);

  position: relative;
  padding: var(--frame-pad-y) var(--frame-pad-x);
  isolation: isolate;
  color: var(--frame-color);
}

/* Système 6 segments — rotational symmetry 180° du logo OpenFrame officiel.
   2 markups supportés (équivalents) :
   ① Recommandé — 6 spans .of-seg.of-seg--top|bottom|tl|tr|br|bl
   ② Compat — 4 spans .of-frame-leg-tl|tr|bl|br + rails ::before/::after */

/* MARKUP ① : 6 segments .of-seg (canonique, lab-style) */
.of-frame > .of-seg {
  position: absolute;
  background: var(--frame-color);
  pointer-events: none;
  z-index: 1;
}
.of-frame > .of-seg--top    { top: 0;    left: 0; right: 0;  height: var(--frame-thickness); transform-origin: left center; }
.of-frame > .of-seg--bottom { bottom: 0; left: 0; right: 0;  height: var(--frame-thickness); transform-origin: right center; }
.of-frame > .of-seg--tl     { top: 0;    left: 0;            width: var(--frame-thickness); height: max(var(--frame-leg), var(--frame-leg-long));  transform-origin: top left; }
.of-frame > .of-seg--tr     { top: 0;    right: 0;           width: var(--frame-thickness); height: max(var(--frame-leg), var(--frame-leg-short)); transform-origin: top right; }
.of-frame > .of-seg--br     { bottom: 0; right: 0;           width: var(--frame-thickness); height: max(var(--frame-leg), var(--frame-leg-long));  transform-origin: bottom right; }
.of-frame > .of-seg--bl     { bottom: 0; left: 0;            width: var(--frame-thickness); height: max(var(--frame-leg), var(--frame-leg-short)); transform-origin: bottom left; }
.of-frame__inner            { position: relative; z-index: 2; }

/* MARKUP ② : 4 spans + rails ::before/::after (compat hero existant)
   Asymétrie rotational : TL + BR longues, TR + BL courtes. */
.of-frame::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: var(--frame-thickness);
  background: var(--frame-color);
  pointer-events: none;
  z-index: 1;
}
.of-frame::after {
  content: '';
  position: absolute;
  bottom: 0; left: 0; right: 0;
  height: var(--frame-thickness);
  background: var(--frame-color);
  pointer-events: none;
  z-index: 1;
}
.of-frame .of-frame-leg-tl,
.of-frame .of-frame-leg-tr,
.of-frame .of-frame-leg-bl,
.of-frame .of-frame-leg-br {
  position: absolute;
  width: var(--frame-thickness);
  background: var(--frame-color);
  pointer-events: none;
  z-index: 2;
}
/* Pattes longues (TL + BR) — 56.25% de la hauteur, fidèles au logo */
.of-frame .of-frame-leg-tl { top: 0;    left: 0;  height: max(var(--frame-leg), var(--frame-leg-long)); }
.of-frame .of-frame-leg-br { bottom: 0; right: 0; height: max(var(--frame-leg), var(--frame-leg-long)); }
/* Pattes courtes (TR + BL) — 18.75% de la hauteur */
.of-frame .of-frame-leg-tr { top: 0;    right: 0; height: max(calc(var(--frame-leg) * 0.34), var(--frame-leg-short)); }
.of-frame .of-frame-leg-bl { bottom: 0; left: 0;  height: max(calc(var(--frame-leg) * 0.34), var(--frame-leg-short)); }

/* ---------- TAILLES ---------- */
.of-frame--xl {
  --frame-thickness: 6px;
  --frame-leg:       32px;
  --frame-pad-x:     clamp(28px, 5vw, 64px);
  --frame-pad-y:     clamp(48px, 7vw, 96px);
}
.of-frame--lg {
  --frame-thickness: 4px;
  --frame-leg:       24px;
  --frame-pad-x:     clamp(24px, 4vw, 48px);
  --frame-pad-y:     clamp(36px, 5vw, 64px);
}
.of-frame--sm {
  --frame-thickness: 2px;
  --frame-leg:       12px;
  --frame-pad-x:     16px;
  --frame-pad-y:     14px;
}

/* ---------- COULEURS / SURFACES ---------- */
.of-frame--ink   { --frame-color: var(--ink); }
.of-frame--gold  { --frame-color: var(--c-or); }
.of-frame--brand { --frame-color: var(--c-vert-foret); }

[data-theme="dark"] .of-frame,
[data-surface="openframe"] .of-frame {
  --frame-color: var(--c-vert-sauge);
}

/* =========================================================
   OF-MARK — DÉCORATION SEULE, SANS PADDING
   ---------------------------------------------------------
   Pour signer des composants qui ont déjà leur propre layout
   (.bignum, .principle, .mod, .cta-final, headlines) avec le
   logo OpenFrame asymétrique. Markup : 6 spans .of-seg posés
   en absolute sur le composant (qui doit être position:relative,
   c'est déjà le cas pour .bignum/.principle/.mod par défaut).

   Usage :
     <div class="bignum of-mark">
       <span class="of-seg of-seg--top"></span>
       <span class="of-seg of-seg--bottom"></span>
       <span class="of-seg of-seg--tl"></span>
       <span class="of-seg of-seg--tr"></span>
       <span class="of-seg of-seg--br"></span>
       <span class="of-seg of-seg--bl"></span>
       ... contenu existant ...
     </div>
   ========================================================= */
.of-mark {
  --frame-color:     var(--accent);
  --frame-thickness: 1.5px;
  --frame-leg-long:  56.25%;
  --frame-leg-short: 18.75%;
  position: relative;  /* containing block pour les .of-seg absolus */
}
.of-mark > .of-seg {
  position: absolute;
  background: var(--frame-color);
  pointer-events: none;
  z-index: 2;
  transition: background-color 0.4s var(--ease-out, ease);
}
.of-mark > .of-seg--top    { top: 0;    left: 0; right: 0;  height: var(--frame-thickness); }
.of-mark > .of-seg--bottom { bottom: 0; left: 0; right: 0;  height: var(--frame-thickness); }
.of-mark > .of-seg--tl     { top: 0;    left: 0;            width: var(--frame-thickness); height: var(--frame-leg-long); }
.of-mark > .of-seg--tr     { top: 0;    right: 0;           width: var(--frame-thickness); height: var(--frame-leg-short); }
.of-mark > .of-seg--br     { bottom: 0; right: 0;           width: var(--frame-thickness); height: var(--frame-leg-long); }
.of-mark > .of-seg--bl     { bottom: 0; left: 0;            width: var(--frame-thickness); height: var(--frame-leg-short); }

/* Variantes épaisseur */
.of-mark--xl { --frame-thickness: 3px; }
.of-mark--md { --frame-thickness: 2px; }
.of-mark--xs { --frame-thickness: 1px; }

/* Variantes couleur */
.of-mark--gold  { --frame-color: var(--c-or); }
.of-mark--brand { --frame-color: var(--c-vert-foret); }
.of-mark--sauge { --frame-color: var(--c-vert-sauge); }
.of-mark--ink   { --frame-color: var(--ink); }

/* Hover : intensifier la signature en or */
.of-mark.is-hoverable:hover { --frame-color: var(--c-or); }

/* =========================================================
   KW-FRAME — encadrement OpenFrame autour de mots-clés inline
   ---------------------------------------------------------
   Pour mettre en avant les mots-clés d'un texte (noms de marque,
   concepts forts, manifestes) en les inscrivant dans un mini-cadre
   OpenFrame asymétrique. Inspiré du concept Brackets N du lab,
   adapté pour un usage texte courant. Markup compact :

     <span class="kw-frame">Slow to scale</span>

   Le span génère 6 segments via ::before/::after + 4 spans cachés
   ne sont pas nécessaires : 2 pseudo-éléments pour les rails + un
   3e couple de pseudos pour les pattes asymétriques via gradients.
   APPROCHE plus simple : 6 spans .kw-seg.
   ========================================================= */
.kw-frame {
  position: relative;
  display: inline-block;
  padding: 0.08em 0.42em 0.12em;
  --frame-color: var(--c-or);
  --frame-thickness: 2px;       /* bumped 1.5 → 2 pour visibilité */
  --frame-leg-long: 56.25%;
  --frame-leg-short: 18.75%;
  /* Le texte garde la couleur héritée du parent */
  color: inherit;
  /* La box reflète le texte, segments collés aux bords */
  line-height: 1.1;
  /* Mini-padding pour que les rails ne mordent pas les ascendantes/descendantes */
  margin: 0 0.05em;
}

/* 6 segments générés via spans dédiés (insérés en JS au load
   pour préserver la propreté du markup HTML écrit par l'auteur).
   Si l'auteur préfère écrire les spans à la main, c'est aussi possible. */
.kw-frame > .kw-seg {
  position: absolute;
  background: var(--frame-color);
  pointer-events: none;
}
.kw-frame > .kw-seg--top    { top: 0;    left: 0; right: 0;  height: var(--frame-thickness); }
.kw-frame > .kw-seg--bottom { bottom: 0; left: 0; right: 0;  height: var(--frame-thickness); }
.kw-frame > .kw-seg--tl     { top: 0;    left: 0;            width: var(--frame-thickness); height: var(--frame-leg-long); }
.kw-frame > .kw-seg--tr     { top: 0;    right: 0;           width: var(--frame-thickness); height: var(--frame-leg-short); }
.kw-frame > .kw-seg--br     { bottom: 0; right: 0;           width: var(--frame-thickness); height: var(--frame-leg-long); }
.kw-frame > .kw-seg--bl     { bottom: 0; left: 0;            width: var(--frame-thickness); height: var(--frame-leg-short); }

/* Variantes couleur pour KW */
.kw-frame--sauge { --frame-color: var(--c-vert-sauge); }
.kw-frame--gold  { --frame-color: var(--c-or); }
.kw-frame--ink   { --frame-color: var(--ink); }
.kw-frame--cream { --frame-color: var(--c-papier-warm); }

/* Hover/focus : passe à or pour subtle attention */
.kw-frame:hover { --frame-color: var(--c-or); transition: color 0.3s ease; }

/* Reveal cascading optionnel — pour mots-clés qui apparaissent
   en cascade quand un parent .reveal devient .in. */
.reveal .kw-frame > .kw-seg {
  opacity: 0;
  transform: scale(0.6);
  transform-origin: center;
  transition: opacity 0.5s cubic-bezier(.65,0,.35,1),
              transform 0.5s cubic-bezier(.65,0,.35,1);
}
.reveal.in .kw-frame > .kw-seg {
  opacity: 1;
  transform: scale(1);
  transition-delay: 0.2s;
}

/* ---------- BOUTON FRAME ---------- */
/* Bouton qui reprend la logique : 2 rails horizontaux + 2 pattes
   verticales aux coins. Côtés ouverts. Pas de rectangle fermé. */
.btn-frame {
  --frame-color:     var(--c-vert-sauge);
  --frame-thickness: 2px;
  --frame-leg:       10px;

  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 16px 24px;
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--ink);
  background: transparent;
  border: none;
  cursor: pointer;
  isolation: isolate;
  transition: color 0.25s var(--ease-out), gap 0.25s var(--ease-out);
}
.btn-frame::before,
.btn-frame::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  height: var(--frame-thickness);
  background: var(--frame-color);
  pointer-events: none;
  transition: background-color 0.25s var(--ease-out), height 0.25s var(--ease-out);
}
.btn-frame::before { top: 0; }
.btn-frame::after  { bottom: 0; }
/* Pattes verticales aux 4 angles — via 4 box-shadows sur un span dédié */
.btn-frame .frame-corners {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.btn-frame .frame-corners::before,
.btn-frame .frame-corners::after {
  content: '';
  position: absolute;
  width: var(--frame-thickness);
  height: var(--frame-leg);
  background: var(--frame-color);
  transition: background-color 0.25s var(--ease-out);
}
/* haut-gauche */
.btn-frame .frame-corners::before { top: 0; left: 0; }
/* bas-droite */
.btn-frame .frame-corners::after  { bottom: 0; right: 0; }
/* On ajoute haut-droite + bas-gauche via des spans supplémentaires
   ou via un 2e pseudo-élément sur le bouton lui-même. Astuce : on
   met le 2e couple de pattes en outline-shadow sur frame-corners */
.btn-frame .frame-corners {
  box-shadow:
    /* HD : déplacer la patte droite via inset négatif → trop fragile.
       On préfère ajouter 2 spans .frame-leg-tr et .frame-leg-bl en HTML. */
}

.btn-frame:hover {
  --frame-color: var(--c-or);
  color: var(--c-or);
  gap: 18px;
}
.btn-frame .arrow {
  transition: transform 0.25s var(--ease-out);
}
.btn-frame:hover .arrow { transform: translateX(4px); }

/* ---------- CARTE FRAME (mini Open Frame) ---------- */
.card-frame {
  --frame-color:     var(--c-vert-sauge);
  --frame-thickness: 3px;
  --frame-leg:       20px;
  --frame-pad-x:     28px;
  --frame-pad-y:     32px;

  position: relative;
  padding: var(--frame-pad-y) var(--frame-pad-x);
  background: transparent;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.card-frame::before,
.card-frame::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  height: var(--frame-thickness);
  background: var(--frame-color);
  pointer-events: none;
}
.card-frame::before { top: 0; }
.card-frame::after  { bottom: 0; }

/* ---------- IMAGE FRAME (fenêtre) ---------- */
/* Image avec 2 barres haut/bas vertes + image qui peut DÉPASSER
   latéralement (overflow visible volontaire) */
.image-frame {
  --frame-color:     var(--c-vert-sauge);
  --frame-thickness: 4px;

  position: relative;
  display: block;
  padding: var(--frame-thickness) 0;
  isolation: isolate;
}
.image-frame::before,
.image-frame::after {
  content: '';
  position: absolute;
  left: -8%; right: -8%;            /* DÉBORDEMENT lateral assumé */
  height: var(--frame-thickness);
  background: var(--frame-color);
  pointer-events: none;
  z-index: 2;
}
.image-frame::before { top: 0; }
.image-frame::after  { bottom: 0; }
.image-frame img,
.image-frame > * {
  display: block;
  width: 100%;
  position: relative;
  z-index: 1;
}

/* ---------- SECTION FRAME (cadre pleine largeur) ---------- */
.section-frame {
  --frame-color:     var(--c-vert-sauge);
  --frame-thickness: 4px;
  --frame-leg:       42px;

  position: relative;
  padding: clamp(80px, 12vw, 140px) 0;
  isolation: isolate;
}
.section-frame::before,
.section-frame::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  height: var(--frame-thickness);
  background: var(--frame-color);
  pointer-events: none;
  z-index: 2;
}
.section-frame::before { top: 0; }
.section-frame::after  { bottom: 0; }

/* ---------- NAVIGATION : actif = rail horizontal vert épais ---------- */
[data-page="openframe"] .nav-links a.active::after,
[data-surface="openframe"] .nav-links a.active::after {
  height: 3px;
  background: var(--c-vert-sauge);
  bottom: -1px;
}

/* ---------- ANIMATION SCROLL : les rails se dessinent ---------- */
/* Quand un .of-frame entre dans le viewport, ses rails se déploient
   de left:50% vers les bords (animation directionnelle).
   Activé via .reveal.in (système existant). */
@media (prefers-reduced-motion: no-preference) {
  .of-frame:not(.in-view)::before,
  .of-frame:not(.in-view)::after,
  .card-frame:not(.in-view)::before,
  .card-frame:not(.in-view)::after,
  .section-frame:not(.in-view)::before,
  .section-frame:not(.in-view)::after,
  .image-frame:not(.in-view)::before,
  .image-frame:not(.in-view)::after {
    transform: scaleX(0);
    transform-origin: center;
  }
  .of-frame.in-view::before,
  .of-frame.in-view::after,
  .card-frame.in-view::before,
  .card-frame.in-view::after,
  .section-frame.in-view::before,
  .section-frame.in-view::after,
  .image-frame.in-view::before,
  .image-frame.in-view::after {
    transform: scaleX(1);
    transform-origin: center;
    transition: transform 1s var(--ease-out);
  }
  /* Pattes verticales : apparaissent après le rail (delay 600ms) */
  .of-frame:not(.in-view) .of-frame-leg-tl,
  .of-frame:not(.in-view) .of-frame-leg-tr,
  .of-frame:not(.in-view) .of-frame-leg-bl,
  .of-frame:not(.in-view) .of-frame-leg-br {
    transform: scaleY(0);
    transform-origin: top center;
  }
  .of-frame.in-view .of-frame-leg-tl,
  .of-frame.in-view .of-frame-leg-tr {
    transform: scaleY(1);
    transition: transform 0.6s var(--ease-out) 0.7s;
    transform-origin: top center;
  }
  .of-frame.in-view .of-frame-leg-bl,
  .of-frame.in-view .of-frame-leg-br {
    transform: scaleY(1);
    transition: transform 0.6s var(--ease-out) 0.7s;
    transform-origin: bottom center;
  }
}

/* ---------- LOGO OPENFRAME — affichage dans nav et hero ---------- */
.logo-openframe {
  display: inline-block;
  width: 64px;
  height: auto;
  vertical-align: middle;
}
.logo-openframe--lg { width: clamp(120px, 14vw, 180px); }
.logo-openframe--xl { width: clamp(200px, 24vw, 320px); }

/* ---------- HERO OPENFRAME : application directe du système ---------- */
.of-hero-frame {
  margin: 32px 0 96px;
  position: relative;
}
.of-hero-title {
  margin: 0;
  position: relative;
  z-index: 2;
}
/* Le logo en grand DÉPASSE le rail bas du cadre — symbolisme "ouverture" */
.of-hero-logo {
  position: absolute;
  right: clamp(-40px, -3vw, -8px);
  bottom: calc(-1 * clamp(40px, 6vw, 80px));
  width: clamp(140px, 18vw, 240px);
  height: auto;
  z-index: 3;
  pointer-events: none;
  filter: contrast(0.96) saturate(0.92);
  opacity: 0.95;
}
@media (max-width: 720px) {
  .of-hero-logo {
    position: relative;
    right: auto; bottom: auto;
    margin: 32px auto 0;
    display: block;
  }
  .of-hero-frame { margin-bottom: 56px; }
}

/* Quand le hero-display est dans un .of-frame, on neutralise les
   marges héritées qui poseraient problème avec les rails absolus */
.of-frame .hero-display {
  margin-top: 0;
  margin-bottom: 0;
}

/* ---------- MODULES OPENFRAME : passage en mini Open Frame ---------- */
/* Sur la page OpenFrame, les .mod du catalogue (4 offres) deviennent
   des cartes inspirées du logo : 2 rails + 4 pattes, côtés ouverts */
[data-page="openframe"] .modules {
  border: none;                    /* on retire le grillage v1 */
  gap: clamp(20px, 2vw, 32px);
  display: grid;
}
[data-page="openframe"] .mod {
  --of-mod-color: var(--c-vert-sauge);
  border: none;                    /* retire la border classique */
  background: transparent;
  position: relative;
  padding: clamp(28px, 3vw, 40px) clamp(20px, 2.5vw, 32px);
  isolation: isolate;
}
/* Rails haut + bas */
[data-page="openframe"] .mod::before,
[data-page="openframe"] .mod::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  height: 3px;
  background: var(--of-mod-color);
  pointer-events: none;
  z-index: 1;
}
[data-page="openframe"] .mod::before { top: 0; }
[data-page="openframe"] .mod::after  { bottom: 0; }
/* Pattes verticales aux 4 angles via box-shadow inset multiple */
[data-page="openframe"] .mod {
  box-shadow:
    inset 0 0 0 0 var(--of-mod-color),                  /* placeholder */
    inset 3px 0 0 0 var(--of-mod-color),                /* gauche pleine */
    inset -3px 0 0 0 var(--of-mod-color);               /* droite pleine */
  /* Override : on veut juste les pattes courtes, pas la verticale entière.
     Switch sur 4 spans dédiés ci-dessous. */
  box-shadow: none;
}
[data-page="openframe"] .mod .of-mod-leg {
  position: absolute;
  width: 3px;
  height: 18px;
  background: var(--of-mod-color);
  pointer-events: none;
  z-index: 2;
}
[data-page="openframe"] .mod .of-mod-leg-tl { top: 0; left: 0; }
[data-page="openframe"] .mod .of-mod-leg-tr { top: 0; right: 0; }
[data-page="openframe"] .mod .of-mod-leg-bl { bottom: 0; left: 0; }
[data-page="openframe"] .mod .of-mod-leg-br { bottom: 0; right: 0; }
/* Hover : le cadre passe en or (lumière de loupe charte) */
[data-page="openframe"] .mod:hover { background: transparent; }
[data-page="openframe"] .mod:hover {
  --of-mod-color: var(--c-or);
}
[data-page="openframe"] .mod:hover .mod-foot { color: var(--c-or); }

/* Mod featured (mod.feat) : épaissit les rails à 5px pour démarquer */
[data-page="openframe"] .mod.feat::before,
[data-page="openframe"] .mod.feat::after {
  height: 5px;
}
[data-page="openframe"] .mod.feat .of-mod-leg {
  width: 5px;
  height: 22px;
}

/* =========================================================
   ANIMATION SIGNATURE OPENFRAME — « la méthode qui se pose »
   ---------------------------------------------------------
   Construction séquentielle d'un cadre Open Frame :
     1. Le rail HAUT se trace de gauche à droite      (0.7s)
     2. La PATTE haut-droite descend                  (0.3s, après rail haut)
     3. Le rail BAS se trace de droite à gauche       (0.7s)
     4. La PATTE bas-gauche monte                     (0.3s, après rail bas)
     5. Les pattes haut-gauche + bas-droite finalisent (0.3s, en parallèle)
   Total : ~1.6s — durée cohérente "Slow to scale".

   Métaphore : le cadre se construit comme un schéma technique,
   pas comme un widget qui s'allume. C'est la méthode visible.
   ========================================================= */

@media (prefers-reduced-motion: no-preference) {
  /* État initial : rails et pattes invisibles, scaleX/Y=0 */
  .of-frame:not(.in-view)::before,
  .of-frame:not(.in-view)::after {
    transform: scaleX(0);
    transform-origin: left center;
  }
  .of-frame:not(.in-view) .of-frame-leg-tl,
  .of-frame:not(.in-view) .of-frame-leg-tr,
  .of-frame:not(.in-view) .of-frame-leg-bl,
  .of-frame:not(.in-view) .of-frame-leg-br {
    transform: scaleY(0);
  }
  .of-frame:not(.in-view) .of-frame-leg-tr,
  .of-frame:not(.in-view) .of-frame-leg-tl { transform-origin: top center; }
  .of-frame:not(.in-view) .of-frame-leg-bl,
  .of-frame:not(.in-view) .of-frame-leg-br { transform-origin: bottom center; }

  /* Séquence à l'entrée du viewport */
  .of-frame.in-view::before {
    transform: scaleX(1);
    transform-origin: left center;
    transition: transform 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0s;
  }
  .of-frame.in-view .of-frame-leg-tr {
    transform: scaleY(1);
    transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1) 0.6s;
  }
  .of-frame.in-view::after {
    transform: scaleX(1);
    transform-origin: right center;
    transition: transform 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.85s;
  }
  .of-frame.in-view .of-frame-leg-bl {
    transform: scaleY(1);
    transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1) 1.45s;
  }
  .of-frame.in-view .of-frame-leg-tl,
  .of-frame.in-view .of-frame-leg-br {
    transform: scaleY(1);
    transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1) 1.6s;
  }

  /* Modules openframe : même séquence mais plus courte (rythme spec) */
  [data-page="openframe"] .mod:not(.in-view)::before,
  [data-page="openframe"] .mod:not(.in-view)::after {
    transform: scaleX(0);
  }
  [data-page="openframe"] .mod:not(.in-view)::before { transform-origin: left center; }
  [data-page="openframe"] .mod:not(.in-view)::after  { transform-origin: right center; }
  [data-page="openframe"] .mod:not(.in-view) .of-mod-leg {
    transform: scaleY(0);
  }
  [data-page="openframe"] .mod:not(.in-view) .of-mod-leg-tl,
  [data-page="openframe"] .mod:not(.in-view) .of-mod-leg-tr { transform-origin: top center; }
  [data-page="openframe"] .mod:not(.in-view) .of-mod-leg-bl,
  [data-page="openframe"] .mod:not(.in-view) .of-mod-leg-br { transform-origin: bottom center; }

  [data-page="openframe"] .mod.in-view::before {
    transform: scaleX(1);
    transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0s;
  }
  [data-page="openframe"] .mod.in-view .of-mod-leg-tr {
    transform: scaleY(1);
    transition: transform 0.25s cubic-bezier(0.22, 1, 0.36, 1) 0.4s;
  }
  [data-page="openframe"] .mod.in-view::after {
    transform: scaleX(1);
    transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.55s;
  }
  [data-page="openframe"] .mod.in-view .of-mod-leg-bl {
    transform: scaleY(1);
    transition: transform 0.25s cubic-bezier(0.22, 1, 0.36, 1) 0.95s;
  }
  [data-page="openframe"] .mod.in-view .of-mod-leg-tl,
  [data-page="openframe"] .mod.in-view .of-mod-leg-br {
    transform: scaleY(1);
    transition: transform 0.25s cubic-bezier(0.22, 1, 0.36, 1) 1.05s;
  }
}

/* =========================================================
   HERO RÉVÉLATION — page identité LinkZen (/linkzen)
   ---------------------------------------------------------
   - Cadre Open Frame XL autour du titre (rails + pattes)
   - Background fog pulsant subtil (halo or charte)
   - Split-text reveal mot par mot (anim au load, séquencée)
   - Compteur live time en mono dans la métadata top
   - 3 colonnes foot (sub-indicateurs) en bas
   - AUCUN CTA above the fold (Beagle-style, confiance retenue)
   ========================================================= */
.hero-identity {
  min-height: 100vh;
  padding-top: clamp(96px, 14vh, 160px);
  padding-bottom: clamp(60px, 8vh, 100px);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  position: relative;
  isolation: isolate;
}

/* Background fog : halo or qui pulse, posture cinématique */
.hero-identity-fog {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}
.hero-identity-fog::before {
  content: '';
  position: absolute;
  width: 80vw;
  height: 60vh;
  left: 50%;
  top: 55%;
  transform: translate(-50%, -50%);
  background: radial-gradient(
    ellipse 50% 40% at center,
    color-mix(in srgb, var(--c-or) 11%, transparent) 0%,
    transparent 70%
  );
  /* Perf : pas de filter blur (very cher GPU), le gradient soft suffit */
  animation: heroFogPulse 12s var(--ease-soft, ease-in-out) infinite;
  will-change: transform, opacity;
}
@keyframes heroFogPulse {
  0%, 100% { opacity: 0.6; transform: translate(-50%, -50%) scale(1); }
  50%      { opacity: 1;   transform: translate(-50%, -52%) scale(1.06); }
}

/* Eyebrow + cluster meta */
.hero-identity-meta {
  margin-bottom: clamp(40px, 6vh, 80px);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 16px;
  border-top: 1px solid var(--line-strong);
  padding-top: 16px;
}

/* Cadre Open Frame XL autour du titre — fidèle au logo officiel */
.hero-identity-frame {
  --frame-color: var(--c-vert-foret);  /* light = vert forêt charte */
  --frame-thickness: 5px;
  --frame-leg: 80px;                   /* fallback px ; override % via .of-frame */
  --frame-pad-x: clamp(20px, 4vw, 56px);
  --frame-pad-y: clamp(56px, 9vh, 112px);
  margin: 0 auto;
  width: 100%;
  max-width: 1280px;
  position: relative;
}
:root[data-theme="dark"] .hero-identity-frame {
  --frame-color: var(--c-vert-sauge);  /* dark = vert sauge charte (cohérent logo) */
}

/* Titre split-text reveal — anim au load séquentielle */
.hero-identity-title {
  font-family: var(--display);
  font-stretch: 88%;
  font-weight: 540;
  font-size: clamp(48px, 9vw, 144px);
  line-height: 0.94;
  letter-spacing: -0.034em;
  color: var(--ink);
  margin: 0;
  text-wrap: balance;
}
.hero-identity-title .line {
  display: block;
  overflow: hidden;
  /* Respiration suffisante haut + bas pour ascendantes (C, f, h) et
     descendantes (p, g, j). Les marges négatives compensent pour ne
     pas changer le rythme vertical entre lignes. */
  padding-block: 0.18em 0.18em;
  margin-block: -0.18em -0.18em;
}
/* Hero frame parallax — JS pilote scale + opacity + .is-behind sur scroll
   (cf. linkzen.js initHeroFrameParallax). Au-delà de 45% de progress de
   sortie hero, .is-behind passe les segments en z-index négatif → ils
   passent DERRIÈRE le titre, créant la sensation de profondeur. */
.hero-identity-frame {
  /* Conteneur stable, le titre h1 reste visible normalement */
  position: relative;
  z-index: 1;
}
.hero-identity-frame .hero-identity-title {
  position: relative;
  z-index: 2;  /* texte toujours au-dessus des segments par défaut */
}
.hero-identity-frame > .of-seg {
  /* Initial : devant le texte (z-index 1 par défaut de .of-mark/of-frame).
     Transition smooth de l'opacity et du transform. */
  transition: transform 0.15s linear, opacity 0.2s linear;
  transform-origin: center;
}
.hero-identity-frame.is-behind > .of-seg {
  z-index: 0;  /* passe DERRIÈRE le titre (qui est z-index 2) */
}

.hero-identity-title .word {
  display: inline-block;
  transform: translateY(110%);
  opacity: 0;
  transition:
    transform 1.1s cubic-bezier(0.16, 1, 0.3, 1),
    opacity 0.6s ease-out;
  /* Failsafe CSS : animation auto au chargement → si le JS plante
     (notamment sur certains forks Chromium type Comet), le contenu
     reste visible. La JS-driven .is-revealed reste prioritaire et
     garde la cascade de timings exacts. */
  animation: hero-word-failsafe 1.1s cubic-bezier(0.16, 1, 0.3, 1) 1.6s forwards;
}
@keyframes hero-word-failsafe {
  to { transform: translateY(0); opacity: 1; }
}
/* JS-driven : si .is-revealed est ajouté avant 1.6s (cas normal),
   la transition reprend la main sur l'animation et applique son timing. */
.hero-identity-title.is-revealed .word { animation: none; }
.hero-identity-title em {
  font-style: normal;               /* anti-Claude — pas d'italique titre */
  color: var(--c-vert-foret);
}
:root[data-theme="dark"] .hero-identity-title em {
  color: var(--c-or);
}

/* État révélé (ajouté en JS au load) */
.hero-identity-title.is-revealed .word {
  transform: translateY(0);
  opacity: 1;
}
/* Stagger par ligne puis par mot — séquence cinétique */
.hero-identity-title.is-revealed .line:nth-child(1) .word:nth-child(1) { transition-delay: 0.20s; }
.hero-identity-title.is-revealed .line:nth-child(1) .word:nth-child(2) { transition-delay: 0.28s; }
.hero-identity-title.is-revealed .line:nth-child(1) .word:nth-child(3) { transition-delay: 0.36s; }
.hero-identity-title.is-revealed .line:nth-child(1) .word:nth-child(4) { transition-delay: 0.44s; }
.hero-identity-title.is-revealed .line:nth-child(2) .word { transition-delay: 0.65s; }
.hero-identity-title.is-revealed .line:nth-child(3) .word:nth-child(1) { transition-delay: 0.85s; }
.hero-identity-title.is-revealed .line:nth-child(3) .word:nth-child(2) { transition-delay: 0.93s; }
.hero-identity-title.is-revealed .line:nth-child(3) .word:nth-child(3) { transition-delay: 1.01s; }

/* Lead sous le titre, dans le même cadre */
.hero-identity-lead {
  margin-top: clamp(32px, 5vh, 56px);
  max-width: 48ch;
  font-family: var(--sans);
  font-size: clamp(15px, 1.1vw, 18px);
  line-height: 1.55;
  color: var(--ink-dim);
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.8s ease 1.2s, transform 0.8s var(--ease-out, cubic-bezier(0.22, 1, 0.36, 1)) 1.2s;
  /* Failsafe : si JS ne déclenche pas .is-revealed, anim auto à 2.4s */
  animation: hero-fade-failsafe 0.8s ease 2.4s forwards;
}
.hero-identity-title.is-revealed ~ .hero-identity-lead,
.hero-identity-frame.is-revealed .hero-identity-lead {
  opacity: 1;
  transform: translateY(0);
  animation: none;
}
@keyframes hero-fade-failsafe {
  to { opacity: 1; transform: translateY(0); }
}

/* Foot 3 colonnes — petits indicateurs discrets */
.hero-identity-foot {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 3vw, 40px);
  margin-top: clamp(48px, 8vh, 96px);
  padding-top: 18px;
  border-top: 1px solid var(--line);
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.8s ease 1.6s, transform 0.8s var(--ease-out, cubic-bezier(0.22, 1, 0.36, 1)) 1.6s;
  /* Failsafe : anim auto à 2.8s si JS échoue */
  animation: hero-fade-failsafe 0.8s ease 2.8s forwards;
}
.hero-identity-foot.is-revealed {
  opacity: 1;
  transform: translateY(0);
  animation: none;
}
.hero-identity-foot-col {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.hero-identity-foot-col .mono.acc {
  font-family: var(--serif-display);     /* chiffres en serif Instrument */
  font-size: clamp(28px, 3vw, 44px);
  letter-spacing: -0.02em;
  text-transform: none;
  color: var(--c-vert-foret);
  font-weight: 400;
  line-height: 1;
}
:root[data-theme="dark"] .hero-identity-foot-col .mono.acc {
  color: var(--c-or);
}

@media (max-width: 720px) {
  .hero-identity-foot { grid-template-columns: 1fr; gap: 16px; }
  .hero-identity-foot-col .mono.acc { font-size: 28px; }
}

/* Live time tabular pour stabilité visuelle */
#heroLiveTime {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

