/* ==========================================================================
   NUMERI — LE SYSTEME DE DESIGN · v2 (refonte du 02/09/2026)
   numeri-video.com · edite par Odratta · marche international (EN)

   LA CHARTE VIENT DU DOSSIER `branding/` (10 fichiers, 02/09/2026) :
     · un monogramme N a deux traits ronds et un point, trace en SVG
       (assets/img/numeri-mark.svg) ; le mot NUMERI en capitales rondes
       (assets/img/numeri-wordmark.svg) — tous deux en currentColor ;
     · deux couleurs : le noir #000 et le blanc casse #FAFAFA ;
     · UNE rampe, bleu royal → violet → orchidee, celle du « 3 minutes » de
       la baseline. Elle ne sert qu'a trois endroits : le mot-cle du titre,
       le monogramme, l'anneau d'attente d'un rendu. Nulle part ailleurs.
     · Poppins, la police de la baseline « Your product video in 3 minutes. »

   CE QUI FAIT LE PREMIUM ICI : le blanc, les proportions, une seule echelle
   d'espacement (multiple de 4), cinq tailles de titre, des angles ronds qui
   viennent du logo, des boutons noirs en pilule, aucune ombre coloree, aucun
   halo, aucun verre depoli, aucune carte dans une carte.

   TOUT est pilote par les variables --n-*. Changer une valeur ici change
   toute la vitrine ET l'application (app.css ne fait que consommer).

   ⚠️ CSP SANS 'unsafe-inline' : aucun style en ligne nulle part. Une valeur
   d'instance passe par une classe ou par element.style.setProperty('--n-…').
   ⚠️ BLANC SEULEMENT : aucune regle data-theme, aucun prefers-color-scheme
   (Joseph, 02/09/2026 : « la seule couleur du site soit le mode white »).
   ⚠️ 83 % des visiteurs arrivent dans la WEBVIEW d'Instagram, sur telephone :
   pas de survol obligatoire, pas de 100vh, cibles de 44 px, texte a 16 px.

   Sommaire
     01  Polices
     02  Jetons
     03  Reset & base
     04  Typographie
     05  Mise en page & utilitaires
     06  Boutons
     07  Champs
     08  Cartes, notes, pastilles
     09  En-tete & menu
     10  Heros & scene video
     11  La bande de films
     12  Etapes, formats, faits
     13  Tarifs
     14  Formulaire sur mesure
     15  Pied de page
     16  Attente, squelettes, progression, vide, modale, menu, tableau
     17  Mouvement & prefers-reduced-motion
     18  WhatsApp
     19  Bandeau de consentement (Microsoft Clarity, derriere un oui)
   ========================================================================== */

/* == 01 · POLICES ==========================================================
   Poppins 400 / 500 / 600, latin + latin-ext, auto-hebergee (SIL OFL).
   Les fichiers 300 et 700 restent sur le disque mais ne sont plus charges :
   trois graisses suffisent, et chaque fichier de moins est un aller-retour
   de moins dans une webview 4G. Aucune police mono : la baseline n'en a pas.
   ⚠️ Jamais de Google Fonts en CDN (CNIL) — la CSP les bloquerait de toute
   facon. Zero requete tierce = aucun bandeau cookies.
   ========================================================================== */
@font-face { font-family:"Poppins"; font-style:normal; font-weight:400; font-display:swap;
  src:url("../fonts/poppins-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:"Poppins"; font-style:normal; font-weight:500; font-display:swap;
  src:url("../fonts/poppins-500-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:"Poppins"; font-style:normal; font-weight:600; font-display:swap;
  src:url("../fonts/poppins-600-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:"Poppins"; font-style:normal; font-weight:400; font-display:swap;
  src:url("../fonts/poppins-400-latinext.woff2") format("woff2");
  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:"Poppins"; font-style:normal; font-weight:500; font-display:swap;
  src:url("../fonts/poppins-500-latinext.woff2") format("woff2");
  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:"Poppins"; font-style:normal; font-weight:600; font-display:swap;
  src:url("../fonts/poppins-600-latinext.woff2") format("woff2");
  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; }
/* Secours cale sur les metriques de Poppins : pendant le swap la ligne a
   deja la bonne largeur, rien ne saute (CLS 0). */
@font-face {
  font-family: "Poppins secours";
  src: local("Helvetica Neue"), local("Arial"), local("Segoe UI"), local("Roboto");
  size-adjust: 112%; ascent-override: 93.75%; descent-override: 31.25%; line-gap-override: 0%;
}

/* == 02 · JETONS ===========================================================
   Chaque couleur d'encre porte son contraste mesure sur #FAFAFA.
   ========================================================================== */
:root {
  color-scheme: light;

  /* --- Fonds & surfaces ------------------------------------------------- */
  --n-bg:          #FAFAFA;   /* le blanc de la charte */
  --n-surface:     #FFFFFF;   /* cartes, champs, en-tete */
  --n-surface-2:   #F2F2F3;   /* puits doux : zone de depot, survol */
  --n-surface-3:   #E9E9EB;   /* pressé, rail de jauge */
  --n-ink:         #000000;   /* le noir de la charte : titres, boutons, marque */
  --n-well:        #050505;   /* le cadre d'une video */
  --n-veil:        rgba(250, 250, 250, 0.88);

  /* --- Encre ------------------------------------------------------------ */
  --n-text:          #111113;   /* 18.6:1 */
  --n-text-2:        #55555B;   /*  7.2:1 — le texte secondaire */
  --n-text-3:        #6B6B72;   /*  5.1:1 — legendes, aide */
  --n-text-disabled: #A6A6AC;   /* exempte 1.4.3 */
  --n-text-inverse:  #FFFFFF;
  --n-text-inverse-2:rgba(255, 255, 255, 0.72);

  /* --- L'accent : une couleur, prise dans la rampe -------------------- */
  --n-accent:      #5F55C9;   /* 5.6:1 — liens, focus, etat coche */
  --n-accent-hi:   #4C43B4;   /* 7.5:1 — survol des liens */
  --n-accent-soft: rgba(95, 85, 201, 0.10);
  --n-accent-veil: rgba(95, 85, 201, 0.05);

  /* --- LA rampe : celle du « 3 minutes » de la baseline ----------------
     Elle se juge sur son arret le plus pale : #B95FD9 tient 3,53:1 sur
     #FAFAFA (mesure du 03/09/2026), ce qui suffit au GRAND texte au sens
     WCAG, soit 24 px et plus en graisse 500 (Poppins 500 n'est pas « gras »).
     Donc .n-display et .n-h1 seulement, jamais un libelle ni un petit texte.
     Elle n'est donc jamais posee sous du petit texte ni sous un libelle. */
  --n-grad:      linear-gradient(90deg, #3940A5 0%, #665DC7 22%, #987DEC 42%, #B287F9 52%,
                                 #BD71E5 68%, #C463D9 84%, #CE51C9 100%);
  --n-grad-soft: linear-gradient(90deg, #E2E3F5 0%, #E8E1F7 50%, #F5DDF6 100%);
  --n-grad-ring: conic-gradient(from 0deg, #3940A5, #987DEC, #CE51C9, #3940A5);

  /* --- LA RAMPE PROFONDE : le degrade la ou il y avait du noir ----------
     Meme famille, meme sens de lecture, mais assez sombre pour porter du
     texte blanc. Son arret le PLUS CLAIR, #8E2676, tient 7,8:1 avec du
     blanc — la rampe claire n'en tiendrait que 3,7 et rendrait illisible
     le paragraphe de l'appel final. C'est elle sur les surfaces (appel
     final, bouton principal, pastilles, etiquettes) ; la rampe claire
     reste sur ce qui ne porte pas de texte (les formes d'ecran, la barre
     d'attente, le mot du titre). Joseph, 03/09/2026 : « le degrade
     habituel a la place du noir, mais parfois utilise le noir oui ». */
  --n-grad-deep: linear-gradient(135deg, #241043 0%, #3E1668 32%, #5E2090 58%,
                                 #7B2A8D 80%, #8E2676 100%);
  --n-grad-deep-hi: linear-gradient(135deg, #2E1454 0%, #4B1B7E 32%, #7128AB 58%,
                                    #9433A9 80%, #A82D8A 100%);
  --n-mark-blue: #4876CC;   /* le point du monogramme, la seule tache bleue */

  /* --- Etats ------------------------------------------------------------ */
  --n-ok:       #167A4B;  --n-ok-soft:     rgba(22, 122, 75, 0.10);
  --n-warn:     #8A5A00;  --n-warn-soft:   rgba(138, 90, 0, 0.10);
  --n-danger:   #C0203C;  --n-danger-soft: rgba(192, 32, 60, 0.09);
  --n-info:     #2F5FC4;  --n-info-soft:   rgba(47, 95, 196, 0.09);

  /* --- Filets ------------------------------------------------------------ */
  --n-line:        rgba(0, 0, 0, 0.08);
  --n-line-2:      rgba(0, 0, 0, 0.14);
  --n-line-strong: #767680;   /* 3.2:1 — le bord d'un controle */
  --n-line-inverse:rgba(255, 255, 255, 0.16);

  /* --- Focus : jamais discret ------------------------------------------- */
  --n-focus: #5F55C9; --n-focus-w: 2px; --n-focus-offset: 2px;

  /* --- Rayons : ceux du logo -------------------------------------------- */
  --n-r-sm: 8px; --n-r-md: 12px; --n-r-lg: 16px; --n-r-xl: 24px; --n-r-2xl: 32px;
  --n-r-pill: 999px;

  /* --- Ombres : grises, basses, trois niveaux, jamais une quatrieme ------ */
  --n-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.05);
  --n-shadow-2: 0 8px 24px -12px rgba(0, 0, 0, 0.18);
  --n-shadow-3: 0 24px 64px -28px rgba(0, 0, 0, 0.30);

  /* --- Durees & courbes -------------------------------------------------- */
  --n-dur-1: 120ms; --n-dur-2: 180ms; --n-dur-3: 280ms; --n-dur-4: 480ms; --n-dur-5: 900ms;
  --n-ease:     cubic-bezier(0.2, 0.7, 0.2, 1);
  --n-ease-in:  cubic-bezier(0.5, 0, 0.75, 0);
  --n-ease-io:  cubic-bezier(0.65, 0, 0.35, 1);

  /* --- Espacement : une echelle, multiple de 4 ------------------------- */
  --n-sp-1: 4px;  --n-sp-2: 8px;  --n-sp-3: 12px; --n-sp-4: 16px; --n-sp-5: 24px;
  --n-sp-6: 32px; --n-sp-7: 48px; --n-sp-8: 64px; --n-sp-9: 96px; --n-sp-10: 128px;
  --n-sp-section: clamp(4rem, 2.5rem + 6vw, 8rem);      /* 64 → 128 */
  --n-sp-block:   clamp(2.5rem, 1.6rem + 3.6vw, 4rem);   /* 40 → 64 : entre un titre et son contenu */
  --n-sp-gutter:  clamp(1.25rem, 0.5rem + 3.6vw, 2.5rem);/* 20 → 40 */

  /* --- Familles & graisses ---------------------------------------------- */
  --n-font: "Poppins", "Poppins secours", ui-sans-serif, system-ui, -apple-system,
            "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --n-fw-normal: 400; --n-fw-medium: 500; --n-fw-semi: 600;

  /* --- L'echelle : cinq titres, un corps, deux petites ------------------- */
  --n-fs-display: clamp(2.5rem, 1.55rem + 4.2vw, 4.75rem);   /* 40 → 76 */
  --n-fs-h1:      clamp(2rem, 1.35rem + 2.9vw, 3.5rem);      /* 32 → 56 */
  --n-fs-h2:      clamp(1.625rem, 1.2rem + 1.9vw, 2.625rem); /* 26 → 42 */
  --n-fs-h3:      clamp(1.25rem, 1.1rem + 0.65vw, 1.5rem);   /* 20 → 24 */
  --n-fs-h4:      1.0625rem;                                 /* 17 */
  --n-fs-lead:    clamp(1.0625rem, 1rem + 0.45vw, 1.25rem);  /* 17 → 20 */
  --n-fs-body:    1rem;                                      /* 16, jamais moins sur telephone */
  --n-fs-sm:      0.875rem;                                  /* 14 : libelles, boutons compacts */
  --n-fs-xs:      0.8125rem;                                 /* 13 : legendes */
  --n-fs-eyebrow: 0.75rem;                                   /* 12 : surtitres en capitales */
  --n-fs-kpi:     clamp(2rem, 1.5rem + 2.2vw, 3rem);         /* 32 → 48 : un chiffre qui compte */

  --n-lh-tight: 1.05; --n-lh-snug: 1.15; --n-lh-title: 1.25; --n-lh-body: 1.6;
  --n-ls-display: -0.03em; --n-ls-title: -0.02em; --n-ls-h3: -0.01em; --n-ls-caps: 0.08em;

  /* --- Grille & plans ---------------------------------------------------- */
  --n-container: 1200px; --n-container-narrow: 760px; --n-measure: 62ch;
  --n-header-h: 64px;
  --n-z-raised: 10; --n-z-sticky: 20; --n-z-nav: 50; --n-z-overlay: 60; --n-z-modal: 70; --n-z-toast: 80;

  /* --- Controles --------------------------------------------------------- */
  --n-control-h: 48px; --n-control-h-sm: 40px; --n-control-h-lg: 56px;
}

/* == 02 bis · ALIAS DE COMPATIBILITE ========================================
   Les anciens noms de jetons (charte du 30/08) pointent sur les nouveaux :
   un consommateur oublie (une regle de checkout.css, un setProperty dans un
   script) ne casse pas en silence. A retirer quand plus rien ne les lit :
   grep -rho -- '--n-[a-z0-9-]*' app/public gestion | sort -u
   ========================================================================= */
:root {
  --n-bg-soft: var(--n-bg); --n-bg-deep: var(--n-well); --n-bg-overlay: var(--n-veil);
  --n-surface-1: var(--n-surface); --n-surface-2-old: var(--n-surface-2); --n-surface-3: var(--n-surface-3);
  --n-surface-4: var(--n-surface-3);
  --n-text-strong: var(--n-ink); --n-text-muted: var(--n-text-2); --n-text-faint: var(--n-text-3);
  --n-text-dim: var(--n-text-3); --n-text-on-accent: #FFFFFF; --n-on-accent: #FFFFFF;
  --n-accent-lo: var(--n-accent); --n-accent-up: #B9AEFF;
  --n-grad-ink: var(--n-grad); --n-grad-ink-hi: var(--n-grad); --n-grad-text: var(--n-grad);
  --n-line-3: var(--n-line-2); --n-line-accent: var(--n-accent); --n-line-bevel: transparent; --n-line-grid: var(--n-line);
  --n-r-xs: var(--n-r-sm); --n-r-3xl: var(--n-r-2xl); --n-radius-sm: var(--n-r-sm);
  --n-shadow-4: var(--n-shadow-3); --n-bevel: none; --n-glow: none;
  --n-dur-6: var(--n-dur-5); --n-ease-out: var(--n-ease); --n-ease-inout: var(--n-ease-io); --n-ease-emph: var(--n-ease);
  --n-sp-0: 0; --n-sp-11: var(--n-sp-7); --n-sp-12: var(--n-sp-8); --n-sp-13: var(--n-sp-9); --n-sp-14: var(--n-sp-9);
  --n-sp-15: var(--n-sp-10); --n-sp-16: var(--n-sp-10); --n-sp-stack: var(--n-sp-block);
  --n-font-sans: var(--n-font); --n-font-mono: var(--n-font); --n-fw-light: 400; --n-fw-regular: 400; --n-fw-bold: 600;
  --n-fs-3xs: var(--n-fs-eyebrow); --n-fs-2xs: var(--n-fs-eyebrow); --n-fs-xs-old: var(--n-fs-xs);
  --n-fs-md: var(--n-fs-body); --n-fs-lg: var(--n-fs-h4); --n-fs-xl: var(--n-fs-lead); --n-fs-2xl: var(--n-fs-h3);
  --n-fs-3xl: var(--n-fs-h3); --n-fs-4xl: var(--n-fs-h2); --n-fs-5xl: var(--n-fs-h2); --n-fs-6xl: var(--n-fs-h1); --n-fs-7xl: var(--n-fs-display);
  --n-lh-normal: 1.4; --n-lh-loose: 1.7; --n-ls-normal: 0; --n-ls-wide: 0.06em; --n-ls-caps-lg: var(--n-ls-caps);
  --n-container-wide: var(--n-container); --n-container-text: var(--n-measure); --n-grid-gap: var(--n-sp-5);
  --n-z-base: 0;
}

/* == 03 · RESET & BASE ===================================================== */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth;
       scroll-padding-top: calc(var(--n-header-h) + 16px); }
html, body { max-inline-size: 100%; overflow-x: clip; }
body {
  margin: 0; min-height: 100%;
  background: var(--n-bg); color: var(--n-text);
  font-family: var(--n-font); font-size: var(--n-fs-body); line-height: var(--n-lh-body);
  font-weight: var(--n-fw-normal);
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}
h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, fieldset { margin: 0; }
fieldset { padding: 0; border: 0; min-inline-size: 0; }
legend { padding: 0; }
ul, ol { margin: 0; padding: 0; }
img, video, svg, canvas { display: block; max-inline-size: 100%; }
img, video { block-size: auto; }
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; letter-spacing: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; }
table { border-collapse: collapse; inline-size: 100%; }
hr { border: 0; border-block-start: 1px solid var(--n-line); margin: 0; }
::selection { background: #E3DEF7; color: #000; }
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: var(--n-focus-w) solid var(--n-focus);
  outline-offset: var(--n-focus-offset);
  border-radius: var(--n-r-sm);
}
/* `hidden` doit gagner contre toute classe qui pose un display : la feuille
   du navigateur n'a aucune specificite, celle-ci si. Paye trois fois. */
[hidden] { display: none !important; }

.n-skip {
  position: absolute; inset-block-start: -100%; inset-inline-start: var(--n-sp-4);
  z-index: var(--n-z-toast); padding: var(--n-sp-3) var(--n-sp-5);
  background: var(--n-ink); color: #fff; border-radius: var(--n-r-pill);
  font-size: var(--n-fs-sm); font-weight: var(--n-fw-medium);
}
.n-skip:focus { inset-block-start: var(--n-sp-4); }
.n-sr-only {
  position: absolute; inline-size: 1px; block-size: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
.n-symbols { position: absolute; inline-size: 0; block-size: 0; overflow: hidden; }

/* == 04 · TYPOGRAPHIE ====================================================== */
.n-display, .n-h1, .n-h2, .n-h3, .n-h4 {
  color: var(--n-ink); font-weight: var(--n-fw-medium);
  text-wrap: balance; font-synthesis-weight: none;
}
.n-display { font-size: var(--n-fs-display); line-height: var(--n-lh-tight); letter-spacing: var(--n-ls-display); }
.n-h1 { font-size: var(--n-fs-h1); line-height: var(--n-lh-snug);  letter-spacing: var(--n-ls-display); }
.n-h2 { font-size: var(--n-fs-h2); line-height: var(--n-lh-snug);  letter-spacing: var(--n-ls-title); }
.n-h3 { font-size: var(--n-fs-h3); line-height: var(--n-lh-title); letter-spacing: var(--n-ls-h3); }
.n-h4 { font-size: var(--n-fs-h4); line-height: 1.35; font-weight: var(--n-fw-semi); }

.n-lead { font-size: var(--n-fs-lead); line-height: 1.55; color: var(--n-text-2);
          max-inline-size: 52ch; text-wrap: pretty; }
.n-body { font-size: var(--n-fs-body); color: var(--n-text-2); max-inline-size: var(--n-measure); text-wrap: pretty; }
.n-small{ font-size: var(--n-fs-sm); line-height: 1.55; color: var(--n-text-2); }
.n-caption { font-size: var(--n-fs-xs); line-height: 1.5; color: var(--n-text-3); }
.n-eyebrow {
  display: block; font-size: var(--n-fs-eyebrow); font-weight: var(--n-fw-medium);
  letter-spacing: var(--n-ls-caps); text-transform: uppercase; color: var(--n-text-3);
}
.n-kpi { font-size: var(--n-fs-kpi); font-weight: var(--n-fw-medium); line-height: 1;
         letter-spacing: var(--n-ls-title); color: var(--n-ink); font-variant-numeric: tabular-nums; }
.n-num { font-variant-numeric: tabular-nums; }
.n-strong { color: var(--n-ink); font-weight: var(--n-fw-medium); }

/* Deux paragraphes qui se suivent respirent. La marge est sur la SUITE. */
.n-body + .n-body, .n-small + .n-small, .n-lead + .n-body, .n-body + .n-small { margin-block-start: var(--n-sp-4); }

/* Le lien courant : soulignement fin, l'accent au survol seulement. */
.n-link { color: var(--n-ink); text-decoration: underline; text-decoration-thickness: 1px;
          text-underline-offset: 0.18em; text-decoration-color: var(--n-line-2);
          transition: text-decoration-color var(--n-dur-2) var(--n-ease), color var(--n-dur-2) var(--n-ease); }
.n-link:hover { color: var(--n-accent-hi); text-decoration-color: currentColor; }

/* LE mot en degrade : la signature de la baseline, une fois par page. */
.n-grad-text {
  color: var(--n-accent);
  background: var(--n-grad); -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  /* Poppins deborde legerement a gauche des lettres rondes : un peu de
     rembourrage pour que le clip ne rogne pas le trait. */
  padding-inline: 0.02em; margin-inline: -0.02em;
}

/* == 05 · MISE EN PAGE & UTILITAIRES ====================================== */
.n-container { inline-size: min(100% - 2 * var(--n-sp-gutter), var(--n-container)); margin-inline: auto; }
.n-container--narrow { max-inline-size: var(--n-container-narrow); }
.n-section { padding-block: var(--n-sp-section); }
.n-section--tight { padding-block: var(--n-sp-block); }
.n-section--flush-top { padding-block-start: 0; }
.n-section__head { margin-block-end: var(--n-sp-block); max-inline-size: 60ch; }
.n-section__head > * + * { margin-block-start: var(--n-sp-4); }
.n-section__head--center { margin-inline: auto; text-align: center; }
.n-section__head--center .n-lead { margin-inline: auto; }
.n-rule { border: 0; border-block-start: 1px solid var(--n-line); }

.n-stack > * + * { margin-block-start: var(--n-sp-4); }
.n-stack--2 > * + * { margin-block-start: var(--n-sp-2); }
.n-stack--3 > * + * { margin-block-start: var(--n-sp-3); }
.n-stack--5 > * + * { margin-block-start: var(--n-sp-5); }
.n-stack--6 > * + * { margin-block-start: var(--n-sp-6); }
.n-cluster { display: flex; flex-wrap: wrap; gap: var(--n-sp-3); align-items: center; }
.n-cluster--between { justify-content: space-between; }
.n-cluster--center { justify-content: center; }
.n-grid { display: grid; gap: var(--n-sp-5); }
.n-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 720px) {
  .n-grid--2\@md { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .n-grid--3\@md { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .n-grid--4\@lg { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .n-grid--3\@lg { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.n-center { text-align: center; }
.n-measure { max-inline-size: var(--n-measure); }
.n-mx-auto { margin-inline: auto; }
.n-mt-2 { margin-block-start: var(--n-sp-2); } .n-mt-3 { margin-block-start: var(--n-sp-3); }
.n-mt-4 { margin-block-start: var(--n-sp-4); } .n-mt-5 { margin-block-start: var(--n-sp-5); }
.n-mt-6 { margin-block-start: var(--n-sp-6); } .n-mt-7 { margin-block-start: var(--n-sp-7); }
.n-mt-8 { margin-block-start: var(--n-sp-8); } .n-mt-auto { margin-block-start: auto; }
.n-mb-4 { margin-block-end: var(--n-sp-4); } .n-mb-6 { margin-block-end: var(--n-sp-6); }
.n-plain-list { list-style: none; }
.n-scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: thin; }
.n-only\@md { display: none; }
@media (min-width: 720px) { .n-only\@md { display: revert; } .n-hide\@md { display: none; } }
.n-todo { outline: 2px dashed var(--n-warn); outline-offset: 2px; }

/* == 06 · BOUTONS ==========================================================
   Une pilule (le rayon du logo), 48 px de haut au doigt, 44 sur bureau.
   Cinq etats sur chacun : repos, survol, focus, presse, desactive — plus
   `chargement`. Le survol change la COULEUR, jamais la taille : un bouton
   qui grossit decale ses voisins.
   ========================================================================== */
.n-btn {
  --btn-bg: var(--n-surface); --btn-fg: var(--n-ink); --btn-line: var(--n-line-2);
  --btn-bg-hover: var(--n-surface-2); --btn-bg-active: var(--n-surface-3);
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  gap: var(--n-sp-2); min-block-size: var(--n-control-h); padding-block: 0; padding-inline: var(--n-sp-5);
  border-radius: var(--n-r-pill); border: 1px solid var(--btn-line);
  background: var(--btn-bg); color: var(--btn-fg);
  font-size: var(--n-fs-body); font-weight: var(--n-fw-medium); line-height: 1.2; letter-spacing: 0;
  text-align: center; white-space: nowrap; cursor: pointer; user-select: none;
  -webkit-tap-highlight-color: transparent; text-decoration: none;
  transition: background-color var(--n-dur-2) var(--n-ease), border-color var(--n-dur-2) var(--n-ease),
              color var(--n-dur-2) var(--n-ease), box-shadow var(--n-dur-2) var(--n-ease);
}
.n-btn svg { inline-size: 18px; block-size: 18px; flex: none; }
@media (hover: hover) { .n-btn:hover { background: var(--btn-bg-hover); } }
.n-btn:active { background: var(--btn-bg-active); }
.n-btn:focus-visible { outline-offset: 3px; border-radius: var(--n-r-pill); }

/* Le bouton principal porte la rampe profonde. Le survol ne peut pas
   « transitionner » un degrade (aucun navigateur ne le fait) : il passe
   donc par une seconde rampe, un cran plus claire, et la transition se
   joue sur l'ombre. La taille ne bouge jamais. */
.n-btn--primary {
  --btn-bg: var(--n-grad-deep); --btn-fg: #fff; --btn-line: transparent;
  --btn-bg-hover: var(--n-grad-deep-hi); --btn-bg-active: var(--n-grad-deep);
  box-shadow: 0 6px 20px -10px rgba(94, 32, 144, 0.55);
}
@media (hover: hover) { .n-btn--primary:hover { border-color: transparent; box-shadow: 0 10px 26px -10px rgba(94, 32, 144, 0.60); } }
.n-btn--primary:active { box-shadow: none; }
.n-btn--primary[disabled], .n-btn--primary[aria-disabled="true"], .n-btn--primary:disabled { box-shadow: none; }
.n-btn--secondary { /* le defaut : blanc, filet */ }
.n-btn--tertiary, .n-btn--ghost {
  --btn-bg: transparent; --btn-line: transparent;
  --btn-bg-hover: var(--n-surface-2); --btn-bg-active: var(--n-surface-3);
}
.n-btn--tertiary { padding-inline: var(--n-sp-3); text-decoration: underline;
  text-underline-offset: 0.2em; text-decoration-color: var(--n-line-2); }
@media (hover: hover) { .n-btn--tertiary:hover { text-decoration-color: currentColor; background: transparent; } }
.n-btn--danger {
  --btn-bg: var(--n-surface); --btn-fg: var(--n-danger); --btn-line: rgba(192, 32, 60, 0.35);
  --btn-bg-hover: var(--n-danger-soft); --btn-bg-active: rgba(192, 32, 60, 0.16);
}
.n-btn--danger-solid {
  --btn-bg: var(--n-danger); --btn-fg: #fff; --btn-line: var(--n-danger);
  --btn-bg-hover: #A81A34; --btn-bg-active: #93172E;
}
.n-btn--inverse {
  --btn-bg: #fff; --btn-fg: var(--n-ink); --btn-line: #fff;
  --btn-bg-hover: #EDEDEF; --btn-bg-active: #E0E0E3;
}
.n-btn--sm { min-block-size: 44px; padding-inline: var(--n-sp-4); font-size: var(--n-fs-sm); }
.n-btn--lg { min-block-size: var(--n-control-h-lg); padding-inline: var(--n-sp-6); font-size: 1.0625rem; }
.n-btn--block { display: flex; inline-size: 100%; }
.n-btn--icon { padding-inline: 0; inline-size: var(--n-control-h); }
.n-btn--icon.n-btn--sm { inline-size: var(--n-control-h-sm); }
@media (min-width: 720px) and (hover: hover) and (pointer: fine) {
  .n-btn { min-block-size: 44px; }
  .n-btn--sm { min-block-size: 40px; }
  .n-btn--lg { min-block-size: 54px; }
  .n-btn--icon { inline-size: 44px; }
}
.n-btn[disabled], .n-btn[aria-disabled="true"], .n-btn:disabled {
  --btn-bg: var(--n-surface-3); --btn-fg: var(--n-text-disabled); --btn-line: var(--n-surface-3);
  cursor: not-allowed; pointer-events: none;
}
/* Chargement : le libelle s'efface, l'anneau prend sa place, la largeur
   ne bouge pas. `aria-busy="true"` sur le bouton fait tout. */
.n-btn[aria-busy="true"], .n-btn.is-loading { color: transparent !important; pointer-events: none; }
.n-btn[aria-busy="true"]::after, .n-btn.is-loading::after {
  content: ""; position: absolute; inset-block-start: 50%; inset-inline-start: 50%;
  inline-size: 18px; block-size: 18px; margin: -9px 0 0 -9px;
  border-radius: 50%; border: 2px solid rgba(127, 127, 127, 0.35); border-block-start-color: var(--btn-fg);
  animation: n-spin 0.8s linear infinite;
}
.n-btn--primary[aria-busy="true"]::after, .n-btn--primary.is-loading::after {
  border-color: rgba(255, 255, 255, 0.3); border-block-start-color: #fff;
}
.n-btn__hint { font-weight: var(--n-fw-normal); opacity: 0.7; }

/* == 07 · CHAMPS ===========================================================
   Un libelle VISIBLE au-dessus, 48 px de haut, un seul rayon, l'erreur
   sous le champ et elle dit quoi faire.
   ========================================================================== */
.n-field { display: grid; gap: var(--n-sp-2); }
.n-field__label, .n-label { font-size: var(--n-fs-sm); font-weight: var(--n-fw-medium); color: var(--n-ink); }
.n-field__label .n-optional, .n-optional { font-weight: var(--n-fw-normal); color: var(--n-text-3); }
.n-field__help { font-size: var(--n-fs-xs); color: var(--n-text-3); line-height: 1.5; }
.n-input, .n-select, .n-textarea {
  inline-size: 100%; min-block-size: var(--n-control-h); padding: var(--n-sp-3) var(--n-sp-4);
  background: var(--n-surface); color: var(--n-text);
  border: 1px solid var(--n-line-2); border-radius: var(--n-r-md);
  font-size: max(16px, var(--n-fs-body)); /* 16 px : iOS ne zoome pas */
  line-height: 1.4; -webkit-appearance: none; appearance: none;
  transition: border-color var(--n-dur-2) var(--n-ease), box-shadow var(--n-dur-2) var(--n-ease);
}
.n-input::placeholder, .n-textarea::placeholder { color: var(--n-text-3); opacity: 1; }
.n-input:hover, .n-select:hover, .n-textarea:hover { border-color: var(--n-line-strong); }
.n-input:focus, .n-select:focus, .n-textarea:focus, .n-input:focus-visible, .n-select:focus-visible, .n-textarea:focus-visible {
  outline: none; border-color: var(--n-ink); box-shadow: 0 0 0 3px var(--n-accent-soft);
}
.n-textarea { min-block-size: 128px; resize: vertical; }
.n-input[aria-invalid="true"], .n-select[aria-invalid="true"], .n-textarea[aria-invalid="true"] {
  border-color: var(--n-danger); box-shadow: 0 0 0 3px var(--n-danger-soft);
}
.n-input:disabled, .n-select:disabled, .n-textarea:disabled {
  background: var(--n-surface-2); color: var(--n-text-disabled); border-color: var(--n-line); cursor: not-allowed;
}
.n-input--code { font-size: 1.75rem; letter-spacing: 0.35em; text-align: center; font-variant-numeric: tabular-nums;
                 padding-inline-start: calc(var(--n-sp-4) + 0.35em); }
/* La fleche du select : un SVG frere, jamais une image data: (CSP). */
.n-select-wrap { position: relative; }
.n-select-wrap .n-select { padding-inline-end: calc(var(--n-sp-4) + 24px); }
.n-select-wrap__arrow { position: absolute; inset-inline-end: var(--n-sp-4); inset-block-start: 50%;
  inline-size: 16px; block-size: 16px; margin-block-start: -8px; pointer-events: none; color: var(--n-text-3); }
.n-field__error { font-size: var(--n-fs-xs); color: var(--n-danger); line-height: 1.5; }
.n-field__error[hidden] { display: none !important; }
.n-check { display: flex; gap: var(--n-sp-3); align-items: flex-start; cursor: pointer; font-size: var(--n-fs-sm); color: var(--n-text-2); }
.n-check input { inline-size: 20px; block-size: 20px; margin: 2px 0 0; accent-color: var(--n-ink); flex: none; }
.n-honeypot { position: absolute !important; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; opacity: 0; pointer-events: none; }
.n-form { display: grid; gap: var(--n-sp-5); }
.n-form__row { display: grid; gap: var(--n-sp-5); }
@media (min-width: 640px) { .n-form__row--2 { grid-template-columns: 1fr 1fr; } }
.n-form__foot { display: flex; flex-wrap: wrap; gap: var(--n-sp-4); align-items: center; }

/* == 08 · CARTES, NOTES, PASTILLES ========================================= */
.n-card { background: var(--n-surface); border: 1px solid var(--n-line); border-radius: var(--n-r-xl); padding: var(--n-sp-5); }
.n-card--lg { padding: var(--n-sp-6); }
@media (min-width: 720px) { .n-card--lg { padding: var(--n-sp-7); } }
.n-card--flat { background: var(--n-surface-2); border-color: transparent; }
.n-card--ink { background: var(--n-grad-deep); color: #fff; border-color: transparent; }
.n-card--ink .n-h2, .n-card--ink .n-h3, .n-card--ink .n-h4, .n-card--ink .n-kpi { color: #fff; }
.n-card--ink .n-lead, .n-card--ink .n-body, .n-card--ink .n-small, .n-card--ink .n-caption { color: var(--n-text-inverse-2); }
.n-card--interactive { cursor: pointer; transition: border-color var(--n-dur-2) var(--n-ease), box-shadow var(--n-dur-2) var(--n-ease), transform var(--n-dur-2) var(--n-ease); }
@media (hover: hover) { .n-card--interactive:hover { border-color: var(--n-line-2); box-shadow: var(--n-shadow-2); } }

.n-note {
  display: grid; grid-template-columns: auto 1fr; gap: var(--n-sp-3); align-items: start;
  padding: var(--n-sp-4) var(--n-sp-5); border-radius: var(--n-r-lg);
  background: var(--n-surface-2); color: var(--n-text); font-size: var(--n-fs-sm); line-height: 1.5;
}
.n-note > svg { inline-size: 18px; block-size: 18px; margin-block-start: 1px; }
.n-note p + p { margin-block-start: var(--n-sp-1); }
.n-note--ok     { background: var(--n-ok-soft);     color: var(--n-ok); }
.n-note--warn   { background: var(--n-warn-soft);   color: var(--n-warn); }
.n-note--danger { background: var(--n-danger-soft); color: var(--n-danger); }
.n-note--info   { background: var(--n-info-soft);   color: var(--n-info); }
.n-note--ok p, .n-note--warn p, .n-note--danger p, .n-note--info p { color: inherit; }

.n-badge {
  display: inline-flex; align-items: center; gap: var(--n-sp-1); min-block-size: 24px; padding: 0 var(--n-sp-3);
  border-radius: var(--n-r-pill); background: var(--n-surface-2); color: var(--n-text-2);
  font-size: var(--n-fs-xs); font-weight: var(--n-fw-medium); line-height: 1; white-space: nowrap;
}
.n-badge--ink    { background: var(--n-grad-deep); color: #fff; }
.n-badge--ok     { background: var(--n-ok-soft); color: var(--n-ok); }
.n-badge--warn   { background: var(--n-warn-soft); color: var(--n-warn); }
.n-badge--danger { background: var(--n-danger-soft); color: var(--n-danger); }
.n-badge--info   { background: var(--n-info-soft); color: var(--n-info); }
.n-badge--outline{ background: transparent; border: 1px solid var(--n-line-2); }

/* Une pastille de credits : le chiffre en tete, toujours au meme endroit. */
.n-credits {
  display: inline-flex; align-items: center; gap: var(--n-sp-2); min-block-size: 36px; padding: 0 var(--n-sp-4) 0 var(--n-sp-3);
  border-radius: var(--n-r-pill); background: var(--n-surface); border: 1px solid var(--n-line-2);
  font-size: var(--n-fs-sm); color: var(--n-text-2); white-space: nowrap;
}
.n-credits b { color: var(--n-ink); font-weight: var(--n-fw-semi); font-variant-numeric: tabular-nums; }
.n-credits svg { inline-size: 16px; block-size: 16px; color: var(--n-accent); }

/* Le segment : deux ou trois choix exclusifs, un seul actif, toujours visibles. */
.n-seg { display: inline-flex; padding: 4px; gap: 2px; border-radius: var(--n-r-pill); background: var(--n-surface-2); }
.n-seg__btn, .n-seg > button, .n-seg > a {
  min-block-size: 44px; padding: 0 var(--n-sp-4); border-radius: var(--n-r-pill);
  font-size: var(--n-fs-sm); font-weight: var(--n-fw-medium); color: var(--n-text-2); white-space: nowrap;
  transition: background-color var(--n-dur-2) var(--n-ease), color var(--n-dur-2) var(--n-ease), box-shadow var(--n-dur-2) var(--n-ease);
}
.n-seg__btn[aria-pressed="true"], .n-seg__btn[aria-selected="true"], .n-seg__btn.is-active,
.n-seg > [aria-pressed="true"], .n-seg > [aria-selected="true"], .n-seg > .is-active {
  background: var(--n-surface); color: var(--n-ink); box-shadow: var(--n-shadow-1), 0 0 0 1px var(--n-line);
}
.n-seg--block { display: flex; } .n-seg--block > * { flex: 1; }

/* L'option : une carte qu'on choisit (produit, style, format, fond, qualite).
   Coche = filet noir 2 px + fond blanc, jamais la couleur seule. */
.n-option {
  position: relative; display: block; inline-size: 100%; text-align: start;
  padding: var(--n-sp-4); border-radius: var(--n-r-lg); background: var(--n-surface);
  border: 1px solid var(--n-line-2); box-shadow: 0 0 0 0 transparent; cursor: pointer;
  transition: border-color var(--n-dur-2) var(--n-ease), box-shadow var(--n-dur-2) var(--n-ease), background-color var(--n-dur-2) var(--n-ease);
}
@media (hover: hover) { .n-option:hover { border-color: var(--n-line-strong); } }
.n-option[aria-checked="true"], .n-option[aria-pressed="true"], .n-option.is-selected, .n-option:has(input:checked) {
  border-color: var(--n-ink); box-shadow: inset 0 0 0 1px var(--n-ink);
}
.n-option[aria-disabled="true"], .n-option:has(input:disabled), .n-option.is-disabled {
  cursor: not-allowed; color: var(--n-text-disabled); background: var(--n-surface-2); border-color: var(--n-line);
}
.n-option__name { display: block; font-size: var(--n-fs-body); font-weight: var(--n-fw-medium); color: var(--n-ink); line-height: 1.3; }
.n-option__desc { display: block; margin-block-start: 2px; font-size: var(--n-fs-xs); color: var(--n-text-3); line-height: 1.45; }
.n-option__meta { display: block; margin-block-start: var(--n-sp-2); font-size: var(--n-fs-xs); color: var(--n-text-2); font-variant-numeric: tabular-nums; }
.n-option[aria-disabled="true"] .n-option__name, .n-option.is-disabled .n-option__name { color: var(--n-text-disabled); }
.n-option input[type="radio"], .n-option input[type="checkbox"] {
  position: absolute; inline-size: 1px; block-size: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.n-option__check {
  position: absolute; inset-block-start: var(--n-sp-3); inset-inline-end: var(--n-sp-3);
  inline-size: 22px; block-size: 22px; border-radius: 50%; display: grid; place-items: center;
  background: var(--n-ink); color: #fff; opacity: 0; transform: scale(0.6);
  transition: opacity var(--n-dur-2) var(--n-ease), transform var(--n-dur-2) var(--n-ease);
}
.n-option__check svg { inline-size: 12px; block-size: 12px; }
.n-option[aria-checked="true"] .n-option__check, .n-option.is-selected .n-option__check, .n-option:has(input:checked) .n-option__check { opacity: 1; transform: none; }

/* == 16 · ATTENTE, SQUELETTES, PROGRESSION, VIDE, MODALE, MENU, TABLEAU ==== */
@keyframes n-spin { to { transform: rotate(360deg); } }
@keyframes n-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
@keyframes n-slide { from { transform: translateX(-100%); } to { transform: translateX(300%); } }
@keyframes n-rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes n-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes n-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }

.n-spinner { inline-size: 20px; block-size: 20px; border-radius: 50%; border: 2px solid var(--n-line-2);
             border-block-start-color: var(--n-ink); animation: n-spin 0.8s linear infinite; }
.n-skeleton {
  border-radius: var(--n-r-md); background: var(--n-surface-2);
  background-image: linear-gradient(90deg, var(--n-surface-2) 0%, #FAFAFA 50%, var(--n-surface-2) 100%);
  background-size: 200% 100%; animation: n-shimmer 1.6s linear infinite; color: transparent;
}
.n-skeleton--text { block-size: 1em; inline-size: 60%; }
.n-skeleton--line { block-size: 12px; inline-size: 100%; }
.n-skeleton--block { min-block-size: 120px; }

/* La progression est INDETERMINEE : on ne connait pas l'avancement, on ne
   l'invente pas. Le rail montre qu'on travaille ; le texte dit l'etape. */
.n-progress { position: relative; block-size: 4px; border-radius: var(--n-r-pill); background: var(--n-surface-3); overflow: hidden; }
.n-progress::after { content: ""; position: absolute; inset-block: 0; inset-inline-start: 0; inline-size: 34%;
  border-radius: inherit; background: var(--n-grad); animation: n-slide 1.6s var(--n-ease-io) infinite; }
.n-progress[data-state="done"]::after { inline-size: 100%; animation: none; background: var(--n-ok); }
.n-progress[data-state="failed"]::after { inline-size: 100%; animation: none; background: var(--n-danger); }
/* L'anneau : la rampe en rotation autour de la photo pendant le rendu. */
.n-ring { position: relative; border-radius: var(--n-r-xl); padding: 3px; background: var(--n-grad-ring);
          animation: n-spin 3s linear infinite; }
.n-ring > * { border-radius: calc(var(--n-r-xl) - 3px); animation: n-spin 3s linear infinite reverse; }

.n-empty { display: grid; justify-items: center; text-align: center; gap: var(--n-sp-3);
           padding: var(--n-sp-8) var(--n-sp-5); border-radius: var(--n-r-xl); border: 1px dashed var(--n-line-2); }
.n-empty svg { inline-size: 32px; block-size: 32px; color: var(--n-text-3); }
.n-empty__title { font-size: var(--n-fs-h4); font-weight: var(--n-fw-medium); color: var(--n-ink); }
.n-empty__text { font-size: var(--n-fs-sm); color: var(--n-text-2); max-inline-size: 40ch; }

.n-modal { position: fixed; inset: 0; z-index: var(--n-z-modal); display: grid; place-items: end center; padding: 0;
           background: rgba(0, 0, 0, 0.45); animation: n-fade var(--n-dur-2) var(--n-ease); }
.n-modal__box { inline-size: 100%; max-inline-size: 520px; max-block-size: 88dvh; overflow: auto;
  background: var(--n-surface); border-radius: var(--n-r-xl) var(--n-r-xl) 0 0; padding: var(--n-sp-6) var(--n-sp-5);
  box-shadow: var(--n-shadow-3); animation: n-rise var(--n-dur-3) var(--n-ease); }
@media (min-width: 640px) { .n-modal { place-items: center; padding: var(--n-sp-5); } .n-modal__box { border-radius: var(--n-r-xl); padding: var(--n-sp-6); } }
.n-modal__title { font-size: var(--n-fs-h3); font-weight: var(--n-fw-medium); color: var(--n-ink); letter-spacing: var(--n-ls-h3); }
.n-modal__body { margin-block-start: var(--n-sp-3); color: var(--n-text-2); }
.n-modal__foot { margin-block-start: var(--n-sp-5); display: flex; flex-wrap: wrap; gap: var(--n-sp-3); justify-content: flex-end; }

.n-menu { position: absolute; z-index: var(--n-z-overlay); min-inline-size: 200px; padding: var(--n-sp-2);
          background: var(--n-surface); border: 1px solid var(--n-line); border-radius: var(--n-r-lg); box-shadow: var(--n-shadow-2); }
.n-menu__item { display: flex; align-items: center; gap: var(--n-sp-3); inline-size: 100%; min-block-size: 44px; padding: 0 var(--n-sp-3);
                border-radius: var(--n-r-sm); font-size: var(--n-fs-sm); color: var(--n-text); text-align: start; }
@media (hover: hover) { .n-menu__item:hover { background: var(--n-surface-2); } }
.n-menu__item svg { inline-size: 18px; block-size: 18px; color: var(--n-text-3); }
.n-menu__item--danger { color: var(--n-danger); }

.n-table-wrap { overflow-x: auto; border: 1px solid var(--n-line); border-radius: var(--n-r-lg); }
.n-table { min-inline-size: 480px; font-size: var(--n-fs-sm); }
.n-table th, .n-table td { padding: var(--n-sp-3) var(--n-sp-4); text-align: start; border-block-end: 1px solid var(--n-line); }
.n-table th { font-size: var(--n-fs-eyebrow); letter-spacing: var(--n-ls-caps); text-transform: uppercase; color: var(--n-text-3); font-weight: var(--n-fw-medium); }
.n-table tr:last-child td { border-block-end: 0; }
.n-table td.n-num, .n-table th.n-num { text-align: end; font-variant-numeric: tabular-nums; }

/* Les faits : une liste cle → valeur, sans tableau. */
.n-facts { display: grid; gap: 0; border-block-start: 1px solid var(--n-line); }
.n-facts__row { display: grid; grid-template-columns: minmax(120px, 1fr) 2fr; gap: var(--n-sp-4);
                padding-block: var(--n-sp-4); border-block-end: 1px solid var(--n-line); }
.n-facts__k { font-size: var(--n-fs-sm); color: var(--n-text-3); }
.n-facts__v { font-size: var(--n-fs-body); color: var(--n-ink); font-weight: var(--n-fw-medium); }

/* L'accordeon : une question par ligne, la reponse en dessous. */
.n-faq { border-block-start: 1px solid var(--n-line); }
.n-faq__item { border-block-end: 1px solid var(--n-line); }
.n-faq__q { display: flex; align-items: center; justify-content: space-between; gap: var(--n-sp-4);
            inline-size: 100%; min-block-size: 56px; padding-block: var(--n-sp-4); text-align: start;
            font-size: var(--n-fs-h4); font-weight: var(--n-fw-medium); color: var(--n-ink); cursor: pointer; }
.n-faq__q svg { inline-size: 20px; block-size: 20px; flex: none; color: var(--n-text-3); transition: transform var(--n-dur-3) var(--n-ease); }
.n-faq__q[aria-expanded="true"] svg { transform: rotate(45deg); }
.n-faq__a { padding-block: 0 var(--n-sp-5); color: var(--n-text-2); max-inline-size: 62ch; }
details.n-faq__item > summary { list-style: none; } details.n-faq__item > summary::-webkit-details-marker { display: none; }
details.n-faq__item[open] > summary svg { transform: rotate(45deg); }

/* == 17 · MOUVEMENT ========================================================
   Une seule animation d'apparition, courte, sur les blocs sous la ligne de
   flottaison — JAMAIS sur le h1 (c'est le LCP, mesure : 1,3 s au lieu de
   0,14). Elle est armee par `data-reveal` et posee par numeri.js.
   ========================================================================== */
.n-reveal { opacity: 0; transform: translateY(14px);
  transition: opacity var(--n-dur-4) var(--n-ease), transform var(--n-dur-4) var(--n-ease); }
.n-reveal.is-in { opacity: 1; transform: none; }
.n-reveal[data-delay="2"] { transition-delay: 70ms; } .n-reveal[data-delay="3"] { transition-delay: 140ms; }
.n-reveal[data-delay="4"] { transition-delay: 210ms; } .n-reveal[data-delay="5"] { transition-delay: 280ms; }
.n-reveal[data-delay="6"] { transition-delay: 350ms; }
html.no-js .n-reveal { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
                           transition-duration: 0.01ms !important; scroll-behavior: auto !important; }
  .n-reveal { opacity: 1; transform: none; transition: none; }
  .n-progress::after { inline-size: 100%; animation: none; opacity: 0.6; }
  .n-ring, .n-ring > * { animation: none; }
  .n-skeleton { animation: none; }
}

/* == 09 · LA BARRE UNIQUE — LE MENU, LE MEME PARTOUT =======================
   NUM_MENU_UNIQUE_03092026. Un seul menu, ecrit une fois, servi identique sur
   les six pages de la vitrine, sur help.html et dans /app. Cinq destinations,
   dans cet ordre, une icone A COTE du libelle :

     Create video · My videos · Plan & buy credit · Need help? · Account

   POURQUOI IL NE DEPEND PLUS DE LA SESSION
   La vitrine basculait « Log in » / « My studio » par data-session-*, et /app
   cachait TOUT son menu derriere data-signed-in. Joseph, 03/09/2026 : « le
   menu je veux continuer de le voir, meme quand je suis pas connecte,
   seulement quand tu clique, affiche le menu pour se connecter ». Un menu qui
   change selon qui regarde n'est plus un repere : on ne peut pas s'en
   souvenir, donc on ne s'en sert pas. C'est la DESTINATION qui demande la
   connexion — #library, #plans et #account ouvrent la carte d'entree.

   POURQUOI LE LOGO EST LE SEUL CHEMIN VERS L'ACCUEIL
   L'accueil n'est pas une chose a faire, c'est la page ou l'on arrive. Aucune
   entree « Home » : cinq entrees, cinq gestes.

   POURQUOI DEUX RENDUS, ET UN SEUL MENU
   Sous 1100 px les cinq entrees sont en bas de l'ecran (.n-tabbar) ; au-dessus
   elles sont dans la rangee du haut (.n-nav__links). Les deux ne coexistent
   JAMAIS : celle qui ne sert pas est en display:none, donc absente de l'arbre
   d'accessibilite. Un lecteur d'ecran ne lit jamais le menu deux fois.

   POURQUOI 1100 px ET PAS 900 — c'est une mesure, pas un gout
   Les cinq libelles en Poppins 500 / 14 px font 422 px de texte. Avec l'icone
   (18), l'ecart (8) et les marges (2 x 12) de chaque entree, plus les quatre
   ecarts de la rangee : 688 px. Il faut encore la marque (100 px), la sortie
   de /app (110 px) et deux ecarts : 940 px de contenu, soit 1020 px de fenetre
   gouttiere comprise. 1100 px laisse la marge d'une police de secours plus
   large. A 900 px la rangee deborderait de 25 px — et un debordement NE SE
   VOIT PAS ici : `html, body { overflow-x: clip }` (§ 03) le COUPE, sans barre
   de defilement et sans rien qui l'annonce. Les entrees de droite
   disparaitraient purement et simplement.

   ⚠️ LA SECTION 09 bis QUI SUIT (le monogramme couleur, NUM_RAMPE_03092026)
      APPARTIENT A UN AUTRE LOT. Elle pose le `gap` du lien de marque et la
      taille de l'image : ne pas la fondre ici, ne pas la doubler.
   ========================================================================== */

/* La hauteur de la barre du bas, ecrite UNE fois. app.css la relit. Deux
   « 64px » dans deux fichiers, c'est deux verites, et c'est la seconde qu'on
   oublie de monter. */
:root { --n-tabbar-h: calc(64px + env(safe-area-inset-bottom, 0px)); }

/* --- LA RANGEE DU HAUT --------------------------------------------------- */
.n-nav {
  position: fixed; inset-block-start: 0; inset-inline: 0; z-index: var(--n-z-nav);
  block-size: var(--n-header-h); background: var(--n-veil);
  -webkit-backdrop-filter: saturate(160%) blur(14px); backdrop-filter: saturate(160%) blur(14px);
  border-block-end: 1px solid var(--n-line);
  /* En paysage sur un appareil a encoche, la gouttiere du conteneur (38 px a
     844 px) est plus petite que l'encoche (47 px) : le logo passerait dessous. */
  padding-inline: env(safe-area-inset-left, 0px) env(safe-area-inset-right, 0px);
}
.n-nav__inner { display: flex; align-items: center; justify-content: space-between; gap: var(--n-sp-4); block-size: 100%; }
.n-nav__brand { display: inline-flex; align-items: center; min-block-size: 44px; color: var(--n-ink); flex: none; justify-self: start; }
/* .n-brand est GLOBALE : le pied de page et /app la relisent. */
.n-brand { block-size: 20px; inline-size: auto; }
.n-nav__side { display: flex; align-items: center; justify-content: flex-end; gap: var(--n-sp-2); justify-self: end; }

/* NUM_RAIL_LARGEUR_03092026 · LA PLACE EST RESERVEE, PAS RENDUE.
   Joseph, 03/09/2026 : « ca doit rester la meme largeur le menu, meme quand
   je change de page ».

   `hidden` vaut `display: none` : un solde pas encore connu et une sortie
   absente de la vitrine ne prenaient AUCUNE place, et le rail changeait de
   largeur — au chargement (le solde arrive apres `GET /me`, donc apres le
   premier rendu) comme d'une page a l'autre. `data-off` laisse la boite ou
   elle est et la rend seulement invisible.

   ⚠️ `visibility: hidden` ET NON `opacity: 0` : la premiere sort de l'arbre
      d'accessibilite ET de l'ordre de tabulation, la seconde laisse un
      bouton « Log out » atteignable au clavier pour qui n'a pas de session.
   ⚠️ CES DEUX ELEMENTS SONT LES SEULS DU PROJET A SE CACHER AINSI. Partout
      ailleurs, `hidden` reste la regle. Repasser l'un des deux sous `hidden`
      — ou sous la fonction `show()` d'app.js, qui pose `hidden` — ramene le
      defaut entier. */
.n-nav__side [data-off] { visibility: hidden; pointer-events: none; }

.n-nav__links { display: none; gap: var(--n-sp-1); justify-self: center; }
.n-nav__link {
  display: inline-flex; align-items: center; gap: var(--n-sp-2);
  min-block-size: 44px; padding: 0 var(--n-sp-3); border-radius: var(--n-r-pill);
  white-space: nowrap; font-size: var(--n-fs-sm); font-weight: var(--n-fw-medium); color: var(--n-text-2);
  transition: color var(--n-dur-2) var(--n-ease), background-color var(--n-dur-2) var(--n-ease);
}
/* L'ICONE EST A COTE DU LIBELLE, JAMAIS A LA PLACE : c'est `gap` qui le dit.
   Elle ne retrecit jamais (`flex: none`). */
.n-nav__link .n-ico { inline-size: 18px; block-size: 18px; flex: none; }
@media (hover: hover) { .n-nav__link:hover { color: var(--n-ink); background: var(--n-surface-2); } }
/* L'entree qui fabrique. Elle porte l'accent de la marque a la place du
   bouton « Create your video » qui vivait dans l'en-tete : deux commandes
   pour la meme chose etaient exactement le menu-qui-change qu'on retire. */
.n-nav__link--go { color: var(--n-accent); }
@media (hover: hover) { .n-nav__link--go:hover { color: var(--n-accent-hi, var(--n-accent)); } }
/* L'ETAT ACTIF SE LIT A LA FORME, PAS SEULEMENT A LA COULEUR (WCAG 1.4.1) :
   une pilule cerclee d'encre. Le cercle est un box-shadow, il ne prend AUCUNE
   place — le libelle ne bouge pas d'un pixel quand il devient courant, la ou
   une graisse plus lourde aurait fait respirer toute la rangee. */
.n-nav__link[aria-current="page"] {
  color: var(--n-ink); background: var(--n-surface);
  box-shadow: inset 0 0 0 1.5px var(--n-ink);
}

/* NUM_RAIL_DROITE_03092026 · LE SOLDE, COMPACT.
   Il tient dans la barre a cote de « Create » : le chiffre en tete, l'icone
   pour le reconnaitre, et le mot « credits » seulement pour un lecteur
   d'ecran. Ecrit en toutes lettres, il coutait 70 px de plus — et il monte le
   point de rupture pour tout le monde. */
.n-credits {
  /* La largeur plancher tient un solde a cinq chiffres separateur compris
     (« 12 500 ») : « 27 » et « 12 500 » occupent alors la MEME case, et le
     rail ne bouge pas quand un rendu debite. Au-dela, la pastille s'elargit
     et la grille `1fr auto 1fr` l'absorbe sans pousser les entrees —
     mesure a 1280 px avec un solde a six chiffres : 16 px de marge. */
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-inline-size: 92px;
  min-block-size: 36px; padding-inline: var(--n-sp-3);
  border-radius: var(--n-r-pill); background: var(--n-surface); border: 1px solid var(--n-line-2);
  font-size: var(--n-fs-sm); color: var(--n-text-2); white-space: nowrap;
}
.n-credits__ico { inline-size: 18px; block-size: 18px; flex: none; color: var(--n-accent); }

/* == LA PIECE DU NUMERICREDIT · NUM_NUMERICREDIT_03092026 ==================
   Elle se pose devant un montant en credits, ou dans un bouton d'achat. Elle
   herite de `currentColor` : sur l'encre, sur la rampe de marque et dans une
   pastille, elle prend la couleur du texte qu'elle accompagne — un seul
   dessin, jamais une variante par fond.
   Elle ne descend pas sous 14 px : en dessous, le N se ferme et il ne reste
   qu'un rond. C'est la taille qui a decide le dessin, pas l'inverse. */
.n-coin {
  /* ⚠️ `display: inline-block` EXPLICITE. Le reset du § 03 pose
     `img, video, svg, canvas { display: block }` : sans cette ligne, la piece
     part a la ligne toute seule et le montant se retrouve dessous. Vu en
     production le 03/09/2026 sur les trois cartes de niveau. */
  display: inline-block;
  inline-size: 18px; block-size: 18px; flex: none;
  vertical-align: -0.2em; margin-inline-end: 6px;
}
.n-coin--sm { inline-size: 15px; block-size: 15px; vertical-align: -0.16em; margin-inline-end: 5px; }
.n-coin--lg { inline-size: 28px; block-size: 28px; vertical-align: -0.16em; margin-inline-end: 8px; }
/* Dans un bouton, la marge vient du `gap` du bouton : la doubler decalerait
   le libelle. */
.n-btn > .n-coin { margin-inline-end: 0; vertical-align: 0; }
.n-credits b { color: var(--n-ink); font-weight: var(--n-fw-semi); font-variant-numeric: tabular-nums; }
@media (hover: hover) { .n-credits:hover { border-color: var(--n-line-accent, var(--n-line-strong)); } }
/* NUM_SANS_PLAN_24092026 · SANS PLAN NI CRÉDIT, LA PASTILLE EST L'OFFRE.
   Joseph, 24/09/2026 : « je veux pas de numeri grisé si il a pas de plan ».
   Plus de pièce pâle devant un « 0 » : un libellé d'action à l'accent de la
   marque (« Try it · $4.90 » ou « Buy a plan »), cerclé, à côté de « Create »
   qui garde le seul fond plein. Même boîte : le rail ne bouge pas. */
.n-credits[data-offer] { color: var(--n-accent); border-color: var(--n-line-accent, var(--n-accent)); }
.n-credits[data-offer] .n-credits__ico { display: none; }
.n-credits[data-offer] b { color: inherit; }
@media (hover: hover) { .n-credits[data-offer]:hover { background: var(--n-surface-2); } }

/* POURQUOI 1280 px ET PLUS 1100 — c'est une mesure, pas un gout.
   La rangee porte maintenant la marque (100), les cinq entrees (688), le
   solde (~96), « Create » (~104) et, dans /app, « Log out » (110), plus les
   ecarts : ~1160 px de contenu, soit ~1240 px de fenetre gouttiere comprise.
   A 1100 px les entrees de droite se seraient fait COUPER — sans barre de
   defilement et sans un signe, `html, body` etant en `overflow-x: clip`.
   En dessous, les cinq entrees sont dans la barre du bas et la rangee du
   haut porte marque + solde + Create : ce que Joseph demande, sur telephone
   aussi. */
@media (min-width: 1280px) {
  .n-nav__inner { display: grid; grid-template-columns: 1fr auto 1fr; }
  .n-nav__links { display: flex; }
  .n-brand { block-size: 22px; }
}

/* --- LA BARRE DU BAS ------------------------------------------------------
   Enfant direct de <body>, JAMAIS d'un header : `backdrop-filter` cree un
   bloc conteneur pour les descendants en position fixed (meme regle que
   `filter`), et .n-nav en porte un — une barre fixee « en bas » s'y collerait
   sous l'en-tete. C'est le bug que /app portait sans qu'on le voie.

   A 320 px chaque case fait 64 px, a 280 px (Galaxy Fold replie) 56 px :
   au-dessus des 44 px de cible dans les deux sens. `minmax(0, 1fr)` autorise
   une case a descendre sous la largeur de son contenu, et le libelle se
   REPLIE plutot que de se couper — une ellipse ferait mentir le nom
   accessible (« Accou… » a l'ecran, « Account » a la voix : WCAG 2.5.3). */
.n-tabbar {
  position: fixed; inset-block-end: 0; inset-inline: 0; z-index: var(--n-z-nav);
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  block-size: var(--n-tabbar-h);
  padding-block-end: env(safe-area-inset-bottom, 0px);
  padding-inline: env(safe-area-inset-left, 0px) env(safe-area-inset-right, 0px);
  background: var(--n-veil);
  -webkit-backdrop-filter: saturate(160%) blur(14px); backdrop-filter: saturate(160%) blur(14px);
  border-block-start: 1px solid var(--n-line);
}
.n-tabbar__link {
  position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; min-inline-size: 0; padding-inline: 2px; text-align: center;
  font-size: var(--n-fs-eyebrow); line-height: var(--n-lh-snug);
  font-weight: var(--n-fw-medium); color: var(--n-text-3);
  transition: color var(--n-dur-2) var(--n-ease);
}
.n-tabbar__ico { inline-size: 22px; block-size: 22px; flex: none; }
.n-tabbar__label { max-inline-size: 100%; white-space: normal; overflow-wrap: anywhere; }
.n-tabbar__link--go { color: var(--n-accent); }
@media (hover: hover) { .n-tabbar__link:hover { color: var(--n-ink); } }
/* Actif : l'encre ET un trait sur l'arete haute. Deux signaux, dont un qui
   n'est pas une couleur. */
.n-tabbar__link[aria-current="page"] { color: var(--n-ink); }
.n-tabbar__link[aria-current="page"]::before {
  content: ""; position: absolute; inset-block-start: 0; inset-inline: 22%;
  block-size: 2px; background: var(--n-ink); border-radius: 0 0 2px 2px;
}
/* Le lisere de focus rentre DANS la case : pose dehors, il serait coupe par
   le bord de l'ecran sur la premiere et la derniere. */
.n-tabbar__link:focus-visible { outline-offset: -4px; }
@media (min-width: 1280px) { .n-tabbar { display: none; } }

/* Le defilement pilote (ancre, focus, scrollIntoView) doit laisser la barre :
   sans ca, un champ vise atterrit DESSOUS. Le pendant du scroll-padding-top
   de la barre du haut, § 03. */
@media (max-width: 1279.98px) {
  html { scroll-padding-block-end: calc(var(--n-tabbar-h) + var(--n-sp-4)); }
}

/* UN BOUTON PLEINE LARGEUR PEUT REVENIR A LA LIGNE. `.n-btn` est en
   `white-space: nowrap` et `overflow-x: clip` ne rend AUCUNE barre de
   defilement : un libelle trop long n'est pas deborde, il est COUPE hors
   ecran, sans un signe. « Message us on WhatsApp » demande 314 px la ou une
   carte en offre 214 a 320 px. */
@media (max-width: 479.98px) {
  .n-btn--block { white-space: normal; padding-block: var(--n-sp-2); line-height: 1.25; }
}

/* == 09 bis · LE MONOGRAMME DANS LA NAV · NUM_RAMPE_03092026 ================
   Le monogramme en couleur est le SEUL endroit ou la marque se voit en
   entier. C'est le fichier de branding, decoupe, pas un trace : ses deux
   traits portent deux degrades differents qu'aucun `linearGradient` ne
   reproduit. Il est purement decoratif — le nom est deja dans le
   `aria-label` du lien — donc `alt=""`.
   ========================================================================= */
/* LA MARQUE, C'EST LE MOT SEUL — Joseph, 03/09/2026 : « juste sans
   l'icone ». Le monogramme ne parait donc nulle part sur le site ; il vit
   dans le favicon et dans `branding/`, pas dans une barre. */

/* Le mot sur l'appel final : le fond y est deja la rampe, donc il s'y pose
   en blanc plein, jamais en couleur sur couleur. Un mot se dimensionne par
   sa LARGEUR — lui donner une hauteur le rendrait minuscule ou geant selon
   la page. */
.n-final__mark { display: block; inline-size: 132px; block-size: auto; margin: 0 auto var(--n-sp-5); color: #fff; opacity: 0.92; }

/* Les credits offerts sous le bouton du heros : c'est la seule chose qui
   leve la main avant de cliquer. Le chiffre passe en encre pleine, le
   reste garde son gris — on lit « 27 free credits » d'un coup d'oeil. */
.n-solo__note b { color: var(--n-ink); font-weight: var(--n-fw-medium); }

/* == 10 · HEROS & SCENE VIDEO ==============================================
   Le titre est la baseline de la marque : « Your product video in 3 minutes. »
   avec « 3 minutes » dans la rampe. Il n'est JAMAIS anime (c'est le LCP).
   Dessous, une phrase, le bouton, puis le film dans un cadre noir aux angles
   ronds, puis les notes (NUM_ERGONOMIE_24092026 · E02 : le film suit
   directement le bouton, les notes passent dessous — voir le § 21) — la place
   est reservee par `aspect-ratio`, rien ne saute.
   ========================================================================== */
.n-hero { padding-block: calc(var(--n-header-h) + var(--n-sp-8)) var(--n-sp-8); }
.n-solo { display: grid; justify-items: center; text-align: center; gap: 0; }
.n-solo__title { max-inline-size: 16ch; }
.n-solo__lead { margin-block-start: var(--n-sp-5); max-inline-size: 44ch; }
.n-solo__cta { margin-block-start: var(--n-sp-6); display: flex; flex-wrap: wrap; justify-content: center; gap: var(--n-sp-3); inline-size: 100%; }
.n-solo__note { margin-block-start: var(--n-sp-4); font-size: var(--n-fs-sm); color: var(--n-text-3); max-inline-size: 40ch; }
.n-solo__stage { margin-block-start: var(--n-sp-8); inline-size: 100%; max-inline-size: 1040px; }
@media (min-width: 720px) {
  .n-hero { padding-block: calc(var(--n-header-h) + var(--n-sp-9)) var(--n-sp-9); }
  .n-solo__cta .n-btn { min-inline-size: 240px; }
}
@media (max-width: 719px) { .n-solo__cta .n-btn { inline-size: 100%; } }
/* NUM_ESSAI_4_90_24092026 · L'APPEL A L'ESSAI PEUT REVENIR A LA LIGNE.
   « Try it on your product — $4.90 » (libelle pose par numeri.js quand /packs
   rend l'essai) demande ~320 px en `nowrap` : sous 480 px, il elargissait la
   colonne du heros (titre et chapo coupes a droite par `overflow-x: clip`,
   des 360 px de large) et sortait du cadre de l'appel final. Meme remede que
   `.n-btn--block` (§ 09) : deux lignes equilibrees plutot qu'un texte coupe,
   et des marges laterales reduites pour tenir sur UNE ligne des 360 px. */
@media (max-width: 479.98px) {
  .n-solo__cta .n-btn, .n-final__cta .n-btn { white-space: normal; padding-block: var(--n-sp-2); padding-inline: var(--n-sp-4); line-height: 1.25; text-wrap: balance; }
}

.n-solo__frame {
  position: relative; inline-size: 100%; aspect-ratio: 832 / 464; overflow: hidden;
  border-radius: var(--n-r-lg); background: var(--n-well); box-shadow: var(--n-shadow-3);
}
@media (min-width: 720px) { .n-solo__frame { border-radius: var(--n-r-xl); } }
.n-solo__poster, .n-solo__video { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; object-fit: cover; }
.n-solo__poster { z-index: 1; transition: opacity var(--n-dur-4) var(--n-ease); }
[data-solo-state="playing"] .n-solo__poster { opacity: 0; }
.n-solo__play, .n-solo__sound {
  position: absolute; z-index: 2; inset-block-end: var(--n-sp-3); display: grid; place-items: center;
  inline-size: 44px; block-size: 44px; border-radius: 50%; background: rgba(255, 255, 255, 0.92); color: var(--n-ink);
  transition: background-color var(--n-dur-2) var(--n-ease), transform var(--n-dur-2) var(--n-ease);
}
.n-solo__sound { inset-inline-end: var(--n-sp-3); }
.n-solo__play { inset-inline-end: calc(var(--n-sp-3) + 52px); }
@media (hover: hover) { .n-solo__play:hover, .n-solo__sound:hover { background: #fff; } }
.n-solo__play:active, .n-solo__sound:active { transform: scale(0.96); }
.n-solo__ico { inline-size: 20px; block-size: 20px; }
.n-solo__ico--play, .n-solo__ico--on { display: none; }
.n-solo__play[aria-pressed="true"] .n-solo__ico--pause { display: none; }
.n-solo__play[aria-pressed="true"] .n-solo__ico--play { display: block; }
[data-solo-sound="on"] .n-solo__ico--off { display: none; }
[data-solo-sound="on"] .n-solo__ico--on { display: block; }
@media (min-width: 720px) {
  .n-solo__play, .n-solo__sound { inset-block-end: var(--n-sp-4); }
  .n-solo__sound { inset-inline-end: var(--n-sp-4); }
  .n-solo__play { inset-inline-end: calc(var(--n-sp-4) + 52px); }
}

/* == 11 · LA BANDE DE FILMS ================================================
   Huit films qui defilent, plein bord. Le defilement est en CSS ; numeri.js
   ne s'occupe que des videos (chargees quand elles approchent du champ) et
   du bouton de pause. La copie de la piste est ce qui rend la boucle sans
   couture : -50 % puis retour a 0, a l'image rien n'a bouge.
   ========================================================================== */
@keyframes n-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.n-bande { padding-block: var(--n-sp-4) var(--n-sp-2); }
.n-bande__piste { overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
                  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent); }
.n-bande__ruban { display: flex; inline-size: max-content; gap: var(--n-sp-3); animation: n-marquee 128s linear infinite; will-change: transform; } /* 128 s : douze films depuis le 25/09 (NUM_GENERALISTE_25092026), meme vitesse que huit en 64 s */
.n-bande__file { display: flex; gap: var(--n-sp-3); list-style: none; }
.n-bande__file + .n-bande__file { margin-inline-start: 0; }
.n-bande__carte { position: relative; flex: none; block-size: 200px; overflow: hidden; border-radius: var(--n-r-md); background: var(--n-well); }
.n-bande__carte--l { inline-size: calc(200px * 16 / 9); }
.n-bande__carte--p { inline-size: calc(200px * 9 / 16); }
@media (min-width: 720px) {
  .n-bande__carte { block-size: 280px; border-radius: var(--n-r-lg); }
  .n-bande__carte--l { inline-size: calc(280px * 16 / 9); }
  .n-bande__carte--p { inline-size: calc(280px * 9 / 16); }
}
.n-bande__affiche, .n-bande__video { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; object-fit: cover; }
.n-bande__affiche { z-index: 1; transition: opacity var(--n-dur-4) var(--n-ease); }
[data-bande-vue="1"] .n-bande__affiche { opacity: 0; }
[data-bande-etat="paused"] .n-bande__ruban { animation-play-state: paused; }
.n-bande__barre { display: flex; justify-content: flex-end; margin-block-start: var(--n-sp-3); }
.n-bande__pause { display: inline-flex; align-items: center; gap: var(--n-sp-2); min-block-size: 44px; padding: 0 var(--n-sp-4) 0 var(--n-sp-3);
  border-radius: var(--n-r-pill); font-size: var(--n-fs-xs); font-weight: var(--n-fw-medium); color: var(--n-text-3); }
@media (hover: hover) { .n-bande__pause:hover { color: var(--n-ink); background: var(--n-surface-2); } }
.n-bande__ico { inline-size: 16px; block-size: 16px; }
.n-bande__ico--play { display: none; }
.n-bande__pause[aria-pressed="true"] .n-bande__ico--pause { display: none; }
.n-bande__pause[aria-pressed="true"] .n-bande__ico--play { display: block; }
@media (prefers-reduced-motion: reduce) {
  .n-bande__ruban { animation: none; }
  .n-bande__piste { overflow-x: auto; -webkit-mask-image: none; mask-image: none; padding-inline: var(--n-sp-gutter); }
  .n-bande__file[aria-hidden="true"] { display: none; }
}

/* == 11 bis · AVANT / APRÈS · NUM_EXEMPLES_11092026 =====================================
   Quatre cartes : la photo deposee, une fleche, la video rendue. La video
   fixe la hauteur de la carte par son aspect-ratio ; la photo s'y range en
   `contain` sur un puits doux, jamais rognee — c'est la photo du client, on
   la montre entiere. Trois cartes portrait sur une rangee, la carte paysage
   sur toute la largeur. Sous 720 px, une carte par ligne, et la carte
   paysage empile la photo au-dessus de la video.
   ========================================================================== */
.n-ex { display: grid; gap: var(--n-sp-5); }
@media (min-width: 720px) {
  .n-ex { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--n-sp-4); }
  .n-ex__item--l { grid-column: 1 / -1; }
}
/* ⚠️ NUM_EXEMPLES_MOBILE_12092026 · `3fr / 4fr` ET PAS L'INVERSE. Les colonnes
   etaient `4fr / 3fr` : la PHOTO DEPOSEE prenait plus de place que le RENDU,
   sur toutes les largeurs. A 1280 px la video tombait a 137 px pour une photo
   de 182. Le bloc est la pour vendre ce qui sort, pas ce qui entre. */
.n-ex__fig { display: grid; grid-template-columns: minmax(0, 3fr) auto minmax(0, 4fr); align-items: stretch; gap: var(--n-sp-2);
             padding: var(--n-sp-3); border-radius: var(--n-r-xl); background: var(--n-surface); border: 1px solid var(--n-line); }
.n-ex__item--l .n-ex__fig { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 720px) {
  .n-ex__item--l .n-ex__fig { grid-template-columns: minmax(0, 550fr) auto minmax(0, 960fr); }
}
.n-ex__avant, .n-ex__apres { position: relative; overflow: hidden; border-radius: var(--n-r-md); }
.n-ex__avant { background: var(--n-surface-2); }
.n-ex__item--l .n-ex__avant { aspect-ratio: 1; }
/* ⚠️ LE PUITS DE LA PHOTO NE S'ETIRE PAS SUR LA HAUTEUR DE LA VIDEO. En
   `stretch` il faisait 137 x 327 pour une photo quasi carree : deux grandes
   plages grises au-dessus et au-dessous d'une vignette perdue au milieu, et
   ca sautait aux yeux des que la colonne photo est passee derriere la video.
   Un puits 3/4 cale au centre, en face de la fleche, et la photo le remplit.
   Les trois sources vont de 1:1 a 2:3 — 3/4 les tient toutes sans rogner. */
.n-ex__item--p .n-ex__avant { aspect-ratio: 3 / 4; align-self: center; }
@media (min-width: 720px) { .n-ex__item--l .n-ex__avant { aspect-ratio: auto; } }
.n-ex__photo { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; object-fit: contain; padding: var(--n-sp-2); }
.n-ex__apres { background: var(--n-well); }
.n-ex__item--p .n-ex__apres { aspect-ratio: 540 / 968; }
.n-ex__item--l .n-ex__apres { aspect-ratio: 960 / 550; }
.n-ex__affiche, .n-ex__video { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; object-fit: cover; }
.n-ex__affiche { z-index: 1; transition: opacity var(--n-dur-4) var(--n-ease); }
[data-ex-vue="1"] .n-ex__affiche { opacity: 0; }
.n-ex__fleche { display: grid; place-items: center; align-self: center; inline-size: 28px; block-size: 28px; border-radius: 50%;
                background: var(--n-grad-deep); color: #fff; }
.n-ex__fleche svg { inline-size: 14px; block-size: 14px; }
.n-ex__item--l .n-ex__fleche { transform: rotate(90deg); justify-self: center; }
@media (min-width: 720px) { .n-ex__item--l .n-ex__fleche { transform: none; } }
.n-ex__tag { position: absolute; z-index: 2; inset-block-start: var(--n-sp-2); inset-inline-start: var(--n-sp-2);
             padding: 2px 8px; border-radius: var(--n-r-pill); font-size: var(--n-fs-eyebrow); font-weight: var(--n-fw-medium);
             letter-spacing: var(--n-ls-caps); text-transform: uppercase; background: rgba(255, 255, 255, 0.92); color: var(--n-ink); }
.n-ex__tag--video { background: rgba(0, 0, 0, 0.6); color: #fff; }
.n-ex__son { position: absolute; z-index: 2; inset-block-end: var(--n-sp-2); inset-inline-end: var(--n-sp-2); display: grid; place-items: center;
             inline-size: 44px; block-size: 44px; border-radius: 50%; background: rgba(255, 255, 255, 0.92); color: var(--n-ink);
             transition: background-color var(--n-dur-2) var(--n-ease), transform var(--n-dur-2) var(--n-ease); }
@media (hover: hover) { .n-ex__son:hover { background: #fff; } }
.n-ex__son:active { transform: scale(0.96); }
.n-ex__ico { inline-size: 20px; block-size: 20px; }
.n-ex__ico--on { display: none; }
.n-ex__son[aria-pressed="true"] .n-ex__ico--off { display: none; }
.n-ex__son[aria-pressed="true"] .n-ex__ico--on { display: block; }
.n-ex__legende { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--n-sp-1) var(--n-sp-3);
                 padding: var(--n-sp-1) var(--n-sp-1) 0; font-size: var(--n-fs-sm); color: var(--n-text-3); }
.n-ex__legende b { color: var(--n-ink); font-weight: var(--n-fw-medium); }
.n-ex__barre { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--n-sp-3); margin-block-start: var(--n-sp-4); }
.n-ex__note { max-inline-size: 60ch; }

/* ⚠️ NUM_EXEMPLES_MOBILE_12092026 · SUR TELEPHONE C'EST LE RENDU QUI DOIT ETRE GRAND.
   Joseph, 12/09/2026 : « sur mobile l'exemple apparait pas ». Il apparaissait :
   MESURE a 390 px, la video du rendu faisait 118 px de large et la photo
   deposee 158 px. Les `4fr / 3fr` de la rangee a trois colonnes — ecrits pour
   un bureau ou la carte fait 420 px — donnaient la moitie de la carte a la
   SOURCE et un timbre-poste au PRODUIT. L'argument de vente occupait 9 % de
   l'ecran : personne ne le voyait, donc il n'existait pas.

   Le rapport s'inverse sous 720 px, il ne s'empile pas : la carte ne vaut que
   parce qu'on voit la photo ET la video D'UN SEUL COUP D'OEIL. Empilees, une
   video 9:16 pleine largeur fait 620 px et chasse la photo hors de l'ecran —
   on perd la comparaison, qui est tout le propos du bloc.
   1fr / 2fr, la fleche et les marges resserrees : la video passe a ~197 px
   (+67 %) et la photo reste lisible a ~99 px. */
@media (max-width: 719.98px) {
  .n-ex__item--p .n-ex__fig { grid-template-columns: minmax(0, 1fr) auto minmax(0, 2fr);
                              gap: var(--n-sp-1); padding: var(--n-sp-2); }
  .n-ex__item--p .n-ex__fleche { inline-size: 24px; block-size: 24px; }
  .n-ex__item--p .n-ex__fleche svg { inline-size: 12px; block-size: 12px; }
  /* La photo du client reste entiere (`contain`) : seul son puits se resserre. */
  .n-ex__item--p .n-ex__photo { padding: var(--n-sp-1); }
  .n-ex__item--p .n-ex__tag { padding: 1px 6px; font-size: 10px; }
  /* La carte paysage empile deja ; sa photo carree prenait 320 px de haut pour
     une video de 183 — le puits passe en 4/3, la photo n'est pas rognee. */
  .n-ex__item--l .n-ex__avant { aspect-ratio: 4 / 3; }
}

/* == 11 ter · LA DEMO PRE-RENDUE · NUM_DEMO_PRE_RENDUE_11092026 ================================
   Trois rangees de choix (.n-seg qui s'enroule), puis la scene : la photo,
   la fleche, la video 16:9. Meme grammaire que les cartes avant/apres ; sur
   telephone la scene s'empile, la fleche tourne. Les boutons de la scene
   font 44 px ; la ligne « Look » reserve sa hauteur avec trois fantomes
   pour que la page ne saute pas quand l'API repond. */
.n-demo__choix { display: grid; gap: var(--n-sp-3); margin-block-end: var(--n-sp-5); }
.n-demo__rangee { display: grid; grid-template-columns: 1fr; align-items: center; gap: var(--n-sp-2); }
@media (min-width: 720px) { .n-demo__rangee { grid-template-columns: 5.5rem minmax(0, 1fr) auto; } }
.n-demo__etiquette { font-size: var(--n-fs-eyebrow); font-weight: var(--n-fw-medium); letter-spacing: var(--n-ls-caps);
                     text-transform: uppercase; color: var(--n-text-3); }
/* ⚠️ Des PUCES, pas le bac `.n-seg` : sept produits s'enroulent sur trois
   lignes au telephone, et un bac en pilule qui s'enroule devient une tache
   grise aux coins qui mangent le premier bouton (vu le 11/09 en 390 px).
   Chaque bouton porte donc son propre fond ; le coche garde le relief du
   `.n-seg` (surface + ombre + filet). */
.n-demo__seg { display: flex; flex-wrap: wrap; justify-self: start; max-inline-size: 100%;
               gap: var(--n-sp-2); padding: 0; background: none; border-radius: 0; }
.n-demo__seg .n-seg__btn { background: var(--n-surface-2); }
.n-demo__seg .n-seg__btn[aria-checked="true"] { background: var(--n-surface); color: var(--n-ink);
                                                box-shadow: var(--n-shadow-1), 0 0 0 1px var(--n-line); }
.n-demo__seg .n-seg__btn { cursor: pointer; }
.n-demo__seg .n-seg__btn small { font-weight: var(--n-fw-normal); color: var(--n-text-3); margin-inline-start: 4px; }
.n-demo__seg .n-seg__btn:focus-visible { outline: 2px solid var(--n-accent); outline-offset: 2px; }
.n-demo__fantome { visibility: hidden; }
.n-demo__aparte { font-size: var(--n-fs-sm); color: var(--n-text-3); }
.n-demo__fig { display: grid; grid-template-columns: minmax(0, 1fr); align-items: stretch; gap: var(--n-sp-2);
               padding: var(--n-sp-3); border-radius: var(--n-r-xl); background: var(--n-surface); border: 1px solid var(--n-line); }
@media (min-width: 720px) { .n-demo__fig { grid-template-columns: minmax(0, 420fr) auto minmax(0, 960fr); } }
.n-demo__avant, .n-demo__apres { position: relative; overflow: hidden; border-radius: var(--n-r-md); }
.n-demo__avant { background: var(--n-surface-2); aspect-ratio: 4 / 3; }
@media (min-width: 720px) { .n-demo__avant { aspect-ratio: auto; min-block-size: 100%; } }
.n-demo__photo { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; object-fit: contain; padding: var(--n-sp-2);
                 transition: opacity var(--n-dur-3) var(--n-ease); }
.n-demo__apres { background: var(--n-well); aspect-ratio: 16 / 9; }
.n-demo__affiche, .n-demo__video { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; object-fit: cover; }
.n-demo__affiche { z-index: 1; transition: opacity var(--n-dur-4) var(--n-ease); }
[data-demo-vue="1"] .n-demo__affiche { opacity: 0; }
[data-demo-chargement="1"] .n-demo__photo, [data-demo-chargement="1"] .n-demo__affiche { opacity: 0.35; }
.n-demo__fleche { display: grid; place-items: center; align-self: center; justify-self: center; inline-size: 28px; block-size: 28px;
                  border-radius: 50%; background: var(--n-grad-deep); color: #fff; transform: rotate(90deg); }
.n-demo__fleche svg { inline-size: 14px; block-size: 14px; }
@media (min-width: 720px) { .n-demo__fleche { transform: none; } }
.n-demo__btn { position: absolute; z-index: 2; inset-block-end: var(--n-sp-2); display: grid; place-items: center; cursor: pointer;
               inline-size: 44px; block-size: 44px; border-radius: 50%; background: rgba(255, 255, 255, 0.92); color: var(--n-ink);
               transition: background-color var(--n-dur-2) var(--n-ease), transform var(--n-dur-2) var(--n-ease); }
.n-demo__btn--son { inset-inline-end: var(--n-sp-2); }
.n-demo__btn--lecture { inset-inline-end: calc(var(--n-sp-2) + 52px); }
@media (hover: hover) { .n-demo__btn:hover { background: #fff; } }
.n-demo__btn:active { transform: scale(0.96); }
.n-demo__btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.n-demo__ico--pause { display: none; }
.n-demo__btn--lecture[aria-pressed="true"] .n-demo__ico--play { display: none; }
.n-demo__btn--lecture[aria-pressed="true"] .n-demo__ico--pause { display: block; }
.n-demo__btn--son[aria-pressed="true"] .n-ex__ico--off { display: none; }
.n-demo__btn--son[aria-pressed="true"] .n-ex__ico--on { display: block; }
.n-demo__legende { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--n-sp-1) var(--n-sp-3);
                   padding: var(--n-sp-1) var(--n-sp-1) 0; font-size: var(--n-fs-sm); color: var(--n-text-3); }
.n-demo__legende b { color: var(--n-ink); font-weight: var(--n-fw-medium); }
.n-demo__faits { margin-inline-start: auto; }
.n-demo__cta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--n-sp-3) var(--n-sp-4); margin-block-start: var(--n-sp-5); }
.n-demo__cta-note { font-size: var(--n-fs-sm); color: var(--n-text-3); }
@media (prefers-reduced-motion: reduce) {
  .n-demo__photo, .n-demo__affiche { transition: none; }
}
/* == 12 · ETAPES, FORMATS, FAITS ============================================ */
.n-steps { display: grid; gap: var(--n-sp-5); counter-reset: n-step; }
@media (min-width: 720px) { .n-steps { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--n-sp-5); } }
.n-step { position: relative; display: grid; gap: var(--n-sp-3); padding: var(--n-sp-5); border-radius: var(--n-r-xl);
          background: var(--n-surface); border: 1px solid var(--n-line); counter-increment: n-step; }
@media (min-width: 720px) { .n-step { padding: var(--n-sp-6) var(--n-sp-5); } }
.n-step__num { display: inline-grid; place-items: center; inline-size: 40px; block-size: 40px; border-radius: 50%;
  background: var(--n-grad-deep); color: #fff; font-size: var(--n-fs-sm); font-weight: var(--n-fw-medium); }
.n-step__num::before { content: counter(n-step); }
.n-step__art { display: grid; place-items: center; block-size: clamp(88px, 22vw, 120px); border-radius: var(--n-r-lg); background: var(--n-grad-soft); color: var(--n-ink); overflow: hidden; }
.n-step__art svg { inline-size: 48px; block-size: 48px; }
.n-step__title { font-size: var(--n-fs-h3); font-weight: var(--n-fw-medium); color: var(--n-ink); letter-spacing: var(--n-ls-h3); line-height: var(--n-lh-title); }
.n-step__body { font-size: var(--n-fs-sm); color: var(--n-text-2); }

/* Les six formats : un rectangle noir aux vraies proportions, borne en hauteur
   pour que 21:9 et 9:16 tiennent a la meme echelle sur un telephone. */
.n-formats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--n-sp-3); }
@media (min-width: 560px) { .n-formats { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 720px) { .n-formats { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: var(--n-sp-4); } }
.n-format { display: grid; gap: var(--n-sp-2); justify-items: center; text-align: center; padding: var(--n-sp-4) var(--n-sp-2);
            border-radius: var(--n-r-lg); background: var(--n-surface); border: 1px solid var(--n-line); }
.n-format__box { display: grid; place-items: center; block-size: 88px; inline-size: 100%; }
.n-format__box > i { display: block; max-inline-size: 100%; max-block-size: 88px; background: var(--n-grad); border-radius: 6px; }
/* Les six proportions, SCOPEES au rectangle : formats.js pose aussi la
   classe n-ar-* sur le bouton entier, qui ne doit pas en heriter. */
.n-format__box > .n-ar-21x9 { aspect-ratio: 21 / 9; inline-size: 100%; }
.n-format__box > .n-ar-16x9 { aspect-ratio: 16 / 9; inline-size: 88%; }
.n-format__box > .n-ar-4x3  { aspect-ratio: 4 / 3;  inline-size: 74%; }
.n-format__box > .n-ar-1x1  { aspect-ratio: 1 / 1;  inline-size: 56%; }
.n-format__box > .n-ar-3x4  { aspect-ratio: 3 / 4;  block-size: 88px; }
.n-format__box > .n-ar-9x16 { aspect-ratio: 9 / 16; block-size: 88px; }
.n-format b { font-size: var(--n-fs-body); font-weight: var(--n-fw-medium); color: var(--n-ink); }
.n-format small { font-size: var(--n-fs-xs); color: var(--n-text-3); line-height: 1.35; }

/* NUM_GENERALISTE_25092026 · « What you can film » : les sept categories, meme
   carte que .n-format. Flex centre plutot que grille : la septieme ne reste pas
   seule a gauche sur deux ou quatre colonnes. */
.n-cats { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--n-sp-3); }
.n-cat { position: relative; flex: 0 1 calc(50% - var(--n-sp-3) / 2); display: grid; gap: var(--n-sp-2); justify-items: center;
         align-content: start; text-align: center; padding: var(--n-sp-5) var(--n-sp-3);
         border-radius: var(--n-r-lg); background: var(--n-surface); border: 1px solid var(--n-line); }
@media (min-width: 640px) { .n-cat { flex-basis: calc(25% - var(--n-sp-3) * 3 / 4); } }
@media (min-width: 1040px) { .n-cats { flex-wrap: nowrap; gap: var(--n-sp-4); } .n-cat { flex: 1 1 0; min-inline-size: 0; } }
.n-cat__ico { inline-size: 32px; block-size: 32px; color: var(--n-accent); }
.n-cat b { display: inline-flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: var(--n-sp-2);
           font-size: var(--n-fs-body); font-weight: var(--n-fw-medium); color: var(--n-ink); }
.n-cat__new { min-block-size: 20px; padding: 0 var(--n-sp-2); }
.n-cat small { font-size: var(--n-fs-xs); color: var(--n-text-3); line-height: 1.35; }
.n-cat__lien { position: absolute; inset: 0; border-radius: inherit; }
.n-cat__lien:focus-visible { outline: 2px solid var(--n-accent); outline-offset: 2px; }
@media (hover: hover) { .n-cat:hover { border-color: var(--n-line-2); } }

/* La rangee de faits : cinq chiffres qui tiennent la promesse, en une ligne. */
.n-proof { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--n-sp-2) var(--n-sp-5); font-size: var(--n-fs-sm); color: var(--n-text-2); }
.n-proof b { color: var(--n-ink); font-weight: var(--n-fw-medium); }
.n-proof li { display: inline-flex; align-items: center; gap: var(--n-sp-2); }
.n-proof li::before { content: ""; inline-size: 6px; block-size: 6px; border-radius: 50%; background: var(--n-accent); }

/* L'appel final : la seule surface noire de la page, le bouton en blanc. */
.n-final { padding: var(--n-sp-8) var(--n-sp-5); border-radius: var(--n-r-2xl); background: var(--n-grad-deep); color: #fff; text-align: center; }
@media (min-width: 720px) { .n-final { padding: var(--n-sp-9) var(--n-sp-8); } }
.n-final__title { font-size: var(--n-fs-h1); font-weight: var(--n-fw-medium); letter-spacing: var(--n-ls-display); line-height: var(--n-lh-snug); color: #fff; text-wrap: balance; max-inline-size: 18ch; margin-inline: auto; }
.n-final__lead { margin-block-start: var(--n-sp-4); color: var(--n-text-inverse-2); font-size: var(--n-fs-lead); max-inline-size: 40ch; margin-inline: auto; }
.n-final__cta { margin-block-start: var(--n-sp-6); display: flex; flex-wrap: wrap; justify-content: center; gap: var(--n-sp-3); }
.n-final__note { margin-block-start: var(--n-sp-4); font-size: var(--n-fs-sm); color: var(--n-text-inverse-2); }

/* Le teaser : deux cartes cote a cote (tarifs, sur mesure). */
.n-duo { display: grid; gap: var(--n-sp-4); }
@media (min-width: 720px) { .n-duo { grid-template-columns: 1fr 1fr; gap: var(--n-sp-5); } }
.n-duo .n-card { display: grid; gap: var(--n-sp-3); align-content: start; }
.n-duo .n-card .n-btn { justify-self: start; margin-block-start: var(--n-sp-2); }

/* == 13 · TARIFS =============================================================
   Deux facons d'acheter, un segment pour choisir, quatre cartes. Une carte
   dit QUATRE choses : le nom, le prix, les credits, ce que ca fait comme
   videos. La liste de sept puces est partie : ce que tous les plans
   partagent tient en une ligne sous la grille.
   Les noms `n-card-plan*` sont ceux que numeri.js manipule (data-plan,
   data-plan-amount, data-plan-quota, __flag, --featured, __cta).
   ========================================================================== */
.n-pricing-toggle { display: flex; justify-content: center; margin-block-end: var(--n-sp-6); }
.n-plans { display: grid; gap: var(--n-sp-4); }
@media (min-width: 640px) { .n-plans { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .n-plans { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--n-sp-4); } }
.n-card-plan {
  position: relative; display: grid; grid-template-rows: auto auto auto auto 1fr auto; gap: var(--n-sp-2); align-content: start;
  padding: var(--n-sp-5); border-radius: var(--n-r-xl); background: var(--n-surface); border: 1px solid var(--n-line);
}
@media (min-width: 1024px) { .n-card-plan { padding: var(--n-sp-6) var(--n-sp-5); } }
.n-card-plan--featured { border-color: var(--n-ink); box-shadow: inset 0 0 0 1px var(--n-ink); }
.n-card-plan__flag { position: absolute; inset-block-start: -12px; inset-inline-start: var(--n-sp-5);
  display: inline-flex; align-items: center; block-size: 24px; padding: 0 var(--n-sp-3); border-radius: var(--n-r-pill);
  background: var(--n-grad-deep); color: #fff; font-size: var(--n-fs-xs); font-weight: var(--n-fw-medium); white-space: nowrap; }
.n-card-plan__flag:empty { display: none; }
.n-card-plan__name { font-size: var(--n-fs-h4); font-weight: var(--n-fw-medium); color: var(--n-ink); }
/* NUM_GRILLE_CLAUDE_24092026 · LE NOM DU FORFAIT EN TETE, PUIS SON ACCROCHE, facon Claude
   (« Pro · Great value for… »). Le nom (`short_name` : « Creator », « Studio 5× ») est le
   titre de la carte, a la taille d'un h3 ; l'accroche (`tagline`) dessous, en texte
   secondaire. « Up to N videos a month » (`.n-card-plan__name`) garde son style et passe
   sous le prix. Classes libres, hors `.n-plans--aligned` : le studio peut les reprendre. */
.n-card-plan__title { font-size: var(--n-fs-h3); font-weight: var(--n-fw-medium); line-height: var(--n-lh-title);
  letter-spacing: var(--n-ls-h3); color: var(--n-ink); }
.n-card-plan__tagline { font-size: var(--n-fs-sm); line-height: 1.45; color: var(--n-text-2); text-wrap: pretty; }
/* La vitrine : chaque carte en colonne, le bouton pousse en bas (`margin-block-start: auto`),
   pour que les quatre boutons s'alignent meme quand une carte porte une ligne de plus (la
   ligne WhatsApp des Studio). L'accroche prend une hauteur fixe des qu'il y a plusieurs
   colonnes (2 lignes a 640 px, 3 a 1024 px) : les prix tombent a la meme hauteur d'une carte
   a l'autre. Telephone (une colonne) : hauteur naturelle, rien de reserve. Le studio garde
   sa grille (`.app-plans .n-card-plan`, app.css) : ce modificateur n'y est pas pose. */
.n-plans--aligned .n-card-plan { display: flex; flex-direction: column; }
.n-plans--aligned .n-card-plan__cta { margin-block-start: auto; padding-block-start: var(--n-sp-4); align-self: stretch; }
@media (min-width: 640px) { .n-plans--aligned .n-card-plan__tagline { min-block-size: 2.9em; } }
@media (min-width: 1024px) { .n-plans--aligned .n-card-plan__tagline { min-block-size: 4.35em; } }
/* NUM_GRILLE_CLAUDE_24092026 · LITE, LA PORTE D'ENTREE (SPEC 2 : il remplace la video d'essai a 4,90 $).
   Un BANDEAU au-dessus des quatre cartes, pas une cinquieme carte : la grille de 4 colonnes reste
   pleine. C'est une `.n-card-plan` (numeri.js la remplit comme les autres) en trois blocs : le nom
   et l'accroche, le prix et le volume, le bouton. Empiles au telephone ; des 640 px (deux colonnes
   de cartes dessous) le nom et le prix cote a cote, le bouton sur toute la largeur en dessous ; des
   1024 px (quatre colonnes dessous) les trois blocs sur une ligne, le bouton a droite. La ligne de
   credits perd son filet (elle n'ouvre pas de grille ici). Hors de `.n-plans--aligned` : l'accroche
   garde sa hauteur naturelle. */
.n-plans__entry { margin-block-end: var(--n-sp-5); }
.n-card-plan--entry { grid-template-rows: none; gap: var(--n-sp-3); align-items: center; }
.n-card-plan__entry-head, .n-card-plan__entry-body { display: grid; gap: var(--n-sp-1); align-content: start; min-inline-size: 0; }
.n-card-plan--entry .n-card-plan__price { margin-block-start: 0; }
.n-card-plan--entry .n-card-plan__credits { margin-block-start: 0; padding-block-start: 0; border-block-start: 0; font-size: var(--n-fs-sm); }
.n-card-plan--entry .n-card-plan__cta { margin-block-start: var(--n-sp-1); align-self: center; }
@media (min-width: 640px) {
  .n-card-plan--entry { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--n-sp-3) var(--n-sp-5); }
  .n-card-plan--entry .n-card-plan__cta { grid-column: 1 / -1; }
}
@media (min-width: 1024px) {
  .n-card-plan--entry { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr) auto; gap: var(--n-sp-3) var(--n-sp-6); }
  .n-card-plan--entry .n-card-plan__cta { grid-column: auto; margin-block-start: 0; min-inline-size: 11rem; }
}
/* NUM_SOLO_17_100_CREDITS_23092026 · ce que compte le titre : des videos de 5 s en Medium */
.n-card-plan__unit { margin-block-start: 2px; font-size: var(--n-fs-sm); color: var(--n-text-3); }
.n-card-plan__price { display: flex; align-items: baseline; gap: var(--n-sp-2); flex-wrap: wrap; margin-block-start: var(--n-sp-2); }
.n-card-plan__amount { font-size: var(--n-fs-kpi); font-weight: var(--n-fw-medium); letter-spacing: var(--n-ls-title); line-height: 1; color: var(--n-ink); font-variant-numeric: tabular-nums; }
.n-card-plan__cycle { font-size: var(--n-fs-sm); color: var(--n-text-3); }
.n-card-plan__vat { font-size: var(--n-fs-xs); color: var(--n-text-3); }
/* NUM_ABONNEMENTS_29_03092026 · « sans engagement », sous le prix de chaque
   carte. Joseph, 03/09/2026 : « met bien sans engagement ». La regle ne vivait
   que dans la FAQ, ou personne ne la lit avant de payer. Discrete par la taille,
   pas par la couleur : elle doit rester lisible (--n-text-2, pas --n-text-3, qui
   tombe sous AA a cette taille sur le fond des cartes). */
.n-card-plan__free { margin-block-start: var(--n-sp-1); font-size: var(--n-fs-sm);
                     color: var(--n-text-2); }
.n-card-plan__credits { margin-block-start: var(--n-sp-3); padding-block-start: var(--n-sp-3); border-block-start: 1px solid var(--n-line);
  font-size: var(--n-fs-body); color: var(--n-ink); font-weight: var(--n-fw-medium); font-variant-numeric: tabular-nums; }
.n-card-plan__videos { font-size: var(--n-fs-sm); color: var(--n-text-2); }
/* NUM_GRILLE_CARTES_23092026 · Joseph : « Avec toutes les qualites, et duree ». Sur chaque carte, le maximum
   du mois par niveau ET par duree (tout le quota sur ce seul type de video), cases relues par numeri.js
   ([data-max-videos-of]). « — » : duree non vendue a ce niveau, ou quota qui n'en paie pas une. */
.n-card-plan__grid { inline-size: 100%; margin-block-start: var(--n-sp-2); border-collapse: collapse;
  font-size: var(--n-fs-xs); font-variant-numeric: tabular-nums; color: var(--n-ink); }
.n-card-plan__grid caption { caption-side: top; text-align: start; padding-block-end: var(--n-sp-1);
  font-size: var(--n-fs-xs); color: var(--n-text-3); }
.n-card-plan__grid th, .n-card-plan__grid td { padding: 3px 4px; text-align: end; border-block-end: 1px solid var(--n-line, rgba(0,0,0,.08)); }
.n-card-plan__grid thead th { font-weight: var(--n-fw-medium); color: var(--n-text-3); }
.n-card-plan__grid tbody th { text-align: start; font-weight: var(--n-fw-medium); white-space: nowrap; padding-inline-start: 0; }
.n-card-plan__grid tbody tr:last-child th, .n-card-plan__grid tbody tr:last-child td { border-block-end: 0; }
.n-card-plan__pitch { font-size: var(--n-fs-sm); color: var(--n-text-2); }
/* NUM_WHATSAPP_PROMPTS_17092026 · l'accompagnement WhatsApp des paliers a partir de 95 $.
   En --n-ink, pas en --n-text-2 : c'est un argument, pas une note. */
.n-card-plan__perk { display: flex; align-items: flex-start; gap: var(--n-sp-2); font-size: var(--n-fs-sm);
  color: var(--n-ink); font-weight: var(--n-fw-medium); }
.n-card-plan__perk-icon { flex: none; inline-size: 16px; block-size: 16px; margin-block-start: 2px; }
/* L'encart au-dessus de la grille : la meme promesse, dite avant les cartes. */
.n-plans__whatsapp { display: flex; align-items: flex-start; justify-content: center; gap: var(--n-sp-2);
  max-inline-size: 44rem; margin: 0 auto var(--n-sp-6); padding: var(--n-sp-3) var(--n-sp-5);
  border: 1px solid var(--n-accent); border-radius: var(--n-r-lg); background: var(--n-accent-veil);
  color: var(--n-ink); font-size: var(--n-fs-body); line-height: 1.45; text-align: start; }
.n-plans__whatsapp b { font-weight: var(--n-fw-medium); }
.n-plans__whatsapp-icon { flex: none; inline-size: 20px; block-size: 20px; margin-block-start: 2px; color: var(--n-accent); }
.n-card-plan__cta { margin-block-start: var(--n-sp-4); align-self: end; }
/* NUM_PREMIER_MOIS_MOINS_20_23092026 · −20 % le premier mois : le bandeau au-dessus des cartes (degrade du
   badge « Most Popular »), et la ligne de chaque carte sous le prix. */
.n-plans__offer { display: flex; align-items: center; justify-content: center; gap: var(--n-sp-4);
  max-inline-size: 44rem; margin: 0 auto var(--n-sp-4); padding: var(--n-sp-3) var(--n-sp-5);
  border-radius: var(--n-r-lg); background: var(--n-grad-deep); color: #fff; text-align: start; }
.n-plans__offer[hidden] { display: none; }
.n-plans__offer-pct { flex: none; font-size: var(--n-fs-kpi); font-weight: var(--n-fw-medium); letter-spacing: var(--n-ls-title);
  line-height: 1; font-variant-numeric: tabular-nums; }
.n-plans__offer-txt { font-size: var(--n-fs-sm); line-height: 1.45; opacity: .92; }
.n-plans__offer-txt b { font-weight: var(--n-fw-medium); font-size: var(--n-fs-body); }
.n-card-plan__offer { display: flex; align-items: center; gap: var(--n-sp-2); margin-block-start: var(--n-sp-2);
  font-size: var(--n-fs-sm); color: var(--n-ink); }
.n-card-plan__offer[hidden] { display: none; }
.n-card-plan__offer-pct { flex: none; display: inline-flex; align-items: center; block-size: 22px; padding: 0 var(--n-sp-2);
  border-radius: var(--n-r-pill); background: var(--n-grad-deep); color: #fff; font-size: var(--n-fs-xs);
  font-weight: var(--n-fw-medium); white-space: nowrap; }
.n-card-plan__offer-txt b { font-weight: var(--n-fw-medium); }
@media (max-width: 560px) { .n-plans__offer { flex-direction: column; align-items: flex-start; gap: var(--n-sp-2); } }
/* NUM_PRIX_PAR_VIDEO_04092026 · CE QU'UNE VIDEO COUTE, SOUS CHAQUE PRIX.
   Joseph, 04/09/2026 : « sous chaque tarif dans numeri, le prix de revient par
   video pour eux ». Le montant d'une formule ne se compare pas d'une carte a
   l'autre -- 29 $ et 499 $ ne rendent pas la meme chose. Ramene a UNE video,
   il se compare : 10 -> 8,70 -> 4,83 -> 4,50 -> 4,27 -> 3,90. L'escalier des prix
   par credit (TARIFS.md §2) devient enfin lisible a l'ecran.
   ⚠️ C'est le prix PAYE PAR LE CLIENT. Notre prix de revient fournisseur ne
      sort d'aucune page (TARIFS.md §12).
   Chiffres a --n-fs-sm sur --n-text-2, le montant seul en --n-ink : la ligne
   doit se lire sans concurrencer le prix qu'elle explique. */
.n-card-plan__pervideo { margin-block-start: var(--n-sp-1); font-size: var(--n-fs-sm);
  color: var(--n-text-2); font-variant-numeric: tabular-nums; }
.n-card-plan__pervideo b { font-weight: var(--n-fw-medium); color: var(--n-ink); }

/* NUM_BONUS_PREMIER_MOIS_11092026 · « + 30 credits offert avec la valeur le
   premier mois » (Joseph, 11/09/2026). Une promesse, donc elle se voit : fond
   --n-ok-soft et texte --n-ok (4,9:1 sur blanc, AA tenu a cette taille), la
   meme famille que les autres bonnes nouvelles de la page. Elle reste SOUS le
   prix par video : elle ajoute au prix, elle ne le remplace pas.
   ⚠️ `hidden` dans le HTML, ouverte par numeri.js seulement si le serveur rend
      un bonus — `[hidden]` doit donc gagner sur ce `display`. */
.n-card-plan__bonus { margin-block-start: var(--n-sp-2); padding: 6px 10px;
  border-radius: var(--n-r-sm, 8px); background: var(--n-ok-soft);
  color: var(--n-ok); font-size: var(--n-fs-sm); line-height: 1.35;
  font-variant-numeric: tabular-nums; }
.n-card-plan__bonus b { font-weight: var(--n-fw-semi); color: var(--n-ok); }
.n-card-plan__bonus[hidden] { display: none; }
/* Dans les lignes de pack, elle passe SOUS le montant : `.n-row__price` est en
   `white-space: nowrap` et deux `small` cote a cote debordaient la colonne sur
   telephone. */
.n-row__price small.n-row__per { display: block; margin-inline-start: 0;
  margin-block-start: 2px; font-size: var(--n-fs-sm); color: var(--n-text-2); }

.n-plans__common { margin-block-start: var(--n-sp-5); }

/* Les packs et les recharges : des lignes, pas des cartes. */
.n-rows { border-block-start: 1px solid var(--n-line); }
.n-row { display: grid; grid-template-columns: 1fr auto; grid-template-areas: "name price" "meta cta"; gap: var(--n-sp-1) var(--n-sp-4);
         align-items: center; padding-block: var(--n-sp-4); border-block-end: 1px solid var(--n-line); }
@media (min-width: 640px) { .n-row { grid-template-columns: 1.2fr 1fr auto auto; grid-template-areas: "name meta price cta"; gap: var(--n-sp-5); } }
.n-row__name { grid-area: name; font-size: var(--n-fs-body); font-weight: var(--n-fw-medium); color: var(--n-ink); }
.n-row__meta { grid-area: meta; font-size: var(--n-fs-sm); color: var(--n-text-2); }
.n-row__price { grid-area: price; font-size: var(--n-fs-h3); font-weight: var(--n-fw-medium); color: var(--n-ink); font-variant-numeric: tabular-nums; justify-self: end; white-space: nowrap; }
.n-row__price small { font-size: var(--n-fs-xs); font-weight: var(--n-fw-normal); color: var(--n-text-3); margin-inline-start: 4px; }
.n-row__cta { grid-area: cta; justify-self: end; }
/* NUM_CONVERSION_23092026 · plus de « Most popular » écrit en dur (faux) : la mention vient de l'API, dans data-mention. */
.n-row__name[data-mention]::after { content: attr(data-mention); margin-inline-start: var(--n-sp-2); display: inline-flex; align-items: center;
  block-size: 20px; padding: 0 var(--n-sp-2); border-radius: var(--n-r-pill); background: var(--n-ink); color: #fff; font-size: 11px; font-weight: var(--n-fw-medium); vertical-align: middle; }

/* Les trois niveaux : un rendu coute 30, 60 ou 100. Une bande de trois. */
/* LE DEPART · NUM_20_CREDITS_OFFERTS_10092026 · « 20 offerts + 10 a 3 $ = la
   premiere video ». Trois colonnes et deux operateurs a partir de 720 px ;
   empile en dessous, l'operateur centre entre les blocs. */
.n-start { display: grid; gap: var(--n-sp-4); align-items: center; padding: var(--n-sp-5); border-radius: var(--n-r-xl); background: var(--n-surface); border: 1px solid var(--n-line); }
/* NUM_DUREE_AU_CHOIX_23092026 · LA RANGEE A CINQ COLONNES PART A 860 px, PLUS
   A 720. Mesure du 23/09/2026 : la somme des largeurs minimales (deux badges
   `nowrap`, le bouton, quatre gouttieres de 24 px) depassait deja la carte de
   22 px a 720 px, et de 40 px avec le badge « One Medium video » ; plus rien ne
   deborde a partir de 800 px. En dessous, le bloc s'empile, comme sur telephone. */
@media (min-width: 860px) { .n-start { grid-template-columns: 1fr auto 1fr auto 1.4fr; gap: var(--n-sp-5); padding: var(--n-sp-6); } }
.n-start__col { display: grid; gap: var(--n-sp-2); justify-items: start; }
.n-start__num { display: flex; align-items: center; gap: var(--n-sp-2); font-size: var(--n-fs-h2); font-weight: var(--n-fw-medium); line-height: 1; color: var(--n-ink); font-variant-numeric: tabular-nums; }
.n-start__num small { font-size: var(--n-fs-xs); font-weight: var(--n-fw-normal); color: var(--n-text-3); }
.n-start__desc { font-size: var(--n-fs-sm); color: var(--n-text-3); }
.n-start__op { font-size: var(--n-fs-h2); line-height: 1; color: var(--n-text-3); text-align: center; }
.n-start__title { font-size: var(--n-fs-h3); font-weight: var(--n-fw-medium); color: var(--n-ink); }
.n-start__col--result .n-btn { margin-block-start: var(--n-sp-1); }

.n-levels { display: grid; gap: var(--n-sp-3); }
/* NUM_MEDIUM_25_CREDITS_12092026 · ⚠️ QUATRE niveaux — et ce chiffre commande
   ces `repeat()`. LA REGLE, payee le 12/09/2026 : le nombre de colonnes doit
   DIVISER le nombre d'elements — 4 → 2x2 ou 4x1, jamais 3+1, sinon le dernier
   niveau tombe SEUL sur une ligne, a un tiers de largeur, et se lit comme un
   oubli. Repasser a `repeat(3, …)` le jour ou un niveau s'eteint. */
@media (min-width: 640px) { .n-levels { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1000px) { .n-levels { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
/* NUM_MEDIUM_EXEMPLES_12092026 · la phrase qui dit que les exemples du site
   ne sont PAS rendus en Medium. Discrete mais lisible : c'est une mise en
   garde, pas une note de bas de page a ignorer. */
/* ⚠️ PAS `.n-note` : celle-la est une grille a deux colonnes (icone + texte),
   et chaque <b> y devient un element de grille qui part a la ligne. Une
   phrase se compose en `display: block`, point. */
.n-levels__note { display: block; margin-block-start: var(--n-sp-3); font-size: var(--n-fs-xs); color: var(--n-text-3); line-height: 1.55; max-inline-size: 68ch; }
.n-levels__note b { color: var(--n-text-2); font-weight: var(--n-fw-medium); }
.n-lead--fine { font-size: var(--n-fs-sm); color: var(--n-text-3); margin-block-start: var(--n-sp-2); }
.n-level { display: grid; gap: var(--n-sp-1); padding: var(--n-sp-4) var(--n-sp-5); border-radius: var(--n-r-lg); background: var(--n-surface); border: 1px solid var(--n-line); }
.n-level__name { font-size: var(--n-fs-sm); font-weight: var(--n-fw-medium); color: var(--n-ink); }
.n-level__credits { font-size: var(--n-fs-h3); font-weight: var(--n-fw-medium); color: var(--n-ink); font-variant-numeric: tabular-nums; }
.n-level__credits small { font-size: var(--n-fs-xs); font-weight: var(--n-fw-normal); color: var(--n-text-3); margin-inline-start: 4px; }
.n-level__desc { font-size: var(--n-fs-xs); color: var(--n-text-3); }
/* NUM_DUREE_AU_CHOIX_23092026 · LE PRIX PAR DUREE, dans chaque carte de
   niveau : quatre cases, 5 / 10 / 15 / 20 s, la duree en legende au-dessus du
   nombre de credits. Quatre colonnes a toutes les largeurs — la carte la plus
   etroite (4 niveaux cote a cote a 1000 px) laisse ~37 px par case, assez pour
   « 139 » ; `minmax(0, 1fr)` interdit a un nombre de pousser la carte. Un
   « — » tient la case d'une duree que le niveau ne vend pas : les colonnes
   restent alignees d'une carte a l'autre. Une duree de plus = un `repeat(5,…)`
   ici ET une case dans pricing.html. */
.n-level__durees { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--n-sp-2); margin-block: var(--n-sp-1); }
.n-level__duree { display: grid; gap: 2px; min-inline-size: 0; }
.n-level__duree dt { font-size: var(--n-fs-xs); color: var(--n-text-3); white-space: nowrap; }
.n-level__duree dd { font-size: var(--n-fs-h4); font-weight: var(--n-fw-medium); color: var(--n-ink); line-height: 1.2; font-variant-numeric: tabular-nums; }

/* Le pan cache par le segment : jamais `hidden` ecrase, une classe. */
.n-pane[hidden] { display: none !important; }

/* == 13 bis · LE TARIF SUR MESURE · NUM_TARIF_BUSINESS_03092026 =================
   La cinquieme offre de la page des tarifs : elle ne rentre pas dans la
   grille des quatre cartes (elle n'a ni prix ni bouton de paiement), et
   elle ne se met pas non plus dans les lignes de packs. Un bloc a part,
   deux colonnes des 900 px : le prix et les deux acces a gauche, ce que
   couvre le sur-mesure a droite.
   Elle reprend `.n-card-plan__amount` et `.n-custom__point` telles quelles :
   le mot « Custom » a le poids d'un prix, les quatre points ont la meme
   forme que sur custom.html. Rien de nouveau a maintenir.
   ========================================================================== */
.n-bizplan { display: grid; gap: var(--n-sp-6); }
@media (min-width: 900px) { .n-bizplan { grid-template-columns: 1fr 1fr; gap: var(--n-sp-8); align-items: start; } }
.n-bizplan__intro { display: grid; gap: var(--n-sp-3); align-content: start; }
.n-bizplan__price { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--n-sp-2); }
/* Les deux acces cote a cote, jamais etires : un bouton pleine largeur sur
   un ecran large ferait une barre, pas un bouton. */
.n-bizplan__cta { display: flex; flex-wrap: wrap; gap: var(--n-sp-3); margin-block-start: var(--n-sp-2); }

/* == 14 · FORMULAIRE SUR MESURE ============================================ */
.n-custom { display: grid; gap: var(--n-sp-6); }
@media (min-width: 900px) { .n-custom { grid-template-columns: 1fr 1.1fr; gap: var(--n-sp-8); align-items: start; } }
.n-custom__points { display: grid; gap: var(--n-sp-4); }
.n-custom__point { display: grid; grid-template-columns: 40px 1fr; gap: var(--n-sp-3); align-items: start; }
.n-custom__point svg { inline-size: 40px; block-size: 40px; padding: 9px; border-radius: 50%; background: var(--n-surface-2); color: var(--n-ink); }
.n-custom__point b { display: block; font-size: var(--n-fs-body); font-weight: var(--n-fw-medium); color: var(--n-ink); }
.n-custom__point span { font-size: var(--n-fs-sm); color: var(--n-text-2); }

/* == 15 · PIED DE PAGE ====================================================== */
.n-footer { margin-block-start: var(--n-sp-8); padding-block: var(--n-sp-8) var(--n-sp-6); border-block-start: 1px solid var(--n-line); }
.n-footer__grid { display: grid; gap: var(--n-sp-6); }
@media (min-width: 720px) { .n-footer__grid { grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: var(--n-sp-6); } }
.n-footer__brand .n-brand { block-size: 22px; color: var(--n-ink); }
.n-footer__pitch { margin-block-start: var(--n-sp-4); font-size: var(--n-fs-sm); color: var(--n-text-2); max-inline-size: 34ch; }
.n-footer__title { font-size: var(--n-fs-eyebrow); font-weight: var(--n-fw-medium); letter-spacing: var(--n-ls-caps); text-transform: uppercase; color: var(--n-text-3); margin-block-end: var(--n-sp-3); }
.n-footer__list { display: grid; gap: 0; justify-items: start; }
.n-footer__link { display: inline-flex; align-items: center; min-block-size: 44px; font-size: var(--n-fs-sm); color: var(--n-text-2); }
@media (hover: hover) { .n-footer__link:hover { color: var(--n-ink); } }
.n-footer__bottom { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--n-sp-3); margin-block-start: var(--n-sp-7);
  padding-block-start: var(--n-sp-5); border-block-start: 1px solid var(--n-line); font-size: var(--n-fs-xs); color: var(--n-text-3); }
.n-footer__by { display: inline-flex; align-items: center; min-block-size: 44px; color: var(--n-text-2); text-decoration: underline; text-decoration-color: var(--n-line-2); text-underline-offset: 0.2em; }
@media (hover: hover) { .n-footer__by:hover { color: var(--n-ink); text-decoration-color: currentColor; } }
/* NUM_MENU_UNIQUE_03092026 · LA RESERVE DU BAS : UNE SEULE, JAMAIS DEUX.
   Sous 1100 px c'est la barre d'onglets qui occupe le bas — et whatsapp.js
   efface alors la pastille tout seul (`garderLaPlace` la retire des que son
   rectangle croise une commande visible, et une barre de 0 a 64 px la croise
   toujours). Au-dessus, la barre disparait et c'est la pastille qui reserve.
   Les deux cas ne se rencontrent jamais : cumuler les deux reserves
   decollerait le pied de 120 px pour rien. */
@media (max-width: 1279.98px) {
  .n-footer { padding-block-end: calc(var(--n-sp-6) + var(--n-tabbar-h)); }
}
@media (min-width: 1280px) {
  body.n-has-whatsapp .n-footer { padding-block-end: calc(var(--n-sp-6) + 56px + env(safe-area-inset-bottom, 0px)); }
}
.n-whatsapp[hidden] { display: none !important; }

/* Les pages de texte (conditions, confidentialite, moderation). */
.n-page { padding-block: calc(var(--n-header-h) + var(--n-sp-7)) var(--n-sp-8); }
.n-page__head { margin-block-end: var(--n-sp-7); }
.n-page__head > * + * { margin-block-start: var(--n-sp-3); }
.n-prose { max-inline-size: var(--n-measure); color: var(--n-text-2); }
.n-prose h2 { font-size: var(--n-fs-h3); font-weight: var(--n-fw-medium); color: var(--n-ink); letter-spacing: var(--n-ls-h3); margin-block: var(--n-sp-7) var(--n-sp-3); scroll-margin-top: calc(var(--n-header-h) + 16px); }
.n-prose h3 { font-size: var(--n-fs-h4); font-weight: var(--n-fw-semi); color: var(--n-ink); margin-block: var(--n-sp-5) var(--n-sp-2); }
.n-prose p + p, .n-prose ul + p, .n-prose p + ul { margin-block-start: var(--n-sp-3); }
.n-prose ul, .n-prose ol { padding-inline-start: 1.2em; display: grid; gap: var(--n-sp-1); }
.n-prose li::marker { color: var(--n-text-3); }
.n-prose a { color: var(--n-ink); text-decoration: underline; text-decoration-color: var(--n-line-2); text-underline-offset: 0.18em; }
.n-prose a:hover { text-decoration-color: currentColor; }
.n-prose strong, .n-prose b { color: var(--n-ink); font-weight: var(--n-fw-medium); }
.n-prose table { font-size: var(--n-fs-sm); }
.n-prose th, .n-prose td { padding: var(--n-sp-2) var(--n-sp-3); border-block-end: 1px solid var(--n-line); text-align: start; vertical-align: top; }
.n-prose th { color: var(--n-ink); font-weight: var(--n-fw-medium); }
.n-toc { display: flex; flex-wrap: wrap; gap: var(--n-sp-2); margin-block-end: var(--n-sp-6); }
.n-toc a { display: inline-flex; align-items: center; min-block-size: 44px; padding: 0 var(--n-sp-3); border-radius: var(--n-r-pill);
  background: var(--n-surface); border: 1px solid var(--n-line); font-size: var(--n-fs-xs); color: var(--n-text-2); }
@media (hover: hover) { .n-toc a:hover { color: var(--n-ink); border-color: var(--n-line-2); } }

/* == 18 · WHATSAPP =========================================================
   Un lien nu, pose par whatsapp.js sur <body>. Noir, en pilule, en bas a
   droite ; l'etiquette se masque sous 380 px ; le script l'efface quand il
   recouvrirait une commande. Rien n'y bouge tout seul.
   ========================================================================== */
.n-whatsapp {
  position: fixed; inset-block-end: max(16px, env(safe-area-inset-bottom)); inset-inline-end: 16px; z-index: var(--n-z-sticky);
  display: inline-flex; align-items: center; gap: var(--n-sp-2); min-block-size: 48px; padding: 0 var(--n-sp-4) 0 var(--n-sp-3);
  border-radius: var(--n-r-pill); background: var(--n-ink); color: #fff; box-shadow: var(--n-shadow-2);
  font-size: var(--n-fs-sm); font-weight: var(--n-fw-medium); white-space: nowrap;
  transition: background-color var(--n-dur-2) var(--n-ease);
}
@media (hover: hover) { .n-whatsapp:hover { background: #222226; } }
.n-whatsapp__ico { inline-size: 22px; block-size: 22px; }
@media (max-width: 379px) { .n-whatsapp { padding-inline: 13px; } .n-whatsapp__label { display: none; } }
@media (min-width: 720px) { .n-whatsapp { inset-block-end: 24px; inset-inline-end: 24px; } }

/* == 19 · LE BANDEAU DE CONSENTEMENT (MICROSOFT CLARITY) ===================
   Pose le 03/09/2026 avec clarity.js. C'est le PREMIER tiers de Numeri, et
   c'est pour lui que le site a desormais un bandeau — CONTRAT-FRONT.md §2
   dit l'exception.

   Le bandeau n'est pas une modale : il ne voile rien, ne bloque rien, ne
   prend pas le focus. Il se pose en bas a gauche, en carte, et prend toute
   la largeur sous 640 px. Les deux boutons ont le MEME dessin — un « Accept »
   plus voyant qu'un « Refuse » est ce que la CNIL appelle un dark pattern.

   ⚠️ z-index = --n-z-modal (70) : au-dessus du tiroir mobile (60), sous le
   lien d'evitement et les toasts (80). Le bouton WhatsApp (20) passe dessous
   et se retire tout seul s'il recouvre un des deux boutons — c'est mesure
   par whatsapp.js, pas suppose.

   ⚠️ RIEN NE BOUGE TOUT SEUL : aucune entree animee, aucun glissement.
   ========================================================================= */

.n-consent {
  position: fixed;
  inset-block-end: calc(var(--n-sp-8) + env(safe-area-inset-bottom, 0px));
  inset-inline-start: calc(var(--n-sp-8) + env(safe-area-inset-left, 0px));
  z-index: var(--n-z-modal);
  inline-size: min(26rem, calc(100vw - 2 * var(--n-sp-8)));
  padding: var(--n-sp-6);
  border: 1px solid var(--n-line-2); border-radius: var(--n-r-lg);
  background: var(--n-surface); color: var(--n-text);
  box-shadow: var(--n-shadow-3);
}
.n-consent__title { margin-block-start: var(--n-sp-4); font-size: var(--n-fs-h4);
                    font-weight: var(--n-fw-medium); letter-spacing: -0.01em; line-height: 1.25; }
.n-consent__text  { margin-block-start: var(--n-sp-5); font-size: var(--n-fs-xs);
                    line-height: 1.6; color: var(--n-text-2); }
.n-consent__actions { display: flex; flex-wrap: wrap; align-items: center;
                      gap: var(--n-sp-3); margin-block-start: var(--n-sp-5); }
.n-consent__link  { margin-inline-start: auto; font-size: var(--n-fs-xs); }

@media (max-width: 639.98px) {
  .n-consent {
    inset-inline-start: 0; inset-block-end: 0; inline-size: 100%;
    border-radius: var(--n-r-lg) var(--n-r-lg) 0 0; border-inline: 0; border-block-end: 0;
    padding-block-end: calc(var(--n-sp-8) + env(safe-area-inset-bottom, 0px));
  }
  .n-consent__actions .n-btn { flex: 1 1 40%; }
  .n-consent__link { flex-basis: 100%; margin-inline-start: 0; }
}

/* L'etat courant sur la page Privacy : une ligne mono, comme les specs. */
.n-consent-state { font-size: var(--n-fs-xs);
                   color: var(--n-text-2); }

/* -- 19 bis · le bandeau, compact au doigt, et deux VRAIS boutons ----------
   Recette du 03/09/2026 (joseph-10) : a 390 px le bandeau couvrait le bouton
   principal ; « Refuse » et « Accept » etaient deux mots nus. Meme dessin
   pour les deux (CNIL), en pilule blanche a filet, 44 px. */
.n-consent { padding: var(--n-sp-5); inline-size: min(24rem, calc(100vw - 2 * var(--n-sp-5))); }
.n-consent__title { margin-block-start: 0; font-size: var(--n-fs-body); }
.n-consent__text { margin-block-start: var(--n-sp-2); font-size: var(--n-fs-xs); line-height: 1.45; }
.n-consent__actions { margin-block-start: var(--n-sp-3); gap: var(--n-sp-2); }
.n-consent__actions .n-btn { --btn-bg: var(--n-surface); --btn-line: var(--n-line-strong); --btn-fg: var(--n-ink); min-block-size: 44px; padding-inline: var(--n-sp-5); font-size: var(--n-fs-sm); }
.n-consent__link { display: inline-flex; align-items: center; min-block-size: 44px; }
@media (max-width: 639.98px) {
  .n-consent { padding: var(--n-sp-4) var(--n-sp-4) calc(var(--n-sp-4) + env(safe-area-inset-bottom, 0px)); }
  .n-consent__text { display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
  .n-consent__actions .n-btn { flex: 1 1 40%; }
}

/* == 19 ter · LE BANDEAU NE S'ASSOIT PAS SUR LE MENU · NUM_MENU_UNIQUE_03092026
   Le bandeau est a --n-z-modal (70), la barre du bas a --n-z-nav (50), et sous
   640 px il prend toute la largeur en bas de l'ecran. Sans cette regle il la
   couvre EN ENTIER : un premier visiteur sur telephone — 61 % du trafic, et
   le bandeau s'ouvre tant qu'aucun choix n'est memorise — n'a plus aucune
   navigation atteignable au doigt, et un Tab pose son anneau de focus
   derriere le bandeau (WCAG 2.4.11 Focus Not Obscured).

   Il se pose AU-DESSUS de la barre, pas dessus. La zone sure est deja payee
   par la barre : on retire le `padding-block-end` de la section 19 bis, qui
   la paierait une seconde fois.

   ⚠️ CETTE SECTION DOIT RESTER LA DERNIERE DU FICHIER : la section 19 bis
      pose `padding` en raccourci sous 640 px, une regle ecrite plus haut
      serait ecrasee. */
@media (max-width: 1279.98px) {
  .n-consent { inset-block-end: var(--n-tabbar-h); }
}
@media (max-width: 639.98px) {
  .n-consent {
    padding-block-end: var(--n-sp-4);
    border-start-start-radius: var(--n-r-lg); border-start-end-radius: var(--n-r-lg);
  }
}

/* Une valeur insecable dans une fiche de faits — une adresse e-mail ne casse
   qu'apres son trait d'union — deborde la piste de 120 px sous 640 px, et
   `overflow-x: clip` la coupe en silence. La cle passe donc AU-DESSUS. */
@media (max-width: 639.98px) {
  .n-facts__row { grid-template-columns: 1fr; gap: var(--n-sp-1); }
}
.n-facts__v { overflow-wrap: anywhere; }

/* == 19 quater · LE BANDEAU NE COUVRE PLUS LE PREMIER GESTE ================
   NUM_CONSENT_NE_COUVRE_PAS_03092026

   Releve le 03/09/2026 sur /app a froid, telephone de 390 x 844 :
   `elementFromPoint()` au centre du bouton « Choose photos » rendait
   `SECTION.n-consent`. Le bouton s'etend de 494 a 542 px, le bandeau
   commencait a 516 — 264 px de haut, 31 % de l'ecran. Le premier geste du
   produit ne recevait pas le clic. Le meme matin : 26 comptes crees, UN
   SEUL depot de photo, ZERO rendu.

   1 · LE BANDEAU MAIGRIT. Deux lignes de texte au lieu de quatre, et
       « Privacy » sur la ligne des boutons au lieu d'une rangee de 44 px
       pour lui seul. La levre haute repasse sous le bouton.

   2 · LA PAGE RESERVE SA PLACE. `n-has-consent` etait posee sur `<body>`
       par clarity.js depuis le debut et AUCUNE regle ne la lisait. Elle
       reserve maintenant la hauteur REELLE du bandeau — le jour ou le
       texte grandira, rien ne restera prisonnier dessous.

   ⚠️ AUCUN `padding` EN RACCOURCI ICI : la section 19 ter pose
      `padding-block-end` sous 640 px, un raccourci ecrit plus bas
      l'effacerait. Meme raison qui lui faisait exiger d'etre la derniere. */
@media (max-width: 639.98px) {
  .n-consent__text { -webkit-line-clamp: 2; }
  .n-consent__actions .n-btn { flex: 1 1 auto; }
  .n-consent__link { flex-basis: auto; margin-inline-start: auto; }
}

body.n-has-consent { padding-block-end: var(--n-consent-h, 0px); }

/* == 19 quinquies · UN ECRAN COURT N'EST PAS UN ECRAN ETROIT ===============
   NUM_DEPOT_AU_DOIGT_SUITE_03092026

   Toutes les regles du bandeau se decident sur la LARGEUR. Mais ce qu'il
   recouvre depend de la HAUTEUR : un telephone large et court — paysage,
   fenetre partagee, vieil Android — est le cas ou il mord. Sous 700 px de
   haut, le texte tient donc en une ligne au lieu de deux.

   ⚠️ DERNIERE SECTION DU FICHIER, pour la meme raison que la 19 ter : les
      regles de la 19 bis posent `padding` en raccourci sous 640 px. */
@media (max-height: 699.98px) {
  .n-consent__text { -webkit-line-clamp: 1; }
}

/* == 19 sexies · LE BANDEAU REPREND TOUTE LA LARGEUR ======================
   NUM_CONSENT_PLEINE_LARGEUR_03092026

   La section 19 posait `inline-size: 100%` sous 640 px. La 19 bis a repose
   `min(24rem, …)` SANS media query, donc pour le telephone aussi : une carte
   de 312 px collee a gauche et 48 px de vide a droite. On rend la largeur. */
@media (max-width: 639.98px) {
  .n-consent { inline-size: auto; inset-inline: 0; }
}

/* == 12 bis · CE SUR QUOI ON PEUT COMPTER · NUM_CONVERSION_04092026 =====================
   Trois faits sous les etapes de l'accueil : fidelite, propriete, echec.
   Des cartes basses, une puce dans la rampe, le fait en encre et son
   explication en gris. Pas une carte dans une carte : elles sont soeurs
   des etapes, pas leurs filles.
   ======================================================================== */
.n-assure { display: grid; gap: var(--n-sp-3); }
@media (min-width: 720px) { .n-assure { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--n-sp-4); } }
.n-assure__item { position: relative; padding: var(--n-sp-4) var(--n-sp-4) var(--n-sp-4) calc(var(--n-sp-4) + 18px);
  border-radius: var(--n-r-lg); background: var(--n-surface); border: 1px solid var(--n-line);
  font-size: var(--n-fs-sm); color: var(--n-text-2); line-height: 1.5; }
.n-assure__item::before { content: ""; position: absolute; inset-inline-start: var(--n-sp-4); inset-block-start: calc(var(--n-sp-4) + 0.5em);
  inline-size: 8px; block-size: 8px; border-radius: 50%; background: var(--n-grad); }
.n-assure__item b { display: block; color: var(--n-ink); font-weight: var(--n-fw-medium); margin-block-end: 2px; }

/* « Terms » dans le pied faisait 43 px de large : la cible au doigt est 44
   (WCAG 2.5.5). Le lien est deja en inline-flex de 44 px de haut. */
.n-footer__link { min-inline-size: 44px; }

/* Le bandeau de consentement sur telephone : le surtitre « Cookies » part,
   le titre « Help us see what works? » suffit — une ligne de moins devant
   le heros. Ne pose AUCUN padding : la 19 ter/quater vivent au-dessus.
   (NUM_CONVERSION_04092026) */
@media (max-width: 639.98px) { .n-consent .n-eyebrow { display: none; } }

/* == 19 septies · « LOG OUT » TIENT DANS LA BARRE A 390 PX ================
   NUM_PARCOURS_CLIENT_04092026

   Releve le 04/09/2026, /app connecte, telephone 390 x 844 : logo + solde
   + « Create » + « Log out » = 431 px. Le bouton de sortie s'etendait de
   319 a 430 px, « out » etait coupe et la moitie du bouton hors ecran.
   Sous 640 px le libelle se cache ; l'icone reste, et `aria-label` sur le
   bouton garde son nom pour la lecture d'ecran. */
@media (max-width: 639.98px) {
  .n-nav__logout-label { display: none; }
  .n-nav__side [data-logout] { padding-inline: var(--n-sp-3); }
}

/* == 19 septies · L'ANDROID COMPACT (360 x 640) ===========================
   NUM_MUR_AU_BOUTON_04092026

   `outils/controle-bandeau-mobile.mjs` le 04/09/2026 : sur 360 x 640 le
   bandeau fait 136 px et couvre « Choose photos » ([463, 511]) ; les quatre
   autres ecrans passent. Sous 380 px de large, le surtitre et le titre
   s'effacent — le texte et les boutons restent, l'information de
   consentement aussi. Derniere section du fichier, meme raison que 19 ter. */
@media (max-width: 379.98px) {
  .n-consent .n-eyebrow, .n-consent__title { display: none; }
  .n-consent__text { margin-block-start: 0; -webkit-line-clamp: 1; }
  .n-consent__actions { margin-block-start: var(--n-sp-2); }
  .n-consent__actions .n-btn, .n-consent__link { min-block-size: 40px; }
}

/* == NUM_CRO_04092026 · AUDIT DU PARCOURS D'ACHAT, 04/09/2026 =================
   Voir outils/patch-cro-04092026.py.
   · une pilule desactivee garde un filet : sinon « Generate » gris se lit
     comme du texte ;
   · les boutons secondaires des cartes de prix et du mur ne sont plus des
     fantomes : un filet, pour qu'on sache qu'ils se pressent ;
   · les lignes de pack sur telephone : « Buy » recouvrait le texte ;
   · les cases des trois etapes de l'accueil et le bandeau cookies, plus bas
     sur telephone. */
.n-btn--primary[disabled], .n-btn--primary[aria-disabled="true"], .n-btn--primary:disabled {
  --btn-line: var(--n-line-2); --btn-bg: var(--n-surface-2);
}
.n-btn--ghost.n-btn--block, .n-btn--ghost.n-btn--lg, .app-block-note .n-btn--ghost {
  --btn-bg: var(--n-surface); --btn-line: var(--n-line-2);
}
@media (max-width: 639px) {
  .n-row { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "name price" "meta meta" "cta cta"; }
  .n-row__cta { justify-self: stretch; }
  .n-row__cta .n-btn { inline-size: 100%; }
  .n-step__art { block-size: 64px; }
  .n-step__art svg { inline-size: 32px; block-size: 32px; }
  .n-consent { padding-block-start: var(--n-sp-3); }
  .n-consent__title { font-size: var(--n-fs-sm); }
  .n-consent__text { margin-block-start: var(--n-sp-1); }
  .n-consent__actions { margin-block-start: var(--n-sp-2); }
  .n-consent__actions .n-btn { min-block-size: 40px; }
  .n-consent__link { min-block-size: 40px; }
  /* Le texte du bandeau ne se coupe plus : une phrase de consentement tronquee
     par une ellipse n'informe pas. Trois lignes, en entier. */
  .n-consent__text { display: block; -webkit-line-clamp: unset; overflow: visible; }
}

/* == 20 · LA CONVERSION DU 23/09/2026 · NUM_CONVERSION_23092026 ==============
   Audit du 23/09/2026 (vitrine C06, C11, C13 ; tarifs C4). Ce qui suit :
     · les clics morts : un tap sur un film joue le son (numeri.js), les cartes
       de la bande et des formats, le cote photo des exemples menent au studio ;
     · la demo sur telephone : la figure AVANT les rangees de boutons, et
       chaque rangee sur une seule ligne qui defile dans son propre conteneur
       (jamais la page : CONTRAT-FRONT regle 7) ;
     · la page tarifs : la video seule (.n-unite) et le bloc replie « How
       credits work » (.n-plie).
   ⚠️ Rien ici n'est indispensable a la lecture : une page servie avec la
      feuille d'avant (meme ?v= en cache) reste juste, seulement moins jolie.
   ========================================================================== */

/* Le film se touche : le curseur le dit. */
.n-solo__frame, .n-demo__apres, .n-ex__apres { cursor: pointer; }

/* La bande : chaque carte est un lien qui couvre toute la carte. L'etiquette
   « Make this with my product » n'apparait qu'au survol ou au clavier : au
   doigt, le tap ouvre le studio directement. */
.n-bande__lien { position: absolute; inset: 0; display: block; border-radius: inherit; color: inherit; }
.n-bande__lien:focus-visible { outline: 2px solid var(--n-accent); outline-offset: -2px; }
.n-bande__cta { position: absolute; z-index: 2; inset-inline: var(--n-sp-2); inset-block-end: var(--n-sp-2);
  padding: 6px 10px; border-radius: var(--n-r-md); background: rgba(255, 255, 255, 0.92); color: var(--n-ink);
  font-size: var(--n-fs-xs); font-weight: var(--n-fw-medium); line-height: 1.3; text-align: center;
  opacity: 0; transform: translateY(4px); pointer-events: none;
  transition: opacity var(--n-dur-2) var(--n-ease), transform var(--n-dur-2) var(--n-ease); }
@media (hover: hover) { .n-bande__lien:hover .n-bande__cta { opacity: 1; transform: none; } }
.n-bande__lien:focus-visible .n-bande__cta { opacity: 1; transform: none; }

/* Les exemples : le cote photo est un <a> (deja un element de grille), la
   legende porte le lien texte, pousse a droite, cible de 44 px. */
a.n-ex__avant { display: block; color: inherit; }
a.n-ex__avant:focus-visible { outline: 2px solid var(--n-accent); outline-offset: 2px; }
.n-ex__lien { display: inline-flex; align-items: center; gap: 4px; min-block-size: 44px; margin-inline-start: auto;
  color: var(--n-accent); font-weight: var(--n-fw-medium); }
.n-ex__lien svg { inline-size: 16px; block-size: 16px; }
@media (hover: hover) { .n-ex__lien:hover { text-decoration: underline; text-underline-offset: 3px; } }

/* Les formats : un lien etire sur toute la carte (le texte reste lisible tel
   quel, le nom du lien est dans un .n-sr-only). */
.n-format { position: relative; }
.n-format__lien { position: absolute; inset: 0; border-radius: inherit; }
.n-format__lien:focus-visible { outline: 2px solid var(--n-accent); outline-offset: 2px; }
@media (hover: hover) { .n-format:hover { border-color: var(--n-line-2); } }

/* La demo sur telephone (audit C11) : la preuve arrivait a y = 1 787 px, apres
   trois rangees de boutons. Sous 760 px, le conteneur passe en colonne flexible
   et la figure remonte juste sous le titre ; les rangees suivent, chacune sur
   une ligne qui defile au doigt. Le DOM ne bouge pas : au clavier et au lecteur
   d'ecran, on choisit toujours avant de voir. */
@media (max-width: 759.98px) {
  .n-demo > .n-container { display: flex; flex-direction: column; }
  .n-demo .n-section__head { order: -2; }
  .n-demo__fig { order: -1; margin-block-end: var(--n-sp-4); }
  .n-demo__choix { margin-block-end: 0; }
  .n-demo__seg { flex-wrap: nowrap; justify-self: stretch; inline-size: 100%; overflow-x: auto;
    padding: 4px 2px; overscroll-behavior-x: contain; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  .n-demo__seg::-webkit-scrollbar { display: none; }
  .n-demo__seg .n-seg__btn { flex: none; }
}

/* La video seule, sur la page tarifs : le texte a gauche, le bouton a droite
   des 640 px ; empiles en dessous. Meme largeur que le bandeau du premier mois.
   NUM_GRILLE_CLAUDE_24092026 · plus aucune page ne s'en sert : le bloc d'essai a quitte
   pricing.html (SPEC 2, Lite le remplace : `.n-card-plan--entry`). Regles gardees, sans
   effet, pour le jour ou l'essai revient (son code reste, eteint). */
.n-unite { display: grid; gap: var(--n-sp-4); align-items: center; max-inline-size: 44rem; margin-inline: auto; }
.n-unite__texte { display: grid; gap: var(--n-sp-2); }
.n-unite .n-btn { justify-self: start; }
@media (min-width: 640px) {
  .n-unite { grid-template-columns: minmax(0, 1fr) auto; }
  .n-unite .n-btn { justify-self: end; }
}

/* « How credits work » : un <details> natif, aucun script (numeri.js ne fait
   que l'ouvrir quand une ancre vise son contenu). Le titre est une cible pleine
   largeur de 56 px ; le « + » tourne en « × » a l'ouverture. */
.n-plie { border-block: 1px solid var(--n-line); }
.n-plie__titre { display: flex; align-items: center; justify-content: center; gap: var(--n-sp-2);
  min-block-size: 56px; padding-block: var(--n-sp-3); cursor: pointer; list-style: none;
  font-size: var(--n-fs-h4); font-weight: var(--n-fw-medium); color: var(--n-ink); }
.n-plie__titre::-webkit-details-marker { display: none; }
.n-plie__titre::marker { content: ""; }
.n-plie__titre svg { inline-size: 20px; block-size: 20px; flex: none; transition: transform var(--n-dur-2) var(--n-ease); }
.n-plie[open] > .n-plie__titre svg { transform: rotate(45deg); }
.n-plie__corps { padding-block: var(--n-sp-3) var(--n-sp-6); }
@media (prefers-reduced-motion: reduce) {
  .n-bande__cta, .n-plie__titre svg { transition: none; }
}


/* == 21 · ERGONOMIE DE LA VITRINE · NUM_ERGONOMIE_24092026 ==================
   Audit d'ergonomie du 24/09/2026 (captures reelles 375 x 812 et 1280 x 800,
   sept angles, contre-expertise « contraintes » et « client »), applique par
   trois groupes : a-accueil (index.html), b-aide (help.html, custom.html),
   c-commun (barre du bas, en-tete, pied de page). Recolle EN FIN DE FICHIER
   expres : chaque regle gagne a specificite egale sur ce qui precede, et
   reste dans le @media de la regle qu'elle corrige. Rien ici ne vise /app
   (qui charge aussi ce fichier) : selecteurs propres a la vitrine, ou
   prefixes (#how, #formats, #examples, :has(a.…--go)).
   ========================================================================== */

/* NUM_ERGONOMIE_24092026 · a-accueil — L'ACCUEIL (index.html) PLUS COURT ET PLUS VITE LISIBLE ====
   Audit d'ergonomie du 24/09/2026, groupe A (plan : E01, E02, E05, E10, E11,
   E12, E14, E16, E19, E20, E24, E27, E39). Fragment recolle A LA FIN de
   numeri.css : chaque regle gagne sur celles de meme specificite plus haut,
   y compris celles rangees dans un @media — d'ou les @media ci-dessous, poses
   expres pour ne rien ecraser hors de leur plage.
   Tout est propre a la vitrine : .n-hero, .n-solo*, .n-demo*, .n-ex*, .n-bande*,
   .n-final__note, .n-steps, .n-formats, #how, #examples, #formats n'existent
   pas dans /app (app.html, app.css). .n-step et .n-format__box y existent :
   ils ne sont touches qu'avec le prefixe #how / #formats ou le parent
   .n-steps / .n-formats.
   ⚠️ Le commentaire du § 10 de numeri.css (« une phrase, le bouton, la note ;
      puis le film ») decrit l'ordre d'AVANT : depuis E02, le film suit
      directement le bouton et les notes passent dessous (index.html).
   ========================================================================== */

/* E02 · LE FILM JUSTE APRES LE BOUTON. Il ne suit plus trois notes grises :
   l'ecart au bouton passe de 64 a 32 px (24 px sur telephone, plus bas). */
.n-solo__stage { margin-block-start: var(--n-sp-6); }
/* E02 · « See it work on a sample product » etait coupe sur deux lignes : le
   lien passe d'un bloc a la ligne suivante s'il ne tient pas. Le padding
   agrandit la cible au doigt sans bouger la ligne (marge negative egale). */
.n-solo__note .n-link { display: inline-block; max-inline-size: 100%; padding-block: 4px; margin-block: -4px; }

/* E01 · HEROS MOBILE COMPACT : LE BOUTON A 4,90 $ PASSE AU-DESSUS DU BANDEAU
   COOKIES. Dans la webview Instagram (~375 x 672, 68 % des vues), le bandeau
   de premiere visite (y 470 → 608) recouvrait le bouton (y 449 → 505). En
   cause : 64 px vides sous l'en-tete, un h1 de 40 px sur 3 lignes, un chapo de
   17,7 px sur 5 lignes et des marges de 24 et 32 px. Le h1 reste non anime
   (LCP) ; la baseline passe de 40 a 32 px sur telephone seulement. Le bandeau
   n'est pas touche.
   E02 · sous le bouton, le film a 24 px (et non plus 64). */
@media (max-width: 719.98px) {
  .n-hero { padding-block: calc(var(--n-header-h) + var(--n-sp-5)) var(--n-sp-7); }
  .n-solo__title { font-size: clamp(2rem, 1.4rem + 2.6vw, 2.5rem); }
  .n-solo__lead { margin-block-start: var(--n-sp-3); font-size: var(--n-fs-body); line-height: 1.5; }
  .n-solo__cta { margin-block-start: var(--n-sp-5); }
  .n-solo__stage { margin-block-start: var(--n-sp-5); }
}

/* E14 · HEROS SUR ORDINATEUR : a 1280 x 800, le film commencait pile sur la
   ligne de flottaison (y 798). Version simple retenue (pas de heros en deux
   colonnes) : on resserre la verticale — 48 px sous l'en-tete au lieu de 96,
   24 px entre le chapo et le bouton au lieu de 32 — et, avec E02, le film
   remonte d'environ 200 px dans le premier ecran. Le h1 garde ses 76 px. */
@media (min-width: 960px) {
  .n-hero { padding-block-start: calc(var(--n-header-h) + var(--n-sp-7)); }
  .n-solo__cta { margin-block-start: var(--n-sp-5); }
}

/* E11 · UN BOUTON APRES LES TROIS ETAPES (copie du bouton du heros, voir
   index.html) : 7,7 ecrans sans bouton plein entre la demo et la carte
   Pricing sur telephone. Centre sous les garanties ; pleine largeur sur
   telephone, comme au heros. */
#how .n-how__cta { display: flex; justify-content: center; margin-block-start: var(--n-sp-6); }
@media (max-width: 719.98px) { #how .n-how__cta .n-btn { inline-size: 100%; } }

/* E05 + E11 · LES APPELS PLEINE LARGEUR REVIENNENT A LA LIGNE sous 480 px,
   comme ceux du heros et de l'appel final (NUM_ESSAI_4_90_24092026, § 10 de
   numeri.css) : « Try it on your product — $4.90 » demande ~320 px en nowrap
   et sortirait du cadre des 360 px. Memes valeurs que la regle d'origine. */
@media (max-width: 479.98px) {
  .n-demo__cta .n-btn, .n-how__cta .n-btn { white-space: normal; padding-block: var(--n-sp-2); padding-inline: var(--n-sp-4); line-height: 1.25; text-wrap: balance; }
}

/* E05 · DEMO MOBILE : LA VIDEO RENDUE PASSE AVANT LA PHOTO DEPOSEE ET FAIT
   PLUS GRAND QU'ELLE. Empilee, la figure faisait 604 px : un puits photo 4:3
   de 306 x 230 PLUS GRAND que la video (306 x 172), une fleche, puis la
   legende. Meme principe que NUM_EXEMPLES_MOBILE_12092026 (c'est le rendu qui
   doit etre grand) : la photo devient une vignette carree de 96 px a cote de
   la legende, la video passe dessous, pleine largeur. La photo reste entiere
   (`contain`), la fleche est aria-hidden, le DOM ne bouge pas (les
   data-demo-* sont lus par attribut). Le chapo de la demo, lui, RESTE (il
   porte « on a sample product », affirmation de provenance). */
@media (max-width: 719.98px) {
  .n-demo__fig { grid-template-columns: 96px minmax(0, 1fr); column-gap: var(--n-sp-3); row-gap: var(--n-sp-3); }
  .n-demo__avant { grid-column: 1; grid-row: 1; aspect-ratio: 1; align-self: start; }
  .n-demo__avant .n-ex__tag { padding: 1px 6px; font-size: 10px; }
  .n-demo__fleche { display: none; }
  .n-demo__legende { grid-column: 2; grid-row: 1; padding: 0; align-self: center; }
  .n-demo__apres { grid-column: 1 / -1; grid-row: 2; }
  /* « Do this with my product » ne faisait que 269 px de large. */
  .n-demo__cta .n-btn { inline-size: 100%; }
}
/* E05 · 24 px au-dessus du surtitre de la demo au lieu de 40 : la section
   suit le heros, qui a deja sa marge basse. */
@media (max-width: 759.98px) {
  .n-demo { padding-block-start: var(--n-sp-5); }
}

/* E19 · PASTILLES PRODUCT / LOOK DE LA DEMO : elles etaient coupees net 21 px
   avant le bord de l'ecran (« Bags », « Prime Time »), sans indice de
   defilement, et l'anneau de focus de la premiere et de la derniere etait
   rogne (2 px de marge). La rangee defile desormais jusqu'au vrai bord de
   l'ecran, et la derniere pastille visible s'efface en fondu : « il y en a
   d'autres ». Sous 720 px seulement : entre 720 et 759 px, l'etiquette est
   a gauche (colonne de 5.5rem) et une marge negative la recouvrirait. Meme
   masque que .n-bande__piste. En arabe et en ourdou (dir="rtl"), la rangee
   deborde a gauche : le fondu passe a gauche. Aucune modification de
   numeri.js (cocher() est appelee au chargement : un scrollIntoView ferait
   defiler la page). `max-inline-size: none` : le `100 %` de la regle de base
   bornait la rangee a la largeur du conteneur, et la marge negative ne
   l'aurait etiree qu'a gauche (mesure : 0 → 332 px au lieu de 0 → 375). */
@media (max-width: 719.98px) {
  .n-demo__seg { inline-size: auto; max-inline-size: none; margin-inline: calc(-1 * var(--n-sp-gutter)); padding: 4px var(--n-sp-gutter);
    scroll-padding-inline: var(--n-sp-gutter);
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
    mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent); }
  [dir="rtl"] .n-demo__seg { -webkit-mask-image: linear-gradient(270deg, #000 calc(100% - 28px), transparent);
    mask-image: linear-gradient(270deg, #000 calc(100% - 28px), transparent); }
}

/* E10 · HOW IT WORKS : 2,1 ECRANS POUR 3 ETAPES sur telephone. Chaque carte
   empilait une pastille seule sur sa ligne, un panneau d'icone decoratif
   (aria-hidden) de 64 px, le titre puis le texte ; les trois garanties
   faisaient chacune leur carte. La pastille passe a gauche du titre, le
   panneau d'icone part, et les garanties se rangent dans UNE carte a filets.
   Tout le texte reste (dont la garantie de fidelite, LEGAL §7.1). Le prefixe
   #how est obligatoire : .n-step sert aussi a la progression du rendu dans
   /app. #how .n-assure l'emporte sur .n-mt-7 par sa specificite. */
@media (max-width: 719.98px) {
  #how .n-steps { gap: var(--n-sp-3); }
  #how .n-step { grid-template-columns: 40px minmax(0, 1fr); column-gap: var(--n-sp-4); row-gap: 2px; padding: var(--n-sp-4); align-items: start; }
  #how .n-step__num { grid-row: 1 / span 2; }
  #how .n-step__art { display: none; }
  #how .n-step__title { font-size: var(--n-fs-h4); align-self: center; }
  #how .n-assure { gap: 0; margin-block-start: var(--n-sp-5); background: var(--n-surface); border: 1px solid var(--n-line); border-radius: var(--n-r-lg); }
  #how .n-assure__item { border: 0; border-radius: 0; background: none; padding-block: var(--n-sp-3); }
  #how .n-assure__item + .n-assure__item { border-block-start: 1px solid var(--n-line); }
  #how .n-assure__item::before { inset-block-start: calc(var(--n-sp-3) + 0.5em); }
}

/* E12 · EXEMPLES AVANT / APRES : 3,1 ECRANS POUR 4 CARTES EMPILEES sur
   telephone, et le bouton Pause (WCAG 2.2.2) 2,5 ecrans sous le premier film.
   Les cartes se rangent sur UNE rangee qui defile au doigt, aimantee, la
   suivante depassant a droite pour dire qu'on peut glisser. Chaque carte
   garde photo ET video d'un seul coup d'oeil (NUM_EXEMPLES_MOBILE_12092026).
   Le defilement est dans son propre conteneur, jamais la page (CONTRAT-FRONT,
   regle 7). Une carte hors champ ne charge sa video qu'une fois glissee
   (observer par [data-ex-carte], numeri.js). */
@media (max-width: 719.98px) {
  #examples .n-ex { grid-auto-flow: column; grid-auto-columns: 84%; gap: var(--n-sp-3); align-items: start;
    overflow-x: auto; scroll-snap-type: x mandatory; overscroll-behavior-x: contain; scrollbar-width: none;
    margin-inline: calc(-1 * var(--n-sp-gutter)); padding-inline: var(--n-sp-gutter); scroll-padding-inline: var(--n-sp-gutter); }
  #examples .n-ex::-webkit-scrollbar { display: none; }
  #examples .n-ex__item { scroll-snap-align: start; }
  /* La carte PAYSAGE (chaussures) empilait photo 4:3, fleche et video : 513 px,
     la plus haute de la rangee, donc ~100 px de vide sous les trois cartes
     portrait avant la note. Dans la rangee, elle prend la grammaire de la demo
     (E05) : vignette carree de 96 px a cote de la legende, video pleine
     largeur dessous. Photo et video restent visibles d'un coup d'oeil. */
  #examples .n-ex__item--l .n-ex__fig { grid-template-columns: 96px minmax(0, 1fr); column-gap: var(--n-sp-3); row-gap: var(--n-sp-3); }
  #examples .n-ex__item--l .n-ex__avant { grid-column: 1; grid-row: 1; aspect-ratio: 1; align-self: start; }
  #examples .n-ex__item--l .n-ex__tag { padding: 1px 6px; font-size: 10px; }
  #examples .n-ex__item--l .n-ex__fleche { display: none; }
  #examples .n-ex__item--l .n-ex__legende { grid-column: 2; grid-row: 1; align-self: center; padding: 0; }
  #examples .n-ex__item--l .n-ex__apres { grid-column: 1 / -1; grid-row: 2; }
}

/* E20 · FORMATS : six cartes de 180 a 200 px sur deux colonnes (620 px) pour
   dire « un ratio par canal ». Sous 560 px, trois colonnes et des rectangles
   de 48 px de haut, a la meme echelle entre eux. La rangee de faits (.n-proof)
   qui redisait le heros est retiree d'index.html ; sa regle CSS reste (pricing).
   Prefixe #formats : .n-format__box existe aussi dans /app. */
@media (max-width: 559.98px) {
  #formats .n-formats { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--n-sp-2); }
  #formats .n-format { padding: var(--n-sp-3) var(--n-sp-1); gap: var(--n-sp-1); }
  #formats .n-format__box, #formats .n-format__box > .n-ar-3x4, #formats .n-format__box > .n-ar-9x16 { block-size: 48px; }
  #formats .n-format__box > i { max-block-size: 48px; }
}

/* E24 · BANDE DE FILMS AU DOIGT : l'etiquette « Make this with my product → »
   n'apparaissait qu'au survol ou au clavier, donc jamais au doigt, alors que
   le tap ouvre le studio. Sur un ecran sans survol, elle est affichee sur les
   deux cartes paysage, ou elle tient sur une ligne (sur une carte portrait de
   113 px, elle en prendrait trois). Le span reste aria-hidden et sans
   pointer-events : le lien et son nom ne changent pas. */
@media (hover: none) {
  .n-bande__carte--l .n-bande__cta { opacity: 1; transform: none; }
}

/* E27 · ACCUEIL SUR ORDINATEUR : lignes de 150 a 170 caracteres, titres
   decales entre cartes voisines.
   · Le titre de la demo, borne a 60ch, cassait sur 3 lignes ; la note sous
     « Do this with my product » courait sur 1 200 px : elle passe a droite du
     bouton, bornee a 62ch. */
@media (min-width: 960px) {
  .n-demo .n-section__head { max-inline-size: 50rem; }
  .n-demo__cta { flex-wrap: nowrap; }
  .n-demo__cta-note { max-inline-size: 62ch; }
}
/* E27 · la note de l'appel final (1 072 px de large) est bornee et centree ;
   la note des exemples (affirmation de provenance, gardee) n'est plus coupee
   a « re- / edited » ; les titres des etapes et des formats s'alignent entre
   cartes voisines (align-content: normal les etirait differemment). Le
   parent .n-steps / .n-formats protege les .n-step et .n-format__box de /app. */
.n-final__note { max-inline-size: 64ch; margin-inline: auto; }
.n-ex__note { max-inline-size: none; }
.n-steps > .n-step, .n-formats > .n-format { align-content: start; }

/* E39 · ANNEAU DE FOCUS DES BOUTONS DE LA DEMO : blanc autour d'un bouton
   blanc a 92 %, sur la video claire de la montre (~1,1:1) — on ne voyait pas
   quel bouton avait le focus (WCAG 2.4.7, 1.4.11). Meme anneau que les autres
   boutons poses sur une video (#5F55C9 : ~5,6:1 sur clair, ~3,4:1 sur noir). */
.n-demo__btn:focus-visible { outline: 2px solid var(--n-accent); outline-offset: 2px; }

/* == NUM_ERGONOMIE_24092026 · b-aide — L'AIDE ET LE SUR MESURE ==================
   Plan d'ergonomie du 24/09/2026, groupe B : help.html et custom.html, plus
   trois reglages generaux qui en decoulent (cartes --lg sur telephone, appel
   final). Fragment recolle A LA FIN de numeri.css : chaque regle est ecrite
   pour gagner a specificite egale sur ce qui precede, et reste dans le meme
   @media que la regle qu'elle corrige.
   Aucun de ces selecteurs n'existe dans /app (app.html, app.css, app.js) :
   .n-page, .n-section, .n-final, .n-custom, .n-select-wrap, .n-faq__cat y sont
   absents, et les deux .n-card--lg d'app.html ne sont sous aucune de ces classes.
   ========================================================================== */

/* E08 (b) · LE CHAPO ET LA PREMIERE CARTE : UN SEUL ECART. Sur l'aide comme
   sur le sur-mesure, les 48 px de .n-page__head s'ajoutaient aux 40 a 64 px
   de .n-section--tight : le bouton « Ask Numeri Bot » glissait sous la barre
   du bas, « Work email » sous la ligne de flottaison. La marge du chapo suffit. */
.n-page__head + #contact.n-section--tight,
.n-page__head + #business.n-section--tight { padding-block-start: 0; }

/* E08 (c) · UNE CARTE --lg SUR TELEPHONE N'A PAS LE REMBOURRAGE DU BUREAU.
   32 px de chaque cote a 375 px laissaient 266 px de texte (~28 signes par
   ligne) : la carte Pricing de l'accueil s'etirait sur 14 lignes. 24/16 px
   rendent ~32 px a la colonne. Limite aux cartes des pages de la vitrine
   (.n-section, .n-section--tight, .n-page) : /app n'en a aucune. */
@media (max-width: 479.98px) {
  :is(.n-section, .n-section--tight, .n-page) .n-card--lg { padding: var(--n-sp-5) var(--n-sp-4); }
}

/* E08 (d) · L'APPEL FINAL, MOINS HAUT SUR TELEPHONE. 64 px de marge en haut
   et en bas sous 720 px : 48/16 suffisent. Le mot-marque (.n-final__mark)
   reste — « LA MARQUE, C'EST LE MOT SEUL » est une decision de Joseph. */
@media (max-width: 719.98px) {
  .n-final { padding: var(--n-sp-7) var(--n-sp-4); }
}

/* E09 · SUR MESURE : LE FORMULAIRE EST PASSE AVANT LES QUATRE POINTS DANS LE
   DOM (custom.html), pour que « Work email » soit au premier ecran du
   telephone. Des 900 px, on garde la disposition d'avant : les points a
   gauche, le formulaire a droite, sur la meme rangee. */
@media (min-width: 900px) {
  .n-custom > .n-form { grid-column: 2; grid-row: 1; }
  .n-custom > .n-stack--6 { grid-column: 1; grid-row: 1; }
}

/* E25 · « Send my request » faisait 211 px sur telephone, seul dans son coin :
   pleine largeur sous 640 px, comme les autres boutons d'envoi du site. */
@media (max-width: 639.98px) {
  .n-custom .n-form__foot .n-btn { inline-size: 100%; }
}

/* E33 · AIDE : LES QUATRE RUBRIQUES DE LA FAQ. Un intitule par groupe de
   questions (h3.n-eyebrow, libelles du menu du formulaire) : de l'air au-dessus
   pour separer les groupes, peu au-dessous pour qu'il colle a sa liste. La
   premiere rubrique suit le sommaire : sa marge se fond avec celle du chapo. */
.n-faq__cat { margin-block: var(--n-sp-7) var(--n-sp-3); }

/* E38 · LE MENU « What is it about? » PORTAIT UNE FLECHE →, celle des liens
   « Talk to us → » : elle se lit « aller plus loin », pas « ouvrir une liste ».
   La meme icone, tournee d'un quart, pointe vers le bas. La fleche est centree
   par margin-block-start, pas par transform : la rotation ne la decale pas. */
.n-select-wrap__arrow { transform: rotate(90deg); }

/* ==========================================================================
   NUM_ERGONOMIE_24092026 · c-commun — ce que les huit pages de la vitrine
   partagent : la barre du bas (E17), le rail droit de l'en-tete (E18), le pied
   de page sur telephone (E35) et le vide avant le pied (E36).

   Fragment RECOLLE A LA FIN de numeri.css : chaque regle est ecrite pour
   gagner a cet endroit, et verifiee contre les regles de meme selecteur plus
   haut, y compris celles rangees dans une @media.

   ⚠️ /app CHARGE AUSSI numeri.css. Les regles de la barre et de l'en-tete ne
      visent que `:has(a.…--go)` : dans /app, « Create » est un <button>
      (app.html:192 et 264), donc rien n'y change. .n-footer, .n-page et
      .n-section sont absents d'app.html et d'app.css.
   Sans :has (Safari < 15.4, Chrome < 105), E17 et E18 ne s'appliquent pas :
   on garde l'etat d'avant, rien ne casse.
   La bulle du chat (E03) et le lien vers la FAQ dans le chat (E26) sont dans
   chat.css et chat.js.
   ========================================================================== */

/* E17 · LA BARRE DU BAS : SEULE LA PAGE COURANTE RESSORT, ET ON LA LIT.
   Le voile passe de 0,88 (--n-veil) a 0,96 : quand une video sombre passait
   dessous, les libelles de 12 px descendaient a 3,86:1, sous le seuil de 4,5.
   Les onglets qui ne sont ni la page courante ni « Create » passent de text-3
   a text-2 (≈ 7:1) ; « Create » garde l'accent de la marque. La page courante
   garde l'encre et le trait, et prend la graisse 600 : un troisieme signal.
   Pas de saut : les cases de la barre sont de largeur fixe (5 x 1fr).
   Le voile de l'en-tete ne change pas. */
.n-tabbar:has(> a.n-tabbar__link--go) { background: rgba(250, 250, 250, 0.96); }
.n-tabbar:has(> a.n-tabbar__link--go) > .n-tabbar__link:not([aria-current]):not(.n-tabbar__link--go) { color: var(--n-text-2); }
/* Le survol a l'encre (numeri.css § 09) est plus faible que la regle du
   dessus : on le repose, sinon il disparaissait sur les tablettes a souris. */
@media (hover: hover) {
  .n-tabbar:has(> a.n-tabbar__link--go) > .n-tabbar__link:not([aria-current]):not(.n-tabbar__link--go):hover { color: var(--n-ink); }
}
.n-tabbar:has(> a.n-tabbar__link--go) > .n-tabbar__link[aria-current="page"] { font-weight: var(--n-fw-semi); }

/* E18 · LE RAIL DROIT D'UN VISITEUR : LES RESERVES SE RANGENT ENSEMBLE.
   Pour un visiteur (boot.js pose data-session="off" avant le premier rendu),
   le solde et « Log out » gardent leur place invisible (data-off,
   NUM_RAIL_LARGEUR_03092026). Rangees la ou elles sont, elles faisaient deux
   trous : « EN » collee a « Account », comme une sixieme entree du menu, puis
   108 px de vide, « Create », encore 120 px de vide. On les regroupe en tete
   du rail, sans les supprimer : [reserve][reserve][EN][Create]. « EN »
   s'ecarte du menu et « Create » se cale au bord droit du contenu. La largeur
   du rail NE CHANGE PAS (ni ici ni dans /app) : la regle de Joseph tient.
   Sous 640 px, i18n.css met deja ces reserves en display:none : sans effet.
   Des que numeri.js retire data-off (solde connu, session), la regle tombe. */
html[data-session="off"] .n-nav:has(a.n-nav__link--go) .n-nav__side [data-off] { order: -1; }

/* E35 · LE PIED DE PAGE SUR TELEPHONE : DEUX COLONNES AU LIEU D'UNE.
   Sous 720 px, les trois listes s'empilaient (44 px par lien) : environ
   1 320 px, deux ecrans avant « Refund policy » ou « Terms ». Product et Legal
   passent cote a cote ; Account, qui porte support@numeri-video.com
   (≈ 175 px), reste en pleine largeur, sur la troisieme rangee. Les 8 pages
   ont la meme grille : marque, Product, Account, Legal. L'ordre de lecture
   (DOM) ne change pas ; les cibles de 44 px non plus.
   Au-dessus de 720 px, la grille 1.6fr 1fr 1fr 1fr de numeri.css reste.
   ⚠️ UNE DEMI-COLONNE FAIT 132 A 158 px (320 a 375 px) : un mot long y
      depassait, puis se faisait COUPER par `overflow-x: clip` (§ 03), sans un
      signe. Mesure le 24/09 sur les 27 langues : « Datenschutzerklärung »
      (de, +22 px a 320), « Политика конфиденциальности » (ru, +9 a 360),
      le georgien (+28 a 360, +21 a 375), nl, sv, hy. Le lien se coupe donc
      avec un trait d'union quand la langue le permet (html[lang] est pose
      par i18n.js), sinon au bord de la colonne. Aucun effet en anglais.
      Un lien replie sur deux lignes collait au suivant (44 px pile) : 8 px de
      padding haut et bas les separent. Sur une ligne, rien ne bouge : le
      texte (≈ 22 px) + 16 px reste sous le plancher de 44 px. */
@media (max-width: 719.98px) {
  .n-footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--n-sp-4); row-gap: var(--n-sp-5); }
  .n-footer__grid > .n-footer__brand { grid-column: 1 / -1; }
  .n-footer__grid > :nth-child(3) { grid-column: 1 / -1; grid-row: 3; }
  .n-footer__grid .n-footer__link, .n-footer__grid .n-footer__title { overflow-wrap: anywhere; -webkit-hyphens: auto; hyphens: auto; }
  .n-footer__grid .n-footer__link { padding-block: var(--n-sp-2); }
}

/* E36 · UN SEUL VIDE AVANT LE PIED DE PAGE, PAS TROIS.
   Sous le dernier bloc s'additionnaient le padding bas de la derniere
   .n-section (64 a 128 px), celui de main.n-page (64 px) et la marge haute du
   pied (64 px) : 190 px sur telephone, jusqu'a 320 px sur ordinateur. Le
   client voyait un ecran vide et croyait la page finie. On garde 64 px entre
   le dernier bloc et le filet du pied, sur toutes les pages :
     · la marge haute du pied tombe ;
     · accueil (main sans .n-page) : la derniere section garde 64 px ;
     · tarifs, aide, sur mesure (main.n-page) : c'est le padding de main qui
       fait les 64 px, la derniere section n'en ajoute plus.
   Les pages legales (article.n-page, dernier enfant : une div) gardent le
   padding de l'article. Les reserves de la barre du bas et du chat portent
   sur le padding-block-end du pied : non touchees. */
.n-footer { margin-block-start: 0; }
main:not(.n-page) > .n-section:last-child { padding-block-end: var(--n-sp-8); }
.n-page > .n-section:last-child { padding-block-end: 0; }

/* == 30 · LE TELEPHONE D'ABORD, SUITE · NUM_MOBILE_ERGONOMIE_25092026 ========
   Joseph, 25/09/2026 : « le site est pas ergonomique sur mobile […] enleve
   les textes qui servent a rien, tout doit etre utile ». Reprend l'audit du
   24/09 (§ 21, NUM_ERGONOMIE_24092026) et le complete. Mesure a 390 px avant
   reprise : accueil de 12 920 px (15 ecrans). 74 % des inscrits sont sur
   telephone, 68 % des vues de l'accueil viennent de la webview Instagram.
   Rien ici ne vise /app (qui charge aussi ce fichier) : selecteurs propres a
   la vitrine. Toutes les cibles restent a 44 px au moins.
   ⚠️ AUCUN `padding` EN RACCOURCI SUR `.n-consent` ICI : voir 19 ter. */

/* Deux garanties depuis le retrait de la troisieme (redite de l'etape 3 et de
   la FAQ) : deux colonnes, pas une case vide. */
@media (min-width: 720px) { #how .n-assure { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* Tarifs : le bloc Business n'a plus que son introduction (ses quatre points
   sont sur custom.html) — une colonne, pas une moitie vide. */
@media (min-width: 900px) { .n-bizplan--seul { grid-template-columns: minmax(0, 40rem); } }

/* Aide : « Still stuck? » porte le formulaire et les adresses (colonne
   etroite), puis les deux cartes pleine largeur. */
.n-aide-ecrire { margin-block-end: var(--n-sp-7); }

@media (max-width: 719.98px) {
  /* 48 px entre deux sections au lieu de 64 + 64 : le telephone montre du
     contenu, pas du vide. Les sections « flush-top » restent collees. */
  main:not(.n-page) > .n-section { padding-block: var(--n-sp-7); }
  main:not(.n-page) > .n-section--flush-top { padding-block-start: 0; }
  main:not(.n-page) .n-section__head { margin-block-end: var(--n-sp-5); }

  /* « What you can film » (NUM_GENERALISTE_25092026) : sept cartes de 150 px
     empilees par deux (1 084 px). Deux colonnes serrees, l'icone a gauche du
     nom, la septieme sur toute la largeur. La carte entiere reste le lien. */
  #products .n-cats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--n-sp-2); }
  #products .n-cat { grid-template-columns: 24px minmax(0, 1fr); column-gap: var(--n-sp-2); row-gap: 0;
                     justify-items: start; align-items: center; text-align: start;
                     padding: var(--n-sp-3); min-block-size: 64px; border-radius: var(--n-r-md); }
  #products .n-cat__ico { inline-size: 24px; block-size: 24px; grid-row: 1 / span 2; }
  #products .n-cat b { justify-content: flex-start; gap: var(--n-sp-1); font-size: var(--n-fs-sm); }
  #products .n-cat small { grid-column: 2; }
  #products .n-cat:last-child:nth-child(odd) { grid-column: 1 / -1; }

  /* Tarifs : le titre de page plus pres de son contenu, des cartes moins
     rembourrees (quatre forfaits empiles). */
  .n-page__head { margin-block-end: var(--n-sp-5); }
  .n-card-plan { padding: var(--n-sp-4); }
  .n-card-plan__flag { inset-inline-start: var(--n-sp-4); }

  /* Les cartes des pages de contenu (aide, sur mesure, tarifs) : 16 px entre
     leurs blocs au lieu de 32 — le titre ne flotte plus loin de son texte. */
  .n-page .n-card.n-stack--6 > * + * { margin-block-start: var(--n-sp-4); }

  /* Le pied de page : la phrase de marque redisait le chapo du heros. */
  .n-footer__pitch { display: none; }
}
