/* Feuille commune aux quatre documents légaux d'Echo.
 *
 * ── Pourquoi un fichier partagé plutôt qu'un <style> par page ───────────────
 *
 * Ces quatre pages ont vécu avec quatre copies du même CSS, déjà divergentes :
 * `privacy.html` avait des couleurs que `mentions.html` n'avait pas. Quatre
 * copies d'une charte, c'est trois occasions d'en oublier une. Une requête de
 * plus est le prix, et elle est mise en cache pour les quatre pages.
 *
 * ── Pourquoi les polices sont hébergées ici ─────────────────────────────────
 *
 * ⚠️ NE PAS les remettre sur Google Fonts. L'audit RGPD relève en finding
 * **RGPD-004** (Élevée) que `google_fonts` transmet l'IP de l'utilisateur à
 * Google avant toute interaction. Le rejouer ici serait particulièrement mal
 * choisi : la page qui décrit le traitement des données ne peut pas en déclencher
 * un, non annoncé, à son propre chargement. Fraunces et Source Serif 4 sont sous
 * licence OFL, qui autorise la redistribution — les fichiers sont dans
 * `public/fonts/`, sous-ensemble latin, en variable.
 *
 * Conséquence à préserver : ces pages ne font **aucune requête vers un tiers**.
 * C'est vrai aussi de `moderation_captcha.html` par exception assumée, mais
 * celui-là n'est pas un document légal et n'est pas servi à un utilisateur.
 *
 * ── La charte, et d'où elle vient ──────────────────────────────────────────
 *
 * Reprise de `lib/components/styles/app_colors.dart` et
 * `app_text_styles.dart`, valeur par valeur, pour qu'un utilisateur qui ouvre
 * les CGU depuis les réglages ne change pas d'univers. Les deux voix de l'app
 * sont conservées telles quelles :
 *
 *   Fraunces        — serif éditorial, la voix des TITRES
 *   Source Serif 4  — serif de lecture, la voix du TEXTE
 *
 * Ces pages sont lues dans une WebView à l'intérieur de l'app, et l'app est
 * volontairement en thème clair uniquement (cf. le commentaire `theme: 'light'`
 * de `turnstile.html`). Donc pas de `prefers-color-scheme` : un document légal
 * qui s'assombrit alors que l'écran qui l'ouvre reste clair se lit comme un bug.
 */

@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 400 700; /* variable : une seule ressource pour toutes les graisses */
  font-display: swap;
  src: url('fonts/fraunces-latin.woff2') format('woff2');
}

@font-face {
  font-family: 'Source Serif 4';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/source-serif-4-latin.woff2') format('woff2');
}

:root {
  /* app_colors.dart, valeurs exactes */
  --ink: #000000;              /* cardTitle */
  --ink-soft: #616161;         /* cardSubtitle — grey.shade700, ~5.7:1 sur blanc */
  --page: #fafafa;             /* screenBackgroud — grey[50] */
  --card: #ffffff;             /* cardBackground */
  --accent: #673ab7;           /* mainColor — deepPurple */
  --accent-dark: #311b92;      /* mainColorDark — deepPurple[900] */
  --accent-pale: #ede7f6;      /* mainColorShade — deepPurple[50] */
  --rule: #eeeeee;
  --title: 'Fraunces', Georgia, 'Times New Roman', serif;
  --body: 'Source Serif 4', Georgia, 'Times New Roman', serif;
}

* { box-sizing: border-box; }

body {
  font-family: var(--body);
  /* 17px et non 16 : ces pages se lisent surtout dans une WebView de téléphone,
   * et la police est un serif de lecture, plus étroit qu'un sans-serif. */
  font-size: 17px;
  line-height: 1.65;
  color: var(--ink);
  background: var(--page);
  margin: 0;
  padding: 20px;
  -webkit-text-size-adjust: 100%;
}

.container {
  background: var(--card);
  max-width: 760px;
  margin: 0 auto;
  padding: 32px 28px 40px;
  border-radius: 12px;
  border: 1px solid var(--rule);
}

h1, h2, h3 { font-family: var(--title); color: var(--ink); }

h1 {
  font-size: 30px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.02em;
  margin: 0 0 6px;
}

/* Le trait sous le titre reprend l'accent de l'app : c'est la seule touche de
 * couleur de la page, et elle suffit à la rattacher à Echo. */
/* Le titre de l'accueil porte l'icône de l'app à sa gauche (20 octobre
 * 2026) : 1,6 em, soit 48 px à 30 px de corps, centrée sur la ligne. */
.brand-title { display: flex; align-items: center; gap: 0.4em; }
.brand-title img { width: 1.6em; height: 1.6em; flex-shrink: 0; }
/* Le slogan sous le titre (21 octobre 2026), aligné sur le nom et non sur
 * l'icône : il commence après elle, à 2 em du titre (1,6 em d'icône + 0,4 em
 * d'écart), redit en px parce que ce paragraphe n'a pas le corps du titre. */
.slogan {
  font-family: var(--title);
  font-style: italic;
  font-size: 15px;
  color: var(--ink-soft);
  margin: -4px 0 14px 60px;
  /* Il suit le <h1> : sans ceci, `h1 + p` lui poserait le trait d'accent des
   * pages légales, que l'accueil n'a jamais eu (cf. `.tagline`). */
  border-bottom: none;
  padding-bottom: 0;
}

h1 + .effective,
h1 + p {
  color: var(--ink-soft);
  font-size: 15px;
  margin: 0 0 28px;
  padding-bottom: 18px;
  border-bottom: 3px solid var(--accent);
}

h2 {
  font-size: 21px;
  font-weight: 600;
  margin: 36px 0 10px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--rule);
}

h3 { font-size: 17px; font-weight: 600; margin: 24px 0 8px; }

p, li { font-size: 17px; }
p { margin: 0 0 14px; }

ul, ol { margin: 0 0 18px; padding-left: 22px; }
li { margin-bottom: 7px; }

strong { font-weight: 600; }

a { color: var(--accent-dark); text-decoration: underline; text-underline-offset: 2px; }
a:hover { color: var(--accent); }

/* `code` sert dans ces pages à nommer des champs techniques (`photos`,
 * `voice_url`, `location`). Volontairement discret : c'est une précision, pas
 * l'objet du paragraphe. */
code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.88em;
  background: var(--accent-pale);
  color: var(--accent-dark);
  padding: 1px 5px;
  border-radius: 4px;
}

table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 20px;
  font-size: 15.5px;
}
th, td { text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--rule); vertical-align: top; }
th { font-family: var(--title); font-weight: 600; font-size: 14px; color: var(--ink-soft); }

/* Les tableaux de ces documents (sous-traitants, durées de conservation) sont
 * larges. Ils défilent dans leur propre boîte plutôt que d'élargir la page :
 * sur un téléphone, un corps de page qui défile horizontalement rend le texte
 * illisible partout ailleurs. */
.tablewrap, .table-responsive { overflow-x: auto; margin: 0 0 20px; }
.tablewrap table, .table-responsive table { margin: 0; }

/* `.var-desc` : une puce de description à l'intérieur d'une cellule de tableau,
 * utilisée 31 fois dans privacy.html pour énumérer les champs d'un traitement.
 * Nom conservé tel quel plutôt que renommé — le renommer aurait demandé 31
 * modifications dans un document légal pour un gain nul. */
.var-desc { display: block; margin-bottom: 5px; }
.var-desc:last-child { margin-bottom: 0; }

/* Encadré d'avertissement, pour les points qu'un lecteur ne doit pas manquer. */
.note {
  background: var(--accent-pale);
  border-left: 4px solid var(--accent);
  padding: 14px 16px;
  border-radius: 0 8px 8px 0;
  margin: 0 0 20px;
}
.note p:last-child { margin-bottom: 0; }

footer {
  margin-top: 36px;
  padding-top: 18px;
  border-top: 1px solid var(--rule);
  color: var(--ink-soft);
  font-size: 14.5px;
}

/* ── Pages d'accueil et d'erreur ───────────────────────────────────────────
 *
 * `index.html` et `404.html` ne sont pas des documents légaux, mais elles sont
 * servies par le même hôte et vues par les mêmes personnes — un utilisateur qui
 * suit un lien depuis le Play Store, ou le relecteur d'un fournisseur. Les
 * laisser dans un autre style aurait été le seul endroit où le domaine ne
 * ressemble pas à l'application. */

.centered { text-align: center; }

/* La page 404 est centrée dans la FENÊTRE, pas seulement dans sa carte.
 *
 * `body.error-page` plutôt qu'une règle sur `.container` : c'est le corps qui
 * doit occuper la hauteur pour que le centrage vertical ait un sens. `min-height`
 * et non `height`, sinon un écran très court rognerait la carte au lieu de la
 * laisser défiler. */
body.error-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
body.error-page .container { width: 100%; padding: 44px 28px 40px; }

/* Le grand nombre de la page 404. Fraunces à cette taille est ce qui rend la
 * page reconnaissable d'un coup d'œil, sans y écrire un logo.
 *
 * `clamp` et non une taille fixe : 84px était trop petit pour « un gros 404 au
 * milieu » sur un écran large, et une valeur fixe assez grande pour le bureau
 * déborderait sur un téléphone. Les trois bornes sont donc min / idéal / max —
 * 76px au plus étroit, 15 % de la largeur de fenêtre entre les deux, 160px au
 * plus large. */
/* La mascotte inquiète au-dessus du 404 (20 octobre 2026). */
.error-mascot {
  display: block;
  width: clamp(88px, 12vw, 120px);
  height: auto;
  margin: 0 auto 10px;
}

.bignum {
  font-family: var(--title);
  font-size: clamp(76px, 15vw, 160px);
  font-weight: 700;
  line-height: 0.95;
  letter-spacing: -0.05em;
  color: var(--accent);
  margin: 0 0 4px;
}

/* Le sous-titre de la 404 : pas de trait sous le titre ici — le grand nombre
 * porte déjà la couleur, et deux accents empilés se disputeraient l'attention. */
.bignum + h2 {
  border-bottom: none;
  margin: 0 0 22px;
  padding-bottom: 0;
  font-size: 24px;
}

/* Liste de liens sans puces, centrée en bloc mais alignée à gauche à
 * l'intérieur : centrer aussi le texte rendrait les intitulés flottants. */
.links-list {
  list-style: none;
  padding: 0;
  margin: 28px 0;
  display: inline-block;
  text-align: left;
}
.links-list li { margin-bottom: 13px; }
.links-list a { font-weight: 600; }

/* La phrase d'accroche de la page d'accueil : centrée, en gris de sous-titre,
 * et sans le trait d'accent que `h1 + p` poserait — la page d'accueil n'a pas de
 * date d'entrée en vigueur à séparer du titre. */
.tagline {
  text-align: center;
  color: var(--ink-soft);
  font-size: 18px;
  margin: 0 0 30px;
  border-bottom: none;
  padding-bottom: 0;
}

/* Les quatre liens de la page d'accueil : UNE TUILE PAR LIGNE.
 *
 * La première version les mettait en rangée (`flex: 1 1 210px`), et c'était un
 * mauvais choix pour deux raisons vues à l'écran : au-delà de deux tuiles la
 * rangée se casse en un pavé irrégulier, et surtout ces quatre intitulés sont
 * longs et inégaux — « Politique de confidentialité » contre « Mentions
 * légales » — donc côte à côte ils se lisaient comme une phrase et non comme un
 * menu. Empilés, chacun est une cible franche, alignée à gauche comme le reste
 * du texte de la page. */
.links { display: flex; flex-direction: column; gap: 10px; margin-top: 20px; }
.links a {
  display: block;
  padding: 15px 18px;
  text-decoration: none;
  font-weight: 600;
  color: var(--ink);
  background: var(--page);
  border: 1px solid var(--rule);
  border-radius: 8px;
  /* Le chevron dit « ça mène ailleurs » sans ajouter de mot, et il est en
   * pseudo-élément pour ne pas se retrouver dans le texte copié. */
  position: relative;
  padding-right: 40px;
}
.links a::after {
  content: '→';
  position: absolute;
  right: 18px;
  color: var(--ink-soft);
}
.links a:hover {
  background: var(--accent-pale);
  border-color: var(--accent);
  color: var(--accent-dark);
}
.links a:hover::after { color: var(--accent); }

.container > footer:last-child { text-align: center; }

/* Pas de surcharge de `.bignum` en petit écran : le `clamp` ci-dessus s'en
 * charge déjà, et une media query par-dessus se battrait avec lui. */

@media (max-width: 600px) {
  body { padding: 12px; }
  .container { padding: 24px 18px 32px; border-radius: 10px; }
  h1 { font-size: 26px; }
  .slogan { margin-left: 52px; }
  h2 { font-size: 19px; }
}

/* ─────────────────────────────────────────────────────────────────────────
 * SÉLECTEUR DE LANGUE — 21 août 2026, mécanique revue le 22
 *
 * Un seul fichier par document, deux blocs de langue, un seul visible. En CSS
 * pur, sans une ligne de JavaScript : `public/` n'a aucune étape de build, et
 * une bascule qui dépendrait de JS tomberait dans une WebView qui le refuse.
 *
 * Le français est la version qui fait foi (clause de prévalence dans chaque
 * document, alignée sur le CGU §8 : droit français, juridiction française).
 * C'est pourquoi il est l'état PAR DÉFAUT et le premier dans le DOM : une URL
 * nue rend le texte qui gouverne. `#en` est un choix explicite.
 *
 * ⚠️ Ce qui a changé le 22 août, et qu'il ne faut pas défaire. La première
 * version portait l'id sur la <section> elle-même et cachait le français par
 * `#en:target ~ #fr`, un sélecteur de frère SUIVANT — ce qui obligeait à placer
 * l'anglais AVANT le français dans le DOM. Deux défauts, dont un visible :
 * l'ordre était contre-intuitif et se « corrigeait » tout seul au premier
 * relecteur, et surtout `:target` faisait DÉFILER la page jusqu'à la section,
 * donc le sélecteur de langue sortait de l'écran à chaque clic. Porter l'id sur
 * une ancre vide placée avant `.container` règle les deux : le sélecteur devient
 * un descendant (`#en:target ~ .container .lang-en`), l'ordre des deux sections
 * cesse d'être porteur, et cibler l'ancre revient à défiler jusqu'au haut du
 * document.
 * ───────────────────────────────────────────────────────────────────────── */

.langswitch {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin: 0 0 20px;
}

.langswitch a {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 5px 12px;
  border: 1px solid rgba(127, 127, 127, 0.35);
  border-radius: 999px;
  text-decoration: none;
}

/* ⚠️ PAS D'INDICATEUR D'ONGLET ACTIF, et ce n'est pas un oubli. Le styler
 * demanderait un sélecteur de frère depuis l'ancre, donc que la bascule suive
 * l'ancre dans le DOM — faisable, mais `.container` s'interpose. L'autre issue
 * était de passer `.container` en `display:flex` pour réordonner avec `order:`,
 * écartée parce que ça change la fusion des marges sur les huit pages d'un coup,
 * pour un liseré. La langue affichée se lit dans le texte ; les deux libellés
 * sont écrits en clair (« Français », « English ») pour que la bascule reste
 * évidente sans état. */

/* ⚠️ L'ANCRE EST UN ÉLÉMENT VIDE EN HAUT DU BODY, ET C'EST TOUT L'OBJET DE
 * CETTE FORME — corrigé le 22 août 2026.
 *
 * `:target` fait défiler le navigateur jusqu'à l'élément ciblé. Quand l'id
 * vivait sur la <section> elle-même, cliquer sur « English » faisait donc
 * descendre la page : la section commençant SOUS le sélecteur de langue, celui-ci
 * sortait de l'écran, avec le haut de la carte. Constaté en captures d'écran, pas
 * déduit.
 *
 * L'id est désormais porté par une ancre vide placée AVANT `.container`, d'où
 * `#en:target ~ .container .lang-en` — l'ancre est frère du conteneur, la
 * section en est descendante. Cibler cette ancre revient à défiler jusqu'au
 * haut du document : aucun mouvement visible quand on y est déjà.
 *
 * Deux propriétés font le travail, et retirer l'une des deux ramène le défaut :
 *
 *   - `position: absolute` la sort du flux. Indispensable et non cosmétique :
 *     `body.error-page` (la 404) est en `display:flex`, où deux <span> en tête
 *     deviendraient des éléments flex posés À CÔTÉ de la carte et la
 *     décaleraient horizontalement.
 *   - `scroll-margin-top: 100vh` force le clamp. Sans elle il resterait le saut
 *     du padding du body (~40 px) : le navigateur viserait `offsetTop`, qui n'est
 *     pas zéro. Avec elle la cible calculée est négative, donc bornée à 0. */
.langtarget {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  overflow: hidden;
  scroll-margin-top: 100vh;
}

/* La bascule. L'anglais caché par défaut, révélé quand son ancre est ciblée ; le
 * français s'efface alors. Les deux sections sont descendantes du même
 * `.container`, qui suit l'ancre. */
.lang-en { display: none; }
#en:target ~ .container .lang-en { display: block; }
#en:target ~ .container .lang-fr { display: none; }

/* La clause de prévalence : présente dans les deux langues, discrète, et
 * jamais cachée par la bascule puisqu'elle vit hors des deux blocs. */
.prevalence {
  font-size: 13px;
  opacity: 0.72;
  border-top: 1px solid rgba(127, 127, 127, 0.25);
  margin-top: 32px;
  padding-top: 14px;
}

/* ─────────────────────────────────────────────────────────────────────────
 * COMPTE À REBOURS DE LA SORTIE — page d'accueil, 19 octobre 2026
 *
 * Le seul aplat de couleur du domaine, et exprès : l'image de présentation Play
 * et l'`og:image` de la page (`scripts/render_feature_graphic.py`) portent le
 * même dégradé, la même mascotte et le même nom. Quelqu'un qui clique sur un aperçu partagé
 * retrouve le même visuel en arrivant. Le reste de la page garde la charte
 * claire : sous le bloc, ce sont les liens de compte et les documents légaux.
 *
 * Les chiffres ont une largeur fixe posée par la feuille (voir « Défilement des
 * chiffres ») : la police n'a pas de chiffres tabulaires. Retirer le bloc une
 * fois la sortie passée.
 *
 * Le dégradé est celui de l'ICÔNE (#673ab7 → #311b92, `scripts/render_app_icon.py`)
 * depuis le 20 octobre 2026, demande de Hugo (« le violet est trop sombre ») ;
 * l'image de présentation et l'aperçu ont été redessinés sur lui le 21. Fond plus
 * clair, donc les textes
 * blancs translucides sont remontés à 0,8 au moins : à 0,6 ou 0,65 ils
 * tombaient à 3,7 et 4,1:1 sur #673ab7 (seuil 4,5:1 pour du texte courant) ;
 * 0,8 donne 5,3:1 au point le plus clair.
 * ───────────────────────────────────────────────────────────────────────── */

.countdown {
  /* Coin à coin (`to bottom right`) et non `135deg` (20 octobre 2026) : c'est
   * le dégradé de l'icône (objectBoundingBox 0,0 → 1,1), et le seul qui reste
   * identique quand on étire la boîte — l'ouverture anime un fond étiré
   * (`.countdown-bg`), qui doit être, à sa première et à sa dernière image,
   * exactement le fond de l'écran plein puis celui du bloc. */
  background: linear-gradient(to bottom right, var(--accent) 0%, var(--accent-dark) 100%);
  color: #ffffff;
  border-radius: 12px;
  padding: 28px 20px 24px;
  margin: 0 0 30px;
  text-align: center;
  /* Flex en colonne dans les DEUX états (plein écran et dans la page) : changer
   * de mode d'affichage au milieu de l'animation ferait sauter le contenu. */
  display: flex;
  flex-direction: column;
  justify-content: center;
  box-sizing: border-box;
}

.countdown p { margin: 0; }
/* Aucun enfant ne rétrécit (20 octobre 2026) : le nom et la mascotte masquent
 * leur débordement pour se replier, et un élément flex à `overflow: hidden`
 * peut être comprimé sous sa taille. Quand l'écran plein dépassait la fenêtre,
 * le navigateur les écrasait — nom et visage coupés à mi-hauteur. Le contenu
 * déborde désormais, et l'écran plein défile (`overflow-y: auto`). */
.countdown > * { flex-shrink: 0; }

.countdown-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin: 18px 0 16px;
}

.countdown-grid [data-unit] {
  display: block;
  font-family: var(--title);
  font-weight: 700;
  font-size: clamp(36px, 11vw, 72px);
  line-height: 1;
}

.countdown-grid small {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.8);
}

.countdown-date {
  font-family: var(--title);
  font-size: 19px;
  font-weight: 600;
}

.countdown-age {
  margin-top: 6px !important;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.8);
}

/* Défilement des chiffres (20 octobre 2026, demande de Hugo) : un compteur
 * mécanique. À chaque changement, l'ancien chiffre descend et sort, le nouveau
 * arrive d'en haut, les deux d'un même mouvement (index.html, `setDigits`).
 * Seuls les chiffres qui changent bougent.
 *
 * ⚠️ Largeur FIXE par chiffre, posée ici : le sous-ensemble de Fraunces servi
 * (`fonts/fraunces-latin.woff2`) n'a PAS de chiffres tabulaires. Mesuré le
 * 20 octobre 2026 en gras à 100 px : de 40 px (« 1 ») à 64 px (« 0 »), identique
 * avec ou sans `tabular-nums`, qui n'avait donc jamais rien fait. Sans cette
 * largeur, la rangée tremblait à chaque seconde. 0,64 em = le plus large (« 0 ») ;
 * chaque chiffre est centré dans sa case.
 *
 * Pas de bord net : un dégradé de masque efface le chiffre en mouvement. Une
 * première version coupait net (`overflow: hidden`) et l'on voyait le masque
 * rogner les chiffres. Le fondu tient dans les 0,13 em du haut et du bas de la
 * boîte du chiffre, où le dessin ne va jamais (à `line-height: 1`, il occupe de
 * 0,13 à 0,87 em de la boîte, mesuré) : à l'arrêt rien n'est estompé, et en
 * mouvement rien ne sort de la rangée. Un fondu qui débordait de la boîte
 * (0,1 puis 0,3 em, essayés) laissait le chiffre sortant traîner sur le libellé
 * « SECONDES », à 6 px dessous. Le débord de 0,1 em sur les CÔTÉS reste : le
 * masque coupe au bord de la case, et la patte fine du « 4 » dépasse. */
.countdown-grid .digit {
  display: inline-block;
  position: relative;
  vertical-align: top;
  /* Les cotes ci-dessous sont celles du CHIFFRE, débord non compris : la
   * feuille met tout en `border-box`, qui aurait mangé le débord dans le 1 em. */
  box-sizing: content-box;
  width: 0.64em;
  height: 1em;
  padding: 0 0.1em;
  margin: 0 -0.1em;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 0.13em, #000 calc(100% - 0.13em), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 0.13em, #000 calc(100% - 0.13em), transparent 100%);
}
.countdown-grid .digit > span {
  position: absolute;
  top: 0;
  left: 0.1em;
  right: 0.1em;
  height: 1em;
  text-align: center;
  will-change: transform;
}
.countdown-grid .digit > .digit-out {
  animation: digit-out 650ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
.countdown-grid .digit > .digit-in {
  animation: digit-in 650ms cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes digit-out {
  from { transform: translateY(0); }
  to { transform: translateY(0.9em); }
}
@keyframes digit-in {
  from { transform: translateY(-0.9em); }
  to { transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .countdown-grid .digit > .digit-out { display: none; }
  .countdown-grid .digit > .digit-in { animation: none; }
}

/* Après l'heure : les chiffres cèdent la place à la phrase. */
.countdown-live {
  display: none;
  font-family: var(--title);
  font-size: clamp(30px, 8vw, 48px);
  font-weight: 700;
  margin: 18px 0 14px !important;
}
.countdown.is-live .countdown-grid { display: none; }
.countdown.is-live .countdown-live { display: block; }
.countdown.is-live .countdown-soon { display: none; }

@media (max-width: 600px) {
  .countdown { padding: 22px 12px 20px; }
  .countdown-grid { gap: 4px; }
  .countdown-grid small { font-size: 10.5px; letter-spacing: 0.04em; }
}

/* ─────────────────────────────────────────────────────────────────────────
 * ÉCRAN D'OUVERTURE — 19 octobre 2026
 *
 * `html.intro` est posée par le script du <head> (jamais sans JavaScript : la
 * page complète est alors l'état par défaut). Le bloc visible devient fixe et
 * couvre la fenêtre ; « Plus d'infos » retire la classe et le script anime le
 * passage (index.html, technique FLIP) : la mise en page finale est posée d'un
 * coup, et seuls des `transform` et des opacités sont animés (`.revealing`,
 * plus bas).
 *
 * Le bouton, le pied, le nom et la mascotte de l'écran plein sont repliés hors
 * intro (hauteur, opacité) plutôt que `display: none` : un reste de la première
 * version, qui les animait. Ils ne sont plus animés ; le script les efface en
 * fondu AVANT de poser la mise en page finale.
 * ───────────────────────────────────────────────────────────────────────── */

html.intro body { overflow: hidden; }

/* Barres de Safari (iPhone, iOS 26) violettes sur l'écran d'ouverture : Safari
 * les teinte avec la COULEUR de fond de <body> (un dégradé ne compte pas, et
 * `<meta name="theme-color">` est ignorée — essayée le 21 oct. 2026, sans
 * effet). Couleur unie = milieu du dégradé de `.countdown-bg` (#673ab7 →
 * #311b92), que les deux barres bordent. Le gris revient avec la page, quand
 * « Plus d'infos » retire `intro`. */
html.intro,
html.intro body { background-color: #4c2aa4; }

html.intro .countdown {
  position: fixed;
  inset: 0;
  z-index: 10;
  margin: 0;
  border-radius: 0;
  padding: 32px 20px 20px;
  /* Centrage par marges automatiques (nom en haut, pied en bas) plutôt que
   * `justify-content: center` + pied en position absolue : sur un écran court,
   * le pied absolu passait SOUS le bouton, et un contenu centré qui déborde est
   * rogné par le haut. Ici le pied pousse, et l'écran défile s'il le faut. */
  justify-content: flex-start;
  overflow-y: auto;
}
/* La marge automatique du haut est portée par la mascotte, premier élément
 * de l'écran plein ; le nom la suit. */
html.intro .countdown-mascot { margin-top: auto !important; }


/* La mascotte (20 octobre 2026) : le visage seul (« L5 »), centré au-dessus du
 * nom, sur l'écran plein seulement — repliée hors de lui comme le nom. Sa
 * largeur suit la HAUTEUR de la fenêtre, comme les chiffres et les écarts plus
 * bas : c'est la hauteur qui manque. Une première version, plus petite et non
 * réglée sur la hauteur, faisait sortir le bouton et le pied de page d'un écran
 * de 800 px. L'image est recadrée sur les traits (render_mascot.py, FACE_BOX) :
 * la largeur est celle du dessin, pas de marges vides. */
.countdown-mascot {
  line-height: 0;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
}
.countdown-mascot img {
  display: block;
  margin: 0 auto;
  width: clamp(110px, 19vh, 280px);
  height: auto;
}
.countdown-mascot .mascot-live { display: none; }
.countdown.is-live .countdown-mascot .mascot-wait { display: none; }
.countdown.is-live .countdown-mascot .mascot-live { display: block; }
html.intro .countdown-mascot {
  max-height: 240px;
  margin-bottom: 1.5vh !important;
  opacity: 1;
}

/* Le nom, sur l'écran plein seulement : dans la page, le <h1> le porte déjà
 * juste au-dessus. Replié comme le bouton (hauteur, opacité) pour disparaître
 * pendant le rétrécissement. `aria-hidden` : le <h1> reste le titre lu. */
.countdown-brand {
  font-family: var(--title);
  font-weight: 700;
  letter-spacing: -0.02em;
  /* 1.3 et non 1.1 : avec `overflow: hidden` (nécessaire au repli), la boîte
   * coupe tout ce qui sort de la ligne, et à 1.1 la queue du « y » de Fraunces
   * en sortait — le nom s'affichait rogné en bas (vu par Hugo le 20 octobre
   * 2026). 72 px × 1.3 = 94 px, sous le `max-height` de 120 px. */
  line-height: 1.3;
  font-size: 0;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
}
html.intro .countdown-brand {
  font-size: clamp(38px, min(8vw, 8vh), 72px);
  max-height: 120px;
  margin-bottom: 0 !important;
  opacity: 1;
}
/* Le slogan (21 octobre 2026), sous le nom de l'écran plein seulement, replié
 * hors de lui comme le nom : dans la page, `.slogan` suit le <h1>. L'écart du
 * nom aux chiffres, porté jusque-là par le nom, est passé sous le slogan. */
.countdown-slogan {
  font-family: var(--title);
  font-style: italic;
  font-size: 0;
  line-height: 1.3;
  color: rgba(255, 255, 255, 0.85);
  max-height: 0;
  opacity: 0;
  overflow: hidden;
}
html.intro .countdown-slogan {
  font-size: clamp(15px, min(2.6vw, 2.4vh), 22px);
  max-height: 60px;
  margin-bottom: min(18px, 1.6vh) !important;
  opacity: 1;
}
html.intro .countdown-grid {
  width: 100%;
  max-width: 920px;
  margin: min(28px, 2.6vh) auto min(24px, 2.4vh);
}
/* `min(15vw, 13vh)` : la largeur bornait seule les chiffres ; la hauteur les
 * borne aussi depuis que la mascotte prend sa place au-dessus du nom. */
html.intro .countdown-grid [data-unit] { font-size: clamp(52px, min(15vw, 12vh), 150px); }
html.intro .countdown-grid small { font-size: 14px; }
html.intro .countdown-date { font-size: clamp(19px, 3vw, 26px); }

.countdown-more {
  align-self: center;
  font-family: var(--title);
  font-weight: 600;
  font-size: 17px;
  color: var(--accent-dark);
  background: #ffffff;
  border: none;
  border-radius: 999px;
  cursor: pointer;
  /* Replié hors intro : voir l'en-tête de section. */
  max-height: 0;
  padding: 0 28px;
  margin: 0;
  opacity: 0;
  overflow: hidden;
  visibility: hidden;
}
html.intro .countdown-more {
  max-height: 60px;
  padding: 14px 28px;
  margin-top: 30px;
  opacity: 1;
  visibility: visible;
}
.countdown-more:hover { background: var(--accent-pale); }
.countdown-more:focus-visible { outline: 3px solid #b39ddb; outline-offset: 3px; }

.countdown-foot {
  font-size: 13px;
  line-height: 1.9;
  color: rgba(255, 255, 255, 0.8);
  /* Replié hors intro, comme le bouton. */
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  visibility: hidden;
}
/* Un lien ne se coupe jamais en deux : la ligne se replie ENTRE les liens. */
.countdown-foot a { color: rgba(255, 255, 255, 0.92); white-space: nowrap; }
.countdown-foot a:hover { color: #ffffff; }
html.intro .countdown-foot {
  max-height: 120px;
  opacity: 1;
  visibility: visible;
  margin-top: auto !important;
  padding-top: min(28px, 2.5vh);
}

/* La page apparaît sous le bloc qui rétrécit, en fondu. Pas d'opacité sur
 * `.container` lui-même : le bloc en est descendant, il disparaîtrait avec. */
.container > :not(section),
.container > section > :not(.countdown) { transition: opacity 600ms ease 200ms; }
html.intro .container > :not(section),
html.intro .container > section > :not(.countdown) { opacity: 0; }

/* Pendant l'ouverture (20 octobre 2026, refonte demandée par Hugo : « deux
 * allers-retours qui saccadent »). Mesuré image par image, la première version
 * animait en TRANSITIONS CSS la géométrie du bloc d'un côté (750 ms vers un
 * rectangle mesuré d'avance) et le contenu de l'autre (`font-size` sur `*`,
 * `max-height` en 400 ms, marges), sur des horloges différentes. Dans WebKit
 * (Safari, tout navigateur d'iPhone), la `font-size` héritée n'avance pas avec
 * le parent : chaque niveau (`[data-unit]` → `.digit` → chiffre) démarre sa
 * propre transition plus tard. À 390 px, les cases des chiffres rapetissaient
 * pendant que les chiffres restaient à 58 px : la grille passait sur deux lignes
 * (81 → 140 px) et débordait du fond (17 px) ; le nettoyage à 800 ms rendait
 * alors au bloc sa hauteur réelle d'un coup (363 → 437 px), puis les chiffres
 * finissaient de rétrécir et il retombait (367 px). Second aller-retour.
 *
 * Désormais rien n'est mis en page pendant le mouvement : la mise en page finale
 * est posée, puis chaque élément part de sa place plein écran par un
 * `transform` (index.html). Le fond est un calque à part, `.countdown-bg`, à la
 * taille FINALE du bloc et étiré au départ jusqu'à la fenêtre : un fond étiré ne
 * déforme rien, contrairement à du texte, et ses coins sont corrigés image par
 * image. Le bloc devient un contexte d'empilement au-dessus de la page, et le
 * calque passe sous ses enfants (`z-index: -1` dans ce contexte). */
.countdown.revealing {
  position: relative;
  z-index: 10;
  background: none;
}
.countdown-bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to bottom right, var(--accent) 0%, var(--accent-dark) 100%);
  transform-origin: 0 0;
  pointer-events: none;
}
/* Le nom, la mascotte, le bouton et le pied s'effacent avant le mouvement :
 * ils n'ont pas de place dans la page, ils n'ont donc nulle part où aller. */
html.intro .countdown.leaving .countdown-mascot,
html.intro .countdown.leaving .countdown-brand,
html.intro .countdown.leaving .countdown-slogan,
html.intro .countdown.leaving .countdown-more,
html.intro .countdown.leaving .countdown-foot,
html.intro .countdown.leaving .rewrap {
  opacity: 0;
  transform: scale(0.96);
  transition: opacity 180ms ease-out, transform 180ms ease-out;
}

@media (max-width: 400px) {
  html.intro .countdown { padding-left: 10px; padding-right: 10px; }
  html.intro .countdown-grid small { font-size: 10px; letter-spacing: 0.02em; }
}

@media (prefers-reduced-motion: reduce) {
  .container > :not(section),
  .container > section > :not(.countdown) { transition: none; }
}

/* ── Badges des stores (20 octobre 2026) ──────────────────────────────────
 * Fichiers officiels d'Apple et de Google, à la MÊME hauteur (Google exige
 * que le sien ne soit jamais plus petit que les autres badges) et jamais sous
 * 40 px (minimum Apple à l'écran). L'écart vaut au moins un quart de la
 * hauteur : c'est l'espace libre minimal de la charte Apple. Aucun filtre,
 * aucune opacité : les deux chartes interdisent de modifier le badge, y
 * compris pour le griser avant la sortie — l'état « pas encore » se dit par
 * l'absence de lien (index.html, `data-store-href`), pas par l'image. */
/* « Bientôt disponible sur » (20 octobre 2026, demande de Hugo) : au-dessus des
 * badges, en petites capitales espacées. L'écart du bloc au-dessus est porté par
 * cette ligne, et non plus par les badges, qui s'en rapprochent. Masquée à
 * l'heure de sortie (`.is-live`) : les badges deviennent alors des liens, et
 * « bientôt » serait faux. */
.countdown-soon {
  margin-top: 36px !important;
  font-family: var(--title);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.8);
}
.countdown-stores {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px 14px;
  margin: 10px 0 0;
}
.store-badge { display: block; line-height: 0; border-radius: 8px; }
.store-badge:not([href]) { cursor: default; }
.store-badge[href]:focus-visible { outline: 3px solid #b39ddb; outline-offset: 3px; }
.store-badge img { display: block; height: 40px; width: auto; }

html.intro .countdown-soon { margin-top: min(64px, 5.5vh) !important; font-size: 13px; }
html.intro .countdown-stores { margin-top: 12px; gap: 14px 16px; }
html.intro .store-badge img { height: 52px; }

/* Le bouton suit les badges. Écart porté de 22 à 40 px le 20 octobre 2026
 * (demande de Hugo : plus d'air entre les badges et « Plus d'infos »). */
html.intro .countdown-stores + .countdown-more { margin-top: min(40px, 4vh); }

.trademarks {
  margin-top: 28px !important;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-soft);
}

/* ── Écrans d'ouverture étroits ou courts ──────────────────────────────────
 * EN FIN DE FICHIER, exprès : à spécificité égale, la dernière règle gagne, et
 * une première version placée plus haut était écrasée par les règles des badges
 * (deux badges empilés à 375 px). */
@media (max-width: 400px) {
  /* Deux badges côte à côte jusqu'à 320 px : 42 px de haut y tiennent. */
  html.intro .store-badge img { height: 42px; }
  html.intro .countdown-soon { margin-top: 48px !important; font-size: 12px; }
  html.intro .countdown-stores { gap: 10px 10px; }
}
@media (max-height: 700px) {
  /* iPhone SE et fenêtres basses : tout tient sans défiler à 640 px. */
  html.intro .countdown { padding-top: 20px; }
  html.intro .countdown-brand { font-size: 34px; }
  html.intro .countdown-slogan { font-size: 14px; margin-bottom: 10px !important; }
  html.intro .countdown-mascot img { width: clamp(70px, 13vh, 120px); }
  html.intro .countdown-grid { margin: 16px auto 14px; }
  html.intro .countdown-grid [data-unit] { font-size: clamp(44px, min(13vw, 12vh), 110px); }
  html.intro .countdown-soon { margin-top: 38px !important; }
  html.intro .countdown-stores { margin-top: 8px; }
  html.intro .countdown-stores + .countdown-more { margin-top: 26px; padding: 12px 26px; }
  html.intro .countdown-foot { padding-top: 16px; }
}
