/* @generated — COPIE de packages/design-tokens/dist/tokens.css (jetons Moka, ADR-29 / ADR-141).
   NE PAS ÉDITER À LA MAIN. Rafraîchir : node apps/site-vitrine/scripts/sync-tokens.mjs
   Copié le 2026-09-16 depuis la branche claude/moka-novation-site-vitrine-2dc4e0. */

/* @generated par packages/design-tokens/build.mjs — NE PAS ÉDITER À LA MAIN.
   Source : packages/design-tokens/tokens.json (ADR-29). Cible : dist/tokens.css.
   Modifier tokens.json puis lancer : pnpm --filter @moka/design-tokens build */

:root {
  /* ===== COLOR — primitives ===== */
  --color-green-900: #0F6650;
  --color-green-600: #237A4F;
  --color-green-400: #8FD0C0;
  --color-green-200: #BFE3D8;
  --color-green-100: #E3F3EF;
  --color-green-50: #F5FAF8;
  --color-blue-800: #13647C;
  --color-blue-700: #1884A4;
  --color-blue-300: #6CCDE9;
  --color-blue-100: #DBF3FA;
  --color-violet-600: #4F46E5;
  --color-violet-100: #EDE9FE;
  --color-purple-700: #7A3FA0;
  --color-yellow-800: #7A5500;
  --color-yellow-700: #A87100;
  --color-yellow-500: #F1B823;
  --color-yellow-200: #F8DF9D;
  --color-yellow-50: #FDF7E7;
  --color-red-700: #B23A2A;
  --color-red-100: #FCE8E6;
  --color-neutral-950: #262626;
  --color-neutral-900: #333333;
  --color-neutral-800: #555555;
  --color-neutral-700: #666666;
  --color-neutral-500: #989898;
  --color-neutral-400: #A5A5A5;
  --color-neutral-300: #B2B2B2;
  --color-neutral-200: #CCCCCC;
  --color-neutral-100: #E5E5E5;
  --color-neutral-50: #F4F4F4;
  --color-neutral-0: #FFFFFF;
  --color-indigo: #3700CB;
  --color-terracotta: #C2593C;
  --color-status-error: #ED2124;
  --color-status-warning: #F1C44D;
  --color-status-success: #269924;

  /* ===== SEMANTIC ALIASES (à utiliser dans les composants) ===== */
  --fg-strong: var(--color-neutral-950);
  --fg-1: var(--color-neutral-900);
  --fg-2: var(--color-neutral-800);
  --fg-3: var(--color-neutral-700);
  --fg-4: var(--color-neutral-300);
  --fg-inverse: var(--color-neutral-0);
  --brand: var(--color-green-900);
  --brand-soft: var(--color-green-100);
  --link: var(--color-blue-800);
  --flux: var(--color-blue-700);
  --fg-attention: var(--color-yellow-800);
  --fg-danger: var(--color-red-700);
  --bg-page: var(--color-green-100);
  --bg-card: var(--color-neutral-0);
  --bg-subtle: var(--color-neutral-100);
  --bg-well: var(--color-neutral-50);
  --bg-well-focus: var(--color-neutral-0);
  --border-hairline: var(--color-neutral-50);
  --border-faint: var(--color-neutral-100);
  --border-subtle: var(--color-neutral-300);
  --border-strong: var(--color-neutral-900);

  /* ===== TYPE FAMILIES ===== */
  --font-display: 'Oswald', 'Arial Narrow', sans-serif;
  --font-body: 'Maven Pro', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, monospace;

  /* ===== TYPE SCALE ===== */
  --ratio: 1.25;
  --fs-kpi: 42.7px;
  --fs-display: 27.3px;
  --fs-page-title: 21.9px;
  --fs-component: 17.5px;
  --fs-body: 14px;
  --fs-label: 11.2px;
  --fs-quote: 11.2px;
  --lh-tight: 1.1;
  --lh-snug: 1.25;
  --lh-body: 1.55;
  --tracking-display: 0.04em;
  --tracking-label: 0.06em;

  /* ===== SPACING — 4px base scale ===== */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;
  --space-20: 80px;

  /* ===== DENSITY ===== */
  --row-density-tight: 3px 10px;
  --row-density-default: 5px 10px;
  --row-density-comfy: 8px 12px;
  --row-gap-tight: 0;
  --row-gap-default: 4px;
  --row-line-height: 1.5;

  /* ===== RADII ===== */
  --radius-xs: 2px;
  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 10px;
  --radius-xl: 16px;
  --radius-pill: 999px;
  --radius-key: 9px;
  --radius-tile: 13px;

  /* ===== SHADOWS — flat-first ===== */
  --shadow-1: 0 1px 0 rgba(15,102,80,0.06), 0 1px 2px rgba(51,51,51,0.04);
  --shadow-2: 0 2px 4px rgba(51,51,51,0.06), 0 4px 12px rgba(15,102,80,0.05);
  --shadow-3: 0 8px 24px rgba(51,51,51,0.10), 0 2px 6px rgba(15,102,80,0.06);
  --shadow-focus: 0 0 0 3px rgba(24,132,164,0.35);

  /* ===== RELIEF — géométrie du mouvement, socles, creux ===== */
  --lift-y-hover: -2px;
  --lift-y-press: 1px;
  --lift-dur: 130ms;
  --lift-brand: 0 4px 0 #8FD0C0, 0 8px 18px rgba(15, 102, 80, 0.20);
  --lift-brand-press: inset 0 2px 5px rgba(15, 102, 80, 0.28);
  --lift-neutral: 0 3px 0 #CCCCCC, 0 8px 18px rgba(51, 51, 51, 0.16);
  --lift-neutral-press: inset 0 2px 5px rgba(51, 51, 51, 0.14);
  --well: inset 0 2px 4px rgba(51, 51, 51, 0.16);
  --well-deep: inset 0 2px 4px rgba(0, 0, 0, 0.28);
  --lift-spread: 18px;
  --lift-clearance: 21px;
  --lift-neutral-drop: drop-shadow(0 3px 0 #CCCCCC) drop-shadow(0 8px 18px rgba(51, 51, 51, 0.16));
  --lift-brand-drop: drop-shadow(0 4px 0 #8FD0C0) drop-shadow(0 8px 18px rgba(15, 102, 80, 0.20));

  /* ===== RÉVÉLATION — durées du déroulé et du battement ===== */
  --reveal-dur: 260ms;
  --reveal-fade-in: 160ms;
  --reveal-fade-delay: 60ms;
  --pulse-dur: 2200ms;
  --pulse-spread: 5px;
  --pulse-size: 8px;

  /* ===== OVERLAY — voile et flou des couches flottantes ===== */
  --scrim: rgba(51, 51, 51, 0.48);
  --overlay-blur: 8px;

  /* ===== BREAKPOINTS / CONTAINERS / GRID / PAGE PADDING / HIT TARGETS ===== */
  --bp-xs: 0;
  --bp-sm: 640px;
  --bp-md: 768px;
  --bp-lg: 1024px;
  --bp-xl: 1280px;
  --container-md: 720px;
  --container-lg: 960px;
  --container-xl: 1200px;
  --grid-cols-xs: 4;
  --grid-cols-sm: 4;
  --grid-cols-md: 8;
  --grid-cols-lg: 12;
  --grid-cols-xl: 12;
  --gutter-xs: 16px;
  --gutter-sm: 16px;
  --gutter-md: 20px;
  --gutter-lg: 24px;
  --gutter-xl: 24px;
  --page-pad-xs: 16px;
  --page-pad-sm: 20px;
  --page-pad-md: 32px;
  --page-pad-lg: 48px;
  --page-pad-xl: 80px;
  --hit-target-min: 44px;
  --hit-target-dense: 32px;

  /* ===== Z-INDEX ===== */
  --z-base: 0;
  --z-sticky: 100;
  --z-overlay: 800;
  --z-modal: 1000;
  --z-toast: 1100;

  /* ===== MOTION ===== */
  --ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --ease-entrance: cubic-bezier(0, 0, 0, 1);
  --ease-exit: cubic-bezier(0.4, 0, 1, 1);
  --dur-1: 120ms;
  --dur-2: 200ms;
  --dur-3: 320ms;
}

/* Surfaces produit — appliquer au shell : <body data-surface="…"> */
[data-surface="control-tower"] { --bg-page: var(--color-green-100); }
[data-surface="diagram-gen"] { --bg-page: var(--color-yellow-50); }
[data-surface="tool-operator"] { --bg-page: var(--color-blue-100); }
[data-surface="dashboard"] { --bg-page: var(--color-neutral-100); }
[data-surface="marketing"] { --bg-page: var(--color-neutral-0); }
[data-surface="auth"] { --bg-page: var(--color-neutral-0); }
[data-surface="presentation"] { --bg-page: var(--color-green-50); }

/* ===== SOMBRE — sur choix explicite UNIQUEMENT =====
   La reprise derrière `@media (prefers-color-scheme: dark)` est VOLONTAIREMENT
   absente, arbitrage du 10/09/2026. Mesuré ce jour-là : le code de l'application
   porte 105 couleurs écrites en dur contre 38 appels aux rôles, et rien n'écrit
   encore l'estampille sur la page. Brancher la préférence du système ferait donc
   basculer, tout seul, un écran à moitié migré — texte clair sur cartes restées
   blanches — sans qu'un lecteur ait le moindre moyen d'en sortir.
   L'ordre retenu est l'inverse : livrer l'interrupteur, reprendre les couleurs en
   dur, PUIS rebrancher la préférence du système. Tant que ce bloc est seul, la
   palette sombre existe et se teste, mais ne s'applique qu'à qui la demande. */
:root[data-theme="dark"] {
  --fg-strong: #FFFFFF;
  --fg-1: #EBEFED;
  --fg-2: #A6B2AD;
  --fg-3: #8DA09A;
  --fg-4: #63736E;
  --fg-inverse: #0F1A16;
  --brand: var(--color-green-400);
  --brand-soft: #17332B;
  --link: var(--color-blue-300);
  --flux: var(--color-blue-300);
  --fg-attention: var(--color-yellow-500);
  --fg-danger: #DD7C6F;
  --bg-page: #0F1A16;
  --bg-card: #16231E;
  --bg-subtle: #21312B;
  --bg-well: #0F1A16;
  --bg-well-focus: #21312B;
  --border-hairline: #1E2A25;
  --border-faint: #25332D;
  --border-subtle: #2C3B35;
  --border-strong: #7C8F89;
  color-scheme: dark;
}
:root[data-theme="dark"] [data-surface="control-tower"], :root[data-theme="dark"][data-surface="control-tower"] { --bg-page: #0F1A16; }
:root[data-theme="dark"] [data-surface="diagram-gen"], :root[data-theme="dark"][data-surface="diagram-gen"] { --bg-page: #1E1A12; }
:root[data-theme="dark"] [data-surface="tool-operator"], :root[data-theme="dark"][data-surface="tool-operator"] { --bg-page: #101F24; }
:root[data-theme="dark"] [data-surface="dashboard"], :root[data-theme="dark"][data-surface="dashboard"] { --bg-page: #1A1A1A; }
:root[data-theme="dark"] [data-surface="marketing"], :root[data-theme="dark"][data-surface="marketing"] { --bg-page: #0E1A17; }
:root[data-theme="dark"] [data-surface="auth"], :root[data-theme="dark"][data-surface="auth"] { --bg-page: #0E1A17; }
:root[data-theme="dark"] [data-surface="presentation"], :root[data-theme="dark"][data-surface="presentation"] { --bg-page: #101A17; }

/* ===== RELIEF — utilitaires d'interaction (F-681) =====
   Plat au repos. Le survol déplie le socle et soulève de 2px ; le clic replie et
   retourne l'ombre vers l'intérieur. Une ombre au repos casse le langage — le
   survol n'aurait alors plus rien à dire.

     .ds-key         touche pressable : bouton, entrée de menu, ligne, onglet, tuile
     .ds-key--brand  la même sur une face vert foncé → socle vert clair
     .ds-well        creux gravé : champ de saisie, zone de texte
     .ds-well--rail  à AJOUTER à .ds-well sur un rail : contrôle segmenté, barre
                     d'onglets — le rail ne remonte pas au focus de ses enfants

   Un élément sélectionné (aria-pressed / aria-selected / data-selected) creuse au
   lieu de se soulever : la règle est plus bas. Un élément DÉPLIÉ, non — voir là. */
.ds-key {
  box-shadow: 0 0 0 rgba(0, 0, 0, 0);
  transition:
    transform var(--lift-dur) var(--ease-standard),
    box-shadow var(--lift-dur) var(--ease-standard),
    background-color var(--dur-1) var(--ease-standard);
}
.ds-key:hover:not(:disabled):not([aria-disabled='true']) {
  transform: translateY(var(--lift-y-hover));
  box-shadow: var(--lift-neutral);
}
.ds-key:active:not(:disabled):not([aria-disabled='true']) {
  transform: translateY(var(--lift-y-press));
  box-shadow: var(--lift-neutral-press);
}
.ds-key--brand:hover:not(:disabled):not([aria-disabled='true']) { box-shadow: var(--lift-brand); }
.ds-key--brand:active:not(:disabled):not([aria-disabled='true']) { box-shadow: var(--lift-brand-press); }
.ds-key:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.ds-key:disabled,
.ds-key[aria-disabled='true'] { transform: none; box-shadow: none; }

.ds-well {
  border: 0;
  /* La face du creux est un token de THÈME, pas une valeur du langage : en mode
     sombre l'échelle des fonds s'inverse (le fond subtil y est plus clair que la
     carte), donc un couple figé assombrirait le champ au focus au lieu de
     l'éclaircir. Chaque thème pose --bg-well / --bg-well-focus ; le repli sert
     les surfaces qui n'ont pas encore de couche sémantique. */
  background: var(--bg-well, var(--color-neutral-50));
  box-shadow: var(--well);
  transition:
    background-color var(--dur-2) var(--ease-standard),
    box-shadow var(--dur-2) var(--ease-standard),
    transform var(--dur-2) var(--ease-standard);
}
.ds-well:focus,
.ds-well:focus-within {
  outline: none;
  background: var(--bg-well-focus, var(--color-neutral-0));
  transform: translateY(-1px);
  box-shadow: var(--lift-neutral), inset 0 0 0 2px var(--color-green-600);
}

/* Rail creusé — contrôle segmenté, barre d'onglets. Même creux, mais il ne remonte
   pas au focus : ce sont ses enfants qui sont manipulables, pas lui. Sans ce
   variant, tabuler dans un onglet ferait lever toute la barre. */
.ds-well--rail:focus-within {
  background: var(--bg-well, var(--color-neutral-50));
  transform: none;
  box-shadow: var(--well);
}

/* La sélection creuse, là où le survol soulève : une touche déjà enfoncée ne se
   soulève plus, et son survol ne fait que l'enfoncer davantage. Les deux gestes ne
   se confondent donc jamais. Sans cette règle ici, chaque surface la réécrirait à
   sa façon et les trois applications divergeraient.

   L'attribut "aria-expanded" n'y figure PAS, et c'est un choix : déplier n'est pas
   choisir. Il a d'abord été rangé avec les états enfoncés, ce qui donnait un creux
   permanent à tout en-tête de section ouverte et à tout chevron déplié — une dizaine
   de creux simultanés dans un arbre. Ce qu'un contenu ouvert doit dire, il le dit par
   sa PRÉSENCE et par la face de son en-tête, pas par un relief emprunté à la
   sélection. Une bascule qui reste réellement enfoncée porte "aria-pressed".
   Arbitré par le développeur le 11/08/2026.
   (Aucun accent grave dans ce commentaire : il est DANS un gabarit de chaîne, et une
   paire suffit à le refermer en silence — tsc et node --check restent verts.)

   Les :not() portent le POIDS, pas la logique : la règle de survol ci-dessus pèse
   quatre classes, il faut l'égaler pour gagner par l'ordre puis la dépasser pour le
   survol. Le focus clavier est préservé — l'anneau de focus ne se laisse jamais
   recouvrir — et le désactivé garde la main sur tout. */
.ds-key:is([aria-pressed='true'], [aria-selected='true'], [data-selected='true']):not(:disabled):not(:focus-visible) {
  transform: none;
  box-shadow: var(--well);
}
.ds-key:is([aria-pressed='true'], [aria-selected='true'], [data-selected='true']):hover:not(:disabled):not(:focus-visible) {
  transform: none;
  box-shadow: var(--well-deep);
}
.ds-key--brand:is([aria-pressed='true'], [aria-selected='true'], [data-selected='true']):not(:disabled):not(:focus-visible) {
  box-shadow: var(--lift-brand-press);
}

/* EXCEPTION — dans un rail, la pastille active reste SURÉLEVÉE : c'est le contrat du
   contrôle segmenté, la sélection y est une touche posée en avant, pas un creux. Ce
   qui la distingue alors du survol n'est pas le relief mais la FACE — vert clair pour
   le choisi, blanc pour le survolé. Sans cette paire, les deux portent la même ombre
   et l'élément survolé se fait passer pour l'élément choisi.
   Les composants ne doivent donc PAS poser ces fonds en style inline : ils gagneraient
   contre ces règles et rétabliraient la confusion. */
.ds-well--rail .ds-key:is([aria-pressed='true'], [aria-selected='true'], [data-selected='true']):not(:disabled):not(:focus-visible),
.ds-well--rail .ds-key:is([aria-pressed='true'], [aria-selected='true'], [data-selected='true']):hover:not(:disabled):not(:focus-visible) {
  transform: translateY(-1px);
  box-shadow: var(--lift-neutral);
  background: var(--brand-soft, var(--color-green-100));
  color: var(--brand, var(--color-green-900));
}
.ds-well--rail .ds-key:hover:not([aria-pressed='true']):not([aria-selected='true']):not([data-selected='true']):not(:disabled) {
  background: var(--bg-well-focus, var(--color-neutral-0));
}

/* Rail défilant : la barre de défilement native traversait le creux juste sous les
   pastilles. On la masque et on ramène l'élément choisi dans le champ — sans quoi,
   une fois sorti par défilement, plus rien n'indique lequel est choisi. */
.ds-rail-scroll {
  scrollbar-width: none;
  scroll-behavior: smooth;
  scroll-padding-inline: var(--space-2);
}
.ds-rail-scroll::-webkit-scrollbar { display: none; }

/* Le mouvement est un renfort, jamais le seul porteur d'information : sans lui,
   l'ombre suffit à distinguer repos / survol / pressé. */
@media (prefers-reduced-motion: reduce) {
  .ds-key,
  .ds-well { transition-duration: 1ms; }
  .ds-key:hover,
  .ds-key:active,
  .ds-well:focus,
  .ds-well:focus-within { transform: none; }
}

/* ===== RÉVÉLATION — utilitaires d'apparition (CHG-568) =====
   Un contenu qui prend sa place déroule sa hauteur ; il ne surgit pas. Sauter d'un coup
   fait bouger la page sous l'œil et fait perdre la ligne qu'on lisait.

     .ds-reveal          conteneur qui s'ouvre et se ferme — piloté par data-open
     .ds-reveal--roomy   la même, quand elle contient des touches Relief : rend au socle
                         la place que le rognage lui prendrait, sans décaler le contenu
     .ds-pulse           pastille qui signale, à distance, un contenu replié à retrouver

   Ouvrir/fermer se pilote par data-open sur le conteneur, JAMAIS en montant et démontant
   le contenu : un enfant qu'on retire du DOM n'a aucune hauteur à animer en sortant. */
.ds-reveal {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--reveal-dur) var(--ease-standard);
}
.ds-reveal > * {
  overflow: hidden;
  min-height: 0;
  opacity: 0;
  /* Replié, le contenu occupe une hauteur nulle mais reste dans le DOM — c'est ce qui
     lui donne une hauteur à animer en sortant. Sans visibility, il resterait donc
     TABULABLE : au clavier, un menu fermé continuerait de traverser ses entrées
     invisibles. La règle vit ici et pas chez le consommateur, sinon chaque surface
     doit y penser — et l'attribut inert, qui la lèverait aussi, n'est pas connu de
     react-dom 18.3.1.
     Le délai est ce qui rend la chose correcte : à la fermeture, visibility ne bascule
     qu'une fois le déroulé fini (var(--reveal-dur)), sans quoi le contenu
     disparaîtrait d'un coup au lieu de se replier. À l'ouverture, bascule immédiate. */
  visibility: hidden;
  transition:
    opacity var(--reveal-fade-in) var(--ease-exit),
    visibility 0s linear var(--reveal-dur);
}
.ds-reveal[data-open='true'] { grid-template-rows: 1fr; }
.ds-reveal[data-open='true'] > * {
  opacity: 1;
  visibility: visible;
  /* Le contenu entre APRÈS que la place se soit faite — sinon on lit un texte qui
     glisse encore. Il sort en revanche sans délai : refermer doit être net. */
  transition:
    opacity var(--reveal-fade-in) var(--ease-entrance) var(--reveal-fade-delay),
    visibility 0s;
}

/* Un contenu révélé est rogné par construction — c'est ce qui lui donne sa hauteur
   animable. Une touche Relief posée dedans y perdrait donc son socle, latéralement et
   surtout par le bas. On lui rend cette place en marge intérieure, reprise en marge
   négative pour que rien ne se décale à l'écran. Les deux littéraux sont ceux de Relief,
   var(--lift-spread) et var(--lift-clearance) ; si tu ajoutes une règle ici, cite de
   même le token dont elle sort, ou n'en ajoute pas. */
.ds-reveal--roomy > * {
  padding: 2px var(--lift-spread) var(--lift-clearance);
  margin: -2px calc(var(--lift-spread) * -1) calc(var(--lift-clearance) * -1);
}

/* Pastille de rappel : dans un menu réduit, elle dit qu'un groupe fermé contient la page
   ouverte — une information autrement invisible, puisque le libellé actif est caché.
   Le halo bat lentement ; il attire l'œil sans réclamer le clic. La couleur reste seule
   porteuse au repos, le battement n'est qu'un renfort. */
.ds-pulse {
  position: relative;
  display: inline-block;
  width: var(--pulse-size);
  height: var(--pulse-size);
  border-radius: 50%;
  background: var(--color-green-600);
}
.ds-pulse::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  animation: ds-pulse-halo var(--pulse-dur) var(--ease-standard) infinite;
}
@keyframes ds-pulse-halo {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--color-green-600) 50%, transparent); }
  70%  { box-shadow: 0 0 0 var(--pulse-spread) color-mix(in srgb, var(--color-green-600) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--color-green-600) 0%, transparent); }
}

/* Le déroulé est un confort de lecture, pas le message : sans lui, l'état ouvert reste
   entièrement lisible. La pastille garde sa couleur, elle perd seulement son battement. */
@media (prefers-reduced-motion: reduce) {
  .ds-reveal,
  .ds-reveal > * { transition-duration: 1ms; transition-delay: 0ms; }
  .ds-pulse::after { animation: none; }
}
