/*
  ============================================================================
  Pragmatic-Utopia — site stylesheet / feuille de style du site / hoja de estilo
  pragmatic-utopia.ca
  ============================================================================

  WHY THIS FILE EXISTS AS A FILE / POURQUOI UN FICHIER EXTERNE
    Every page links this one stylesheet instead of carrying its own inline
    <style> block. That is deliberate: an external stylesheet lets the
    Content-Security-Policy in `_headers` stay at `style-src 'self'` with no
    'unsafe-inline' escape hatch, and it means a spacing or colour change lands
    on all pages at once rather than being edited eight times.

    Chaque page pointe vers cette feuille unique plutôt que d'embarquer son
    propre bloc <style>. Cela permet à la CSP de rester `style-src 'self'` sans
    'unsafe-inline', et une correction se répercute sur toutes les pages.
    Cada página enlaza esta única hoja en lugar de incrustar su propio <style>,
    lo que permite mantener `style-src 'self'` sin 'unsafe-inline'.

  THE DESIGN IDEA / L'IDÉE DIRECTRICE / LA IDEA RECTORA
    "Field notes from workable futures." The page reads as warm paper with fine
    graph-paper ruling — the pragmatic half: measured, gridded, checkable — and
    a soft horizon bloom rising through it — the utopian half. Stories are
    numbered dispatches (No. 001) in a monospaced hand, set for long reading in a
    text serif, under display headings with tight tracking.

    « Notes de terrain sur des futurs praticables. » Papier chaud quadrillé (le
    versant pragmatique : mesuré, vérifiable) traversé d'une lueur d'horizon (le
    versant utopique). Les récits sont des dépêches numérotées.
    «Notas de campo sobre futuros viables.» Papel cálido cuadriculado (lo
    pragmático: medido, verificable) atravesado por un resplandor de horizonte
    (lo utópico). Los relatos son despachos numerados.

  CONTENTS / SOMMAIRE / ÍNDICE
    01  Brand tokens + typography scale
    02  Light theme (default) and dark theme
    03  Reset + document base
    04  Language visibility toggling
    05  Atmosphere: graph rule, horizon bloom, grain
    06  Header: wordmark, nav, language + theme controls
    07  Home: hero, principles, dispatch list
    08  Story pages: article prose
    09  Document pages: privacy / security / 404
    10  Contact form
    11  Footer
    12  Motion, reduced motion, and responsive
  ============================================================================
*/

/* ---------------------------------------------------------------------------
   01  Brand tokens / Jetons de marque / Tokens de marca
   Raw brand colours live here once and are consumed only through the semantic
   tokens in section 02, so a theme can re-map meaning without re-picking hues.
   Les couleurs brutes ne sont utilisées qu'à travers les jetons sémantiques.
--------------------------------------------------------------------------- */
:root {
  /* Raw palette / Palette brute / Paleta bruta */
  --paper:     #F7F3EA;  /* warm unbleached paper — the ground of the light theme */
  --paper-2:   #EDE5D5;  /* a shade deeper, for banded and inset surfaces        */
  --loam:      #16211C;  /* near-black with green in it — soil, not soot         */
  --verdigris: #146B57;  /* weathered copper green — the primary accent          */
  --mint:      #5FCCA8;  /* the same green lifted for legibility on dark ground  */
  --clay:      #B4502B;  /* fired earth — the secondary accent, used sparingly   */
  --sky:       #3B6E8F;  /* muted daylight blue — tertiary, for quiet emphasis   */

  /* Typography / Typographie / Tipografía */
  --font-display: "Bricolage Grotesque", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-body: "Newsreader", Georgia, "Times New Roman", serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;

  /* Rhythm / Rythme / Ritmo
     --edge is the single page gutter; --measure caps line length for reading. */
  --edge: clamp(1.4rem, 5vw, 6rem);
  --measure: 66ch;
  --rule: 1px;
}

/* ---------------------------------------------------------------------------
   02  Themes / Thèmes / Temas

   DARK is the default. The dark palette therefore sits on bare `:root` as well
   as on [data-theme="dark"], so that a reader whose JavaScript never runs — and
   who therefore never gets a data-theme attribute — still lands on the dark
   ground the site is designed around, rather than flashing a light page the
   toggle cannot then correct.

   Light is the alternate, reachable from the header toggle on every page and
   remembered in localStorage from then on.

   Every ink token clears WCAG AA (4.5:1) against its own ground; the accents
   were checked at small text size in both themes.

   Le thème SOMBRE est celui par défaut : la palette sombre est donc portée par
   `:root` nu autant que par [data-theme="dark"], afin qu'un lecteur sans
   JavaScript obtienne malgré tout le fond sombre. Le clair est l'alternative,
   accessible depuis la bascule d'en-tête et mémorisée ensuite.
   El tema OSCURO es el predeterminado: la paleta oscura la lleva `:root` a
   secas tanto como [data-theme="dark"], para que un lector sin JavaScript
   obtenga igualmente el fondo oscuro. El claro es la alternativa, accesible
   desde el conmutador de la cabecera y recordada después.
--------------------------------------------------------------------------- */
:root[data-theme="light"] {
  /* Tell the browser to render its OWN widgets light too: scrollbars, the
     interior of text inputs, and the paint before CSS lands.
     Indique au navigateur de rendre ses propres widgets en clair.
     Indica al navegador que renderice sus propios widgets en claro. */
  color-scheme: light;
  --bg: var(--paper);
  --bg-2: var(--paper-2);
  --ink: var(--loam);
  --ink-dim: #33423A;
  --muted: #61705F;
  --accent: var(--verdigris);
  --accent-bright: #0E5343;
  --accent-2: var(--clay);
  --tertiary: #2F5C78;
  --line: rgba(22, 33, 28, 0.16);
  --line-soft: rgba(22, 33, 28, 0.075);
  --bloom-warm: rgba(212, 148, 76, 0.20);
  --bloom-cool: rgba(20, 107, 87, 0.13);
  --bloom-strength: 0.85;
  --grain-opacity: 0.035;
  --grain-blend: multiply;
  --focus: var(--verdigris);
}

:root,
:root[data-theme="dark"] {
  /* Dark is the default, so this is also what an unstyled first paint uses.
     Le sombre étant le défaut, c'est aussi le premier rendu.
     Al ser oscuro el valor por defecto, es también el primer pintado. */
  color-scheme: dark;
  --bg: #101A16;
  --bg-2: #18241F;
  --ink: #F1EBDE;
  --ink-dim: #CBD5CB;
  --muted: #8B9C91;
  --accent: var(--mint);
  --accent-bright: #8CE3C4;
  --accent-2: #E08A62;
  --tertiary: #7FB2D0;
  --line: rgba(241, 235, 222, 0.14);
  --line-soft: rgba(241, 235, 222, 0.06);
  --bloom-warm: rgba(224, 160, 92, 0.13);
  --bloom-cool: rgba(95, 204, 168, 0.10);
  --bloom-strength: 1;
  --grain-opacity: 0.05;
  --grain-blend: overlay;
  --focus: var(--mint);
}

/* ---------------------------------------------------------------------------
   03  Reset + document base / Réinitialisation / Reinicio
--------------------------------------------------------------------------- */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1.02rem;
  font-weight: 400;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  position: relative;
  transition: background-color 0.45s ease, color 0.45s ease;
}

img { max-width: 100%; height: auto; }

/* One visible focus treatment for every interactive element on the site.
   Un seul traitement de focus visible pour tout élément interactif.
   Un único tratamiento de foco visible para todo elemento interactivo. */
:where(a, button, input, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Skip link — first tab stop, hidden until focused.
   Lien d'évitement — masqué jusqu'au focus clavier.
   Enlace para saltar — oculto hasta recibir el foco. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 99;
  background: var(--bg);
  color: var(--accent);
  border: var(--rule) solid var(--line);
  padding: 0.7rem 1.1rem;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
}
.skip:focus { left: var(--edge); top: 0.6rem; }

/* ---------------------------------------------------------------------------
   04  Language visibility / Bascule de langue / Alternancia de idioma

   All three languages ship in the DOM of every page and are shown or hidden by
   the data-lang attribute on <html>. One URL serves all three, which is why the
   sitemap declares no hreflang alternates — and why every language stays
   crawlable. The switch itself is in assets/site.js.

   Les trois langues sont présentes dans le DOM ; l'attribut data-lang décide de
   celle qui s'affiche. Une seule URL pour les trois, donc aucune alternance
   hreflang à déclarer, et les trois restent indexables.
   Los tres idiomas están en el DOM; el atributo data-lang decide cuál se
   muestra. Una sola URL para los tres, sin alternancias hreflang.
--------------------------------------------------------------------------- */
:root[data-lang="en"] .l-fr, :root[data-lang="en"] .l-es { display: none !important; }
:root[data-lang="fr"] .l-en, :root[data-lang="fr"] .l-es { display: none !important; }
:root[data-lang="es"] .l-en, :root[data-lang="es"] .l-fr { display: none !important; }

/* ---------------------------------------------------------------------------
   05  Atmosphere / Ambiance / Ambiente

   Three fixed, non-interactive layers behind everything:
     .atmos::before  the graph rule — the pragmatic half, a measuring surface
     .atmos::after   the horizon bloom — the utopian half, warm light rising
     .grain          film grain, so flat colour never reads as plastic
   Trois couches fixes et non interactives : quadrillage, lueur d'horizon, grain.
   Tres capas fijas y no interactivas: cuadrícula, resplandor y grano.
--------------------------------------------------------------------------- */
.atmos {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.atmos::before {
  content: "";
  position: absolute;
  inset: -2px;
  background-image:
    linear-gradient(var(--line-soft) var(--rule), transparent var(--rule)),
    linear-gradient(90deg, var(--line-soft) var(--rule), transparent var(--rule));
  background-size: 34px 34px;
  /* Fade the ruling out toward the bottom so long pages don't feel caged.
     Le quadrillage s'estompe vers le bas. / La cuadrícula se desvanece abajo. */
  mask-image: linear-gradient(to bottom, #000 0%, #000 42%, transparent 88%);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 42%, transparent 88%);
}
.atmos::after {
  content: "";
  position: absolute;
  top: -34vh;
  left: 50%;
  transform: translateX(-50%);
  width: 130vw;
  height: 95vh;
  opacity: var(--bloom-strength);
  background:
    radial-gradient(closest-side at 62% 58%, var(--bloom-warm), transparent 72%),
    radial-gradient(closest-side at 28% 46%, var(--bloom-cool), transparent 70%);
  filter: blur(12px);
}
.grain {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: var(--grain-opacity);
  mix-blend-mode: var(--grain-blend);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Everything real sits above the atmosphere. / Le contenu passe au-dessus. */
.shell { position: relative; z-index: 2; }

/* ---------------------------------------------------------------------------
   06  Header / En-tête / Encabezado
--------------------------------------------------------------------------- */
header.site {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 1.4rem;
  flex-wrap: wrap;
  padding: 1.3rem var(--edge);
  border-bottom: var(--rule) solid var(--line-soft);
}

/* ---- Wordmark / Mot-symbole / Logotipo ----
   Set in live text rather than an image file: it stays crisp at any size,
   inherits the theme's ink with no second asset to keep in sync, and can be
   read and translated by assistive technology. The glyph beside it is the site
   mark — a measured frame, strata below a horizon, a sun rising over it: the
   two halves of the name in one shape.
   Composé en texte vivant plutôt qu'en image : net à toute taille, suit le
   thème, lisible par les technologies d'assistance. Le glyphe reprend le nom :
   un cadre mesuré, des strates sous un horizon, un soleil qui se lève.
   Compuesto como texto vivo, no como imagen: nítido a cualquier tamaño, sigue
   el tema y es legible por tecnologías de asistencia. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  text-decoration: none;
  color: var(--ink);
}
.brand .glyph {
  width: clamp(26px, 3.2vw, 32px);
  height: clamp(26px, 3.2vw, 32px);
  flex: none;
  display: block;
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.brand:hover .glyph { transform: translateY(-2px) rotate(-3deg); }
.brand .glyph .sun { fill: var(--accent); }
.brand .word {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.05rem, 2.1vw, 1.32rem);
  letter-spacing: -0.035em;
  line-height: 1;
  white-space: nowrap;
}
.brand .word .sep { color: var(--accent); padding: 0 0.06em; }

/* ---- Header controls / Commandes d'en-tête / Controles del encabezado ---- */
.controls { display: flex; align-items: center; gap: 1.1rem; }

.site-nav {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.site-nav a {
  color: var(--muted);
  text-decoration: none;
  padding-bottom: 2px;
  border-bottom: var(--rule) solid transparent;
  transition: color 0.25s ease, border-color 0.25s ease;
}
.site-nav a:hover { color: var(--ink); border-bottom-color: var(--accent); }
/* aria-current marks the page you are already on. / Page courante. */
.site-nav a[aria-current="page"] { color: var(--accent); border-bottom-color: var(--accent); }

.lang {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
}
.lang-btn {
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font: inherit;
  letter-spacing: inherit;
  text-transform: uppercase;
  padding: 0.35rem 0.2rem;
  transition: color 0.25s ease;
}
.lang-btn[aria-pressed="true"] { color: var(--accent); }
.lang-btn:hover { color: var(--ink); }
.lang-div { color: var(--line); }

.theme-btn {
  background: none;
  border: var(--rule) solid var(--line);
  color: var(--ink);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: border-color 0.25s ease, color 0.25s ease, transform 0.25s ease;
}
.theme-btn:hover { border-color: var(--accent); color: var(--accent); transform: rotate(-18deg); }
.theme-btn svg { width: 16px; height: 16px; }
:root[data-theme="dark"] .theme-btn .ic-sun { display: none; }
:root[data-theme="light"] .theme-btn .ic-moon { display: none; }
/* No data-theme attribute means JavaScript has not run, and bare :root is the
   dark palette — so show the moon, exactly as the dark theme does. Without this
   both icons would render at once.
   Sans attribut data-theme, le fond est sombre : afficher la lune, comme en
   thème sombre. Sinon les deux icônes s'afficheraient ensemble.
   Sin atributo data-theme el fondo es oscuro: mostrar la luna, igual que en el
   tema oscuro. Si no, se mostrarían ambos iconos a la vez. */
:root:not([data-theme]) .theme-btn .ic-sun { display: none; }

/* ---------------------------------------------------------------------------
   07  Home page / Page d'accueil / Portada
--------------------------------------------------------------------------- */

/* ---- The eyebrow rule, used above every major section ----
   Un filet-chapeau au-dessus de chaque grande section.
   Un filete sobre cada sección principal. */
.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.71rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--accent);
  display: inline-flex;
  align-items: center;
  gap: 0.85rem;
  margin-bottom: 1.6rem;
}
.eyebrow::before {
  content: "";
  width: clamp(24px, 6vw, 54px);
  height: var(--rule);
  background: linear-gradient(90deg, var(--accent), transparent);
}
.eyebrow.alt { color: var(--tertiary); }
.eyebrow.alt::before { background: linear-gradient(90deg, var(--tertiary), transparent); }

.hero {
  padding: clamp(2.4rem, 7vh, 5rem) var(--edge) clamp(2.6rem, 7vh, 4.5rem);
  max-width: 1240px;
}
h1.headline {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2.7rem, 8.4vw, 6.2rem);
  line-height: 0.94;
  letter-spacing: -0.045em;
  max-width: 15ch;
  margin-bottom: 1.7rem;
  text-wrap: balance;
}
/* The utopian word in the headline gets the accent and an optical lift.
   Le mot « utopique » reçoit l'accent. / La palabra utópica recibe el acento. */
h1.headline em {
  font-style: normal;
  color: var(--accent);
  position: relative;
}
h1.headline em::after {
  /* A drawn underline that sits low and loose, like a pen mark.
     Un soulignement tracé à la main. / Un subrayado trazado a mano. */
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0.06em;
  height: 0.055em;
  background: var(--accent);
  opacity: 0.32;
  border-radius: 2px;
}
.lede {
  font-size: clamp(1.1rem, 1.75vw, 1.4rem);
  line-height: 1.62;
  color: var(--ink-dim);
  max-width: 52ch;
}
.lede strong { color: var(--ink); font-weight: 600; }

/* ---- Principles / Principes / Principios ----
   Three columns stating the editorial rule of the site. Numbered in mono so
   they read as clauses, not marketing bullets.
   Trois colonnes énonçant la règle éditoriale, numérotées comme des clauses.
   Tres columnas con la regla editorial, numeradas como cláusulas. */
.principles {
  padding: clamp(3rem, 8vh, 5rem) var(--edge);
  border-top: var(--rule) solid var(--line);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: clamp(2rem, 4.5vw, 3.6rem);
  max-width: 1240px;
}
.principle .idx {
  font-family: var(--font-mono);
  font-size: 0.71rem;
  letter-spacing: 0.2em;
  color: var(--accent);
  margin-bottom: 1rem;
}
.principle h3 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.42rem;
  letter-spacing: -0.03em;
  line-height: 1.15;
  margin-bottom: 0.8rem;
}
.principle p { color: var(--muted); font-size: 1rem; line-height: 1.66; }

/* ---- Dispatch list / Liste des dépêches / Lista de despachos ----
   The stories index. Each entry is a full-width row: number and date in a left
   rail, title and standfirst in the body. Hovering nudges the row right, the
   way a card is pulled part-way out of a drawer.
   Chaque entrée est une rangée pleine largeur ; le survol la décale vers la
   droite, comme une fiche tirée d'un tiroir.
   Cada entrada es una fila a todo lo ancho; al pasar el cursor se desplaza a la
   derecha, como una ficha que se saca de un cajón. */
.dispatches {
  padding: clamp(3rem, 8vh, 5rem) var(--edge) clamp(3rem, 7vh, 4.5rem);
  border-top: var(--rule) solid var(--line);
  max-width: 1240px;
}
.dispatch-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.8rem 1.5rem;
  margin-bottom: 2.2rem;
}
.dispatch-head h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.8rem, 4vw, 2.8rem);
  letter-spacing: -0.04em;
  line-height: 1.02;
}
.dispatch-list { list-style: none; }
.dispatch {
  border-top: var(--rule) solid var(--line-soft);
}
.dispatch:last-child { border-bottom: var(--rule) solid var(--line-soft); }
.dispatch a {
  display: grid;
  grid-template-columns: minmax(88px, 130px) 1fr;
  gap: 0.4rem 2rem;
  align-items: start;
  padding: 1.7rem 0.6rem 1.7rem 0;
  text-decoration: none;
  color: inherit;
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1),
              background-color 0.4s ease,
              padding-left 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.dispatch a:hover {
  background: var(--bg-2);
  padding-left: 0.9rem;
}
.dispatch .rail {
  font-family: var(--font-mono);
  font-size: 0.71rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  padding-top: 0.42rem;
}
.dispatch .rail .no { color: var(--accent); display: block; margin-bottom: 0.35rem; }
.dispatch h3 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.32rem, 2.7vw, 1.85rem);
  letter-spacing: -0.035em;
  line-height: 1.1;
  margin-bottom: 0.55rem;
  transition: color 0.3s ease;
  text-wrap: balance;
}
.dispatch a:hover h3 { color: var(--accent); }
.dispatch p {
  color: var(--muted);
  font-size: 1rem;
  line-height: 1.6;
  max-width: 62ch;
}
/* The arrow only earns its place on hover / focus.
   La flèche n'apparaît qu'au survol. / La flecha aparece al pasar el cursor. */
.dispatch .go {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  color: var(--accent);
  margin-top: 0.75rem;
  opacity: 0;
  transform: translateX(-6px);
  transition: opacity 0.35s ease, transform 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}
.dispatch a:hover .go, .dispatch a:focus-visible .go { opacity: 1; transform: none; }

/* Utility pair used by /stories, whose list IS the page and therefore needs no
   rule above it. Kept as classes rather than style="" attributes so the CSP can
   refuse inline styles outright.
   Paire d'utilitaires pour /stories ; en classes plutôt qu'en attributs style,
   afin que la CSP puisse refuser tout style en ligne.
   Par de utilidades para /stories; como clases y no como atributos style, para
   que la CSP pueda rechazar todo estilo en línea. */
.dispatches.top { border-top: none; padding-top: clamp(1.6rem, 4vh, 2.8rem); }
.lede.spaced { margin-bottom: 2.4rem; }

/* ---- Standing button / Bouton / Botón ---- */
.btn {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
  background: none;
  text-decoration: none;
  border: var(--rule) solid var(--line);
  padding: 0.92rem 1.7rem;
  border-radius: 2px;
  cursor: pointer;
  transition: border-color 0.25s ease, color 0.25s ease, background-color 0.25s ease;
}
.btn:hover { border-color: var(--accent); color: var(--accent-bright); background: var(--bg-2); }
.btn:disabled { opacity: 0.55; cursor: not-allowed; }

/* ---- Closing invitation / Invitation de clôture / Invitación final ---- */
.invite {
  padding: clamp(3.4rem, 9vh, 5.5rem) var(--edge) clamp(3.6rem, 8vh, 5rem);
  border-top: var(--rule) solid var(--line);
  max-width: 1240px;
}
.invite h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.9rem, 4.6vw, 3rem);
  line-height: 1.04;
  letter-spacing: -0.04em;
  max-width: 20ch;
  margin-bottom: 1.4rem;
  text-wrap: balance;
}
.invite p {
  font-size: clamp(1.05rem, 1.7vw, 1.24rem);
  line-height: 1.64;
  color: var(--ink-dim);
  max-width: 52ch;
  margin-bottom: 1.8rem;
}

/* ---------------------------------------------------------------------------
   08  Story pages / Pages de récit / Páginas de relato

   A single reading column. Everything here exists to keep prose comfortable
   over several minutes: a capped measure, a serif at a generous line-height,
   and a pull-quote that breaks the column rather than sitting inside it.
   Une colonne de lecture unique, mesure limitée, interligne généreux.
   Una única columna de lectura, medida limitada, interlineado generoso.
--------------------------------------------------------------------------- */
.article {
  max-width: 760px;
  margin: 0 auto;
  padding: clamp(1.6rem, 4vh, 2.8rem) var(--edge) clamp(4rem, 9vh, 6rem);
}
.article .meta {
  font-family: var(--font-mono);
  font-size: 0.71rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.1rem;
  margin-bottom: 1.5rem;
}
.article .meta .no { color: var(--accent); }
.article .meta .dot { color: var(--line); }
.article h1 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2.1rem, 5.6vw, 3.5rem);
  line-height: 1.03;
  letter-spacing: -0.045em;
  margin-bottom: 1.1rem;
  text-wrap: balance;
}
.article .standfirst {
  font-size: clamp(1.08rem, 1.7vw, 1.28rem);
  line-height: 1.6;
  color: var(--ink-dim);
  font-style: italic;
  padding-bottom: 1.8rem;
  margin-bottom: 2rem;
  border-bottom: var(--rule) solid var(--line);
}
.article h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.42rem;
  letter-spacing: -0.03em;
  line-height: 1.2;
  margin: 2.6rem 0 0.9rem;
}
.article p {
  font-size: 1.09rem;
  line-height: 1.78;
  color: var(--ink-dim);
  margin-bottom: 1.15rem;
  max-width: var(--measure);
}
.article p strong { color: var(--ink); font-weight: 600; }
.article ul, .article ol {
  color: var(--ink-dim);
  font-size: 1.09rem;
  line-height: 1.78;
  margin: 0 0 1.2rem 1.3rem;
  max-width: var(--measure);
}
.article li { margin-bottom: 0.45rem; }
.article a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.article a:hover { color: var(--accent-bright); }

/* Pull-quote — steps out of the measure to break a long read.
   Exergue — sort de la colonne pour aérer une longue lecture.
   Destacado — sale de la columna para airear una lectura larga. */
.article blockquote {
  margin: 2.4rem 0;
  padding-left: 1.5rem;
  border-left: 3px solid var(--accent);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.25rem, 2.4vw, 1.6rem);
  line-height: 1.28;
  letter-spacing: -0.025em;
  color: var(--ink);
}

/* The "so what would it take" box that closes each dispatch — the pragmatic
   half of the name, made explicit: what this future actually costs.
   L'encadré de clôture : ce que ce futur exigerait concrètement.
   El recuadro final: lo que ese futuro exigiría en concreto. */
.ledger {
  margin: 2.8rem 0 0;
  padding: 1.6rem 1.7rem;
  background: var(--bg-2);
  border: var(--rule) solid var(--line);
  border-radius: 3px;
}
.ledger h2 {
  font-family: var(--font-mono);
  font-size: 0.71rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 0.9rem;
}
.ledger ul { margin: 0 0 0 1.2rem; font-size: 1.02rem; line-height: 1.7; }
.ledger li:last-child { margin-bottom: 0; }

/* The sample-dispatch badge. The three seed stories are composites written to
   demonstrate the template, so they say so on the page rather than passing as
   reporting. Delete the <span class="sample"> block when a real story replaces
   one — this rule can stay, unused, for the next placeholder.
   Mention « dépêche d'exemple » : les récits d'amorçage sont des composites et
   l'annoncent, plutôt que de passer pour du reportage.
   Etiqueta «despacho de muestra»: los relatos iniciales son composiciones y así
   lo declaran, en lugar de pasar por periodismo. */
.article .meta .sample {
  color: var(--accent-2);
  border: var(--rule) solid currentColor;
  border-radius: 2px;
  padding: 0.1rem 0.45rem;
  letter-spacing: 0.12em;
}

/* ---------------------------------------------------------------------------
   08b  The essay / L'essai / El ensayo

   The home page carries a long-form argument, not a marketing block. It gets
   the same reading column as a story page, one size up, plus two devices the
   text itself calls for: `.beat` for the short standalone lines the argument
   turns on, and `.measure-block` for the closing test a society is asked to
   apply to itself.
   La page d'accueil porte un argumentaire long, dans la même colonne de lecture
   qu'un récit : `.beat` pour les phrases-pivots isolées, `.measure-block` pour
   l'épreuve finale.
   La portada lleva un argumento largo, en la misma columna de lectura que un
   relato: `.beat` para las frases aisladas que articulan el texto y
   `.measure-block` para la prueba final.
--------------------------------------------------------------------------- */
/* The section spans the same 1240px band as every other home-page section, so
   its top rule lines up with theirs instead of floating mid-page; the reading
   column inside it is left-aligned to the page gutter, sharing an edge with the
   hero above. A centred column under a gutter-aligned hero reads as two
   unrelated pages stacked.
   La section occupe la même bande que les autres : son filet supérieur s'aligne
   sur les leurs, et la colonne de lecture partage le bord gauche de l'accroche.
   La sección ocupa la misma banda que las demás: su filete superior se alinea
   con los suyos y la columna de lectura comparte el borde izquierdo del hero. */
.essay {
  max-width: 1240px;
  padding: clamp(1.6rem, 4vh, 2.6rem) var(--edge) clamp(3rem, 8vh, 5rem);
  border-top: var(--rule) solid var(--line);
}
/* The three language blocks are the reading column itself.
   Les trois blocs de langue constituent la colonne de lecture.
   Los tres bloques de idioma son la columna de lectura. */
.essay > div { max-width: 780px; }
.essay p {
  font-size: 1.11rem;
  line-height: 1.78;
  color: var(--ink-dim);
  margin-bottom: 1.25rem;
  max-width: var(--measure);
}
.essay p strong { color: var(--ink); font-weight: 600; }
.essay em { font-style: italic; }
.essay h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.6rem, 3.4vw, 2.15rem);
  letter-spacing: -0.04em;
  line-height: 1.08;
  margin: 3.2rem 0 1.2rem;
  padding-top: 1.6rem;
  border-top: var(--rule) solid var(--line-soft);
}

/* A short line the argument pivots on, set larger and given air on both sides.
   Une phrase-pivot, plus grande et aérée. / Una frase pivote, mayor y aireada. */
.essay p.beat {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.22rem, 2.3vw, 1.5rem);
  line-height: 1.3;
  letter-spacing: -0.025em;
  color: var(--ink);
  margin: 2rem 0;
  padding-left: 1.4rem;
  border-left: 3px solid var(--accent);
  max-width: 44ch;
}
.essay p.beat strong { font-weight: 700; }

/* The closing measure — the test a society applies to itself.
   La mesure de clôture. / La medida final. */
.measure-block {
  margin: 3rem 0 0;
  padding: 1.9rem 1.9rem 1.7rem;
  background: var(--bg-2);
  border: var(--rule) solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: 3px;
}
.measure-block p {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.12rem, 2.1vw, 1.35rem);
  line-height: 1.34;
  letter-spacing: -0.02em;
  color: var(--ink);
  margin: 0;
  max-width: none;
}

/* ---------------------------------------------------------------------------
   09  Document pages / Pages de politique / Páginas de políticas
   Privacy, security, contact, and 404 share this narrower column.
--------------------------------------------------------------------------- */
.doc {
  max-width: 760px;
  margin: 0 auto;
  padding: clamp(1.6rem, 4vh, 2.8rem) var(--edge) clamp(4rem, 9vh, 6rem);
}
.doc h1 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2.1rem, 5.6vw, 3.4rem);
  line-height: 1.04;
  letter-spacing: -0.045em;
  margin-bottom: 0.9rem;
  text-wrap: balance;
}
.doc .updated {
  font-family: var(--font-mono);
  font-size: 0.69rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 2.4rem;
}
.doc h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.36rem;
  letter-spacing: -0.03em;
  margin: 2.5rem 0 0.85rem;
}
.doc p {
  color: var(--ink-dim);
  font-size: 1.05rem;
  line-height: 1.75;
  margin-bottom: 1rem;
  max-width: var(--measure);
}
.doc ul {
  color: var(--ink-dim);
  font-size: 1.05rem;
  line-height: 1.75;
  margin: 0 0 1.1rem 1.3rem;
  max-width: var(--measure);
}
.doc li { margin-bottom: 0.5rem; }
.doc strong { color: var(--ink); font-weight: 600; }
.doc a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.doc a:hover { color: var(--accent-bright); }
.doc .lede {
  font-size: clamp(1.06rem, 1.7vw, 1.22rem);
  color: var(--ink-dim);
  line-height: 1.68;
  margin-bottom: 1.8rem;
  max-width: var(--measure);
}

/* Inline code, used where a policy page names an actual directive.
   Code en ligne, là où une politique nomme une directive réelle.
   Código en línea, donde una política nombra una directiva real. */
.doc code, .article code {
  font-family: var(--font-mono);
  font-size: 0.88em;
  background: var(--bg-2);
  border: var(--rule) solid var(--line-soft);
  border-radius: 2px;
  padding: 0.08em 0.34em;
  word-break: break-word;
}

/* Bottom navigation shared by document and story pages.
   Navigation de bas de page partagée. / Navegación inferior compartida. */
.doc-nav {
  margin-top: 2.8rem;
  padding-top: 1.4rem;
  border-top: var(--rule) solid var(--line);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}
.doc-nav a { color: var(--accent); text-decoration: none; transition: color 0.25s ease; }
.doc-nav a:hover { color: var(--accent-bright); text-decoration: underline; text-underline-offset: 3px; }

/* ---------------------------------------------------------------------------
   10  Contact form / Formulaire de contact / Formulario de contacto
--------------------------------------------------------------------------- */
.contact-form { margin-top: 2rem; max-width: 34rem; }
.field { margin-bottom: 1.4rem; }
.field label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.71rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.55rem;
}
.field input,
.field textarea {
  width: 100%;
  background: var(--bg-2);
  border: var(--rule) solid var(--line);
  border-radius: 2px;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1.02rem;
  line-height: 1.6;
  padding: 0.8rem 0.9rem;
  transition: border-color 0.25s ease;
}
.field textarea { resize: vertical; min-height: 8.5rem; }
.field input:focus,
.field textarea:focus { border-color: var(--accent); }
.field input::placeholder,
.field textarea::placeholder { color: var(--muted); opacity: 0.7; }

/* Turnstile widget slot / Emplacement du widget / Espacio del widget */
.cf-turnstile { margin: 0 0 1.4rem; min-height: 65px; }

.form-status {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  line-height: 1.6;
  margin-top: 1.1rem;
  color: var(--muted);
}
.form-status.ok { color: var(--accent); }
.form-status.err { color: var(--accent-2); }
.form-note {
  font-size: 0.92rem;
  line-height: 1.65;
  color: var(--muted);
  margin-top: 1.5rem;
  max-width: 46ch;
}
.form-note a { color: var(--accent); }

/* ---------------------------------------------------------------------------
   11  Footer / Pied de page / Pie de página
--------------------------------------------------------------------------- */
footer.site {
  padding: 2.1rem var(--edge) 2.6rem;
  border-top: var(--rule) solid var(--line-soft);
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.5rem;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
}
footer.site .sep { color: var(--line); }
footer.site a {
  color: var(--muted);
  text-decoration: none;
  transition: color 0.25s ease;
}
footer.site a:hover { color: var(--accent); }

/* ---------------------------------------------------------------------------
   12  Motion / Mouvement / Movimiento

   One idea only: elements arrive once, staggered, on first scroll into view.
   Only transform and opacity are animated, so nothing triggers layout.
   Une seule idée : une arrivée échelonnée au premier défilement. Seuls
   transform et opacity sont animés, donc aucun recalcul de mise en page.
   Una sola idea: una llegada escalonada al entrar en pantalla. Solo se animan
   transform y opacity, así que nada provoca recálculo de diseño.
--------------------------------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.85s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.85s cubic-bezier(0.16, 1, 0.3, 1);
}
.reveal.in { opacity: 1; transform: none; }

/* If JavaScript never runs, nothing may stay invisible.
   Sans JavaScript, rien ne doit rester invisible.
   Sin JavaScript, nada debe quedar invisible. */
.no-js .reveal { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  .brand .glyph, .theme-btn, .dispatch a, .dispatch .go { transition: none; }
  .brand:hover .glyph, .theme-btn:hover { transform: none; }
}

/* ---- Responsive / Adaptatif / Adaptativo ---- */
@media (max-width: 720px) {
  header.site { gap: 0.9rem; }
  .controls { gap: 0.8rem; width: 100%; justify-content: space-between; }
  .site-nav { gap: 0.9rem; }
  .dispatch a { grid-template-columns: 1fr; gap: 0.6rem; }
  .dispatch .rail { padding-top: 0; display: flex; gap: 0.8rem; }
  .dispatch .rail .no { margin-bottom: 0; }
  .article blockquote { padding-left: 1.1rem; }
}
