/* =============================================================================
   atelier-forme.css — couche de forme pour « Mon atelier »
   Version 1.0 · 18 septembre 2026

   CE QUE CE FICHIER FAIT
   Il traite l'apparence et la composition : échelle typographique, rang des
   blocs, surfaces, signature d'atelier, états, navigation mobile.

   CE QUE CE FICHIER NE FAIT PAS
   Aucune règle pédagogique, aucune règle de complétion, aucune règle de
   confidentialité, aucun libellé, aucune route. Il ne masque aucun contenu
   porteur d'information. Les seuls éléments neutralisés sont décoratifs et
   portent déjà aria-hidden dans les gabarits.

   OÙ LE CHARGER
   En dernier, après review-polish.css, dans templates/base.html :
     <link rel="stylesheet" href="/static/atelier-forme.css">
   L'ordre compte : plusieurs règles ci-dessous égalent la spécificité de la
   couche existante et ne gagnent que par la position dans la cascade.

   PORTÉE
   Tout est sous .ui-v1 ou body.ui-v1, comme universes.css. Rien ne fuit hors
   de l'interface refondue.

   DÉPENDANCES DE BALISAGE
   Trois ajouts facultatifs sont décrits dans PATCHS-gabarits.md. En leur
   absence, ce fichier reste valide : la composition à deux registres et la
   barre d'onglets restent inactives, tout le reste s'applique.
   ============================================================================= */


/* =============================================================================
   1 · CORRECTIONS DE JETONS
   ============================================================================= */

/* 1.1 · Taille du texte courant.
   L'état actuel fixe le corps à --text-body-s, soit 15 px. C'est le registre
   secondaire du jeu de jetons, pas le registre de lecture. Les HIG d'Apple
   donnent 17 pt pour Body, et ces écrans comportent des paragraphes longs.
   Le passage à 17 px est la correction qui change le plus l'impression
   d'ensemble, avant toute décision de mise en page.
   Sélecteur triplé pour égaler la passerelle de universes.css
   (body.ui-v1.ui-v1.ui-v1), départagée ensuite par l'ordre de chargement. */
body.ui-v1.ui-v1.ui-v1{
  font-size: var(--text-body);          /* 17 px */
  line-height: var(--leading-text);     /* 1,5  */
  -webkit-text-size-adjust: 100%;
}

/* 1.2 · Échelle d'affichage par atelier.
   L'existant aplatit tout : h1 à 28 px, h2 à 22 px, corps à 15 px. Trois pas
   trop serrés pour hiérarchiser. On rouvre l'échelle par le haut, sans
   toucher au plancher de 13 px. */
body.ui-v1{
  --u-title-page:    clamp(1.875rem, 1.10rem + 2.6vw, 2.5rem);   /* 30 → 40 px */
  --u-title-lead:    clamp(1.375rem, 1.15rem + 0.8vw, 1.625rem); /* 22 → 26 px */
  --u-title-section: clamp(1.3125rem, 1.10rem + 0.7vw, 1.5rem);  /* 21 → 24 px */
  --u-title-card:    clamp(1.1875rem, 1.05rem + 0.5vw, 1.375rem);/* 19 → 22 px */
  --u-title-leading: 1.14;
  /* Quatre pas, pas trois : le titre de page, l'énoncé de l'étape du jour,
     les titres de section, les titres de carte. L'état actuel en aligne deux
     sur la même valeur, ce qui empêche la carte première de primer. */
}
body.ui-v1[data-universe="dyna"]{
  --u-title-leading: 1.16;   /* la sérif respire davantage */
  --u-title-tracking: -0.008em;
}
body.ui-v1[data-universe="syrine"]{
  --u-title-leading: 1.04;   /* la grasse se compacte */
  --u-title-tracking: -0.03em;
}

/* 1.3 · Le rebord des actions en thème sombre.
   Les deux palettes sombres posent --action-ledge à la valeur de --action :
   le rebord disparaît et le bouton devient plat. Ce n'est pas un défaut
   d'accessibilité — l'aplat se détache déjà de la page à 9,70:1 pour Dyna et
   9,94:1 pour Syrine — mais la perte de l'affordance physique qui donne au
   thème clair son caractère. On rend au rebord une valeur assombrie.
   Contrôle calculé : rebord/aplat = 1,53 (Dyna) et 1,49 (Syrine). Valeurs
   décoratives, hors exigence 1.4.11, l'identification du bouton reposant sur
   l'aplat lui-même. */
body.ui-v1[data-palette="rose"][data-display="dark"]{ --action-ledge: #C489A6; }
body.ui-v1[data-palette="blue"][data-display="dark"]{ --action-ledge: #8B95CC; }

/* 1.4 · Jetons de signature, ajoutés.
   --u-texture  : la texture de surface, tramée, appliquée à la carte.
   --u-rule     : le trait de signature au-dessus du titre de la carte.
   --u-lift     : l'amplitude du relief au survol. */
body.ui-v1[data-universe="dyna"]{
  --u-texture: radial-gradient(circle at 1px 1px,
               color-mix(in srgb, var(--action) 30%, transparent) 1px, transparent 0);
  --u-texture-size: 19px 19px;
  --u-texture-alpha: 0.42;
  --u-rule-height: 3px;
  --u-lift: 1px;
}
body.ui-v1[data-universe="syrine"]{
  --u-texture: repeating-linear-gradient(115deg,
               color-mix(in srgb, var(--action) 24%, transparent) 0 2px, transparent 2px 15px);
  --u-texture-size: auto;
  --u-texture-alpha: 0.5;
  --u-rule-height: 5px;
  --u-lift: 2px;
}
/* Repli sans atelier : pas de texture, pas de trait. Rien ne casse. */
body.ui-v1:not([data-universe]){ --u-texture: none; --u-texture-alpha: 0; --u-rule-height: 0px; --u-lift: 1px; }


/* =============================================================================
   2 · CIBLES TACTILES : LEVER LA SUR-APPLICATION SUR LES LIENS EN LIGNE
   ============================================================================= */

/* universes.css impose display:inline-flex et 44×44 px à tout <a> qui n'est ni
   bouton, ni carte. Appliqué à un lien situé dans une phrase, cela casse le
   rythme des lignes et gonfle les liens courts.
   WCAG 2.2, critère 2.5.8 « Taille de la cible (minimum) », niveau AA, prévoit
   une exception explicite dite « En ligne » : la cible est dans une phrase, ou
   sa taille est contrainte par la hauteur de ligne du texte non-cible.
   Les liens autonomes — listes de navigation, actions, cartes — restent, eux,
   soumis à la règle et ne sont pas touchés ici.
   Spécificité 0,4,2, supérieure au 0,4,1 de la règle d'origine. */
.ui-v1 :is(p, li, dd, dt, blockquote, summary, figcaption, .preserve, .small, footer)
  a:not(.button):not(.card):not(.discovery-visual){
  display: inline;
  min-height: 0;
  min-width: 0;
  align-items: initial;
}
/* Les listes de navigation restent des cibles autonomes. */
.ui-v1 :is(nav, .local-tabs, .folder-tabs, .quick-actions) li a{
  display: inline-flex;
  min-height: var(--target-min);
}


/* =============================================================================
   3 · LE BANDEAU DE PAGE
   ============================================================================= */

/* Le titre de page flotte aujourd'hui sans ancrage, et il redit l'entrée de
   navigation déjà signalée par aria-current à quelques centimètres. On ne
   supprime pas le titre — il porte le repère du document et la structure de
   lecture pour les technologies d'assistance. On lui donne un ancrage visuel
   pour qu'il cesse d'être un doublon flottant. */
.ui-v1 .welcome{
  position: relative;
  padding: clamp(1.25rem, 2.5vw, 2rem) 0 clamp(1rem, 2vw, 1.5rem);
  margin-bottom: clamp(1rem, 2vw, 1.5rem);
  border-bottom: 1px solid var(--border);
}
.ui-v1 .welcome h1,
.ui-v1 h1{
  font-family: var(--font-display, var(--font-family));
  font-size: var(--u-title-page);
  font-weight: var(--u-display-weight, 700);
  letter-spacing: var(--u-title-tracking, var(--u-display-tracking, -0.01em));
  line-height: var(--u-title-leading);
  color: var(--text-primary);
  margin: 0 0 0.35rem;
  text-wrap: balance;
}
/* Le titre d'accueil est court : on l'empêche de s'étirer sur 800 px.
   Les titres de document, eux, sont longs et ne sont pas bridés : un
   plafond de 18ch les casserait en trois lignes sans raison. */
.ui-v1 .welcome h1{ max-width: 18ch; }
.ui-v1 .folder-heading h1,
.ui-v1 .chat-heading h1,
.ui-v1 main h1{ max-width: 26ch; }
.ui-v1 .welcome h1{ max-width: 18ch; }
.ui-v1 .welcome p{
  margin: 0;
  color: var(--text-secondary);
  font-size: var(--text-body);
  max-width: 52ch;
}

/* La marque d'atelier : un filet vertical à gauche du bandeau, tracé en CSS.
   Dyna : filet fin, dédoublé, registre imprimé.
   Syrine : barre épaisse, inclinée, registre affiché. */
.ui-v1[data-universe] .welcome::before{
  content: "";
  position: absolute;
  left: calc(-1 * clamp(0.75rem, 1.6vw, 1.25rem));
  top: clamp(1.25rem, 2.5vw, 2rem);
  height: calc(var(--u-title-page) * var(--u-title-leading));
  width: var(--u-rule-height);
  background: var(--action);
  border-radius: 2px;
}
.ui-v1[data-universe="dyna"] .welcome::before{
  width: 1px;
  box-shadow: 3px 0 0 var(--action);   /* le filet dédoublé */
  opacity: 0.85;
}
.ui-v1[data-universe="syrine"] .welcome::before{
  transform: skewX(-12deg);
}
/* Sous 900 px la marge de gauche n'existe plus : le filet passe au-dessus. */
@media (max-width: 899px){
  .ui-v1[data-universe] .welcome::before{
    left: 0; top: 0.4rem;
    height: var(--u-rule-height); width: 2.75rem;
  }
  .ui-v1[data-universe="dyna"] .welcome::before{
    height: 1px; box-shadow: 0 3px 0 var(--action);
  }
  .ui-v1 .welcome{ padding-top: 1.6rem; }
}


/* =============================================================================
   4 · LA CARTE D'ACTION PREMIÈRE
   ============================================================================= */

/* Elle porte l'unique action par défaut de la journée. Aujourd'hui elle a le
   même poids visuel qu'un paragraphe : fond blanc, filet de 1 px, texte de
   15 px. Elle doit se détacher franchement, et se détacher différemment selon
   l'atelier — c'est le premier endroit où les deux espaces doivent devenir
   reconnaissables. */
.ui-v1 .today-primary{
  position: relative;
  isolation: isolate;
  padding: clamp(1.5rem, 3vw, 2.25rem);
  border-radius: var(--u-radius-card, var(--radius-400));
  border: 1px solid var(--border);
  background: var(--bg-surface);
  box-shadow: var(--elevation-2);
  overflow: hidden;
}
/* La texture d'atelier, sur toute la carte, sous le texte.
   Remplace le bandeau de 11 × 112 px de review-polish.css, qui se lit à
   l'écran comme un artefact de rendu et non comme une matière. */
.ui-v1[data-universe] .today-primary::after{
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: var(--u-texture);
  background-size: var(--u-texture-size);
  opacity: var(--u-texture-alpha);
  pointer-events: none;
  /* La matiere habille la carte par ses bords et s'efface sous la copie :
     sans ce degrade de masque, la trame traverse le texte et nuit a la
     lecture. Le masque est decoratif, il ne masque aucun contenu. */
  -webkit-mask-image: radial-gradient(130% 115% at 88% 8%, #000 0%, #000 34%, transparent 72%);
  mask-image: radial-gradient(130% 115% at 88% 8%, #000 0%, #000 34%, transparent 72%);
}
.ui-v1 .today-primary h2{
  font-family: var(--font-display, var(--font-family));
  font-size: var(--u-title-lead);
  font-weight: var(--u-display-weight, 700);
  letter-spacing: var(--u-title-tracking, -0.01em);
  line-height: 1.2;
  color: var(--text-primary);
  margin: 0 0 0.5rem;
  max-width: 24ch;
}
.ui-v1 .today-primary > p{
  font-size: var(--text-body-l);      /* 19 px : c'est l'énoncé de l'étape */
  line-height: 1.45;
  color: var(--text-primary);
  margin: 0 0 1.5rem;
  max-width: 46ch;
  text-wrap: pretty;
}
.ui-v1 .today-primary .button{
  font-size: var(--text-body);
  padding: 0.85rem 1.5rem;
}

/* Le trait de signature, au-dessus du titre.
   .universe-motif existe déjà dans le gabarit et porte aria-hidden. On le
   conserve et on le requalifie : il cesse d'être un fragment de trame pour
   devenir le trait qui ouvre la carte. */
.ui-v1 .universe-motif{
  display: block;
  width: 3rem;
  height: var(--u-rule-height);
  margin: 0 0 1.1rem;
  background-image: none;
  background-color: var(--action);
  opacity: 1;
  border-radius: 0;
}
.ui-v1[data-universe="dyna"] .universe-motif{
  height: 2px;
  box-shadow: 0 5px 0 var(--action);   /* filet dédoublé, comme le bandeau */
  margin-bottom: 1.6rem;
  width: 3.5rem;
  opacity: 1;
}
.ui-v1[data-universe="syrine"] .universe-motif{
  transform: skewX(-12deg);
  border-radius: 1px;
}
.ui-v1:not([data-universe]) .universe-motif{ display: none; }
.ui-v1 .empty-motif{ width: 2rem; }

/* Survol et appui : le rebord est la seule chose qui bouge. */
.ui-v1 .today-primary .button{
  box-shadow: 0 var(--ledge-depth) 0 var(--action-ledge);
  transition: transform var(--u-duration, 200ms) var(--ease-standard),
              box-shadow var(--u-duration, 200ms) var(--ease-standard);
}
.ui-v1 .today-primary .button:hover{
  transform: translateY(calc(-1 * var(--u-lift)));
  box-shadow: 0 calc(var(--ledge-depth) + var(--u-lift)) 0 var(--action-ledge);
  filter: none;
}
.ui-v1 .today-primary .button:active{
  transform: translateY(var(--ledge-depth));
  box-shadow: 0 0 0 var(--action-ledge);
}

/* « Pourquoi cette méthode » : la signature fonctionnelle de Dyna.
   Elle doit se voir sans concurrencer l'action. */
.ui-v1 .method-why{ margin-top: 1.25rem; border-top: 1px solid var(--border); }
.ui-v1 .method-why summary{
  font-size: var(--text-body-s);
  color: var(--action-on-subtle);
  padding: 0.75rem 0 0.5rem;
}
.ui-v1 .method-why p{ font-size: var(--text-body-s); color: var(--text-secondary); margin: 0 0 0.75rem; }


/* =============================================================================
   5 · LES DISCLOSURES
   ============================================================================= */

/* Le triangle par défaut du navigateur signe l'inachevé. On le remplace par
   un chevron tracé, qui pivote, et qui reste visible en cas de perte de CSS
   partielle puisque le marqueur natif n'est retiré qu'ici. */
.ui-v1 details > summary{
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: var(--target-min);
}
.ui-v1 details > summary::-webkit-details-marker{ display: none; }
.ui-v1 details > summary::before{
  content: "";
  flex: 0 0 auto;
  width: 0.5rem; height: 0.5rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform var(--u-duration, 200ms) var(--ease-standard);
}
.ui-v1 details[open] > summary::before{ transform: rotate(45deg); }


/* =============================================================================
   6 · LA LISTE SUBORDONNÉE DES SUPPORTS
   ============================================================================= */

.ui-v1 .secondary-supports{
  border-top: 1px solid var(--border);
  margin-top: 1.5rem;
  padding-top: 0;
}
.ui-v1 .secondary-supports > summary{ color: var(--text-secondary); font-size: var(--text-body-s); }
.ui-v1 .secondary-supports li:last-child{ border-bottom: 0; }
.ui-v1 .secondary-supports a{
  align-items: center;
  border-radius: var(--radius-200);
  padding-inline: 0.5rem;
  margin-inline: -0.5rem;
  transition: background-color var(--u-duration, 200ms);
}
.ui-v1 .secondary-supports a:hover{ background: var(--bg-surface-2); text-decoration: none; }
.ui-v1 .secondary-supports a:hover span{ text-decoration: underline; }


/* =============================================================================
   7 · LES ÉTATS VIDES
   ============================================================================= */

/* Le cadrage impose une icône, un titre, une ligne d'explication et une action
   ancrée. Dans le gabarit de l'accueil, l'action existe mais vit à l'extérieur
   du bloc, en lien nu détaché. On la rattache visuellement sans déplacer la
   balise, par le sélecteur de voisinage. */
.ui-v1 .empty-next{
  border: 1px dashed var(--border-strong);
  border-radius: var(--u-radius-card, var(--radius-400));
  padding: 1.5rem 1.5rem 0.5rem;
  background: var(--bg-surface);
}
.ui-v1 .empty-next h3{
  font-family: var(--font-display, var(--font-family));
  font-size: var(--text-body-l);
  color: var(--text-primary);
}
.ui-v1 .empty-next p{ margin: 0.6rem 0 0; color: var(--text-secondary); max-width: 46ch; }
.ui-v1 .empty-next + p,
.ui-v1 .empty-next ~ a:not(.button){
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0;
  padding: 0.85rem 1.25rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-full);
  color: var(--action-on-subtle);
  background: var(--action-subtle);
  font-weight: var(--weight-semibold);
  font-size: var(--text-body-s);
  min-height: var(--target-min);
}
.ui-v1 .empty-next ~ a:not(.button):hover{
  background: var(--bg-surface-2);
  text-decoration: none;
  border-color: var(--action);
}
/* Le bloc vide et son action forment un ensemble : on les rapproche. */
.ui-v1 .empty-next{ margin-bottom: 0.9rem; }

/* L'état vide des écrans secondaires, déjà stylé ailleurs, reçoit la même
   logique de bord marqué. */
.ui-v1 .secondary-empty{
  border: 1px dashed var(--border-strong);
  border-radius: var(--u-radius-card, var(--radius-400));
  background: var(--bg-surface);
}


/* =============================================================================
   8 · LA MESURE DE LIGNE, ET LES SECTIONS
   ============================================================================= */

/* 8.0 · La mesure de ligne.
   review-polish.css pose p{max-width:75ch}, mais l'exception de largeur
   accordée à timetable et library — main > :not(.topbar){max-width:64rem} —
   la supplante par spécificité sur les enfants directs de main. Résultat
   mesuré sur /timetable à 1440 px : une note de 13 px étalée sur 938 px,
   soit 130 caractères par ligne.
   Le critère 1.4.8 de WCAG 2.2, de niveau AAA, retient 80 caractères. L'unité
   ch vaut la largeur du zéro, plus large que le caractère moyen d'environ un
   dixième : 72ch tient sous les 80 caractères réels.
   La largeur du bloc n'est pas touchée, seule la mesure du texte l'est.
   Spécificité 0,2,3, supérieure au 0,2,2 de l'exception. */
body.ui-v1 main :is(p, li, dd, blockquote):not(.preserve):not(.source-preview){
  max-width: min(100%, 72ch);
}


/* ----- Les sections ----- */

.ui-v1 main > section.spaced{ margin-top: clamp(1.75rem, 3vw, 2.5rem); }
.ui-v1 main > section.spaced > h2,
.ui-v1 .section-heading h2{
  font-family: var(--font-display, var(--font-family));
  font-size: var(--u-title-section);
  font-weight: var(--u-display-weight, 700);
  letter-spacing: var(--u-title-tracking, -0.01em);
  line-height: 1.2;
  color: var(--text-primary);
  margin: 0 0 0.9rem;
}
/* Le rang est tenu par la taille et par l'espace, pas par la couleur seule :
   critère 1.4.1 « Utilisation de la couleur ». */


/* =============================================================================
   9 · LA BARRE LATÉRALE, EN LARGEUR BUREAU
   ============================================================================= */

.ui-v1 .sidebar{ padding: 1.5rem 0.875rem; }
.ui-v1 .sidebar nav a{
  font-size: var(--text-body-s);
  border-radius: var(--radius-200);
  padding: 0.7rem 0.75rem;
  color: var(--text-secondary);
}
.ui-v1 .sidebar nav a:hover{ background: var(--bg-surface-2); color: var(--text-primary); text-decoration: none; }
.ui-v1 .sidebar nav a[aria-current]{
  background: var(--action-subtle);
  color: var(--action-on-subtle);
  font-weight: var(--weight-semibold);
  border-left: 3px solid var(--action);
}

/* Inversion de hiérarchie à corriger : universes.css remplit le lien vers
   l'assistante de Syrine en aplat plein, poids 700. Il devient l'élément le
   plus fort de la page, plus fort que la rubrique où l'on se trouve. On le
   garde distinct — c'est sa signature fonctionnelle — mais au second rang :
   contour plein, fond doux, pas d'aplat.
   Spécificité 0,4,1 contre 0,3,1 pour la règle d'origine. */
.ui-v1[data-universe="syrine"] .secondary-nav a[href='/chat']:not(#x){
  background: var(--action-subtle);
  color: var(--action-on-subtle);
  border: 1px solid var(--action);
  border-radius: var(--radius-full);
  font-weight: var(--weight-semibold);
  padding-inline: 0.9rem;
}
.ui-v1[data-universe="syrine"] .secondary-nav a[href='/chat']:not(#x):hover{
  background: var(--action); color: var(--text-on-fill);
}

.ui-v1 .sidebar-foot{ padding-top: 1rem; row-gap: 0.15rem; }
.ui-v1 .sidebar-foot strong{ font-size: var(--text-body-s); color: var(--text-primary); }
.ui-v1 .demo-indicator{
  display: inline-block;
  padding: 0.1rem 0.45rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-full);
  font-size: var(--text-caption);
  color: var(--text-secondary);
  white-space: nowrap;      /* « Démonstration isolée » ne casse plus au milieu */
}


/* =============================================================================
   10 · LA COMPOSITION
   ============================================================================= */

/* 10.0 · Le bord gauche en dents de scie, sur neuf écrans.
   secondary-screens.css met main en flex colonne sur evening, memory, paths,
   exam, grades, progress, timetable, chat et discover, pour obtenir un pied
   de page en bas de fenêtre (min-height:100dvh).
   Conséquence non voulue : review-polish.css donne à chaque enfant
   max-width:50rem et margin-inline:auto. En disposition flexible, une marge
   automatique sur l'axe transversal neutralise l'étirement par align-items et
   l'élément se réduit à la largeur de son contenu, puis se centre.
   Mesuré sur /discover à 1440 px, avant correction : le titre commence à
   x = 629, la section à x = 521, le pied à x = 464. Trois bords gauches
   différents sur le même écran.
   La largeur explicite rétablit le comportement attendu : 800 px, centrés,
   un seul bord. */
body.ui-v1 main{ align-items: stretch; }
body.ui-v1 main > *:not(.tabbar){ width: 100%; }

/* 10.0 bis · Le pied de page en bas de fenêtre.
   min-height:100dvh est déjà posé sur ces neuf écrans mais rien ne pousse le
   pied vers le bas : la page courte s'arrête au milieu et laisse un vide. */
body.ui-v1 main > footer{ margin-top: auto; }


/* ----- Deux registres en largeur bureau ----- */

/* Constat : à 1440 px, la colonne de 800 px laisse plus de 200 px morts à
   droite et, sur une page courte, autant en bas. Ce n'est pas la mesure de
   ligne qui est en cause — 800 px est juste — c'est l'absence d'un second
   registre. On n'ajoute aucun contenu : on replace des blocs déjà présents.

   Actif seulement si un bloc porte la classe .a-aside (voir PATCHS-gabarits).
   Sans elle, la page reste en colonne unique, exactement comme aujourd'hui. */
@media (min-width: 1200px){
  body.ui-v1 main:has(> .a-aside){
    display: grid;
    grid-template-columns: minmax(0, 44rem) 19rem;   /* 704 px + 304 px */
    column-gap: 3rem;
    justify-content: center;
    align-items: start;
    max-width: none;
    margin-inline-start: 208px;
    margin-inline-end: 0;
  }
  body.ui-v1 main:has(> .a-aside) > *{ grid-column: 1; max-width: none; margin-inline: 0; }
  body.ui-v1 main:has(> .a-aside) > .topbar,
  body.ui-v1 main:has(> .a-aside) > .local-tabs,
  body.ui-v1 main:has(> .a-aside) > .notice{ grid-column: 1 / -1; }
  body.ui-v1 main:has(> .a-aside) > .a-aside{
    grid-column: 2;
    grid-row: 2 / span 40;
    position: sticky;
    top: 1.5rem;
    margin-top: 0;
    padding: 1.25rem;
    border: 1px solid var(--border);
    border-radius: var(--u-radius-card, var(--radius-400));
    background: var(--bg-surface-2);
  }
  body.ui-v1 main:has(> .a-aside) > .a-aside > h2{
    font-size: var(--u-title-card);
    margin-bottom: 0.75rem;
  }
  body.ui-v1 main:has(> .a-aside) > .a-aside .empty-next{
    border: 0; padding: 0; background: none;
  }
  body.ui-v1 main:has(> .a-aside) > footer{ grid-column: 1; }
}

/* 10.1 · La fin de page.
   Sans repli visuel, la colonne s'arrête sans finir. Le pied devient une
   bande discrète, qui ferme la page sans peser autant qu'un contenu. */
.ui-v1 footer{
  margin-top: clamp(2.5rem, 5vw, 4rem);
  padding: 1rem 1.25rem;
  border: 0;
  border-radius: var(--radius-300);
  background: var(--bg-surface-2);
  color: var(--text-secondary);
  font-size: var(--text-caption);
  line-height: 1.5;
  max-width: 68ch;
}

/* 10.2 · La barre du haut.
   La bascule clair/sombre occupe aujourd'hui le coin supérieur droit de tous
   les écrans, au même rang visuel que les actions de contenu. Elle reste
   accessible et étiquetée, en retrait. */
.ui-v1 .topbar{ min-height: 2.75rem; align-items: center; }
.ui-v1 .display-switch button{
  background: transparent;
  color: var(--text-secondary);
  border: 1px solid var(--border);
  font-size: var(--text-caption);
  padding: 0.4rem 0.8rem;
  min-height: var(--target-min);
}
.ui-v1 .display-switch button:hover{
  background: var(--bg-surface-2); color: var(--text-primary); filter: none;
}


/* =============================================================================
   11 · LARGEUR TÉLÉPHONE
   ============================================================================= */

/* 11.1 · Repli sans changement de balisage.
   Tant que la barre d'onglets n'est pas posée, on corrige au moins ce qui
   coince : la réserve de 104 px imposée à la barre du haut pour dégager le
   bouton « Menu », et le tiroir qui s'ouvre en flottant. */
@media (max-width: 849px){
  .ui-v1 .topbar{ justify-content: flex-end; gap: 0.5rem; padding-top: 0.5rem; }
  .ui-v1 .mobile-menu{
    position: fixed; top: 0.5rem; left: 0.75rem; z-index: 30;
    min-height: var(--target-min);
    background: var(--bg-surface);
    color: var(--text-primary);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-full);
    padding-inline: 0.9rem;
    font-size: var(--text-body-s);
  }
  /* La réserve qui dégage le bouton « Menu » est un remplissage, pas une marge.
     Avec width:100 pour cent, une marge de début s'ajoute à la largeur et fait
     déborder la page : mesuré à 390 px, 390 + 112 = 502 px de document. */
  .ui-v1 .topbar{ padding-inline-start: 7rem; }
  .ui-v1 .sidebar.menu-open{
    left: 0.5rem; right: 0.5rem; width: auto;
    border-radius: var(--radius-600);
    box-shadow: var(--elevation-5);
    padding: 1rem 0.75rem;
  }
  .ui-v1 .welcome{ padding-top: 1rem; }
  .ui-v1 .today-primary{ padding: 1.25rem; }
  .ui-v1 .today-primary > p{ font-size: var(--text-body); }
  .ui-v1 footer{ margin-top: 2rem; }
}

/* 11.2 · La barre d'onglets.
   Actif seulement si <nav class="tabbar"> est posé (voir PATCHS-gabarits).
   Cinq destinations tiennent dans une barre basse ; les enfermer derrière un
   bouton « Menu » coûte un geste et masque la structure de l'application.
   Le libellé est à 11 px : c'est le registre des barres d'onglets iOS, et la
   seule dérogation assumée au plancher de 13 px du jeu de jetons. La cible
   fait 56 px de haut sur un cinquième de la largeur, très au-delà des 24 px
   exigés par le critère 2.5.8 et des 44 px de la recommandation AAA 2.5.5. */
.ui-v1 .tabbar{ display: none; }
@media (max-width: 849px){
  .ui-v1 .tabbar{
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 0;
    position: fixed;
    inset: auto 0 0 0;
    z-index: 50;
    background: color-mix(in srgb, var(--bg-surface) 92%, transparent);
    backdrop-filter: saturate(140%) blur(12px);
    border-top: 1px solid var(--border);
    padding: 0.25rem 0.25rem calc(0.25rem + env(safe-area-inset-bottom));
  }
  .ui-v1 .tabbar a{
    display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
    gap: 0.25rem;
    min-height: 3.5rem;
    padding: 0.45rem 0.1rem 0.3rem;
    border-radius: var(--radius-300);
    color: var(--text-secondary);
    font-size: 0.8125rem;      /* 11 px, registre barre d'onglets */
    font-weight: var(--weight-semibold);
    line-height: 1.15;
    text-align: center;
    text-decoration: none;
  }
  .ui-v1 .tabbar a svg{
    width: 1.375rem; height: 1.375rem;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
  }
  /* L'état courant ne repose pas sur la seule couleur : le libellé passe en
     gras et un repère plein s'affiche sous l'icône. Critère 1.4.1. */
  .ui-v1 .tabbar a[aria-current]{ color: var(--action); font-weight: var(--weight-bold); }
  .ui-v1 .tabbar a[aria-current]::after{
    content: ""; width: 1rem; height: 2px; border-radius: 2px; background: var(--action);
  }
  .ui-v1 .tabbar a:active{ background: var(--action-subtle); }

  /* Avec la barre d'onglets, le bouton « Menu » n'a plus d'objet et la page
     doit réserver la hauteur de la barre. */
  body.ui-v1:has(.tabbar) .mobile-menu{ display: block; }
  body.ui-v1:has(.tabbar) .topbar{ padding-inline-start: 7rem; }
  body.ui-v1:has(.tabbar) main{ padding-bottom: calc(4.5rem + env(safe-area-inset-bottom)); }
  /* La barre latérale reste la source des rubriques secondaires. */
  body.ui-v1:has(.tabbar) .sidebar{ display: none; }
  body.ui-v1:has(.tabbar) .sidebar.menu-open{ display: block; }
}


/* 11.3 · Le bouton vocal de Syrine, en largeur téléphone.
   Il doit rester immédiatement atteignable — c'est sa signature fonctionnelle —
   sans devenir l'élément le plus fort de l'écran, au-dessus du titre de page.
   Même traitement que dans la barre latérale : contour plein, fond doux. */
@media (max-width: 849px){
  .ui-v1 .mobile-voice.button{
    background: var(--action-subtle);
    color: var(--action-on-subtle);
    border: 1px solid var(--action);
    border-radius: var(--radius-full);
    font-size: var(--text-body-s);
    font-weight: var(--weight-semibold);
    padding: 0.5rem 1rem;
    box-shadow: none;
  }
  .ui-v1 .mobile-voice.button:active{ background: var(--action); color: var(--text-on-fill); }
}


/* =============================================================================
   12 · FOCUS, MOUVEMENT, IMPRESSION
   ============================================================================= */

/* Le focus visible doit rester lisible sur les deux thèmes et sur les aplats.
   Double anneau : le trait coloré porte l'identité, le liseré de contraste
   inverse garantit la visibilité quel que soit le fond. */
.ui-v1 :is(a, button, .button, summary, input, select, textarea, [tabindex]):focus-visible{
  outline: 3px solid var(--action);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px var(--bg-page);
  border-radius: var(--radius-200);
}
.ui-v1 .tabbar a:focus-visible{ box-shadow: 0 0 0 5px var(--bg-surface); }

@media (prefers-reduced-motion: reduce){
  .ui-v1 *, .ui-v1 *::before, .ui-v1 *::after{
    transition-duration: 1ms !important;
    animation-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
  .ui-v1 .today-primary .button:hover{ transform: none; }
  /* La texture reste : elle ne bouge pas, elle n'est pas un mouvement. */
}

@media (prefers-contrast: more){
  .ui-v1 .today-primary,
  .ui-v1 .empty-next,
  .ui-v1 footer{ border: 2px solid var(--border-strong); }
  .ui-v1[data-universe] .today-primary::after{ opacity: 0.25; }
}

@media print{
  .ui-v1 .sidebar, .ui-v1 .tabbar, .ui-v1 .topbar, .ui-v1 .mobile-menu{ display: none !important; }
  .ui-v1 main{ margin: 0 !important; padding: 0 !important; }
  .ui-v1 .today-primary{ box-shadow: none; border: 1px solid #999; }
  .ui-v1[data-universe] .today-primary::after{ display: none; }
  .ui-v1 details{ display: block; }
  .ui-v1 details > summary::before{ display: none; }
}


/* =============================================================================
   13 · LA COULEUR DE MATIÈRE
   ============================================================================= */

/* Constat. La table folders porte bien une colonne color, et library.py en
   propose quatre : rose, blue, violet, green. Trois limites les rendent peu
   utiles pour distinguer une matière d'un coup d'œil.
   Quatre couleurs pour une dizaine de matières en troisième.
   Les quatre valeurs codées en dur dans compact.css ont presque la même clarté :
   mesuré, rose #C774A1 contre bleu #658BE0 donne 1,02:1, et aucune paire ne
   dépasse 1,24:1. Elles diffèrent en teinte, pas en valeur.
   Elles ne s'affichent que sur un filet de 4 px, trop peu de surface pour
   qu'une couleur se lise.
   Or six couleurs de matière sont déjà déclarées dans universes.css, chacune
   avec son rebord, sa surface douce et son texte associé, toutes calculées.
   Aucune règle ne les consommait. Elles sont branchées ici.

   Répartition retenue, mesurée contre le blanc de la carte :
     bleu     --subject-1  #3557C8  226°  6,27:1
     sarcelle --subject-2  #0F7A85  186°  5,07:1
     ambre    --subject-3  #A35C00   34°  5,14:1
     rose     --subject-4  #C02B62  338°  5,58:1
     violet   --subject-5  #6E43C0  261°  6,51:1
     vert     --subject-6  #2E7046  142°  5,96:1
   Écart de teinte le plus serré : bleu et violet, 35 degrés. Les deux sont
   volontairement éloignés l'un de l'autre dans l'ordre du menu.
   La couleur n'est jamais le seul porteur : le nom du dossier l'accompagne
   toujours, conformément au critère 1.4.1 de WCAG 2.2.

   Les quatre noms déjà stockés en base sont conservés à l'identique. Aucune
   migration n'est nécessaire, seules deux valeurs s'ajoutent. */

.ui-v1 .folder-card,
.ui-v1 [data-couleur]{
  --matiere:       var(--subject-1-bold);
  --matiere-rebord:var(--subject-1-ledge);
  --matiere-douce: var(--subject-1-subtle);
  --matiere-encre: var(--subject-1-on-subtle);
}
.ui-v1 .folder-blue,   .ui-v1 [data-couleur="blue"]{
  --matiere:var(--subject-1-bold); --matiere-rebord:var(--subject-1-ledge);
  --matiere-douce:var(--subject-1-subtle); --matiere-encre:var(--subject-1-on-subtle);
}
.ui-v1 .folder-teal,   .ui-v1 [data-couleur="teal"]{
  --matiere:var(--subject-2-bold); --matiere-rebord:var(--subject-2-ledge);
  --matiere-douce:var(--subject-2-subtle); --matiere-encre:var(--subject-2-on-subtle);
}
.ui-v1 .folder-amber,  .ui-v1 [data-couleur="amber"]{
  --matiere:var(--subject-3-bold); --matiere-rebord:var(--subject-3-ledge);
  --matiere-douce:var(--subject-3-subtle); --matiere-encre:var(--subject-3-on-subtle);
}
.ui-v1 .folder-rose,   .ui-v1 [data-couleur="rose"]{
  --matiere:var(--subject-4-bold); --matiere-rebord:var(--subject-4-ledge);
  --matiere-douce:var(--subject-4-subtle); --matiere-encre:var(--subject-4-on-subtle);
}
.ui-v1 .folder-violet, .ui-v1 [data-couleur="violet"]{
  --matiere:var(--subject-5-bold); --matiere-rebord:var(--subject-5-ledge);
  --matiere-douce:var(--subject-5-subtle); --matiere-encre:var(--subject-5-on-subtle);
}
.ui-v1 .folder-green,  .ui-v1 [data-couleur="green"]{
  --matiere:var(--subject-6-bold); --matiere-rebord:var(--subject-6-ledge);
  --matiere-douce:var(--subject-6-subtle); --matiere-encre:var(--subject-6-on-subtle);
}

/* 13.1 · La pastille.
   C'est la correction principale. Un filet de 4 px ne donne pas assez de
   surface pour qu'une couleur se lise : la pastille de l'icône, déjà présente
   dans le gabarit, reçoit l'aplat de matière et porte le glyphe en négatif.
   La surface douce, elle, ne peut pas servir de repère : les six valeurs
   -subtle se situent entre 1,19 et 1,26 contre le blanc, et entre 1,00 et
   1,05 les unes contre les autres. Deux teintes claires côte à côte sont la
   même couleur. L'aplat, lui, tient entre 5,07 et 6,51 contre la carte. */
.ui-v1 .folder-card .type-icon{
  box-sizing: content-box;
  width: 22px; height: 22px; padding: 9px;   /* pastille de 40 × 40 px */
  background: var(--matiere);
  color: var(--text-on-fill);
  border-radius: 12px;
  box-shadow: 0 2px 0 var(--matiere-rebord);
  margin-bottom: 14px;
}
/* Le losange décoratif devant « DOSSIER » faisait doublon avec la pastille. */
.ui-v1 .folder-card .eyebrow::before{ content: none; }
/* Le mot « DOSSIER » se répète sur chaque carte et n'apprend rien : il passe
   au second rang, le nom du dossier prend le premier. La mention « ARCHIVÉ »,
   elle, porte une information et reste lisible. */
.ui-v1 .folder-card .eyebrow{
  color: var(--text-tertiary);
  font-size: var(--text-caption);
  letter-spacing: .08em;
  margin: 0 0 2px;
}
.ui-v1 .folder-card h3{
  font-family: var(--font-display, var(--font-family));
  font-size: var(--text-body-l);
  font-weight: var(--u-display-weight, 700);
  letter-spacing: var(--u-title-tracking, -0.01em);
  line-height: 1.25;
  color: var(--text-primary);
  margin: 0;
}

/* 13.2 · Le filet de carte, repris des jetons au lieu des quatre valeurs
   codées en dur. Deux d'entre elles passaient sous 3:1 contre le blanc. */
.ui-v1 .folder-card{ border-bottom-color: var(--matiere); }
@media (max-width: 849px){
  .ui-v1 .folder-card{ border-left-color: var(--matiere); }
}
.ui-v1 .folder-card:hover{ border-color: var(--matiere); }

/* 13.3 · À l'intérieur du dossier, sa couleur se retrouve sous le titre.
   Demande data-couleur sur l'en-tête, voir le patch 4. */
.ui-v1 .folder-heading[data-couleur] h1::after{
  content: "";
  display: block;
  width: 2.75rem;
  height: 3px;
  margin-top: 0.55rem;
  border-radius: 2px;
  background: var(--matiere);
}

/* 13.4 · Le menu de choix.
   Une liste déroulante ne peut pas montrer les couleurs. Le repère visuel
   se trouve juste à côté, sur la pastille du dossier une fois créé. */
.ui-v1 select[name="color"]{ max-width: 16rem; }

/* Integration: preserve readable labels and access to secondary navigation. */
@media(max-width:849px){.ui-v1 .tabbar a span{overflow-wrap:normal;white-space:normal}.ui-v1 .sidebar.menu-open{max-height:calc(100dvh - 8rem);overflow-y:auto;top:4rem}.ui-v1 .topbar{flex-wrap:wrap}.ui-v1 .tabbar{padding-bottom:calc(.25rem + env(safe-area-inset-bottom))}body.ui-v1:has(.tabbar) main{padding-bottom:calc(6.5rem + env(safe-area-inset-bottom))}}

/* Ma pause: original editorial illustrations, separate from sourced news. */
.ui-v1 .culture-rail .pause-picture{position:relative;margin:0 0 1rem;overflow:hidden;border-radius:18px;background:var(--bg-surface-2)}
.ui-v1 .pause-picture img{display:block;width:100%;height:auto;aspect-ratio:3/2;object-fit:cover}
.ui-v1 .pause-picture figcaption{position:absolute;bottom:8px;right:8px;background:#171826d9;color:#fff;padding:3px 8px;border-radius:8px;font-size:12px;line-height:1.5}
.ui-v1 .daily-discovery>.secondary-empty{padding:12px 16px;min-height:0;display:block}
.ui-v1 .daily-discovery>.secondary-empty>svg{display:none}
.ui-v1 .daily-discovery>.secondary-empty h3{margin:0 0 4px;font-size:16px}
.ui-v1 .daily-discovery>.secondary-empty p{margin:4px 0}
.ui-v1 .culture-rails{grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr))}

.ui-v1 .article-preview{display:block;overflow:hidden;border-radius:14px;margin-bottom:12px}
.ui-v1 .article-preview img{display:block;width:100%;height:auto;aspect-ratio:16/9;object-fit:cover;background:var(--bg-surface-2)}
