/* =============================================================
   Socle · Couche 2 : tokens sémantiques
   Des rôles, pas des valeurs. Chaque token pointe vers une
   primitive. C'est LA seule couche que les composants
   consomment. Le thème sombre est une simple redéfinition de
   cette couche sous [data-theme="dark"].
   ============================================================= */

:root {
  color-scheme: light;

  /* ---------------------------------------------------------
     Couleur · Fonds
     --------------------------------------------------------- */
  --color-bg-page: var(--neutral-50);          /* fond de page */
  --color-bg-surface: var(--neutral-0);        /* cartes, panneaux, champs */
  --color-bg-subtle: var(--neutral-100);       /* zones secondaires, fonds adoucis */
  --color-bg-hover: var(--neutral-100);        /* survol d'un élément posé sur une surface */
  /* Enfoncé d'un élément posé sur une surface, et survol d'un élément
     déjà posé sur --color-bg-subtle : le survol neutre vaut la même
     valeur que le fond adouci, il ne s'y verrait pas. */
  --color-bg-active: var(--neutral-200);
  /* Troisième cran neutre, celui qui manquait. Enfoncé d'un élément
     posé sur --color-bg-subtle, dont le survol a déjà pris le cran
     au-dessus : sans lui, survol et enfoncé rendent le même pixel.
     Lu par l'étiquette et sa croix, l'onglet pastille, la croix de
     l'alerte neutre. */
  --color-bg-subtle-active: var(--neutral-300);
  /* Bande de balayage d'une forme d'attente. Seul fond neutre qui ne
     suit pas l'inversion de la palette : un miroitement reste plus
     clair que son aplat dans les deux thèmes, ce qu'aucun couple de
     rôles neutres ne peut tenir puisque l'échelle s'inverse.
     Lu par le balayage du squelette. */
  --color-bg-shimmer: var(--neutral-0);
  --color-bg-inverse: var(--neutral-900);      /* tooltips, éléments inversés */
  --color-bg-overlay: color-mix(in srgb, var(--neutral-1000) 40%, transparent); /* voile des modales */
  /* Ce que le voile fait à ce qu'il couvre, en plus de l'assombrir.
     Aucune direction artistique par défaut : c'est l'assombrissement
     qui sépare, et le flou n'est là que pour qu'un bord de panneau ne
     se pose pas sur du détail dur. Lu par le voile du dialogue et par
     celui du tiroir. */
  --overlay-filter: blur(8px);

  /* ---------------------------------------------------------
     Couleur · Texte
     --------------------------------------------------------- */
  --color-text: var(--neutral-900);
  --color-text-secondary: var(--neutral-800);
  --color-text-muted: var(--neutral-600);
  --color-text-inverse: var(--neutral-50);
  --color-text-on-accent: var(--neutral-50);
  /* L'ambre reste clair dans les deux thèmes : son texte ne bascule pas. */
  --color-text-on-warning: var(--neutral-950);
  /* Le vert et le rouge, eux, changent de cran d'un thème à l'autre :
     600 en clair, 500 en sombre, donc plus clairs en sombre. Un texte
     clair fixe posé dessus tombe à 3,27 sur le vert et à 4,05 sur le
     rouge du thème sombre. Ces deux rôles reprennent donc le remède de
     l'ambre, un texte qui bascule avec le fond, au lieu d'emprunter
     --color-text-on-accent qui, lui, ne bascule pas.
     Lus par le badge à fond plein en --success et en --danger, et par le
     bouton destructif, qui est le même aplat que ce badge et ne peut pas
     s'en écarter. Le sens du survol et de l'enfoncé d'un aplat découle
     de ce choix : voir le bloc du thème sombre. */
  --color-text-on-success: var(--neutral-0);
  --color-text-on-danger: var(--neutral-0);
  --color-text-link: var(--blue-600);
  --color-text-link-hover: var(--blue-700);

  /* ---------------------------------------------------------
     Couleur · Bordures
     --------------------------------------------------------- */
  --color-border: var(--neutral-200);
  --color-border-strong: var(--neutral-300);
  --color-border-hover: var(--neutral-400);   /* contrôle survolé */
  --color-border-active: var(--neutral-500);  /* contrôle enfoncé */
  --color-control-track: var(--neutral-300);  /* piste éteinte d'un interrupteur */
  /* Le pouce d'un interrupteur, au repos et enfoncé. Comme
     --color-bg-shimmer, ce rôle ne suit pas l'inversion de la palette :
     un pouce se lit clair sur sa piste dans les deux thèmes, parce que
     c'est un objet posé dessus. --color-bg-surface tenait ce rôle et
     ne marchait qu'en clair, par coïncidence : en sombre il donnait un
     pouce plus foncé que sa piste, donc un trou percé dans la pilule
     plutôt qu'un bouton. Lu par le pouce de l'interrupteur. */
  --color-control-knob: var(--neutral-0);
  --color-control-knob-active: var(--neutral-100);

  /* ---------------------------------------------------------
     Couleur · Accent
     --------------------------------------------------------- */
  --color-accent: var(--blue-600);
  --color-accent-hover: var(--blue-700);
  --color-accent-active: var(--blue-800);
  --color-accent-subtle: var(--blue-50);
  --color-accent-subtle-hover: var(--blue-100);
  --color-accent-border: var(--blue-200);
  --color-accent-text: var(--blue-600);
  /* L'arête d'un aplat d'accent : un cran plus soutenu que lui. C'est
     ce qui donne son assise au bouton primaire, et c'est la signature
     de forme la plus reconnaissable de la référence. Un aplat sans
     arête flotte, un aplat cerné se pose. */
  --color-accent-edge: var(--blue-700);

  /* ---------------------------------------------------------
     Couleur · Commande principale
     L'aplat que porte le bouton primaire, et lui seul.
     Il a longtemps été l'accent, parce que c'est le cas le plus
     répandu et que Socle n'avait qu'une direction artistique. Deux
     peaux d'essai sur trois le démentent : save.design pose un aplat
     blanc et garde son magenta pour le trait et le texte ; la console
     Vibecoded pose l'encre pleine et réserve l'accent aux jauges et
     aux points actifs, en écrivant la raison, « sur un fond presque
     noir, du blanc plein est l'élément le plus contrasté de la page,
     donc l'action principale se voit sans avoir besoin d'une
     couleur ». Tant que le bouton lisait --color-accent, une peau qui
     voulait ce geste devait teindre avec lui la case à cocher, le
     bouton radio, l'interrupteur et la barre de progression.
     Ce que dit un accent : voilà la couleur de la marque. Ce que dit
     une commande : voilà où cliquer. Deux intentions.
     Par défaut la commande est l'accent, donc rien ne bouge dans la
     DA de Socle. Le bloc est répété dans le thème sombre parce qu'une
     variable qui pointe vers une autre est résolue là où elle est
     déclarée, pas là où elle est lue. */
  --color-action: var(--color-accent);
  --color-action-hover: var(--color-accent-hover);
  --color-action-active: var(--color-accent-active);
  --color-action-text: var(--color-text-on-accent);
  --color-action-edge: var(--color-accent-edge);

  /* L'aplat du bouton destructif, sur le modèle de --color-action.
     Le même besoin, pour la même raison : Vibecoded n'a aucun aplat
     rouge, sa variante destructive est creuse, filet en rouge à 30 %
     sur un fond discret. Tant que le bouton lisait --color-danger,
     obtenir ce geste emmenait avec lui le filet d'un champ invalide,
     l'icône d'un toast et la barre de progression, qui doivent rester
     au cran le plus visible.
     Par défaut c'est le rouge plein, donc rien ne bouge. */
  --color-destructive: var(--red-50);
  --color-destructive-hover: var(--red-100);
  --color-destructive-active: var(--red-200);
  --color-destructive-text: var(--red-700);
  --color-destructive-edge: color-mix(in srgb, var(--red-700) 30%, transparent);

  /* ---------------------------------------------------------
     Couleur · Statuts
     (motif : solid / hover / subtle / border / text)
     --------------------------------------------------------- */
  --color-success: var(--green-700);
  --color-success-hover: var(--green-800);
  --color-success-subtle: var(--green-50);
  --color-success-border: var(--green-200);
  --color-success-text: var(--green-700);

  --color-danger: var(--red-700);
  --color-danger-hover: var(--red-800);
  --color-danger-active: var(--red-900);
  --color-danger-subtle: var(--red-50);
  /* Second cran du fond discret, sur le modèle de
     --color-accent-subtle-hover : item de menu --danger enfoncé,
     croix d'une étiquette .sc-tag--danger survolée. */
  --color-danger-subtle-hover: var(--red-100);
  --color-danger-border: var(--red-200);
  --color-danger-text: var(--red-700);
  --color-danger-edge: var(--red-800);

  --color-warning: var(--amber-500);
  --color-warning-hover: var(--amber-600);
  --color-warning-subtle: var(--amber-50);
  --color-warning-border: var(--amber-200);
  --color-warning-text: var(--amber-800);

  /* L'information ne recopie pas ses primitives, elle reprend l'accent.
     Les deux familles gardent des noms distincts parce qu'elles disent
     deux intentions, une information et un accent de marque, et c'est
     pourquoi on ne les fusionne pas. Mais leurs valeurs se ressemblent
     aujourd'hui : en rattachant l'une à l'autre, une marque qui change
     de teinte emmène l'information avec elle sans qu'on ait à y penser. */
  --color-info: var(--color-accent);
  --color-info-subtle: var(--color-accent-subtle);
  --color-info-border: var(--color-accent-border);
  --color-info-text: var(--color-accent-text);

  /* Les quatre séries d'un graphique, dans l'ordre. Un produit qui en
     trace prend --color-series-1 puis 2 puis 3 puis 4, sans sauter ni
     réarranger : c'est l'ordre qui garantit que deux séries voisines
     restent distinctes en vision déficiente. Une cinquième ne s'invente
     pas, elle se replie en « tout le reste » ou passe en petits
     multiples. */
  --color-series-1: var(--serie-1);
  --color-series-2: var(--serie-2);
  --color-series-3: var(--serie-3);
  --color-series-4: var(--serie-4);

  /* ---------------------------------------------------------
     Texture du fond de page
     Le sol de la page, quand il n'est pas un aplat. Une trame de
     points, un quadrillage, un grain : c'est ce qui se voit en
     premier d'une direction artistique, avant le moindre composant,
     et aucun rôle ne le portait. Une peau devait alors écrire une
     règle CSS à elle, ce qu'une peau n'a pas le droit de faire.
     Par défaut il n'y en a pas. Lu par le corps de la page. */
  --page-texture: radial-gradient(color-mix(in srgb, var(--neutral-900) 5%, transparent) 1px, transparent 0);
  --page-texture-size: 24px 24px;

  /* ---------------------------------------------------------
     Épaisseurs de trait
     Trois intentions, pas trois valeurs : ce qui délimite, ce qui
     signale, et ce qui dessine. Un composant qui écrivait --border-1
     disait « 1 pixel » au lieu de dire « un filet ».
     --------------------------------------------------------- */
  --border-hairline: var(--border-1);   /* ce qui délimite ou sépare : cadres, filets, séparateurs */
  --border-emphasis: var(--border-2);   /* ce qui signale : indicateur d'onglet, anneau d'avatar */
  --icon-stroke-width: var(--border-2); /* ce qui dessine : glyphe tracé en CSS, à égalité avec le stroke-width des SVG */

  /* ---------------------------------------------------------
     Focus
     --------------------------------------------------------- */
  --color-focus-ring: var(--blue-600);
  --focus-ring-width: var(--border-2);
  --focus-ring-offset: var(--space-2);

  /* ---------------------------------------------------------
     Opacités
     --------------------------------------------------------- */
  --opacity-disabled: 0.5;

  /* ---------------------------------------------------------
     Espacement sémantique
     Les composants consomment ces rôles, jamais --space-*.
     Changer --padding-s ici retouche tout ce qui s'en sert.
     --------------------------------------------------------- */
  --padding-2xs: var(--space-4);
  --padding-xs: var(--space-6);
  --padding-s: var(--space-8);
  --padding-m: var(--space-10);
  --padding-l: var(--space-14);
  --padding-xl: var(--space-16);
  --padding-2xl: var(--space-20);
  --padding-3xl: var(--space-24);
  --padding-4xl: var(--space-32);

  --gap-2xs: var(--space-4);
  --gap-xs: var(--space-6);
  --gap-s: var(--space-8);
  --gap-m: var(--space-10);
  --gap-l: var(--space-14);
  --gap-xl: var(--space-20);
  --gap-2xl: var(--space-24);
  /* Le rythme d'une page, pas celui d'une carte. L'échelle s'arrêtait
     à 32px, l'écart qui sépare deux blocs à l'intérieur d'une même
     surface : une page bordée de --padding-4xl dont les sections se
     suivent à 32px se lit comme une seule masse. Ce cran tombe sur la
     même primitive que --padding-4xl, comme --gap-xl tombe déjà sur
     celle de --padding-2xl. Lu par l'empilement des sections d'une
     page et par la gouttière entre une colonne principale et son
     aparté. */
  --gap-3xl: var(--space-32);

  /* ---------------------------------------------------------
     Mesures
     Les largeurs que porte la mise en page et qu'aucun composant ne
     détient : la page, la colonne de lecture, le repli d'une grille.
     Sans elles, trois pages du même système ont écrit trois nombres
     différents pour la même intention. Ces rôles existent pour que le
     nombre soit dit une seule fois.
     Un point de bascule, lui, n'est pas ici : @media n'accepte pas
     var(), et la couche de mise en page écrit son seuil en clair.
     --------------------------------------------------------- */

  /* Largeur maximale d'une page, gouttières comprises. C'est la somme
     des pièces que le système pose déjà : une barre latérale à 16rem,
     une gouttière --gap-l, une colonne de contenu à 56rem, et
     --padding-2xl de chaque côté. Lue par la coquille de page et par
     la bande d'en-tête qui doit s'aligner sur elle. */
  --measure-page: 76rem;

  /* Largeur maximale d'une colonne de texte courant. Cette
     documentation plafonne déjà sa prose à 44rem en trois endroits, et
     le plus large des dialogues vaut la même chose : un texte ne
     s'étale jamais plus loin que la surface qui le porte. Lue par le
     corps d'un article et par le chapeau d'un en-tête de page. */
  --measure-prose: 44rem;

  /* Largeur minimale d'une colonne de grille avant repli, à donner au
     premier argument du minmax() d'un repeat(auto-fit, ...). 16rem est
     déjà une largeur du système, celle de la barre latérale et le
     plafond de la bulle d'aide, et c'est le compte qui laisse trois
     colonnes à côté d'une barre latérale sur une page à
     --measure-page. Lue par la grille de cartes de mesures d'un
     tableau de bord et par la grille de champs d'un formulaire. */
  --measure-column-min: 16rem;

  /* ---------------------------------------------------------
     Rayons sémantiques
     --------------------------------------------------------- */
  --radius-control-s: var(--radius-2); /* petits contrôles : case à cocher, puce */
  --radius-control: var(--radius-4);   /* boutons, champs, items de menu */
  --radius-surface: var(--radius-4);   /* cartes, alertes, popovers */
  --radius-overlay: var(--radius-8);  /* dialogues, panneaux */
  --radius-pill: var(--radius-full);   /* badges, avatars, interrupteurs */

  /* ---------------------------------------------------------
     Typographie sémantique
     --------------------------------------------------------- */
  --font-body: var(--font-sans);
  --font-heading: var(--font-display);
  --font-code: var(--font-mono);
  /* Le sur-titre technique en chasse fixe : STATUS, COMPUTE, les
     intitulés en capitales espacées. Un rôle à lui plutôt que
     --font-code dans base.css : un sur-titre n'est pas du code, et
     une marque qui garde ses sur-titres en linéale ne repointe que
     ce token. */
  --font-overline: var(--font-mono);

  /* 30 et non 40. Vibecoded a deux valeurs pour ce rôle et j'avais pris
     la mauvaise : 40 est le titre de leur site, 22 celui d'une page de
     console. Socle est un système d'application, pas de page vitrine.
     30 est le cran de notre échelle le plus proche du leur sans écraser
     le titre de calque juste en dessous. */
  --text-h1-size: var(--font-size-30);
  --text-h1-weight: var(--font-weight-regular);
  --text-h2-size: var(--font-size-24);
  --text-h2-weight: var(--font-weight-regular);
  /* 20 : c'est le cran qui manquait entre le titre de calque et le
     titre de carte, et celui qu'un nom de produit réclame. À 18 il
     passait sous les boutons posés à côté de lui, à 24 il écrasait la
     carte. */
  --text-h3-size: var(--font-size-20);
  --text-h3-weight: var(--font-weight-regular);
  --text-h4-size: var(--font-size-14);
  --text-h4-weight: var(--font-weight-semibold);

  /* Le chiffre d'une mesure : celui qu'une carte de tableau de bord
     pose en grand, seul, sous son intitulé. L'échelle s'arrêtait à
     --text-h1-size, et deux pages construites à l'aveugle avec l'export
     ont détourné --text-h2-size pour l'afficher, chacune rajoutant à la
     main l'interlignage, l'interlettrage et les chiffres tabulaires.
     Un chiffre n'est pas un titre : il n'ouvre pas une section, il se
     lit d'un seul regard, donc il passe devant les titres et prend le
     cran des grands affichages, le seul de l'échelle qu'aucun rôle ne
     réclamait.
     Il se pose avec --leading-tight, --tracking-tight et
     font-variant-numeric: tabular-nums. Sans les chiffres tabulaires,
     deux cartes côte à côte ne font pas tomber leurs unités au même
     endroit, et c'est exactement ce que les deux pages avaient dû
     retrouver seules.
     La graisse a un rôle à elle plutôt que d'emprunter celle d'un
     titre : c'est la molette qui laisse un chiffre peser plus lourd
     sans emmener les titres avec lui.
     Lu par la carte de mesure à venir. Un tableau de chiffres partage
     son intention d'alignement mais pas sa taille : il garde
     --text-body-size et ses chiffres tabulaires, que .sc-table .is-num
     pose déjà. */
  --text-metric-size: var(--font-size-32);
  --text-metric-weight: var(--font-weight-semibold);

  --text-body-size: var(--font-size-14);
  --text-body-s-size: var(--font-size-13);
  --text-label-size: var(--font-size-14);     /* ce qui nomme un champ, un onglet, une colonne */
  /* Le libellé porté par un contrôle. Séparé de --text-label-size :
     un bouton se resserre avec sa hauteur, l'étiquette posée au-dessus
     d'un champ garde la taille du texte qui l'entoure. */
  --text-action-s-size: var(--font-size-13);
  --text-action-size: var(--font-size-14);
  --text-action-l-size: var(--font-size-15);
  --text-caption-size: var(--font-size-12);   /* légendes, aides */
  --text-code-size: var(--font-size-13);

  /* Graisses. Les titres ont les leurs plus haut ; celles-ci disent le
     rôle d'un texte courant : ce qui se lit, ce qui nomme une commande,
     ce qui ouvre une section. */
  --text-body-weight: var(--font-weight-regular);    /* le texte qui se lit */
  --text-label-weight: var(--font-weight-medium);    /* ce qui nomme : bouton, onglet, badge, en-tête de tableau */
  --text-strong-weight: var(--font-weight-semibold); /* ce qui ouvre : titre de toast, marque, sur-titre de section */

  /* Interlignage et interlettrage : des rôles, pour que les
     composants n'aient jamais à toucher aux primitives. */
  --leading-tight: var(--line-height-tight);       /* titres, et chiffre de mesure */
  --leading-snug: var(--line-height-snug);         /* libellés sur deux lignes */
  --leading-normal: var(--line-height-normal);     /* corps par défaut */
  --leading-relaxed: var(--line-height-relaxed);   /* prose longue */

  --tracking-tight: var(--letter-spacing-tight);   /* grands titres, chiffre de mesure */
  --tracking-normal: var(--letter-spacing-normal);
  --tracking-wide: var(--letter-spacing-wide);     /* capitales, sur-titres */

  /* ---------------------------------------------------------
     Contrôles
     Trois familles qui font la densité d'un bouton, d'un champ ou
     d'un select : sa hauteur, ses épaules, la taille de son libellé.
     C'est le seul endroit où se règle cette densité, et elles se
     tiennent : changer la hauteur sans les épaules donne un contrôle
     qui a l'air écrasé.
     --------------------------------------------------------- */
  --control-height-s: 2rem;  /* 26px */
  --control-height-m: 2.5rem;      /* 32px */
  --control-height-l: 3rem;    /* 40px */

  /* Les épaules d'un bouton : son padding horizontal. Elles pointent
     sur les primitives et non sur --padding-*, parce qu'un bouton et
     une carte n'ont pas à respirer ensemble. Resserrer les contrôles
     ne doit pas resserrer les pages.
     Les 24 de la référence sont ceux de son bouton d'invitation, celui
     qu'une page vitrine pose une fois au milieu d'un écran. Posés par
     défaut, ils donnent une barre d'outils dont chaque bouton réclame
     la place d'une phrase. Ils descendent donc au grand cran, là où
     l'invitation a sa place, et le cran ordinaire prend 16. */
  --control-padding-s: var(--space-12);
  --control-padding: var(--space-16);
  --control-padding-l: var(--space-24);

  /* Une seule échelle de hauteurs, et c'est une règle de composition
     avant d'être une règle de dessin : deux commandes qui peuvent se
     retrouver côte à côte doivent obéir aux mêmes crans. Un champ posé
     dans une barre à côté d'un bouton, un sélecteur entre les deux, un
     contrôle segmenté au bout : si « moyen » ne veut pas dire la même
     hauteur pour tous, la barre se casse.
     C'est pour ça qu'il n'existe pas de --field-height-*. J'en avais
     ajouté un après la première réponse de Vibecoded, qui pose son
     champ à 48 quand son bouton fait 36 et assume qu'ils ne s'alignent
     pas. Mesuré chez nous, ça donnait --s à 26 sur un bouton et 36 sur
     un champ, --m à 36 et 48, et un --l qui ne faisait rien sur un
     champ. Les noms mentaient. Une règle qu'on ne peut pas enfreindre
     vaut mieux qu'une règle écrite quelque part.
     Ce qui reste séparé, ce sont les épaules : elles ne décident
     d'aucun alignement vertical. */

  /* La taille du texte saisi. Elle ne suit pas celle d'un libellé de
     bouton : un libellé se lit, un texte saisi se relit et se corrige.
     C'est aussi le seul rôle du système dont la valeur a une
     conséquence hors du dessin, le zoom d'iOS à 16px. */
  --text-field-s-size: var(--font-size-13);
  --text-field-size: var(--font-size-16);
  --text-field-l-size: var(--font-size-16);

  /* Les épaules d'un champ, et elles ne sont pas celles d'un bouton.
     Les deux ont partagé un seul rôle tant que Socle a été la seule
     direction artistique du système : les mêmes 12px allaient aux deux,
     donc rien ne les distinguait. Deux peaux d'essai les ont séparées
     coup sur coup. save.design pose 24px d'épaules à ses boutons, et un
     champ dont le texte commence à 24px du bord ne se lit plus. Le
     tableau de bord de Supabase écarte les deux de la même façon, 16px
     au bouton et 12px au champ.
     Un bouton écarte ses épaules pour donner du corps à une cible que
     l'on vise ; un champ les resserre pour rapprocher le texte saisi de
     son cadre. Deux intentions, donc deux rôles, et les hauteurs, elles,
     restent communes puisqu'un champ et un bouton posés côte à côte dans
     une barre doivent s'aligner.
     Les valeurs par défaut restent celles des boutons : la DA de Socle
     ne bouge pas d'un pixel, c'est la possibilité de les séparer qui
     apparaît. Lues par le champ de saisie, la zone de texte et le
     déclencheur du sélecteur. */
  --field-padding-s: var(--space-12);
  --field-padding: var(--space-16);
  --field-padding-l: var(--space-20);

  /* ---------------------------------------------------------
     Élévation
     --------------------------------------------------------- */
  --elevation-raised: var(--shadow-xs);    /* cartes survolées : le système est à plat, la bordure fait le travail */
  --elevation-floating: var(--shadow-m);   /* menus, popovers, tooltips */
  --elevation-overlay: var(--shadow-l);    /* dialogues, panneaux */
  --elevation-toast: var(--shadow-l);

  /* ---------------------------------------------------------
     Mouvement
     --------------------------------------------------------- */
  /* Raccourcis durée + courbe, pour les transitions. */
  --motion-fast: var(--duration-fast) var(--ease-standard);
  --motion-base: var(--duration-base) var(--ease-standard);
  --motion-slow: var(--duration-slow) var(--ease-out);

  /* Parties séparables, pour les animations et les délais où le
     raccourci ci-dessus ne peut pas servir. */
  --motion-duration-fast: var(--duration-fast);
  --motion-duration-base: var(--duration-base);
  --motion-duration-slow: var(--duration-slow);
  /* Durée d'un aller de boucle d'attente : barre de progression
     indéterminée, balayage d'un squelette de chargement. */
  --motion-duration-loop: calc(var(--duration-slow) * 5);

  /* Trois durées qui disent une intention plutôt qu'une vitesse.
     --motion-duration-fast et ses deux sœurs disent « vite », « moyen »,
     « lent », ce qui ne suffit pas à placer une durée : rien n'y dit
     qu'un panneau qui s'ouvre et le même panneau qui se ferme ne
     durent pas pareil. Vibecoded l'écrit en une phrase, et c'est la
     règle qui tient sous tous leurs chiffres : « une entrée se regarde,
     une sortie s'attend. Tout ce qui sort est plus rapide que ce qui
     entre, et le rapport tourne autour de deux tiers. »
     Une entrée, on découvre ce qui arrive. Une sortie, on a déjà décidé
     et on attend que ça libère la place. Une traversée, elle, ne change
     pas d'état : elle parcourt une distance, et c'est le seul cas où le
     geste a le droit d'être plus long qu'une entrée.
     Lues par le dialogue, le tiroir, le menu, le sélecteur, le popover,
     le toast et la pastille d'un contrôle segmenté. */
  --motion-duration-enter: var(--duration-slow);
  --motion-duration-leave: var(--duration-fast);
  --motion-duration-travel: calc(var(--duration-slow) + var(--duration-fast));

  --motion-enter: var(--motion-duration-enter) var(--ease-standard);
  --motion-leave: var(--motion-duration-leave) var(--ease-standard);
  --motion-travel: var(--motion-duration-travel) var(--ease-standard);
  --motion-ease-standard: var(--ease-standard);
  --motion-ease-out: var(--ease-out);
  --motion-ease-loop: var(--ease-in-out);   /* va-et-vient d'une boucle */
}

/* =============================================================
   Thème sombre
   Seule la couche sémantique bouge : les composants suivent.
   ============================================================= */
[data-theme="dark"] {
  color-scheme: dark;

  --color-bg-page: var(--neutral-950);
  --color-bg-surface: var(--neutral-925);
  --color-bg-subtle: var(--neutral-900);
  --color-bg-hover: var(--neutral-900);
  --color-bg-active: var(--neutral-800);
  --color-bg-subtle-active: var(--neutral-700);
  /* Plus clair que --color-bg-subtle ici aussi : le rôle garde son
     sens d'un thème à l'autre, seule sa valeur change. */
  --color-bg-shimmer: var(--neutral-800);
  --color-bg-inverse: var(--neutral-50);
  --color-bg-overlay: color-mix(in srgb, var(--neutral-1000) 40%, transparent);

  --color-text: var(--neutral-50);
  --color-text-secondary: var(--neutral-200);
  /* Pas le même cran qu'en clair : sur une surface sombre,
     --neutral-500 ne tient que 3,67 de contraste, sous 4,5. */
  --color-text-muted: var(--neutral-400);
  --color-text-inverse: var(--neutral-900);
  /* Blanc cassé plutôt que blanc pur : c'est la valeur de production
     de Vibecoded sur son bouton d'accent en sombre. */
  --color-text-on-accent: var(--neutral-50);
  /* Le vert et le rouge s'éclaircissent ici : leur texte s'assombrit,
     comme celui de l'ambre le fait dans les deux thèmes. */
  --color-text-on-success: var(--neutral-950);
  --color-text-on-danger: var(--neutral-950);
  --color-text-link: var(--blue-500);
  --color-text-link-hover: var(--blue-400);

  --color-border: var(--neutral-850);
  --color-border-strong: var(--neutral-700);
  --color-border-hover: var(--neutral-600);
  --color-border-active: var(--neutral-500);
  --color-control-track: var(--neutral-700);
  /* Le pouce reste clair ici aussi : le rôle garde son sens d'un thème
     à l'autre, seule sa valeur change. */
  --color-control-knob: var(--neutral-50);
  --color-control-knob-active: var(--neutral-300);

  /* Le survol et l'enfoncé d'un aplat s'enfoncent, ici comme en clair.
     Miroiter l'échelle vers le haut aurait été le réflexe du thème
     sombre, mais ces trois rôles portent un aplat plein sous un texte
     clair fixe : éclaircir le fond efface le libellé. Le repos garde
     --blue-500, la valeur de marque, et les deux crans d'interaction
     descendent, si bien que le contraste monte quand on agit au lieu
     de tomber. Lus par le bouton primaire, la case à cocher, le bouton
     radio et la piste de l'interrupteur, tous des aplats. */
  --color-accent: var(--blue-600);
  --color-accent-hover: var(--blue-500);
  --color-accent-active: var(--blue-400);
  --color-accent-subtle: var(--blue-975);
  --color-accent-subtle-hover: var(--blue-950);
  --color-accent-border: var(--blue-900);
  --color-accent-text: var(--blue-500);
  /* En sombre l'arête va vers le clair : sur un fond presque noir,
     un cran plus foncé que l'aplat disparaît au lieu de le cerner. */
  --color-accent-edge: var(--blue-500);

  --color-success: var(--green-400);
  --color-success-hover: var(--green-300);
  --color-success-subtle: var(--green-950);
  --color-success-border: var(--green-900);
  --color-success-text: var(--green-400);

  /* L'inverse de l'accent, et c'est la même règle. Un aplat s'écarte de
     son libellé quand on agit dessus ; c'est le libellé qui décide donc
     du sens. L'accent garde un texte clair, donc il s'enfonce. Le rouge
     du thème sombre est assez clair pour porter un texte sombre, celui
     de --color-text-on-danger, donc il monte : 4,71 au repos, 6,25 au
     survol, 9,09 enfoncé. --color-danger reste au cran 500, le plus
     visible, celui que lisent le filet d'un champ invalide, la barre de
     progression et l'icône du toast. */
  --color-danger: var(--red-500);
  --color-danger-hover: var(--red-400);
  --color-danger-active: var(--red-300);
  --color-danger-subtle: var(--red-950);
  --color-danger-subtle-hover: var(--red-925);
  --color-danger-border: var(--red-850);
  --color-danger-text: var(--red-500);
  --color-danger-edge: var(--red-400);

  --color-warning: var(--amber-400);
  --color-warning-hover: var(--amber-300);
  --color-warning-subtle: var(--amber-950);
  --color-warning-border: var(--amber-900);
  --color-warning-text: var(--amber-400);

  /* Même rattachement que dans le thème clair : l'information suit
     l'accent, ici comme là-bas. Le bloc est répété pour que la règle
     tienne même si [data-theme="dark"] est porté par un sous-arbre
     plutôt que par la racine. */
  --color-info: var(--color-accent);
  --color-info-subtle: var(--color-accent-subtle);
  --color-info-border: var(--color-accent-border);
  --color-info-text: var(--color-accent-text);

  --color-action: var(--blue-600);
  --color-action-hover: var(--blue-700);
  --color-action-active: var(--blue-800);
  --color-action-text: var(--neutral-50);
  --color-action-edge: var(--blue-500);

  --color-destructive: var(--red-950);
  --color-destructive-hover: var(--red-925);
  --color-destructive-active: var(--red-850);
  --color-destructive-text: var(--red-500);
  --color-destructive-edge: color-mix(in srgb, var(--red-500) 30%, transparent);

  --color-series-1: var(--serie-1);
  --color-series-2: var(--serie-2-sombre);
  --color-series-3: var(--serie-3-sombre);
  --color-series-4: var(--serie-4-sombre);

  --color-focus-ring: var(--blue-500);

  --elevation-raised: var(--shadow-dark-xs);
  --elevation-floating: var(--shadow-dark-m);
  --elevation-overlay: var(--shadow-dark-l);
  --elevation-toast: var(--shadow-dark-l);


  /* Rôles que le thème sombre ne redéfinissait pas encore. */
  --page-texture: radial-gradient(color-mix(in srgb, var(--neutral-50) 3.5%, transparent) 1px, transparent 0);
}
