/* ═══════════════════════════════════════════════════════════════
   AstraLaika — First in orbit · "Stellar Plan" edition
   Soviet constructivism × Asimov retro-futurism, kept modern and
   corporate: cream paper, black ink, cosmic red, one gold accent.
   Signature glyph: the five-pointed star ★.
   ═══════════════════════════════════════════════════════════════ */

/* ── Self-hosted typefaces (SIL OFL) — no Google Fonts request ── */
@font-face {
  font-family: "Anton";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/anton-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "PT Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/ptmono-400-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "PT Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/ptmono-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "PT Serif";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/ptserif-400i-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "PT Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/ptserif-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "PT Serif";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/ptserif-700-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}


/* Extra subsets: without these, any character outside Latin-1 falls back to a
   system font mid-word (Vietnamese, Hungarian, Czech, Polish, Romanian...).
   unicode-range means a browser only downloads the subset it actually needs. */
@font-face {
  font-family: "Anton";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/anton-400-latin-ext.woff2") format("woff2");  /* latin-ext : pl, cs, hu, ro, tr */
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Anton";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/anton-400-vietnamese.woff2") format("woff2");  /* vietnamese */
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: "PT Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/ptserif-400-latin-ext.woff2") format("woff2");  /* latin-ext */
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "PT Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/ptserif-400-cyrillic.woff2") format("woff2");  /* cyrillic : ru, uk */
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "PT Serif";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/ptserif-700-latin-ext.woff2") format("woff2");  /* latin-ext */
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "PT Serif";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/ptserif-700-cyrillic.woff2") format("woff2");  /* cyrillic */
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "PT Serif";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/ptserif-400i-latin-ext.woff2") format("woff2");  /* latin-ext */
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "PT Serif";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/ptserif-400i-cyrillic.woff2") format("woff2");  /* cyrillic */
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* ── Design tokens ────────────────────────────────────────────── */
:root {
  --paper: #F3EDE0;
  --paper-2: #EAE2CF;
  --card: #FBF7EC;
  --ink: #16130E;
  --ink-soft: #57503F;
  --faded: #B4AA93;
  --red: #C8321E;
  --red-dark: #9E2412;
  --gold: #C89B3C;
  /* Le jaune de Vector, repris du bandeau des applications de
     LaikaSuite : les deux sites partagent la même palette de presse. */
  --yellow: #D9A017;
  --line: rgba(22, 19, 14, 0.16);

  --font-d: "Anton", "Arial Black", system-ui, sans-serif;
  --font-d-weight: 400;             /* Anton is single-weight; scripts it lacks go heavier */
  --font-b: "PT Serif", Georgia, serif;
  --font-m: "PT Mono", monospace;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --bar-h: 68px;
  --pad: clamp(20px, 4.5vw, 64px);
}

/* ── Base ─────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* overflow-x: clip stops off-stage carousel cards (position:absolute) from
   widening the layout viewport on mobile — which used to stretch the fixed
   nav bar past the screen and leave an empty right column that pinch-zoom
   couldn't reset. `clip` (not `hidden`) avoids creating a scroll container,
   so scroll-padding-top / smooth scrolling below keep working. */
html { scroll-behavior: smooth; scroll-padding-top: calc(var(--bar-h) + 16px); overflow-x: clip; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-b);
  font-size: 16px;
  line-height: 1.6;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--red); color: var(--paper); }

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

h1, h2, h3 {
  font-family: var(--font-d);
  font-weight: var(--font-d-weight);
  text-transform: uppercase;
  line-height: 1.02;
  letter-spacing: 0.01em;
}

.mono {
  font-family: var(--font-m);
  font-size: 12.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}


/* ── Display font for the scripts Anton does not cover ──────────
   Anton is Latin-only. Without this, headings in Cyrillic/Greek/CJK/Arabic/
   Devanagari/Thai fall back to whatever the OS picks — and to weight 400,
   so they look thin next to Anton. We name a face per script and go heavy. */
html:lang(ru), html:lang(uk), html:lang(el) {
  --font-d: "Arial Black", "Noto Sans", "DejaVu Sans", system-ui, sans-serif;
  --font-d-weight: 900;
}
html:lang(zh) {
  --font-d: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "Source Han Sans SC", sans-serif;
  --font-d-weight: 800;
}
html:lang(ja) {
  --font-d: "Hiragino Kaku Gothic ProN", "Yu Gothic", Meiryo, "Noto Sans CJK JP", "Source Han Sans JP", sans-serif;
  --font-d-weight: 800;
}
html:lang(ko) {
  --font-d: "Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans CJK KR", "Source Han Sans KR", sans-serif;
  --font-d-weight: 800;
}
html:lang(ar) {
  --font-d: "Noto Naskh Arabic", "Geeza Pro", Tahoma, "Segoe UI", sans-serif;
  --font-d-weight: 700;
}
html:lang(hi) {
  --font-d: "Noto Sans Devanagari", "Nirmala UI", "Kohinoor Devanagari", sans-serif;
  --font-d-weight: 700;
}
html:lang(th) {
  --font-d: "Noto Sans Thai", "Leelawadee UI", Thonburi, sans-serif;
  --font-d-weight: 700;
}
/* Marks in these scripts stack high or hang low: 1.02 would collide. */
html:lang(ar) :is(h1, h2, h3),
html:lang(hi) :is(h1, h2, h3),
html:lang(th) :is(h1, h2, h3) { line-height: 1.3; }
html:lang(zh) :is(h1, h2, h3),
html:lang(ja) :is(h1, h2, h3),
html:lang(ko) :is(h1, h2, h3) { line-height: 1.15; letter-spacing: 0; }

/* ── Stamp preloader ──────────────────────────────────────────── */
.stamp-loader {
  position: fixed; inset: 0;
  z-index: 3000;
  display: grid; place-items: center;
  background: var(--paper);
  transition: opacity 0.45s var(--ease), visibility 0.45s;
}
.stamp-loader.done { opacity: 0; visibility: hidden; pointer-events: none; }
.stamp-mark {
  font-family: var(--font-d);
  font-weight: var(--font-d-weight);
  font-size: clamp(26px, 6vw, 54px);
  color: var(--red);
  border: 4px solid var(--red);
  padding: 10px 26px;
  transform: rotate(-4deg);
  animation: stamp-slam 0.5s cubic-bezier(0.34, 1.4, 0.64, 1) both;
}
@keyframes stamp-slam {
  from { transform: rotate(-11deg) scale(2); opacity: 0; }
  to { transform: rotate(-4deg) scale(1); opacity: 1; }
}

/* ── Top bar ──────────────────────────────────────────────────── */
.bar {
  position: fixed; top: 0; left: 0; right: 0;
  z-index: 1000;
  height: var(--bar-h);
  display: flex; align-items: center; gap: 22px;
  padding: 0 var(--pad);
  background: var(--paper);
  border-top: 6px solid var(--red);
  border-bottom: 3px solid var(--ink);
}

.brand { display: flex; align-items: center; gap: 12px; }
.brand-name { font-family: var(--font-d); font-weight: var(--font-d-weight); font-size: 18px; letter-spacing: 0.05em; }

/* Brand logo: Laika in her helmet (assets/logo.svg, cosmic red) */
.logo-slot {
  display: grid; place-items: center;
  width: 40px; height: 40px;
}
.logo-slot img { width: 100%; height: 100%; object-fit: contain; transition: transform 0.3s var(--ease); }
.brand:hover .logo-slot img { transform: rotate(-10deg) scale(1.1); }

.bar-nav { display: flex; margin-left: auto; }
.bar-nav a { padding: 8px 12px; border-bottom: 3px solid transparent; transition: border-color 0.15s, color 0.15s; }
.bar-nav a:hover { border-bottom-color: var(--red); color: var(--red-dark); }

/* Language dropdown (opens downward) */
.lang { position: relative; }
.lang-current {
  padding: 7px 12px;
  font-size: 12px;
  letter-spacing: 0.08em;
  border: 2px solid var(--ink);
  background: var(--card);
  transition: background 0.15s, color 0.15s;
}
.lang-current:hover, .lang.is-open .lang-current { background: var(--ink); color: var(--paper); }
.lang-menu {
  position: absolute;
  top: calc(100% + 8px); right: 0;
  min-width: 200px;
  max-height: min(62vh, 460px);
  overflow-y: auto;
  background: var(--card);
  border: 2px solid var(--ink);
  box-shadow: 6px 6px 0 rgba(22, 19, 14, 0.16);
  opacity: 0;
  transform: translateY(-8px);
  visibility: hidden;
  transition: opacity 0.18s var(--ease), transform 0.18s var(--ease), visibility 0.18s;
  z-index: 1200;
}
/* La barre de défilement de la page, teintée du rouge de l'affiche.
   Elle ne l'était que dans le menu des langues : le reste du site laissait
   la barre grise du système, alors que c'est l'un des rares endroits où le
   navigateur accepte de porter une couleur de marque sur toute la hauteur
   de l'écran. Carrée, comme tout le reste — un pouce arrondi serait la
   seule rondeur du site. */
/* Deux lignes, là où il y en avait huit. Un bloc ::-webkit-scrollbar suivait
   celles-ci — onze pixels, une bordure transparente de trois, un pouce teinté
   — et rien n'en était appliqué : un navigateur qui comprend `scrollbar-width`
   ou `scrollbar-color` abandonne les pseudo-éléments WebKit, et la règle
   ci-dessus pose les deux sur `*`. Chaque élément de chaque page se retirait
   donc du bloc écrit pour lui. Mesuré avant d'être cru : un débordement
   horizontal forcé donne une barre de 4 px en rgb(200,50,30) — le « thin » des
   propriétés standard, pas les onze pixels que les règles mortes réclamaient.

   Les deux axes sont couverts : ni l'une ni l'autre propriété ne connaît de
   direction, donc la barre horizontale est teintée par la même ligne que la
   verticale.

   Ce que cela coûte : Safari antérieur à 18.2 ne connaît aucune des deux
   propriétés standard et n'obéissait qu'au bloc supprimé. Ces lecteurs
   retrouvent la barre du système — une couleur, pas une fonction. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--red) transparent;
}

/* The OS scrollbar, kept at its native width and simply tinted poster red. */
.lang-menu { scrollbar-color: var(--red) var(--paper-2); }
.lang.is-open .lang-menu { opacity: 1; transform: translateY(0); visibility: visible; }
.lang-menu button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 10px 14px;
  font-size: 12px;
  letter-spacing: 0.08em;
  border-bottom: 1px solid var(--line);
  transition: background 0.12s;
}
.lang-menu button:last-child { border-bottom: 0; }
.lang-menu button:hover { background: var(--paper-2); }
.lang-menu button[aria-selected="true"] { color: var(--red); }
.lang-menu button[aria-selected="true"]::before { content: "★ "; }

/* ── Buttons: notched constructivist plates ───────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-d);
  font-size: 14px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 13px 26px;
  clip-path: polygon(0 0, calc(100% - 14px) 0, 100% 14px, 100% 100%, 0 100%);
  transition: transform 0.12s var(--ease), box-shadow 0.12s var(--ease), background 0.15s;
}
.btn-red { background: var(--red); color: var(--paper); }
.btn-red:hover { background: var(--red-dark); transform: translate(-2px, -2px); box-shadow: 4px 4px 0 var(--ink); }
/* Le contour suit la coupe du coin.
   `border` dessine sur la BOÎTE, `clip-path` découpe ensuite : la
   diagonale du coin n'avait donc aucun trait, et le bord du haut
   s'arrêtait en l'air. Sur un bouton large on lit encore une boîte ; sur
   un bouton étroit — un téléphone, où le libellé passe à la ligne — on ne
   lit plus qu'un tiret flottant. Le contour est donc peint : un fond
   d'encre découpé à 14 px, et par-dessus un fond de papier découpé à
   12 px et rentré de deux — la diagonale garde alors son trait comme les
   quatre côtés. */
.btn-line {
  border: 0;
  position: relative;
  background: var(--ink);
}
.btn-line::before {
  content: "";
  position: absolute;
  inset: 2px;
  background: var(--paper);
  clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 0 100%);
  transition: background 0.15s;
}
.btn-line > * { position: relative; }
.btn-line:hover { color: var(--paper); transform: translate(-2px, -2px); box-shadow: 4px 4px 0 var(--red); }
.btn-line:hover::before { background: var(--ink); }
.btn:active { transform: translate(0, 0); box-shadow: none; }
.bar-cta { flex-shrink: 0; }

.burger { display: none; padding: 8px 14px; border: 2px solid var(--ink); font-weight: 700; }
.burger:hover, .burger.is-open { background: var(--ink); color: var(--paper); }

/* Fullscreen menu (mobile) */
.menu-overlay {
  position: fixed; inset: calc(var(--bar-h)) 0 0 0;
  z-index: 900;
  background: var(--paper);
  padding: 26px var(--pad);
  transform: translateY(-103%);
  transition: transform 0.35s var(--ease);
  visibility: hidden;
}
.menu-overlay.is-open { transform: translateY(0); visibility: visible; }
.menu-overlay nav { display: flex; flex-direction: column; }
.menu-overlay a {
  font-family: var(--font-d);
  font-weight: var(--font-d-weight);
  font-size: clamp(30px, 8.5vw, 50px);
  text-transform: uppercase;
  padding: 16px 4px;
  border-bottom: 2px solid var(--ink);
}
.menu-overlay a:hover { color: var(--red); }
.menu-note { margin-top: 26px; color: var(--ink-soft); }

/* ── Scroll reveals ───────────────────────────────────────────── */
.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
  transition-delay: calc(var(--i, 0) * 0.09s);
}
.reveal.in { opacity: 1; transform: translateY(0); }

/* ── Hero ─────────────────────────────────────────────────────── */
.hero {
  position: relative;
  /* La structure de la première page du hub, reprise à l'identique : une
     hauteur d'écran exactement — vh en secours, dvh en vrai —, la rangée
     du milieu centrée, le bandeau en dernière rangée DANS ces 100dvh. On
     arrive sur une affiche complète ; le site se découvre en défilant. */
  /* La hauteur de l'appareil, exactement — pas « au moins ». Le héros
     était en `min-height` et grandissait dès que son contenu dépassait :
     mesuré à 892 px sur une vue de 800, 884 sur une vue de 700, si bien
     que le bulletin d'état passait sous la ligne de flottaison sur tout
     écran un peu court. C'est le titre qui débordait — il n'était
     dimensionné que sur la LARGEUR. */
  height: 100vh;
  height: 100dvh;
  display: flex; flex-direction: column;
  padding: calc(var(--bar-h) + clamp(12px, 3vh, 44px)) var(--pad) 0;
  overflow: hidden;
}
/* Une hauteur d'écran exactement — sauf quand le contenu n'y tient pas.
   « Exactement 100dvh » et « overflow: hidden » se retournent l'un contre
   l'autre sur un écran couché : mesuré sur un téléphone en paysage, la
   rangée du milieu réclamait 288 px pour 170 disponibles, si bien que le
   sur-titre était rogné par le haut et que les DEUX boutons d'appel du
   site — « Découvrir nos produits », « Lancer un projet » — se retrouvaient
   entièrement hors du cadre, invisibles et incliquables. Une affiche qui
   cache son propre appel n'est plus une affiche.

   Au-dessous de 560 px de haut, et seulement couché — un portrait étroit
   n'a jamais eu le défaut —, le héros cesse donc de prétendre faire une
   hauteur d'écran et prend celle de son contenu. Le plancher reste
   `min-height`, si bien qu'une fenêtre courte mais assez haute pour tout
   afficher garde l'affiche pleine et ne change pas d'aspect. */
@media (max-height: 560px) and (orientation: landscape) {
  .hero {
    height: auto;
    min-height: 100vh;
    min-height: 100dvh;
  }
  .hero-inner { overflow: visible; }
  /* Le rythme se resserre avant de s'allonger : on préfère un héros qui
     tient encore presque dans l'écran à un héros qui déborde de moitié. */
  .hero-kicker { margin-bottom: clamp(6px, 1.5vh, 16px); }
  .hero .hero-sub { margin-block: clamp(6px, 1.5vh, 16px) 0; }

  /* Couché, la place manque en hauteur et abonde en largeur : le
     paragraphe cesse donc de s'arrêter à sa mesure de lecture. Mesuré sur
     un téléphone en paysage, il tenait 540 px sur 768 disponibles et
     s'écrivait en trois lignes là où il en tient deux — trois lignes
     prises sur la seule ressource rare. La mesure de lecture est une règle
     juste quand la hauteur est libre ; ici elle coûte plus qu'elle ne
     rend. */
  .hero .hero-sub { max-width: none; }
}

#chart { position: absolute; inset: 0; width: 100%; height: 100%; }
/* Sous 900 px, la colonne de texte prend toute la largeur et le diagramme
   passerait derrière le titre. Un décor qui gêne la lecture n'est plus un
   décor : il s'efface. */
@media (max-width: 900px) { #chart { display: none; } }

/* Le diagramme revient dès qu'il y a de la place à côté du texte.

   Il s'efface sous 900 px de large parce que la colonne de texte y prend
   toute la largeur et qu'il passerait derrière le titre. Couché, cette
   raison tombe : mesuré à 844 × 390, le texte s'arrête à 578 et il reste
   266 px de vide à droite — c'est le « il ne prend pas toute la longueur »
   du propriétaire. Le seuil de 700 px garde la règle d'origine là où elle
   vaut encore : sous cette largeur, même couché, il n'y a pas de place à
   côté. */
@media (max-height: 560px) and (orientation: landscape) and (min-width: 700px) {
  #chart { display: block; }
}

/* Poster sunburst: soft red rays from the lower-left corner */
.hero-rays {
  position: absolute; inset: 0;
  pointer-events: none;
  background: repeating-conic-gradient(from -12deg at -4% 104%,
    rgba(200, 50, 30, 0.055) 0 3.4deg, transparent 3.4deg 8deg);
}

.hero-inner {
  position: relative; z-index: 2; flex: 1;
  max-width: 1180px; margin: 0 auto; width: 100%;
  /* Le hub centre sa rangée du milieu ; sans ce centrage le titre restait
     collé sous la barre et un vide mort s'ouvrait au-dessus du bandeau —
     c'est ce vide qui donnait l'impression que la page ne prenait pas
     l'écran alors que sa hauteur était déjà la bonne. */
  display: flex; flex-direction: column; justify-content: center;
  /* `min-height: 0`, sans quoi un enfant flex refuse de descendre sous la
     hauteur de son contenu : la rangée du milieu poussait alors le pied
     HORS du héros, et le bulletin repassait sous la ligne de flottaison
     sur les écrans courts — 742 px de bas pour une vue de 700. */
  min-height: 0;
  overflow: hidden;
}
.hero-kicker { color: var(--ink-soft); margin-bottom: clamp(12px, 3vh, 40px); }

/* Le titre se règle sur la plus contraignante des deux dimensions. Sur un
   portable large mais bas — 1440 × 700 — 12,5vw donnait 180 px par ligne,
   soit 360 px de titre à eux seuls, et le héros débordait. `min()` prend
   la plus petite des deux mesures : la largeur commande sur un écran
   étroit, la hauteur sur un écran écrasé. */
.hero-title { font-size: clamp(46px, min(12.5vw, 17vh), 180px); }
.hero-title .red { color: var(--red); }
/* The reveal mask (overflow:hidden) would clip stacked diacritics that rise
   above the caps — Ầ Ỹ Ạ (vi), Ő (hu), Ě (cs). Padding opens the mask, the
   negative margin cancels it so the layout does not move. */
.hero-title .line {
  display: block; overflow: hidden;
  padding-top: 0.20em; margin-top: -0.20em;
  padding-bottom: 0.06em; margin-bottom: -0.06em;
}
.hero-title .line-inner { display: inline-block; transform: translateY(112%); }
body.is-ready .hero-title .line-inner { animation: rise 0.9s var(--ease) forwards; }
body.is-ready .hero-title .line:nth-child(2) .line-inner { animation-delay: 0.12s; }
@keyframes rise { to { transform: translateY(0); } }

.hero-sub {
  max-width: 540px;
  margin-top: 28px;
  font-size: clamp(16.5px, 2vw, 19px);
  color: var(--ink-soft);
}
.hero-cta { display: flex; gap: 14px; flex-wrap: wrap; margin: 38px 0 60px; }
.hero .reveal { transition-delay: calc(0.45s + var(--i, 0) * 0.12s); }
.hero .hero-sub { margin-block: clamp(8px, 2vh, 28px) 0; --i: 1; }
.hero .hero-cta { --i: 2; }

/* Bulletin strip: black band with key figures */
/* Le pied du héros : le ruban rouge puis la bande d'encre, tous deux dans
   les 100dvh de la première image. Le ruban vivait sous la section, si bien
   qu'on arrivait sur une affiche close par le bulletin et qu'il fallait
   défiler pour le voir — un bas de page au-dessus du bas de page. */
.hero-foot {
  position: relative; z-index: 2;
  margin: auto calc(-1 * var(--pad)) 0;
}
.bulletin {
  /* Les trois mentions au centre, l'étoile au bord. Elles étaient tassées
     à gauche — 64 à 818 sur une bande de 1440 — pendant que l'étoile,
     poussée par une marge automatique, filait à 1356 : un tiers de texte,
     deux tiers de vide, et un astérisque perdu à l'autre bout. L'étoile
     sort du flux pour que le centrage porte sur ce qui se lit. */
  position: relative;
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
  gap: clamp(16px, 3.5vw, 44px);
  padding: 15px calc(var(--pad) + 28px);
  background: var(--ink);
  color: var(--paper);
}
/* Les deux étoiles du bulletin, tournant sur elles-mêmes. Taillées contre
   le texte du bulletin plutôt qu'héritées de lui : à 1em elles se lisaient
   comme de la ponctuation, alors qu'elles bornent la ligne.

   Elles sont deux et non une : une seule à droite faisait pencher la
   bande, puisque les trois mentions sont centrées sur la page entière et
   qu'un astérisque isolé à un bout demande son pendant à l'autre. Même
   écart au bord, même rotation, même sens — c'est une paire, pas un reflet
   (un miroir ferait tourner celle de gauche à l'envers, et l'œil le voit). */
.bulletin-star {
  position: absolute;
  right: var(--pad);
  top: 50%;
  transform-origin: center;
  margin-top: -0.5em;
  color: var(--red);
  font-size: 1.75em;
  line-height: 1;
  animation: star-spin 9s linear infinite;
}
.bulletin-star--start { right: auto; left: var(--pad); }
@media (prefers-reduced-motion: reduce) { .bulletin-star { animation: none; } }
@keyframes star-spin { to { transform: rotate(360deg); } }

/* ── Ticker ───────────────────────────────────────────────────── */
/* Le jaune du bandeau des applications de LaikaSuite — la teinte de
   Vector, #D9A017 — plutôt que le rouge de l'affiche : le ruban ferme le
   héros juste au-dessus d'une bande d'encre, et deux rouges superposés
   perdaient la plaque de la barre. L'encre passe en texte, parce qu'un
   papier clair sur ce jaune ne tient pas le contraste. */
.ticker {
  overflow: hidden;
  background: var(--yellow);
  color: var(--ink);
  padding: 10px 0;
}
.ticker-track { display: flex; width: max-content; animation: ticker 28s linear infinite; }
.ticker:hover .ticker-track { animation-play-state: paused; }
.ticker-track span { font-size: 13px; letter-spacing: 0.18em; white-space: nowrap; }
@keyframes ticker { to { transform: translateX(-50%); } }

/* ── Sections ─────────────────────────────────────────────────── */
.section {
  max-width: 1180px;
  margin: 0 auto;
  padding: clamp(80px, 12vh, 150px) var(--pad);
}
.section-head { margin-bottom: clamp(36px, 6vh, 60px); }
.section-tag { display: inline-flex; align-items: center; gap: 12px; color: var(--ink-soft); margin-bottom: 18px; }
.section-tag b { background: var(--red); color: var(--paper); padding: 3px 9px; font-weight: 400; }
.section-head h2 { font-size: clamp(32px, 5.4vw, 68px); max-width: 22ch; }
.section-sub { max-width: 600px; margin-top: 18px; color: var(--ink-soft); font-size: 16.5px; }

/* ── Directives (who we are) ──────────────────────────────────── */
.directives {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 20px;
}
.directive {
  background: var(--card);
  border: 2px solid var(--ink);
  border-left: 8px solid var(--red);
  padding: 26px 24px 28px;
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease),
              opacity 0.7s var(--ease);
}
.directive:hover { transform: translateY(-5px); box-shadow: 8px 8px 0 rgba(22, 19, 14, 0.14); }
.directive-label { color: var(--red); margin-bottom: 14px; }
.directive h3 { font-size: 22px; margin-bottom: 12px; }
.directive-body { color: var(--ink-soft); font-size: 15px; }

.open-note { margin-top: 36px; color: var(--ink-soft); text-align: center; }
.open-note a { color: var(--red); border-bottom: 2px solid var(--red); margin-left: 8px; }
.open-note a:hover { background: var(--red); color: var(--paper); }

/* ── The Catalogue: stamp-sheet carousel ──────────────────────── */
.catalogue {
  max-width: none;
  background:
    repeating-linear-gradient(45deg, transparent 0 30px, rgba(22, 19, 14, 0.028) 30px 31px),
    var(--paper-2);
  border-top: 3px solid var(--ink);
  border-bottom: 3px solid var(--ink);
}
.catalogue .section-head { max-width: 1180px; margin-left: auto; margin-right: auto; }

.sheet {
  position: relative;
  height: 540px;
  outline: none;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}
.sheet:focus-visible { outline: 3px solid var(--red); outline-offset: 4px; }
.sheet-stage { position: absolute; inset: 0; perspective: 1200px; }

/* Postage-stamp product card */
.stamp {
  position: absolute;
  top: 50%; left: 50%;
  width: min(340px, 80vw);
  background: var(--card);
  border: 1px solid var(--line);
  padding: 14px;
  box-shadow: 0 14px 34px rgba(22, 19, 14, 0.16);
  will-change: transform, opacity;
}
/* Perforation: punched dots along the top and bottom edges */
.stamp::before, .stamp::after {
  content: "";
  position: absolute; left: 6px; right: 6px;
  height: 12px;
  background-image: radial-gradient(circle 4.5px at 8px 6px, var(--paper-2) 4.4px, transparent 4.6px);
  background-size: 17px 12px;
  background-repeat: repeat-x;
}
.stamp::before { top: -6px; }
.stamp::after { bottom: -6px; }

.stamp-in { border: 2px solid var(--ink); display: flex; flex-direction: column; height: 100%; background: var(--card); }
.stamp.is-front[data-live] { cursor: pointer; }
.stamp.is-front[data-live]:hover .stamp-in { border-color: var(--red); }
.stamp.is-front.is-secret { cursor: not-allowed; }

.stamp-head {
  display: flex; justify-content: space-between; align-items: center;
  padding: 9px 14px;
  border-bottom: 2px solid var(--ink);
}
.stamp-num { color: var(--ink-soft); }
/* Rubber-stamp status mark */
.rubber {
  font-family: var(--font-m);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 2px 8px;
  border: 2px solid;
  transform: rotate(-6deg);
}
.rubber.live, .rubber.launch { color: var(--red); border-color: var(--red); }
.rubber.secret { color: var(--paper); background: var(--red); border-color: var(--red-dark); font-weight: 700; }

.stamp-media {
  position: relative;
  height: 175px;
  display: grid; place-items: center;
  border-bottom: 2px solid var(--ink);
  overflow: hidden;
}
.stamp-media.live, .stamp-media.launch { background: var(--red); }
/* contain: keeps non-square logos (GanttGen) from being stretched; a square
   logo (CaptainFork) still renders at its full 104px height. */
.stamp-media img { width: 140px; height: 104px; object-fit: contain; }
/* Forming star: pulsing glyph behind the classified stamp */
.stamp-embryo {
  font-size: 66px;
  line-height: 1;
  color: transparent;
  -webkit-text-stroke: 2.5px var(--ink);
  animation: embryo-pulse 3.4s ease-in-out infinite;
}
@keyframes embryo-pulse {
  0%, 100% { transform: scale(0.92) rotate(0deg); opacity: 0.7; }
  50% { transform: scale(1.08) rotate(18deg); opacity: 1; }
}

/* Classified file (TOP-SECRET, USSR style): red-hatched field,
   double-framed diagonal stamp, ACCESS DENIED flash on click */
.stamp-media.secret {
  background:
    repeating-linear-gradient(-45deg, transparent 0 10px, rgba(200, 50, 30, 0.06) 10px 20px),
    var(--paper-2);
}
/* The hatched paper above belongs to the anonymous file alone, whose whole
   point is that there is nothing to show. Everything that HAS a mark gets
   the red field and the white mark, `.live` and `.launch` alike — which is
   already the rule two lines up, and is why SEF needs no exception here.
   It was written as one, briefly, when SEF was `secret`; the exception
   went away with the status rather than being kept and worked around. */
.stamp-media.secret .stamp-embryo { -webkit-text-stroke: 2.5px var(--red); opacity: 0.35; animation: none; }
.stamp-secret {
  position: absolute;
  text-align: center;
  color: var(--red);
  border: 4px double var(--red);
  padding: 8px 18px;
  transform: rotate(-12deg);
  background: rgba(251, 247, 236, 0.72);
}
.stamp-secret b {
  display: block;
  font-family: var(--font-d);
  font-weight: 400;
  font-size: 27px;
  letter-spacing: 0.1em;
  line-height: 1;
}
.stamp-secret i {
  display: block;
  margin-top: 5px;
  font-family: var(--font-m);
  font-style: normal;
  font-size: 9px;
  letter-spacing: 0.3em;
  white-space: nowrap;
}
.stamp-denied {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background: rgba(158, 36, 18, 0.93);
  color: var(--paper);
  text-align: center;
  line-height: 2;
  opacity: 0;
  pointer-events: none;
}

.stamp-body { padding: 16px 16px 14px; flex: 1; }
.stamp-body h3 { font-size: 24px; margin-bottom: 8px; }
.stamp-body p { color: var(--ink-soft); font-size: 14px; min-height: 66px; }
.stamp-tags { margin-top: 10px; color: var(--faded); font-size: 11px; }

.stamp-foot {
  display: flex; justify-content: space-between; align-items: center;
  padding: 10px 16px;
  border-top: 2px solid var(--ink);
}
.stamp-link { font-family: var(--font-m); font-size: 11.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--red); font-weight: 700; }
.stamp-soon { font-family: var(--font-m); font-size: 11.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-soft); }
.stamp-foot .star { color: var(--red); }

.sheet-ui {
  display: flex; align-items: center; justify-content: center;
  gap: 22px;
  margin-top: 26px;
}
.sheet-arrow {
  width: 48px; height: 48px;
  border: 2px solid var(--ink);
  background: var(--card);
  font-size: 17px;
  display: grid; place-items: center;
  transition: background 0.15s, color 0.15s;
}
.sheet-arrow:hover { background: var(--ink); color: var(--paper); }
.sheet-dots { display: flex; gap: 10px; }
.sheet-dots button {
  width: 10px; height: 10px;
  padding: 0;
  border: 2px solid var(--ink);
  background: transparent;
  transition: background 0.15s, transform 0.2s var(--ease);
}
.sheet-dots button.is-active { background: var(--red); border-color: var(--red); transform: rotate(45deg) scale(1.15); }
.sheet-hint { text-align: center; margin-top: 16px; color: var(--faded); }

/* ── LaikaSuite band ──────────────────────────────────────────────
   The suite is glass; this site is print, and the band stays print.
   What travels across is the marks and their hues — never the material.
   So: an ink plate, solid rules, hard offset shadows, no translucency
   and no blur anywhere below this line.
   The catalogue above already closes on a 3px ink rule, so the band
   opens without one of its own. */
.suite-band {
  background: var(--ink);
  color: var(--paper);
  border-bottom: 3px solid var(--ink);
  padding: clamp(46px, 7vh, 76px) 0 clamp(40px, 6vh, 64px);
  /* The rail's track is wider than the screen by design — clip it here so
     it can never widen the layout viewport. */
  overflow: hidden;
}
/* Copy left, the way into the suite right — the band fills its width
   instead of trailing off, and both stack once there is no width to
   share. */
.suite-head {
  max-width: 1180px;
  margin: 0 auto clamp(32px, 5vh, 48px);
  padding: 0 var(--pad);
  display: flex; align-items: flex-end; justify-content: space-between;
  flex-wrap: wrap;
  gap: 26px 40px;
}
.suite-copy { max-width: 700px; }
.suite-kicker { color: var(--gold); margin-bottom: 16px; }
.suite-line {
  font-size: clamp(28px, 4.6vw, 56px);
  max-width: 20ch;
}
.suite-sub {
  max-width: 620px;
  margin-top: 18px;
  font-size: clamp(15.5px, 1.8vw, 18px);
  /* Paper at three quarters: the band's body copy, one step under the
     headline, without introducing a colour the palette does not have. */
  color: rgba(243, 237, 224, 0.74);
}
/* .btn-red drops an ink shadow on hover, which is invisible on ink. */
.suite-cta:hover { box-shadow: 4px 4px 0 var(--paper); }

/* Marquee: one row, printed twice. The animation walks the track by
   exactly half its width, so the copy lands where the original started
   and the seam is never visible. */
.suite-rail { overflow: hidden; }
.suite-track {
  display: flex;
  width: max-content;
  --rail-time: 60s;                  /* replaced from the measured width */
  animation: suite-rail var(--rail-time) linear infinite;
}
/* The bottom padding is the plates' offset shadow: without it the rail's
   own clipping would cut the shadow off. */
.suite-row { display: flex; gap: 14px; padding: 4px 14px 12px 0; }
@keyframes suite-rail { to { transform: translateX(-50%); } }
/* In RTL the track hangs off the left edge instead of the right, so the
   seamless direction is the mirror one — which is also the direction the
   reader is travelling. */
[dir="rtl"] .suite-track { animation-name: suite-rail-rtl; }
@keyframes suite-rail-rtl { to { transform: translateX(50%); } }

/* Hover parks the band so a mark can actually be clicked; focus-within
   does the same for a keyboard, which cannot chase a moving target. */
.suite-rail:hover .suite-track,
.suite-rail:focus-within .suite-track { animation-play-state: paused; }

/* One mark: the suite's tile on this site's plate. */
/* The tile of the two product plates, brought to the application marks:
   an ink outline, rounded corners and a hard offset shadow — the drawn box
   the rest of the site uses for anything that is an object rather than a
   surface. It already had the outline and the shadow; the corners are what
   the effect actually rests on.

   Two things of `.fork-logo` are deliberately NOT taken. Its float
   animation would set fourteen tiles bobbing out of phase along a rail
   that already scrolls — two motions competing for the same eye. And its
   `rotate(-2deg)` reads as a snapshot dropped on a desk when there is one
   of them; on fourteen in a row it reads as fourteen tiles hung crooked.
   A single object may be casual, a series may not.

   The shadow stays red rather than taking the plates' ink: this band is
   printed ON ink, and an ink shadow there is a shadow nobody sees. */
.suite-mark {
  flex: none;
  display: grid;
  justify-items: center;
  gap: 9px;
  width: 122px;
  padding: 16px 10px 13px;
  background: var(--card);
  border: 2px solid var(--ink);
  border-radius: 22px;
  box-shadow: 5px 5px 0 var(--red);
  transition: transform 0.14s var(--ease), box-shadow 0.14s var(--ease);
}
/* The icon itself gets the drawn box: ink outline, rounded corners, hard
   shadow — the tile you would tap on a home screen, which is what these
   marks are. The plate around it keeps its own outline, so the two nest:
   the outer box is the card, the inner one is the application.

   The shadow is ink here, not the plate's red. It falls on the card's own
   paper rather than on the band's ink, so it is visible — and an icon
   whose shadow matched the plate's would read as one shape with a thick
   red edge instead of two objects. */
/* The icon itself gets the drawn box: ink outline, rounded corners, hard
   shadow — the tile you would tap on a home screen, which is what these
   marks are. The plate around it keeps its own outline, so the two nest:
   the outer box is the card, the inner one is the application.

   The artwork is pushed out to meet the rule. These marks draw their
   rounded square 60 wide inside a 64 viewBox — `x="2" y="2" width="60"` —
   so a rule at the element's edge stands a whisker off the drawing and
   leaves a pale ring. Scaling by 64/60 closes it, and `overflow: hidden`
   trims what the push sends past the corners. The radius is a percentage
   because the tile's own corner is 16 of its 60: 27 % lands on the drawn
   corner whatever size the mark is shown at.

   The shadow is ink here, not the plate's red. It falls on the card's own
   paper rather than on the band's ink, so it is visible — and an icon
   whose shadow matched the plate's would read as one shape with a thick
   red edge instead of two objects. */
.suite-mark-art {
  display: block;
  width: 48px;
  height: 48px;
  overflow: hidden;
  border: 2px solid var(--ink);
  border-radius: 27%;
  box-shadow: 3px 3px 0 var(--ink);
}
.suite-mark-art img { display: block; width: 100%; height: 100%; transform: scale(1.0667); }
.suite-mark-name {
  font-size: 10.5px;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
  white-space: nowrap;
}
.suite-mark:hover { transform: translate(-3px, -3px); box-shadow: 8px 8px 0 var(--red); }
.suite-mark:focus-visible { outline: 3px solid var(--gold); outline-offset: 3px; }

/* ── The Plan (studio) ────────────────────────────────────────── */
.manifesto-wrap {
  border-left: 8px solid var(--red);
  padding-left: clamp(20px, 3.5vw, 40px);
  margin-bottom: clamp(72px, 10vh, 112px);
}
.manifesto {
  font-family: var(--font-b);
  font-style: italic;
  font-size: clamp(22px, 3.4vw, 38px);
  line-height: 1.45;
  max-width: 1000px;
}
.manifesto .w { color: var(--faded); transition: color 0.3s ease; }
.manifesto .w.lit { color: var(--ink); }
.manifesto .w.hot { color: var(--red); }
.manifesto-cite { display: block; margin-top: 18px; color: var(--ink-soft); font-style: normal; }

.plan {
  position: relative;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 34px;
  /* La fusée a besoin d'air. Le décollage se jouait dans 56 px, coincé
     entre la citation et les trois étapes ; la traînée touchait les
     numéros. On double le dégagement au-dessus des étapes, et la citation
     s'écarte d'autant en amont. */
  padding-top: 82px;
}
.plan-arrow {
  position: absolute;
  top: 24px; left: 0; right: 0;
  width: 100%; height: 60px;
}
.plan-arrow path { stroke: var(--red); stroke-width: 3; }

/* La fusée suit sa traînée. Elle vit dans son propre SVG parce que celui
   de la traînée est en `preserveAspectRatio: none` — il doit s'étirer sur
   toute la largeur, et la fusée s'y serait écrasée avec lui. */
.plan-rocket {
  position: absolute;
  /* Centrée sur la traînée : le trait passe au milieu du corps, pas sous
     lui. La traînée est tracée à y=30 dans un cadre de 60 de haut, posé à
     18px du sommet — la fusée fait 34 de haut à l'échelle 78/34. */
  top: 34px;
  left: 0;
  width: 92px;
  height: 40px;
  overflow: visible;
  /* Elle part à l'aplomb du « ★ 01 » et du début de la traînée — les
     trois à 194 px. Un décalage de -16 px la faisait démarrer à gauche de
     la colonne, ce qui se lisait comme un défaut d'alignement plutôt que
     comme un élan. */
  transform: translateX(0);
  opacity: 0;
}
.plan-rocket.is-launched {
  animation: rocket-launch 1.5s cubic-bezier(0.22, 1, 0.36, 1) 0.2s forwards;
}
@keyframes rocket-launch {
  from { transform: translateX(0); opacity: 0; }
  8%   { opacity: 1; }
  to   { transform: translateX(var(--run, 900px)); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .plan-rocket { opacity: 1; transform: translateX(var(--run, 900px)); animation: none; }
}
.plan-num {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-d);
  font-size: 15px;
  background: var(--ink);
  color: var(--paper);
  padding: 6px 14px;
}
.plan-num i { font-style: normal; color: var(--red); font-size: 12px; }
.plan-step h3 { font-size: 22px; margin: 18px 0 10px; }
.plan-step p { color: var(--ink-soft); font-size: 15px; }

/* ── Contact (subcontracting channel) ─────────────────────────── */
.contact { text-align: center; }
.contact-title { font-size: clamp(38px, 7vw, 92px); margin: 14px 0 12px; }
.contact-sub { max-width: 560px; margin: 0 auto 42px; color: var(--ink-soft); }

.telegram {
  position: relative;
  display: inline-grid;
  gap: 8px;
  text-align: left;
  background: var(--ink);
  color: var(--paper);
  border-top: 8px solid var(--red);
  padding: clamp(26px, 4.5vw, 46px) clamp(28px, 6vw, 76px) clamp(28px, 4.5vw, 48px);
  clip-path: polygon(0 0, calc(100% - 22px) 0, 100% 22px, 100% 100%, 0 100%);
  max-width: 100%;
  transition: background 0.2s;
}
.telegram:hover { background: var(--red-dark); }
.telegram-label { color: var(--gold); }
.telegram-mail {
  font-family: var(--font-d);
  font-weight: var(--font-d-weight);
  font-size: clamp(22px, 5vw, 60px);
  line-height: 1.05;
  word-break: break-all;
}
.telegram-star {
  position: absolute;
  top: 16px; right: 18px;
  color: var(--red);
  transition: transform 0.4s var(--ease), color 0.2s;
}
.telegram:hover .telegram-star { transform: rotate(72deg) scale(1.3); color: var(--paper); }

.contact-meta {
  display: flex; justify-content: center; flex-wrap: wrap;
  gap: 10px 34px;
  margin-top: 28px;
  color: var(--ink-soft);
  --i: 1;
}

/* ── Who is behind the studio ──────────────────────────────────
   Built on the directive card of the section above: the same two pixels
   of ink around it, the same eight of red down the left edge, the same
   lift and hard offset shadow on hover. Laid on its side rather than
   upright, and carrying a face, so the reader meets a fourth object of
   the house and not a fourth directive.

   No arrow. The whole card is the link, the cursor says so, and one more
   glyph in a section that already has a star would be one too many. */
.author {
  display: inline-grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: clamp(16px, 2.5vw, 24px);
  margin-top: 36px;
  padding: 18px clamp(22px, 3.5vw, 34px) 18px 18px;
  text-align: left;
  color: inherit;
  text-decoration: none;
  background: var(--card);
  border: 2px solid var(--ink);
  border-left: 8px solid var(--red);
  max-width: 100%;
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease),
              opacity 0.7s var(--ease);
}
.author:hover, .author:focus-visible {
  transform: translateY(-5px);
  box-shadow: 8px 8px 0 rgba(22, 19, 14, 0.14);
}
.author:focus-visible { outline: 2px solid var(--red); outline-offset: 4px; }

.author-face {
  display: block;
  inline-size: clamp(60px, 9vw, 78px);
  block-size: clamp(60px, 9vw, 78px);
  overflow: hidden;
  border: 2px solid var(--ink);
  background: var(--ink);
}
.author-face img { width: 100%; height: 100%; object-fit: cover; display: block; }

.author-text { display: grid; gap: 4px; min-width: 0; }
.author-label { color: var(--red); font-size: 11px; letter-spacing: 0.14em; }
.author-name {
  font-family: var(--font-d);
  font-weight: var(--font-d-weight);
  text-transform: uppercase;
  font-size: clamp(18px, 2.6vw, 22px);
  line-height: 1.05;
}
.author-role { font-size: 15px; line-height: 1.4; color: var(--ink-soft); }

@media (prefers-reduced-motion: reduce) {
  .author { transition: none; }
  .author:hover, .author:focus-visible { transform: none; }
}

/* ── Footer ───────────────────────────────────────────────────── */
.footer {
  background: var(--ink);
  color: var(--paper);
  border-top: 6px solid var(--red);
  padding: 40px var(--pad) 26px;
}
.footer-inner {
  max-width: 1180px;
  margin: 0 auto;
  display: flex; align-items: center; gap: 26px; flex-wrap: wrap;
}
.footer .logo-slot { width: 34px; height: 34px; }
.footer .brand-name { font-size: 15px; }
.footer-links { display: flex; gap: 2px 20px; flex-wrap: wrap; margin-left: auto; }
.footer-links a { opacity: 0.75; transition: opacity 0.2s, color 0.2s; }
.footer-links a:hover { opacity: 1; color: #E9B0A5; }
.footer-note { max-width: 1180px; margin: 20px auto 0; opacity: 0.55; }

/* ── Legal pages ──────────────────────────────────────────────── */
.legal-page {
  max-width: 840px;
  margin: 0 auto;
  padding: calc(var(--bar-h) + 54px) var(--pad) 90px;
}
.legal-page h1 { font-size: clamp(32px, 5.5vw, 58px); margin: 12px 0 22px; }
.legal-page h2 {
  font-size: 18px;
  margin: 44px 0 14px;
  padding: 6px 12px;
  background: var(--ink);
  color: var(--paper);
  display: inline-block;
}
.legal-page p, .legal-page dd, .legal-page li { color: var(--ink-soft); font-size: 15.5px; }
.legal-page a { color: var(--red); border-bottom: 1px solid var(--red); }
.legal-page ul { padding-left: 20px; display: grid; gap: 12px; margin-top: 12px; }
.legal-page strong { color: var(--ink); }
.legal-page section { border-top: 2px solid var(--ink); margin-top: 32px; }
.legal-intro { margin-bottom: 8px; }
.legal-langs { margin: 4px 0 24px; color: var(--faded); }

.legal-list { display: grid; grid-template-columns: 235px 1fr; gap: 12px 20px; margin-top: 16px; }
.legal-list dt { font-family: var(--font-m); font-size: 11.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink); padding-top: 3px; }
.legal-list em { font-style: italic; opacity: 0.6; font-size: 13px; }
@media (max-width: 640px) {
  .legal-list { grid-template-columns: 1fr; gap: 3px; }
  .legal-list dd { margin-bottom: 14px; }
}

/* Empty zone still to be filled in (missing legal information) */
.fill-me {
  display: inline-block;
  font-family: var(--font-m);
  font-size: 0.85em;
  padding: 2px 8px;
  border: 2px dashed var(--red);
  background: rgba(200, 50, 30, 0.07);
  color: var(--red-dark);
}

/* ── Responsive ───────────────────────────────────────────────── */
@media (max-width: 1100px) {
  .bar-nav { display: none; }
}
@media (max-width: 780px) {
  .bar-cta { display: none; }
  .burger { display: block; margin-left: auto; }
  .lang { margin-left: auto; }
  .burger { margin-left: 0; }
  .plan { grid-template-columns: 1fr; gap: 38px; padding-top: 10px; }
  .plan-arrow, .plan-rocket { display: none; }
  .sheet { height: 520px; }
  .stamp-body p { min-height: 0; }
  .telegram-star { display: none; }
}

/* ── Reduced motion ───────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  html { scroll-behavior: auto; }
  .reveal, .hero-title .line-inner { opacity: 1; transform: none; }
  .manifesto .w { color: var(--ink); }
  .stamp-loader { display: none; }
  /* The rule above would leave the marquee parked at its end frame — on
     the duplicate row, halfway through the loop. Stop it outright, drop
     the copy that exists only to close the seam, and give the row back to
     the reader as something they scroll themselves. */
  .suite-track { animation: none !important; transform: none !important; }
  .suite-row[aria-hidden="true"] { display: none; }
  .suite-rail { overflow-x: auto; }
}

/* ══════════════════ CaptainFork, disponible ══════════════════
   L'annonce de disponibilité, sous le catalogue. Le logo est le PNG
   couleur publié par CaptainFork lui-même — le vert de l'app est le seul
   aplat étranger à la palette du site, et c'est voulu : c'est un produit
   qui parle, pas le site. Tout le reste — plaque bordée d'encre, ombre
   dure, coin coupé — est la langue de la maison. */
/* Le rythme vertical est celui de toutes les sections — un premier jet
   l'avait mis à zéro et la plaque venait se coller au catalogue, ce qui
   la faisait lire comme une annexe du carrousel au lieu d'une annonce à
   part entière. Une annonce respire. */
.fork-card {
  display: flex;
  align-items: center;
  gap: 40px;
  max-width: 880px;
  margin: 0 auto;
  padding: 36px 40px;
  background: var(--paper-2);
  border: 2px solid var(--ink);
  box-shadow: 8px 8px 0 var(--ink);
  clip-path: polygon(0 0, calc(100% - 18px) 0, 100% 18px, 100% 100%, 0 100%);
}
/* The two plates sit on the same axis, and that is a decision rather than
   a default. They were offset to opposite sides for a while — left, then
   right — on the idea that a column of identical cards would read as a
   rhythm rather than as a stack. Seen on the page, it read as two cards
   that had failed to line up: the eye takes an alignment as intended and a
   near-alignment as a mistake, and there is no third card yet to establish
   the alternation as a pattern. Centred, they say plainly what they are —
   the same announcement, made twice. */

.fork-logo-wrap { flex: none; position: relative; }
.fork-logo {
  display: block;
  width: 132px; height: 132px;
  border: 2px solid var(--ink);
  border-radius: 24px;
  box-shadow: 5px 5px 0 var(--ink);
  transform: rotate(-2deg);
  animation: fork-float 5.5s ease-in-out infinite;
}
@keyframes fork-float {
  0%, 100% { transform: rotate(-2deg) translateY(0); }
  50%      { transform: rotate(-2deg) translateY(-7px); }
}
.fork-copy { min-width: 0; }
.fork-copy h2 { margin: 6px 0 10px; }
.fork-copy .section-sub { margin: 0 0 18px; max-width: 52ch; }

.fork-platforms {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 20px;
  padding: 0;
  list-style: none;
}
.fork-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px;
  border: 2px solid var(--ink);
  background: var(--paper);
  font-size: 12.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  /* Elles entrent l'une après l'autre quand la plaque se révèle : la
     cadence dit « une, deux — et la troisième arrive », ce qui est
     exactement le message. */
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease);
  transition-delay: calc(0.25s + var(--i, 0) * 0.14s);
}
.reveal.in .fork-chip { opacity: 1; transform: none; }
.fork-dot { width: 9px; height: 9px; border: 2px solid var(--ink); flex: none; }
.fork-chip.is-live .fork-dot { background: #1E8F4E; }
.fork-chip.is-soon .fork-dot { background: var(--gold); }
.fork-chip.is-soon { border-style: dashed; color: var(--ink-soft); }
.fork-cta { margin-top: 2px; }

@media (max-width: 700px) {
  .fork-card { flex-direction: column; align-items: flex-start; gap: 24px; padding: 28px 22px; }
  .fork-logo { width: 104px; height: 104px; }
}
@media (prefers-reduced-motion: reduce) {
  .fork-logo { animation: none; }
  .fork-chip { opacity: 1; transform: none; transition: none; }
}


/* ══════════════════ La Base, fusionnée avec le contact ══════════════════
   Les anciennes sections 01 et 04 n'en font plus qu'une : les directives
   disent qui on est, et la porte est juste dessous. Le filet d'encre est
   la couture entre les deux moitiés. */
.base-contact {
  margin-top: clamp(64px, 9vh, 100px);
  padding-top: clamp(48px, 7vh, 76px);
  border-top: 2px solid var(--ink);
}

/* La zone cliquable du timbre — un vrai lien, couvrant toute la carte.
   La section promet « cliquez sur un timbre » ; l'ancienne mécanique
   passait par un window.open dans un gestionnaire de pointeur, qui
   n'ouvrait rien (mesuré) et qu'un navigateur peut bloquer. Le lien est
   posé au-dessus du contenu mais sous rien : le glissement du carrousel
   continue de fonctionner, le clic après glissement est annulé en JS. */
.stamp-hit {
  position: absolute;
  inset: 0;
  z-index: 3;
  border-radius: inherit;
}
.stamp-hit:focus-visible {
  outline: 3px solid var(--red);
  outline-offset: 3px;
}
