/* =============================================================================
   HOLCO / FIVEPEERS · SYSTEME DE DESIGN FIGE
   Fichier unique. Source de verite des tokens et des composants.
   Version 1.0 · 22/08/2026
   Reference : HOLCO_Fivepeers_Product_AI_Operating_System_2026-08-22.md, §22 et §23,
   priorite 5 « freeze enough UI choices to stop wasting time on CSS ».

   -----------------------------------------------------------------------------
   LA REGLE QUI GOUVERNE TOUT LE FICHIER : COMPOSITION, JAMAIS INVENTION.
   Un agent de code n'ecrit JAMAIS une valeur brute dans un composant. Ni une
   couleur, ni un espacement, ni un rayon, ni une taille de texte, ni une ombre,
   ni une duree. Il compose avec les tokens ci-dessous. Si le token manque, on
   ouvre ce fichier et on en discute ; on n'invente pas dans le composant.
   Mesure du 22/08/2026 sur holco-next/src, l'etat que ce fichier corrige :
   1 411 hexadecimaux codes en dur (361 valeurs distinctes, 98 fichiers sur 175),
   40 tailles de texte arbitraires, 11 rayons arbitraires, et 17 occurrences de
   l'ancien gris #71717A que la correction de token du 15/08 n'a jamais atteintes.
   Le garde-fou qui bloque la recidive : design-system/garde-fou.sh

   -----------------------------------------------------------------------------
   PORTEE
   Le NOYAU (structure, espacements, rayons, echelle de texte, composants, etats,
   mouvement) est PARTAGE entre HOLCO et Fivepeers : les deux produits partagent
   la philosophie d'interaction (conversation a gauche, artefact vivant a droite).
   La MARQUE (couleurs, familles typographiques) est portee par un bloc
   [data-brand] : les deux produits ne se ressemblent pas et ne doivent pas.

   USAGE
   1. HTML autonome  : <link rel="stylesheet" href="ds.css"> puis
                       <html data-brand="holco"> ou data-brand="fivepeers".
   2. Next + Tailwind: importer ds.css avant tailwindcss, et faire le pont
                       @theme decrit dans STACK.md. Les utilitaires Tailwind
                       lisent alors les memes tokens.
   3. Fond sombre    : poser data-surface="dark" sur n'importe quel conteneur.
   ============================================================================= */

@layer ds-tokens, ds-base, ds-layout, ds-components, ds-states, ds-utils;

/* =============================================================================
   1. TOKENS
   ========================================================================== */
@layer ds-tokens {

  /* ---------------------------------------------------------------------------
     1.1 PRIMITIVES DE MARQUE · HOLCO
     Source de verite : holco-next/src/app/globals.css + charte arbitree le
     15/08/2026. Aucune valeur inventee ici. Contrastes mesures le 22/08/2026.
     ------------------------------------------------------------------------ */
  [data-brand="holco"], :root {
    --ds-brand-white:       #FFFFFF;
    --ds-brand-cream:       #F7F5F1;  /* fond du hero, et seul fond creme retenu */
    --ds-brand-ink:         #09090B;  /* 19,90:1 sur blanc */
    --ds-brand-graphite:    #27272A;  /* 14,89:1 sur blanc */
    --ds-brand-grey:        #6B6B74;  /* 5,28:1 sur blanc · 4,85:1 sur creme */
    --ds-brand-border:      #E4E4E7;
    --ds-brand-hairline:    #D4D4D8;
    --ds-brand-navy:        #0E1B36;
    --ds-brand-navy-soft:   #16243F;
    --ds-brand-blue:        #2C5BAA;  /* 6,60:1 sur blanc */
    --ds-brand-blue-soft:   #4A78C4;
    --ds-brand-blue-muted:  #9DB8E0;  /* accent sur fond sombre, 8,44:1 sur navy */
    --ds-brand-blue-tint:   rgba(44, 91, 170, 0.08);

    /* ACCENT D'ATTENTE. Voir la regle absolue en 1.4. */
    --ds-brand-amber:       #E8963A;  /* sur fond sombre, 7,21:1 sur navy */
    --ds-brand-amber-text:  #B45309;  /* sur fond clair, 5,02:1 blanc · 4,61:1 creme */

    /* ⚠️ Le repli DOIT etre dans le var(), pas apres la virgule. Sans lui, une page
       qui ne definit pas --font-inter (tout ce qui n'est pas Next) rend la declaration
       font-family invalide en entier et retombe sur la serif du navigateur. Defaut
       constate en recette visuelle le 22/08 : le corps entier passait en serif. */
    --ds-font-heading: var(--font-source-serif, "Source Serif 4"), Georgia, "Times New Roman", serif;
    --ds-font-sans:    var(--font-inter, "Inter"), system-ui, -apple-system, "Segoe UI", sans-serif;
    --ds-font-display: var(--font-space-grotesk, "Space Grotesk"), system-ui, sans-serif;
    --ds-font-mono:    var(--font-jetbrains-mono, "JetBrains Mono"), ui-monospace, "SFMono-Regular", monospace;

    /* Poids du corps sur le site HOLCO : 300, decide et en place. */
    --ds-weight-body: 300;
  }

  /* ---------------------------------------------------------------------------
     1.2 PRIMITIVES DE MARQUE · FIVEPEERS
     Palette ACHROMATIQUE, arbitrage Pierre du 22/08/2026 : « inspire-toi des
     couleurs de neuralink.com ». Relevee sur le site, pas inventee (mesure des
     styles calcules du 22/08) : blanc pur, noir pur, gris de surface #F5F5F5,
     bordures #D9D9D9, et AUCUN accent chromatique. Les boutons y sont noir sur
     blanc ou blanc sur noir, jamais colores.
     Deux ecarts assumes par rapport a la mesure, et leur raison :
       1. leur gris de texte attenue #BABABA plafonne a 1,94:1 sur blanc. Il est
          inutilisable pour du texte. Retenu : #6B6B6B, mesure a 5,33:1 sur blanc
          et 4,89:1 sur #F5F5F5.
       2. l'accent de marque passe au NOIR faute d'accent chez eux. La distinction
          n'est plus portee par la teinte mais par l'italique, le mono en capitales
          et le filet de 2px. L'ambre d'attente ne bouge pas : il ne signale
          toujours qu'un point qui attend un humain.
     Palette chaude editoriale precedente, conservee pour pouvoir revenir :
     cream #F1EDE4 · ink #15120D · graphite #4B453B · grey #6B6355 ·
     border #D9D2C3 · hairline #C8C0AD · navy #20272D · navy-soft #3A464E ·
     accent seal #8F2D22 · ember #C24A2E · steel-light #8A97A0.
     Elle reste en vigueur sur les 30 autres pages du site, qui lisent
     styles.css et non ce fichier : la home est donc seule sur cette palette
     tant que le reste n'a pas suivi.
     ------------------------------------------------------------------------ */
  /* ARBITRAGE 25/08/2026 (go maquette-fivepeers-pairs) : la palette achromatique
     « neuralink » du 22/08 est REMPLACEE par la palette chaude editoriale (celle que les
     30 autres pages n'ont jamais quittee via styles.css), enrichie des CINQ couleurs de
     sieges du brief du 25/08 : une par pair, spectre maitrise, jamais de neon.
     Maquette validee : work/maquettes-fivepeers-20260825/maquette-pairs.html.
     La palette neuralink relevee le 22/08 reste consultable dans l'historique git. */
  [data-brand="fivepeers"] {
    --ds-brand-white:       #FFFFFF;
    --ds-brand-cream:       #F7F5F1;  /* creme holco.co (socle commun, 25/08) */
    --ds-brand-ink:         #141310;  /* encre holco.co */
    --ds-brand-graphite:    #4B453B;  /* 8,9:1 sur blanc */
    --ds-brand-grey:        #6B6B74;  /* gris holco.co */
    --ds-brand-border:      #E7E4DC;  /* bordure holco.co */
    --ds-brand-hairline:    #D8D4CA;
    --ds-brand-navy:        #20272D;  /* navy editorial : la surface sombre de la marque */
    --ds-brand-navy-soft:   #3A464E;
    --ds-brand-blue:        #8F2D22;  /* accent seal, 8,0:1 sur blanc · 6,7:1 sur #F1EDE4 */
    --ds-brand-blue-soft:   #6E211A;
    --ds-brand-blue-muted:  #E8B7AE;  /* accent SUR fond sombre */
    --ds-brand-blue-tint:   rgba(143, 45, 34, 0.07);

    --ds-brand-amber:       #E8963A;
    --ds-brand-amber-text:  #B45309;

    /* Les cinq sieges du board : une couleur par pair (brief 25/08 §12). Portees par
       .fp-seat--p1..p5 sur la home ; toute reutilisation passe par ces tokens. */
    --ds-pair-1: #8F2D22;  /* Falsification · seal */
    --ds-pair-2: #1F5F73;  /* Base rates · petrole */
    --ds-pair-3: #5B4A8A;  /* Second-order · prune */
    --ds-pair-4: #B4511F;  /* Pre-mortem · braise */
    --ds-pair-5: #166B52;  /* Execution · vert bouteille */

    --ds-font-heading: "Source Serif 4", Georgia, serif;  /* serif holco.co (25/08) */
    --ds-font-sans:    "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    --ds-font-display: "Source Serif 4", Georgia, serif;
    --ds-font-mono:    "JetBrains Mono", ui-monospace, "SFMono-Regular", monospace;

    --ds-weight-body: 400;
  }

  /* ---------------------------------------------------------------------------
     1.3 ECHELLES PARTAGEES (identiques pour les deux marques)
     ------------------------------------------------------------------------ */
  :root {
    /* Espacement · base 4px. Aucune autre valeur n'est permise. */
    --ds-space-0:  0;
    --ds-space-1:  0.25rem;   /*  4px */
    --ds-space-2:  0.5rem;    /*  8px */
    --ds-space-3:  0.75rem;   /* 12px */
    --ds-space-4:  1rem;      /* 16px */
    --ds-space-5:  1.25rem;   /* 20px */
    --ds-space-6:  1.5rem;    /* 24px */
    --ds-space-8:  2rem;      /* 32px */
    --ds-space-10: 2.5rem;    /* 40px */
    --ds-space-12: 3rem;      /* 48px */
    --ds-space-16: 4rem;      /* 64px */
    --ds-space-20: 5rem;      /* 80px */
    --ds-space-24: 6rem;      /* 96px */

    /* Texte · noms SEMANTIQUES, pour rendre le mauvais usage difficile.
       Remplace les 40 tailles arbitraires mesurees le 22/08. */
    --ds-text-micro:   0.6875rem;  /* 11px · sur-titre, mention legale */
    --ds-text-mini:    0.75rem;    /* 12px · badge, note */
    --ds-text-caption: 0.8125rem;  /* 13px · legende, aide de champ */
    --ds-text-ui:      0.875rem;   /* 14px · bouton, champ, tableau */
    --ds-text-body:    1rem;        /* aligne sur holco.co (16px) le 26/08/2026 */  /* 15px · corps et conversation */
    --ds-text-lead:    1.0625rem;  /* 17px · chapeau */
    --ds-text-h4:      1.25rem;    /* 20px */
    --ds-text-h3:      1.5rem;     /* 24px */
    --ds-text-h2:      1.875rem;   /* 30px */
    --ds-text-h1:      2.375rem;   /* 38px */
    --ds-text-display: 3rem;       /* 48px */
    --ds-text-hero:    3.75rem;    /* 60px */

    --ds-leading-tight:   1.05;  /* titres, valeur de la charte */
    --ds-leading-snug:    1.25;
    --ds-leading-normal:  1.5;
    --ds-leading-relaxed: 1.6;   /* conversation */

    --ds-tracking-heading: -0.012em;  /* une serif ne supporte pas -0.02em */
    --ds-tracking-label:    0.12em;

    --ds-weight-regular: 400;
    --ds-weight-medium:  500;
    --ds-weight-semi:    600;

    /* Rayons · la charte veut des coins droits (sobriete memo.bank).
       Cinq valeurs, pas onze. */
    --ds-radius-none: 0;
    --ds-radius-sm:   2px;
    --ds-radius-md:   4px;    /* defaut : cartes, champs, boutons */
    --ds-radius-lg:   6px;    /* panneaux et surfaces larges */
    --ds-radius-pill: 999px;  /* uniquement les puces de reponse rapide */

    /* Ombres · derivees de celles deja en production. */
    --ds-shadow-hairline: 0 1px 2px rgba(16, 24, 40, 0.05);
    --ds-shadow-card:
      0 1px 2px rgba(16, 24, 40, 0.05),
      0 18px 44px -32px rgba(14, 27, 54, 0.28);
    --ds-shadow-raised:
      0 1px 2px rgba(16, 24, 40, 0.05),
      0 24px 62px -40px rgba(14, 27, 54, 0.32),
      0 36px 88px -60px rgba(14, 27, 54, 0.20);
    --ds-shadow-overlay: 0 24px 64px -24px rgba(9, 9, 11, 0.28);

    /* Mouvement · une seule courbe, quatre durees. */
    --ds-ease:      cubic-bezier(0.2, 0, 0, 1);
    --ds-ease-out:  cubic-bezier(0.16, 1, 0.3, 1);
    --ds-dur-fast:  150ms;
    --ds-dur:       220ms;
    --ds-dur-slow:  380ms;
    --ds-dur-enter: 500ms;  /* apparition d'un bloc d'artefact */

    /* Profondeur. Une seule echelle, on ne pose plus de z-index au jugé. */
    --ds-z-base:    0;
    --ds-z-raised:  10;
    --ds-z-sticky:  50;   /* barre de navigation, deja en place */
    --ds-z-overlay: 100;
    --ds-z-modal:   200;
    --ds-z-toast:   300;
    --ds-z-skip:    2000; /* lien d'evitement, deja en place */

    /* Grille. */
    --ds-container:    1200px;
    --ds-container-narrow: 900px;
    --ds-gutter:       var(--ds-space-6);   /* mobile  · 24px */
    --ds-gutter-wide:  var(--ds-space-12);  /* desktop · 48px */
    --ds-section-py:      4.5rem;
    --ds-section-py-wide: 7rem;

    /* POINTS DE RUPTURE (constantes documentaires : une variable CSS ne peut pas
       servir dans une media query). Quatre, pas davantage. Le 880px historique
       est retire, il ne servait qu'a une page.
         --ds-bp-sm :  480px   mobile large
         --ds-bp-md :  768px   tablette / bascule des colonnes
         --ds-bp-lg :  980px   bascule du duo conversation + artefact
         --ds-bp-xl : 1200px   largeur maximale du conteneur                     */
  }

  /* ---------------------------------------------------------------------------
     1.4 TOKENS SEMANTIQUES · SURFACE CLAIRE (defaut)
     Les composants ne lisent QUE ces tokens la, jamais les primitives.
     Changer de fond revient donc a changer un seul bloc.
     ------------------------------------------------------------------------ */
  /* ⚠️ [data-brand] DOIT figurer dans ce selecteur. Une custom property est resolue
     la ou elle est declaree : si le mapping semantique n'existe que sur :root, un
     sous-arbre qui redefinit les primitives herite quand meme des valeurs deja
     calculees a la racine, et la marque n'a aucun effet. Defaut constate en recette
     visuelle le 22/08 : le bloc Fivepeers rendait a l'identique de HOLCO.
     Consequence d'usage : poser data-brand et data-surface sur le MEME element. */
  :root, [data-brand], [data-surface="light"] {
    --ds-bg:              var(--ds-brand-white);
    --ds-bg-alt:          var(--ds-brand-cream);
    --ds-surface:         var(--ds-brand-white);
    --ds-surface-sunken:  var(--ds-brand-cream);
    --ds-text:            var(--ds-brand-ink);
    --ds-text-secondary:  var(--ds-brand-graphite);
    --ds-text-muted:      var(--ds-brand-grey);
    --ds-border:          var(--ds-brand-border);
    --ds-border-strong:   var(--ds-brand-hairline);
    --ds-link:            var(--ds-brand-blue);
    --ds-link-hover:      var(--ds-brand-blue-soft);
    --ds-focus:           var(--ds-brand-blue);
    --ds-tint:            var(--ds-brand-blue-tint);
    --ds-shadow:          var(--ds-shadow-card);

    /* ⚑ ACCENT D'ATTENTE, regle absolue de la charte (arbitrage du 15/08/2026).
       Il ne signale QU'UNE chose : un point qui attend un humain.
       JAMAIS un bouton, JAMAIS un lien, JAMAIS un titre, JAMAIS une decoration.
       Le seul composant autorise a le porter est .ds-badge--attente et la
       pastille .ds-dot--attente. Le garde-fou verifie cette regle. */
    --ds-attente:      var(--ds-brand-amber-text);
    --ds-attente-bg:   rgba(180, 83, 9, 0.08);

    /* Le bouton primaire sur fond clair est le navy editorial, pas l'accent. Valeur alignee
       le 22/08 sur ce qui est DEJA en production (le bouton d'envoi du hero) plutot que sur
       une valeur nouvelle : un systeme qui contredit la page qu'il doit servir cree une
       migration au lieu de l'eviter. Blanc sur navy : 17,09:1. */
    --ds-btn-primary-bg:     var(--ds-brand-navy);
    --ds-btn-primary-fg:     var(--ds-brand-white);
    --ds-btn-primary-bg-hov: var(--ds-brand-navy-soft);

    --ds-ok:   #15803D;  /* 5,02:1 sur blanc */
    --ds-risk: #B91C1C;  /* 6,47:1 sur blanc */
  }

  /* ---------------------------------------------------------------------------
     1.5 TOKENS SEMANTIQUES · SURFACE SOMBRE
     ------------------------------------------------------------------------ */
  [data-surface="dark"] {
    --ds-bg:              var(--ds-brand-navy);
    --ds-bg-alt:          var(--ds-brand-navy-soft);
    --ds-surface:         var(--ds-brand-navy-soft);
    --ds-surface-sunken:  var(--ds-brand-navy);
    --ds-text:            #EFEFEF;             /* 14,86:1 sur navy */
    --ds-text-secondary:  #C7C7CC;
    --ds-text-muted:      #9A9A9A;
    --ds-border:          rgba(255, 255, 255, 0.10);
    --ds-border-strong:   rgba(255, 255, 255, 0.20);
    --ds-link:            var(--ds-brand-blue-muted);
    --ds-link-hover:      #C3D4EC;
    --ds-focus:           var(--ds-brand-blue-muted);
    --ds-tint:            rgba(255, 255, 255, 0.06);
    --ds-shadow:          0 1px 2px rgba(0, 0, 0, 0.20), 0 22px 54px -38px rgba(0, 0, 0, 0.60);

    --ds-attente:      var(--ds-brand-amber);   /* 7,21:1 sur navy */
    --ds-attente-bg:   rgba(232, 150, 58, 0.12);

    /* Consequence directe de la regle de l'accent : sur fond sombre le bouton
       principal est BLANC. Jamais ambre, jamais bleu. */
    --ds-btn-primary-bg:     var(--ds-brand-white);
    --ds-btn-primary-fg:     var(--ds-brand-navy);
    --ds-btn-primary-bg-hov: #E6E6E6;

    --ds-ok:   #4ADE80;
    --ds-risk: #FCA5A5;
  }
  /* ⚠️ Le gris attenu ci-dessus est calibre pour le navy HOLCO. Sur le vert
     bouteille de Fivepeers, plus clair, il tombe a 4,44:1 et devient non
     conforme. Correctif borne a la marque, mesure le 22/08 : 4,96:1. */
  [data-brand="fivepeers"] [data-surface="dark"],
  [data-brand="fivepeers"][data-surface="dark"] {
    --ds-text-muted: #A3A3A3;
  }

}

/* =============================================================================
   2. BASE
   ========================================================================== */
@layer ds-base {
  *, *::before, *::after { box-sizing: border-box; }

  html {
    font-family: var(--ds-font-sans);
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    scroll-padding-top: 80px;
  }

  body {
    margin: 0;
    background: var(--ds-bg);
    color: var(--ds-text);
    font-weight: var(--ds-weight-body);
    font-feature-settings: "tnum" 1, "ss01" 1;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }

  /* ⚠️ LA TAILLE DU CORPS NE S'IMPOSE PAS A UNE APPLICATION HOTE. Elle n'est posee que sur
     les pages qui declarent une marque. Motif mesure le 22/08, et c'est le defaut le plus
     sournois de la journee : pose sur `body` nu, le 15px du systeme remplacait le 16px par
     defaut du navigateur sur TOUT holco.co. Rien ne le montrait dans les couleurs ni les
     polices, seuls 32 elements de pied de page perdaient 1px de hauteur.
     Lecon de mesure autant que de code : une recette de non-regression qui compare la
     famille, le poids et la couleur SANS comparer la TAILLE ne voit pas passer un corps
     entier qui retrecit. */
  [data-brand] body,
  body[data-brand] {
    font-size: var(--ds-text-body);
    line-height: var(--ds-leading-normal);
  }

  h1, h2, h3, h4, h5, h6 {
    margin: 0;
    font-family: var(--ds-font-heading);
    font-weight: var(--ds-weight-semi);
    line-height: var(--ds-leading-tight);
    letter-spacing: var(--ds-tracking-heading);
    color: inherit;
    text-wrap: balance;
  }
  /* ⚠️ LES REGLES SUR BALISES NUES SONT DOUBLEMENT BORNEES, et il a fallu les DEUX bornes.
     1. `[data-brand]` : elles ne s'appliquent qu'aux pages qui declarent explicitement une
        marque, c'est-a-dire les pages autonomes du systeme. Une application existante ne
        declare rien et n'est donc jamais touchee, tout en lisant les tokens par `:root`.
     2. `:not([class])` : meme sur une page du systeme, une balise stylee par ailleurs garde
        la main.
     Historique, parce que la premiere borne seule ne suffisait pas : avec `:not([class])`
     seul, la recette multi-pages du 22/08 a trouve un lien sans classe de /demo/ qui passait
     de l'encre au bleu. Une application a utilitaires n'a pas QUE des balises classees.
     Ne jamais styler une balise nue depuis une feuille destinee a etre importee ailleurs. */
  [data-brand] h1:not([class]) { font-size: var(--ds-text-h1); }
  [data-brand] h2:not([class]) { font-size: var(--ds-text-h2); }
  [data-brand] h3:not([class]) { font-size: var(--ds-text-h3); }
  [data-brand] h4:not([class]) { font-size: var(--ds-text-h4); }

  p { margin: 0; text-wrap: pretty; }

  [data-brand] a:not([class]) {
    color: var(--ds-link);
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
    transition: color var(--ds-dur-fast) var(--ds-ease);
  }
  [data-brand] a:not([class]):hover { color: var(--ds-link-hover); }

  code, kbd, pre, samp { font-family: var(--ds-font-mono); font-size: 0.92em; }

  hr {
    border: 0;
    border-top: 1px solid var(--ds-border);
    margin: var(--ds-space-6) 0;
  }

  img, svg, video { max-width: 100%; height: auto; display: block; }

  /* Focus clavier : une seule facon de le montrer, partout. */
  :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
    outline: 2px solid var(--ds-focus);
    outline-offset: 3px;
    border-radius: var(--ds-radius-sm);
  }

  .ds-sr-only {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }

  .ds-skip-link {
    position: absolute; left: var(--ds-space-4); top: -48px;
    z-index: var(--ds-z-skip);
    padding: var(--ds-space-3) var(--ds-space-4);
    background: var(--ds-brand-blue); color: #fff;
    font-size: var(--ds-text-ui); font-weight: var(--ds-weight-medium);
    text-decoration: none;
    transition: top var(--ds-dur-fast) var(--ds-ease);
  }
  .ds-skip-link:focus-visible { top: var(--ds-space-3); }
}

/* =============================================================================
   3. LAYOUT
   ========================================================================== */
@layer ds-layout {

  .ds-container {
    width: 100%;
    max-width: var(--ds-container);
    margin-inline: auto;
    padding-inline: var(--ds-gutter);
  }
  .ds-container--narrow { max-width: var(--ds-container-narrow); }

  @media (min-width: 768px) {
    .ds-container { padding-inline: var(--ds-gutter-wide); }
  }

  .ds-section { padding-block: var(--ds-section-py); background: var(--ds-bg); color: var(--ds-text); }
  .ds-section--alt { background: var(--ds-bg-alt); }
  @media (min-width: 768px) {
    .ds-section { padding-block: var(--ds-section-py-wide); }
  }

  .ds-stack   { display: flex; flex-direction: column; gap: var(--ds-space-4); }
  .ds-stack-2 { gap: var(--ds-space-2); }
  .ds-stack-6 { gap: var(--ds-space-6); }
  .ds-row     { display: flex; align-items: center; gap: var(--ds-space-3); flex-wrap: wrap; }

  /* ---------------------------------------------------------------------------
     LE DUO · conversation a gauche, artefact vivant a droite.
     C'est le motif d'interaction central du document (§4, §14, §21). Il est
     defini UNE fois ici et reutilise par HOLCO comme par Fivepeers.
     ------------------------------------------------------------------------ */
  .ds-duo {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--ds-space-6);
    align-items: start;
  }
  @media (min-width: 980px) {
    .ds-duo {
      grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
      gap: var(--ds-space-12);
    }
    /* L'artefact suit la lecture : la conversation defile, le livrable reste. */
    .ds-duo__artefact { position: sticky; top: var(--ds-space-20); }
  }

  /* Le comportement mobile du duo est defini dans ds-components, avec le panneau
     lui-meme. Raison : avec les couches CSS, l'ordre des couches l'emporte sur la
     specificite, donc une regle posee ici serait ecrasee par le display:none du
     panneau declare plus loin. Defaut constate en recette visuelle le 22/08. */
}

/* =============================================================================
   4. COMPOSANTS
   ========================================================================== */
@layer ds-components {

  /* ------------------------------------------------------------ 4.1 BOUTONS */
  /* Un seul bouton primaire par ecran (regle du PLAYBOOK design SaaS).
     Le libelle dit le benefice, jamais la techno. */
  .ds-btn {
    display: inline-flex; align-items: center; justify-content: center;
    gap: var(--ds-space-2);
    min-height: 44px;                       /* cible tactile */
    padding: var(--ds-space-2) var(--ds-space-5);
    border: 1px solid transparent;
    border-radius: var(--ds-radius-md);
    font-family: var(--ds-font-sans);
    font-size: var(--ds-text-ui);
    font-weight: var(--ds-weight-medium);
    line-height: 1.2;
    text-decoration: none;
    cursor: pointer;
    white-space: nowrap;
    transition: background-color var(--ds-dur-fast) var(--ds-ease),
                border-color var(--ds-dur-fast) var(--ds-ease),
                color var(--ds-dur-fast) var(--ds-ease),
                transform var(--ds-dur-fast) var(--ds-ease);
  }
  .ds-btn:active { transform: translateY(1px); }
  .ds-btn[disabled], .ds-btn.is-disabled {
    opacity: 0.45; cursor: not-allowed; transform: none;
  }

  .ds-btn--primary {
    background: var(--ds-btn-primary-bg);
    color: var(--ds-btn-primary-fg);
  }
  .ds-btn--primary:hover:not([disabled]) { background: var(--ds-btn-primary-bg-hov); color: var(--ds-btn-primary-fg); }

  .ds-btn--secondary {
    background: transparent;
    color: var(--ds-text);
    border-color: var(--ds-border-strong);
  }
  .ds-btn--secondary:hover:not([disabled]) { background: var(--ds-tint); color: var(--ds-text); }

  .ds-btn--ghost {
    background: transparent; color: var(--ds-text-secondary); border-color: transparent;
    padding-inline: var(--ds-space-3);
  }
  .ds-btn--ghost:hover:not([disabled]) { background: var(--ds-tint); color: var(--ds-text); }

  .ds-btn--sm { min-height: 36px; padding: var(--ds-space-1) var(--ds-space-3); font-size: var(--ds-text-caption); }
  .ds-btn--lg { min-height: 52px; padding: var(--ds-space-3) var(--ds-space-8); font-size: var(--ds-text-body); }
  .ds-btn--block { width: 100%; }

  /* -------------------------------------------------------------- 4.2 CHAMPS */
  .ds-field-group { display: flex; flex-direction: column; gap: var(--ds-space-2); }

  .ds-label {
    font-size: var(--ds-text-caption);
    font-weight: var(--ds-weight-medium);
    color: var(--ds-text-secondary);
  }

  .ds-field {
    width: 100%;
    min-height: 44px;
    padding: var(--ds-space-2) var(--ds-space-3);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-md);
    background: var(--ds-surface);
    color: var(--ds-text);
    font-family: var(--ds-font-sans);
    font-size: var(--ds-text-ui);
    line-height: var(--ds-leading-normal);
    outline: none;
    transition: border-color var(--ds-dur-fast) var(--ds-ease),
                box-shadow var(--ds-dur-fast) var(--ds-ease);
  }
  .ds-field::placeholder { color: var(--ds-text-muted); }
  .ds-field:hover  { border-color: var(--ds-border-strong); }
  .ds-field:focus  { border-color: var(--ds-focus); box-shadow: 0 0 0 3px var(--ds-tint); }
  .ds-field[aria-invalid="true"] { border-color: var(--ds-risk); }
  .ds-field[disabled] { opacity: 0.55; cursor: not-allowed; }
  textarea.ds-field { min-height: 96px; resize: vertical; }

  .ds-help  { font-size: var(--ds-text-caption); color: var(--ds-text-muted); }
  .ds-error { font-size: var(--ds-text-caption); color: var(--ds-risk); }

  /* -------------------------------------------------------------- 4.3 CARTES */
  .ds-card {
    background: var(--ds-surface);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-md);
    padding: var(--ds-space-5);
    box-shadow: var(--ds-shadow-hairline);
  }
  .ds-card--raised { box-shadow: var(--ds-shadow); }
  .ds-card--flat   { box-shadow: none; }
  .ds-card__titre  {
    font-family: var(--ds-font-heading);
    font-size: var(--ds-text-h4);
    margin-bottom: var(--ds-space-2);
  }

  /* --------------------------------------------------- 4.4 ETIQUETTES ET PUCES */
  /* Sur-titre. Le prefixe est un point median, jamais un tiret cadratin. */
  .ds-eyebrow {
    display: inline-flex; align-items: center; gap: var(--ds-space-2);
    font-family: var(--ds-font-mono);
    font-size: var(--ds-text-micro);
    font-weight: var(--ds-weight-medium);
    letter-spacing: var(--ds-tracking-label);
    text-transform: uppercase;
    color: var(--ds-text-muted);
  }
  .ds-eyebrow::before { content: "·"; font-size: 1.4em; line-height: 0.5; opacity: 0.6; }

  .ds-badge {
    display: inline-flex; align-items: center; gap: var(--ds-space-1);
    padding: 2px var(--ds-space-2);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-sm);
    background: var(--ds-surface);
    color: var(--ds-text-secondary);
    font-size: var(--ds-text-mini);
    font-weight: var(--ds-weight-medium);
    line-height: 1.5;
  }
  /* ⚑ Le SEUL usage autorise de l'accent d'attente : un point qui attend un humain. */
  .ds-badge--attente { color: var(--ds-attente); border-color: var(--ds-attente); background: var(--ds-attente-bg); }
  .ds-badge--ok      { color: var(--ds-ok);   border-color: color-mix(in srgb, var(--ds-ok) 45%, transparent); }
  .ds-badge--risque  { color: var(--ds-risk); border-color: color-mix(in srgb, var(--ds-risk) 45%, transparent); }

  .ds-dot { display: inline-block; width: 7px; height: 7px; border-radius: var(--ds-radius-pill); background: var(--ds-text-muted); }
  .ds-dot--attente { background: var(--ds-attente); }
  .ds-dot--ok      { background: var(--ds-ok); }
  .ds-dot--risque  { background: var(--ds-risk); }

  /* --------------------------------------------------------- 4.5 CONVERSATION */
  /* Nora (HOLCO) et l'agent de board (Fivepeers) partagent ce composant. */
  .ds-conv {
    display: flex; flex-direction: column;
    background: var(--ds-surface);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-lg);
    overflow: hidden;
  }

  .ds-conv__flux {
    display: flex; flex-direction: column; gap: var(--ds-space-4);
    padding: var(--ds-space-5);
    max-height: 420px;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .ds-msg {
    font-size: var(--ds-text-body);
    line-height: var(--ds-leading-relaxed);
    white-space: pre-wrap;
    /* Une saisie sans espace (une URL collee, un mot tres long) casserait la
       colonne sur mobile. Releve a la relecture codex du 22/08. */
    overflow-wrap: anywhere;
  }
  /* L'agent parle en texte nu : il tient le fil, il n'est pas un interlocuteur
     de messagerie. L'humain a une bulle : c'est lui qu'on retrouve en relisant. */
  .ds-msg--agent {
    align-self: flex-start;
    max-width: 92%;
    color: var(--ds-text);
  }
  .ds-msg--user {
    align-self: flex-end;
    max-width: 88%;
    padding: var(--ds-space-2) var(--ds-space-4);
    border-radius: var(--ds-radius-md);
    background: var(--ds-surface-sunken);
    border: 1px solid var(--ds-border);
    font-size: var(--ds-text-ui);
    line-height: var(--ds-leading-normal);
    color: var(--ds-text-secondary);
  }

  /* L'agent reflechit. Trois points, pas un curseur qui clignote. */
  .ds-typing { display: inline-flex; align-items: center; gap: 5px; padding: var(--ds-space-1) 0; }
  .ds-typing span {
    width: 6px; height: 6px; border-radius: var(--ds-radius-pill);
    background: var(--ds-text-muted); opacity: 0.35;
  }
  @media (prefers-reduced-motion: no-preference) {
    .ds-typing span { animation: dsTyping 1.2s var(--ds-ease) infinite; }
    .ds-typing span:nth-child(2) { animation-delay: 0.15s; }
    .ds-typing span:nth-child(3) { animation-delay: 0.30s; }
  }
  @keyframes dsTyping {
    0%, 60%, 100% { opacity: 0.25; transform: translateY(0); }
    30%           { opacity: 1;    transform: translateY(-3px); }
  }

  /* Reponses rapides. Une seule est primaire, comme pour les boutons. */
  .ds-reponses {
    display: flex; flex-wrap: wrap; gap: var(--ds-space-2);
    padding: var(--ds-space-4) var(--ds-space-5);
    border-top: 1px solid var(--ds-border);
  }
  .ds-puce {
    min-height: 38px;
    padding: var(--ds-space-2) var(--ds-space-3);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-pill);
    background: var(--ds-surface);
    color: var(--ds-text-secondary);
    font-family: var(--ds-font-sans);
    font-size: var(--ds-text-caption);
    line-height: 1.3;
    text-align: left;
    cursor: pointer;
    transition: background-color var(--ds-dur-fast) var(--ds-ease),
                border-color var(--ds-dur-fast) var(--ds-ease);
  }
  .ds-puce:hover { background: var(--ds-tint); border-color: var(--ds-border-strong); color: var(--ds-text); }
  .ds-puce--primaire {
    background: var(--ds-btn-primary-bg);
    color: var(--ds-btn-primary-fg);
    border-color: transparent;
    font-weight: var(--ds-weight-medium);
  }
  .ds-puce--primaire:hover { background: var(--ds-btn-primary-bg-hov); color: var(--ds-btn-primary-fg); }

  /* Zone de saisie. */
  .ds-composer {
    display: flex; align-items: center; gap: var(--ds-space-2);
    padding: var(--ds-space-2);
    border-top: 1px solid var(--ds-border);
    background: var(--ds-surface);
  }
  .ds-composer .ds-field { border: 0; box-shadow: none; background: transparent; }
  .ds-composer .ds-field:focus { box-shadow: none; }
  .ds-composer__envoi {
    display: inline-flex; align-items: center; justify-content: center;
    width: 42px; height: 42px; flex-shrink: 0;
    border: 0; border-radius: var(--ds-radius-md);
    background: var(--ds-btn-primary-bg); color: var(--ds-btn-primary-fg);
    cursor: pointer;
    transition: background-color var(--ds-dur-fast) var(--ds-ease);
  }
  .ds-composer__envoi:hover:not([disabled]) { background: var(--ds-btn-primary-bg-hov); }
  .ds-composer__envoi[disabled] { opacity: 0.35; cursor: not-allowed; }

  /* ---------------------------------------------------- 4.6 PANNEAU D'ARTEFACT */
  /* Le livrable qui se construit pendant la conversation. Doit ressembler a un
     premier livrable, pas a une animation marketing (§7 du document). */
  .ds-artefact {
    background: var(--ds-surface);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-lg);
    box-shadow: var(--ds-shadow);
    overflow: hidden;
  }

  .ds-artefact__entete {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--ds-space-3);
    padding: var(--ds-space-4) var(--ds-space-5);
    border-bottom: 1px solid var(--ds-border);
    background: var(--ds-surface-sunken);
  }
  .ds-artefact__titre {
    font-family: var(--ds-font-heading);
    font-size: var(--ds-text-h4);
    line-height: var(--ds-leading-snug);
  }
  /* Avancement : un compteur honnete, jamais une barre decorative. */
  .ds-artefact__avancement {
    font-family: var(--ds-font-mono);
    font-size: var(--ds-text-mini);
    color: var(--ds-text-muted);
    white-space: nowrap;
  }

  /* L'annonce : ce que le panneau VA contenir, dit avant qu'il contienne quoi que ce soit.
     Elle se place en haut, jamais en pied. Motif mesure au QA du 22/08 sur la home : posee en
     pied, elle tombait sous la ligne de flottaison, et le visiteur ne voyait que cinq lignes
     grises sans savoir qu'elles allaient se remplir. Une promesse hors ecran n'existe pas. */
  .ds-artefact__annonce {
    padding: var(--ds-space-3) var(--ds-space-5);
    border-bottom: 1px solid var(--ds-border);
    font-size: var(--ds-text-caption);
    line-height: var(--ds-leading-normal);
    color: var(--ds-text-muted);
  }

  .ds-artefact__corps { padding: var(--ds-space-2) var(--ds-space-5) var(--ds-space-5); }

  /* Tant que rien n'est renseigne, les intitules se lisent comme une liste, pas comme des
     champs vides : moins de hauteur, donc moins de vide a traverser. */
  .ds-artefact--compact .ds-artefact__bloc { padding-block: var(--ds-space-3); }

  .ds-artefact__bloc {
    display: none;                       /* etat 1 : le bloc n'existe pas encore */
    padding-block: var(--ds-space-4);
    border-bottom: 1px solid var(--ds-border);
  }
  .ds-artefact__bloc:last-child { border-bottom: 0; }

  /* Etat 2 : en attente de reponse. Le bloc s'annonce, en gris, sans mentir. */
  .ds-artefact__bloc.is-attendu { display: block; opacity: 0.55; }
  /* Etat 3 : rempli. C'est le seul moment ou le contenu apparait. */
  .ds-artefact__bloc.is-rempli  { display: block; opacity: 1; }

  @media (prefers-reduced-motion: no-preference) {
    .ds-artefact__bloc.is-rempli { animation: dsBlocEntre var(--ds-dur-enter) var(--ds-ease-out) both; }
  }
  @keyframes dsBlocEntre {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
  }

  .ds-artefact__intitule {
    font-family: var(--ds-font-mono);
    font-size: var(--ds-text-micro);
    letter-spacing: var(--ds-tracking-label);
    text-transform: uppercase;
    color: var(--ds-text-muted);
    margin-bottom: var(--ds-space-2);
  }
  .ds-artefact__valeur {
    font-size: var(--ds-text-body);
    line-height: var(--ds-leading-normal);
    color: var(--ds-text);
    overflow-wrap: anywhere;
  }
  .ds-artefact__liste { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--ds-space-1); }
  .ds-artefact__liste li { display: flex; align-items: baseline; gap: var(--ds-space-2); font-size: var(--ds-text-ui); }
  .ds-artefact__liste li::before { content: "·"; color: var(--ds-text-muted); }

  /* Distinguer ce que l'agent SAIT de ce qu'il SUPPOSE (playbook Nora, §27). */
  .ds-artefact__source {
    font-size: var(--ds-text-mini);
    color: var(--ds-text-muted);
    margin-top: var(--ds-space-1);
  }
  .ds-artefact__source--hypothese { font-style: italic; }

  /* Bascule mobile du panneau. */
  .ds-artefact__bascule {
    display: none;
    width: 100%;
    align-items: center; justify-content: space-between; gap: var(--ds-space-2);
    padding: var(--ds-space-3) var(--ds-space-5);
    border: 0; border-top: 1px solid var(--ds-border);
    background: var(--ds-surface);
    color: var(--ds-text-secondary);
    font-family: var(--ds-font-sans);
    font-size: var(--ds-text-caption);
    cursor: pointer;
  }

  .ds-artefact__pied {
    padding: var(--ds-space-4) var(--ds-space-5);
    border-top: 1px solid var(--ds-border);
    background: var(--ds-surface-sunken);
  }

  /* Comportement mobile du duo, decide une fois (§23 « mobile behavior ») :
     la conversation passe en premier et occupe l'ecran ; l'artefact la suit,
     replie derriere son resume, jamais en colonne ecrasee. */
  @media (max-width: 979px) {
    .ds-artefact__entete { flex-wrap: wrap; }
    /* En colonne, la bascule PORTE le titre et le compteur : garder aussi l'entete afficherait
       deux fois la meme information, et le titre se casserait sur deux lignes pour rien. */
    .ds-duo__artefact .ds-artefact__entete { display: none; }
    .ds-duo__artefact .ds-artefact__corps { display: none; }
    .ds-duo__artefact.is-ouvert .ds-artefact__corps { display: block; }
    .ds-duo__artefact .ds-artefact__bascule { display: flex; }
    /* Le resume passe au-dessus du contenu : c'est lui qu'on lit d'abord. */
    .ds-duo__artefact { display: flex; flex-direction: column; }
    /* ⚠️ TOUT enfant doit recevoir un ordre. Un seul oubli suffit : sans `order`, l'annonce
       vaut 0 et passe DEVANT la bascule, donc le visiteur lisait « ce panneau se remplit »
       avant de savoir de quel panneau on parle. Constate au QA mobile du 22/08. */
    .ds-duo__artefact .ds-artefact__entete  { order: 1; }
    .ds-duo__artefact .ds-artefact__bascule { order: 2; border-top: 0; border-bottom: 1px solid var(--ds-border); }
    .ds-duo__artefact .ds-artefact__annonce { order: 3; }
    .ds-duo__artefact .ds-artefact__corps   { order: 4; }
    .ds-duo__artefact .ds-artefact__pied    { order: 5; }
  }

  /* -------------------------------------------------------- 4.7 LIGNE DE PILE */
  /* 1 item = 1 decision = 1 action (PLAYBOOK design SaaS, niveau 2). */
  .ds-ligne {
    display: flex; align-items: flex-start; gap: var(--ds-space-3);
    padding: var(--ds-space-4) var(--ds-space-5);
    border-bottom: 1px solid var(--ds-border);
    background: var(--ds-surface);
    text-align: left;
  }
  .ds-ligne:last-child { border-bottom: 0; }
  .ds-ligne__corps  { flex: 1 1 auto; min-width: 0; }
  .ds-ligne__titre  { font-size: var(--ds-text-ui); color: var(--ds-text); line-height: var(--ds-leading-snug); }
  .ds-ligne__detail { font-size: var(--ds-text-caption); color: var(--ds-text-muted); margin-top: var(--ds-space-1); }
  a.ds-ligne, button.ds-ligne { width: 100%; border-left: 0; border-right: 0; cursor: pointer; text-decoration: none; }
  a.ds-ligne:hover, button.ds-ligne:hover { background: var(--ds-tint); }

  /* Chiffre de tete : l'etat du travail, en premier (niveau 1). */
  .ds-chiffre {
    font-family: var(--ds-font-mono);
    font-size: var(--ds-text-h2);
    font-weight: var(--ds-weight-medium);
    line-height: 1;
    letter-spacing: -0.01em;
    color: var(--ds-text);
    font-variant-numeric: tabular-nums;
  }
  .ds-chiffre__legende { font-size: var(--ds-text-caption); color: var(--ds-text-muted); margin-top: var(--ds-space-1); }
}

/* =============================================================================
   5. ETATS (§23 : loading, empty, conversational, artifact-panel)
   ========================================================================== */
@layer ds-states {

  /* -------------------------------------------------------- 5.1 CHARGEMENT */
  /* On ne montre jamais un ecran blanc : on montre la forme de ce qui arrive. */
  .ds-skeleton {
    display: block;
    border-radius: var(--ds-radius-sm);
    background: linear-gradient(
      90deg,
      var(--ds-surface-sunken) 0%,
      color-mix(in srgb, var(--ds-surface-sunken) 55%, var(--ds-border)) 50%,
      var(--ds-surface-sunken) 100%
    );
    background-size: 200% 100%;
  }
  .ds-skeleton--texte  { height: 0.9em; margin-block: 0.25em; }
  .ds-skeleton--titre  { height: 1.4em; width: 60%; }
  .ds-skeleton--bloc   { height: 96px; }
  @media (prefers-reduced-motion: no-preference) {
    .ds-skeleton { animation: dsSkeleton 1.4s linear infinite; }
  }
  @keyframes dsSkeleton {
    from { background-position: 200% 0; }
    to   { background-position: -200% 0; }
  }

  /* -------------------------------------------------------------- 5.2 VIDE */
  /* Jamais un mur de zeros : une action concrete, et ce qui travaille en fond. */
  .ds-vide {
    display: flex; flex-direction: column; align-items: flex-start;
    gap: var(--ds-space-3);
    padding: var(--ds-space-8) var(--ds-space-5);
    border: 1px dashed var(--ds-border-strong);
    border-radius: var(--ds-radius-md);
    background: var(--ds-surface-sunken);
  }
  .ds-vide__titre   { font-family: var(--ds-font-heading); font-size: var(--ds-text-h4); }
  .ds-vide__texte   { font-size: var(--ds-text-ui); color: var(--ds-text-muted); max-width: 52ch; }

  /* ------------------------------------------------------------- 5.3 ERREUR */
  .ds-alerte {
    display: flex; align-items: flex-start; gap: var(--ds-space-3);
    padding: var(--ds-space-4);
    border: 1px solid var(--ds-border);
    border-left: 3px solid var(--ds-text-muted);
    border-radius: var(--ds-radius-md);
    background: var(--ds-surface);
    font-size: var(--ds-text-ui);
  }
  .ds-alerte--risque  { border-left-color: var(--ds-risk); }
  .ds-alerte--attente { border-left-color: var(--ds-attente); }
  .ds-alerte--ok      { border-left-color: var(--ds-ok); }

  /* --------------------------------------------- 5.4 MOUVEMENT, INTERRUPTEUR */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }
  }
}

/* =============================================================================
   6. UTILITAIRES (le strict minimum : tout le reste passe par un composant)
   ========================================================================== */
@layer ds-utils {
  .ds-muted     { color: var(--ds-text-muted); }
  .ds-secondary { color: var(--ds-text-secondary); }
  .ds-mono      { font-family: var(--ds-font-mono); font-variant-numeric: tabular-nums; }
  .ds-lead      { font-size: var(--ds-text-lead); line-height: var(--ds-leading-normal); color: var(--ds-text-secondary); }
  .ds-caption   { font-size: var(--ds-text-caption); color: var(--ds-text-muted); }
  .ds-nowrap    { white-space: nowrap; }
}
