/* 1-socle.css — la mise en page, premier de neuf. Aucune couleur en dur : tout vient de tokens.css.
 *
 * C'est vérifiable, donc ce sera vérifié : le check `no-literal` refuse un hex, un px de
 * couleur ou une valeur arbitraire hors des tokens. Sur les quatre produits existants il y a
 * aujourd'hui environ 700 couleurs écrites à la main, réparties sur trois palettes qui
 * n'ont aucun rapport entre elles. C'est ce que cette règle existe pour empêcher.
 *
 * Mobile d'abord, et pas seulement en largeur : la navigation est EN BAS, à portée de pouce.
 * Un back-office qu'on consulte debout, d'une main, ne met pas ses onglets hors d'atteinte.
 *
 * ── L'ORDRE COMPTE ─────────────────────────────────────────────────────────────────
 * Ce fichier est une TRANCHE de l'ancien app.css, découpé en neuf morceaux contigus. Le
 * chiffre en tête du nom est son rang de cascade, et ce n'est pas de la décoration : lus
 * dans l'ordre, les neuf fichiers valent exactement l'ancien, à l'octet près. Les intervertir
 * change ce qui l'emporte — et une cascade inversée ne se voit pas, elle se constate trois
 * jours plus tard sur un écran précis.
 *
 * Deux raisons de ne PAS regrouper par thème en réordonnant : les paires
 * `@media`/`@container` se redoublent (chacune doit rester APRÈS la @media qu'elle double),
 * et `container-type` définit l'unité de mesure de tout ce qu'il contient. Le découpage
 * contigu rend ces deux pièges impossibles au lieu de demander qu'on y pense.
 *
 * `tests/test_feuilles_css.py` vérifie que les quatre points de chargement listent bien ces
 * neuf fichiers, dans ce rang.
 */


/* Un élément sur mesure est `display: inline` par défaut — un piège discret et coûteux :
 * en ligne, il ignore largeur et débordement, et son contenu déborde de son parent sans
 * qu'aucune règle sur le parent n'y change quoi que ce soit. C'est ce qui faisait sortir les
 * cartes de chantier de l'écran malgré leurs contraintes de largeur. */
lane-pillar, lane-settings, lane-steps, lane-backlog, lane-gallery, lane-jalons,
lane-sante, lane-fil, lane-toile {
  display: block;
  min-width: 0;
  max-width: 100%;
}

lane-app {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;   /* dvh et pas vh : la barre d'URL mobile fausse vh */
  max-width: 720px;
  margin-inline: auto;
  /* `clip` et surtout PAS `hidden`. Les deux coupent le débordement, mais `hidden` fait de
   * l'élément un conteneur de défilement — et un enfant `position: sticky` se colle alors à
   * CE conteneur au lieu de la fenêtre. C'est ce qui a fait disparaître la barre de
   * navigation dès qu'on faisait défiler : elle restait collée en bas d'un contenu plus haut
   * que l'écran, donc hors de vue. `clip` coupe sans créer de conteneur. */
  overflow-x: clip;
}

/* — le repli, le temps que les composants prennent la main —
 * `index.html` porte à l'intérieur de `lane-app` un titre, une phrase et un lien. Ils ne
 * sont pas décoratifs : un robot d'indexation ne rend pas le JS, et sans eux la page servie
 * n'a aucun H1 (SEO005). Mais ils s'affichent AUSSI à l'humain, en haut à gauche et sans
 * mise en forme, le temps que le module charge — un rechargement montrait donc une demi-
 * seconde de page étrangère avant le back-office.
 *
 * `:not(:defined)` vise exactement cette fenêtre : il cesse de correspondre à la seconde où
 * `define('lane-app', …)` s'exécute, sans qu'aucun script ait à poser de classe — ce que la
 * politique de sécurité interdirait de faire en ligne de toute façon.
 *
 * Le délai est ce qui empêche de troquer un défaut contre un pire. Masquer sans condition
 * donnerait une page BLANCHE le jour où le module ne charge pas — un déploiement à moitié
 * copié, un réseau qui coupe — et une page blanche ne dit rien, là où « Chargement du
 * back-office… » et le lien de démonstration disent au moins où on en est. Passé une
 * seconde et demie, le chargement n'est plus un scintillement : il devient une attente, et
 * une attente se raconte.
 *
 * L'animation ne bouge rien : durée nulle, elle ne sert qu'à retarder un changement d'état.
 * D'où sa survie au `prefers-reduced-motion` de `tokens.css`, qui écrase les DURÉES et pas
 * les délais — ce serait un bogue si elle animait quoi que ce soit. */
lane-app:not(:defined) {
  visibility: hidden;
  animation: repli-patiente 0s linear 1.5s forwards;
}

@keyframes repli-patiente { to { visibility: visible; } }

/* — en-tête — */

/* Le conteneur de l'en-tête et des onglets. Sur téléphone il n'existe pas : `display:
 * contents` le retire de la mise en page et ses deux enfants redeviennent des éléments de
 * `lane-app`, comme avant. L'ordre est alors donné explicitement — sans lui, les onglets
 * remonteraient avant le contenu, puisqu'ils le précèdent désormais dans le balisage. */
.chrome { display: contents; }

header {
  order: -1;
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  background: var(--ground);
  z-index: 1;
}

header h1 { flex: 1; font-size: var(--t6); letter-spacing: -.02em; }

/* Le nom du produit était un bouton qui ne ressemblait pas à un bouton : on ne trouvait
 * plus le chemin du retour. Une flèche dit ce qu'elle fait sans qu'on ait à l'essayer. */
.back {
  width: var(--tap);
  margin-left: calc(var(--s2) * -1);
  font-size: var(--t7);
  color: var(--mist);
}

.title {
  flex: 1;
  font: var(--w-gras) var(--t6)/var(--lh-titre) var(--font-head);
  letter-spacing: -.02em;
}

.icon { width: var(--tap); color: var(--mist); font-size: var(--t6); }

main { flex: 1; padding: var(--s4); }

/* — les niveaux d'état —
 * Des CLASSES, pas des `style=` en ligne. La politique de sécurité du site les interdit, et
 * elle a raison : c'est ce qui rend une injection inexploitable. Le symptôme quand on
 * l'oublie est particulièrement trompeur — la page s'affiche parfaitement, mais toutes les
 * couleurs d'état retombent en silence sur leur valeur par défaut. Tout paraît calme. */

.lv-ok      { --lv: var(--ok);    --lv-bg: var(--ok-bg); }
.lv-warn    { --lv: var(--warn);  --lv-bg: var(--warn-bg); }
.lv-error   { --lv: var(--error); --lv-bg: var(--error-bg); }
.lv-unknown { --lv: var(--mist);  --lv-bg: var(--panel2); }
/* Ce que lane répare en ce moment. La couleur de l'accent, pas le gris de
 * l'inconnu : quelque chose s'en occupe, et c'est une bonne nouvelle. */
.lv-encours { --lv: var(--accent); --lv-bg: var(--accent-bg); }

/* — grille de produits —
 * On reconnaît un logo ; on doit LIRE un nom. Sur un téléphone, la différence décide si le
 * choix du produit prend une seconde ou cinq. */

.grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: var(--s4);
}

.grid a {
  display: grid;
  justify-items: center;
  gap: var(--s2);
  padding: var(--s3) var(--s1);
  color: inherit;
  text-decoration: none;
  border-radius: var(--radius);
}

.tile {
  position: relative;
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}

.tile img { width: 40px; height: 40px; object-fit: contain; }

/* Repli en initiales. Trois des sites n'ont réellement aucune icône : l'absence doit se
 * voir, sans donner l'impression que l'outil est cassé. */
.tile i {
  font: var(--w-gras) var(--t7)/var(--lh-plat) var(--font-head);
  font-style: normal;
  color: var(--mist);
  letter-spacing: -.02em;
}

.grid b {
  font: var(--w-fort) var(--t3)/var(--lh-titre) var(--font-body);
  text-align: center;
  overflow-wrap: anywhere;
}

.grid small { font-size: var(--t1); color: var(--mist); }

/* Les deux nombres du produit, côte à côte sous son nom : l'audience puis l'argent, dans
 * l'ordre où on se les demande. Sur une seule ligne — empilés, ils poussaient le sixième
 * produit sous la ligne de flottaison, et un produit qu'on ne voit pas n'existe pas. */
.kpi {
  display: flex;
  gap: var(--s2);
  align-items: baseline;
  justify-content: center;
  white-space: nowrap;
}

.kpi span:first-child { color: var(--text); font-weight: var(--w-fort); }

/* Le signe suffirait, la couleur épargne la lecture : on voit d'un coup lesquels paient
 * leur hébergement. Le gris est le seul honnête quand on ne sait pas. */
.kpi .pos { color: var(--ok); font-weight: var(--w-fort); }
.kpi .neg { color: var(--error); font-weight: var(--w-fort); }
.kpi .nil { color: var(--mist); }

/* — créer une application —
 *
 * La tuile d'ajout est DANS la grille, en première position, à la taille exacte de ce
 * qu'elle ajoute. Un bouton relégué en bas de page ou dans un menu de réglages se cherche ;
 * celui-là se trouve sans qu'on l'explique. Le trait pointillé dit « emplacement libre »
 * là où un fond plein dirait « application ».
 */
.grid .neuf button {
  display: grid;
  justify-items: center;
  gap: var(--s2);
  padding: var(--s3) var(--s1);
  width: 100%;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
}

.grid .neuf .tile {
  background: none;
  border-style: dashed;
  box-shadow: none;
}

.grid .neuf .tile i { font-size: var(--t8); color: var(--accent); }
.grid .neuf b { color: var(--mist); }

/* Le premier écran d'un compte neuf. Centré et court : il ne se lit qu'une fois dans la
 * vie d'un compte, et tout ce qu'on ajouterait ici serait une chose de plus entre quelqu'un
 * et sa première application. */
.accueil {
  display: grid;
  gap: var(--s3);
  max-width: 34rem;
  margin: var(--s6) auto;
  text-align: center;
}

.accueil h2 { font: var(--w-gras) var(--t7)/var(--lh-titre) var(--font-head); }
.accueil p { color: var(--text); line-height: var(--lh-corps); }
.accueil .muted { background: none; padding: 0; }
.accueil .btn { margin-top: var(--s3); }

/* Un champ de formulaire. L'étiquette AU-DESSUS et pas à l'intérieur : un libellé qui
 * disparaît à la saisie oblige à se souvenir de ce qu'on remplit, et sur un téléphone le
 * clavier cache déjà la moitié de l'écran. */
.champ {
  display: grid;
  gap: var(--s1);
  text-align: left;
}

.champ > span { font: var(--w-fort) var(--t3)/var(--lh-ui) var(--font-body); color: var(--mist); }

.champ input,
.champ select {
  width: 100%;
  padding: var(--s3);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--text);
  /* 16 px au minimum : en dessous, iOS zoome sur le champ au moment de la mise au point et
   * ne dézoome jamais — la page reste décalée pour tout le reste de la session. */
  font: var(--w-normal) var(--t5)/var(--lh-ui) var(--font-body);
}

.champ input:focus-visible,
.champ select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.champ textarea {
  width: 100%;
  padding: var(--s3);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--text);
  font: var(--w-normal) var(--t4)/var(--lh-ui) var(--font-mono, ui-monospace, monospace);
  resize: vertical;
}

.champ textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

